<commit_before>
// Setup depends on: -
// Runtime depends on: console.js, helpers.js

// API:
// st.communication.send(data, handlerId)
// st.communication.sendForked(data, handlerId)
// st.communication.addMessageHandler(string, function)
// st.communication.getMessageHandler(string)

// (st.setup_session_id(int) can be called only once)

// Settings:
// st.communication.MESSAGE_PATH (string)

(function() {

	// Set up the namespace
	if (!window.st) window.st = {};
	var home = st.communication ? st.communication : (st.communication = {});

	// 
	// Settings
	// 

	if (!("MESSAGE_PATH" in home)) home.MESSAGE_PATH = "message";

	// 
	// Local variables
	// 

	var messageHandlers = [];
	var session_id = -1;

	// 
	// API functions
	// 

	home.send = function(data, handlerId) {
		awaitedAnswers++;
		return doSend(data, true, "blocked", handlerId);
	};

	home.sendForked = function(data, handlerId) {
		// No meaningfull result-value when sending forked
		awaitedAnswers++;
		doSend(data, false, "forked", handlerId, true);
		return null;
	};

	home.addMessageHandler = function(handlerId, handlerFunction) {
		messageHandlers[handlerId] = handlerFunction;
	};

	home.getMessageHandler = function(handlerId) {
		return messageHandlers[handlerId];
	}

	home.setup_session_id = function(id) {
		// Allow calling this function only once - delete after usage
		delete home.setup_session_id;
		st.console.log("This session-id is " + id);
		session_id = id;
		openLongPoll(); // Open the connection initially
	};

	// 
	// Private functions
	// 

	// Set up default handler
	home.addMessageHandler("default",
		function(messageString) { st.console.log("Received unhandled message: " + messageString); });
	home.addMessageHandler("code",
		function(messageString) { return st.globalEval(messageString); });

	// Use the configured message-handler to evaluate and log the content
	var handleMessage = function(content, handlerId) {
		var result;
		try {
			st.console.log("Received message to '" + handlerId + "': " + content);
			var handler = messageHandlers[handlerId];
			if (!handler) {
				// Use the default handler or do nothing by default
				handler = messageHandlers["default"];
			}
			if (handler)
				result = handler(content);
		} catch (e) {
			st.console.log("Error handling the content of a server-message: " + e + ".\r\n" +
			"Message was: " + content);
		}
		return result;
	};

	// Increased with every send and decreased with every received "answer"
	var awaitedAnswers = 0;

	var openLongPoll = function() {
		sendAndHandleResponse("", false, "longPoll", "default", function(request) {
			// If the server answeres the long-poll-request,
			// it must be renewed immediately to allow it to send more requests.
			// In addition to reopening the long-poll, the server-message is handled.
			// This handling could result in another connection being opened,
			// especially when the server-request was a blocking send and the client sends an answer.
			openLongPoll();
			return answerToMessage(request);
		});
	}

	var doSend = function(data, isSynchronous, status, handlerId, ignoreResponse) {
		return sendAndHandleResponse(data, isSynchronous, status, handlerId, ignoreResponse ? null : answerToMessage);
	}

	var sendAndHandleResponse = function(data, isSynchronous, status, handlerId, responseHandlerFunction) {
		if (session_id == -1) {
			throw "Session-ID has not been set up yet! Cannot send.";
		}
		var url = st.fullURL(home.MESSAGE_PATH) + "?id=" + session_id;
		
		var request = st.createRequest();
		var content = "status=" + st.escapeAll(status);
		content += "&message=" + st.escapeAll(data);
		content += "&handlerId=" + st.escapeAll(handlerId ? handlerId : "default");
		if (responseHandlerFunction && !isSynchronous)
			request.onreadystatechange = function() { responseHandlerFunction(request); };
		request.open("POST", url, !isSynchronous);
		request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		st.console.log("Sending (synchronous: " +isSynchronous + ", awaited answers: " + awaitedAnswers + ") " + status + " to " + handlerId + ": " + data);
		request.send(content);
		if (responseHandlerFunction && isSynchronous)
			return responseHandlerFunction(request);
		return request.responseText;
	}

	// Answering a message can recursively cause doing more sends
	// (either by sending the answer or from inside a message handler)
	// The execution stack will preserve the info, where to return after all the sends
	var answerToMessage = function(request) {
		if (request.readyState == 4) {
			if (request.status == 200) {
				var response = /status=([^&]*)&handlerId=([^&]*)&message=([^&]*)/.exec(request.responseText);
				if (response && response.length >= 2) {
					var status = unescape(response[1]);
					var handlerId = unescape(response[2]);
					var message = unescape(response[3]);
					if (status == "empty") {
						// do nothing. The server has answered to a request,
						// that has no meaningfull answer-semanitcs.
						// Mainly this happens in return to an answer sent from the client.
						return message;
					} else if (status == "answer") {
						// "answerTo: (answer)"
						console.log("Received answer: " + message);
						awaitedAnswers--;
						if (awaitedAnswers < 0) {
							awaitedAnswers = 0;
							st.console.log("Illegal state: Received more answers than sends!");
						}
						return message; // No new connection
					} else if (status == "blocked") {
						// "answerTo: (blocked)"
						var result = handleMessage(message, handlerId);
						if (awaitedAnswers > 0) {
							return doSend(result, true, "answer");
						} else {
							return doSend(result, false, "answer");
						}
					} else if (status == "forked") {
						// "answerTo: (forked)"
						if (awaitedAnswers > 0) {
							// Inside any blocking send from the client, a forked
							// send from the server becomes blocking, because we need
							// to keep the context on the client intact.
							handleMessage(message, handlerId); // Ignore result
							return doSend("", true, "answer");
						} else {
							var result = doSend("", false , "answer");
							handleMessage(message, handlerId); // Ignore result
							return result;
						}
					}
				}
				st.console.log("Illegal message received from the server: " + request.responseText);
			} else if (request.status == 299) { // Server-request to reconnect (timeout), not 408 since that's displayed as error in chrome
				st.console.statusInfo("Server-timeout, reconnecting. (Opening server-send connection)", 229);
				// TODO Server needs to send info, which kind of connection is timed out (synchronous or not)
				// re-open same kind of connection here
				doSend("", false, "forked");
			} else {
				st.console.statusInfo("Channel disconnected: " + request.responseText, request.status);
			}
		}
	}

})();<commit_msg>fixing the communication-management.<commit_after>
// Setup depends on: -
// Runtime depends on: console.js, helpers.js

// API:
// st.communication.send(data, handlerId)
// st.communication.sendForked(data, handlerId)
// st.communication.addMessageHandler(string, function)
// st.communication.getMessageHandler(string)

// (st.setup_session_id(int) can be called only once)

// Settings:
// st.communication.MESSAGE_PATH (string)

(function() {

	// Set up the namespace
	if (!window.st) window.st = {};
	var home = st.communication ? st.communication : (st.communication = {});

	// 
	// Settings
	// 

	if (!("MESSAGE_PATH" in home)) home.MESSAGE_PATH = "message";

	// 
	// Local variables
	// 

	var messageHandlers = [];
	var session_id = -1;

	// 
	// API functions
	// 

	home.send = function(data, handlerId) {
		awaitedAnswers++;
		return doSend(data, true, "blocked", handlerId);
	};

	home.sendForked = function(data, handlerId) {
		// No meaningfull result-value when sending forked
		awaitedAnswers++;
		doSend(data, false, "forked", handlerId, true);
		return null;
	};

	home.addMessageHandler = function(handlerId, handlerFunction) {
		messageHandlers[handlerId] = handlerFunction;
	};

	home.getMessageHandler = function(handlerId) {
		return messageHandlers[handlerId];
	}

	home.setup_session_id = function(id) {
		// Allow calling this function only once - delete after usage
		delete home.setup_session_id;
		st.console.log("This session-id is " + id);
		session_id = id;
		openLongPoll(); // Open the connection initially
	};

	// 
	// Private functions
	// 

	// Set up default handler
	home.addMessageHandler("default",
		function(messageString) { st.console.log("Received unhandled message: " + messageString); });
	home.addMessageHandler("code",
		function(messageString) { return st.globalEval(messageString); });

	// Use the configured message-handler to evaluate and log the content
	var handleMessage = function(content, handlerId) {
		var result;
		try {
			st.console.log("Received message to '" + handlerId + "': " + content);
			var handler = messageHandlers[handlerId];
			if (!handler) {
				// Use the default handler or do nothing by default
				handler = messageHandlers["default"];
			}
			if (handler)
				result = handler(content);
		} catch (e) {
			st.console.log("Error handling the content of a server-message: " + e + ".\r\n" +
			"Message was: " + content);
		}
		return result;
	};

	// Increased with every send and decreased with every received "answer"
	var awaitedAnswers = 0;

	var openLongPoll = function() {
		sendAndHandleResponse("", false, "longPoll", "default", function(request) {
			// If the server answeres the long-poll-request,
			// it must be renewed immediately to allow it to send more requests.
			// In addition to reopening the long-poll, the server-message is handled.
			// This handling could result in another connection being opened,
			// especially when the server-request was a blocking send and the client sends an answer.
			openLongPoll();
			return answerToMessage(request);
		});
	}

	var doSend = function(data, isSynchronous, status, handlerId, ignoreResponse) {
		return sendAndHandleResponse(data, isSynchronous, status, handlerId, ignoreResponse ? null : answerToMessage);
	}

	var sendAndHandleResponse = function(data, isSynchronous, status, handlerId, responseHandlerFunction) {
		if (session_id == -1) {
			throw "Session-ID has not been set up yet! Cannot send.";
		}
		var url = st.fullURL(home.MESSAGE_PATH) + "?id=" + session_id;
		
		var request = st.createRequest();
		var content = "status=" + st.escapeAll(status);
		content += "&message=" + st.escapeAll(data);
		content += "&handlerId=" + st.escapeAll(handlerId ? handlerId : "default");
		if (responseHandlerFunction && !isSynchronous)
			request.onreadystatechange = function() { responseHandlerFunction(request); };
		request.open("POST", url, !isSynchronous);
		request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
		st.console.log("Sending (synchronous: " + isSynchronous + ", awaited answers: " + awaitedAnswers + ") " + status + " to " + handlerId + ": " + data);
		request.send(content);
		if (responseHandlerFunction && isSynchronous)
			return responseHandlerFunction(request);
		return request.responseText;
	}

	// Answering a message can recursively cause doing more sends
	// (either by sending the answer or from inside a message handler)
	// The execution stack will preserve the info, where to return after all the sends
	var answerToMessage = function(request) {
		if (request.readyState == 4) {
			if (request.status == 200) {
				var response = /status=([^&]*)&handlerId=([^&]*)&message=([^&]*)/.exec(request.responseText);
				if (response && response.length >= 2) {
					var status = unescape(response[1]);
					var handlerId = unescape(response[2]);
					var message = unescape(response[3]);
					if (status == "empty") {
						// do nothing. The server has answered to a request,
						// that has no meaningfull answer-semanitcs.
						// Mainly this happens in return to an answer sent from the client.
						return message;
					else if (status == "renewLongPoll") {
						// The long-poll-connection has timed out. Reopen it to show that we're still alive.
						openLongPoll();
					} else if (status == "answer") {
						// "answerTo: (answer)"
						console.log("Received answer: " + message);
						awaitedAnswers--;
						if (awaitedAnswers < 0) {
							awaitedAnswers = 0;
							st.console.log("Illegal state: Received more answers than sends!");
						}
						return message; // No new connection
					} else if (status == "blocked") {
						// "answerTo: (blocked)"
						var result = handleMessage(message, handlerId);
						if (awaitedAnswers > 0) {
							return doSend(result, true, "answer");
						} else {
							return doSend(result, false, "answer");
						}
					} else if (status == "forked") {
						// "answerTo: (forked)"
						if (awaitedAnswers > 0) {
							// Inside any blocking send from the client, a forked
							// send from the server becomes blocking, because we need
							// to keep the context on the client intact.
							handleMessage(message, handlerId); // Ignore result
							return doSend("", true, "answer");
						} else {
							var result = doSend("", false , "answer");
							handleMessage(message, handlerId); // Ignore result
							return result;
						}
					}
				}
				st.console.log("Illegal message received from the server: " + request.responseText);
			} else {
				st.console.statusInfo("Channel disconnected (" + 
					(request.responseText ? ("text: " + request.responseText) : "no text")
					+ "). Reconnecting..."
				, request.status);
				// TODO This is maybe not good; the long poll connection might be still up.
				// Maybe just drop reconnecting here, something has gone wrong after all.
				openLongPoll();
			}
		}
	}

})();
{'fraction_non_alphanumeric': 0.13028654334421472, 'fraction_numerical': 0.0028291621327529923, 'mean_word_length': 3.3974481658692186, 'pattern_counts': {'":': 2, '<': 5, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '69845', 'n_tokens_mistral': 4838, 'n_tokens_neox': 4027, 'n_words': 1207}