ChatLogView.js

// 
/*
	simulate chat room with archived logs - currently in experimental stages
	affects:
		
			
			
		
	
	
	installation:
		Chat Logs Viewer Entry Point
*/
/* defie signle object to functions */
ChatLogView = {};
ChatLogView.lastDate = null;
/* markup starts here */
// Chat Logs Viewer Entry Point
$("#chat-log-view").replaceWith('<fieldset>\n\
\t<legend>Search for chat logs</legend>\n\
\t<form id="chat-logs-form">\n\
\t\t<img id="chat-logs-form-error" src="http://www.famfamfam.com/lab/icons/mini/icons/icon_alert.gif" style="display: none;" title="Your date input is not valid. Please try again." />\n\
\t\t<select name="chat-logs-form-mm">\n\
\t\t\t<option value="none">-- Pick a Month --</option>\n\
\t\t\t<option value="January">January</option>\n\
\t\t\t<option value="February">February</option>\n\
\t\t\t<option value="March">March</option>\n\
\t\t\t<option value="April">April</option>\n\
\t\t\t<option value="May">May</option>\n\
\t\t\t<option value="June">June</option>\n\
\t\t\t<option value="July">July</option>\n\
\t\t\t<option value="August">August</option>\n\
\t\t\t<option value="September">September</option>\n\
\t\t\t<option value="October">October</option>\n\
\t\t\t<option value="November">November</option>\n\
\t\t\t<option value="December">December</option>\n\
\t\t</select>\n\
\t\t<select name="chat-logs-form-dd">\n\
\t\t\t<option value="none">-- Pick a Day --</option>\n\
\t\t\t<option value="01">01</option>\n\
\t\t\t<option value="02">02</option>\n\
\t\t\t<option value="03">03</option>\n\
\t\t\t<option value="04">04</option>\n\
\t\t\t<option value="05">05</option>\n\
\t\t\t<option value="06">06</option>\n\
\t\t\t<option value="07">07</option>\n\
\t\t\t<option value="08">08</option>\n\
\t\t\t<option value="09">09</option>\n\
\t\t\t<option value="10">10</option>\n\
\t\t\t<option value="11">11</option>\n\
\t\t\t<option value="12">12</option>\n\
\t\t\t<option value="13">13</option>\n\
\t\t\t<option value="14">14</option>\n\
\t\t\t<option value="15">15</option>\n\
\t\t\t<option value="16">16</option>\n\
\t\t\t<option value="17">17</option>\n\
\t\t\t<option value="18">18</option>\n\
\t\t\t<option value="19">19</option>\n\
\t\t\t<option value="20">20</option>\n\
\t\t\t<option value="21">21</option>\n\
\t\t\t<option value="22">22</option>\n\
\t\t\t<option value="23">23</option>\n\
\t\t\t<option value="24">24</option>\n\
\t\t\t<option value="25">25</option>\n\
\t\t\t<option value="26">26</option>\n\
\t\t\t<option value="27">27</option>\n\
\t\t\t<option value="28">28</option>\n\
\t\t\t<option value="29">29</option>\n\
\t\t\t<option value="30">30</option>\n\
\t\t\t<option value="31">31</option>\n\
\t\t</select>\n\
\t\t<select name="chat-logs-form-yy">\n\
\t\t\t<option value="none">-- Pick a Year --</option>\n\
\t\t\t<option value="2014">2014</option>\n\
\t\t\t<option value="2013">2013</option>\n\
\t\t\t<option value="2012">2012</option>\n\
\t\t</select>\n\
\t\t\n\
\t\t<input type="button" value="Go" id="chat-logs-form-go" />\n\
\t</form>\n\
</fieldset>\n\
 Results \n\
\n\
\t\n\
\t\n\
');
/* functions start here */
// escape regex - by CoolAJ86 http://stackoverflow.com/questions/3446170/escape-string-for-use-in-javascript-regex#answer-6969486
ChatLogView.escape = function(s) {
	return s.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
// avatar loader
ChatLogView.getAvatars = function(users) {
	$.getJSON("/api.php?action=query&format=json&list=users&ususers=" + users.join("|"), function(data) {
		var a = data.query.users,
			b = [];
		for (var i = 0; i < a.length; i++) {
			b.push(a[i].userid);
			if (i + 1 == a.length) {
				var getAvatarUrls = function(ids) {
					$.getJSON("/api/v1/User/Details?ids=" + ids.splice(0,50).join(","), function(data) {
			window.FOO = data; // ALERT
						var items = data.items;
						for (var i = 0; i < items.length; i++) {
							console.log("Setting avatar image to " + items[i].title + ".");
							$('#chat-logs-container img.avatar[data-user="' + items[i].title.replace(/_/g," ") + '"]').each(function() {
								$(this)
									.attr("src", items[i].avatar)
									.removeAttr("data-user").
									error(function() {
										$(this).attr(
											"src",
											$(this).attr("src").split("?")[0] + "?cb=" + new Date().getTime()
										);
									});
							});
							if (i + 1 == items.length) {
								if (ids.length == 0) {
									$("#chat-logs-container > div").html("");
								} else {
									getAvatarUrls(ids);
								}
							}
						}
					});
				}
				getAvatarUrls(b);
			}
		}
	});
}
// creator
ChatLogView.getLogs = function(dd,mm,yy) {
	$("div#chat-logs-container > div").html('<img src="http://upload.wikimedia.org/wikipedia/commons/2/2a/Loading_Key.gif" />');
	var logPagePath = (String(dd).length == 1 ? String("0" + dd) : String(dd)) + "_" + mm + "_" + yy;
	$.getJSON("/api.php?action=query&format=json&prop=revisions&titles=Club_Penguin_Wiki:Chat/Logs/" + logPagePath + "&rvprop=content&cb=" + new Date().getTime(), function(data) {
		var cotentRawObject = data.query.pages;
		if (typeof cotentRawObject["-1"] === "object") {
			// logs for the inserted value don't exist
			$("div#chat-logs-container > div").html('Chat logs for ' + mm + ' ' + dd + ', ' + yy + ' don\'t exist. Please try a different date');
		} else {
			// chat logs exist - find the content and process it
			// usage of web workers might be better than executing on the same page
			for (var pageid in cotentRawObject) {
				contentRaw = cotentRawObject[pageid].revisions[0]["*"],
					content = contentRaw.split('<pre class=\"ChatLog\">\n')[1].split('\n<\/pre>')[0],
					contentArray = content.split("\n"),
					results = [],
					users = [],
					iOffset = 1; // in case of a log problem, which doesn't log the problematic line, increase this number to allow knowin how many numbers were skipped to allow accessing the last "results" array item by its correct index and not by the variable "i", which doesn't tell the number of skips
				for (i = 0; i < contentArray.length; i++) {
					if (["</pr",""].indexOf(contentArray[i]) > -1) {
						// problem with the log itself - increase iOfseet by 1 - see comment next to the variable for more info
						iOffset++;
					} else if (results.length > 0 && contentArray[i].search(/(\d\d\:\d\d|\[\\d{4,}-\\d\\d-\\d\\d \\d\\d\\:\\d\\d\\:\\d\\d\\]) /) != 0) {
						// a problem wiht C.P.W-Bot - doesn't post time and username wen posting messages in a row
						var time = "N/A",
							user = results[i-iOffset].substr(15).split('"')[0],
							message = contentArray[i],
							markup = '\n' +
										'\t<img width="28" height="28" class="avatar" data-user="' + user + '" />\n' +
										'\t' + time + '\n' +
										'\t' + user + '\n' +
										'\t' + message + '\n' +
									'';
						results.push(markup);
					} else if (contentArray[i].substr(6).indexOf("-!-") === 0) {
						// this is an inline alert (with traditional time stamps)
						results.push('' + contentArray[i].substr(10) + '');
					} else if (contentArray[i].search(/\[\d{4,}-\d\d-\d\d \d\d\:\d\d\:\d\d\] -\!- /) === 0) {
						// this is an inline alert (with modern time stamps)
						results.push('' + contentArray[i].substr(26) + '');
					} else if (contentArray[i].search(/\[\d{4,}-\d\d-\d\d \d\d\:\d\d\:\d\d\] +\* /) == 0) {
						// this is a parsed /me message
						var parsedMeMsg = contentArray[i].substr(contentArray[i].search(/\*/) + 2),
							checkMeUsername = function(NAME) {
								if (content.search(new RegExp("\\[\\d{4,}-\\d\\d-\\d\\d \\d\\d\\:\\d\\d\\:\\d\\d\\] -\\!- " + ChatLogView.escape(NAME) + " has (joined|left) Special\\:Chat")) > -1) {
									return NAME;
								} else if (NAME.length == 0) {
									results.push('LINE ERROR - /ME COMMAND - AT INDEX ' + i + '');
								} else {
									return checkMeUsername(NAME.split(" ").splice(0, NAME.split(" ").length - 1).join(" "));
								}
							},
							time = contentArray[i].search(/[012][0-9]\:[0-5][0-9]/) == 0 ? /* traditional time format */ contentArray[i].substr(0,5) : contentArray[i].substr(12,5),
							user = checkMeUsername(parsedMeMsg),
							message = contentArray[i].substr(contentArray[i].search("] * " + user) + 1),
							clearMessage = message.substr(/^\[[\d- \:]+\] +/.exec(message)[0].length),
							markup = '\n' +
										'\t<img width="28" height="28" class="avatar" data-user="' + user + '" />\n' +
										'\t' + time + '\n' +
										'\t' + user + '\n' +
										'\t' + clearMessage + '\n' +
									'';
						results.push(markup);
						if (users.indexOf(encodeURIComponent(user.replace(/ /g, "_"))) === -1) {
							users.push(encodeURIComponent(user.replace(/ /g, "_"))); // add user to the user array if hasn't been added yet
						}
					} else {
						// this is an ordinary message, or also an unparsed /me message, for some reason
						var time = contentArray[i].search(/[012][0-9]\:[0-5][0-9]/) == 0 ? /* traditional time format */ contentArray[i].substr(0,5) : contentArray[i].substr(12,5),
							user = contentArray[i].split("<")[1].split(">")[0], // might cause problems if the user has a & in his name
							message = contentArray[i].substr(contentArray[i].search("> ") + 5),
							markup = '\n' +
										'\t<img width="28" height="28" class="avatar" data-user="' + user + '" />\n' +
										'\t' + time + '\n' +
										'\t' + user + '\n' +
										'\t' + message + '\n' +
									'';
						results.push(markup);
						if (users.indexOf(encodeURIComponent(user.replace(/ /g, "_"))) === -1) {
							users.push(encodeURIComponent(user.replace(/ /g, "_"))); // add user to the user array if hasn't been added yet
						}
					}
					if (i + 1 == contentArray.length) {
						$("div#chat-logs-container ul").html(results.join("\n"));
						// now add "continued" class to messages in a arow - better than adding a case to each one of the cases above
						for (var j = 1; j < results.length; j++) {
							if (
								results[j-1].search('') != 0 &&
								results[j].search('') != 0
							) {
								if (results[j-1].substr(15).split('"')[0] == results[j].substr(15).split('"')[0]) {
									results[j].replace(/>\n/, function() {
										if (results[j].split("\n")[0].search(/ class\="[^\n"]"/) == -1) {
											return ' class="continued">\n';
										} else {
											return results[j].split("\n")[0].replace(/ class\="/, 'class="continued ');
										}
									})
								}
							}
							if (j + 1 == results.length) {
								$("div#chat-logs-container > div").prepend('Starts updating avatars...');
								console.log("Starts avatar replacing.");
								ChatLogView.getAvatars(users);
							}
						}
					}
				}
			}
		}
	});
}
/* function trigger */
$("#chat-logs-form-go").click(function() {
	if (
		$('select[name="chat-logs-form-dd"]').val() == "none" ||
		$('select[name="chat-logs-form-mm"]').val() == "none" ||
		$('select[name="chat-logs-form-yy"]').val() == "none"
	) {
		$("img#chat-logs-form-error").show();
		$("form#chat-logs-form select").each(function() {
			if ($(this).val() == "none") {
				$(this).css("border", "2px solid #c00");
			} else {
				$(this).removeAttr("style");
			}
		});
	} else if (ChatLogView.lastDate === $('[name="chat-logs-form-yy"]').val() + $('[name="chat-logs-form-mm"]').val() + $('[name="chat-logs-form-dd"]').val()) {
		$("div#chat-logs-container > div").html('The date that you tried to trigger is currently displayed. Please pick a different date.');
	} else {
		$("img#chat-logs-form-error").hide();
		$("form#chat-logs-form select[style]").removeAttr("style");
		ChatLogView.lastDate = $('[name="chat-logs-form-yy"]').val() + $('[name="chat-logs-form-mm"]').val() + $('[name="chat-logs-form-dd"]').val();
		ChatLogView.getLogs(
			$('select[name="chat-logs-form-dd"]').val(),
			$('select[name="chat-logs-form-mm"]').val(),
			$('select[name="chat-logs-form-yy"]').val()
		);
	}
});
/* style the results */
mw.util.addCSS(
	'#chat-logs-container {\n' +
		'\tmax-height: 482px;\n' +
		'\toverflow-y: scroll;\n' + 
		'\tborder: 1px solid #ccc;\n' +
	'}\n' +
	'#chat-logs-container .time {\n' +
		'\tcolor: #9c9c9c;\n' +
		'\tfloat: right;\n' + 
		'\tfont-size: 12px;\n' +
	'}\n' +
	'#chat-logs-container ul {\n' +
		'\tlist-style: none;\n' +
		'\tmargin: 0px;\n' +
		'\tpadding-top: 5px;\n' +
		'\tpadding-bottom: 5px;\n' +
	'}\n' +
	'#chat-logs-container li {\n' +
		'\tword-wrap: break-word;\n' +
		'\tmin-height: 32px;\n' +
		'\tpadding: 18px 15px 16px 55px;\n' +
		'\tposition: relative;\n' +
	'}\n' +
	'#chat-logs-container img.avatar {\n' +
		'\tborder: 1px solid #ccc;\n' +
		'\tleft: 15px;\n' +
		'\tposition: absolute;\n' +
		'\ttop: 20px;\n' +
	'}\n' +
	'#chat-logs-container img.avatar:not([src]) {\n' +
		'\t-webkit-box-shadow: 0 0 2px 2px #cc0000;\n' +
		'\tbox-shadow: 0 0 2px 2px #cc0000;\n' +
	'}\n' +
	'#chat-logs-container li.continued img.avatar {\n' +
		'\tdisplay: none;\n' +
	'}\n' +
	'#chat-logs-container .username {\n' +
		'\tdisplay: block;\n' +
		'\tfont-weight: bold;\n' +
	'}\n' +
	'#chat-logs-container .message {\n' +
		'\tdisplay: white-space: pre-line;\n' +
	'}' +
	'#chat-logs-container .message.me-message {\n' +
		'\tcolor: #9c9c9c;\n' +
		'\tcolor: #9c9c9c;\n' +
	'}' +
	'#chat-logs-container .inline-alert {\n' +
		'\tcolor: #9c9c9c;\n' +
		'\tfont-weight: bold;\n' +
		'\tmin-height: 0;\n' +
		'\tpadding-bottom: 10px;\n' +
		'\tpadding-top: 10px;\n' +
		'\ttext-align: center;\n' +
	'}' +
	'#chat-logs-container .inline-alert:before {\n' +
		'\tcontent: "~ ";' +
	'}' +
	'#chat-logs-container .inline-alert:after {\n' +
		'\tcontent: " ~";' +
	'}' +
	'#chat-logs-container > div {' +
		'\tpadding-top: 10px;' +
	'}'
);
//