<commit_before>/**
Copyright (c) 2010 Dennis Hotson

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

(function() {

jQuery.fn.springy = function(params) {
	var graph = this.graph = params.graph || new Springy.Graph();
	var nodeFont = "12px Verdana, sans-serif";
	var edgeFont = "8px Verdana, sans-serif";
	var stiffness = params.stiffness || 400.0;
	var repulsion = params.repulsion || 400.0;
	var damping = params.damping || 0.5;
	var nodeSelected = params.nodeSelected || null;

	var canvas = this[0];
	var ctx = canvas.getContext("2d");

	var layout = this.layout = new Springy.Layout.ForceDirected(graph, stiffness, repulsion, damping);

	// calculate bounding box of graph layout.. with ease-in
	var currentBB = layout.getBoundingBox();
	var targetBB = {bottomleft: new Springy.Vector(-2, -2), topright: new Springy.Vector(2, 2)};

	// auto adjusting bounding box
	Springy.requestAnimationFrame(function adjust() {
		targetBB = layout.getBoundingBox();
		// current gets 20% closer to target every iteration
		currentBB = {
			bottomleft: currentBB.bottomleft.add( targetBB.bottomleft.subtract(currentBB.bottomleft)
				.divide(10)),
			topright: currentBB.topright.add( targetBB.topright.subtract(currentBB.topright)
				.divide(10))
		};

		Springy.requestAnimationFrame(adjust);
	});

	// convert to/from screen coordinates
	var toScreen = function(p) {
		var size = currentBB.topright.subtract(currentBB.bottomleft);
		var sx = p.subtract(currentBB.bottomleft).divide(size.x).x * canvas.width;
		var sy = p.subtract(currentBB.bottomleft).divide(size.y).y * canvas.height;
		return new Springy.Vector(sx, sy);
	};

	var fromScreen = function(s) {
		var size = currentBB.topright.subtract(currentBB.bottomleft);
		var px = (s.x / canvas.width) * size.x + currentBB.bottomleft.x;
		var py = (s.y / canvas.height) * size.y + currentBB.bottomleft.y;
		return new Springy.Vector(px, py);
	};

	// half-assed drag and drop
	var selected = null;
	var nearest = null;
	var dragged = null;

	jQuery(canvas).mousedown(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		selected = nearest = dragged = layout.nearest(p);

		if (selected.node !== null) {
			dragged.point.m = 10000.0;

			if (nodeSelected) {
				nodeSelected(selected.node);
			}
		}

		renderer.start();
	});

	// Basic double click handler
	jQuery(canvas).dblclick(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		selected = layout.nearest(p);
		node = selected.node;
		if (node && node.data && node.data.ondoubleclick) {
			node.data.ondoubleclick();
		}
	});

	jQuery(canvas).mousemove(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		nearest = layout.nearest(p);

		if (dragged !== null && dragged.node !== null) {
			dragged.point.p.x = p.x;
			dragged.point.p.y = p.y;
		}

		renderer.start();
	});

	jQuery(window).bind('mouseup',function(e) {
		dragged = null;
	});

	Springy.Node.prototype.getWidth = function() {
		var text = (this.data.label !== undefined) ? this.data.label : this.id;
		if (this._width && this._width[text])
			return this._width[text];

		ctx.save();
		ctx.font = (this.data.font !== undefined) ? this.data.font : nodeFont;
		var width = ctx.measureText(text).width + 10;
		ctx.restore();

		this._width || (this._width = {});
		this._width[text] = width;

		return width;
	};

	Springy.Node.prototype.getHeight = function() {
		return 20;
	};

	var renderer = this.renderer = new Springy.Renderer(layout,
		function clear() {
			ctx.clearRect(0,0,canvas.width,canvas.height);
		},
		function drawEdge(edge, p1, p2) {
			var x1 = toScreen(p1).x;
			var y1 = toScreen(p1).y;
			var x2 = toScreen(p2).x;
			var y2 = toScreen(p2).y;

			var direction = new Springy.Vector(x2-x1, y2-y1);
			var normal = direction.normal().normalise();

			var from = graph.getEdges(edge.source, edge.target);
			var to = graph.getEdges(edge.target, edge.source);

			var total = from.length + to.length;

			// Figure out edge's position in relation to other edges between the same nodes
			var n = 0;
			for (var i=0; i<from.length; i++) {
				if (from[i].id === edge.id) {
					n = i;
				}
			}

			//change default to  10.0 to allow text fit between edges
			var spacing = 10.0; //was 6.0 before

			// Figure out how far off center the line should be drawn
			var offset = normal.multiply(-((total - 1) * spacing)/2.0 + (n * spacing));

			var s1 = toScreen(p1).add(offset);
			var s2 = toScreen(p2).add(offset);

			var boxWidth = edge.target.getWidth();
			var boxHeight = edge.target.getHeight();

			var intersection = intersect_line_box(s1, s2, {x: x2-boxWidth/2.0, y: y2-boxHeight/2.0}, boxWidth, boxHeight);

			if (!intersection) {
				intersection = s2;
			}

			var stroke = (edge.data.color !== undefined) ? edge.data.color : '#000000';

			var arrowWidth;
			var arrowLength;

			var weight = (edge.data.weight !== undefined) ? edge.data.weight : 1.0;

			ctx.lineWidth = Math.max(weight *  2, 0.1);
			arrowWidth = 1 + ctx.lineWidth;
			arrowLength = 8;

			var directional = (edge.data.directional !== undefined) ? edge.data.directional : true;

			// line
			var lineEnd;
			if (directional) {
				lineEnd = intersection.subtract(direction.normalise().multiply(arrowLength * 0.5));
			} else {
				lineEnd = s2;
			}

			ctx.strokeStyle = stroke;
			ctx.beginPath();
			ctx.moveTo(s1.x, s1.y);
			ctx.lineTo(lineEnd.x, lineEnd.y);
			ctx.stroke();

			// arrow
			if (directional) {
				ctx.save();
				ctx.fillStyle = stroke;
				ctx.translate(intersection.x, intersection.y);
				ctx.rotate(Math.atan2(y2 - y1, x2 - x1));
				ctx.beginPath();
				ctx.moveTo(-arrowLength, arrowWidth);
				ctx.lineTo(0, 0);
				ctx.lineTo(-arrowLength, -arrowWidth);
				ctx.lineTo(-arrowLength * 0.8, -0);
				ctx.closePath();
				ctx.fill();
				ctx.restore();
			}

			// label
			if (edge.data.label !== undefined) {
				text = edge.data.label
				ctx.save();
				ctx.textAlign = "center";
				ctx.textBaseline = "top";
				ctx.font = (edge.data.font !== undefined) ? edge.data.font : edgeFont;
				ctx.fillStyle = "#5BA6EC";
				var xText = (s1.x+s2.x)/2;
				var yText = (s1.y+s2.y)/2;
				ctx.translate(xText, yText);
				ctx.rotate(getTextAngle(s1.x,s1.y,s2.x,s2.y));
				ctx.fillText(text, 0,-2);
				ctx.restore();
			}

		},
		function drawNode(node, p) {
			var s = toScreen(p);

			ctx.save();

			var boxWidth = node.getWidth();
			var boxHeight = node.getHeight();

			// clear background
			ctx.clearRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20);

			// fill background
			if (selected !== null && selected.node !== null && selected.node.id === node.id) {
				ctx.fillStyle = "#FFFFE0";
			} else if (nearest !== null && nearest.node !== null && nearest.node.id === node.id) {
				ctx.fillStyle = "#EEEEEE";
			} else {
				ctx.fillStyle = "#FFFFFF";
			}
			ctx.fillRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20);

			ctx.textAlign = "left";
			ctx.textBaseline = "top";
			ctx.font = (node.data.font !== undefined) ? node.data.font : nodeFont;
			ctx.fillStyle = "#000000";
			ctx.font = "16px Verdana, sans-serif";
			var text = (node.data.label !== undefined) ? node.data.label : node.id;
			ctx.fillText(text, s.x - boxWidth/2 + 5, s.y - 8);

			ctx.restore();
		}
	);

	renderer.start();

	// return angle of text in radian
	function getTextAngle(x1, y1, x2, y2){
		var tan = (y2-y1)/(x2-x1);
		return Math.atan(tan);
	}

	// helpers for figuring out where to draw arrows
	function intersect_line_line(p1, p2, p3, p4) {
		var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y));

		// lines are parallel
		if (denom === 0) {
			return false;
		}

		var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom;
		var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom;

		if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
			return false;
		}

		return new Springy.Vector(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
	}

	function intersect_line_box(p1, p2, p3, w, h) {
		var tl = {x: p3.x, y: p3.y};
		var tr = {x: p3.x + w, y: p3.y};
		var bl = {x: p3.x, y: p3.y + h};
		var br = {x: p3.x + w, y: p3.y + h};

		var result;
		if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top
		if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right
		if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom
		if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left

		return false;
	}

	return this;
}

})();
<commit_msg>Improved calculation of text angle and spacing<commit_after>/**
Copyright (c) 2010 Dennis Hotson

 Permission is hereby granted, free of charge, to any person
 obtaining a copy of this software and associated documentation
 files (the "Software"), to deal in the Software without
 restriction, including without limitation the rights to use,
 copy, modify, merge, publish, distribute, sublicense, and/or sell
 copies of the Software, and to permit persons to whom the
 Software is furnished to do so, subject to the following
 conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 OTHER DEALINGS IN THE SOFTWARE.
*/

(function() {

jQuery.fn.springy = function(params) {
	var graph = this.graph = params.graph || new Springy.Graph();
	var nodeFont = "12px Verdana, sans-serif";
	var edgeFont = "8px Verdana, sans-serif";
	var stiffness = params.stiffness || 400.0;
	var repulsion = params.repulsion || 400.0;
	var damping = params.damping || 0.5;
	var nodeSelected = params.nodeSelected || null;

	var canvas = this[0];
	var ctx = canvas.getContext("2d");

	var layout = this.layout = new Springy.Layout.ForceDirected(graph, stiffness, repulsion, damping);

	// calculate bounding box of graph layout.. with ease-in
	var currentBB = layout.getBoundingBox();
	var targetBB = {bottomleft: new Springy.Vector(-2, -2), topright: new Springy.Vector(2, 2)};

	// auto adjusting bounding box
	Springy.requestAnimationFrame(function adjust() {
		targetBB = layout.getBoundingBox();
		// current gets 20% closer to target every iteration
		currentBB = {
			bottomleft: currentBB.bottomleft.add( targetBB.bottomleft.subtract(currentBB.bottomleft)
				.divide(10)),
			topright: currentBB.topright.add( targetBB.topright.subtract(currentBB.topright)
				.divide(10))
		};

		Springy.requestAnimationFrame(adjust);
	});

	// convert to/from screen coordinates
	var toScreen = function(p) {
		var size = currentBB.topright.subtract(currentBB.bottomleft);
		var sx = p.subtract(currentBB.bottomleft).divide(size.x).x * canvas.width;
		var sy = p.subtract(currentBB.bottomleft).divide(size.y).y * canvas.height;
		return new Springy.Vector(sx, sy);
	};

	var fromScreen = function(s) {
		var size = currentBB.topright.subtract(currentBB.bottomleft);
		var px = (s.x / canvas.width) * size.x + currentBB.bottomleft.x;
		var py = (s.y / canvas.height) * size.y + currentBB.bottomleft.y;
		return new Springy.Vector(px, py);
	};

	// half-assed drag and drop
	var selected = null;
	var nearest = null;
	var dragged = null;

	jQuery(canvas).mousedown(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		selected = nearest = dragged = layout.nearest(p);

		if (selected.node !== null) {
			dragged.point.m = 10000.0;

			if (nodeSelected) {
				nodeSelected(selected.node);
			}
		}

		renderer.start();
	});

	// Basic double click handler
	jQuery(canvas).dblclick(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		selected = layout.nearest(p);
		node = selected.node;
		if (node && node.data && node.data.ondoubleclick) {
			node.data.ondoubleclick();
		}
	});

	jQuery(canvas).mousemove(function(e) {
		var pos = jQuery(this).offset();
		var p = fromScreen({x: e.pageX - pos.left, y: e.pageY - pos.top});
		nearest = layout.nearest(p);

		if (dragged !== null && dragged.node !== null) {
			dragged.point.p.x = p.x;
			dragged.point.p.y = p.y;
		}

		renderer.start();
	});

	jQuery(window).bind('mouseup',function(e) {
		dragged = null;
	});

	Springy.Node.prototype.getWidth = function() {
		var text = (this.data.label !== undefined) ? this.data.label : this.id;
		if (this._width && this._width[text])
			return this._width[text];

		ctx.save();
		ctx.font = (this.data.font !== undefined) ? this.data.font : nodeFont;
		var width = ctx.measureText(text).width + 10;
		ctx.restore();

		this._width || (this._width = {});
		this._width[text] = width;

		return width;
	};

	Springy.Node.prototype.getHeight = function() {
		return 20;
	};

	var renderer = this.renderer = new Springy.Renderer(layout,
		function clear() {
			ctx.clearRect(0,0,canvas.width,canvas.height);
		},
		function drawEdge(edge, p1, p2) {
			var x1 = toScreen(p1).x;
			var y1 = toScreen(p1).y;
			var x2 = toScreen(p2).x;
			var y2 = toScreen(p2).y;

			var direction = new Springy.Vector(x2-x1, y2-y1);
			var normal = direction.normal().normalise();

			var from = graph.getEdges(edge.source, edge.target);
			var to = graph.getEdges(edge.target, edge.source);

			var total = from.length + to.length;

			// Figure out edge's position in relation to other edges between the same nodes
			var n = 0;
			for (var i=0; i<from.length; i++) {
				if (from[i].id === edge.id) {
					n = i;
				}
			}

			//change default to  10.0 to allow text fit between edges
			var spacing = 12.0;

			// Figure out how far off center the line should be drawn
			var offset = normal.multiply(-((total - 1) * spacing)/2.0 + (n * spacing));

			var s1 = toScreen(p1).add(offset);
			var s2 = toScreen(p2).add(offset);

			var boxWidth = edge.target.getWidth();
			var boxHeight = edge.target.getHeight();

			var intersection = intersect_line_box(s1, s2, {x: x2-boxWidth/2.0, y: y2-boxHeight/2.0}, boxWidth, boxHeight);

			if (!intersection) {
				intersection = s2;
			}

			var stroke = (edge.data.color !== undefined) ? edge.data.color : '#000000';

			var arrowWidth;
			var arrowLength;

			var weight = (edge.data.weight !== undefined) ? edge.data.weight : 1.0;

			ctx.lineWidth = Math.max(weight *  2, 0.1);
			arrowWidth = 1 + ctx.lineWidth;
			arrowLength = 8;

			var directional = (edge.data.directional !== undefined) ? edge.data.directional : true;

			// line
			var lineEnd;
			if (directional) {
				lineEnd = intersection.subtract(direction.normalise().multiply(arrowLength * 0.5));
			} else {
				lineEnd = s2;
			}

			ctx.strokeStyle = stroke;
			ctx.beginPath();
			ctx.moveTo(s1.x, s1.y);
			ctx.lineTo(lineEnd.x, lineEnd.y);
			ctx.stroke();

			// arrow
			if (directional) {
				ctx.save();
				ctx.fillStyle = stroke;
				ctx.translate(intersection.x, intersection.y);
				ctx.rotate(Math.atan2(y2 - y1, x2 - x1));
				ctx.beginPath();
				ctx.moveTo(-arrowLength, arrowWidth);
				ctx.lineTo(0, 0);
				ctx.lineTo(-arrowLength, -arrowWidth);
				ctx.lineTo(-arrowLength * 0.8, -0);
				ctx.closePath();
				ctx.fill();
				ctx.restore();
			}

			// label
			if (edge.data.label !== undefined) {
				text = edge.data.label
				ctx.save();
				ctx.textAlign = "center";
				ctx.textBaseline = "top";
				ctx.font = (edge.data.font !== undefined) ? edge.data.font : edgeFont;
				ctx.fillStyle = stroke;
				var textPos = s1.add(s2).divide(2).add(normal.multiply(-8));
				ctx.translate(textPos.x, textPos.y);
				ctx.rotate(Math.atan2(s2.y - s1.y, s2.x - s1.x));
				ctx.fillText(text, 0,-2);
				ctx.restore();
			}

		},
		function drawNode(node, p) {
			var s = toScreen(p);

			ctx.save();

			var boxWidth = node.getWidth();
			var boxHeight = node.getHeight();

			// clear background
			ctx.clearRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20);

			// fill background
			if (selected !== null && selected.node !== null && selected.node.id === node.id) {
				ctx.fillStyle = "#FFFFE0";
			} else if (nearest !== null && nearest.node !== null && nearest.node.id === node.id) {
				ctx.fillStyle = "#EEEEEE";
			} else {
				ctx.fillStyle = "#FFFFFF";
			}
			ctx.fillRect(s.x - boxWidth/2, s.y - 10, boxWidth, 20);

			ctx.textAlign = "left";
			ctx.textBaseline = "top";
			ctx.font = (node.data.font !== undefined) ? node.data.font : nodeFont;
			ctx.fillStyle = "#000000";
			ctx.font = "16px Verdana, sans-serif";
			var text = (node.data.label !== undefined) ? node.data.label : node.id;
			ctx.fillText(text, s.x - boxWidth/2 + 5, s.y - 8);

			ctx.restore();
		}
	);

	renderer.start();

	// helpers for figuring out where to draw arrows
	function intersect_line_line(p1, p2, p3, p4) {
		var denom = ((p4.y - p3.y)*(p2.x - p1.x) - (p4.x - p3.x)*(p2.y - p1.y));

		// lines are parallel
		if (denom === 0) {
			return false;
		}

		var ua = ((p4.x - p3.x)*(p1.y - p3.y) - (p4.y - p3.y)*(p1.x - p3.x)) / denom;
		var ub = ((p2.x - p1.x)*(p1.y - p3.y) - (p2.y - p1.y)*(p1.x - p3.x)) / denom;

		if (ua < 0 || ua > 1 || ub < 0 || ub > 1) {
			return false;
		}

		return new Springy.Vector(p1.x + ua * (p2.x - p1.x), p1.y + ua * (p2.y - p1.y));
	}

	function intersect_line_box(p1, p2, p3, w, h) {
		var tl = {x: p3.x, y: p3.y};
		var tr = {x: p3.x + w, y: p3.y};
		var bl = {x: p3.x, y: p3.y + h};
		var br = {x: p3.x + w, y: p3.y + h};

		var result;
		if (result = intersect_line_line(p1, p2, tl, tr)) { return result; } // top
		if (result = intersect_line_line(p1, p2, tr, br)) { return result; } // right
		if (result = intersect_line_line(p1, p2, br, bl)) { return result; } // bottom
		if (result = intersect_line_line(p1, p2, bl, tl)) { return result; } // left

		return false;
	}

	return this;
}

})();
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 Ethan White. All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//BigInt.js from http://www.leemon.com/crypto/BigInt.html
var keyCache = {};
var prevChatLength = 0;
var _firstChatGet = true;
$("#chat-message").keyup(function(e) {
    if (e.keyCode == 13) {
        var thiz = this;
        var doNext = function(publickey) {
            publickey = rsa.unstringifyPublicKey(publickey);
            //Get a new AES key
            var key = aes.getKey();
            //Encrypt it with RSA
            var encryptedKey = rsa.encryptAESKey(key, publickey);
            //Encrypt the message with AES
            var encrypted = aes.encrypt(thiz.value, key);
            var chat = firebase.child("users").child(encodeUsername(activeUser)).child("chat").child(username);
            chat.child(new Date().getTime() + "").set({
                msg: encrypted,
                from: username,
                key: encryptedKey
            });
            thiz.value = "";
        }
        if(typeof(keyCache[activeUser]) == "undefined") {
            //Get their public key
            firebase.child("users").child(encodeUsername(activeUser)).child("rsa").once("value", function(snapshot) {
                var val = snapshot.val();
                keyCache[activeUser] = val;
                doNext(val);
            });
        } else {
            doNext(keyCache[activeUser]);
        }
    }
});
var handle = function(snapshot) {
    try {
        $("#chat-messages").empty();
        var val = snapshot.val();
        var length = 0;
        for(var key in val) {
            length++;
            //For now, we're not worrying about any RSA stuff; that comes later.
            var entry = val[key];
            var from = entry["from"];
            var msgEnc = entry["msg"];
            //Decrypt the message
            var key = rsa.decryptAESKey(entry["key"]);
            var msg = aes.decrypt(msgEnc, key);
            $("#chat-messages").append($("<div>")
                .addClass("chat-message")
                .append($("<span>")
                    .addClass("chat-message-sender")
                    .html(decodeUsername(from)))
                .append($("<span>")
                    .addClass("chat-message-content")
                    .html(msg)))
        }
        console.log("-----");
        console.log(!_firstChatGet);
        console.log(document.hidden);
        console.log(prevChatLength != length);
        console.log("-----");
        if(!_firstChatGet && document.hidden && prevChatLength != length) {
            messageAudio.play();
            showNotification("Message", "From " + activeUser);
        } else if(_firstChatGet) {
            _firstChatGet = false;
        }
        prevChatLength = length;
    } catch(e) {
        console.log(e);
    }
}
var handlerFb = null;
function updateChat() {
    //#chat-messages
    //.chat-message
    if(handlerFb != null) {
        handlerFb.off("value", handle);
    }
    handlerFb = firebase.child("users").child(username).child("chat").child(encodeUsername(activeUser));
    handlerFb.on("value", handle);
}<commit_msg>You can now see messages you've sent<commit_after>/**
 * Copyright (c) 2015 Ethan White. All rights reserved.
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
 *   1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
 *   2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation
 *      and/or other materials provided with the distribution.
 *   3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
//BigInt.js from http://www.leemon.com/crypto/BigInt.html
var keyCache = {};
var prevChatLength = 0;
var _firstChatGet = true;
$("#chat-message").keyup(function(e) {
    if (e.keyCode == 13) {
        var thiz = this;
        var doNext = function(publickey) {
            publickey = rsa.unstringifyPublicKey(publickey);
            //Get a new AES key
            var key = aes.getKey();
            //Encrypt it with RSA
            var encryptedKey = rsa.encryptAESKey(key, publickey);
            //Encrypt the message with AES
            var encrypted = aes.encrypt(thiz.value, key);
            var chat = firebase.child("users").child(encodeUsername(activeUser)).child("chat").child(username);
            chat.child(new Date().getTime() + "").set({
                msg: encrypted,
                from: username,
                key: encryptedKey
            });
            thiz.value = "";
        }
        if(typeof(keyCache[activeUser]) == "undefined") {
            //Get their public key
            firebase.child("users").child(encodeUsername(activeUser)).child("rsa").once("value", function(snapshot) {
                var val = snapshot.val();
                keyCache[activeUser] = val;
                doNext(val);
            });
        } else {
            doNext(keyCache[activeUser]);
        }
        (function() {
            var key = aes.getKey();
            //Encrypt it with RSA
            var encryptedKey = rsa.encryptAESKey(key, rsa.keypair);
            //Encrypt the message with AES
            var encrypted = aes.encrypt(thiz.value, key);
            var chat = firebase.child("users").child(username).child("chat").child(encodeUsername(activeUser));
            chat.child(new Date().getTime() + "").set({
                msg: encrypted,
                from: username,
                key: encryptedKey
            });
        })();
    }
});
var handle = function(snapshot) {
    try {
        $("#chat-messages").empty();
        var val = snapshot.val();
        var length = 0;
        for(var key in val) {
            length++;
            //For now, we're not worrying about any RSA stuff; that comes later.
            var entry = val[key];
            var from = entry["from"];
            var msgEnc = entry["msg"];
            //Decrypt the message
            var key = rsa.decryptAESKey(entry["key"]);
            var msg = aes.decrypt(msgEnc, key);
            $("#chat-messages").append($("<div>")
                .addClass("chat-message")
                .append($("<span>")
                    .addClass("chat-message-sender")
                    .html(decodeUsername(from)))
                .append($("<span>")
                    .addClass("chat-message-content")
                    .html(msg)))
        }
        console.log("-----");
        console.log(!_firstChatGet);
        console.log(document.hidden);
        console.log(prevChatLength != length);
        console.log("-----");
        if(!_firstChatGet && document.hidden && prevChatLength != length) {
            messageAudio.play();
            showNotification("Message", "From " + activeUser);
        } else if(_firstChatGet) {
            _firstChatGet = false;
        }
        prevChatLength = length;
    } catch(e) {
        console.log(e);
    }
}
var handlerFb = null;
function updateChat() {
    //#chat-messages
    //.chat-message
    if(handlerFb != null) {
        handlerFb.off("value", handle);
    }
    handlerFb = firebase.child("users").child(username).child("chat").child(encodeUsername(activeUser));
    handlerFb.on("value", handle);
}<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("./HarmonyImportDependency");

class HarmonyAcceptImportDependency extends HarmonyImportDependency {
	constructor(request, originModule, parserScope) {
		super(request, originModule, NaN, parserScope);
		this.weak = true;
	}

	get type() {
		return "harmony accept";
	}
}

HarmonyAcceptImportDependency.Template = class HarmonyAcceptImportDependencyTemplate
	extends HarmonyImportDependency.Template {
		apply(dep, source, runtime) {}
	};

module.exports = HarmonyAcceptImportDependency;
<commit_msg>Fix formatting<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";
const HarmonyImportDependency = require("./HarmonyImportDependency");

class HarmonyAcceptImportDependency extends HarmonyImportDependency {
	constructor(request, originModule, parserScope) {
		super(request, originModule, NaN, parserScope);
		this.weak = true;
	}

	get type() {
		return "harmony accept";
	}
}

HarmonyAcceptImportDependency.Template = class HarmonyAcceptImportDependencyTemplate extends HarmonyImportDependency.Template {
	apply(dep, source, runtime) {}
};

module.exports = HarmonyAcceptImportDependency;
<|endoftext|>


<commit_before>export const db = process.env.MONGOHQ_URL || process.env.MONGODB_URI || 'mongodb://localhost/ReactWebpackNode';

export default {
  db
};
<commit_msg>adding authentication to db<commit_after>require('dotenv').config();

export const db = process.env.MONGOHQ_URL || process.env.MONGODB_URI || `mongodb://${process.env.DB_USER}:${process.env.DB_PASS}@localhost/ReactWebpackNode`;

export default {
  db
};
<|endoftext|>


<commit_before>String.prototype.substitute = function () {
  let count = 0;
  let args = arguments;
  return this.replace(/%var%/g, () => args[count++]);
};
<commit_msg>Added toTitleCase string prototype<commit_after>String.prototype.substitute = function () {
  let count = 0;
  let args = arguments;
  return this.replace(/%var%/g, () => args[count++]);
};

String.prototype.toTitleCase = function() {
  let newstr = this.split(' ');
  for (let i = 0; i < newstr.length; i++) {
    if (newstr[i] === '') continue;
    let copy = newstr[i].substring(1).toLowerCase();
    newstr[i] = newstr[i][0].toUpperCase() + copy;
  }
  newstr = newstr.join(' ');
  return newstr;
};
<|endoftext|>


<commit_before>/**
 * @module node-serialosc
 * @author Tom Dinchak <dinchak@gmail.com>
 */

var _ = require('underscore');
var OscEmitter = require('osc-emitter');
var OscReceiver = require('osc-receiver');
var EventEmitter = require('events').EventEmitter;

/**
 * A Device represents either an arc or a grid connected to serialosc
 *
 * The following properties can be passed as options or will be provided
 * by serialosc as the result of a /sys/info call.
 *
 * host       - the hostname to listen on (default: localhost)
 * port       - the port to listen on (default: random between 1024-65535)
 * id         - The id of the device (ie. m128-302, provided by serialosc)
 * type       - The type of device, grid or arc
 * model      - The model of device (ie. monome 128, provided by serialosc)
 * deviceHost - the hostname the device is listening on (ie. localhost, provided by serialosc)
 * devicePort - the port the device is listening on (ie. 38717, provided by serialosc)
 * sizeX      - the width of a grid device (ie. 16)
 * sizeY      - the height of a grid device (ie. 8)
 * encoders   - the number of encoders for an arc device (ie. 4)
 * prefix     - the prefix of the device (ie. /monome)
 * connected  - true after device is ready to use
 *
 * @constructor
 */
function Device() {}
/**
 * Extend the EventEmitter prototype
 * Provides .on(), .emit(), etc.
 */
Device.prototype = Object.create(EventEmitter.prototype);

/**
 * Configure device
 * @param  {Object} opts [description]
 */
Device.prototype.config = function (opts) {
  // choose a random port if none is provided
  this.port = opts.port || Math.floor(Math.random() * (65536 - 1024)) + 1024;

  this.oscReceiver = new OscReceiver();
  this.oscEmitter = new OscEmitter();

  this.on = this.oscReceiver.on;

  this.connected = false;

  // set all keys passed in opts
  for (var key in opts) {
    this[key] = opts[key];
  }
};

/**
 * Begin listening on a host/port to respond to device messages
 * Setup listeners for /sys messages
 * Set the device's port and hostname
 * Send /sys/info to get device information
 */
Device.prototype.start = function () {
  var initMsgs = [
    '/sys/host',
    '/sys/port',
    '/sys/prefix',
    '/sys/rotation',
    '/sys/size',
  ];

  // listen on host/port to respond to device messages
  this.oscReceiver.bind(this.port, this.host);
  var self = this;

  function receiveInitMsg(msg) {
    if (self.connected) {
      return;
    }
    initMsgs = _.without(initMsgs, msg);
    if (initMsgs.length === 0) {
      self.connected = true;
      self.emit('initialized');
    }
  }

  // handle basic /sys messages by setting device properties

  this.oscReceiver.on('/sys/id', function () {
    self.id = arguments[0];
  });

  this.oscReceiver.on('/sys/size', function () {
    self.sizeX = arguments[0];
    self.sizeY = arguments[1];
    receiveInitMsg('/sys/size');
  });

  this.oscReceiver.on('/sys/rotation', function () {
    self.rotation = arguments[0];
    receiveInitMsg('/sys/rotation');
  });

  this.oscReceiver.on('/sys/connect', function () {
    self.connected = true;
    self.emit('connected');
  });

  this.oscReceiver.on('/sys/disconnect', function () {
    self.connected = false;
    self.emit('disconnected');
  });

  // device initialization is handled through these responders
  // we wait for a response from /sys/port, /sys/host, and /sys/rotation
  // before broadcasting the connected event

  this.oscReceiver.on('/sys/port', function () {
    self.port = arguments[0];
    receiveInitMsg('/sys/port');
  });

  this.oscReceiver.on('/sys/host', function () {
    self.host = arguments[0];
    receiveInitMsg('/sys/host');
  });

  this.oscReceiver.on('/sys/prefix', function () {
    // remove existing listeners for old prefix
    self.removeListeners();
    self.prefix = arguments[0];
    // add new listeners for new prefix
    self.addListeners();
    receiveInitMsg('/sys/prefix');
  });

  // add deviceHost and devicePort to the broadcast list
  this.oscEmitter.add(this.deviceHost, this.devicePort);

  // initialize device
  this.oscEmitter.emit(
    '/sys/port',
    {
      type: 'integer',
      value: this.port
    }
  );
  this.oscEmitter.emit(
    '/sys/host',
    {
      type: 'string',
      value: self.host
    }
  );
  this.oscEmitter.emit('/sys/info');
};

/**
 * Called when the device is unplugged
 */
Device.prototype.stop = function () {
  this.connected = false;
};

/**
 * Sets a device's rotation
 * @param  {Number} r new rotation value (0, 90, 180, 270)
 */
Device.prototype.setRotation = function (r) {
  this.oscEmitter.emit(
    '/sys/rotation',
    {
      type: 'integer',
      value: r
    }
  );
};

module.exports = Device;
<commit_msg>jsdoc update<commit_after>/**
 * @module node-serialosc
 * @author Tom Dinchak <dinchak@gmail.com>
 */

var _ = require('underscore');
var OscEmitter = require('osc-emitter');
var OscReceiver = require('osc-receiver');
var EventEmitter = require('events').EventEmitter;

/**
 * A Device represents either an arc or a grid connected to serialosc
 *
 * The following properties can be passed as options or will be provided
 * by serialosc as the result of a /sys/info call.
 *
 * host       - the hostname to listen on (default: localhost)
 * port       - the port to listen on (default: random between 1024-65535)
 * id         - The id of the device (ie. m128-302, provided by serialosc)
 * type       - The type of device, grid or arc
 * model      - The model of device (ie. monome 128, provided by serialosc)
 * deviceHost - the hostname the device is listening on (ie. localhost, provided by serialosc)
 * devicePort - the port the device is listening on (ie. 38717, provided by serialosc)
 * sizeX      - the width of a grid device (ie. 16)
 * sizeY      - the height of a grid device (ie. 8)
 * encoders   - the number of encoders for an arc device (ie. 4)
 * prefix     - the prefix of the device (ie. /monome)
 * connected  - true after device is ready to use
 *
 * @constructor
 */
function Device() {}
/**
 * Extend the EventEmitter prototype
 * Provides .on(), .emit(), etc.
 */
Device.prototype = Object.create(EventEmitter.prototype);

/**
 * Configure device
 * @param  {Object} opts configuration options
 */
Device.prototype.config = function (opts) {
  // choose a random port if none is provided
  this.port = opts.port || Math.floor(Math.random() * (65536 - 1024)) + 1024;

  this.oscReceiver = new OscReceiver();
  this.oscEmitter = new OscEmitter();

  this.on = this.oscReceiver.on;

  this.connected = false;

  // set all keys passed in opts
  for (var key in opts) {
    this[key] = opts[key];
  }
};

/**
 * Begin listening on a host/port to respond to device messages
 * Setup listeners for /sys messages
 * Set the device's port and hostname
 * Send /sys/info to get device information
 */
Device.prototype.start = function () {
  var initMsgs = [
    '/sys/host',
    '/sys/port',
    '/sys/prefix',
    '/sys/rotation',
    '/sys/size',
  ];

  // listen on host/port to respond to device messages
  this.oscReceiver.bind(this.port, this.host);
  var self = this;

  function receiveInitMsg(msg) {
    if (self.connected) {
      return;
    }
    initMsgs = _.without(initMsgs, msg);
    if (initMsgs.length === 0) {
      self.connected = true;
      self.emit('initialized');
    }
  }

  // handle basic /sys messages by setting device properties

  this.oscReceiver.on('/sys/id', function () {
    self.id = arguments[0];
  });

  this.oscReceiver.on('/sys/size', function () {
    self.sizeX = arguments[0];
    self.sizeY = arguments[1];
    receiveInitMsg('/sys/size');
  });

  this.oscReceiver.on('/sys/rotation', function () {
    self.rotation = arguments[0];
    receiveInitMsg('/sys/rotation');
  });

  this.oscReceiver.on('/sys/connect', function () {
    self.connected = true;
    self.emit('connected');
  });

  this.oscReceiver.on('/sys/disconnect', function () {
    self.connected = false;
    self.emit('disconnected');
  });

  // device initialization is handled through these responders
  // we wait for a response from /sys/port, /sys/host, and /sys/rotation
  // before broadcasting the connected event

  this.oscReceiver.on('/sys/port', function () {
    self.port = arguments[0];
    receiveInitMsg('/sys/port');
  });

  this.oscReceiver.on('/sys/host', function () {
    self.host = arguments[0];
    receiveInitMsg('/sys/host');
  });

  this.oscReceiver.on('/sys/prefix', function () {
    // remove existing listeners for old prefix
    self.removeListeners();
    self.prefix = arguments[0];
    // add new listeners for new prefix
    self.addListeners();
    receiveInitMsg('/sys/prefix');
  });

  // add deviceHost and devicePort to the broadcast list
  this.oscEmitter.add(this.deviceHost, this.devicePort);

  // initialize device
  this.oscEmitter.emit(
    '/sys/port',
    {
      type: 'integer',
      value: this.port
    }
  );
  this.oscEmitter.emit(
    '/sys/host',
    {
      type: 'string',
      value: self.host
    }
  );
  this.oscEmitter.emit('/sys/info');
};

/**
 * Called when the device is unplugged
 */
Device.prototype.stop = function () {
  this.connected = false;
};

/**
 * Sets a device's rotation
 * @param  {Number} r new rotation value (0, 90, 180, 270)
 */
Device.prototype.setRotation = function (r) {
  this.oscEmitter.emit(
    '/sys/rotation',
    {
      type: 'integer',
      value: r
    }
  );
};

module.exports = Device;
<|endoftext|>


<commit_before>/*
Copyright (c) 2016 CATTHINGS: Nicholas McHale, Andrew McCann, Susmita Awasthi,
Manpreet Bahl, Austen Ruzicka, Luke Kazmierowicz, Hillman Chen

See LICENSE.txt for full information.
*/
module.exports = {

  /****************************************************
  * /path     a/admin/stats/weeklyavg/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Returns average weekly consumption of item
  ****************************************************/
  weeklyavg: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id)

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        // If you want to change this rolling window that is used to calculate the average,
        // just alter the INTERVAL amount per SQL guidelines.
        client.query("SELECT weekly.item_name, weekly.item_id, AVG(sum) AS weekly_avg FROM (SELECT i.item_name, i.item_id, SUM(ABS(t.qty_changed)) FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_name, i.item_id, date_trunc('week', timestamp)) AS weekly GROUP BY 1,2", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/threshold/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Returns the rough day count between most recent
  *           checkin, and threshold.
  *
  * /author   Andrew McCann
  * /date     2/26/2017
  ****************************************************/
  threshold: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id)

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }
        client.query("WITH last_checkin AS (SELECT MAX(timestamp) FROM transactions AS t2 WHERE t2.qty_changed > 0 AND t2.item_id = $1) SELECT i.item_name, t.item_id, EXTRACT(DAY FROM age(t.timestamp, (SELECT max from last_checkin))) AS days_til_threshold FROM transactions AS t, items AS i WHERE t.item_id = $1 AND i.item_id = t.item_id AND t.qty_remaining < i.threshold AND t.timestamp > (SELECT max from last_checkin)", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },

  /****************************************************
  * /path     a/admin/stats/avgperday/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Gets the average usage (aka checkout/day) by
  *           day of the week.
  ****************************************************/
  avg: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
           console.error('Error fetching client from pool', err);
           res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id);

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        // OLD QUERY: SELECT i.item_name, i.item_id, ABS(SUM(t.qty_changed)) AS checkout_per_day, to_char(timestamp, 'day') AS dow FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_id, i.item_name, dow

        client.query("SELECT * FROM checkout_per_day WHERE item_id = $1", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/avgperday/
  * /params   None
  *
  *
  * /brief    Route to pull satistics for all items
  *           Returns the net change in qty/day for
  *           the past week
  ****************************************************/
  avg_All: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
           console.error('Error fetching client from pool', err);
           res.sendStatus(500);
        }

        // OLD QUERY: SELECT i.item_name, i.item_id, ABS(SUM(t.qty_changed)) AS checkout_per_day, to_char(timestamp, 'day') AS dow FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_id, i.item_name, dow

        client.query("SELECT item_id, item_name, ROUND(AVG(checkout_per_day),2) FROM checkout_per_day NATURAL JOIN items GROUP BY item_id", function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/netperday/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Route to pull satistics for an item
  *           Returns the net change in qty/day for
  *           the past week
  ****************************************************/
  item_id: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id);
        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        client.query("SELECT i.item_name, i.item_id, SUM(t.qty_changed) AS Net_Change, to_char(timestamp, 'day') AS dow FROM (SELECT * FROM transactions WHERE timestamp > current_date - interval '7 days') AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id GROUP BY i.item_name, i.item_id, dow", [id], function(err, result) {
            done();

            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  }

}
<commit_msg>Some more SQL<commit_after>/*
Copyright (c) 2016 CATTHINGS: Nicholas McHale, Andrew McCann, Susmita Awasthi,
Manpreet Bahl, Austen Ruzicka, Luke Kazmierowicz, Hillman Chen

See LICENSE.txt for full information.
*/
module.exports = {

  /****************************************************
  * /path     a/admin/stats/weeklyavg/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Returns average weekly consumption of item
  ****************************************************/
  weeklyavg: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id)

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        // If you want to change this rolling window that is used to calculate the average,
        // just alter the INTERVAL amount per SQL guidelines.
        client.query("SELECT weekly.item_name, weekly.item_id, AVG(sum) AS weekly_avg FROM (SELECT i.item_name, i.item_id, SUM(ABS(t.qty_changed)) FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_name, i.item_id, date_trunc('week', timestamp)) AS weekly GROUP BY 1,2", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/threshold/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Returns the rough day count between most recent
  *           checkin, and threshold.
  *
  * /author   Andrew McCann
  * /date     2/26/2017
  ****************************************************/
  threshold: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id)

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }
        client.query("WITH last_checkin AS (SELECT MAX(timestamp) FROM transactions AS t2 WHERE t2.qty_changed > 0 AND t2.item_id = $1) SELECT i.item_name, t.item_id, EXTRACT(DAY FROM age(t.timestamp, (SELECT max from last_checkin))) AS days_til_threshold FROM transactions AS t, items AS i WHERE t.item_id = $1 AND i.item_id = t.item_id AND t.qty_remaining < i.threshold AND t.timestamp > (SELECT max from last_checkin)", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },

  /****************************************************
  * /path     a/admin/stats/avgperday/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Gets the average usage (aka checkout/day) by
  *           day of the week.
  ****************************************************/
  avg: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
           console.error('Error fetching client from pool', err);
           res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id);

        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        // OLD QUERY: SELECT i.item_name, i.item_id, ABS(SUM(t.qty_changed)) AS checkout_per_day, to_char(timestamp, 'day') AS dow FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_id, i.item_name, dow

        client.query("SELECT * FROM checkout_per_day WHERE item_id = $1", [id], function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/avgperday/
  * /params   None
  *
  *
  * /brief    Route to pull satistics for all items
  *           Returns the net change in qty/day for
  *           the past week
  ****************************************************/
  avg_All: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
           console.error('Error fetching client from pool', err);
           res.sendStatus(500);
        }

        // OLD QUERY: SELECT i.item_name, i.item_id, ABS(SUM(t.qty_changed)) AS checkout_per_day, to_char(timestamp, 'day') AS dow FROM transactions AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id AND t.qty_changed < 0 AND t.timestamp > (current_timestamp - INTERVAL '3 months') GROUP BY i.item_id, i.item_name, dow

        client.query("SELECT item_id, ROUND(AVG(checkout_per_day),2) FROM checkout_per_day", function(err, result) {
            done();
            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  },
  /****************************************************
  * /path     a/admin/stats/netperday/:item_id
  * /params   :item_id - item_id of what we are looking for
  *
  *
  * /brief    Route to pull satistics for an item
  *           Returns the net change in qty/day for
  *           the past week
  ****************************************************/
  item_id: (req, res) => {
    res.app.locals.pool.connect(function(err, client, done) {
        if(err) {
            console.error('Error fetching client from pool', err);
            res.sendStatus(500);
        }
        // Quickly validate if id is an int
        var id = parseInt(req.params.item_id);
        if(req.params.item_id != id) {
            done();
            console.error('Invalid ID number', err);
            res.sendStatus(400);
        }

        client.query("SELECT i.item_name, i.item_id, SUM(t.qty_changed) AS Net_Change, to_char(timestamp, 'day') AS dow FROM (SELECT * FROM transactions WHERE timestamp > current_date - interval '7 days') AS t, items AS i WHERE t.item_id = $1 AND t.item_id = i.item_id GROUP BY i.item_name, i.item_id, dow", [id], function(err, result) {
            done();

            res.app.locals.helpers.errResultHandler(err, result.rows, res);
        });
    });
  }

}
<|endoftext|>


<commit_before>asynctest(
  'Browser Test: .ui.SerialisedLinkTest',

  [
    'ephox.agar.api.ApproxStructure',
    'ephox.agar.api.Assertions',
    'ephox.agar.api.Chain',
    'ephox.agar.api.FocusTools',
    'ephox.agar.api.GeneralSteps',
    'ephox.agar.api.Keyboard',
    'ephox.agar.api.Keys',
    'ephox.agar.api.Logger',
    'ephox.agar.api.Mouse',
    'ephox.agar.api.Pipeline',
    'ephox.agar.api.Step',
    'ephox.agar.api.UiControls',
    'ephox.agar.api.UiFinder',
    'ephox.agar.api.Waiter',
    'ephox.alloy.api.system.Attachment',
    'ephox.alloy.log.AlloyLogger',
    'ephox.alloy.test.GuiSetup',
    'ephox.alloy.test.TestStore',
    'ephox.katamari.api.Cell',
    'ephox.katamari.api.Fun',
    'ephox.katamari.api.Result',
    'ephox.sugar.api.dom.Focus',
    'ephox.sugar.api.node.Body',
    'ephox.sugar.api.node.Element',
    'ephox.sugar.api.properties.Attr',
    'ephox.sugar.api.properties.Css',
    'ephox.sugar.api.properties.Html',
    'ephox.sugar.api.properties.TextContent',
    'ephox.sugar.api.search.Traverse',
    'tinymce.themes.mobile.ui.IosRealm',
    'tinymce.themes.mobile.ui.LinkButton'
  ],

  function (
    ApproxStructure, Assertions, Chain, FocusTools, GeneralSteps, Keyboard, Keys, Logger, Mouse, Pipeline, Step, UiControls, UiFinder, Waiter, Attachment, AlloyLogger,
    GuiSetup, TestStore, Cell, Fun, Result, Focus, Body, Element, Attr, Css, Html, TextContent, Traverse, IosRealm, LinkButton
  ) {
    var success = arguments[arguments.length - 2];
    var failure = arguments[arguments.length - 1];

    var realm = IosRealm();

    var body = Body.body();
    Attachment.attachSystem(body, realm.system());

    var doc = Traverse.owner(body);

    var text = Element.fromText('just-text');
    var link = Element.fromHtml('Link');

    var styles = document.createElement('link');
    styles.setAttribute('rel', 'Stylesheet');
    styles.setAttribute('href', '/project/src/themes/mobile/src/main/css/mobile.css');
    styles.setAttribute('type', 'text/css');
    document.head.appendChild(styles);

    var store = TestStore();

    var editorState = {
      start: Cell(null),
      content: Cell('')
    };

    var editor = {
      selection: {
        getStart: editorState.start.get,
        getContent: editorState.content.get
      },
      insertContent: function (data) {
        store.adder({ method: 'insertContent', data: data })();
      },
      dom: {
        createHTML: function (tag, attributes, innerText) {
          return { tag: tag, attributes: attributes, innerText: innerText };
        },
        encode: Fun.identity
      },
      focus: Fun.noop
    };

    realm.setToolbarGroups([
      {
        label: 'group1',
        items: [
          LinkButton.sketch(realm, editor)
        ]
      }
    ]);

    var sPrepareState = function (node, content) {
      return Step.sync(function () {
        editorState.start.set(node);
        editorState.content.set(content);
      });
    };

    var root = realm.system().getByDom(realm.element()).getOrDie();

    var sTriggerEvent = function (event, selector, data) {
      return Chain.asStep({ }, [
        Chain.inject(realm.element()),
        UiFinder.cFindIn(selector),
        Chain.op(function (target) {
          root.getSystem().triggerEvent(event, target, data(target));
        })
      ]);
    };

    var sAssertNavigation = function (label, prevEnabled, nextEnabled) {
      return Logger.t(
        label,
        Step.sync(function () {
          var active = Focus.active().getOrDie();
          // The buttons are next and previous siblings
          var prev = Traverse.prevSibling(active).getOrDie('Could not find button to left');
          var next = Traverse.nextSibling(active).getOrDie('Could not find button to right');

          var assertNavButton = function (buttonLabel, expected, button) {
            Assertions.assertStructure(
              'Checking ' + buttonLabel + ' button should be enabled = ' + expected,
              ApproxStructure.build(function (s, str, arr) {
                return s.element('span', {
                  attr: {
                    role: str.is('button')
                  },
                  classes: [
                    (expected ? arr.not : arr.has)('tinymce-mobile-toolbar-navigation-disabled')
                  ]
                });
              }),
              button
            );
          };

          assertNavButton('previous', prevEnabled, prev);
          assertNavButton('next', nextEnabled, next);
        })
      );
    };

    var cGetFocused = Chain.binder(function () {
      return Focus.active().fold(function () {
        return Result.error('Could not find focused element');
      }, Result.value);
    });

    var cGetParent = Chain.binder(function (elem) {
      return Traverse.parent(elem).fold(function () {
        return Result.error('Could not find parent of ' + AlloyLogger.element(elem));
      }, Result.value);
    });

    var sClickNavigation = function (selector) {
      return Chain.asStep({ }, [
        cGetFocused,
        cGetParent,
        UiFinder.cFindIn(selector),
        Mouse.cClick
      ]);
    };

    var sClickPrev = sClickNavigation('.tinymce-mobile-toolbar-previous');
    var sClickNext = sClickNavigation('.tinymce-mobile-toolbar-next');

    var sAssertUrlFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true)
    ]);

    var sAssertTextFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link text', doc, 'input[placeholder="Link text"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);

    var sAssertTitleFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link title', doc, 'input[placeholder="Link title"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);

    var sAssertTargetFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link target', doc, 'input[placeholder="Link target"]'),
      sAssertNavigation('Checking navigation for link target', true, false)
    ]);

    var sTestNavigation = GeneralSteps.sequence([
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTextFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: true }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: false }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),

      Step.wait(1000),
      Logger.t('Checking pressing tab at the end should not move focus', sAssertTargetFocused),

      sClickPrev,
      sAssertTitleFocused,
      sClickNext,
      sAssertTargetFocused,
      sClickPrev,
      sAssertTitleFocused,
      sClickPrev,
      sAssertTextFocused,
      sClickPrev,
      sAssertUrlFocused
    ]);

    var sClickLink = Mouse.sClickOn(realm.element(), '.tinymce-mobile-toolbar-button-link');

    var sSetFieldValue = function (value) {
      return Chain.asStep({ }, [
        cGetFocused,
        UiControls.cSetValue(value)
      ]);
    };

    Pipeline.async({}, [
      GuiSetup.mAddStyles(doc, [
        '.tinymce-mobile-toolbar-button-link:before { content: "LINK"; background: black; color: white; }'
      ]),

      Waiter.sTryUntil(
        'Waiting until CSS has loaded',
        Chain.asStep(realm.element(), [
          UiFinder.cFindIn('.tinymce-mobile-toolstrip'),
          Chain.op(function (toolstrip) {
            Assertions.assertEq('Checking toolstrip is flex', 'flex', Css.get(toolstrip, 'display'));
          })
        ]),
        100,
        8000
      ),

      sPrepareState(text, 'link-text'),

      // sTriggerEvent
      sClickLink,

      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true),

      // sTestNavigation,
      Step.sync(function () {
        realm.restoreToolbar();
      }),

      sPrepareState(text, ''),
      sClickLink,
      sAssertUrlFocused,
      sSetFieldValue('http://fake-url'),
      Keyboard.sKeydown(doc, Keys.enter(), { }),
      store.sAssertEq('Pressing enter after just setting URL', [{
        method: 'insertContent',
        data: {
          tag: 'a',
          attributes: {
            href: 'http://fake-url'
          },
          innerText: 'http://fake-url'
        }
      }]),
      store.sClear,

      sClickLink,
      sAssertUrlFocused,
      sSetFieldValue('http://fake-url-2'),
      sClickNext,
      sAssertTextFocused,
      sSetFieldValue('My Link Text 2'),
      Keyboard.sKeydown(doc, Keys.enter(), { }),
      store.sAssertEq('Pressing enter after just setting URL and text', [{
        method: 'insertContent',
        data: {
          tag: 'a',
          attributes: {
            href: 'http://fake-url-2'
          },
          innerText: 'My Link Text 2'
        }
      }]),
      store.sClear,

      sClickLink,
      sAssertUrlFocused,
      sSetFieldValue('http://fake-url-3'),
      sClickNext,
      sAssertTextFocused,
      sSetFieldValue('My Link Text 3'),
      sClickNext,
      sAssertTitleFocused,
      sSetFieldValue('Title-3'),
      Step.debugging,
      Keyboard.sKeydown(doc, Keys.enter(), { }),
      store.sAssertEq('Pressing enter after setting URL, text, and title', [{
        method: 'insertContent',
        data: {
          tag: 'a',
          attributes: {
            href: 'http://fake-url-3',
            title: 'Title-3'
          },
          innerText: 'My Link Text 3'
        }
      }]),

      // sTestEnterOnUrl
      function () { }
    ], function () { document.head.removeChild(styles); success(); }, failure);
  }
);
<commit_msg>TM-43: streamlining tests<commit_after>asynctest(
  'Browser Test: .ui.SerialisedLinkTest',

  [
    'ephox.agar.api.ApproxStructure',
    'ephox.agar.api.Assertions',
    'ephox.agar.api.Chain',
    'ephox.agar.api.FocusTools',
    'ephox.agar.api.GeneralSteps',
    'ephox.agar.api.Keyboard',
    'ephox.agar.api.Keys',
    'ephox.agar.api.Logger',
    'ephox.agar.api.Mouse',
    'ephox.agar.api.Pipeline',
    'ephox.agar.api.Step',
    'ephox.agar.api.UiControls',
    'ephox.agar.api.UiFinder',
    'ephox.agar.api.Waiter',
    'ephox.alloy.api.system.Attachment',
    'ephox.alloy.log.AlloyLogger',
    'ephox.alloy.test.GuiSetup',
    'ephox.alloy.test.TestStore',
    'ephox.boulder.api.FieldSchema',
    'ephox.boulder.api.ValueSchema',
    'ephox.katamari.api.Cell',
    'ephox.katamari.api.Fun',
    'ephox.katamari.api.Result',
    'ephox.sugar.api.dom.Focus',
    'ephox.sugar.api.node.Body',
    'ephox.sugar.api.node.Element',
    'ephox.sugar.api.properties.Attr',
    'ephox.sugar.api.properties.Css',
    'ephox.sugar.api.properties.Html',
    'ephox.sugar.api.properties.TextContent',
    'ephox.sugar.api.search.Traverse',
    'tinymce.themes.mobile.ui.IosRealm',
    'tinymce.themes.mobile.ui.LinkButton'
  ],

  function (
    ApproxStructure, Assertions, Chain, FocusTools, GeneralSteps, Keyboard, Keys, Logger, Mouse, Pipeline, Step, UiControls, UiFinder, Waiter, Attachment, AlloyLogger,
    GuiSetup, TestStore, FieldSchema, ValueSchema, Cell, Fun, Result, Focus, Body, Element, Attr, Css, Html, TextContent, Traverse, IosRealm, LinkButton
  ) {
    var success = arguments[arguments.length - 2];
    var failure = arguments[arguments.length - 1];

    var realm = IosRealm();

    var body = Body.body();
    Attachment.attachSystem(body, realm.system());

    var doc = Traverse.owner(body);

    var text = Element.fromText('just-text');
    var link = Element.fromHtml('Link');

    var styles = document.createElement('link');
    styles.setAttribute('rel', 'Stylesheet');
    styles.setAttribute('href', '/project/src/themes/mobile/src/main/css/mobile.css');
    styles.setAttribute('type', 'text/css');
    document.head.appendChild(styles);

    var store = TestStore();

    var editorState = {
      start: Cell(null),
      content: Cell('')
    };

    var editor = {
      selection: {
        getStart: editorState.start.get,
        getContent: editorState.content.get
      },
      insertContent: function (data) {
        store.adder({ method: 'insertContent', data: data })();
      },
      dom: {
        createHTML: function (tag, attributes, innerText) {
          return { tag: tag, attributes: attributes, innerText: innerText };
        },
        encode: Fun.identity
      },
      focus: Fun.noop
    };

    realm.setToolbarGroups([
      {
        label: 'group1',
        items: [
          LinkButton.sketch(realm, editor)
        ]
      }
    ]);

    var sPrepareState = function (node, content) {
      return Step.sync(function () {
        editorState.start.set(node);
        editorState.content.set(content);
      });
    };

    var root = realm.system().getByDom(realm.element()).getOrDie();

    var sTriggerEvent = function (event, selector, data) {
      return Chain.asStep({ }, [
        Chain.inject(realm.element()),
        UiFinder.cFindIn(selector),
        Chain.op(function (target) {
          root.getSystem().triggerEvent(event, target, data(target));
        })
      ]);
    };

    var sAssertNavigation = function (label, prevEnabled, nextEnabled) {
      return Logger.t(
        label,
        Step.sync(function () {
          var active = Focus.active().getOrDie();
          // The buttons are next and previous siblings
          var prev = Traverse.prevSibling(active).getOrDie('Could not find button to left');
          var next = Traverse.nextSibling(active).getOrDie('Could not find button to right');

          var assertNavButton = function (buttonLabel, expected, button) {
            Assertions.assertStructure(
              'Checking ' + buttonLabel + ' button should be enabled = ' + expected,
              ApproxStructure.build(function (s, str, arr) {
                return s.element('span', {
                  attr: {
                    role: str.is('button')
                  },
                  classes: [
                    (expected ? arr.not : arr.has)('tinymce-mobile-toolbar-navigation-disabled')
                  ]
                });
              }),
              button
            );
          };

          assertNavButton('previous', prevEnabled, prev);
          assertNavButton('next', nextEnabled, next);
        })
      );
    };

    var cGetFocused = Chain.binder(function () {
      return Focus.active().fold(function () {
        return Result.error('Could not find focused element');
      }, Result.value);
    });

    var cGetParent = Chain.binder(function (elem) {
      return Traverse.parent(elem).fold(function () {
        return Result.error('Could not find parent of ' + AlloyLogger.element(elem));
      }, Result.value);
    });

    var sClickNavigation = function (selector) {
      return Chain.asStep({ }, [
        cGetFocused,
        cGetParent,
        UiFinder.cFindIn(selector),
        Mouse.cClick
      ]);
    };

    var sClickPrev = sClickNavigation('.tinymce-mobile-toolbar-previous');
    var sClickNext = sClickNavigation('.tinymce-mobile-toolbar-next');

    var sAssertUrlFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true)
    ]);

    var sAssertTextFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link text', doc, 'input[placeholder="Link text"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);

    var sAssertTitleFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link title', doc, 'input[placeholder="Link title"]'),
      sAssertNavigation('Checking navigation for link text', true, true)
    ]);

    var sAssertTargetFocused = GeneralSteps.sequence([
      FocusTools.sTryOnSelector('Focus should be on input with link target', doc, 'input[placeholder="Link target"]'),
      sAssertNavigation('Checking navigation for link target', true, false)
    ]);

    var sTestNavigation = GeneralSteps.sequence([
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTextFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: true }),
      sAssertTitleFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { shift: false }),
      sAssertTargetFocused,
      Keyboard.sKeydown(doc, Keys.tab(), { }),

      Step.wait(1000),
      Logger.t('Checking pressing tab at the end should not move focus', sAssertTargetFocused),

      sClickPrev,
      sAssertTitleFocused,
      sClickNext,
      sAssertTargetFocused,
      sClickPrev,
      sAssertTitleFocused,
      sClickPrev,
      sAssertTextFocused,
      sClickPrev,
      sAssertUrlFocused
    ]);

    var sClickLink = Mouse.sClickOn(realm.element(), '.tinymce-mobile-toolbar-button-link');

    var sSetFieldValue = function (value) {
      return Chain.asStep({ }, [
        cGetFocused,
        UiControls.cSetValue(value)
      ]);
    };

    var sSetFieldOptValue = function (optVal) {
      return optVal.fold(function () {
        return Step.pass;
      }, sSetFieldValue);
    };

    var sTestScenario = function (rawScenario) {
      var scenario = ValueSchema.asRawOrDie('Checking scenario', ValueSchema.objOf([
        FieldSchema.strict('label'),
        FieldSchema.defaulted('content', ''),
        FieldSchema.strict('node'),
        FieldSchema.strictObjOf('fields', [
          FieldSchema.option('url'),
          FieldSchema.option('text'),
          FieldSchema.option('title'),
          FieldSchema.option('target')
        ]),
        FieldSchema.strict('expected')
      ]), rawScenario);

      return Logger.t(
        scenario.label,
        GeneralSteps.sequence([
          sPrepareState(scenario.node, scenario.content),
          sClickLink,
          sSetFieldOptValue(scenario.fields.url),
          sClickNext,
          sAssertTextFocused,
          sSetFieldOptValue(scenario.fields.text),
          sClickNext,
          sAssertTitleFocused,
          sSetFieldOptValue(scenario.fields.title),
          sClickNext,
          sAssertTargetFocused,
          sSetFieldOptValue(scenario.fields.target),
          // sClickPrev,
          // sAssertTitleFocused,
          // sClickPrev,
          // sAssertTextFocused,
          // sClickPrev,
          // sAssertUrlFocused,
          Keyboard.sKeydown(doc, Keys.enter(), { }),
          store.sAssertEq('Checking insert content', scenario.expected(scenario.node)),
          store.sClear
        ])
      );
    };

    Pipeline.async({}, [
      GuiSetup.mAddStyles(doc, [
        '.tinymce-mobile-toolbar-button-link:before { content: "LINK"; background: black; color: white; }'
      ]),

      Waiter.sTryUntil(
        'Waiting until CSS has loaded',
        Chain.asStep(realm.element(), [
          UiFinder.cFindIn('.tinymce-mobile-toolstrip'),
          Chain.op(function (toolstrip) {
            Assertions.assertEq('Checking toolstrip is flex', 'flex', Css.get(toolstrip, 'display'));
          })
        ]),
        100,
        8000
      ),

      sPrepareState(text, 'link-text'),

      // sTriggerEvent
      sClickLink,

      FocusTools.sTryOnSelector('Focus should be on input with link URL', doc, 'input[placeholder="Type or paste URL"]'),
      sAssertNavigation('Checking initial navigation on text node', false, true),

      // sTestNavigation,
      Step.sync(function () {
        realm.restoreToolbar();
      }),

      sTestScenario({
        label: 'Testing hitting ENTER after just setting URL',
        node: text,
        fields: {
          url: 'http://fake-url'
        },
        expected: Fun.constant([
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url'
              },
              innerText: 'http://fake-url'
            }
          }
        ])
      }),

      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL and text',
        node: text,
        fields: {
          url: 'http://fake-url-2',
          text: 'LinkText-2'
        },
        expected: Fun.constant([
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url-2'
              },
              innerText: 'LinkText-2'
            }
          }
        ])
      }),

      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL and title (not text)',
        node: text,
        fields: {
          url: 'http://fake-url-3',
          title: 'Title-3'
        },
        expected: Fun.constant([
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url-3',
                title: 'Title-3'
              },
              innerText: 'http://fake-url-3'
            }
          }
        ])
      }),

      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL, text, and title',
        node: text,
        fields: {
          url: 'http://fake-url-4',
          text: 'LinkText-4',
          title: 'Title-4'
        },
        expected: Fun.constant([
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url-4',
                title: 'Title-4'
              },
              innerText: 'LinkText-4'
            }
          }
        ])
      }),

      sTestScenario({
        label: 'Testing hitting ENTER after filling in URL, text, title, and target',
        node: text,
        fields: {
          url: 'http://fake-url-5',
          text: 'LinkText-5',
          title: 'Title-5',
          target: 'Target-5'
        },
        expected: Fun.constant([
          {
            method: 'insertContent',
            data: {
              tag: 'a',
              attributes: {
                href: 'http://fake-url-5',
                title: 'Title-5',
                target: 'Target-5'
              },
              innerText: 'LinkText-5'
            }
          }
        ])
      }),

      // sTestEnterOnUrl
      function () { }
    ], function () { document.head.removeChild(styles); success(); }, failure);
  }
);
<|endoftext|>


<commit_before>var Habitat = Habitat || {};

jQuery(document).ready(function () {
    Habitat.AppDeeplink.DomReady();
});

Habitat.AppDeeplink = {
    DomReady: function () {
        Habitat.AppDeeplink.Init();
    },
    Init: function () {

        if (jQuery("body").hasClass("pagemode-edit"))
            return;

        var deeplinkData = Habitat.AppDeeplink.GetDeepLinkData();

        if (deeplinkData == null ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.storeUrl) ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.deviceUrl))
            return;

        Habitat.AppDeeplink.OpenApp(deeplinkData);

    },
    OpenApp: function (deepLinkData) {

        document.location.href = deepLinkData.deviceUrl;
        console.log("Opening URL: " + deepLinkData.deviceUrl);

        Habitat.AppDeeplink.OpenStoreTimeOut(deeplinkData);

    },
    OpenStoreTimeOut: function (deeplinkData) {

        setTimeout(function () {
            Habitat.AppDeeplink.OpenStore(deeplinkData);
        }, 400);
    },
    OpenStore: function (deepLinkData) {

        console.log("Timeout. Open fallback url: " + deepLinkData.storeUrl);
        document.location.href = deepLinkData.storeUrl;

    },
    GetDeepLinkData: function () {

        var DeepLinkData = {};
        DeepLinkData.storeUrl = jQuery(".AppDeeplink").data("storeurl");
        DeepLinkData.deviceUrl = jQuery(".AppDeeplink").data("deviceurl");

        return DeepLinkData;

    },
    IsNullOrEmpty: function(value) {
        return value == null || value === "";
    }

}

<commit_msg>Refactoring in js file<commit_after>var Habitat = Habitat || {};

jQuery(document).ready(function () {
    Habitat.AppDeeplink.DomReady();
});

Habitat.AppDeeplink = {
    DomReady: function () {
        Habitat.AppDeeplink.Init();
    },
    Init: function () {

        if (jQuery("body").hasClass("pagemode-edit"))
            return;

        var deeplinkData = Habitat.AppDeeplink.GetDeeplinkData("AppDeeplink");

        if (deeplinkData == null ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.storeUrl) ||
            Habitat.AppDeeplink.IsNullOrEmpty(deeplinkData.deviceUrl))
            return;

        Habitat.AppDeeplink.OpenApp(deeplinkData);

    },
    OpenApp: function (deepLinkData) {

        document.location.href = deepLinkData.deviceUrl;
        console.log("Opening URL: " + deepLinkData.deviceUrl);

        Habitat.AppDeeplink.OpenStoreTimeOut(deeplinkData);

    },
    OpenStoreTimeOut: function (deeplinkData) {

        setTimeout(function () {
            Habitat.AppDeeplink.OpenStore(deeplinkData);
        }, 400);
    },
    OpenStore: function (deepLinkData) {

        console.log("Timeout. Open fallback url: " + deepLinkData.storeUrl);
        document.location.href = deepLinkData.storeUrl;

    },
    GetDeeplinkData: function (className) {

        var deeplinkData = {};
        deeplinkData.storeUrl = jQuery("." + className).data("storeurl");
        deeplinkData.deviceUrl = jQuery("." + className).data("deviceurl");

        return deeplinkData;

    },
    IsNullOrEmpty: function(value) {
        return value == null || value === "";
    }

}

<|endoftext|>


<commit_before>module.exports = render;
render.raw = function (html) {
  return new RawHtml(html);
};

var CLASS_MATCH = /\.[^.#]+/g,
    ID_MATCH = /#[^.#]+/,
    TAG_MATCH = /^[^.#]+/;

function stripFirst(part) {
  return part.substr(1);
}

function render(value) {
  if (value instanceof RawHtml) return value.html;
  if (typeof value === "string") return escapeHtml(value);
  if (!Array.isArray(value)) {
    throw new Error("Value must be array, string, or raw html");
  }
  var tag = "div", attributes = {};
  var id, classes;
  var first = value[0];
  if (typeof value[0] === "string") {
    value = value.slice(1);
    var match = first.match(TAG_MATCH);
    if (match) tag = match[0];
    match = first.match(CLASS_MATCH);
    if (match) classes = match.map(stripFirst).join(' ');
    match = first.match(ID_MATCH);
    if (match) id = match[0].substring(1);
  }
  else if (typeof first === "function") {
    return render(first.apply(null, value.slice(1)));
  }
  else if (Array.isArray(first)) {
    return value.map(render).join("");
  }
  var next = value[0];
  if (next && typeof next.constructor === Object) {
    attributes = next;
    value = value.slice(1);
  }
  if (id) attributes.id = id;
  if (classes) {
    attributes.class = attributes.class ?
      attributes.class + " " + classes :
      classes;
  }
  var html = "<" + tag;
  var keys = Object.keys(attributes);
  for (var i = 0, l = keys.length; i < l; ++i) {
    var key = keys[i];
    var attribute = attributes[key];
    if (attribute === true) {
      html += " " + key;
    }
    else {
      html += " " + key + '="' + escapeHtml(attribute, true) + '"';
    }
  }
  html += ">";
  if (voidElements[tag]) {
    if (value.length) {
      throw new Error("No body allowed in " + tag);
    }
    return html;
  }
  html += value.map(render).join("") + "</" + tag + ">";
  if (tag === "html") {
    html = "<!DOCTYPE html>" + html;
  }
  return html;
}

// var elements = [
//   // Root element
//   "html",
//   // Document metadata
//   "head", "title", "base", "link", "meta", "style",
//   // Scripting
//   "script", "noscript", "template",
//   // Sections
//   "body", "section", "nav", "article", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "main",
//   // Grouping content
//   "p", "hr", "pre", "blockquote", "ol", "ul", "li", "dl", "dt", "dd", "figure", "figcaption", "div",
//   // Text-level semantics
//   "a", "em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "ruby", "rt", "rp", "bdi", "bdo", "span", "br", "wbr",
//   // Edits
//   "ins", "del",
//   // Embedded content
//   "img", "iframe", "embed", "object", "param", "video", "audio", "source", "track", "canvas", "map", "area", "svg", "math",
//   // Tabular data
//   "table", "caption", "colgroup", "col", "tbody", "thead", "tfoot", "tr", "td", "th",
//   // Forms
//   "form", "fieldset", "legend", "label", "input", "button", "select", "datalist", "optgroup", "option", "textarea", "keygen", "output", "progress", "meter",
//   // Interactive elements
//   "details", "summary", "menuitem", "menu"
// ];

var voidElements = {
  area: true, base: true, br: true, col: true, command: true, embed: true,
  hr: true, img: true, input: true, keygen: true, link: true, meta: true,
  param: true, source: true, track: true, wbr: true,
};

// Will act like a string for everything except bracket access.
function RawHtml(html) {
  this.html = html;
}

var escapeMap = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"',
};

function escapeHtml(string, quotes) {
  return String(string).replace(quotes ? /[&<>"]/g : /[&<>]/g, function (char) {
    return escapeMap[char];
  });
}
<commit_msg>Fix attribute bug<commit_after>module.exports = render;
render.raw = function (html) {
  return new RawHtml(html);
};

var CLASS_MATCH = /\.[^.#]+/g,
    ID_MATCH = /#[^.#]+/,
    TAG_MATCH = /^[^.#]+/;

function stripFirst(part) {
  return part.substr(1);
}

function render(value) {
  if (value instanceof RawHtml) return value.html;
  if (typeof value === "string") return escapeHtml(value);
  if (!Array.isArray(value)) {
    throw new Error("Value must be array, string, or raw html");
  }
  var tag = "div", attributes = {};
  var id, classes;
  var first = value[0];
  if (typeof value[0] === "string") {
    value = value.slice(1);
    var match = first.match(TAG_MATCH);
    if (match) tag = match[0];
    match = first.match(CLASS_MATCH);
    if (match) classes = match.map(stripFirst).join(' ');
    match = first.match(ID_MATCH);
    if (match) id = match[0].substring(1);
  }
  else if (typeof first === "function") {
    return render(first.apply(null, value.slice(1)));
  }
  else if (Array.isArray(first)) {
    return value.map(render).join("");
  }
  var next = value[0];
  if (next && typeof next === "object") {
    attributes = next;
    value = value.slice(1);
  }
  if (id) attributes.id = id;
  if (classes) {
    attributes.class = attributes.class ?
      attributes.class + " " + classes :
      classes;
  }
  var html = "<" + tag;
  var keys = Object.keys(attributes);
  for (var i = 0, l = keys.length; i < l; ++i) {
    var key = keys[i];
    var attribute = attributes[key];
    if (attribute === true) {
      html += " " + key;
    }
    else {
      html += " " + key + '="' + escapeHtml(attribute, true) + '"';
    }
  }
  html += ">";
  if (voidElements[tag]) {
    if (value.length) {
      throw new Error("No body allowed in " + tag);
    }
    return html;
  }
  html += value.map(render).join("") + "</" + tag + ">";
  if (tag === "html") {
    html = "<!DOCTYPE html>" + html;
  }
  return html;
}

// var elements = [
//   // Root element
//   "html",
//   // Document metadata
//   "head", "title", "base", "link", "meta", "style",
//   // Scripting
//   "script", "noscript", "template",
//   // Sections
//   "body", "section", "nav", "article", "aside", "h1", "h2", "h3", "h4", "h5", "h6", "header", "footer", "address", "main",
//   // Grouping content
//   "p", "hr", "pre", "blockquote", "ol", "ul", "li", "dl", "dt", "dd", "figure", "figcaption", "div",
//   // Text-level semantics
//   "a", "em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "ruby", "rt", "rp", "bdi", "bdo", "span", "br", "wbr",
//   // Edits
//   "ins", "del",
//   // Embedded content
//   "img", "iframe", "embed", "object", "param", "video", "audio", "source", "track", "canvas", "map", "area", "svg", "math",
//   // Tabular data
//   "table", "caption", "colgroup", "col", "tbody", "thead", "tfoot", "tr", "td", "th",
//   // Forms
//   "form", "fieldset", "legend", "label", "input", "button", "select", "datalist", "optgroup", "option", "textarea", "keygen", "output", "progress", "meter",
//   // Interactive elements
//   "details", "summary", "menuitem", "menu"
// ];

var voidElements = {
  area: true, base: true, br: true, col: true, command: true, embed: true,
  hr: true, img: true, input: true, keygen: true, link: true, meta: true,
  param: true, source: true, track: true, wbr: true,
};

// Will act like a string for everything except bracket access.
function RawHtml(html) {
  this.html = html;
}

var escapeMap = {
  '&': '&',
  '<': '<',
  '>': '>',
  '"': '"',
};

function escapeHtml(string, quotes) {
  return String(string).replace(quotes ? /[&<>"]/g : /[&<>]/g, function (char) {
    return escapeMap[char];
  });
}
<|endoftext|>


<commit_before>import { each } from 'underscore';

module.exports = (errors) => {
  const errorObject = {};
  if (errors.graphQLErrors) {
    each(errors.graphQLErrors, (error) => {
      errorObject[error.extensions && error.extensions.attribute] = error.message;
    });
  }
  return errorObject;
};
<commit_msg>Capitalize string<commit_after>import { each } from 'underscore';
import { capitalize } from 'underscore.string';

module.exports = (errors) => {
  const errorObject = {};
  if (errors.graphQLErrors) {
    each(errors.graphQLErrors, (error) => {
      errorObject[error.extensions && error.extensions.attribute] = capitalize(error.message, true);
    });
  }
  return errorObject;
};
<|endoftext|>


<commit_before>var user_events = (function () {

var exports = {};

// This module is kind of small, but it will help us keep
// server_events.js simple while breaking some circular
// dependencies that existed when this code was in people.js.
// (We should do bot updates here too.)

exports.update_person = function update(person) {
    var person_obj = people.get_person_from_user_id(person.user_id);

    if (!person_obj) {
        blueslip.error("Got update_person event for unexpected user",
                       {email: person.user_id});
        return;
    }

    if (_.has(person, 'new_email')) {
        var user_id = person.user_id;
        var new_email = person.new_email;

        narrow_state.update_email(user_id, new_email);
        compose.update_email(user_id, new_email);

        if (people.is_my_user_id(person.user_id)) {
            settings_account.update_email(new_email);
        }

        people.update_email(user_id, new_email);
    }

    if (_.has(person, 'full_name')) {
        people.set_full_name(person_obj, person.full_name);

        settings_users.update_user_data(person.user_id, person);
        activity.redraw();
        message_live_update.update_user_full_name(person.user_id, person.full_name);
        pm_list.update_private_messages();
    }

    if (_.has(person, 'is_admin')) {
        person_obj.is_admin = person.is_admin;

        if (people.is_my_user_id(person.user_id)) {
            page_params.is_admin = person.is_admin;
            admin.show_or_hide_menu_item();
        }
    }

    if (_.has(person, 'avatar_url')) {
        var url = person.avatar_url;
        person_obj.avatar_url = url;

        if (people.is_my_user_id(person.user_id)) {
            page_params.avatar_source = person.avatar_source;
            page_params.avatar_url = url;
            page_params.avatar_url_medium = person.avatar_url_medium;
            $("#user-settings-avatar").attr("src", person.avatar_url_medium);
        }

        message_live_update.update_avatar(person_obj.user_id, person.avatar_url);
    }

     if (_.has(person, 'timezone')) {
         person_obj.timezone = person.timezone;
    }
};

return exports;

}());
if (typeof module !== 'undefined') {
    module.exports = user_events;
}
<commit_msg>user_events: Fix buggy-looking logging code.<commit_after>var user_events = (function () {

var exports = {};

// This module is kind of small, but it will help us keep
// server_events.js simple while breaking some circular
// dependencies that existed when this code was in people.js.
// (We should do bot updates here too.)

exports.update_person = function update(person) {
    var person_obj = people.get_person_from_user_id(person.user_id);

    if (!person_obj) {
        blueslip.error("Got update_person event for unexpected user " + person.user_id);
        return;
    }

    if (_.has(person, 'new_email')) {
        var user_id = person.user_id;
        var new_email = person.new_email;

        narrow_state.update_email(user_id, new_email);
        compose.update_email(user_id, new_email);

        if (people.is_my_user_id(person.user_id)) {
            settings_account.update_email(new_email);
        }

        people.update_email(user_id, new_email);
    }

    if (_.has(person, 'full_name')) {
        people.set_full_name(person_obj, person.full_name);

        settings_users.update_user_data(person.user_id, person);
        activity.redraw();
        message_live_update.update_user_full_name(person.user_id, person.full_name);
        pm_list.update_private_messages();
    }

    if (_.has(person, 'is_admin')) {
        person_obj.is_admin = person.is_admin;

        if (people.is_my_user_id(person.user_id)) {
            page_params.is_admin = person.is_admin;
            admin.show_or_hide_menu_item();
        }
    }

    if (_.has(person, 'avatar_url')) {
        var url = person.avatar_url;
        person_obj.avatar_url = url;

        if (people.is_my_user_id(person.user_id)) {
            page_params.avatar_source = person.avatar_source;
            page_params.avatar_url = url;
            page_params.avatar_url_medium = person.avatar_url_medium;
            $("#user-settings-avatar").attr("src", person.avatar_url_medium);
        }

        message_live_update.update_avatar(person_obj.user_id, person.avatar_url);
    }

     if (_.has(person, 'timezone')) {
         person_obj.timezone = person.timezone;
    }
};

return exports;

}());
if (typeof module !== 'undefined') {
    module.exports = user_events;
}
<|endoftext|>