<commit_before>/**
 * Tells
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Tells are the offline messaging system for PS. They are received when a
 * user successfully connects under a name that has tells waiting, and are
 * sent when a pm is sent to a user who does not exist or is not online.
 *
 * Tells are cleared after they have existed for a certain length of time
 * in order to remove any inactive messages. This length of time can be
 * specified in config.js
 *
 * @license MIT license
 */

'use strict';

let fs = require('fs');
let color = require('./config/color');

let tells = {inbox: {}, outbox: {}};
try {
	tells = JSON.parse(fs.readFileSync('config/tells.json'));
} catch (e) {} // file doesn't exist (yet)

/**
 * Purge expired messages from those stored
 * @param threshold	The age limit of an "old" tell, in ms
 */
let pruneOld = exports.pruneOld = function (threshold) {
	let now = Date.now();
	let receivers = Object.keys(Tells.inbox);
	for (let i = 0; i < receivers.length; i++) {
		for (let n = 0; n < Tells.inbox[receivers[i]].length; n++) {
			if ((now - Tells.inbox[receivers[i]][n].time) >= threshold) {
				let ips = Object.keys(Tells.inbox[receivers[i]][n].ips);
				for (let ip = 0; ip < ips.length; ip++) {
					if (Tells.outbox[ips[ip]]) Tells.outbox[ips[ip]]--;
					if (Tells.outbox[ips[ip]] <= 0) delete Tells.outbox[ips[ip]];
				}
				Tells.inbox[receivers[i]].splice(n, 1);
				n--;
			}
		}
		if (!Tells.inbox[receivers[i]].length) delete Tells.inbox[receivers[i]];
	}
	Tells.writeTells();
};

exports.inbox = tells.inbox || {};
exports.outbox = tells.outbox || {};

/**
 * Write the inbox and outbox to file
 */
exports.writeTells = (function () {
	let writing = false;
	let writePending = false; // whether or not a new write is pending
	let finishWriting = function () {
		writing = false;
		if (writePending) {
			writePending = false;
			Tells.writeTells();
		}
	};
	return function () {
		if (writing) {
			writePending = true;
			return;
		}
		writing = true;
		let data = JSON.stringify({inbox: Tells.inbox, outbox: Tells.outbox});
		fs.writeFile('config/tells.json.0', data, function () {
			// rename is atomic on POSIX, but will throw an error on Windows
			fs.rename('config/tells.json.0', 'config/tells.json', function (err) {
				if (err) {
					// This should only happen on Windows.
					fs.writeFile('config/tells.json', data, finishWriting);
					return;
				}
				finishWriting();
			});
		});
	};
})();

/**
 * Format a user's inbox and send it on to the client to be delivered
 * @param userid	The userid whose tells to send
 * @param user		The User object to send the tells to
 */
exports.sendTell = function (userid, user) {
	let buffer = '|raw|';
	let tellsToSend = Tells.inbox[userid];
	for (let i = 0; i < tellsToSend.length; i++) {
		let ips = Object.keys(tellsToSend[i].ips);
		for (let ip = 0; ip < ips.length; ip++) {
			if (Tells.outbox[ips[ip]]) Tells.outbox[ips[ip]]--;
			if (Tells.outbox[ips[ip]] <= 0) delete Tells.outbox[ips[ip]];
		}
		let timeStr = Tells.getTellTime(tellsToSend[i].time);
		buffer += '
[' + timeStr + ' ago] ' + tellsToSend[i].sender + ': ' + Tools.escapeHTML(tellsToSend[i].msg.replace(/\|/g, '|')) + '
';
	}
	user.send(buffer);
	delete Tells.inbox[userid];
	Tells.writeTells();
};

/**
 * Store a tell to be received later
 * @param sender	The User object of the sender
 * @param receiver	The target userid
 * @param msg		The message to be send
 * @return		false if the receiver has a full inbox
 *			null if the sender has a full outbox
 *			otherwise true
 */
exports.addTell = function (sender, receiver, msg) {
	if (Tells.inbox[receiver] && Tells.inbox[receiver].length >= 5) return false;
	let ips = Object.keys(sender.ips);
	for (let i = 0; i < ips.length; i++) {
		if (!Tells.outbox[ips[i]]) {
			Tells.outbox[ips[i]] = 1;
		} else {
			if (Tells.outbox[ips[i]] >= 10) return null;
			Tells.outbox[ips[i]]++;
		}
	}
	if (!Tells.inbox[receiver]) Tells.inbox[receiver] = [];
	let newTell = {
		'sender': sender.name,
		time: Date.now(),
		'msg': msg,
		ips: sender.ips,
	};
	Tells.inbox[receiver].push(newTell);
	Tells.writeTells();
	return true;
};

/**
 * Converts a UNIX timestamp into 'x minutes, y seconds ago' form
 * @param time	UNIX timestamp (e.g., 1405460769855)
 * @return 	A human readable time difference between now and the given time
 */
exports.getTellTime = function (time) {
	time = Date.now() - time;
	time = Math.round(time / 1000); // rounds to nearest second
	let seconds = time % 60;
	let times = [];
	if (seconds) times.push(String(seconds) + (seconds === 1 ? ' second' : ' seconds'));
	let minutes, hours, days;
	if (time >= 60) {
		time = (time - seconds) / 60; // converts to minutes
		minutes = time % 60;
		if (minutes) times.unshift(String(minutes) + (minutes === 1 ? ' minute' : ' minutes'));
		if (time >= 60) {
			time = (time - minutes) / 60; // converts to hours
			hours = time % 24;
			if (hours) times.unshift(String(hours) + (hours === 1 ? ' hour' : ' hours'));
			if (time >= 24) {
				days = (time - hours) / 24; // you can probably guess this one
				if (days) times.unshift(String(days) + (days === 1 ? ' day' : ' days'));
			}
		}
	}
	if (!times.length) times.push('0 seconds');
	return times.join(', ');
};

// clear old messages every two hours
exports.pruneOldTimer = setInterval(pruneOld, 1000 * 60 * 60 * 2,
        Config.tellsexpiryage || 1000 * 60 * 60 * 24 * 7);
<commit_msg>updated with main<commit_after>/**
 * Tells
 * Pokemon Showdown - http://pokemonshowdown.com/
 *
 * Tells are the offline messaging system for PS. They are received when a
 * user successfully connects under a name that has tells waiting, and are
 * sent when a pm is sent to a user who does not exist or is not online.
 *
 * Tells are cleared after they have existed for a certain length of time
 * in order to remove any inactive messages. This length of time can be
 * specified in config.js
 *
 * @license MIT license
 */

'use strict';

let fs = require('fs');
let color = require('./config/color');

let tells = {inbox: {}, outbox: {}};
try {
	tells = JSON.parse(fs.readFileSync('config/tells.json'));
} catch (e) {} // file doesn't exist (yet)

/**
 * Purge expired messages from those stored
 * @param threshold	The age limit of an "old" tell, in ms
 */
let pruneOld = exports.pruneOld = function (threshold) {
	let now = Date.now();
	let receivers = Object.keys(Tells.inbox);
	for (let i = 0; i < receivers.length; i++) {
		for (let n = 0; n < Tells.inbox[receivers[i]].length; n++) {
			if ((now - Tells.inbox[receivers[i]][n].time) >= threshold) {
				let ips = Object.keys(Tells.inbox[receivers[i]][n].ips);
				for (let ip = 0; ip < ips.length; ip++) {
					if (Tells.outbox[ips[ip]]) Tells.outbox[ips[ip]]--;
					if (Tells.outbox[ips[ip]] <= 0) delete Tells.outbox[ips[ip]];
				}
				Tells.inbox[receivers[i]].splice(n, 1);
				n--;
			}
		}
		if (!Tells.inbox[receivers[i]].length) delete Tells.inbox[receivers[i]];
	}
	Tells.writeTells();
};

exports.inbox = tells.inbox || {};
exports.outbox = tells.outbox || {};

/**
 * Write the inbox and outbox to file
 */
exports.writeTells = (function () {
	let writing = false;
	let writePending = false; // whether or not a new write is pending
	let finishWriting = function () {
		writing = false;
		if (writePending) {
			writePending = false;
			Tells.writeTells();
		}
	};
	return function () {
		if (writing) {
			writePending = true;
			return;
		}
		writing = true;
		let data = JSON.stringify({inbox: Tells.inbox, outbox: Tells.outbox});
		fs.writeFile('config/tells.json.0', data, function () {
			// rename is atomic on POSIX, but will throw an error on Windows
			fs.rename('config/tells.json.0', 'config/tells.json', function (err) {
				if (err) {
					// This should only happen on Windows.
					fs.writeFile('config/tells.json', data, finishWriting);
					return;
				}
				finishWriting();
			});
		});
	};
})();

/**
 * Format a user's inbox and send it on to the client to be delivered
 * @param userid	The userid whose tells to send
 * @param user		The User object to send the tells to
 */
exports.sendTell = function (userid, user) {
	let buffer = '|raw|';
	let tellsToSend = Tells.inbox[userid];
	for (let i = 0; i < tellsToSend.length; i++) {
		let ips = Object.keys(tellsToSend[i].ips);
		for (let ip = 0; ip < ips.length; ip++) {
			if (Tells.outbox[ips[ip]]) Tells.outbox[ips[ip]]--;
			if (Tells.outbox[ips[ip]] <= 0) delete Tells.outbox[ips[ip]];
		}
		let timeStr = Tells.getTellTime(tellsToSend[i].time);
		buffer += '
[' + timeStr + ' ago] ' + tellsToSend[i].sender + ': ' + Chat.escapeHTML(tellsToSend[i].msg.replace(/\|/g, '|')) + '
';
	}
	user.send(buffer);
	delete Tells.inbox[userid];
	Tells.writeTells();
};

/**
 * Store a tell to be received later
 * @param sender	The User object of the sender
 * @param receiver	The target userid
 * @param msg		The message to be send
 * @return		false if the receiver has a full inbox
 *			null if the sender has a full outbox
 *			otherwise true
 */
exports.addTell = function (sender, receiver, msg) {
	if (Tells.inbox[receiver] && Tells.inbox[receiver].length >= 5) return false;
	let ips = Object.keys(sender.ips);
	for (let i = 0; i < ips.length; i++) {
		if (!Tells.outbox[ips[i]]) {
			Tells.outbox[ips[i]] = 1;
		} else {
			if (Tells.outbox[ips[i]] >= 10) return null;
			Tells.outbox[ips[i]]++;
		}
	}
	if (!Tells.inbox[receiver]) Tells.inbox[receiver] = [];
	let newTell = {
		'sender': sender.name,
		time: Date.now(),
		'msg': msg,
		ips: sender.ips,
	};
	Tells.inbox[receiver].push(newTell);
	Tells.writeTells();
	return true;
};

/**
 * Converts a UNIX timestamp into 'x minutes, y seconds ago' form
 * @param time	UNIX timestamp (e.g., 1405460769855)
 * @return 	A human readable time difference between now and the given time
 */
exports.getTellTime = function (time) {
	time = Date.now() - time;
	time = Math.round(time / 1000); // rounds to nearest second
	let seconds = time % 60;
	let times = [];
	if (seconds) times.push(String(seconds) + (seconds === 1 ? ' second' : ' seconds'));
	let minutes, hours, days;
	if (time >= 60) {
		time = (time - seconds) / 60; // converts to minutes
		minutes = time % 60;
		if (minutes) times.unshift(String(minutes) + (minutes === 1 ? ' minute' : ' minutes'));
		if (time >= 60) {
			time = (time - minutes) / 60; // converts to hours
			hours = time % 24;
			if (hours) times.unshift(String(hours) + (hours === 1 ? ' hour' : ' hours'));
			if (time >= 24) {
				days = (time - hours) / 24; // you can probably guess this one
				if (days) times.unshift(String(days) + (days === 1 ? ' day' : ' days'));
			}
		}
	}
	if (!times.length) times.push('0 seconds');
	return times.join(', ');
};

// clear old messages every two hours
exports.pruneOldTimer = setInterval(pruneOld, 1000 * 60 * 60 * 2,
        Config.tellsexpiryage || 1000 * 60 * 60 * 24 * 7);
<|endoftext|>


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

/**
 * @name SvgImporter
 * @class The SvgImporter object represents an object created using the SVG
 * Canvas that will be converted into a Paper.js object.
 * The SVG object is imported into Paper.js by converting it into items
 * within groups.
 *
 */
var SvgImporter = this.SvgImporter = new function() {

	function importGroup(svg) {
		var group = new Group(),
			nodes = svg.childNodes;
		for (var i = 0, l = nodes.length; i < l; i++) {
			var child = nodes[i];
			if (child.nodeType == 1) {
				var item = SvgImporter.importSvg(child);
				if (item)
					group.addChild(item);
			}
		}
		return group;
	}

	function importPoly(svg) {
		var poly = new Path(),
			points = svg.points,
			start = points.getItem(0);
		poly.moveTo(start);
		for (var i = 1; i < points.length; i++)
			poly.lineTo(points.getItem(i));
		if (svg.nodeName.toLowerCase() == 'polygon')
			poly.closePath();
		return poly;
	}

	function getValue(svg, key, index) {
		var base = svg[key].baseVal;
		return index !== undefined
				? base.numberOfItems > 0 ? base.getItem(index).value || 0 : 0
				: base.value || 0;
	}

	function getPoint(svg, x, y, index) {
		return Point.create(getValue(svg, x, index), getValue(svg, y, index));
	}

	function getSize(svg, w, h, index) {
		return Size.create(getValue(svg, w, index), getValue(svg, h, index));
	}

	var importers = {
		g: importGroup,
		svg: importGroup,
		polygon: importPoly,
		polyline: importPoly,

		circle: function(svg) {
			return new Path.Circle(getPoint(svg, 'cx', 'cy'),
					getValue(svg, 'r'));
		},

		ellipse: function(svg) {
			var center = getPoint(svg, 'cx', 'cy'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.Oval(new Rectangle(center.subtract(radius),
					center.add(radius)));
		},

		rect: function(svg) {
			var point = getPoint(svg, 'x', 'y'),
				size = getSize(svg, 'width', 'height'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.RoundRectangle(new Rectangle(point, size), radius);
		},

		line: function(svg) {
			return new Path.Line(getPoint(svg, 'x1', 'y1'),
					getPoint(svg, 'x2', 'y2'));
		},

		text: function(svg) {
			var bottomLeft = getPoint(svg, 'x', 'y', 0),
				textLength = getValue(svg, 'textLength'),
				delta = getPoint(svg, 'dx', 'dy', 0);
			// Not supported by Paper.js
			// x: multiple values for x
			// y: multiple values for y
			// dx: multiple values for x
			// dy: multiple values for y
			// rotate: character rotation
			// lengthAdjust:
			var point = bottomLeft.add(delta).subtract(textLength / 2, 0);
			var text = new PointText(point);
			text.content = svg.textContent || '';
			return text;
		},

		path: function(svg) {
			var path = new Path();
			var segments = svg.pathSegList;
			var segment;
			var j;
			var relativeToPoint;
			var controlPoint;
			var prevCommand;
			var segmentTo;
			for (var i = 0; i < segments.numberOfItems; i++) {
				segment = segments.getItem(i);
				if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
					continue;
				}
				if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
					relativeToPoint = path.lastSegment.point;
				} else {
					relativeToPoint = new Point(0, 0);
				}
				segmentTo = new Point(segment.x, segment.y);
				segmentTo = segmentTo.add(relativeToPoint);
				switch (segment.pathSegType) {
				case SVGPathSeg.PATHSEG_CLOSEPATH:
					path.closePath();
					break;
				case SVGPathSeg.PATHSEG_MOVETO_ABS:
				case SVGPathSeg.PATHSEG_MOVETO_REL:
					path.moveTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_LINETO_ABS:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_REL:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
					path.lineTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
					path.cubicCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
					path.quadraticCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_ARC_ABS:
				case SVGPathSeg.PATHSEG_ARC_REL:
					//TODO: Implement Arcs.
					//TODO: Requires changes in Paper.js's Path to do.
					//TODO: http://www.w3.org/TR/SVG/implnote.html
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
					prevCommand = segments.getItem(i - 1);
					controlPoint = new Point(prevCommand.x2, prevCommand.y2);
					controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
					controlPoint = controlPoint.add(path.lastSegment.point);
					controlPoint = path.lastSegment.point.subtract(controlPoint);
					controlPoint = path.lastSegment.point.add(controlPoint);
					path.cubicCurveTo(
						controlPoint,
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
					for (j = i; j >= 0; --j) {
						prevCommand = segments.getItem(j);
						if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
							prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
						) {
							controlPoint = new Point(prevCommand.x1, prevCommand.y1);
							controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
							controlPoint = controlPoint.add(path.segments[j].point);
							break;
						}
					}
					for (j; j < i; ++j) {
						controlPoint = path.segments[j].point.subtract(controlPoint);
						controlPoint = path.segments[j].point.add(controlPoint);
					}
					path.quadraticCurveTo(controlPoint, segmentTo);
					break;
				}
			}

			return path;
		},

		symbol: function(svg) {
			var item = importGroup(svg);
			importAttributesAndStyles(svg, item);
			// TODO: We're returning a symbol. How to handle this?
			return new Symbol(item);
		}
	};

	/**
	 * Converts various SVG styles and attributes into Paper.js styles and
	 * attributes and applies them to the passed item.
	 *
	 * @param {SVGSVGElement} svg an SVG node to read style and attributes from.
	 * @param {Item} item the item to apply the style and attributes to.
	 */
	function importAttributesAndStyles(svg, item) {
		var name,
			value,
			cssName;
		for (var i = 0; i < svg.style.length; i++) {
			name = svg.style[i];
			cssName = name.replace(/-(.)/g, function(match, p) {
				return p.toUpperCase();
			});
			value = svg.style[cssName];
			applyAttributeOrStyle(name, value, item, svg);
		}
		for (var i = 0; i < svg.attributes.length; i++) {
			name = svg.attributes[i].name;
			value = svg.attributes[i].value;
			applyAttributeOrStyle(name, value, item, svg);
		}
	}

	/**
	 * Parses an SVG style attibute and applies it to a Paper.js item.
	 *
	 * @param {String} name an SVG style name
	 * @param value the value of the SVG style
	 * @param {Item} item the item to apply the style or attribute to.
	 * @param {SVGSVGElement} svg an SVG node
	 */
	 function applyAttributeOrStyle(name, value, item, svg) {
		if (!value) {
			return;
		}
		switch (name) {
		case 'id':
			item.name = value;
			break;
		case 'fill':
			if (value != 'none') {
				item.fillColor = value;
			}
			break;
		case 'stroke':
			if (value != 'none') {
				item.strokeColor = value;
			}
			break;
		case 'stroke-width':
			item.strokeWidth = parseFloat(value, 10);
			break;
		case 'stroke-linecap':
			item.strokeCap = value;
			break;
		case 'stroke-linejoin':
			item.strokeJoin = value;
			break;
		case 'stroke-dasharray':
			value = value.replace(/px/g, '');
			value = value.replace(/, /g, ',');
			value = value.replace(/ /g, ',');
			value = value.split(',');
			for (var i in value) {
				value[i] = parseFloat(value[i], 10);
			}
			item.dashArray = value;
			break;
		case 'stroke-dashoffset':
			item.dashOffset = parseFloat(value, 10);
			break;
		case 'stroke-miterlimit':
			item.miterLimit = parseFloat(value, 10);
			break;
		case 'transform':
			applyTransform(item, svg);
			break;
		case 'opacity':
			item.opacity = parseFloat(value, 10);
			break;
		case 'visibility':
			item.visibility = (value == 'visible') ? true : false;
			break;
		case 'font':
		case 'font-family':
		case 'font-size':
			//Implemented in characterStyle below.
			break;
		default:
			// Not supported yet.
			break;
		}
		if (item.characterStyle) {
			switch (name) {
			case 'font':
				var text = document.createElement('span');
				text.style.font = value;
				for (var i = 0; i < text.style.length; i++) {
					var n = text.style[i];
					applyAttributeOrStyle(n, text.style[n], item, svg);
				}
				break;
			case 'font-family':
				var fonts = value.split(',');
				fonts[0] = fonts[0].replace(/^\s+|\s+$/g, "");
				item.characterStyle.font = fonts[0];
				break;
			case 'font-size':
				item.characterStyle.fontSize = parseFloat(value, 10);
				break;
			}
		}
	}

	/**
	 * Applies the transformations specified on the SVG node to a Paper.js item
	 *
	 * @param {Item} item a Paper.js item
	 * @param {SVGSVGElement} svg an SVG node
	 */
	function applyTransform(item, svg) {
		var transforms = svg.transform.baseVal;
		var transform;
		var matrix = new Matrix();

		for (var i = 0; i < transforms.numberOfItems; i++) {
			transform = transforms.getItem(i);
			if (transform.type == SVGTransform.SVG_TRANSFORM_UNKNOWN) {
				continue;
			}
			var transformMatrix = new Matrix(
				transform.matrix.a,
				transform.matrix.c,
				transform.matrix.b,
				transform.matrix.d,
				transform.matrix.e,
				transform.matrix.f
			);
			switch (transform.type) {
			case SVGTransform.SVG_TRANSFORM_TRANSLATE:
				break;
			case SVGTransform.SVG_TRANSFORM_SCALE:
				break;
			//Compensate for SVG's theta rotation going the opposite direction
			case SVGTransform.SVG_TRANSFORM_MATRIX:
				var temp = transformMatrix.getShearX();
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(temp);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWX:
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(0);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWY:
				transformMatrix.setShearY(transformMatrix.getShearX());
				transformMatrix.setShearX(0);
				break;
			case SVGTransform.SVG_TRANSFORM_ROTATE:
				transformMatrix.setShearX(-transformMatrix.getShearX());
				transformMatrix.setShearY(-transformMatrix.getShearY());
				break;
			}
			matrix.concatenate(transformMatrix);
		}
		item.transform(matrix);
	}

	return /** @Lends SvgImporter */{
		/**
		 * Creates a Paper.js item using data parsed from the selected
		 * SVG DOM node.
		 *
		 * @param {SVGSVGElement} svg the SVG DOM node to convert
		 * @return {Item} the converted Paper.js item
		 */
		importSvg: function(svg) {
			var importer = importers[svg.nodeName.toLowerCase()];
			// TODO: importer == null: Not supported yet.
			var item = importer && importer(svg);
			if (item)
				importAttributesAndStyles(svg, item);
			return item;
		}
	};
};
<commit_msg>Fix index lookup in getValue().<commit_after>/*
 * Paper.js
 *
 * This file is part of Paper.js, a JavaScript Vector Graphics Library,
 * based on Scriptographer.org and designed to be largely API compatible.
 * http://paperjs.org/
 * http://scriptographer.org/
 *
 * Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
 * http://lehni.org/ & http://jonathanpuckey.com/
 *
 * Distributed under the MIT license. See LICENSE file for details.
 *
 * All rights reserved.
 *
 * The base for this code was donated by Stetson-Team-Alpha.
 * @author Stetson-Team-Alpha
 */

/**
 * @name SvgImporter
 * @class The SvgImporter object represents an object created using the SVG
 * Canvas that will be converted into a Paper.js object.
 * The SVG object is imported into Paper.js by converting it into items
 * within groups.
 *
 */
var SvgImporter = this.SvgImporter = new function() {

	function importGroup(svg) {
		var group = new Group(),
			nodes = svg.childNodes;
		for (var i = 0, l = nodes.length; i < l; i++) {
			var child = nodes[i];
			if (child.nodeType == 1) {
				var item = SvgImporter.importSvg(child);
				if (item)
					group.addChild(item);
			}
		}
		return group;
	}

	function importPoly(svg) {
		var poly = new Path(),
			points = svg.points,
			start = points.getItem(0);
		poly.moveTo(start);
		for (var i = 1; i < points.length; i++)
			poly.lineTo(points.getItem(i));
		if (svg.nodeName.toLowerCase() == 'polygon')
			poly.closePath();
		return poly;
	}

	function getValue(svg, key, index) {
		var base = svg[key].baseVal;
		return index !== undefined
				? index < base.numberOfItems ? base.getItem(index).value || 0 : 0
				: base.value || 0;
	}

	function getPoint(svg, x, y, index) {
		return Point.create(getValue(svg, x, index), getValue(svg, y, index));
	}

	function getSize(svg, w, h, index) {
		return Size.create(getValue(svg, w, index), getValue(svg, h, index));
	}

	var importers = {
		g: importGroup,
		svg: importGroup,
		polygon: importPoly,
		polyline: importPoly,

		circle: function(svg) {
			return new Path.Circle(getPoint(svg, 'cx', 'cy'),
					getValue(svg, 'r'));
		},

		ellipse: function(svg) {
			var center = getPoint(svg, 'cx', 'cy'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.Oval(new Rectangle(center.subtract(radius),
					center.add(radius)));
		},

		rect: function(svg) {
			var point = getPoint(svg, 'x', 'y'),
				size = getSize(svg, 'width', 'height'),
				radius = getSize(svg, 'rx', 'ry');
			return new Path.RoundRectangle(new Rectangle(point, size), radius);
		},

		line: function(svg) {
			return new Path.Line(getPoint(svg, 'x1', 'y1'),
					getPoint(svg, 'x2', 'y2'));
		},

		text: function(svg) {
			var bottomLeft = getPoint(svg, 'x', 'y', 0),
				textLength = getValue(svg, 'textLength'),
				delta = getPoint(svg, 'dx', 'dy', 0);
			// Not supported by Paper.js
			// x: multiple values for x
			// y: multiple values for y
			// dx: multiple values for x
			// dy: multiple values for y
			// rotate: character rotation
			// lengthAdjust:
			var point = bottomLeft.add(delta).subtract(textLength / 2, 0);
			var text = new PointText(point);
			text.content = svg.textContent || '';
			return text;
		},

		path: function(svg) {
			var path = new Path();
			var segments = svg.pathSegList;
			var segment;
			var j;
			var relativeToPoint;
			var controlPoint;
			var prevCommand;
			var segmentTo;
			for (var i = 0; i < segments.numberOfItems; i++) {
				segment = segments.getItem(i);
				if (segment.pathSegType == SVGPathSeg.PATHSEG_UNKNOWN) {
					continue;
				}
				if (segment.pathSegType % 2 == 1 && path.segments.length > 0) {
					relativeToPoint = path.lastSegment.point;
				} else {
					relativeToPoint = new Point(0, 0);
				}
				segmentTo = new Point(segment.x, segment.y);
				segmentTo = segmentTo.add(relativeToPoint);
				switch (segment.pathSegType) {
				case SVGPathSeg.PATHSEG_CLOSEPATH:
					path.closePath();
					break;
				case SVGPathSeg.PATHSEG_MOVETO_ABS:
				case SVGPathSeg.PATHSEG_MOVETO_REL:
					path.moveTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_LINETO_ABS:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_ABS:
				case SVGPathSeg.PATHSEG_LINETO_REL:
				case SVGPathSeg.PATHSEG_LINETO_HORIZONTAL_REL:
				case SVGPathSeg.PATHSEG_LINETO_VERTICAL_REL:
					path.lineTo(segmentTo);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_REL:
					path.cubicCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL:
					path.quadraticCurveTo(
						relativeToPoint.add([segment.x1, segment.y1]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_ARC_ABS:
				case SVGPathSeg.PATHSEG_ARC_REL:
					//TODO: Implement Arcs.
					//TODO: Requires changes in Paper.js's Path to do.
					//TODO: http://www.w3.org/TR/SVG/implnote.html
					break;
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
					prevCommand = segments.getItem(i - 1);
					controlPoint = new Point(prevCommand.x2, prevCommand.y2);
					controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
					controlPoint = controlPoint.add(path.lastSegment.point);
					controlPoint = path.lastSegment.point.subtract(controlPoint);
					controlPoint = path.lastSegment.point.add(controlPoint);
					path.cubicCurveTo(
						controlPoint,
						relativeToPoint.add([segment.x2, segment.y2]),
						segmentTo
					);
					break;
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
				case SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
					for (j = i; j >= 0; --j) {
						prevCommand = segments.getItem(j);
						if (prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_ABS ||
							prevCommand.pathSegType == SVGPathSeg.PATHSEG_CURVETO_QUADRATIC_REL
						) {
							controlPoint = new Point(prevCommand.x1, prevCommand.y1);
							controlPoint = controlPoint.subtract([prevCommand.x, prevCommand.y]);
							controlPoint = controlPoint.add(path.segments[j].point);
							break;
						}
					}
					for (j; j < i; ++j) {
						controlPoint = path.segments[j].point.subtract(controlPoint);
						controlPoint = path.segments[j].point.add(controlPoint);
					}
					path.quadraticCurveTo(controlPoint, segmentTo);
					break;
				}
			}

			return path;
		},

		symbol: function(svg) {
			var item = importGroup(svg);
			importAttributesAndStyles(svg, item);
			// TODO: We're returning a symbol. How to handle this?
			return new Symbol(item);
		}
	};

	/**
	 * Converts various SVG styles and attributes into Paper.js styles and
	 * attributes and applies them to the passed item.
	 *
	 * @param {SVGSVGElement} svg an SVG node to read style and attributes from.
	 * @param {Item} item the item to apply the style and attributes to.
	 */
	function importAttributesAndStyles(svg, item) {
		var name,
			value,
			cssName;
		for (var i = 0; i < svg.style.length; i++) {
			name = svg.style[i];
			cssName = name.replace(/-(.)/g, function(match, p) {
				return p.toUpperCase();
			});
			value = svg.style[cssName];
			applyAttributeOrStyle(name, value, item, svg);
		}
		for (var i = 0; i < svg.attributes.length; i++) {
			name = svg.attributes[i].name;
			value = svg.attributes[i].value;
			applyAttributeOrStyle(name, value, item, svg);
		}
	}

	/**
	 * Parses an SVG style attibute and applies it to a Paper.js item.
	 *
	 * @param {String} name an SVG style name
	 * @param value the value of the SVG style
	 * @param {Item} item the item to apply the style or attribute to.
	 * @param {SVGSVGElement} svg an SVG node
	 */
	 function applyAttributeOrStyle(name, value, item, svg) {
		if (!value) {
			return;
		}
		switch (name) {
		case 'id':
			item.name = value;
			break;
		case 'fill':
			if (value != 'none') {
				item.fillColor = value;
			}
			break;
		case 'stroke':
			if (value != 'none') {
				item.strokeColor = value;
			}
			break;
		case 'stroke-width':
			item.strokeWidth = parseFloat(value, 10);
			break;
		case 'stroke-linecap':
			item.strokeCap = value;
			break;
		case 'stroke-linejoin':
			item.strokeJoin = value;
			break;
		case 'stroke-dasharray':
			value = value.replace(/px/g, '');
			value = value.replace(/, /g, ',');
			value = value.replace(/ /g, ',');
			value = value.split(',');
			for (var i in value) {
				value[i] = parseFloat(value[i], 10);
			}
			item.dashArray = value;
			break;
		case 'stroke-dashoffset':
			item.dashOffset = parseFloat(value, 10);
			break;
		case 'stroke-miterlimit':
			item.miterLimit = parseFloat(value, 10);
			break;
		case 'transform':
			applyTransform(item, svg);
			break;
		case 'opacity':
			item.opacity = parseFloat(value, 10);
			break;
		case 'visibility':
			item.visibility = (value == 'visible') ? true : false;
			break;
		case 'font':
		case 'font-family':
		case 'font-size':
			//Implemented in characterStyle below.
			break;
		default:
			// Not supported yet.
			break;
		}
		if (item.characterStyle) {
			switch (name) {
			case 'font':
				var text = document.createElement('span');
				text.style.font = value;
				for (var i = 0; i < text.style.length; i++) {
					var n = text.style[i];
					applyAttributeOrStyle(n, text.style[n], item, svg);
				}
				break;
			case 'font-family':
				var fonts = value.split(',');
				fonts[0] = fonts[0].replace(/^\s+|\s+$/g, "");
				item.characterStyle.font = fonts[0];
				break;
			case 'font-size':
				item.characterStyle.fontSize = parseFloat(value, 10);
				break;
			}
		}
	}

	/**
	 * Applies the transformations specified on the SVG node to a Paper.js item
	 *
	 * @param {Item} item a Paper.js item
	 * @param {SVGSVGElement} svg an SVG node
	 */
	function applyTransform(item, svg) {
		var transforms = svg.transform.baseVal;
		var transform;
		var matrix = new Matrix();

		for (var i = 0; i < transforms.numberOfItems; i++) {
			transform = transforms.getItem(i);
			if (transform.type == SVGTransform.SVG_TRANSFORM_UNKNOWN) {
				continue;
			}
			var transformMatrix = new Matrix(
				transform.matrix.a,
				transform.matrix.c,
				transform.matrix.b,
				transform.matrix.d,
				transform.matrix.e,
				transform.matrix.f
			);
			switch (transform.type) {
			case SVGTransform.SVG_TRANSFORM_TRANSLATE:
				break;
			case SVGTransform.SVG_TRANSFORM_SCALE:
				break;
			//Compensate for SVG's theta rotation going the opposite direction
			case SVGTransform.SVG_TRANSFORM_MATRIX:
				var temp = transformMatrix.getShearX();
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(temp);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWX:
				transformMatrix.setShearX(transformMatrix.getShearY());
				transformMatrix.setShearY(0);
				break;
			case SVGTransform.SVG_TRANSFORM_SKEWY:
				transformMatrix.setShearY(transformMatrix.getShearX());
				transformMatrix.setShearX(0);
				break;
			case SVGTransform.SVG_TRANSFORM_ROTATE:
				transformMatrix.setShearX(-transformMatrix.getShearX());
				transformMatrix.setShearY(-transformMatrix.getShearY());
				break;
			}
			matrix.concatenate(transformMatrix);
		}
		item.transform(matrix);
	}

	return /** @Lends SvgImporter */{
		/**
		 * Creates a Paper.js item using data parsed from the selected
		 * SVG DOM node.
		 *
		 * @param {SVGSVGElement} svg the SVG DOM node to convert
		 * @return {Item} the converted Paper.js item
		 */
		importSvg: function(svg) {
			var importer = importers[svg.nodeName.toLowerCase()];
			// TODO: importer == null: Not supported yet.
			var item = importer && importer(svg);
			if (item)
				importAttributesAndStyles(svg, item);
			return item;
		}
	};
};
<|endoftext|>


<commit_before>import React from 'react';
import { mount } from 'enzyme';
import Select from '..';
import Icon from '../../Icon';
import focusTest from '../../../tests/shared/focusTest';

const { Option } = Select;

describe('Select', () => {
  focusTest(Select);

  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('should have default notFoundContent', () => {
    const wrapper = mount(
      <Select mode="multiple" />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(1);
    expect(dropdownWrapper.find('MenuItem').at(0).text()).toBe('Not Found');
  });

  it('should support set notFoundContent to null', () => {
    const wrapper = mount(
      <Select mode="multiple" notFoundContent={null} />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(0);
  });

  it('should not have default notFoundContent when mode is combobox', () => {
    const wrapper = mount(
      <Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(0);
  });

  it('should not have notFoundContent when mode is combobox and notFoundContent is set', () => {
    const wrapper = mount(
      <Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} notFoundContent="not at all" />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(1);
    expect(dropdownWrapper.find('MenuItem').at(0).text()).toBe('not at all');
  });

  it('should be controlled by open prop', () => {
    const onDropdownVisibleChange = jest.fn();
    const wrapper = mount(
      <Select open onDropdownVisibleChange={onDropdownVisibleChange}>
        <Option value="1">1</Option>
      </Select>
    );
    let dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.props().visible).toBe(true);
    wrapper.find('.ant-select').simulate('click');
    expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(false);
    expect(dropdownWrapper.props().visible).toBe(true);

    wrapper.setProps({ open: false });
    dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.props().visible).toBe(false);
    wrapper.find('.ant-select').simulate('click');
    expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(true);
    expect(dropdownWrapper.props().visible).toBe(false);
  });

  describe('Select Custom Icons', () => {
    it('should support customized icons', () => {
      const wrapper = mount(
        <Select
          removeIcon={<Icon type="close" />}
          clearIcon={<Icon type="close" />}
          menuItemSelectedIcon={<Icon type="close" />}
        >
          <Option value="1">1</Option>
        </Select>
      );
      wrapper.setProps({ count: 10 });
      jest.runAllTimers();
      expect(wrapper).toMatchSnapshot();
    });
  });
});
<commit_msg>:bug: Fix case sensitive module path<commit_after>import React from 'react';
import { mount } from 'enzyme';
import Select from '..';
import Icon from '../../icon';
import focusTest from '../../../tests/shared/focusTest';

const { Option } = Select;

describe('Select', () => {
  focusTest(Select);

  beforeEach(() => {
    jest.useFakeTimers();
  });

  afterEach(() => {
    jest.useRealTimers();
  });

  it('should have default notFoundContent', () => {
    const wrapper = mount(
      <Select mode="multiple" />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(1);
    expect(dropdownWrapper.find('MenuItem').at(0).text()).toBe('Not Found');
  });

  it('should support set notFoundContent to null', () => {
    const wrapper = mount(
      <Select mode="multiple" notFoundContent={null} />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(0);
  });

  it('should not have default notFoundContent when mode is combobox', () => {
    const wrapper = mount(
      <Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(0);
  });

  it('should not have notFoundContent when mode is combobox and notFoundContent is set', () => {
    const wrapper = mount(
      <Select mode={Select.SECRET_COMBOBOX_MODE_DO_NOT_USE} notFoundContent="not at all" />
    );
    wrapper.find('.ant-select').simulate('click');
    jest.runAllTimers();
    const dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.find('MenuItem').length).toBe(1);
    expect(dropdownWrapper.find('MenuItem').at(0).text()).toBe('not at all');
  });

  it('should be controlled by open prop', () => {
    const onDropdownVisibleChange = jest.fn();
    const wrapper = mount(
      <Select open onDropdownVisibleChange={onDropdownVisibleChange}>
        <Option value="1">1</Option>
      </Select>
    );
    let dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.props().visible).toBe(true);
    wrapper.find('.ant-select').simulate('click');
    expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(false);
    expect(dropdownWrapper.props().visible).toBe(true);

    wrapper.setProps({ open: false });
    dropdownWrapper = mount(wrapper.find('Trigger').instance().getComponent());
    expect(dropdownWrapper.props().visible).toBe(false);
    wrapper.find('.ant-select').simulate('click');
    expect(onDropdownVisibleChange).toHaveBeenLastCalledWith(true);
    expect(dropdownWrapper.props().visible).toBe(false);
  });

  describe('Select Custom Icons', () => {
    it('should support customized icons', () => {
      const wrapper = mount(
        <Select
          removeIcon={<Icon type="close" />}
          clearIcon={<Icon type="close" />}
          menuItemSelectedIcon={<Icon type="close" />}
        >
          <Option value="1">1</Option>
        </Select>
      );
      wrapper.setProps({ count: 10 });
      jest.runAllTimers();
      expect(wrapper).toMatchSnapshot();
    });
  });
});
<|endoftext|>


<commit_before>var async     = require('async');
var _         = require('lodash');
var serverlib = require('./server-lib');
var validator = require('./schema-validator');

var DEFAULT_RESULTS_PER_PAGE = 10;
var NUM_TRANSACTION_TYPES = 5;

function getTransaction(remote, dbinterface, opts, callback) {
  if (opts.identifier) {
    if (validator.isValid(opts.identifier, 'Hash256')) {
      opts.hash = opts.identifier;
    } else if (validator.isValid(opts.identifier, 'ResourceId')) {
      opts.client_resource_id = opts.identifier;
    } else {
      callback(new Error('Invalid Parameter: hash or client_resource_id. Must provide a transaction hash or a client_resource_id to lookup a notification'));
    }
  }

  var steps = [

    function(async_callback) {
      serverlib.ensureConnected(remote, async_callback);
    },

    function(connected, async_callback) {
      dbinterface.getTransaction(opts, async_callback);
    },

    function(db_entry, async_callback) {
      if (db_entry && db_entry.transaction) {
        async_callback(null, db_entry.transaction);
      } else if (db_entry) {
        remote.requestTx(db_entry.hash, async_callback);
      } else if (opts.hash) {
        remote.requestTx(opts.hash, async_callback);
      } else {
        async_callback({ remote: { error: 'txnNotFound' } });
      }
    },

    function(transaction, async_callback) {

      console.log(opts.account, transaction);

      if (opts.account) {
        var transaction_string = JSON.stringify(transaction);
        var account_regex = new RegExp(opts.account);
        if (!account_regex.test(transaction_string)) {
          return async_callback(new Error('Transaction unrelated to account. The transaction specified did not affect the given account'));
        }
      }

      async_callback(null, transaction);

    },

    function(transaction, async_callback) {
      if (transaction && opts.client_resource_id) {
        transaction.client_resource_id = opts.client_resource_id;
      }

      async_callback(null, transaction);
    },

    function(transaction, async_callback) {
      if (!transaction || transaction.date || !transaction.ledger_index) {
        return async_callback(null, transaction);
      }

      remote.requestLedger(transaction.ledger_index, function(err, res){
        if (err) {
          return async_callback(err);
        }

        transaction.date = res.ledger.close_time;
        async_callback(null, transaction);
      });
    }

  ];

  async.waterfall(steps, function(err, transaction) {
    if (err) {
      if (err.remote && err.remote.error === 'txnNotFound') {
        callback(new Error('Transaction Not Found. No transaction corresponding to the given hash or client_resource_id was found either in the local ripple-rest database or in the rippled\'s database. This may indicate that the transaction was never or not yet validated and written into the Ripple Ledger, or this error may be seen if either the ripple-rest or rippled database were recently created or deleted. ' + JSON.stringify(err)));
      } else {
        callback(err);
      }
      return;
    }

    callback(null, transaction);
  });
};

/**
 *  Get all failed and validated transactions for the specified account
 *  from the local database as well as the rippled.
 *
 *  opts:
 *    account
 *    source_account
 *    destination_account
 *    ledger_index or ledger_index_min / ledger_index_max
 *    descending
 *    max
 *    min
 *    offset
 *    exclude_failed
 *    types
 */

 // TODO Refactor this code and clean up the logic
function getAccountTransactions(remote, dbinterface, opts, callback, previous_transactions) {
  if (!opts.max) {
    opts.max = DEFAULT_RESULTS_PER_PAGE;
  }
  if (!opts.min) {
    opts.min = DEFAULT_RESULTS_PER_PAGE;
  }

  // Limit will be set if this function is called recursively
  if (!opts.limit) {
    if (opts.types && opts.types.length < NUM_TRANSACTION_TYPES) {
      opts.limit = 2 * Math.max(opts.max, DEFAULT_RESULTS_PER_PAGE);
    } else {
      opts.limit = opts.max;
    }
  }

  var steps = [

    function(async_callback) {
      serverlib.ensureConnected(remote, async_callback);
    },

    // Get transactions from rippled and local db
    function(connected, async_callback) {
      getLocalAndRemoteTransactions(remote, dbinterface, opts, async_callback);
    },

    // Filter results so that they are unique and match the given parameters
    function(transactions, async_callback) {
      var filtered = filterTransactions(transactions, opts);
      async_callback(null, filtered);
    },

    // Sort results
    function(transactions, async_callback) {
      transactions.sort(_.partialRight(compareTransactions, opts.descending));
      async_callback(null, transactions);
    }

  ];

  async.waterfall(steps, function(err, transactions){
    if (err) {
      callback(err);
      return;
    }

    // Combine transactions with previous_transactions from previous
    // recursive call of this function
    if (previous_transactions && previous_transactions.length > 0) {
      transactions = previous_transactions.concat(transactions);
    }

    // Handle offset
    if (opts.offset && opts.offset > 0) {
      var offset_remaining = opts.offset - transactions.length;
      transactions = transactions.slice(opts.offset);
      opts.offset = offset_remaining;
    }

    // Truncate results if there are too many
    if (transactions.length > opts.max) {
      transactions = transactions.slice(0, opts.max);
    }

    // If there are enough transactions, send them back to the client
    // Otherwise recur
    if (!opts.min || transactions.length >= opts.min || !opts.marker) {
      callback(null, transactions);
    } else {
      setImmediate(function(){
        getAccountTransactions(remote, dbinterface, opts, callback, transactions);
      });
    }

  });
}

function getLocalAndRemoteTransactions(remote, dbinterface, opts, callback) {
  var transaction_sources = [

    function(parallel_callback){
      getAccountTx(remote, opts, function(err, results){
        if (err) {
          parallel_callback(err);
          return;
        }

        // Set marker so that when this function is called again
        // recursively it starts from the last place it left off
        opts.marker = results.marker;

        parallel_callback(null, results.transactions);
      });
    },

    function(parallel_callback) {
      if (opts.exclude_failed) {
        parallel_callback(null, []);
      } else {
        dbinterface.getFailedTransactions(opts, parallel_callback);
      }
    }

  ];

  async.parallel(transaction_sources, function(err, results){
    if (err) {
      callback(err);
      return;
    }

    var transactions = _.uniq(results[0].concat(results[1]), function(tx){
      return tx.hash;
    });

    callback(null, transactions);
  });
}

function filterTransactions(transactions, opts) {

  var filtered = _.filter(transactions, function(transaction){

    if (opts.exclude_failed) {
      if (transaction.state === 'failed' ||
        (transaction.meta && transaction.meta.TransactionResult !== 'tesSUCCESS')) {
        return false;
      }
    }

    if (opts.types && opts.types.length > 0) {
      if (opts.types.indexOf(transaction.TransactionType.toLowerCase()) === -1) {
        return false;
      }
    }

    if (opts.source_account) {
      if (transaction.Account !== opts.source_account) {
        return false;
      }
    }
    if (opts.destination_account) {
      if (transaction.Destination !== opts.destination_account) {
        return false;
      }
    }

    if (opts.direction) {
      if (opts.direction === 'outgoing' && transaction.Account !== opts.account) {
        return false;
      }
      if (opts.direction === 'incoming' && transaction.Destination && transaction.Destination !== opts.account) {
        return false;
      }
    }

    return true;

  });

  return filtered;
}

/**
 *  Order two transactions based on their ledger_index and date
 */
function compareTransactions(a, b, descending) {
  var a_index = a.ledger || a.ledger_index,
    b_index = b.ledger || b.ledger_index,
    a_less_than_b = true;

  if (a_index === b_index) {

    if (a.date <= b.date) {
      a_less_than_b = true;
    } else {
      a_less_than_b = false;
    }

  } else if (a_index < b_index) {
    a_less_than_b = true;
  } else {
    a_less_than_b = false;
  }

  // If the results are meant to be descending, swap this value
  if (descending) {
    a_less_than_b = !a_less_than_b;
  }

  if (a_less_than_b) {
    return -1;
  } else {
    return 1;
  }

}

function getAccountTx(remote, opts, callback) {
  var params = {
    account: opts.account,
    ledger_index_min: opts.ledger_index_min || opts.ledger_index || -1,
    ledger_index_max: opts.ledger_index_max || opts.ledger_index || -1,
    limit: opts.limit || DEFAULT_RESULTS_PER_PAGE,
    forward: (opts.hasOwnProperty('descending') ? !opts.descending : true),
    marker: opts.marker
  };

  remote.requestAccountTx(params, function(err, account_tx_results){
    if (err) {
      callback(err);
      return;
    }

    var transactions = [];

    if (opts.binary) {
      transactions = account_tx_results.transactions;
    } else {

      account_tx_results.transactions.forEach(function(tx_entry){
        if (!tx_entry.validated) {
          return;
        }

        var tx = tx_entry.tx;
        tx.meta = tx_entry.meta;
        transactions.push(tx);
      });

    }

    callback(null, {
      transactions: transactions,
      marker: account_tx_results.marker
    });
  });
}

function countAccountTransactionsInLedger(remote, dbinterface, opts, callback) {
  getAccountTransactions(remote, dbinterface, {
    account: opts.account,
    ledger_index_min: opts.ledger_index,
    ledger_index_max: opts.ledger_index
  }, function(err, transactions){
    if (err) {
      return callback(err);
    }

    callback(null, transactions.length);
  });
}

module.exports.getTransaction                   = getTransaction;
module.exports.getAccountTransactions           = getAccountTransactions;
module.exports.countAccountTransactionsInLedger = countAccountTransactionsInLedger;
<commit_msg>[BUG] Return transaction on getpayment.<commit_after>var async     = require('async');
var _         = require('lodash');
var serverlib = require('./server-lib');
var validator = require('./schema-validator');

var DEFAULT_RESULTS_PER_PAGE = 10;
var NUM_TRANSACTION_TYPES = 5;

function getTransaction(remote, dbinterface, opts, callback) {
  if (opts.identifier) {
    if (validator.isValid(opts.identifier, 'Hash256')) {
      opts.hash = opts.identifier;
    } else if (validator.isValid(opts.identifier, 'ResourceId')) {
      opts.client_resource_id = opts.identifier;
    } else {
      callback(new Error('Invalid Parameter: hash or client_resource_id. Must provide a transaction hash or a client_resource_id to lookup a notification'));
    }
  }

  var steps = [

    function(async_callback) {
      serverlib.ensureConnected(remote, async_callback);
    },

    function(connected, async_callback) {
      dbinterface.getTransaction(opts, function(error, transaction){
        if (!transaction){
          async_callback(null, null);
        } else {
          async_callback(error, transaction);
        };
      });
    },

    function(db_entry, async_callback) {
      if (db_entry && db_entry.transaction) {
        async_callback(null, db_entry.transaction);
      } else if (db_entry) {
        remote.requestTx(db_entry.hash, async_callback);
      } else if (opts.hash) {
        remote.requestTx(opts.hash, async_callback);
      } else {
        async_callback({ remote: { error: 'txnNotFound' } });
      }
    },

    function(transaction, async_callback) {

      console.log(opts.account, transaction);

      if (opts.account) {
        var transaction_string = JSON.stringify(transaction);
        var account_regex = new RegExp(opts.account);
        if (!account_regex.test(transaction_string)) {
          return async_callback(new Error('Transaction unrelated to account. The transaction specified did not affect the given account'));
        }
      }

      async_callback(null, transaction);

    },

    function(transaction, async_callback) {
      if (transaction && opts.client_resource_id) {
        transaction.client_resource_id = opts.client_resource_id;
      }

      async_callback(null, transaction);
    },

    function(transaction, async_callback) {
      if (!transaction || transaction.date || !transaction.ledger_index) {
        return async_callback(null, transaction);
      }

      remote.requestLedger(transaction.ledger_index, function(err, res){
        if (err) {
          return async_callback(err);
        }

        transaction.date = res.ledger.close_time;
        async_callback(null, transaction);
      });
    }

  ];

  async.waterfall(steps, function(err, transaction) {
    if (err) {
      if (err.remote && err.remote.error === 'txnNotFound') {
        callback(new Error('Transaction Not Found. No transaction corresponding to the given hash or client_resource_id was found either in the local ripple-rest database or in the rippled\'s database. This may indicate that the transaction was never or not yet validated and written into the Ripple Ledger, or this error may be seen if either the ripple-rest or rippled database were recently created or deleted. ' + JSON.stringify(err)));
      } else {
        callback(err);
      }
      return;
    }

    callback(null, transaction);
  });
};

/**
 *  Get all failed and validated transactions for the specified account
 *  from the local database as well as the rippled.
 *
 *  opts:
 *    account
 *    source_account
 *    destination_account
 *    ledger_index or ledger_index_min / ledger_index_max
 *    descending
 *    max
 *    min
 *    offset
 *    exclude_failed
 *    types
 */

 // TODO Refactor this code and clean up the logic
function getAccountTransactions(remote, dbinterface, opts, callback, previous_transactions) {
  if (!opts.max) {
    opts.max = DEFAULT_RESULTS_PER_PAGE;
  }
  if (!opts.min) {
    opts.min = DEFAULT_RESULTS_PER_PAGE;
  }

  // Limit will be set if this function is called recursively
  if (!opts.limit) {
    if (opts.types && opts.types.length < NUM_TRANSACTION_TYPES) {
      opts.limit = 2 * Math.max(opts.max, DEFAULT_RESULTS_PER_PAGE);
    } else {
      opts.limit = opts.max;
    }
  }

  var steps = [

    function(async_callback) {
      serverlib.ensureConnected(remote, async_callback);
    },

    // Get transactions from rippled and local db
    function(connected, async_callback) {
      getLocalAndRemoteTransactions(remote, dbinterface, opts, async_callback);
    },

    // Filter results so that they are unique and match the given parameters
    function(transactions, async_callback) {
      var filtered = filterTransactions(transactions, opts);
      async_callback(null, filtered);
    },

    // Sort results
    function(transactions, async_callback) {
      transactions.sort(_.partialRight(compareTransactions, opts.descending));
      async_callback(null, transactions);
    }

  ];

  async.waterfall(steps, function(err, transactions){
    if (err) {
      callback(err);
      return;
    }

    // Combine transactions with previous_transactions from previous
    // recursive call of this function
    if (previous_transactions && previous_transactions.length > 0) {
      transactions = previous_transactions.concat(transactions);
    }

    // Handle offset
    if (opts.offset && opts.offset > 0) {
      var offset_remaining = opts.offset - transactions.length;
      transactions = transactions.slice(opts.offset);
      opts.offset = offset_remaining;
    }

    // Truncate results if there are too many
    if (transactions.length > opts.max) {
      transactions = transactions.slice(0, opts.max);
    }

    // If there are enough transactions, send them back to the client
    // Otherwise recur
    if (!opts.min || transactions.length >= opts.min || !opts.marker) {
      callback(null, transactions);
    } else {
      setImmediate(function(){
        getAccountTransactions(remote, dbinterface, opts, callback, transactions);
      });
    }

  });
}

function getLocalAndRemoteTransactions(remote, dbinterface, opts, callback) {
  var transaction_sources = [

    function(parallel_callback){
      getAccountTx(remote, opts, function(err, results){
        if (err) {
          parallel_callback(err);
          return;
        }

        // Set marker so that when this function is called again
        // recursively it starts from the last place it left off
        opts.marker = results.marker;

        parallel_callback(null, results.transactions);
      });
    },

    function(parallel_callback) {
      if (opts.exclude_failed) {
        parallel_callback(null, []);
      } else {
        dbinterface.getFailedTransactions(opts, parallel_callback);
      }
    }

  ];

  async.parallel(transaction_sources, function(err, results){
    if (err) {
      callback(err);
      return;
    }

    var transactions = _.uniq(results[0].concat(results[1]), function(tx){
      return tx.hash;
    });

    callback(null, transactions);
  });
}

function filterTransactions(transactions, opts) {

  var filtered = _.filter(transactions, function(transaction){

    if (opts.exclude_failed) {
      if (transaction.state === 'failed' ||
        (transaction.meta && transaction.meta.TransactionResult !== 'tesSUCCESS')) {
        return false;
      }
    }

    if (opts.types && opts.types.length > 0) {
      if (opts.types.indexOf(transaction.TransactionType.toLowerCase()) === -1) {
        return false;
      }
    }

    if (opts.source_account) {
      if (transaction.Account !== opts.source_account) {
        return false;
      }
    }
    if (opts.destination_account) {
      if (transaction.Destination !== opts.destination_account) {
        return false;
      }
    }

    if (opts.direction) {
      if (opts.direction === 'outgoing' && transaction.Account !== opts.account) {
        return false;
      }
      if (opts.direction === 'incoming' && transaction.Destination && transaction.Destination !== opts.account) {
        return false;
      }
    }

    return true;

  });

  return filtered;
}

/**
 *  Order two transactions based on their ledger_index and date
 */
function compareTransactions(a, b, descending) {
  var a_index = a.ledger || a.ledger_index,
    b_index = b.ledger || b.ledger_index,
    a_less_than_b = true;

  if (a_index === b_index) {

    if (a.date <= b.date) {
      a_less_than_b = true;
    } else {
      a_less_than_b = false;
    }

  } else if (a_index < b_index) {
    a_less_than_b = true;
  } else {
    a_less_than_b = false;
  }

  // If the results are meant to be descending, swap this value
  if (descending) {
    a_less_than_b = !a_less_than_b;
  }

  if (a_less_than_b) {
    return -1;
  } else {
    return 1;
  }

}

function getAccountTx(remote, opts, callback) {
  var params = {
    account: opts.account,
    ledger_index_min: opts.ledger_index_min || opts.ledger_index || -1,
    ledger_index_max: opts.ledger_index_max || opts.ledger_index || -1,
    limit: opts.limit || DEFAULT_RESULTS_PER_PAGE,
    forward: (opts.hasOwnProperty('descending') ? !opts.descending : true),
    marker: opts.marker
  };

  remote.requestAccountTx(params, function(err, account_tx_results){
    if (err) {
      callback(err);
      return;
    }

    var transactions = [];

    if (opts.binary) {
      transactions = account_tx_results.transactions;
    } else {

      account_tx_results.transactions.forEach(function(tx_entry){
        if (!tx_entry.validated) {
          return;
        }

        var tx = tx_entry.tx;
        tx.meta = tx_entry.meta;
        transactions.push(tx);
      });

    }

    callback(null, {
      transactions: transactions,
      marker: account_tx_results.marker
    });
  });
}

function countAccountTransactionsInLedger(remote, dbinterface, opts, callback) {
  getAccountTransactions(remote, dbinterface, {
    account: opts.account,
    ledger_index_min: opts.ledger_index,
    ledger_index_max: opts.ledger_index
  }, function(err, transactions){
    if (err) {
      return callback(err);
    }

    callback(null, transactions.length);
  });
}

module.exports.getTransaction                   = getTransaction;
module.exports.getAccountTransactions           = getAccountTransactions;
module.exports.countAccountTransactionsInLedger = countAccountTransactionsInLedger;
<|endoftext|>


<commit_before>function click_clear_button() {
  $(".form-control").val("").focus();
}

function droparea_ondragover(event) {
	event.preventDefault();
}

function droparea_ondrop(event) {
	// Prevent the transition of the page
	event.preventDefault();

	// Retrieve the files stored in the event
	var f = event.dataTransfer.files[0];

	// Display the filename
	alert(f.name);
}

$(document).ready(function() {
  $(document).on('click', '.star', function(e) {
    var id = $(this).attr('honyomi-id');
    var page_no = $(this).attr('honyomi-page-no');
    var title = $(this).attr('honyomi-title');

    if (!$(this).hasClass('favorited')) {
      // NEW
      $(this).addClass('favorited');

      var num = parseInt($(".boomark-number").text());
      $(".boomark-number").text(num + 1);

      // ajax
      $.post(
        '/command',
        {
          kind:      'favorite',
          id:        id,
          page_no:   page_no,
          favorited: true
        },
        function(data) {
          // Return a result of POST
        }
      );

    } else {
      // EDIT
      $('#modal-bookmark-label').html(
        "" +
        title + " (P" + page_no + ")"
      );

      $(".modal-body textarea").val($(this).attr('honyomi-comment'));

      $('#modal-bookmark-edit').modal();
    }

    e.preventDefault();
  });

  var updateMemo = function () {
    var id = $(".modal-bookmark-data").attr('honyomi-id');
    var page_no = $(".modal-bookmark-data").attr('honyomi-page-no');
    var comment = $(".modal-body textarea").val();

    $("#star-" + id + "-" + page_no).attr("honyomi-comment", comment);

    $.post(
      '/command',
      {
        kind:      'favorite-update',
        id:        id,
        page_no:   page_no,
        comment:   comment
      },
      function(data) {
        var comment = $("#" + page_no + " .comment");

        if (comment.size() == 0) {
          $('
' + data + '
').insertBefore("#" + page_no + " .main");
        } else {
          comment.html(data);
        }
      }
    );
  }

  $("#modal-bookmark-edit .btn-primary").click(function (e) {
    updateMemo();
  });

  $(".modal-body textarea").keydown(function (e) {
    if (e.ctrlKey && e.keyCode == 13) {
      $('#modal-bookmark-edit').modal('hide');
      updateMemo();
    }
  });

  $("#modal-bookmark-edit .btn-danger").click(function (e) {
    var id = $(".modal-bookmark-data").attr('honyomi-id');
    var page_no = $(".modal-bookmark-data").attr('honyomi-page-no');

    if (confirm("Delete this bookmark?")) {
      $("#star-" + id + "-" + page_no).removeClass('favorited');
      $("#" + page_no + " .comment").remove();

      var num = parseInt($(".boomark-number").text());
      $(".boomark-number").text(num - 1);

      $.post(
        '/command',
        {
          kind:      'favorite',
          id:        id,
          page_no:   page_no,
          favorited: false
        }
      );
    }
  });

  $(".edit-link").click(function (e) {
    $(".title").addClass('hide');
    $(".title-form").removeClass('hide');
    $("#title-form-title").val($("#book-title").text());
    $("#title-form-author").val($("#book-author").text());
    $("#title-form-url").val($("#book-url").text());
  });

  var hideTitleForm = function() {
    $(".title").removeClass('hide');
    $(".title-form").addClass('hide');
  }

  $("#title-form-save").click(function (e) {
    var id = $("#book-title").attr('honyomi-book-id');
    var title = $("#title-form-title").val();
    var author = $("#title-form-author").val();
    var url = $("#title-form-url").val();

    $("#book-title").text(title);
    $("#book-author").text(author);
    $("#book-url").attr('href', url);
    $("#book-url").text(url);

    $.post(
      '/command',
      {
        kind:      'title-form-save',
        id:        id,
        title:     title,
        author:    author,
        url:       url
      },
      function(data) {
        // Return a result of POST
      }
    );

    hideTitleForm();
  });

  $("#title-form-cancel").click(function (e) {
    hideTitleForm();
  });
});<commit_msg>Refactor<commit_after>function click_clear_button() {
  $(".form-control").val("").focus();
}

function droparea_ondragover(e) {
  e.preventDefault();
}

function droparea_ondrop(e) {
  // Prevent the transition of the page
  e.preventDefault();

  // Retrieve the files stored in the e
  var f = e.dataTransfer.files[0];

  // Display the filename
  alert(f.name);
}

$(document).ready(function() {
  $(document).on('click', '.star', function(e) {
    var id = $(this).attr('honyomi-id');
    var page_no = $(this).attr('honyomi-page-no');
    var title = $(this).attr('honyomi-title');

    if (!$(this).hasClass('favorited')) {
      // NEW
      $(this).addClass('favorited');

      var num = parseInt($(".boomark-number").text());
      $(".boomark-number").text(num + 1);

      // ajax
      $.post(
        '/command',
        {
          kind:      'favorite',
          id:        id,
          page_no:   page_no,
          favorited: true
        },
        function(data) {
          // Return a result of POST
        }
      );

    } else {
      // EDIT
      $('#modal-bookmark-label').html(
        "" +
        title + " (P" + page_no + ")"
      );

      $(".modal-body textarea").val($(this).attr('honyomi-comment'));

      $('#modal-bookmark-edit').modal();
    }

    e.preventDefault();
  });

  var updateMemo = function () {
    var id = $(".modal-bookmark-data").attr('honyomi-id');
    var page_no = $(".modal-bookmark-data").attr('honyomi-page-no');
    var comment = $(".modal-body textarea").val();

    $("#star-" + id + "-" + page_no).attr("honyomi-comment", comment);

    $.post(
      '/command',
      {
        kind:      'favorite-update',
        id:        id,
        page_no:   page_no,
        comment:   comment
      },
      function(data) {
        var comment = $("#" + page_no + " .comment");

        if (comment.size() == 0) {
          $('
' + data + '
').insertBefore("#" + page_no + " .main");
        } else {
          comment.html(data);
        }
      }
    );
  }

  $("#modal-bookmark-edit .btn-primary").click(function (e) {
    updateMemo();
  });

  $(".modal-body textarea").keydown(function (e) {
    if (e.ctrlKey && e.keyCode == 13) {
      $('#modal-bookmark-edit').modal('hide');
      updateMemo();
    }
  });

  $("#modal-bookmark-edit .btn-danger").click(function (e) {
    var id = $(".modal-bookmark-data").attr('honyomi-id');
    var page_no = $(".modal-bookmark-data").attr('honyomi-page-no');

    if (confirm("Delete this bookmark?")) {
      $("#star-" + id + "-" + page_no).removeClass('favorited');
      $("#" + page_no + " .comment").remove();

      var num = parseInt($(".boomark-number").text());
      $(".boomark-number").text(num - 1);

      $.post(
        '/command',
        {
          kind:      'favorite',
          id:        id,
          page_no:   page_no,
          favorited: false
        }
      );
    }
  });

  $(".edit-link").click(function (e) {
    $(".title").addClass('hide');
    $(".title-form").removeClass('hide');
    $("#title-form-title").val($("#book-title").text());
    $("#title-form-author").val($("#book-author").text());
    $("#title-form-url").val($("#book-url").text());
  });

  var hideTitleForm = function() {
    $(".title").removeClass('hide');
    $(".title-form").addClass('hide');
  }

  $("#title-form-save").click(function (e) {
    var id = $("#book-title").attr('honyomi-book-id');
    var title = $("#title-form-title").val();
    var author = $("#title-form-author").val();
    var url = $("#title-form-url").val();

    $("#book-title").text(title);
    $("#book-author").text(author);
    $("#book-url").attr('href', url);
    $("#book-url").text(url);

    $.post(
      '/command',
      {
        kind:      'title-form-save',
        id:        id,
        title:     title,
        author:    author,
        url:       url
      },
      function(data) {
        // Return a result of POST
      }
    );

    hideTitleForm();
  });

  $("#title-form-cancel").click(function (e) {
    hideTitleForm();
  });
});<|endoftext|>


<commit_before>/** AutoMarkings Plugin for flot
 * https://github.com/kcdr/flot-autoMarkings/edit/master/README.md
 *
 * Licensed under the MIT license.
 *
 */

(function ($) {
    function init(plot) {

        function autoMarkingsFunction(plot, offset) {
        	$(plot.getData()).each(function(){
        		if (this.autoMarkings && this.autoMarkings.enabled === true) {
                    if( plot.getOptions().grid.markings==null ) plot.getOptions().grid.markings = new Array();

                    if( this.autoMarkings.showMinMax === true || this.autoMarkings.showAvg === true )
                    {
	                    if( this.autoMarkings.min==null || this.autoMarkings.max==null || this.autoMarkings.avg==null ){
	        				var min=Number.MAX_VALUE;
	        				var max=0;
	        				var sum = 0;
	        				var count = 0;
	        				$(this.data).each(function(){
	        					if( this[1] < min) min=this[1];
	        					if( this[1] > max) max=this[1];
	        					count++;
	        					sum += this[1];
	        				});
	        				this.autoMarkings.min = min;
	        				this.autoMarkings.max = max;
	        				this.autoMarkings.avg = sum/count;
	        			}
                    }

                    var seriesColor = this.autoMarkings.color || this.color;

                    var axis = "y"+(this.yaxis.n>1?this.yaxis.n:"")+"axis";

	        		if( this.autoMarkings.showMinMax === true && this.autoMarkings.min!=Number.MAX_VALUE && this.autoMarkings.max!=0 )
	        		{
	        			var marking = { color: seriesColor.replace('rgb(','rgba(').replace(')',','+this.autoMarkings.minMaxAlpha+')') };
	        			marking[axis] = { from: this.autoMarkings.min, to: this.autoMarkings.max };
	        			plot.getOptions().grid.markings.push(marking);
	        		}
	        		if( this.autoMarkings.showAvg === true && this.autoMarkings.avg!=Number.NaN)
	        		{
	        			var marking = { color: seriesColor };
	        			marking[axis] = { from: this.autoMarkings.avg, to: this.autoMarkings.avg };
	        			plot.getOptions().grid.markings.push(marking);
	        		}
        		}
        	});
        }

        plot.hooks.processOffset.push(autoMarkingsFunction);
    }

    var options = {series: { autoMarkings: {enabled: false, minMaxAlpha:0.2}}};

    /** Options
     * enabled
     * color
     * showMinMax
     * minMaxAlpha
     * showAvg
     * min
     * max
     * avg
     */

    $.plot.plugins.push({
        init: init,
        options: options,
        name: "autoMarkings",
        version: "0.1"
    });
})(jQuery);
<commit_msg>Fixes #2 On redraw of a chart the markers where not reset. So the markers stacked above each other. This seemed as if the transparency was lowered with each redraw.<commit_after>/** AutoMarkings Plugin for flot
 * https://github.com/kcdr/flot-autoMarkings/edit/master/README.md
 *
 * Licensed under the MIT license.
 *
 * Version v0.2.1.dev
 *
 */

(function ($) {
    function init(plot) {

        function autoMarkingsFunction(plot, offset) {
        	plot.getOptions().grid.markings = new Array();
        	$(plot.getData()).each(function(){
        		if (this.autoMarkings && this.autoMarkings.enabled === true) {
                    if( plot.getOptions().grid.markings==null ) plot.getOptions().grid.markings = new Array();

                    if( this.autoMarkings.showMinMax === true || this.autoMarkings.showAvg === true )
                    {
	                    if( this.autoMarkings.min==null || this.autoMarkings.max==null || this.autoMarkings.avg==null ){
	        				var min=Number.MAX_VALUE;
	        				var max=0;
	        				var sum = 0;
	        				var count = 0;
	        				$(this.data).each(function(){
	        					if( this[1] < min) min=this[1];
	        					if( this[1] > max) max=this[1];
	        					count++;
	        					sum += this[1];
	        				});
	        				this.autoMarkings.min = min;
	        				this.autoMarkings.max = max;
	        				this.autoMarkings.avg = sum/count;
	        			}
                    }

                    var seriesColor = this.autoMarkings.color || this.color;

                    var axis = "y"+(this.yaxis.n>1?this.yaxis.n:"")+"axis";

	        		if( this.autoMarkings.showMinMax === true && this.autoMarkings.min!=Number.MAX_VALUE && this.autoMarkings.max!=0 )
	        		{
	        			var marking = { color: seriesColor.replace('rgb(','rgba(').replace(')',','+this.autoMarkings.minMaxAlpha+')') };
	        			marking[axis] = { from: this.autoMarkings.min, to: this.autoMarkings.max };
	        			plot.getOptions().grid.markings.push(marking);
	        		}
	        		if( this.autoMarkings.showAvg === true && this.autoMarkings.avg!=Number.NaN)
	        		{
	        			var marking = { color: seriesColor };
	        			marking[axis] = { from: this.autoMarkings.avg, to: this.autoMarkings.avg };
	        			plot.getOptions().grid.markings.push(marking);
	        		}
        		}
        	});
        }

        plot.hooks.processOffset.push(autoMarkingsFunction);
    }

    var options = {series: { autoMarkings: {enabled: false, minMaxAlpha:0.2}}};

    /** Options
     * enabled
     * color
     * showMinMax
     * minMaxAlpha
     * showAvg
     * min
     * max
     * avg
     */

    $.plot.plugins.push({
        init: init,
        options: options,
        name: "autoMarkings",
        version: "0.1"
    });
})(jQuery);
<|endoftext|>