<commit_before>/*
 *
 * FeatureInfoDisplay.js -- part of Quantum GIS Web Client
 *
 * Copyright (2010-2012), The QGIS Project All rights reserved.
 * Quantum GIS Web Client is released under a BSD license. Please see
 * https://github.com/qgis/qgis-web-client/blob/master/README
 * for the full text of the license and the list of contributors.
 *
*/

/* FeatureInfos are presented to the user in two ways using OpenLayers.Popup classes:
 * If the mouse stops and GetFeatureInfo has results for this mouse position
 * a small box presents the contents of the info field (GetProjectSettings) or the
 * field named "toolbox" (GetCapabilities), this is called hoverPopup throughout this script.
 * If the user clicks in the map the contents of all visible fields (and if activated the wkt geometry)
 * is presented in a popup called clickPopup throughout this script.
 * hoverPopups are disabled when a clickPopup is open, however clicking at another position in the map
 * closes the currently opened clickPopup and opens a new one (if there is GetFeatureInfo response).
 * If the cursor is at a position where there is GetFeatureInfo response it indicates the possibility
 * to click by changing to "hand".
*/

var featureInfoPopupContents;
var closePopupClick = false; // stores if the click results from closing a clickPopup

function showFeatureInfo(evt) {
	if (identifyToolActive) {
		if (!closePopupClick) {
			var map = geoExtMap.map; // gets OL map object
			if (window.DOMParser) {
				var parser = new DOMParser();
				xmlDoc = parser.parseFromString(evt.text, "text/xml");
			} else {
				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async = "false";
				xmlDoc.loadXML(evt.text);
			}
			// open AttributeTree panel
			featureInfoResultLayers = new Array();
			highLightGeometry = new Array();
			parseFIResult(xmlDoc);
			featureInfoResultLayers.reverse();
			highLightGeometry.reverse();
			if (featureInfoResultLayers.length > 0) {
				if (hoverPopup) {removeHoverPopup();}
				if (clickPopup) {removeClickPopup();}
				var text = "";
				if (identificationMode == 'topMostHit') {
					text += featureInfoResultLayers[0];
					featureInfoHighlightLayer.addFeatures(highLightGeometry[0]);
					//feature.geometry.getBounds().getCenterLonLat()
				} else {
					for (var i = 0; i < featureInfoResultLayers.length; i++) {
						text += featureInfoResultLayers[i];
						featureInfoHighlightLayer.addFeatures(highLightGeometry[i]);
					}
				}
				clickPopup = new OpenLayers.Popup.FramedCloud(
					null, // id
					map.getLonLatFromPixel(evt.xy), // lonlat
					null, //new OpenLayers.Size(1,1), // contentSize
					text, //contentHTML
					null, // anchor
					true,  // closeBox
					onClickPopupClosed // closeBoxCallBackFunction
					);
				clickPopup.autoSize = true;
				clickPopup.events.fallThrough = false;
				//clickPopup.closeOnMove = true;
				//hoverPopup.setBackgroundColor("#C8C8C8");
				//hoverPopup.setOpacity(0.8);
				map.addPopup(clickPopup); //*/
				changeCursorInMap("default");
			}
		} else {
			closePopupClick = false;
		}
		activateGetFeatureInfo(true);
	}
}

function showFeatureInfoHover(evt) {
	var map = geoExtMap.map; // gets OL map object
	if (identifyToolActive) {
		if (hoverPopup) {removeHoverPopup();}
		if (window.DOMParser) {
			var parser = new DOMParser();
			xmlDoc = parser.parseFromString(evt.text, "text/xml");
		} else {
			xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
			xmlDoc.async = "false";
			xmlDoc.loadXML(evt.text);
		}
		var layerNodes = xmlDoc.getElementsByTagName("Layer");
		var text = ''
		var result = false;
		for (var i = layerNodes.length - 1; i > -1; --i) {
			var featureNodes = layerNodes[i].getElementsByTagName("Feature");
			// show layer display field or if missing, the attribute 'tooltip'
			var tooltipAttributeName = wmsLoader.layerProperties[layerNodes[i].getAttribute("name")].displayField || "tooltip";
			for (var j = 0; j < featureNodes.length; ++j) {
				if (j == 0) {
					text += '' + layerNodes[i].getAttribute("name") + '
';
					result = true;
				}
				var attribNodes = featureNodes[j].getElementsByTagName("Attribute");
				for (var k = 0; k < attribNodes.length; ++k) {
					if (attribNodes[k].getAttribute("name") == tooltipAttributeName) {
						attribText = attribNodes[k].getAttribute("value").replace(/\n/, "
");
						attribText = attribText.replace("\n", "
");
						text += attribText + "
";
					} else {
						if (attribNodes[k].getAttribute("name") == "geometry") {
							var feature = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(attribNodes[k].getAttribute("value")));
							featureInfoHighlightLayer.addFeatures([feature]);
						}
					}
				}
			}
			if (identificationMode == 'topMostHit' && result) {
				break;
			}
		}

		if (result) {
			changeCursorInMap("pointer");
			if (!clickPopup) {
				// only show hoverPopup if no clickPopup is open
				featureInfoHighlightLayer.removeAllFeatures();
				text = text.substring(0, text.lastIndexOf("
"));
				hoverPopup = new OpenLayers.Popup(
					null, // id
					map.getLonLatFromPixel(evt.xy), // lonlat
					null, //new OpenLayers.Size(1,1), // contentSize
					text , //contentHTML
					/*null, // anchor */
					false // closeBox
					);
				hoverPopup.autoSize = true;
				//hoverPopup.setBackgroundColor("#C8C8C8");
				hoverPopup.setOpacity(0.8);
				hoverPopup.events.on({"click": onHoverPopupClick});
				map.addPopup(hoverPopup); //*/
			}
		} else {
			changeCursorInMap("default");
		}
	}
}

// disable all GetFeatureInfoRequest until we have a reponse
function onBeforeGetFeatureInfoClick(evt){
	activateGetFeatureInfo(false);
}

// reenable GetFeatureInfo
function noFeatureInfoClick(evt){
	activateGetFeatureInfo(true);
}

/* we need this function in order to pass through the click to the map events
 * */
function onHoverPopupClick(evt){
	if (hoverPopup) {removeHoverPopup();}
	var map = geoExtMap.map; // gets OL map object
	evt.xy = map.events.getMousePosition(evt); // non api function of OpenLayers.Events
	map.events.triggerEvent("click", evt);
}

function onClickPopupClosed(evt) {
	removeClickPopup();
	// enable the hover popup for the curent mosue position
	WMSGetFInfoHover.activate();
	var map = geoExtMap.map; // gets OL map object
	evt.xy = map.events.getMousePosition(evt); // non api function of OpenLayers.Events
	map.events.triggerEvent("mousemove", evt);
	closePopupClick = true; // indicate to not open a new clickPopup
}

function removeClickPopup() {
	var map = geoExtMap.map; // gets OL map object
	map.removePopup(clickPopup);
	clickPopup.destroy();
	clickPopup = null;
	featureInfoHighlightLayer.removeAllFeatures();
}

function removeHoverPopup(){
	var map = geoExtMap.map; // gets OL map object
	map.removePopup(hoverPopup);
	hoverPopup.destroy();
	hoverPopup = null;
}

function showFeatureSelected(args) {
	// select feature in layer
	thematicLayer.mergeNewParams({
		"SELECTION": args["layer"] + ":" + args["id"]
	});
	if (args["doZoomToExtent"]){
		geoExtMap.map.zoomToExtent(args["bbox"]);
	}
	else{
		geoExtMap.map.setCenter(new OpenLayers.LonLat(args["x"], args["y"]), args["zoom"]);
	}
}

function clearFeatureSelected() {
	// clear selection
	thematicLayer.mergeNewParams({
		"SELECTION": null
	});
}

function parseFIResult(node) {
	if (node.hasChildNodes) {
		if (node.hasChildNodes && node.nodeName == "Layer") {
			var hasAttributes = false;
			var htmlText = "
" + node.getAttribute("name") + "
";
			var geoms = new Array();
			var featureNode = node.firstChild;
			while (featureNode) {
				if (featureNode.hasChildNodes && featureNode.nodeName == "Feature") {
					htmlText += "\n

\n
\n
";
					var attributeNode = featureNode.firstChild;
					while (attributeNode) {
						if (attributeNode.nodeName == "Attribute") {
							var attName = attributeNode.getAttribute("name");
							var attValue = attributeNode.getAttribute("value");
							if ((attName !== mapInfoFieldName) && (suppressEmptyValues && attValue.replace(/^\s\s*/, '').replace(/\s\s*$/, '') !== "")) {
								if (attName === "geometry") {
									var feature = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(attValue));
									geoms.push(feature);
									if (! suppressInfoGeometry) {
										htmlText += "\n
";
										if (showFieldNamesInClickPopup) {
											htmlText += "" + attName + ":	";
										}
										htmlText += "" + attValue + "
";
										var hasAttributes = true;
									}
								} else {
									htmlText += "\n
";
									if (showFieldNamesInClickPopup) {
										htmlText += "" + attName + ":	";
									}
									// add hyperlinks for URLs in attribute values
									if (attValue != '' && /http:\/\/.+\..+/i.test(attValue)) {
										attValue = "" + attValue + "";
									}
									htmlText += "" + attValue + "
";
									var hasAttributes = true;
								}
							}
						}
						attributeNode = attributeNode.nextSibling;
					}
					htmlText += "\n
\n
";
					//htmlText += "\n
\n
";
				}
				featureNode = featureNode.nextSibling;
			}
			//htmlText += "\n
";
			if (hasAttributes) {
				featureInfoResultLayers.push(htmlText);
				highLightGeometry.push(geoms);
			}
		} else {
			var child = node.firstChild;
			while (child) {
				parseFIResult(child);
				child = child.nextSibling;
			}
		}
	}
}

function listLayersWithFeatures(node) {
	if (node.hasChildNodes()) {
		if (node.nodeName == "Layer") {
			featureInfoResultLayers.push(node.getAttribute("name"));
		} else {
			var child = node.firstChild;
			while (child) {
				listLayersWithFeatures(child);
				child = child.nextSibling;
			}
		}
	}
}

function getFeatures(layerName, node) {
	if (node.hasChildNodes()) {
		if (node.nodeName == "Layer" && node.getAttribute("name") == layerName) {
			return node.firstChild;
		} else {
			var child = node.firstChild;
			while (child) {
				getFeatures(layerName, child);
				child = child.nextSibling;
			}
		}
	}
}
<commit_msg>Auto create link only for url in attribute having no -tag<commit_after>/*
 *
 * FeatureInfoDisplay.js -- part of Quantum GIS Web Client
 *
 * Copyright (2010-2012), The QGIS Project All rights reserved.
 * Quantum GIS Web Client is released under a BSD license. Please see
 * https://github.com/qgis/qgis-web-client/blob/master/README
 * for the full text of the license and the list of contributors.
 *
*/

/* FeatureInfos are presented to the user in two ways using OpenLayers.Popup classes:
 * If the mouse stops and GetFeatureInfo has results for this mouse position
 * a small box presents the contents of the info field (GetProjectSettings) or the
 * field named "toolbox" (GetCapabilities), this is called hoverPopup throughout this script.
 * If the user clicks in the map the contents of all visible fields (and if activated the wkt geometry)
 * is presented in a popup called clickPopup throughout this script.
 * hoverPopups are disabled when a clickPopup is open, however clicking at another position in the map
 * closes the currently opened clickPopup and opens a new one (if there is GetFeatureInfo response).
 * If the cursor is at a position where there is GetFeatureInfo response it indicates the possibility
 * to click by changing to "hand".
*/

var featureInfoPopupContents;
var closePopupClick = false; // stores if the click results from closing a clickPopup

function showFeatureInfo(evt) {
	if (identifyToolActive) {
		if (!closePopupClick) {
			var map = geoExtMap.map; // gets OL map object
			if (window.DOMParser) {
				var parser = new DOMParser();
				xmlDoc = parser.parseFromString(evt.text, "text/xml");
			} else {
				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async = "false";
				xmlDoc.loadXML(evt.text);
			}
			// open AttributeTree panel
			featureInfoResultLayers = new Array();
			highLightGeometry = new Array();
			parseFIResult(xmlDoc);
			featureInfoResultLayers.reverse();
			highLightGeometry.reverse();
			if (featureInfoResultLayers.length > 0) {
				if (hoverPopup) {removeHoverPopup();}
				if (clickPopup) {removeClickPopup();}
				var text = "";
				if (identificationMode == 'topMostHit') {
					text += featureInfoResultLayers[0];
					featureInfoHighlightLayer.addFeatures(highLightGeometry[0]);
					//feature.geometry.getBounds().getCenterLonLat()
				} else {
					for (var i = 0; i < featureInfoResultLayers.length; i++) {
						text += featureInfoResultLayers[i];
						featureInfoHighlightLayer.addFeatures(highLightGeometry[i]);
					}
				}
				clickPopup = new OpenLayers.Popup.FramedCloud(
					null, // id
					map.getLonLatFromPixel(evt.xy), // lonlat
					null, //new OpenLayers.Size(1,1), // contentSize
					text, //contentHTML
					null, // anchor
					true,  // closeBox
					onClickPopupClosed // closeBoxCallBackFunction
					);
				clickPopup.autoSize = true;
				clickPopup.events.fallThrough = false;
				//clickPopup.closeOnMove = true;
				//hoverPopup.setBackgroundColor("#C8C8C8");
				//hoverPopup.setOpacity(0.8);
				map.addPopup(clickPopup); //*/
				changeCursorInMap("default");
			}
		} else {
			closePopupClick = false;
		}
		activateGetFeatureInfo(true);
	}
}

function showFeatureInfoHover(evt) {
	var map = geoExtMap.map; // gets OL map object
	if (identifyToolActive) {
		if (hoverPopup) {removeHoverPopup();}
		if (window.DOMParser) {
			var parser = new DOMParser();
			xmlDoc = parser.parseFromString(evt.text, "text/xml");
		} else {
			xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
			xmlDoc.async = "false";
			xmlDoc.loadXML(evt.text);
		}
		var layerNodes = xmlDoc.getElementsByTagName("Layer");
		var text = ''
		var result = false;
		for (var i = layerNodes.length - 1; i > -1; --i) {
			var featureNodes = layerNodes[i].getElementsByTagName("Feature");
			// show layer display field or if missing, the attribute 'tooltip'
			var tooltipAttributeName = wmsLoader.layerProperties[layerNodes[i].getAttribute("name")].displayField || "tooltip";
			for (var j = 0; j < featureNodes.length; ++j) {
				if (j == 0) {
					text += '' + layerNodes[i].getAttribute("name") + '
';
					result = true;
				}
				var attribNodes = featureNodes[j].getElementsByTagName("Attribute");
				for (var k = 0; k < attribNodes.length; ++k) {
					if (attribNodes[k].getAttribute("name") == tooltipAttributeName) {
						attribText = attribNodes[k].getAttribute("value").replace(/\n/, "
");
						attribText = attribText.replace("\n", "
");
						text += attribText + "
";
					} else {
						if (attribNodes[k].getAttribute("name") == "geometry") {
							var feature = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(attribNodes[k].getAttribute("value")));
							featureInfoHighlightLayer.addFeatures([feature]);
						}
					}
				}
			}
			if (identificationMode == 'topMostHit' && result) {
				break;
			}
		}

		if (result) {
			changeCursorInMap("pointer");
			if (!clickPopup) {
				// only show hoverPopup if no clickPopup is open
				featureInfoHighlightLayer.removeAllFeatures();
				text = text.substring(0, text.lastIndexOf("
"));
				hoverPopup = new OpenLayers.Popup(
					null, // id
					map.getLonLatFromPixel(evt.xy), // lonlat
					null, //new OpenLayers.Size(1,1), // contentSize
					text , //contentHTML
					/*null, // anchor */
					false // closeBox
					);
				hoverPopup.autoSize = true;
				//hoverPopup.setBackgroundColor("#C8C8C8");
				hoverPopup.setOpacity(0.8);
				hoverPopup.events.on({"click": onHoverPopupClick});
				map.addPopup(hoverPopup); //*/
			}
		} else {
			changeCursorInMap("default");
		}
	}
}

// disable all GetFeatureInfoRequest until we have a reponse
function onBeforeGetFeatureInfoClick(evt){
	activateGetFeatureInfo(false);
}

// reenable GetFeatureInfo
function noFeatureInfoClick(evt){
	activateGetFeatureInfo(true);
}

/* we need this function in order to pass through the click to the map events
 * */
function onHoverPopupClick(evt){
	if (hoverPopup) {removeHoverPopup();}
	var map = geoExtMap.map; // gets OL map object
	evt.xy = map.events.getMousePosition(evt); // non api function of OpenLayers.Events
	map.events.triggerEvent("click", evt);
}

function onClickPopupClosed(evt) {
	removeClickPopup();
	// enable the hover popup for the curent mosue position
	WMSGetFInfoHover.activate();
	var map = geoExtMap.map; // gets OL map object
	evt.xy = map.events.getMousePosition(evt); // non api function of OpenLayers.Events
	map.events.triggerEvent("mousemove", evt);
	closePopupClick = true; // indicate to not open a new clickPopup
}

function removeClickPopup() {
	var map = geoExtMap.map; // gets OL map object
	map.removePopup(clickPopup);
	clickPopup.destroy();
	clickPopup = null;
	featureInfoHighlightLayer.removeAllFeatures();
}

function removeHoverPopup(){
	var map = geoExtMap.map; // gets OL map object
	map.removePopup(hoverPopup);
	hoverPopup.destroy();
	hoverPopup = null;
}

function showFeatureSelected(args) {
	// select feature in layer
	thematicLayer.mergeNewParams({
		"SELECTION": args["layer"] + ":" + args["id"]
	});
	if (args["doZoomToExtent"]){
		geoExtMap.map.zoomToExtent(args["bbox"]);
	}
	else{
		geoExtMap.map.setCenter(new OpenLayers.LonLat(args["x"], args["y"]), args["zoom"]);
	}
}

function clearFeatureSelected() {
	// clear selection
	thematicLayer.mergeNewParams({
		"SELECTION": null
	});
}

function parseFIResult(node) {
	if (node.hasChildNodes) {
		if (node.hasChildNodes && node.nodeName == "Layer") {
			var hasAttributes = false;
			var htmlText = "
" + node.getAttribute("name") + "
";
			var geoms = new Array();
			var featureNode = node.firstChild;
			while (featureNode) {
				if (featureNode.hasChildNodes && featureNode.nodeName == "Feature") {
					htmlText += "\n

\n
\n
";
					var attributeNode = featureNode.firstChild;
					while (attributeNode) {
						if (attributeNode.nodeName == "Attribute") {
							var attName = attributeNode.getAttribute("name");
							var attValue = attributeNode.getAttribute("value");
							if ((attName !== mapInfoFieldName) && (suppressEmptyValues && attValue.replace(/^\s\s*/, '').replace(/\s\s*$/, '') !== "")) {
								if (attName === "geometry") {
									var feature = new OpenLayers.Feature.Vector(OpenLayers.Geometry.fromWKT(attValue));
									geoms.push(feature);
									if (! suppressInfoGeometry) {
										htmlText += "\n
";
										if (showFieldNamesInClickPopup) {
											htmlText += "" + attName + ":	";
										}
										htmlText += "" + attValue + "
";
										var hasAttributes = true;
									}
								} else {
									htmlText += "\n
";
									if (showFieldNamesInClickPopup) {
										htmlText += "" + attName + ":	";
									}
									// add hyperlinks for URLs in attribute values
									if (attValue != '' && /http:\/\/.+\..+/i.test(attValue)) {
										if (! /\<a./i.test(attValue)) {
											//do not reformat already formated tags
											attValue = "" + attValue + "";
										}
									}
									htmlText += "" + attValue + "
";
									var hasAttributes = true;
								}
							}
						}
						attributeNode = attributeNode.nextSibling;
					}
					htmlText += "\n
\n
";
					//htmlText += "\n
\n
";
				}
				featureNode = featureNode.nextSibling;
			}
			//htmlText += "\n
";
			if (hasAttributes) {
				featureInfoResultLayers.push(htmlText);
				highLightGeometry.push(geoms);
			}
		} else {
			var child = node.firstChild;
			while (child) {
				parseFIResult(child);
				child = child.nextSibling;
			}
		}
	}
}

function listLayersWithFeatures(node) {
	if (node.hasChildNodes()) {
		if (node.nodeName == "Layer") {
			featureInfoResultLayers.push(node.getAttribute("name"));
		} else {
			var child = node.firstChild;
			while (child) {
				listLayersWithFeatures(child);
				child = child.nextSibling;
			}
		}
	}
}

function getFeatures(layerName, node) {
	if (node.hasChildNodes()) {
		if (node.nodeName == "Layer" && node.getAttribute("name") == layerName) {
			return node.firstChild;
		} else {
			var child = node.firstChild;
			while (child) {
				getFeatures(layerName, child);
				child = child.nextSibling;
			}
		}
	}
}
<|endoftext|>


<commit_before>// pubsub.js
// --------------------
//
// These methods get appended to the Model class objects
// They take in the request object as an argument in order to
// get access to the user's socket

var _ = require('underscore');
_.str = require('underscore.string');

var pubsub = {
	/**
	* Subscribe to a handful of models in this collection
	*/
	subscribe: function (req, models) {
		if (!req.isSocket) return;
		var my = this;
		models = my.pluralize(models);

		// Subscribe to class room to hear about new models
		req.socket.join( my.classRoom() );
		sails.log.verbose("Subscribed to "+my.classRoom());

		// Subscribe to existing models
		var ids = _.pluck(models,'id');
		_.each(ids,function (id) {
			sails.log.verbose("Subscribed to "+my.room(id));
			req.socket.join( my.room(id) );
		});
	},

	/**
	* Unsubscribe from some models
	*/
	unsubscribe: function (req,models) {
		if (!req.isSocket) return;
		var my = this;

		// If no models provided, unsubscribe from the class room
		if (!models) req.socket.leave( my.classRoom() );
		else {
			models = my.pluralize(models);
			var ids = _.pluck(models,'id');
			_.each(ids,function (id) {
				req.socket.leave( my.room(id) );
			});
		}
	},

	/**
	* Take all of the class room models and 'introduce' them to a new instance room
	* (good for when a new instance is created-- connecting sockets must subscribe to it)
	*/
	introduce: function (req, id) {
		var my = this;
		_.each(this.subscribers(),function (socket) {
			socket.join( my.room(id) );
		});
	},

	/**
	* Broadcast a message to sockets connected to the specified models
	*/
	publish: function (req,models,message) {
		if (!req.isSocket) return;
		var my = this;
		this.pluralize(models);

		// If no models provided, publish to the class room
		if (!models) {
			sails.io.sockets['in']( my.classRoom() ).json.emit(message);
			// req.socket.broadcast.to( my.classRoom() ).json.send(message);
			sails.log.verbose("Published "+message+" to "+my.classRoom());
		}
		// Otherwise publish to each instance room
		else {
			var ids = _.pluck(models,'id');
			_.each(ids,function (id) {
				sails.io.sockets['in']( my.room(id) ).json.emit(message);
				// req.socket.broadcast.to( my.room(id) ).json.send(message);
				sails.log.verbose("Published "+message+" to "+my.room(id));
			});
		}
	},

	// Check that models are a list, if not, make them a list
	pluralize: function (models) {
		if (!_.isArray(models)) {
			if (!_.isObject(models)) {
				sails.log.error("Trying to pluralize invalid model(s)! "+models);
				return models;
			}
			else return models = [models]; // TODO: review it
		}
		else return models;
	},

	// Return the room name for an instance in this collection with the given id
	room: function (id) {
		return 'sails_c_'+this.identity+'_'+id;
	},

	classRoom: function () {
		return 'sails_c_create_'+this.identity;
	},

	// Return the set of sockets subscribed to this instance or class room
	subscribers: function (id) {
		var room = id ? this.room(id) : this.classRoom();
		return sails.io.sockets.clients(room);
	}
};

// Export logic
module.exports = pubsub;<commit_msg>Added some more clarification around what pluralize is doing (hopefully that resolves the 'review it' todo?) Also added one more application of this fix in pubsub.publish().<commit_after>// pubsub.js
// --------------------
//
// These methods get appended to the Model class objects
// They take in the request object as an argument in order to
// get access to the user's socket

var _ = require('underscore');
_.str = require('underscore.string');

var pubsub = {
	/**
	* Subscribe to a handful of models in this collection
	*/
	subscribe: function (req, models) {
		if (!req.isSocket) return;
		var my = this;

		// Subscribe to class room to hear about new models
		req.socket.join( my.classRoom() );
		sails.log.verbose("Subscribed to "+my.classRoom());

		// Subscribe to existing models
		models = my.pluralize(models);
		var ids = _.pluck(models,'id');
		_.each(ids,function (id) {
			sails.log.verbose("Subscribed to "+my.room(id));
			req.socket.join( my.room(id) );
		});
	},

	/**
	* Unsubscribe from some models
	*/
	unsubscribe: function (req,models) {
		if (!req.isSocket) return;
		var my = this;

		// If no models provided, unsubscribe from the class room
		if (!models) req.socket.leave( my.classRoom() );
		else {
			models = my.pluralize(models);
			var ids = _.pluck(models,'id');
			_.each(ids,function (id) {
				req.socket.leave( my.room(id) );
			});
		}
	},

	/**
	* Take all of the class room models and 'introduce' them to a new instance room
	* (good for when a new instance is created-- connecting sockets must subscribe to it)
	*/
	introduce: function (req, id) {
		var my = this;
		_.each(this.subscribers(),function (socket) {
			socket.join( my.room(id) );
		});
	},

	/**
	* Broadcast a message to sockets connected to the specified models
	*/
	publish: function (req,models,message) {
		if (!req.isSocket) return;
		var my = this;

		// If no models provided, publish to the class room
		if (!models) {
			sails.io.sockets['in']( my.classRoom() ).json.emit(message);
			// req.socket.broadcast.to( my.classRoom() ).json.send(message);
			sails.log.verbose("Published "+message+" to "+my.classRoom());
		}
		// Otherwise publish to each instance room
		else {
			models = this.pluralize(models);
			var ids = _.pluck(models,'id');
			_.each(ids,function (id) {
				sails.io.sockets['in']( my.room(id) ).json.emit(message);
				// req.socket.broadcast.to( my.room(id) ).json.send(message);
				sails.log.verbose("Published "+message+" to "+my.room(id));
			});
		}
	},

	// Check that models are a list, if not, make them a list
	pluralize: function (models) {
		if (!_.isArray(models)) {
			if (!_.isObject(models)) {
				sails.log.error("Trying to pluralize invalid model(s)! "+models);
				return models;
			}

			// If `models` is a non-array object,
			// turn it into a single-item array ("pluralize" it)
			else return models = [models];
		}
		else return models;
	},

	// Return the room name for an instance in this collection with the given id
	room: function (id) {
		return 'sails_c_'+this.identity+'_'+id;
	},

	classRoom: function () {
		return 'sails_c_create_'+this.identity;
	},

	// Return the set of sockets subscribed to this instance or class room
	subscribers: function (id) {
		var room = id ? this.room(id) : this.classRoom();
		return sails.io.sockets.clients(room);
	}
};

// Export logic
module.exports = pubsub;<|endoftext|>


<commit_before>'use strict';

angular.module('myApp.controllers', [])
    .controller('MainCtrl', ['$scope', '$rootScope', '$window', '$location', function ($scope, $rootScope, $window, $location) {
        $scope.slide = '';
        $rootScope.back = function() {
          $scope.slide = 'slide-right';
          $window.history.back();
        }
        $rootScope.go = function(path){
          $scope.slide = 'slide-left';
          $location.url(path);
        }
    }])
    .controller('LoginCtrl', ['$scope', 'Employee', '$http', function ($scope, Employee, $http) {
        // $scope.employees = Employee.query();
        $scope.doLogin = function() {
            $scope.error = {};

            if ( $scope.user === undefined || !$scope.user.username || !$scope.user.password) {
                $scope.error.message = 'Invalid login details';
                return;
            }

            $http.post("http://offerfans.ngrok.com/api/v1/auth/login", $scope.user)
                 .success(function(data) {
                    window.localStorage.setItem("access_token", data.token);
                    window.localStorage.setItem("username", $scope.user.username);
                    window.location = "/#/scan";
                    return true;
                 })
                 .error(function(data, status, headers, config) {
                    $scope.error.message = data.error;
                    return;
                 });
        }
    }])
    .controller('ScanCtrl', ['$scope', 'Employee', function ($scope, Employee) {

        if (!window.localStorage.getItem("access_token")) {
            window.location = "/#/login";
            return false;
        }

        $scope.user  = { username: window.localStorage.getItem("username") };
    }])
    .controller('EmployeeListCtrl', ['$scope', 'Employee', function ($scope, Employee) {
        $scope.employees = Employee.query();
    }])
    .controller('EmployeeDetailCtrl', ['$scope', '$routeParams', 'Employee', function ($scope, $routeParams, Employee) {
        $scope.employee = Employee.get({employeeId: $routeParams.employeeId});
    }])
    .controller('ReportListCtrl', ['$scope', '$routeParams', 'Report', function ($scope, $routeParams, Report) {
        $scope.employees = Report.query({employeeId: $routeParams.employeeId});
    }]);
<commit_msg>Change redirect urls to not have first /<commit_after>'use strict';

angular.module('myApp.controllers', [])
    .controller('MainCtrl', ['$scope', '$rootScope', '$window', '$location', function ($scope, $rootScope, $window, $location) {
        $scope.slide = '';
        $rootScope.back = function() {
          $scope.slide = 'slide-right';
          $window.history.back();
        }
        $rootScope.go = function(path){
          $scope.slide = 'slide-left';
          $location.url(path);
        }
    }])
    .controller('LoginCtrl', ['$scope', 'Employee', '$http', function ($scope, Employee, $http) {
        // $scope.employees = Employee.query();
        $scope.doLogin = function() {
            $scope.error = {};

            if ( $scope.user === undefined || !$scope.user.username || !$scope.user.password) {
                $scope.error.message = 'Invalid login details';
                return;
            }

            $http.post("http://offerfans.ngrok.com/api/v1/auth/login", $scope.user)
                 .success(function(data) {
                    window.localStorage.setItem("access_token", data.token);
                    window.localStorage.setItem("username", $scope.user.username);
                    window.location = "#/scan";
                    return true;
                 })
                 .error(function(data, status, headers, config) {
                    $scope.error.message = data.error;
                    return;
                 });
        }
    }])
    .controller('ScanCtrl', ['$scope', 'Employee', function ($scope, Employee) {

        if (!window.localStorage.getItem("access_token")) {
            window.location = "#/login";
            return false;
        }

        $scope.user  = { username: window.localStorage.getItem("username") };
    }])
    .controller('EmployeeListCtrl', ['$scope', 'Employee', function ($scope, Employee) {
        $scope.employees = Employee.query();
    }])
    .controller('EmployeeDetailCtrl', ['$scope', '$routeParams', 'Employee', function ($scope, $routeParams, Employee) {
        $scope.employee = Employee.get({employeeId: $routeParams.employeeId});
    }])
    .controller('ReportListCtrl', ['$scope', '$routeParams', 'Report', function ($scope, $routeParams, Report) {
        $scope.employees = Report.query({employeeId: $routeParams.employeeId});
    }]);
<|endoftext|>


<commit_before>'use strict';

(function () {

  var fs = require('fs');
  var highlightStyles = fs.readFileSync(__dirname + '/../../../node_modules/highlight.js/styles/github.css', 'utf8');

  var headTag = document.getElementsByTagName('head')[0];

  var styleTag = document.createElement('style');
  styleTag.setAttribute('type', 'text/css');

  styleTag.innerHTML = highlightStyles;

  headTag.appendChild(styleTag);

  require('./styles');
  var React = require('react');
  var ReactDOM = require('react-dom');
  var App = require('./components/app');

  ReactDOM.render(<App />, document.getElementById('app'));

})();
<commit_msg>Fix linting<commit_after>/* global __dirname */

'use strict';

(function () {

  var fs = require('fs');
  var highlightStyles = fs.readFileSync(__dirname + '/../../../node_modules/highlight.js/styles/github.css', 'utf8');

  var headTag = document.getElementsByTagName('head')[0];

  var styleTag = document.createElement('style');
  styleTag.setAttribute('type', 'text/css');

  styleTag.innerHTML = highlightStyles;

  headTag.appendChild(styleTag);

  require('./styles');
  var React = require('react');
  var ReactDOM = require('react-dom');
  var App = require('./components/app');

  ReactDOM.render(<App />, document.getElementById('app'));

})();
<|endoftext|>


<commit_before>$("document").ready(function(){
	$(".remove_helios_as_trustee").click(function() {
		if (confirm(gettext('Are you sure you want to remove Helios as a trustee?'))){
			return true;
		}
		return false;
	});
	$(".remove_trustee").click(function(){
		if (confirm(gettext('Are you sure you want to remove this Trustee?'))){
			return true;
		}
		return false;
	});
	$("#add_trustee").click(function(){
		var msg = gettext('Adding your own trustee requires a good bit more work to tally the election.\nYou will need to have trustees generate keypairs and safeguard their secret key.\n\n If you are not sure what that means, we strongly recommend\n clicking Cancel and letting Helios tally the election for you.');
		if (confirm(msg)){
			return true;
		}
		return false;
	});
	$(".send_trustee_url").click(function(){
		if (confirm(gettext('Are you sure you want to send this trustee his/her admin URL?'))) {
			return true;
		}
		return false;
	});
	$("#begin_tally").submit(function(){
		alert(gettext('ok, tally has begun'));
	});
})<commit_msg>Update static tracked files<commit_after>$("document").ready(function(){
	$(".remove_helios_as_trustee").click(function() {
		if (confirm(gettext('Are you sure you want to remove Helios as a trustee?'))){
			return true;
		}
		return false;
	});
	$(".remove_trustee").click(function(){
		if (confirm(gettext('Are you sure you want to remove this Trustee?'))){
			return true;
		}
		return false;
	});
	$("#add_trustee").click(function(){
		var msg = gettext('Adding your own trustee requires a good bit more work to tally the election.\nYou will need to have trustees generate keypairs and safeguard their secret key.\n\n If you are not sure what that means, we strongly recommend\n clicking Cancel and letting Helios tally the election for you.');
		if (confirm(msg)){
			return true;
		}
		return false;
	});
	$(".send_trustee_url").click(function(){
		if (confirm(gettext('Are you sure you want to send this trustee his/her admin URL?'))) {
			return true;
		}
		return false;
	});
	$("#begin_tally").submit(function(){
		alert(gettext('ok, tally has begun'));
	});
	$(".remove_voter").click(function(){
		var voter = $(this).attr("data-name");
		if (confirm(gettext('Are you sure you want to remove voter ' + voter + '?'))) {
			return true;
		}
		return false;
	});
})<|endoftext|>


<commit_before>"use strict";

angular.module('myApp.controllers', [])
.controller('mainCtrl',function($scope,$ionicPlatform,$cordovaDevice){
	var _this=this;
//	_this.token = 'GO';
//	$scope.token = 'WO';
    $ionicPlatform.ready(function() {
		 //_this.token = $cordovaDevice.getUUID();
 		_this.model = 'HELLO';
 		$scope.$apply();
 	});

    document.addEventListener("deviceready", function() {
    	_this.model = $cordovaDevice.getModel();
    	$scope.$apply();
      }, false);
})
;
<commit_msg>Update controllers.js<commit_after>"use strict";

angular.module('myApp.controllers', [])
.controller('mainCtrl',function($scope,$ionicPlatform,$cordovaDevice){
	var _this=this;
//	_this.token = 'GO';
//	$scope.token = 'WO';
    $ionicPlatform.ready(function() {
		 //_this.token = $cordovaDevice.getUUID();
 		_this.model = 'HELLO';
 		$scope.$apply();
 	});

    document.addEventListener("deviceready", function() {
    	// _this.model = $cordovaDevice.getModel();
    	// $scope.$apply();
      }, false);
})
;
<|endoftext|>


<commit_before>
import { Editor, Raw } from '../..'
import React from 'react'
import initialState from './state.json'
import keycode from 'keycode'

/**
 * Define a schema.
 *
 * @type {Object}
 */

const schema = {
  nodes: {
    'table': props =>

{props.children}

,
    'table-row': props =>
{props.children}
,
    'table-cell': props => {props.children}	,
  },
  marks: {
    'bold': props => {props.children}
  }
}

/**
 * The tables example.
 *
 * @type {Component}
 */

class Tables extends React.Component {

  /**
   * Deserialize the raw initial state.
   *
   * @type {Object}
   */

  state = {
    state: Raw.deserialize(initialState, { terse: true })
  };

  /**
   * On backspace, do nothing if at the start of a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onBackspace = (e, state) => {
    if (state.startOffset != 0) return
    e.preventDefault()
    return state
  }

  /**
   * On change.
   *
   * @param {State} state
   */

  onChange = (state) => {
    this.setState({ state })
  }

  /**
   * On delete, do nothing if at the end of a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onDelete = (e, state) => {
    if (state.endOffset != state.startText.length) return
    e.preventDefault()
    return state
  }

  /**
   * On return, do nothing if inside a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onEnter = (e, state) => {
    e.preventDefault()
    return state
  }

  /**
   * On key down, check for our specific key shortcuts.
   *
   * @param {Event} e
   * @param {Object} data
   * @param {State} state
   * @return {State or Null} state
   */

  onKeyDown = (e, data, state) => {
    if (state.startBlock.type != 'table-cell') return
    switch (data.key) {
      case 'backspace': return this.onBackspace(e, state)
      case 'delete': return this.onDelete(e, state)
      case 'enter': return this.onEnter(e, state)
    }
  }

  /**
   * Render the example.
   *
   * @return {Component} component
   */

  render = () => {
    return (

        <Editor
          schema={schema}
          state={this.state.state}
          onKeyDown={this.onKeyDown}
          onChange={this.onChange}
        />

    )
  }

}

/**
 * Export.
 */

export default Tables
<commit_msg>Prevent backspace to move selection into table-cell (#345)<commit_after>
import { Editor, Raw } from '../..'
import React from 'react'
import initialState from './state.json'
import keycode from 'keycode'

/**
 * Define a schema.
 *
 * @type {Object}
 */

const schema = {
  nodes: {
    'table': props =>

{props.children}

,
    'table-row': props =>
{props.children}
,
    'table-cell': props => {props.children}	,
  },
  marks: {
    'bold': props => {props.children}
  }
}

/**
 * The tables example.
 *
 * @type {Component}
 */

class Tables extends React.Component {

  /**
   * Deserialize the raw initial state.
   *
   * @type {Object}
   */

  state = {
    state: Raw.deserialize(initialState, { terse: true })
  };

  /**
   * On backspace, do nothing if at the start of a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onBackspace = (e, state) => {
    if (state.startOffset != 0) return
    e.preventDefault()
    return state
  }

  /**
   * On change.
   *
   * @param {State} state
   */

  onChange = (state) => {
    this.setState({ state })
  }

  /**
   * On delete, do nothing if at the end of a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onDelete = (e, state) => {
    if (state.endOffset != state.startText.length) return
    e.preventDefault()
    return state
  }

  /**
   * On return, do nothing if inside a table cell.
   *
   * @param {Event} e
   * @param {State} state
   * @return {State or Null} state
   */

  onEnter = (e, state) => {
    e.preventDefault()
    return state
  }

  /**
   * On key down, check for our specific key shortcuts.
   *
   * @param {Event} e
   * @param {Object} data
   * @param {State} state
   * @return {State or Null} state
   */

  onKeyDown = (e, data, state) => {
    const { document, selection } = state
    const { startKey } = selection
    const startNode = document.getDescendant(startKey)

    if (selection.isAtStartOf(startNode)) {
      const previous = document.getPreviousText(startNode)
      const prevBlock = document.getClosestBlock(previous)

      if (prevBlock.type == 'table-cell') {
        e.preventDefault()
        return state
      }
    }

    if (state.startBlock.type != 'table-cell') return
    switch (data.key) {
      case 'backspace': return this.onBackspace(e, state)
      case 'delete': return this.onDelete(e, state)
      case 'enter': return this.onEnter(e, state)
    }
  }

  /**
   * Render the example.
   *
   * @return {Component} component
   */

  render = () => {
    return (

        <Editor
          schema={schema}
          state={this.state.state}
          onKeyDown={this.onKeyDown}
          onChange={this.onChange}
        />

    )
  }

}

/**
 * Export.
 */

export default Tables
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict'

const fs = require('fs')
const path = require('path')
const flags = require('commander')
const splash = require('./splash')

console.log(process.argv)

const markserv = require(path.join(__dirname, 'server'))
const pkg = require(path.join(__dirname, '..', './package.json'))

const run = args => {
	splash(args)

	const cwd = process.cwd()
	/* eslint-disable-next-line no-mixed-operators */
	flags.dir = args && args.dir || cwd
	flags.$pathProvided = true

	const fileExistsSync = uri => {
		let exists

		try {
			const stat = fs.statSync(uri)
			if (stat.isFile()) {
				exists = true
			}
		} catch (err) {
			exists = false
		}

		return exists
	}

	const findFileUp = (dir, fileToFind) => {
		const filepath = path.join(dir, fileToFind)
		const existsHere = fileExistsSync(filepath)

		if (dir === path.sep || dir === '.') {
			return false
		}

		if (existsHere) {
			return filepath
		}

		const nextDirUp = path.dirname(dir)
		return findFileUp(nextDirUp, fileToFind)
	}

	const findReadmeFile = dir => {
		const readmeFile = findFileUp(dir, 'README.md') ||
			findFileUp(dir, 'readme.md') ||
			findFileUp(dir, 'README.MD') ||
			findFileUp(dir, 'Readme.md')
		return readmeFile
	}

	let argsToParse
	if (Array.isArray(args)) {
		argsToParse = process.argv
	} else if (typeof args === 'object') {
		argsToParse = process.argv.slice(0, 2)

		Reflect.ownKeys(args).forEach(key => {
			if (key === 'dir') {
				return
			}
			argsToParse.push('--' + key, args[key])
		})
	}

	console.log('argsToParse', argsToParse)

	flags.version(pkg.version)
		.usage('[path_to_readme]')
		.option('-p, --port [type]', 'HTTP port [port]', 8642)
		.option('-l, --livereloadport [type]', 'LiveReload port [livereloadport]', 35729)
		.option('-i, --silent [type]', 'Silent (no logs to CLI)', false)
		.option('-a, --address [type]', 'Serve on ip/address [address]', 'localhost')
		.option('-b, --browser [type]', 'Launch browser', true)
		.option('-v, --verbose', 'verbose output')
		.action(pathToReadme => {
			pathToReadme = path.resolve(pathToReadme)

			if (flags.dir[0] === '/') {
				flags.dir = pathToReadme
			} else {
				flags.dir = path.normalize(path.join(cwd, pathToReadme))
			}

			const readmeFile = findReadmeFile(flags.dir)
			flags.dir = readmeFile || flags.dir
			flags.$pathProvided = true
		}).parse(argsToParse)

	if (typeof flags.pathToReadme === 'undefined') {
		const readmeFile = findReadmeFile(flags.dir)
		flags.dir = readmeFile || flags.dir
		flags.$pathProvided = true
	}

	console.log('flags', flags)

	return markserv.init(flags)
}

const cli = !module.parent

if (cli) {
	run(process.argv)
} else {
	module.exports = {run}
}

<commit_msg>Trying to fix path issue for testing readme launch.<commit_after>#!/usr/bin/env node

'use strict'

const fs = require('fs')
const path = require('path')
const flags = require('commander')
const splash = require('./splash')

const markserv = require(path.join(__dirname, 'server'))
const pkg = require(path.join(__dirname, '..', './package.json'))

const run = args => {
	splash(args)

	const cwd = process.cwd()
	/* eslint-disable-next-line no-mixed-operators */
	flags.dir = args && args.dir || cwd
	flags.$pathProvided = true

	const fileExistsSync = uri => {
		let exists

		try {
			const stat = fs.statSync(uri)
			if (stat.isFile()) {
				exists = true
			}
		} catch (err) {
			exists = false
		}

		return exists
	}

	const findFileUp = (dir, fileToFind) => {
		const filepath = path.join(dir, fileToFind)
		const existsHere = fileExistsSync(filepath)

		if (dir === path.sep || dir === '.') {
			return false
		}

		if (existsHere) {
			return filepath
		}

		const nextDirUp = path.dirname(dir)
		return findFileUp(nextDirUp, fileToFind)
	}

	const findReadmeFile = dir => {
		const readmeFile = findFileUp(dir, 'README.md') ||
			findFileUp(dir, 'readme.md') ||
			findFileUp(dir, 'README.MD') ||
			findFileUp(dir, 'Readme.md')
		return readmeFile
	}

	flags.version(pkg.version)
		.usage('[path_to_readme]')
		.option('-p, --port [type]', 'HTTP port [port]', 8642)
		.option('-l, --livereloadport [type]', 'LiveReload port [livereloadport]', 35729)
		.option('-i, --silent [type]', 'Silent (no logs to CLI)', false)
		.option('-a, --address [type]', 'Serve on ip/address [address]', 'localhost')
		.option('-b, --browser [type]', 'Launch browser', true)
		.option('-v, --verbose', 'verbose output')
		.action(pathToReadme => {
			pathToReadme = path.resolve(pathToReadme)

			if (flags.dir[0] === '/') {
				flags.dir = pathToReadme
			} else {
				flags.dir = path.normalize(path.join(cwd, pathToReadme))
			}

			const readmeFile = findReadmeFile(flags.dir)
			flags.dir = readmeFile || flags.dir
			flags.$pathProvided = true
		}).parse(process.argv)

	if (!Array.isArray(args) && typeof args === 'object') {
		Reflect.ownKeys(args).forEach(key => {
			flags[key] = args[key]
		})
	}

	if (typeof flags.pathToReadme === 'undefined') {
		const readmeFile = findReadmeFile(flags.dir)
		flags.dir = readmeFile || flags.dir
		flags.$pathProvided = true
		flags.$openLocation = true
	}

	return markserv.init(flags)
}

const cli = !module.parent

if (cli) {
	run()
} else {
	module.exports = {run}
}

<|endoftext|>


<commit_before>angular.module('tournia.controllers', [])

.controller('MatchesCtrl', function($scope, Matches, $ionicModal, $http, $stateParams, Tournaments, Matches) {
    $scope.doRefresh = function() {
        self.updateView();
    };

    Tournaments.getCurrentTournament().then(function(tournament){
        $scope.isLiveScoreAllowed = tournament.isLiveScoreAllowed;
        $scope.isLive2ndCallAllowed = tournament.isLive2ndCallAllowed;
        $scope.currentTournament = tournament;
    });

    self.updateView = function() {
        $scope.isLoading = true;
        if ($scope.view == "upcoming") {
            Matches.getUpcoming().then(function(data){
                $scope.upcomingMatches = data;
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        } else if ($scope.view == "current") {
            Matches.getCurrent().then(function(data){
                $scope.currentMatches = data;
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        } else if ($scope.view == "finished") {
            Matches.getFinished($scope.page).then(function(data){
                if ($scope.page > 0) {
                    $scope.finishedMatches = angular.extend($scope.finishedMatches, data);

                } else {
                    $scope.finishedMatches = data;
                }
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        }
    }

    $scope.setView = function(view) {
        $scope.view = view;
        $scope.page = 0;
        self.updateView();
    }

    // initial load
    $scope.view = 'current';
    $scope.setView($scope.view);

    $scope.loadMoreFinishedMatches = function() {
        $scope.page += 1;
        self.updateView();
    }

    // Match scoring modal //
    $ionicModal.fromTemplateUrl('score-modal.html', {
        scope: $scope,
        animation: 'slide-in-up'
    }).then(function(modal) {
        $scope.scoreModal = modal;
    });
    $scope.openScoreModal = function(match) {
        // define sets
        $scope.scoreModalSets = {};
        for(setNr = 1; setNr <= $scope.currentTournament.nrSets ; setNr++) {
            $scope.scoreModalSets[setNr] = {team1: null, team2: null};
        }

        $scope.scoreModal.show();
        $scope.modalMatch = match;
    };
    // Second call modal //
    $ionicModal.fromTemplateUrl('secondCall-modal.html', {
        scope: $scope,
        animation: 'slide-in-up'
    }).then(function(modal) {
        $scope.secondCallModal = modal;
    });
    $scope.openSecondCallModal = function(match) {
        // define secondCall in players
        match.team1 = {};
        for (playerId in match.team1Players) {
            match.team1[playerId] = {
                playerId: playerId,
                name: match.team1Players[playerId],
                secondCall: false,
            }
        }
        match.team2 = {};
        for (playerId in match.team2Players) {
            match.team2[playerId] = {
                playerId: playerId,
                name: match.team2Players[playerId],
                secondCall: false,
            }
        }

        $scope.secondCallModal.show();
        $scope.modalMatch = match;
    };
    // Other modal functions //
    $scope.closeModal = function() {
        $scope.scoreModal.hide();
        $scope.secondCallModal.hide();
    };
    //Cleanup the modal when we're done with it!
    $scope.$on('$destroy', function() {
        $scope.scoreModal.remove();
        $scope.secondCallModal.remove();
    });
    // Execute action on hide modal
    $scope.$on('modal.hidden', function() {
        // Execute action
    });

    $scope.sendScoreModal = function(match) {
        for(setNr in $scope.scoreModalSets) {
            if (!Matches.checkScore($scope.currentTournament, $scope.scoreModalSets[setNr].team1, $scope.scoreModalSets[setNr].team2)) {
                confirmMsg = 'Set %setNr% is an incorrect score, do you want to continue saving it?';
                if (!confirm(confirmMsg.replace('%setNr%', setNr))) {
                    return;
                }
            }
        }
        Matches.sendScore($scope.currentTournament, match, $scope.scoreModalSets).then(function(data){
            alert(data);
            self.updateView();
        });
        $scope.scoreModal.hide();
    }
    $scope.sendSecondCallModal = function(match) {
        Matches.sendSecondCall($scope.currentTournament, match).then(function(data){
            alert(data);
        });
        $scope.secondCallModal.hide();
    }

})

.controller('RankingsCtrl', function($scope, Rankings) {
    Rankings.getDisciplines().then(function(data){
            $scope.disciplines = data;
        });

    var selectedDisciplineId = 0;
    $scope.selectedDiscipline = "Select discipline";
    $scope.showRanking = function(disciplineId, disciplineName) {
        $scope.isLoading = true;
        selectedDisciplineId = disciplineId;

        $scope.selectedDiscipline = disciplineName;
        Rankings.get(disciplineId).then(function(data){
            $scope.ranking = data;
            $scope.isLoading = false;
            $scope.$broadcast('scroll.refreshComplete');
        });
    }

    $scope.doRefresh = function() {
        if (selectedDisciplineId != 0) {
            $scope.showRanking(selectedDisciplineId, $scope.selectedDiscipline);
        } else {
            $scope.$broadcast('scroll.refreshComplete');
        }
    };
})

.controller('PlayersCtrl', function($scope, Matches) {
    $scope.searchPlayer = function(searchText) {
        if ((searchText == null) || (searchText == '')) {
            $scope.searchMatches = {};
        } else {
            $scope.isLoading = true;
            Matches.searchPlayer(searchText).then(function(data){
                $scope.searchMatches = data;
                $scope.isLoading = false;
            });
        }
    }
})

.controller('AppCtrl', function($scope, $ionicModal, $http, $ionicPopup, $localstorage, authService, $rootScope, $stateParams) {
    // Form data for the login modal
    $scope.loginData = {};

    // Create the login modal that we will use later
    $ionicModal.fromTemplateUrl('templates/login.html', {
        scope: $scope
    }).then(function(modal) {
        $scope.modal = modal;
    });

    // Triggered in the login modal to close it
    $scope.closeLogin = function() {
        $scope.modal.hide();
    };

    // Open the login modal
    $scope.login = function() {
        $scope.modal.show();
//        $scope.loginData.username.trigger('focus');
    };

    // Open the logout alert
    $scope.logout = function() {
        var confirmPopup = $ionicPopup.confirm({
            title: 'Logout',
            template: 'Do you want to logout?'
        }).then(function(res) {
            if (res) {
                $localstorage.setObject('oauth', null);
                $rootScope.isLoggedin = false;
            }
        });
    };

    // Perform the login action when the user submits the login form
    $scope.doLogin = function() {
        self.postData = {
            client_id: oAuthClientId,
            client_secret: oAuthClientSecret,
            grant_type: 'password',
            username: $scope.loginData.username,
            password: $scope.loginData.password
        };

        $http.post(apiUrl +'/oauth/token', postData).
            success(function(data, status, headers, config) {
                $localstorage.setObject('oauth', data);

                $http.defaults.headers.common.Authorization = "Bearer "+ data.access_token;

                // retry requests with new token
                authService.loginConfirmed('success', function(config){
                    config.headers["Authorization"] = "Bearer "+ data.access_token;
                    return config;
                })

                $scope.closeLogin();
            }).
            error(function(data, status, headers, config) {
                console.error('Error while fetching oauth token');

                if (status == 400) {
                    // Show incorrect login alert
                    var alertPopup = $ionicPopup.alert({
                        title: 'Incorrect login',
                        template: 'Your username / password combination is not correct'
                    }).then(function(res) {
                        $scope.loginData.password = '';
                    });
                }
            });
    };

    // Handles incoming device tokens
    $rootScope.$on('$cordovaPush:tokenReceived', function(event, data) {
        //alert("Successfully registered token " + data.token);
        //console.log('Ionic Push: Got token ', data.token, data.platform);
        $scope.token = data.token;
    });

    $rootScope.tournamentUrl = $stateParams.tournamentUrl;
})

.controller('TournamentsCtrl', function($scope, $localstorage, $http, $rootScope) {
    if ($rootScope.isLoggedin) {
        $http.get(apiUrl +'/mytournaments').
            success(function(data, status, headers, config) {
                $scope.mytournaments = data;
            });
    }

    $http.get(apiUrl +'/tournaments').
        success(function(data, status, headers, config) {
            $scope.alltournaments = data;
        });
})

.controller('InfoCtrl', function($scope, $stateParams, $localstorage, $http, $ionicLoading, Tournaments, $stateParams, $cordovaInAppBrowser) {
    $ionicLoading.show({
        templateUrl: 'templates/loadingPane.html'
    });

    $scope.openDesktopSite = function(url) {
        url = $scope.desktopUrl + $stateParams.tournamentUrl;
        $cordovaInAppBrowser.open(url, '_system');
    }

    Tournaments.getCurrentTournament().then(function(tournament){
        $scope.tournament = tournament;
        $ionicLoading.hide();
    });
})

.controller('TabsController', function($scope, $stateParams) {
    $scope.tournamentUrl = $stateParams.tournamentUrl;
})

.controller('SettingsController', function($scope, Settings, $rootScope) {;
    $scope.setNotificationsEnabled = function(enabled) {
        Settings.setNotificationsEnabled(enabled);
    }
    $scope.setNotificationsUpcomingMatch = function(period) {
        Settings.setNotificationsUpcomingMatch(period);
    }

    $scope.setNotificationsScoreMatch = function(enabled) {
        Settings.setNotificationsScoreMatch(enabled);
    }

    $scope.setNotificationsNewMatch = function(enabled) {
        Settings.setNotificationsNewMatch(enabled);
    }

    Settings.getNotifications().then(function(notifications){
        $scope.notifications = notifications;
    });

    /*
    $scope.identifyUser = function() {
        alert('Identifying');
        console.log('Identifying user');

        var user = $ionicUser.get();
        if(!user.user_id) {
            // Set your user_id here, or generate a random one
            user.user_id = $ionicUser.generateGUID()
        };

        angular.extend(user, {
            name: 'Test User',
            message: 'I come from planet Ion'
        });

        $ionicUser.identify(user);

    }
     //$scope.identifyUser();
     */

});
<commit_msg>Update after login<commit_after>angular.module('tournia.controllers', [])

.controller('MatchesCtrl', function($scope, Matches, $ionicModal, $http, $stateParams, Tournaments, Matches) {
    $scope.doRefresh = function() {
        self.updateView();
    };

    Tournaments.getCurrentTournament().then(function(tournament){
        $scope.isLiveScoreAllowed = tournament.isLiveScoreAllowed;
        $scope.isLive2ndCallAllowed = tournament.isLive2ndCallAllowed;
        $scope.currentTournament = tournament;
    });

    self.updateView = function() {
        $scope.isLoading = true;
        if ($scope.view == "upcoming") {
            Matches.getUpcoming().then(function(data){
                $scope.upcomingMatches = data;
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        } else if ($scope.view == "current") {
            Matches.getCurrent().then(function(data){
                $scope.currentMatches = data;
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        } else if ($scope.view == "finished") {
            Matches.getFinished($scope.page).then(function(data){
                if ($scope.page > 0) {
                    $scope.finishedMatches = angular.extend($scope.finishedMatches, data);

                } else {
                    $scope.finishedMatches = data;
                }
                $scope.isLoading = false;
                $scope.$broadcast('scroll.refreshComplete');
            });
        }
    }

    $scope.setView = function(view) {
        $scope.view = view;
        $scope.page = 0;
        self.updateView();
    }

    // initial load
    $scope.view = 'current';
    $scope.setView($scope.view);

    $scope.loadMoreFinishedMatches = function() {
        $scope.page += 1;
        self.updateView();
    }

    // Match scoring modal //
    $ionicModal.fromTemplateUrl('score-modal.html', {
        scope: $scope,
        animation: 'slide-in-up'
    }).then(function(modal) {
        $scope.scoreModal = modal;
    });
    $scope.openScoreModal = function(match) {
        // define sets
        $scope.scoreModalSets = {};
        for(setNr = 1; setNr <= $scope.currentTournament.nrSets ; setNr++) {
            $scope.scoreModalSets[setNr] = {team1: null, team2: null};
        }

        $scope.scoreModal.show();
        $scope.modalMatch = match;
    };
    // Second call modal //
    $ionicModal.fromTemplateUrl('secondCall-modal.html', {
        scope: $scope,
        animation: 'slide-in-up'
    }).then(function(modal) {
        $scope.secondCallModal = modal;
    });
    $scope.openSecondCallModal = function(match) {
        // define secondCall in players
        match.team1 = {};
        for (playerId in match.team1Players) {
            match.team1[playerId] = {
                playerId: playerId,
                name: match.team1Players[playerId],
                secondCall: false,
            }
        }
        match.team2 = {};
        for (playerId in match.team2Players) {
            match.team2[playerId] = {
                playerId: playerId,
                name: match.team2Players[playerId],
                secondCall: false,
            }
        }

        $scope.secondCallModal.show();
        $scope.modalMatch = match;
    };
    // Other modal functions //
    $scope.closeModal = function() {
        $scope.scoreModal.hide();
        $scope.secondCallModal.hide();
    };
    //Cleanup the modal when we're done with it!
    $scope.$on('$destroy', function() {
        $scope.scoreModal.remove();
        $scope.secondCallModal.remove();
    });
    // Execute action on hide modal
    $scope.$on('modal.hidden', function() {
        // Execute action
    });

    $scope.sendScoreModal = function(match) {
        for(setNr in $scope.scoreModalSets) {
            if (!Matches.checkScore($scope.currentTournament, $scope.scoreModalSets[setNr].team1, $scope.scoreModalSets[setNr].team2)) {
                confirmMsg = 'Set %setNr% is an incorrect score, do you want to continue saving it?';
                if (!confirm(confirmMsg.replace('%setNr%', setNr))) {
                    return;
                }
            }
        }
        Matches.sendScore($scope.currentTournament, match, $scope.scoreModalSets).then(function(data){
            alert(data);
            self.updateView();
        });
        $scope.scoreModal.hide();
    }
    $scope.sendSecondCallModal = function(match) {
        Matches.sendSecondCall($scope.currentTournament, match).then(function(data){
            alert(data);
        });
        $scope.secondCallModal.hide();
    }

})

.controller('RankingsCtrl', function($scope, Rankings) {
    Rankings.getDisciplines().then(function(data){
            $scope.disciplines = data;
        });

    var selectedDisciplineId = 0;
    $scope.selectedDiscipline = "Select discipline";
    $scope.showRanking = function(disciplineId, disciplineName) {
        $scope.isLoading = true;
        selectedDisciplineId = disciplineId;

        $scope.selectedDiscipline = disciplineName;
        Rankings.get(disciplineId).then(function(data){
            $scope.ranking = data;
            $scope.isLoading = false;
            $scope.$broadcast('scroll.refreshComplete');
        });
    }

    $scope.doRefresh = function() {
        if (selectedDisciplineId != 0) {
            $scope.showRanking(selectedDisciplineId, $scope.selectedDiscipline);
        } else {
            $scope.$broadcast('scroll.refreshComplete');
        }
    };
})

.controller('PlayersCtrl', function($scope, Matches) {
    $scope.searchPlayer = function(searchText) {
        if ((searchText == null) || (searchText == '')) {
            $scope.searchMatches = {};
        } else {
            $scope.isLoading = true;
            Matches.searchPlayer(searchText).then(function(data){
                $scope.searchMatches = data;
                $scope.isLoading = false;
            });
        }
    }
})

.controller('AppCtrl', function($scope, $ionicModal, $http, $ionicPopup, $localstorage, authService, $rootScope, $stateParams) {
    // Form data for the login modal
    $scope.loginData = {};

    // Create the login modal that we will use later
    $ionicModal.fromTemplateUrl('templates/login.html', {
        scope: $scope
    }).then(function(modal) {
        $scope.modal = modal;
    });

    // Triggered in the login modal to close it
    $scope.closeLogin = function() {
        $scope.modal.hide();
    };

    // Open the login modal
    $scope.login = function() {
        $scope.modal.show();
//        $scope.loginData.username.trigger('focus');
    };

    // Open the logout alert
    $scope.logout = function() {
        var confirmPopup = $ionicPopup.confirm({
            title: 'Logout',
            template: 'Do you want to logout?'
        }).then(function(res) {
            if (res) {
                $localstorage.setObject('oauth', null);
                $rootScope.isLoggedin = false;
            }
        });
    };

    // Perform the login action when the user submits the login form
    $scope.doLogin = function() {
        self.postData = {
            client_id: oAuthClientId,
            client_secret: oAuthClientSecret,
            grant_type: 'password',
            username: $scope.loginData.username,
            password: $scope.loginData.password
        };

        $http.post(apiUrl +'/oauth/token', postData).
            success(function(data, status, headers, config) {
                $localstorage.setObject('oauth', data);

                $http.defaults.headers.common.Authorization = "Bearer "+ data.access_token;

                // retry requests with new token
                authService.loginConfirmed('success', function(config){
                    config.headers["Authorization"] = "Bearer "+ data.access_token;
                    return config;
                })

                $scope.closeLogin();
            }).
            error(function(data, status, headers, config) {
                console.error('Error while fetching oauth token');

                if (status == 400) {
                    // Show incorrect login alert
                    var alertPopup = $ionicPopup.alert({
                        title: 'Incorrect login',
                        template: 'Your username / password combination is not correct'
                    }).then(function(res) {
                        $scope.loginData.password = '';
                    });
                }
            });
    };

    // Handles incoming device tokens
    $rootScope.$on('$cordovaPush:tokenReceived', function(event, data) {
        //alert("Successfully registered token " + data.token);
        //console.log('Ionic Push: Got token ', data.token, data.platform);
        $scope.token = data.token;
    });

    $rootScope.tournamentUrl = $stateParams.tournamentUrl;
})

.controller('TournamentsCtrl', function($scope, $localstorage, $http, $rootScope) {
    updateMyTournaments = function() {
        $http.get(apiUrl +'/mytournaments').
        success(function(data, status, headers, config) {
            $scope.mytournaments = data;
        });
    }
    if ($rootScope.isLoggedin) {
        self.updateMyTournaments();
    }
    $rootScope.$on('event:auth-loginConfirmed', function(event, data){
        self.updateMyTournaments();
    });

    $http.get(apiUrl +'/tournaments').
        success(function(data, status, headers, config) {
            $scope.alltournaments = data;
        });
})

.controller('InfoCtrl', function($scope, $stateParams, $localstorage, $http, $ionicLoading, Tournaments, $stateParams, $cordovaInAppBrowser) {
    $ionicLoading.show({
        templateUrl: 'templates/loadingPane.html'
    });

    $scope.openDesktopSite = function(url) {
        url = $scope.desktopUrl + $stateParams.tournamentUrl;
        $cordovaInAppBrowser.open(url, '_system');
    }

    Tournaments.getCurrentTournament().then(function(tournament){
        $scope.tournament = tournament;
        $ionicLoading.hide();
    });
})

.controller('TabsController', function($scope, $stateParams) {
    $scope.tournamentUrl = $stateParams.tournamentUrl;
})

.controller('SettingsController', function($scope, Settings, $rootScope) {;
    $scope.setNotificationsEnabled = function(enabled) {
        Settings.setNotificationsEnabled(enabled);
    }
    $scope.setNotificationsUpcomingMatch = function(period) {
        Settings.setNotificationsUpcomingMatch(period);
    }

    $scope.setNotificationsScoreMatch = function(enabled) {
        Settings.setNotificationsScoreMatch(enabled);
    }

    $scope.setNotificationsNewMatch = function(enabled) {
        Settings.setNotificationsNewMatch(enabled);
    }

    updateSettings = function() {
        Settings.getNotifications().then(function(notifications){
            $scope.notifications = notifications;
        });
    }
    if ($rootScope.isLoggedin) {
        self.updateSettings();
    }
    $rootScope.$on('event:auth-loginConfirmed', function(event, data){
        self.updateSettings();
    });

    /*
    $scope.identifyUser = function() {
        alert('Identifying');
        console.log('Identifying user');

        var user = $ionicUser.get();
        if(!user.user_id) {
            // Set your user_id here, or generate a random one
            user.user_id = $ionicUser.generateGUID()
        };

        angular.extend(user, {
            name: 'Test User',
            message: 'I come from planet Ion'
        });

        $ionicUser.identify(user);

    }
     //$scope.identifyUser();
     */

});
<|endoftext|>


<commit_before>

module.exports = function (doc, rc) {

  var c = require('charm')(process.stdout)

  var renderers = []
  var offset = 0

  var height = rc.height

  function render (line, x, y) {
    if(!line) return

    //don't render the '\n'. this can mess-up escape codes.
    line = line.substring(0, line.length - 1)

    //iterate over the renderers, each gets to modify the line.
    renderers.forEach(function (render) {
      if(!render) return
      var l = render(line, x, y)
      if(l) line = l
    })
    return line
  }

  function updateLine(line, x, y, noErase) {
    if(y < offset || y > offset + height)
      return
    if(!noErase)
      c.position(1, y - offset).erase('line')
    line = render(line, x, y)
    c.write(line).display('reset').write('\n')
  }

  function redraw (_, x, y) {
    c.reset()
    for (var i = offset; i < offset + height && doc.lines[i]; i++)
      if(i < doc.lines.length) updateLine(doc.lines[i], x, i + 1, true)
  }
  function eraseLine (line, x, y) {
    c.position(1, y - offset).erase('line')
  }

  function deleteLine (line, x, y) {
    c.position(1, y - offset)
     .delete('line')
    //'when the document is shortened, clear the last line
    updateLine(doc.lines[offset + height], 1, offset + height)
  }

  function smaller (m, M) {
    if(!m) return false
    return m.y < M.y ? true : m.x < M.x
  }

  var _min, _max
  function updateMark (min, max) {
    var m = smaller(_min, min) ? _min : min
    var M = smaller(_max, max) ? _max : max
    for(var i = m.y; i <= M.y; i ++)
      updateLine(doc.lines[i], 1, i+1)
    doc.move()
    _min = min; _max = max
  }

  function newLine (line, x, y) {
    c.position(1, y - offset)
      .insert('line')
    updateLine(line, x, y, true)
    eraseLine('', 1, offset + height + 1)
  }

  doc.on('update_line', updateLine)
  doc.on('redraw', redraw)
  doc.on('new_line', newLine)
  doc.on('mark', updateMark)
  doc.on('unmark', updateMark)
  doc.on('delete_line', deleteLine)

  //cursor has moved.
  function cursor (line, x, y) {
    scroll(line, x, y)
    c.position(x + rc.margin, y - offset)
  }

  doc.on('cursor', cursor)
  c.cursor(true)

  function scroll (line, x, y) {
    var target = offset
    //there is an off by one error in here somewhere.
    //when I scroll down,
    if (y - (offset + height) > 0)
      target = y - height
    else if (y - offset<= 0)
      target = y - 1

    //if there was lots of scrolling, redraw the whole screen
    if(Math.abs(target - offset) >= rc.page) {
      offset = target
      return redraw()
    }

    //there are event listeners that pop off the lines at the other end
    //when scrolling happens.
    if(target != offset) {
      var sa = Math.abs(target - offset)
      var i = 1
      while(offset !== target) {
        if(target > offset) {
          //scrolling down, delete line from TOP.
          deleteLine('', 1, 1 + offset)
          offset ++
        }
        else if(target < offset){
          //scrolling up, add line to top.
          newLine(doc.lines[offset - 1], 1, offset)
          offset --
        }
      } while(offset !== target);
    }
  }

  //clear the screen after exiting
  process.on('exit', function () {
    c.reset()
  })

  return {
    redraw: redraw,
    updateLine: updateLine,
    renderers: renderers,
    write: c.write.bind(c),
    reset: c.reset.bind(c),
    cursor: function (x, y) {
      cursor('', x, y)
    }
  }
}
<commit_msg>don't update empty line<commit_after>

module.exports = function (doc, rc) {

  var c = require('charm')(process.stdout)

  var renderers = []
  var offset = 0

  var height = rc.height

  function render (line, x, y) {
    if(!line) return

    //don't render the '\n'. this can mess-up escape codes.
    line = line.substring(0, line.length - 1)

    //iterate over the renderers, each gets to modify the line.
    renderers.forEach(function (render) {
      if(!render) return
      var l = render(line, x, y)
      if(l) line = l
    })
    return line
  }

  function updateLine(line, x, y, noErase) {
    if(y < offset || y > offset + height)
      return
    if(!line) return
    if(!noErase)
      c.position(1, y - offset).erase('line')
    line = render(line, x, y)
    c.write(line).display('reset').write('\n')
  }

  function redraw (_, x, y) {
    c.reset()
    for (var i = offset; i < offset + height && doc.lines[i]; i++)
      if(i < doc.lines.length) updateLine(doc.lines[i], x, i + 1, true)
  }
  function eraseLine (line, x, y) {
    c.position(1, y - offset).erase('line')
  }

  function deleteLine (line, x, y) {
    c.position(1, y - offset)
     .delete('line')
    //'when the document is shortened, clear the last line
    updateLine(doc.lines[offset + height], 1, offset + height)
  }

  function smaller (m, M) {
    if(!m) return false
    return m.y < M.y ? true : m.x < M.x
  }

  var _min, _max
  function updateMark (min, max) {
    var m = smaller(_min, min) ? _min : min
    var M = smaller(_max, max) ? _max : max
    for(var i = m.y; i <= M.y; i ++)
      updateLine(doc.lines[i], 1, i+1)
    doc.move()
    _min = min; _max = max
  }

  function newLine (line, x, y) {
    c.position(1, y - offset)
      .insert('line')
    updateLine(line, x, y, true)
    eraseLine('', 1, offset + height + 1)
  }

  doc.on('update_line', updateLine)
  doc.on('redraw', redraw)
  doc.on('new_line', newLine)
  doc.on('mark', updateMark)
  doc.on('unmark', updateMark)
  doc.on('delete_line', deleteLine)

  //cursor has moved.
  function cursor (line, x, y) {
    scroll(line, x, y)
    c.position(x + rc.margin, y - offset)
  }

  doc.on('cursor', cursor)
  c.cursor(true)

  function scroll (line, x, y) {
    var target = offset
    //there is an off by one error in here somewhere.
    //when I scroll down,
    if (y - (offset + height) > 0)
      target = y - height
    else if (y - offset<= 0)
      target = y - 1

    //if there was lots of scrolling, redraw the whole screen
    if(Math.abs(target - offset) >= rc.page) {
      offset = target
      return redraw()
    }

    //there are event listeners that pop off the lines at the other end
    //when scrolling happens.
    if(target != offset) {
      var sa = Math.abs(target - offset)
      var i = 1
      while(offset !== target) {
        if(target > offset) {
          //scrolling down, delete line from TOP.
          deleteLine('', 1, 1 + offset)
          offset ++
        }
        else if(target < offset){
          //scrolling up, add line to top.
          newLine(doc.lines[offset - 1], 1, offset)
          offset --
        }
      } while(offset !== target);
    }
  }

  //clear the screen after exiting
  process.on('exit', function () {
    c.reset()
  })

  return {
    redraw: redraw,
    updateLine: updateLine,
    renderers: renderers,
    write: c.write.bind(c),
    reset: c.reset.bind(c),
    cursor: function (x, y) {
      cursor('', x, y)
    }
  }
}
<|endoftext|>


<commit_before>import './css/general.scss'
import './css/header.scss'
import './css/content.scss'
import './css/nav.scss'
import './css/kemo.scss'

import language from './language.json';

const words = (() => {
  // Detect the language of the wiki page
  switch (/https:\/\/(.*?).wiki/.exec(document.URL)[1]) {
    case "en" : return language.en; break;
    case "jp" : return language.jp; break;
    default: return language.jp; break;
  }
})()

const sayRate = 0.3

const randomPick = list => list[Math.floor(Math.random() * list.length)]

const kemoInnerHTML = list => `<span class="kemo-say">＼${randomPick(list)}／</span>`

const japarizeWikipedia = () => {
  const headPattern = 'wikipedia'
  const contextPatterns = [
    'wikipedia',
    'ウィキペディア',
  ]
  const after = 'ジャパリ図書館'

  // document title
  document.title = document.title.replace(
    new RegExp(`${headPattern}$`, 'i'),
    after
  )

  // search props
  const searchInput = document.getElementById('searchInput')
  const props = ['placeholder', 'title']
  const searchRegex = new RegExp(headPattern, 'i')
  props.forEach((prop) => {
    searchInput[prop] = searchInput[prop].replace(searchRegex, after)
  })

  // text content
  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT,
    null,
    false
  )

  let n
  const contextRegex = new RegExp(`(${contextPatterns.join('|')})`, 'gi')
  while ((n = walker.nextNode())) {
    n.textContent = n.textContent.replace(contextRegex, after)
  }
}

const japarizeHeader = () => {
  document
  .querySelectorAll('.mw-headline, #mw-panel h3')
  .forEach(el => (el.textContent = words.titlemin + el.textContent))
}

const becomeFriendWithSectionTitle = () => {
  const head = document.getElementById('firstHeading')
  head.textContent = [
    randomPick(words.exHeadList),
    randomPick(words.preHeadList),
    head.textContent,
    randomPick(words.postHeadList),
  ].join(' ')
}

const insertWordsInContent = () => {
  document
  .querySelectorAll('#mw-content-text > p')
  .forEach((para) => {
    let sentence = para.innerHTML.match(/(.+?[。]|.+?\. )(?!([^<]+)?>)/g)
    if (!sentence) {
      return
    }

    const last = sentence.length - 1
    sentence = sentence
    .map((s, i) => (
      (i !== last && Math.random() < sayRate)
        ? s + ' ' + kemoInnerHTML(words.postPeriodList)
        : s
      )
    )

    if (Math.random() < sayRate) {
      sentence.push(' ' + kemoInnerHTML(words.postParaList))
    }

    para.innerHTML = sentence.join('')
  })
}

export default () => {
  japarizeWikipedia()
  japarizeHeader()
  becomeFriendWithSectionTitle()
  insertWordsInContent()
}
<commit_msg>🐙 Making Japari-pedia more i18n friendly<commit_after>import './css/general.scss'
import './css/header.scss'
import './css/content.scss'
import './css/nav.scss'
import './css/kemo.scss'
import language from './language.yml'

const defaultLang = 'en'
const lang = document.documentElement.lang
const words = Object.prototype.hasOwnProperty.call(language, lang)
? language[lang]
: language[defaultLang]

const sayRate = 0.3

const randomPick = list => list[Math.floor(Math.random() * list.length)]
const kemoInnerHTML = list => `<span class="kemo-say">＼${randomPick(list)}／</span>`
const escapeRegExp = str => str.replace(/[\\^$.*+?()[\]{}|]/g, '\\$&')

const japarizeWikipedia = () => {
  const wikipedia = 'wikipedia'
  const contextPatterns = [
    wikipedia,
    words.wikipedia,
  ]

  // document title
  document.title = document.title.replace(
    new RegExp(`${wikipedia}$`, 'i'),
    words.title
  )

  // search props
  const searchInput = document.getElementById('searchInput')
  const props = ['placeholder', 'title']
  const searchRegex = new RegExp(wikipedia, 'i')
  props.forEach((prop) => {
    searchInput[prop] = searchInput[prop].replace(searchRegex, words.title)
  })

  // text content
  const walker = document.createTreeWalker(
    document.body,
    NodeFilter.SHOW_TEXT,
    null,
    false
  )

  let n
  const contextRegex = new RegExp(`(${contextPatterns.join('|')})`, 'gi')
  while ((n = walker.nextNode())) {
    n.textContent = n.textContent.replace(contextRegex, words.title)
  }
}

const japarizeHeader = () => {
  document
  .querySelectorAll('.mw-headline, #mw-panel h3')
  .forEach(el => (el.textContent = words.titlemin + el.textContent))
}

const becomeFriendWithSectionTitle = () => {
  const head = document.getElementById('firstHeading')
  head.textContent = [
    randomPick(words.exHeadList),
    randomPick(words.preHeadList),
    head.textContent,
    randomPick(words.postHeadList),
  ].join(' ')
}

const insertWordsInContent = () => {
  const regex = new RegExp(`(.+?${escapeRegExp(words.delimiter)})(?!([^<]+)?>)`, 'g')
  document
  .querySelectorAll('#mw-content-text > p')
  .forEach((para) => {
    let sentence = para.innerHTML.match(regex)
    if (!sentence) {
      return
    }

    const last = sentence.length - 1
    sentence = sentence
    .map((s, i) => (
      (i !== last && Math.random() < sayRate)
        ? `${s}${kemoInnerHTML(words.postPeriodList)}`
        : s
      )
    )

    if (Math.random() < sayRate) {
      sentence.push(kemoInnerHTML(words.postParaList))
    }

    para.innerHTML = sentence.join('')
  })
}

export default () => {
  japarizeWikipedia()
  japarizeHeader()
  becomeFriendWithSectionTitle()
  insertWordsInContent()
}
<|endoftext|>


<commit_before>import React                from 'react';
import { bindAll }          from '../helpers';

export default function hoverable(ComposedComponent) {
  return class extends React.Component {
    static propTypes = {
      onHoverStart:           React.PropTypes.func,
      onHoverStop:            React.PropTypes.func,
    };

    constructor(props) {
      super(props);
      this.state = { hovering: null };
      bindAll(this, [
        'onHoverStart',
        'onHoverStop',
      ]);
    }

    render() {
      return (
        <ComposedComponent
          {...this.props}
          hovering={this.state.hovering}
          onHoverStart={this.onHoverStart}
          onHoverStop={this.onHoverStop}
        />
      );
    }

    onHoverStart(ev) {
      const id = ev.currentTarget.id || true;
      this.setState({ hovering: id });
      if (this.props.onHoverStart) this.props.onHoverStart(ev);
    }

    onHoverStop(ev) {
      this.setState({ hovering: null });
      if (this.props.onHoverStop) this.props.onHoverStop(ev);
    }
  };
}
<commit_msg>Minor<commit_after><|endoftext|>