diff --git a/public/src/ajaxify.js b/public/src/ajaxify.js
index 0c8fc84f06..81155638f8 100644
--- a/public/src/ajaxify.js
+++ b/public/src/ajaxify.js
@@ -91,12 +91,9 @@ var ajaxify = {};
 		ajaxify.enable();
 	});
 
+
 	function exec_body_scripts(body_el) {
-		//http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
-		// Finds and executes scripts in a newly added element's body.
-		// Needed since innerHTML does not run scripts.
-		//
-		// Argument body_el is an element in the dom.
+		// modified from http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
 
 		function nodeName(elem, name) {
 			return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
@@ -110,35 +107,38 @@ var ajaxify = {};
 
 			script.type = "text/javascript";
 			try {
-			  // doesn't work on ie...
 			  script.appendChild(document.createTextNode(data));      
 			} catch(e) {
-			  // IE has funky script nodes
 			  script.text = data;
 			}
 
+			if (elem.src) {
+				script.src = elem.src;
+			}
+
 			head.insertBefore(script, head.firstChild);
 			head.removeChild(script);
 		};
 
-		// main section of function
 		var scripts = [],
-		  script,
-		  children_nodes = body_el.childNodes,
-		  child,
-		  i;
+			script,
+			children_nodes = body_el.childNodes,
+			child,
+			i;
 
 		for (i = 0; children_nodes[i]; i++) {
-		child = children_nodes[i];
-		if (nodeName(child, "script" ) &&
-		  (!child.type || child.type.toLowerCase() === "text/javascript")) {
-		      scripts.push(child);
-		  }
+			child = children_nodes[i];
+			if (nodeName(child, "script" ) &&
+			(!child.type || child.type.toLowerCase() === "text/javascript")) {
+				scripts.push(child);
+			}
 		}
 
 		for (i = 0; scripts[i]; i++) {
 			script = scripts[i];
-			if (script.parentNode) {script.parentNode.removeChild(script);}
+			if (script.parentNode) {
+				script.parentNode.removeChild(script);
+			}
 			evalScript(scripts[i]);
 		}
 	};
diff --git a/public/src/forum/account.js b/public/src/forum/account.js
new file mode 100644
index 0000000000..edc08ed60d
--- /dev/null
+++ b/public/src/forum/account.js
@@ -0,0 +1,42 @@
+(function() {
+	var yourid = templates.get('yourid'),
+		theirid = templates.get('theirid');
+		
+	var isFriend = {isFriend};
+
+    $(document).ready(function() {
+
+        var rep = $('#reputation');
+        rep.html(app.addCommas(rep.html()));
+        
+        var postcount = $('#postcount');
+        postcount.html(app.addCommas(postcount.html()));
+        
+        var editLink = $('#editLink');
+		var addFriendBtn = $('#add-friend-btn');
+		
+        
+        if( yourid !== theirid) {
+            editLink.hide();
+            if(isFriend)
+           		addFriendBtn.hide();
+           	else
+           		addFriendBtn.show();
+        }
+    	else {
+    		addFriendBtn.hide();        
+    	}
+        
+        addFriendBtn.on('click', function() {
+        	$.post('/users/addfriend', {uid: theirid},
+            	function(data) {
+            		addFriendBtn.remove();
+            		$('#user-action-alert').html('Friend Added!').show();
+				}                
+			);
+        	return false;
+        });
+
+    });
+
+}());
\ No newline at end of file
diff --git a/public/src/forum/accountedit.js b/public/src/forum/accountedit.js
new file mode 100644
index 0000000000..2978ad5576
--- /dev/null
+++ b/public/src/forum/accountedit.js
@@ -0,0 +1,218 @@
+
+
+var gravatarPicture = templates.get('gravatarpicture');
+var uploadedPicture = templates.get('uploadedpicture');
+
+$(document).ready(function() {
+ 
+
+
+    $('#uploadForm').submit(function() {
+        status('uploading the file ...');
+		
+		$('#upload-progress-bar').css('width', '0%');
+		$('#upload-progress-box').show();
+		
+		if(!$('#userPhotoInput').val()) {
+			error('select an image to upload!');
+			return false;			
+		}
+
+        $(this).ajaxSubmit({
+ 
+			error: function(xhr) {
+				error('Error: ' + xhr.status);
+			},
+ 			
+ 			uploadProgress : function(event, position, total, percent) {
+ 				$('#upload-progress-bar').css('width', percent+'%');
+ 			},
+ 			
+ 
+			success: function(response) {
+				if(response.error) {
+					error(response.error);
+					return;
+				}
+ 
+				var imageUrlOnServer = response.path;
+ 				
+ 				$('#user-current-picture').attr('src', imageUrlOnServer);
+ 				$('#user-uploaded-picture').attr('src', imageUrlOnServer);
+ 				
+				uploadedPicture = imageUrlOnServer;        
+ 								
+				setTimeout(function() {
+					hideAlerts();
+		        	$('#upload-picture-modal').modal('hide');
+				}, 750);
+	        	
+				socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
+				success('File uploaded successfully!');
+            }
+		});
+
+		return false;
+    });
+ 
+ 	function hideAlerts() {
+ 		$('#alert-status').hide();
+		$('#alert-success').hide();
+		$('#alert-error').hide();
+		$('#upload-progress-box').hide();
+ 	}
+ 
+    function status(message) {
+    	hideAlerts();
+		$('#alert-status').text(message).show();
+    }
+    
+    function success(message) {
+    	hideAlerts();
+		$('#alert-success').text(message).show();
+    }
+    
+    function error(message) {
+    	hideAlerts();
+		$('#alert-error').text(message).show();
+    }
+	
+	function changeUserPicture(type) { 
+		var userData = {
+            uid: $('#inputUID').val(),
+			type: type
+        };
+            
+		$.post('/users/changepicture',
+        	userData,
+            function(data) {	
+       			socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
+            }                
+		);
+	}
+        
+	var selectedImageType = '';
+    
+    $('#submitBtn').on('click',function(){
+    	
+       var userData = {
+            uid:$('#inputUID').val(),
+            email:$('#inputEmail').val(),
+            fullname:$('#inputFullname').val(),
+            website:$('#inputWebsite').val(),
+            birthday:$('#inputBirthday').val(),
+            location:$('#inputLocation').val(),
+            signature:$('#inputSignature').val(),
+        };
+            
+		$.post('/users/doedit',
+        	userData,
+            function(data) {
+            	if(data.error) {
+            		app.alert({
+				      'alert_id': 'user_profile_updated',
+				      type: 'error',
+				      title: 'Profile Update Error',
+				      message: data.error,
+				      timeout: 2000
+				    });
+            		return;
+            	}
+            	
+				app.alert({
+			      'alert_id': 'user_profile_updated',
+			      type: 'success',
+			      title: 'Profile Updated',
+			      message: 'Your profile has been updated successfully',
+			      timeout: 2000
+			    });
+            }
+		);
+		return false;
+    });
+    
+    function updateImages() {
+		var currentPicture = $('#user-current-picture').attr('src');
+
+		if(gravatarPicture) {
+        	$('#user-gravatar-picture').attr('src', gravatarPicture);
+        	$('#gravatar-box').show();
+        }
+        else
+        	$('#gravatar-box').hide();
+
+    	if(uploadedPicture) {
+        	$('#user-uploaded-picture').attr('src', uploadedPicture);
+        	$('#uploaded-box').show();
+        }
+        else
+        	$('#uploaded-box').hide();
+        	
+        	
+         if(currentPicture == gravatarPicture)
+	        $('#gravatar-box .icon-ok').show();
+		else
+	        $('#gravatar-box .icon-ok').hide();
+	        
+        if(currentPicture == uploadedPicture)
+	        $('#uploaded-box .icon-ok').show();
+		else
+	        $('#uploaded-box .icon-ok').hide();
+    }
+    
+    
+    $('#changePictureBtn').on('click', function() {
+		selectedImageType = '';
+		updateImages();
+    	
+    	$('#change-picture-modal').modal('show');
+    	
+    	return false;
+    });
+    
+    $('#gravatar-box').on('click', function(){
+		$('#gravatar-box .icon-ok').show();
+        $('#uploaded-box .icon-ok').hide();
+        selectedImageType = 'gravatar';
+	});
+	
+	$('#uploaded-box').on('click', function(){
+		$('#gravatar-box .icon-ok').hide();
+        $('#uploaded-box .icon-ok').show();
+        selectedImageType = 'uploaded';
+	});
+	
+	$('#savePictureChangesBtn').on('click', function() {
+    	$('#change-picture-modal').modal('hide');
+
+    	if(selectedImageType) {
+        	changeUserPicture(selectedImageType);
+        	
+        	if(selectedImageType == 'gravatar')
+		        $('#user-current-picture').attr('src', gravatarPicture);		
+			else if(selectedImageType == 'uploaded')
+		        $('#user-current-picture').attr('src', uploadedPicture);						
+        }
+    	
+	});
+	
+	$('#upload-picture-modal').on('hide', function() {
+		$('#userPhotoInput').val('');
+	});
+	
+	$('#uploadPictureBtn').on('click', function(){
+		
+    	$('#change-picture-modal').modal('hide');
+    	$('#upload-picture-modal').modal('show');
+
+    	hideAlerts();
+    	
+    	return false;
+	});
+	
+	$('#pictureUploadSubmitBtn').on('click', function() {
+		$('#uploadForm').submit();
+	});
+    
+    
+});
\ No newline at end of file
diff --git a/public/src/forum/category.js b/public/src/forum/category.js
new file mode 100644
index 0000000000..74aa54b037
--- /dev/null
+++ b/public/src/forum/category.js
@@ -0,0 +1,78 @@
+(function() {
+	var cid = templates.get('category_id'),
+		room = 'category_' + cid;
+		
+	app.enter_room(room);
+
+	var new_post = document.getElementById('new_post');
+	new_post.onclick = function() {
+		app.open_post_window('topic', {category_id});
+	}
+
+	ajaxify.register_events([
+		'event:new_topic'
+	]);
+
+	if (jQuery('.category-item').length == 0) {
+		jQuery('#topics-container, .category-sidebar').hide();
+		jQuery('#category-no-topics').show();
+	}
+
+	socket.on('event:new_topic', function(data) {
+		var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }),
+			topic = document.createElement('div'),
+			container = document.getElementById('topics-container'),
+			topics = document.querySelectorAll('#topics-container a'),
+			numTopics = topics.length,
+			x;
+
+		jQuery('#topics-container, .category-sidebar').show();
+		jQuery('#category-no-topics').hide();
+
+		topic.innerHTML = html;
+		if (numTopics > 0) {
+			for(x=0;x<numTopics;x++) {
+				if (topics[x].querySelector('.icon-pushpin')) continue;
+				container.insertBefore(topic.querySelector('a'), topics[x]);
+				$(topic).hide().fadeIn('slow');
+				break;
+			}
+		} else {
+			container.insertBefore(topic.querySelector('a'), null);
+			$(topic).hide().fadeIn('slow');
+		}
+
+		// jQuery('<div></div>').appendTo("#topics-container").hide().append(html).fadeIn('slow');	
+		// set_up_posts(uniqueid);
+	});
+
+
+
+	socket.emit('api:categories.getRecentReplies', cid);
+	socket.on('api:categories.getRecentReplies', function(replies) {
+		if (replies === false) {
+			return;
+		}
+		
+		var users = replies.users,
+			posts = replies.posts,
+			recent_replies = document.getElementById('category_recent_replies');
+
+		recent_replies.innerHTML = '';
+		for (var i=0, ii=posts.pids.length; i<ii; i++) {
+			var a = document.createElement('a'),
+				ul = document.createElement('ul'),
+				username = users[posts.uid[i]].username,
+				picture = users[posts.uid[i]].picture;
+
+			//temp until design finalized
+			ul.innerHTML = '<li><img title="' + username + '" style="width: 48px; height: 48px; /*temporary*/" src="' + picture + '" class="" />'
+							+ '<p><strong>' + username + '</strong>: ' + posts.content[i] + '</p><span>posted ' + utils.relativeTime(posts.timestamp[i]) + ' ago</span></li>';
+			
+			a.appendChild(ul);
+			recent_replies.appendChild(a);
+		}
+		
+	});
+
+})();
\ No newline at end of file
diff --git a/public/src/forum/footer.js b/public/src/forum/footer.js
new file mode 100644
index 0000000000..79562a60f5
--- /dev/null
+++ b/public/src/forum/footer.js
@@ -0,0 +1,124 @@
+(function() {
+	var num_users = document.getElementById('number_of_users'),
+		latest_user = document.getElementById('latest_user'),
+		active_users = document.getElementById('active_users'),
+		user_label = document.getElementById('user_label'),
+		active_record = document.getElementById('active_record'),
+		right_menu = document.getElementById('right-menu');
+
+	socket.emit('user.count', {});
+	socket.on('user.count', function(data) {
+		num_users.innerHTML = "We currently have <b>" + data.count + "</b> registered users.";
+	});
+	socket.emit('user.latest', {});
+	socket.on('user.latest', function(data) {
+		if (data.username == '') {
+			latest_user.innerHTML = '';
+		} else {
+			latest_user.innerHTML = "The most recent user to register is <b><a href='/users/"+data.username+"'>" + data.username + "</a></b>.";
+		}
+	});
+	socket.emit('api:user.active.get');
+	socket.on('api:user.active.get', function(data) {
+		var plural_users = parseInt(data.users) !== 1,
+			plural_anon = parseInt(data.anon) !== 1;
+
+		active_users.innerHTML = 'There ' + (plural_users ? 'are' : 'is') + ' <strong>' + data.users + '</strong> user' + (plural_users ? 's' : '') + ' and <strong>' + data.anon + '</strong> guest' + (plural_anon ? 's' : '') + ' online';
+	});
+	socket.emit('api:user.active.get_record');
+	socket.on('api:user.active.get_record', function(data) {
+		active_record.innerHTML = "most users ever online was <strong>" + data.record + "</strong> on <strong>" + (new Date(parseInt(data.timestamp,10))).toUTCString() + "</strong>";
+	});
+
+	socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
+
+	socket.on('api:updateHeader', function(data) {
+		var rightMenu = $('#right-menu');
+		if (data.uid > 0) {
+			var userLabel = rightMenu.find('#user_label');
+			userLabel.attr('href','/users/'+data['username']);
+
+			userLabel.find('img').attr('src',data['picture']+"?s=24");
+			userLabel.find('span').html(data['username']);
+			
+		} else {
+			
+			rightMenu.html('');
+
+			var registerEl = document.createElement('li'),
+				loginEl = document.createElement('li');
+
+			registerEl.innerHTML = '<a href="/register">Register</a>';
+			loginEl.innerHTML = '<a href="/login">Login</a>';
+
+			right_menu.appendChild(registerEl);
+			right_menu.appendChild(loginEl);
+		}
+	});
+
+	// Post window events
+	var	postWindowEl = document.getElementById('post_window'),
+		discardEl = document.getElementById('discard-post');
+	discardEl.addEventListener('click', function() {
+		$(postWindowEl).slideToggle(250);
+		$(document.body).removeClass('composing');
+	}, false);
+
+	// Notifications dropdown
+	var notifContainer = document.getElementsByClassName('notifications')[0],
+		notifTrigger = notifContainer.querySelector('a'),
+		notifList = document.getElementById('notif-list');
+	notifTrigger.addEventListener('click', function() {
+		if (notifContainer.className.indexOf('open') === -1) socket.emit('api:notifications.get');
+	});
+	notifList.addEventListener('click', function(e) {
+		var target;
+		switch(e.target.nodeName) {
+			case 'SPAN': target = e.target.parentNode.parentNode; break;
+			case 'A': target = e.target.parentNode; break;
+			case 'li': target = e.target; break;
+		}
+		if (target) {
+			var nid = parseInt(target.getAttribute('data-nid'));
+			if (nid > 0) socket.emit('api:notifications.mark_read', nid);
+		}
+	})
+	socket.on('api:notifications.get', function(data) {
+		var	notifFrag = document.createDocumentFragment(),
+			notifEl = document.createElement('li'),
+			numRead = data.read.length,
+			numUnread = data.unread.length,
+			x;
+		notifList.innerHTML = '';
+		if (data.read.length + data.unread.length > 0) {
+			for(x=0;x<numUnread;x++) {
+				notifEl.setAttribute('data-nid', data.unread[x].nid);
+				notifEl.className = 'unread';
+				notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
+				notifFrag.appendChild(notifEl.cloneNode(true));
+			}
+			for(x=0;x<numRead;x++) {
+				notifEl.setAttribute('data-nid', data.read[x].nid);
+				notifEl.className = '';
+				notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>';
+				notifFrag.appendChild(notifEl.cloneNode(true));
+			}
+		} else {
+			notifEl.innerHTML = '<a>You have no notifications</a>';
+			notifFrag.appendChild(notifEl);
+		}
+		notifList.appendChild(notifFrag);
+	});
+	socket.on('api:notifications.counts', function(counts) {
+		var notifIcon = document.querySelector('.notifications a i');
+		if(notifIcon) {
+			if (counts.unread > 0) notifIcon.className = 'icon-circle active';
+			else notifIcon.className = 'icon-circle-blank';
+		}
+	});
+	socket.on('event:new_notification', function() {
+		console.log('WOOT');
+		document.querySelector('.notifications a i').className = 'icon-circle active';
+	});
+	socket.emit('api:notifications.counts');
+}());
\ No newline at end of file
diff --git a/public/src/forum/friends.js b/public/src/forum/friends.js
new file mode 100644
index 0000000000..7a34e83815
--- /dev/null
+++ b/public/src/forum/friends.js
@@ -0,0 +1,44 @@
+(function() {
+
+	var yourid = templates.get('yourid'),
+		theirid = templates.get('theirid'),
+		friendCount = templates.get('friendCount');
+
+    $(document).ready(function() {
+    	
+    	if(parseInt(friendCount, 10) === 0) {
+    		$('#no-friend-notice').show();
+    	}
+    	var editLink = $('#editLink');
+
+		if(yourid !== theirid) {
+			editLink.hide();
+			$('.remove-friend-btn').hide();
+		}
+		else {
+			$('.remove-friend-btn').on('click',function(){
+
+				var removeBtn = $(this);
+				var friendid = $(this).attr('friendid');
+				
+				$.post('/users/removefriend', {uid: friendid},
+	            	function(data) {
+	            		removeBtn.parent().remove();
+					}                
+				);
+				return false;
+			});
+		}
+
+        $('.reputation').each(function(index, element) {
+        	$(element).html(app.addCommas($(element).html()));
+        });
+        
+        $('.postcount').each(function(index, element) {
+        	$(element).html(app.addCommas($(element).html()));
+        });
+        
+    });
+    
+
+}());
\ No newline at end of file
diff --git a/public/src/forum/login.js b/public/src/forum/login.js
new file mode 100644
index 0000000000..f63fc15c28
--- /dev/null
+++ b/public/src/forum/login.js
@@ -0,0 +1,9 @@
+(function() {
+	// Alternate Logins
+	var altLoginEl = document.querySelector('.alt-logins');
+	altLoginEl.addEventListener('click', function(e) {
+		if (e.target.nodeName === 'LI') {
+			document.location.href = e.target.getAttribute('data-url');
+		}
+	});	
+}());
diff --git a/public/src/forum/register.js b/public/src/forum/register.js
new file mode 100644
index 0000000000..90768919a5
--- /dev/null
+++ b/public/src/forum/register.js
@@ -0,0 +1,83 @@
+(function() {
+	var username = document.getElementById('username'),
+		password = document.getElementById('password'),
+		register = document.getElementById('register'),
+		emailEl = document.getElementById('email'),
+		username_notify = document.getElementById('username-notify'),
+		email_notify = document.getElementById('email-notify'),
+		password_notify = document.getElementById('password-notify');
+
+	username.onkeyup = function() {
+		if (username.value.length > 2) socket.emit('user.exists', {username: username.value});
+		else {
+			username_notify.innerHTML = 'Username too short';
+			username_notify.className = 'label label-important';
+		}
+	}
+	emailEl.addEventListener('change', function() {
+		socket.emit('user.email.exists', { email: emailEl.value });
+	}, false);
+	password.addEventListener('keyup', function() {
+		if (password.value.length < 5) {
+			password_notify.innerHTML = 'Password too short';
+		} else {
+			password_notify.innerHTML = '';
+		}
+	}, false);
+
+	ajaxify.register_events(['user.exists', 'user.email.exists']);
+
+	socket.on('user.exists', function(data) {
+		if (data.exists == true) {
+			username_notify.innerHTML = 'Username exists';
+			username_notify.className = 'label label-important';
+		} else {
+			username_notify.innerHTML = 'Not taken';
+			username_notify.className = 'label label-success';
+		}
+	});
+	socket.on('user.email.exists', function(data) {
+		if (data.exists === true) {
+			email_notify.innerHTML = 'Email Address exists';
+		} else {
+			email_notify.innerHTML = '';
+		}
+	});
+
+	// Alternate Logins
+	var altLoginEl = document.querySelector('.alt-logins');
+	altLoginEl.addEventListener('click', function(e) {
+		if (e.target.nodeName === 'LI') {
+			document.location.href = e.target.getAttribute('data-url');
+		}
+	});
+
+	// Form Validation
+	function validateForm() {
+		var validated = true;
+		if (username.value.length < 2) {
+			username_notify.innerHTML = 'Invalid username';
+			username_notify.className = 'label label-important';
+			validated = false;
+		}
+
+		if (password.value.length < 5) {
+			password_notify.innerHTML = 'Password too short';
+			validated = false;
+		} else {
+			password_notify.innerHTML = '';
+		}
+
+		if (email.value.indexOf('@') === -1) {
+			email_notify.innerHTML = 'Invalid email address';
+			validated = false;
+		} else {
+			email_notify.innerHTML = '';
+		}
+
+		return validated;
+	}
+	register.addEventListener('click', function(e) {
+		if (!validateForm()) e.preventDefault();
+	}, false);
+}());
\ No newline at end of file
diff --git a/public/src/forum/reset.js b/public/src/forum/reset.js
new file mode 100644
index 0000000000..053db42e43
--- /dev/null
+++ b/public/src/forum/reset.js
@@ -0,0 +1,39 @@
+(function() {
+	var	inputEl = document.getElementById('email'),
+		errorEl = document.getElementById('error'),
+		errorTextEl = errorEl.querySelector('p');
+
+	document.getElementById('reset').onclick = function() {
+		if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
+			socket.emit('user:reset.send', { email: inputEl.value });
+		} else {
+			jQuery('#success').hide();
+			jQuery(errorEl).show();
+			errorTextEl.innerHTML = 'Please enter a valid email';
+		}
+	};
+
+	ajaxify.register_events(['user.send_reset']);
+	
+	socket.on('user.send_reset', function(data) {
+		var	submitEl = document.getElementById('reset');
+
+		if (data.status === 'ok') {
+			jQuery('#error').hide();
+			jQuery('#success').show();
+			jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
+			inputEl.value = '';
+		} else {
+			jQuery('#success').hide();
+			jQuery(errorEl).show();
+			switch(data.message) {
+				case 'invalid-email':
+					errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
+				break;
+				case 'send-failed':
+					errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
+				break;
+			}
+		}
+	});
+}());
\ No newline at end of file
diff --git a/public/src/forum/reset_code.js b/public/src/forum/reset_code.js
new file mode 100644
index 0000000000..fffc3dadf3
--- /dev/null
+++ b/public/src/forum/reset_code.js
@@ -0,0 +1,42 @@
+(function() {
+	var reset_code = templates.get('reset_code');
+
+	var	resetEl = document.getElementById('reset'),
+		password = document.getElementById('password'),
+		repeat = document.getElementById('repeat'),
+		noticeEl = document.getElementById('notice');
+
+	resetEl.addEventListener('click', function() {
+		if (password.value.length < 6) {
+			$('#error').hide();
+			noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
+			noticeEl.querySelector('p').innerHTML = 'The password entered it too short, please pick a different password!';
+			noticeEl.style.display = 'block';
+		} else if (password.value === repeat.value) {
+			socket.emit('user:reset.commit', { code: reset_code, password: password.value });
+		}
+	}, false);
+
+	// Enable the form if the code is valid
+	socket.emit('user:reset.valid', { code: reset_code });
+
+
+	ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
+	socket.on('user:reset.valid', function(data) {
+		if (!!data.valid) resetEl.disabled = false;
+		else {
+			var formEl = document.getElementById('reset-form');
+			// Show error message
+			$('#error').show();
+			formEl.parentNode.removeChild(formEl);
+		}
+	})
+
+	socket.on('user:reset.commit', function(data) {
+		if (data.status === 'ok') {
+			$('#error').hide();
+			$('#notice').hide();
+			$('#success').show();
+		}
+	});
+}());
\ No newline at end of file
diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js
new file mode 100644
index 0000000000..c11128eb86
--- /dev/null
+++ b/public/src/forum/topic.js
@@ -0,0 +1,603 @@
+(function() {
+	var	expose_tools = templates.get('expose_tools'),
+		tid = templates.get('topic_id'),
+		postListEl = document.getElementById('post-container'),
+		editBtns = document.querySelectorAll('#post-container .post-buttons .edit, #post-container .post-buttons .edit i'),
+		thread_state = {
+			locked: templates.get('locked'),
+			deleted: templates.get('deleted'),
+			pinned: templates.get('pinned')
+		},
+		topic_name = templates.get('topic_name');
+
+	function addCommasToNumbers() {
+		$('.formatted-number').each(function(index, element) {
+        	$(element).html(app.addCommas($(element).html()));
+    	});
+	}
+
+	jQuery('document').ready(function() {
+
+		addCommasToNumbers();
+		
+		var	room = 'topic_' + tid,
+			adminTools = document.getElementById('thread-tools');
+
+		app.enter_room(room);
+		set_up_posts();
+
+		if (thread_state.locked === '1') set_locked_state(true);
+		if (thread_state.deleted === '1') set_delete_state(true);
+		if (thread_state.pinned === '1') set_pinned_state(true);
+
+		if (expose_tools === '1') {
+			var deleteThreadEl = document.getElementById('delete_thread'),
+				lockThreadEl = document.getElementById('lock_thread'),
+				pinThreadEl = document.getElementById('pin_thread'),
+				moveThreadEl = document.getElementById('move_thread'),
+				moveThreadModal = $('#move_thread_modal');
+
+			adminTools.style.visibility = 'inherit';
+
+			// Add events to the thread tools
+			deleteThreadEl.addEventListener('click', function(e) {
+				e.preventDefault();
+				if (thread_state.deleted !== '1') {
+					if (confirm('really delete thread? (THIS DIALOG TO BE REPLACED WITH BOOTBOX)')) {
+						socket.emit('api:topic.delete', { tid: tid });
+					}
+				} else {
+					if (confirm('really restore thread? (THIS DIALOG TO BE REPLACED WITH BOOTBOX)')) {
+						socket.emit('api:topic.restore', { tid: tid });
+					}
+				}
+			}, false);
+
+			lockThreadEl.addEventListener('click', function(e) {
+				e.preventDefault();
+				if (thread_state.locked !== '1') {
+					socket.emit('api:topic.lock', { tid: tid });
+				} else {
+					socket.emit('api:topic.unlock', { tid: tid });
+				}
+			}, false);
+
+			pinThreadEl.addEventListener('click', function(e) {
+				e.preventDefault();
+				if (thread_state.pinned !== '1') {
+					socket.emit('api:topic.pin', { tid: tid });
+				} else {
+					socket.emit('api:topic.unpin', { tid: tid });
+				}
+			}, false);
+
+			moveThreadEl.addEventListener('click', function(e) {
+				e.preventDefault();
+				moveThreadModal.modal('show');
+			}, false);
+
+			moveThreadModal.on('shown', function() {
+				var loadingEl = document.getElementById('categories-loading');
+				if (loadingEl) {
+					socket.once('api:categories.get', function(data) {
+						// Render categories
+						var	categoriesFrag = document.createDocumentFragment(),
+							categoryEl = document.createElement('li'),
+							numCategories = data.categories.length,
+							modalBody = moveThreadModal.find('.modal-body'),
+							categoriesEl = modalBody[0].getElementsByTagName('ul')[0],
+							confirmDiv = document.getElementById('move-confirm'),
+							confirmCat = confirmDiv.getElementsByTagName('span')[0],
+							commitEl = document.getElementById('move_thread_commit'),
+							cancelEl = document.getElementById('move_thread_cancel'),
+							x, info, targetCid, targetCatLabel;
+
+						categoriesEl.className = 'category-list';
+						for(x=0;x<numCategories;x++) {
+							info = data.categories[x];
+							categoryEl.className = info.blockclass;
+							categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name;
+							categoryEl.setAttribute('data-cid', info.cid);
+							categoriesFrag.appendChild(categoryEl.cloneNode(true));
+						}
+						categoriesEl.appendChild(categoriesFrag);
+						modalBody[0].removeChild(loadingEl);
+
+						categoriesEl.addEventListener('click', function(e) {
+							if (e.target.nodeName === 'LI') {
+								confirmCat.innerHTML = e.target.innerHTML;
+								confirmDiv.style.display = 'block';
+								targetCid = e.target.getAttribute('data-cid');
+								targetCatLabel = e.target.innerHTML;
+								commitEl.disabled = false;
+							}
+						}, false);
+
+						commitEl.addEventListener('click', function() {
+							if (!commitEl.disabled && targetCid) {
+								commitEl.disabled = true;
+								$(cancelEl).fadeOut(250);
+								$(moveThreadModal).find('.modal-header button').fadeOut(250);
+								commitEl.innerHTML = 'Moving <i class="icon-spin icon-refresh"></i>';
+
+								socket.once('api:topic.move', function(data) {
+									moveThreadModal.modal('hide');
+									if (data.status === 'ok') {
+										app.alert({
+											'alert_id': 'thread_move',
+											type: 'success',
+											title: 'Topic Successfully Moved',
+											message: 'This topic has been successfully moved to ' + targetCatLabel,
+											timeout: 5000
+										});
+									} else {
+										app.alert({
+											'alert_id': 'thread_move',
+											type: 'error',
+											title: 'Unable to Move Topic',
+											message: 'This topic could not be moved to ' + targetCatLabel + '.<br />Please try again later',
+											timeout: 5000
+										});
+									}
+								});
+								socket.emit('api:topic.move', { tid: tid, cid: targetCid });
+							}
+						});
+					});
+					socket.emit('api:categories.get');
+				}
+			});
+		}
+
+		// Fix delete state for this thread's posts
+		var	postEls = document.querySelectorAll('#post-container li[data-deleted]');
+		for(var x=0,numPosts=postEls.length;x<numPosts;x++) {
+			if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid'));
+			postEls[x].removeAttribute('data-deleted');
+		}
+	});
+
+
+	$('.post-container').delegate('.edit', 'click', function(e) {
+		var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1];
+
+		var main = $(this).parents('.main-post');
+		if(main.length > 0) 
+			app.open_post_window('edit', tid, topic_name, pid);
+		else 
+			app.open_post_window('edit', tid, "", pid);
+	
+	});
+
+	$('.post-container').delegate('.delete', 'click', function(e) {
+		var	pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1],
+		postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
+		deleteAction = !postEl.hasClass('deleted') ? true : false,
+		confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
+
+		if (confirmDel) {
+			deleteAction ?
+			socket.emit('api:posts.delete', { pid: pid }) :
+			socket.emit('api:posts.restore', { pid: pid });
+		}
+	}); 
+
+	// CHAT, move to chat.js later?
+
+	$('.post-container').delegate('.chat', 'click', function(e){
+
+		var username = $(this).parents('li').attr('data-username');
+		var touid = $(this).parents('li').attr('data-uid');
+
+		var chatModal = createModalIfDoesntExist(username, touid);
+
+		chatModal.show();
+		bringModalToTop(chatModal);
+		
+	});
+
+	function bringModalToTop(chatModal) {
+		var topZ = 0;
+		$('.modal').each(function(){
+		  var thisZ = parseInt($(this).css('zIndex'), 10);
+		  if (thisZ > topZ){
+		    topZ = thisZ;
+		  }
+		});
+		chatModal.css('zIndex', topZ+1);
+	}
+
+	function createModalIfDoesntExist(username, touid) {
+		var chatModal = $('#chat-modal-'+touid);
+
+		if(!chatModal.length) {
+			var chatModal = $('#chat-modal').clone();
+			chatModal.attr('id','chat-modal-'+touid);
+			chatModal.appendTo($('body'));
+			chatModal.draggable({
+				start:function(){
+					bringModalToTop(chatModal);
+				}
+			});
+			chatModal.find('#chat-with-name').html(username);
+
+			chatModal.find('.close').on('click',function(e){
+				chatModal.hide();
+			});
+
+			chatModal.on('click', function(e){
+				bringModalToTop(chatModal);
+			});
+
+			addSendHandler(chatModal, touid);	
+		}
+
+		return chatModal;
+	}
+
+	function addSendHandler(chatModal, touid) {
+		chatModal.find('#chat-message-input').off('keypress');
+		chatModal.find('#chat-message-input').on('keypress', function(e) {
+			if(e.which === 13) {
+				sendMessage(chatModal, touid);
+			}
+		});
+
+		chatModal.find('#chat-message-send-btn').off('click');
+		chatModal.find('#chat-message-send-btn').on('click', function(e){
+			sendMessage(chatModal, touid);
+			return false;
+		});
+	}
+
+	function sendMessage(chatModal, touid) {
+		var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
+		if(msg.length) {
+			msg = msg +'\n';
+			socket.emit('sendChatMessage', { touid:touid, message:msg});
+			chatModal.find('#chat-message-input').val('');
+			appendChatMessage(chatModal, 'You : ' + msg);
+		}
+	}
+
+	socket.on('chatMessage', function(data){
+		var username = data.username;
+		var fromuid = data.fromuid;
+		var message = data.message;
+
+		var chatModal = createModalIfDoesntExist(username, fromuid);
+		chatModal.show();
+		bringModalToTop(chatModal);
+	
+		appendChatMessage(chatModal, message)
+	});
+
+	function appendChatMessage(chatModal, message){
+		var chatContent = chatModal.find('#chat-content');
+		chatContent.append(message);
+		chatContent.scrollTop(
+    		chatContent[0].scrollHeight - chatContent.height()
+		);
+	}
+	//end of chat
+
+
+	ajaxify.register_events([
+		'event:rep_up', 'event:rep_down', 'event:new_post', 'api:get_users_in_room',
+		'event:topic_deleted', 'event:topic_restored', 'event:topic:locked',
+		'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned',
+		'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored',
+		'api:posts.favourite', 'chatMessage'
+	]);
+
+
+	socket.on('api:get_users_in_room', function(users) {
+		var anonymous = users.anonymous,
+			usernames = users.usernames,
+			usercount = usernames.length;
+
+		for (var i = 0, ii=usercount; i<ii; i++) {
+			usernames[i] = '<strong>' + '<a href="/users/'+usernames[i]+'">' + usernames[i] + '</a></strong>';
+		}
+
+		// headexplosion.gif for fun, to see if I could do this in one line of code. feel free to refactor haha
+		var active =
+			((usercount === 1) ? usernames[0] : '')
+			+ ((usercount === 2 && anonymous === 0) ? usernames[0] + ' and ' + usernames[1] : '')
+			+ ((usercount > 2 && anonymous === 0) ? usernames.join(', ').replace(/,([^,]*)$/, ", and$1") : '')
+			+ (usercount > 1 && anonymous > 0 ? usernames.join(', ') : '')
+			+ ((anonymous > 0) ? (usercount > 0 ? ' and ': '') + anonymous + ' guest' + (anonymous > 1  ? 's are': ' is') : '')
+			+ (anonymous === 0 ? (usercount > 1 ? ' are' : ' is') : '') + ' browsing this thread';
+
+		document.getElementById('thread_active_users').innerHTML = active;
+	});
+
+	socket.on('event:rep_up', function(data) {
+		adjust_rep(1, data.pid, data.uid);
+	});
+
+	socket.on('event:rep_down', function(data) {
+		adjust_rep(-1, data.pid, data.uid);
+	});
+
+	socket.on('event:new_post', function(data) {
+		var html = templates.prepare(templates['topic'].blocks['posts']).parse(data),
+			uniqueid = new Date().getTime();
+			
+		jQuery('<div id="' + uniqueid + '"></div>')
+			.appendTo("#post-container")
+			.hide()
+			.append(html)
+			.fadeIn('slow');
+
+		set_up_posts(uniqueid);
+		
+		addCommasToNumbers();
+	});
+
+	socket.on('event:topic_deleted', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_locked_state(true);
+			set_delete_state(true);
+		}
+	});
+
+	socket.on('event:topic_restored', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_locked_state(false);
+			set_delete_state(false);
+		}
+	});
+
+	socket.on('event:topic_locked', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_locked_state(true, 1);
+		}
+	});
+
+	socket.on('event:topic_unlocked', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_locked_state(false, 1);
+		}
+	});
+
+	socket.on('event:topic_pinned', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_pinned_state(true, 1);
+		}
+	});
+
+	socket.on('event:topic_unpinned', function(data) {
+		if (data.tid === tid && data.status === 'ok') {
+			set_pinned_state(false, 1);
+		}
+	});
+
+	socket.on('event:topic_moved', function(data) {
+		if (data && data.tid > 0) ajaxify.go('topic/' + data.tid);
+	});
+
+	socket.on('event:post_edited', function(data) {
+		var editedPostEl = document.getElementById('content_' + data.pid);
+
+		var editedPostTitle = $('#topic_title_'+data.pid);
+		
+		if(editedPostTitle.length > 0) {
+			editedPostTitle.fadeOut(250, function() {
+				editedPostTitle.html(data.title);
+				editedPostTitle.fadeIn(250);
+			});
+		}
+
+		$(editedPostEl).fadeOut(250, function() {
+			this.innerHTML = data.content;
+			$(this).fadeIn(250);
+		});
+	});
+
+	socket.on('api:posts.favourite', function(data) {
+		if (data.status !== 'ok' && data.pid) {
+			var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
+			if (favEl) favEl.className = 'icon-star-empty';
+		}
+	});
+
+	socket.on('event:post_deleted', function(data) {
+		if (data.pid) toggle_post_delete_state(data.pid, true);
+	});
+
+	socket.on('event:post_restored', function(data) {
+		if (data.pid) toggle_post_delete_state(data.pid, true);
+	});
+
+	function adjust_rep(value, pid, uid) {
+		var post_rep = jQuery('.post_rep_' + pid),
+			user_rep = jQuery('.user_rep_' + uid);
+
+		var ptotal = parseInt(post_rep.html(), 10),
+			utotal = parseInt(user_rep.html(), 10);
+
+		ptotal += value;
+		utotal += value;
+
+		post_rep.html(ptotal);
+		user_rep.html(utotal);
+	}
+
+
+	function set_up_posts(div) {
+		if (div == null) div = '';
+		else div = '#' + div;
+
+		jQuery(div + ' .post_reply').click(function() {
+			if (thread_state.locked !== '1') app.open_post_window('reply', tid, topic_name);
+		});
+
+		jQuery(div + ' .quote').click(function() {
+			if (thread_state.locked !== '1') app.open_post_window('quote', tid, topic_name);
+			
+			var pid = $(this).parents('li').attr('data-pid');
+			
+			$('#post_content').val('> ' + $('#content_' + pid).html() + '\n');
+			console.log("POST ID "+pid);
+		});
+
+		jQuery(div + ' .edit, ' + div + ' .delete').each(function() {
+			var ids = this.id.replace('ids_', '').split('_'),
+				pid = ids[0],
+				uid = ids[1];
+
+		});
+
+		jQuery(div + ' .favourite').click(function() {
+			var ids = this.id.replace('favs_', '').split('_'),
+				pid = ids[0],
+				uid = ids[1];
+
+			if (thread_state.locked !== '1') {
+				if (this.children[1].className == 'icon-star-empty') {
+					this.children[1].className = 'icon-star';
+					socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room});
+				}
+				else {
+					this.children[1].className = 'icon-star-empty';
+					socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room});
+				}
+			}
+		});
+	}
+
+	function set_locked_state(locked, alert) {
+		var	threadReplyBtn = document.getElementById('post_reply'),
+			postReplyBtns = document.querySelectorAll('#post-container .post_reply'),
+			quoteBtns = document.querySelectorAll('#post-container .quote'),
+			editBtns = document.querySelectorAll('#post-container .edit'),
+			deleteBtns = document.querySelectorAll('#post-container .delete'),
+			numReplyBtns = postReplyBtns.length,
+			lockThreadEl = document.getElementById('lock_thread'),
+			x;
+
+		if (locked === true) {
+			lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread';
+			threadReplyBtn.disabled = true;
+			threadReplyBtn.innerHTML = 'Locked <i class="icon-lock"></i>';
+			for(x=0;x<numReplyBtns;x++) {
+				postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>';
+				quoteBtns[x].style.display = 'none';
+				editBtns[x].style.display = 'none';
+				deleteBtns[x].style.display = 'none';
+			}
+
+			if (alert) {
+				app.alert({
+					'alert_id': 'thread_lock',
+					type: 'success',
+					title: 'Thread Locked',
+					message: 'Thread has been successfully locked',
+					timeout: 5000
+				});
+			}
+
+			thread_state.locked = '1';
+		} else {
+			lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread';
+			threadReplyBtn.disabled = false;
+			threadReplyBtn.innerHTML = 'Reply';
+			for(x=0;x<numReplyBtns;x++) {
+				postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
+				quoteBtns[x].style.display = 'inline-block';
+				editBtns[x].style.display = 'inline-block';
+				deleteBtns[x].style.display = 'inline-block';
+			}
+
+			if (alert) {
+				app.alert({
+					'alert_id': 'thread_lock',
+					type: 'success',
+					title: 'Thread Unlocked',
+					message: 'Thread has been successfully unlocked',
+					timeout: 5000
+				});
+			}
+
+			thread_state.locked = '0';
+		}
+	}
+
+	function set_delete_state(deleted) {
+		var	deleteThreadEl = document.getElementById('delete_thread'),
+			deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0],
+			threadEl = document.querySelector('.post-container'),
+			deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
+
+		if (deleted) {
+			deleteTextEl.innerHTML = '<i class="icon-comment"></i> Restore Thread';
+			$(threadEl).addClass('deleted');
+
+			// Spawn a 'deleted' notice at the top of the page
+			deleteNotice.setAttribute('id', 'thread-deleted');
+			deleteNotice.className = 'alert';
+			deleteNotice.innerHTML = 'This thread has been deleted. Only users with thread management privileges can see it.';
+			document.getElementById('content').insertBefore(deleteNotice, threadEl);
+
+			thread_state.deleted = '1';
+		} else {
+			deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread';
+			$(threadEl).removeClass('deleted');
+			deleteNotice.parentNode.removeChild(deleteNotice);
+
+			thread_state.deleted = '0';
+		}
+	}
+
+	function set_pinned_state(pinned, alert) {
+		var pinEl = document.getElementById('pin_thread');
+
+		if (pinned) {
+			pinEl.innerHTML = '<i class="icon-pushpin"></i> Unpin Thread';
+			if (alert) {
+				app.alert({
+					'alert_id': 'thread_pin',
+					type: 'success',
+					title: 'Thread Pinned',
+					message: 'Thread has been successfully pinned',
+					timeout: 5000
+				});
+			}
+
+			thread_state.pinned = '1';
+		} else {
+			pinEl.innerHTML = '<i class="icon-pushpin"></i> Pin Thread';
+			if (alert) {
+				app.alert({
+					'alert_id': 'thread_pin',
+					type: 'success',
+					title: 'Thread Unpinned',
+					message: 'Thread has been successfully unpinned',
+					timeout: 5000
+				});
+			}
+
+			thread_state.pinned = '0';
+		}
+	}
+
+	function toggle_post_delete_state(pid) {
+		var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]'));
+			quoteEl = $(postEl[0].querySelector('.quote')),
+			favEl = $(postEl[0].querySelector('.favourite')),
+			replyEl = $(postEl[0].querySelector('.post_reply'));
+
+		if (!postEl.hasClass('deleted')) {
+			quoteEl.addClass('none');
+			favEl.addClass('none');
+			replyEl.addClass('none');
+		} else {
+			quoteEl.removeClass('none');
+			favEl.removeClass('none');
+			replyEl.removeClass('none');
+		}
+
+		postEl.toggleClass('deleted');
+	}
+})();
\ No newline at end of file
diff --git a/public/src/forum/users.js b/public/src/forum/users.js
new file mode 100644
index 0000000000..3b33869b02
--- /dev/null
+++ b/public/src/forum/users.js
@@ -0,0 +1,15 @@
+(function() {
+	
+    $(document).ready(function() {
+        
+        $('.reputation').each(function(index, element) {
+        	$(element).html(app.addCommas($(element).html()));
+        });
+        
+        $('.postcount').each(function(index, element) {
+        	$(element).html(app.addCommas($(element).html()));
+        });
+        
+    });
+
+}());
\ No newline at end of file
diff --git a/public/templates/account.tpl b/public/templates/account.tpl
index f269317b50..fc90419d9e 100644
--- a/public/templates/account.tpl
+++ b/public/templates/account.tpl
@@ -75,49 +75,4 @@
 <input type="hidden" template-variable="yourid" value="{yourid}" />
 <input type="hidden" template-variable="theirid" value="{theirid}" />
 
-<script type="text/javascript">
-
-var yourid = templates.get('yourid'),
-	theirid = templates.get('theirid');
-
-(function() {
-
-	var isFriend = {isFriend};
-
-    $(document).ready(function() {
-
-        var rep = $('#reputation');
-        rep.html(app.addCommas(rep.html()));
-        
-        var postcount = $('#postcount');
-        postcount.html(app.addCommas(postcount.html()));
-        
-        var editLink = $('#editLink');
-		var addFriendBtn = $('#add-friend-btn');
-		
-        
-        if( yourid !== theirid) {
-            editLink.hide();
-            if(isFriend)
-           		addFriendBtn.hide();
-           	else
-           		addFriendBtn.show();
-        }
-    	else {
-    		addFriendBtn.hide();        
-    	}
-        
-        addFriendBtn.on('click', function() {
-        	$.post('/users/addfriend', {uid: theirid},
-            	function(data) {
-            		addFriendBtn.remove();
-            		$('#user-action-alert').html('Friend Added!').show();
-				}                
-			);
-        	return false;
-        });
-
-    });
-
-}());
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/account.js"></script>
\ No newline at end of file
diff --git a/public/templates/accountedit.tpl b/public/templates/accountedit.tpl
index 2625aa10df..3d727d5547 100644
--- a/public/templates/accountedit.tpl
+++ b/public/templates/accountedit.tpl
@@ -140,223 +140,4 @@
 <input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
 <input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />
 
-<script type="text/javascript">
-
-
-var gravatarPicture = templates.get('gravatarpicture');
-var uploadedPicture = templates.get('uploadedpicture');
-
-$(document).ready(function() {
- 
-
-
-    $('#uploadForm').submit(function() {
-        status('uploading the file ...');
-		
-		$('#upload-progress-bar').css('width', '0%');
-		$('#upload-progress-box').show();
-		
-		if(!$('#userPhotoInput').val()) {
-			error('select an image to upload!');
-			return false;			
-		}
-
-        $(this).ajaxSubmit({
- 
-			error: function(xhr) {
-				error('Error: ' + xhr.status);
-			},
- 			
- 			uploadProgress : function(event, position, total, percent) {
- 				$('#upload-progress-bar').css('width', percent+'%');
- 			},
- 			
- 
-			success: function(response) {
-				if(response.error) {
-					error(response.error);
-					return;
-				}
- 
-				var imageUrlOnServer = response.path;
- 				
- 				$('#user-current-picture').attr('src', imageUrlOnServer);
- 				$('#user-uploaded-picture').attr('src', imageUrlOnServer);
- 				
-				uploadedPicture = imageUrlOnServer;        
- 								
-				setTimeout(function() {
-					hideAlerts();
-		        	$('#upload-picture-modal').modal('hide');
-				}, 750);
-	        	
-				socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
-				success('File uploaded successfully!');
-            }
-		});
-
-		return false;
-    });
- 
- 	function hideAlerts() {
- 		$('#alert-status').hide();
-		$('#alert-success').hide();
-		$('#alert-error').hide();
-		$('#upload-progress-box').hide();
- 	}
- 
-    function status(message) {
-    	hideAlerts();
-		$('#alert-status').text(message).show();
-    }
-    
-    function success(message) {
-    	hideAlerts();
-		$('#alert-success').text(message).show();
-    }
-    
-    function error(message) {
-    	hideAlerts();
-		$('#alert-error').text(message).show();
-    }
-	
-	function changeUserPicture(type) { 
-		var userData = {
-            uid: $('#inputUID').val(),
-			type: type
-        };
-            
-		$.post('/users/changepicture',
-        	userData,
-            function(data) {	
-       			socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
-            }                
-		);
-	}
-        
-	var selectedImageType = '';
-    
-    $('#submitBtn').on('click',function(){
-    	
-       var userData = {
-            uid:$('#inputUID').val(),
-            email:$('#inputEmail').val(),
-            fullname:$('#inputFullname').val(),
-            website:$('#inputWebsite').val(),
-            birthday:$('#inputBirthday').val(),
-            location:$('#inputLocation').val(),
-            signature:$('#inputSignature').val(),
-        };
-            
-		$.post('/users/doedit',
-        	userData,
-            function(data) {
-            	if(data.error) {
-            		app.alert({
-				      'alert_id': 'user_profile_updated',
-				      type: 'error',
-				      title: 'Profile Update Error',
-				      message: data.error,
-				      timeout: 2000
-				    });
-            		return;
-            	}
-            	
-				app.alert({
-			      'alert_id': 'user_profile_updated',
-			      type: 'success',
-			      title: 'Profile Updated',
-			      message: 'Your profile has been updated successfully',
-			      timeout: 2000
-			    });
-            }
-		);
-		return false;
-    });
-    
-    function updateImages() {
-		var currentPicture = $('#user-current-picture').attr('src');
-
-		if(gravatarPicture) {
-        	$('#user-gravatar-picture').attr('src', gravatarPicture);
-        	$('#gravatar-box').show();
-        }
-        else
-        	$('#gravatar-box').hide();
-
-    	if(uploadedPicture) {
-        	$('#user-uploaded-picture').attr('src', uploadedPicture);
-        	$('#uploaded-box').show();
-        }
-        else
-        	$('#uploaded-box').hide();
-        	
-        	
-         if(currentPicture == gravatarPicture)
-	        $('#gravatar-box .icon-ok').show();
-		else
-	        $('#gravatar-box .icon-ok').hide();
-	        
-        if(currentPicture == uploadedPicture)
-	        $('#uploaded-box .icon-ok').show();
-		else
-	        $('#uploaded-box .icon-ok').hide();
-    }
-    
-    
-    $('#changePictureBtn').on('click', function() {
-		selectedImageType = '';
-		updateImages();
-    	
-    	$('#change-picture-modal').modal('show');
-    	
-    	return false;
-    });
-    
-    $('#gravatar-box').on('click', function(){
-		$('#gravatar-box .icon-ok').show();
-        $('#uploaded-box .icon-ok').hide();
-        selectedImageType = 'gravatar';
-	});
-	
-	$('#uploaded-box').on('click', function(){
-		$('#gravatar-box .icon-ok').hide();
-        $('#uploaded-box .icon-ok').show();
-        selectedImageType = 'uploaded';
-	});
-	
-	$('#savePictureChangesBtn').on('click', function() {
-    	$('#change-picture-modal').modal('hide');
-
-    	if(selectedImageType) {
-        	changeUserPicture(selectedImageType);
-        	
-        	if(selectedImageType == 'gravatar')
-		        $('#user-current-picture').attr('src', gravatarPicture);		
-			else if(selectedImageType == 'uploaded')
-		        $('#user-current-picture').attr('src', uploadedPicture);						
-        }
-    	
-	});
-	
-	$('#upload-picture-modal').on('hide', function() {
-		$('#userPhotoInput').val('');
-	});
-	
-	$('#uploadPictureBtn').on('click', function(){
-		
-    	$('#change-picture-modal').modal('hide');
-    	$('#upload-picture-modal').modal('show');
-
-    	hideAlerts();
-    	
-    	return false;
-	});
-	
-	$('#pictureUploadSubmitBtn').on('click', function() {
-		$('#uploadForm').submit();
-	});
-    
-    
-});
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/accountedit.js"></script>
\ No newline at end of file
diff --git a/public/templates/category.tpl b/public/templates/category.tpl
index 320134ef11..89cc373aa7 100644
--- a/public/templates/category.tpl
+++ b/public/templates/category.tpl
@@ -82,84 +82,4 @@
 
 
 <input type="hidden" template-variable="category_id" value="{category_id}" />
-
-<script type="text/javascript">
-(function() {
-	var cid = templates.get('category_id'),
-		room = 'category_' + cid;
-		
-	app.enter_room(room);
-
-	var new_post = document.getElementById('new_post');
-	new_post.onclick = function() {
-		app.open_post_window('topic', {category_id});
-	}
-
-	ajaxify.register_events([
-		'event:new_topic'
-	]);
-
-	if (jQuery('.category-item').length == 0) {
-		jQuery('#topics-container, .category-sidebar').hide();
-		jQuery('#category-no-topics').show();
-	}
-
-	socket.on('event:new_topic', function(data) {
-		var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }),
-			topic = document.createElement('div'),
-			container = document.getElementById('topics-container'),
-			topics = document.querySelectorAll('#topics-container a'),
-			numTopics = topics.length,
-			x;
-
-		jQuery('#topics-container, .category-sidebar').show();
-		jQuery('#category-no-topics').hide();
-
-		topic.innerHTML = html;
-		if (numTopics > 0) {
-			for(x=0;x<numTopics;x++) {
-				if (topics[x].querySelector('.icon-pushpin')) continue;
-				container.insertBefore(topic.querySelector('a'), topics[x]);
-				$(topic).hide().fadeIn('slow');
-				break;
-			}
-		} else {
-			container.insertBefore(topic.querySelector('a'), null);
-			$(topic).hide().fadeIn('slow');
-		}
-
-		// jQuery('<div></div>').appendTo("#topics-container").hide().append(html).fadeIn('slow');	
-		// set_up_posts(uniqueid);
-	});
-
-
-
-	socket.emit('api:categories.getRecentReplies', cid);
-	socket.on('api:categories.getRecentReplies', function(replies) {
-		if (replies === false) {
-			return;
-		}
-		
-		var users = replies.users,
-			posts = replies.posts,
-			recent_replies = document.getElementById('category_recent_replies');
-
-		recent_replies.innerHTML = '';
-		for (var i=0, ii=posts.pids.length; i<ii; i++) {
-			var a = document.createElement('a'),
-				ul = document.createElement('ul'),
-				username = users[posts.uid[i]].username,
-				picture = users[posts.uid[i]].picture;
-
-			//temp until design finalized
-			ul.innerHTML = '<li><img title="' + username + '" style="width: 48px; height: 48px; /*temporary*/" src="' + picture + '" class="" />'
-							+ '<p><strong>' + username + '</strong>: ' + posts.content[i] + '</p><span>posted ' + utils.relativeTime(posts.timestamp[i]) + ' ago</span></li>';
-			
-			a.appendChild(ul);
-			recent_replies.appendChild(a);
-		}
-		
-	});
-
-})();
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/category.js"></script>
\ No newline at end of file
diff --git a/public/templates/footer.tpl b/public/templates/footer.tpl
index 133771bda6..276d9abaa2 100644
--- a/public/templates/footer.tpl
+++ b/public/templates/footer.tpl
@@ -11,132 +11,7 @@
 		<footer class="footer">Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.com">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></footer>
 	</div>
 
-	<script type="text/javascript">
-	(function() {
-		var num_users = document.getElementById('number_of_users'),
-			latest_user = document.getElementById('latest_user'),
-			active_users = document.getElementById('active_users'),
-			user_label = document.getElementById('user_label'),
-			active_record = document.getElementById('active_record'),
-			right_menu = document.getElementById('right-menu');
-
-		socket.emit('user.count', {});
-		socket.on('user.count', function(data) {
-			num_users.innerHTML = "We currently have <b>" + data.count + "</b> registered users.";
-		});
-		socket.emit('user.latest', {});
-		socket.on('user.latest', function(data) {
-			if (data.username == '') {
-				latest_user.innerHTML = '';
-			} else {
-				latest_user.innerHTML = "The most recent user to register is <b><a href='/users/"+data.username+"'>" + data.username + "</a></b>.";
-			}
-		});
-		socket.emit('api:user.active.get');
-		socket.on('api:user.active.get', function(data) {
-			var plural_users = parseInt(data.users) !== 1,
-				plural_anon = parseInt(data.anon) !== 1;
-
-			active_users.innerHTML = 'There ' + (plural_users ? 'are' : 'is') + ' <strong>' + data.users + '</strong> user' + (plural_users ? 's' : '') + ' and <strong>' + data.anon + '</strong> guest' + (plural_anon ? 's' : '') + ' online';
-		});
-		socket.emit('api:user.active.get_record');
-		socket.on('api:user.active.get_record', function(data) {
-			active_record.innerHTML = "most users ever online was <strong>" + data.record + "</strong> on <strong>" + (new Date(parseInt(data.timestamp,10))).toUTCString() + "</strong>";
-		});
-
-		socket.emit('api:updateHeader', { fields: ['username', 'picture'] });
-
-		socket.on('api:updateHeader', function(data) {
-			var rightMenu = $('#right-menu');
-			if (data.uid > 0) {
-				var userLabel = rightMenu.find('#user_label');
-				userLabel.attr('href','/users/'+data['username']);
-
-				userLabel.find('img').attr('src',data['picture']+"?s=24");
-				userLabel.find('span').html(data['username']);
-				
-			} else {
-				
-				rightMenu.html('');
-
-				var registerEl = document.createElement('li'),
-					loginEl = document.createElement('li');
-
-				registerEl.innerHTML = '<a href="/register">Register</a>';
-				loginEl.innerHTML = '<a href="/login">Login</a>';
-
-				right_menu.appendChild(registerEl);
-				right_menu.appendChild(loginEl);
-			}
-		});
-
-		// Post window events
-		var	postWindowEl = document.getElementById('post_window'),
-			discardEl = document.getElementById('discard-post');
-		discardEl.addEventListener('click', function() {
-			$(postWindowEl).slideToggle(250);
-			$(document.body).removeClass('composing');
-		}, false);
-
-		// Notifications dropdown
-		var notifContainer = document.getElementsByClassName('notifications')[0],
-			notifTrigger = notifContainer.querySelector('a'),
-			notifList = document.getElementById('notif-list');
-		notifTrigger.addEventListener('click', function() {
-			if (notifContainer.className.indexOf('open') === -1) socket.emit('api:notifications.get');
-		});
-		notifList.addEventListener('click', function(e) {
-			var target;
-			switch(e.target.nodeName) {
-				case 'SPAN': target = e.target.parentNode.parentNode; break;
-				case 'A': target = e.target.parentNode; break;
-				case 'li': target = e.target; break;
-			}
-			if (target) {
-				var nid = parseInt(target.getAttribute('data-nid'));
-				if (nid > 0) socket.emit('api:notifications.mark_read', nid);
-			}
-		})
-		socket.on('api:notifications.get', function(data) {
-			var	notifFrag = document.createDocumentFragment(),
-				notifEl = document.createElement('li'),
-				numRead = data.read.length,
-				numUnread = data.unread.length,
-				x;
-			notifList.innerHTML = '';
-			if (data.read.length + data.unread.length > 0) {
-				for(x=0;x<numUnread;x++) {
-					notifEl.setAttribute('data-nid', data.unread[x].nid);
-					notifEl.className = 'unread';
-					notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
-					notifFrag.appendChild(notifEl.cloneNode(true));
-				}
-				for(x=0;x<numRead;x++) {
-					notifEl.setAttribute('data-nid', data.read[x].nid);
-					notifEl.className = '';
-					notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>';
-					notifFrag.appendChild(notifEl.cloneNode(true));
-				}
-			} else {
-				notifEl.innerHTML = '<a>You have no notifications</a>';
-				notifFrag.appendChild(notifEl);
-			}
-			notifList.appendChild(notifFrag);
-		});
-		socket.on('api:notifications.counts', function(counts) {
-			var notifIcon = document.querySelector('.notifications a i');
-			if(notifIcon) {
-				if (counts.unread > 0) notifIcon.className = 'icon-circle active';
-				else notifIcon.className = 'icon-circle-blank';
-			}
-		});
-		socket.on('event:new_notification', function() {
-			console.log('WOOT');
-			document.querySelector('.notifications a i').className = 'icon-circle active';
-		});
-		socket.emit('api:notifications.counts');
-	}());
-	</script>
+	<script type="text/javascript" src="src/forum/footer.js"></script>
 	<!-- END Forum Info -->
 </body>
 </html>
\ No newline at end of file
diff --git a/public/templates/friends.tpl b/public/templates/friends.tpl
index 37a899de38..aee731db35 100644
--- a/public/templates/friends.tpl
+++ b/public/templates/friends.tpl
@@ -44,50 +44,4 @@
 <input type="hidden" template-variable="theirid" value="{theirid}" />
 <input type="hidden" template-variable="friendCount" value="{friendCount}" />
 
-<script type="text/javascript">
-
-var yourid = templates.get('yourid'),
-	theirid = templates.get('theirid'),
-	friendCount = templates.get('friendCount');
-
-(function() {
-    
-    $(document).ready(function() {
-    	
-    	if(parseInt(friendCount, 10) === 0) {
-    		$('#no-friend-notice').show();
-    	}
-    	var editLink = $('#editLink');
-
-		if(yourid !== theirid) {
-			editLink.hide();
-			$('.remove-friend-btn').hide();
-		}
-		else {
-			$('.remove-friend-btn').on('click',function(){
-
-				var removeBtn = $(this);
-				var friendid = $(this).attr('friendid');
-				
-				$.post('/users/removefriend', {uid: friendid},
-	            	function(data) {
-	            		removeBtn.parent().remove();
-					}                
-				);
-				return false;
-			});
-		}
-
-        $('.reputation').each(function(index, element) {
-        	$(element).html(app.addCommas($(element).html()));
-        });
-        
-        $('.postcount').each(function(index, element) {
-        	$(element).html(app.addCommas($(element).html()));
-        });
-        
-    });
-    
-
-}());
-</script>
+<script type="text/javascript" src="/src/forum/friends.js"></script>
diff --git a/public/templates/login.tpl b/public/templates/login.tpl
index 875e4760bd..c01517915f 100644
--- a/public/templates/login.tpl
+++ b/public/templates/login.tpl
@@ -22,12 +22,4 @@
 	</div>
 </div>
 
-<script>
-	// Alternate Logins
-	var altLoginEl = document.querySelector('.alt-logins');
-	altLoginEl.addEventListener('click', function(e) {
-		if (e.target.nodeName === 'LI') {
-			document.location.href = e.target.getAttribute('data-url');
-		}
-	});
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/login.js"></script>
\ No newline at end of file
diff --git a/public/templates/register.tpl b/public/templates/register.tpl
index 875b3bab8d..fdf65eb37b 100644
--- a/public/templates/register.tpl
+++ b/public/templates/register.tpl
@@ -17,88 +17,5 @@
 		</ul>
 	</div>
 </div>
-<script type="text/javascript">
-(function() {
-	var username = document.getElementById('username'),
-		password = document.getElementById('password'),
-		register = document.getElementById('register'),
-		emailEl = document.getElementById('email'),
-		username_notify = document.getElementById('username-notify'),
-		email_notify = document.getElementById('email-notify'),
-		password_notify = document.getElementById('password-notify');
 
-	username.onkeyup = function() {
-		if (username.value.length > 2) socket.emit('user.exists', {username: username.value});
-		else {
-			username_notify.innerHTML = 'Username too short';
-			username_notify.className = 'label label-important';
-		}
-	}
-	emailEl.addEventListener('change', function() {
-		socket.emit('user.email.exists', { email: emailEl.value });
-	}, false);
-	password.addEventListener('keyup', function() {
-		if (password.value.length < 5) {
-			password_notify.innerHTML = 'Password too short';
-		} else {
-			password_notify.innerHTML = '';
-		}
-	}, false);
-
-	ajaxify.register_events(['user.exists', 'user.email.exists']);
-
-	socket.on('user.exists', function(data) {
-		if (data.exists == true) {
-			username_notify.innerHTML = 'Username exists';
-			username_notify.className = 'label label-important';
-		} else {
-			username_notify.innerHTML = 'Not taken';
-			username_notify.className = 'label label-success';
-		}
-	});
-	socket.on('user.email.exists', function(data) {
-		if (data.exists === true) {
-			email_notify.innerHTML = 'Email Address exists';
-		} else {
-			email_notify.innerHTML = '';
-		}
-	});
-
-	// Alternate Logins
-	var altLoginEl = document.querySelector('.alt-logins');
-	altLoginEl.addEventListener('click', function(e) {
-		if (e.target.nodeName === 'LI') {
-			document.location.href = e.target.getAttribute('data-url');
-		}
-	});
-
-	// Form Validation
-	function validateForm() {
-		var validated = true;
-		if (username.value.length < 2) {
-			username_notify.innerHTML = 'Invalid username';
-			username_notify.className = 'label label-important';
-			validated = false;
-		}
-
-		if (password.value.length < 5) {
-			password_notify.innerHTML = 'Password too short';
-			validated = false;
-		} else {
-			password_notify.innerHTML = '';
-		}
-
-		if (email.value.indexOf('@') === -1) {
-			email_notify.innerHTML = 'Invalid email address';
-			validated = false;
-		} else {
-			email_notify.innerHTML = '';
-		}
-
-		return validated;
-	}
-	register.addEventListener('click', function(e) {
-		if (!validateForm()) e.preventDefault();
-	}, false);
-}());
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/register.js"></script>
\ No newline at end of file
diff --git a/public/templates/reset.tpl b/public/templates/reset.tpl
index 3dee8b302b..3a1857a4f4 100644
--- a/public/templates/reset.tpl
+++ b/public/templates/reset.tpl
@@ -13,44 +13,5 @@
 	<label for="email">Email Address</label><input type="text" placeholder="Enter Email Address" id="email" /><br />
 	<button class="btn btn-primary" id="reset" type="submit">Reset Password</button>
 </div>
-<script type="text/javascript">
-(function() {
-	var	inputEl = document.getElementById('email'),
-		errorEl = document.getElementById('error'),
-		errorTextEl = errorEl.querySelector('p');
 
-	document.getElementById('reset').onclick = function() {
-		if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
-			socket.emit('user:reset.send', { email: inputEl.value });
-		} else {
-			jQuery('#success').hide();
-			jQuery(errorEl).show();
-			errorTextEl.innerHTML = 'Please enter a valid email';
-		}
-	};
-
-	ajaxify.register_events(['user.send_reset']);
-	
-	socket.on('user.send_reset', function(data) {
-		var	submitEl = document.getElementById('reset');
-
-		if (data.status === 'ok') {
-			jQuery('#error').hide();
-			jQuery('#success').show();
-			jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
-			inputEl.value = '';
-		} else {
-			jQuery('#success').hide();
-			jQuery(errorEl).show();
-			switch(data.message) {
-				case 'invalid-email':
-					errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
-				break;
-				case 'send-failed':
-					errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
-				break;
-			}
-		}
-	});
-}());
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/reset.js"></script>
\ No newline at end of file
diff --git a/public/templates/reset_code.tpl b/public/templates/reset_code.tpl
index b791933bf6..a37d4534df 100644
--- a/public/templates/reset_code.tpl
+++ b/public/templates/reset_code.tpl
@@ -22,47 +22,4 @@
 <input type="hidden" template-variable="reset_code" value="{reset_code}" />
 
 
-<script type="text/javascript">
-(function() {
-	var reset_code = templates.get('reset_code');
-
-	var	resetEl = document.getElementById('reset'),
-		password = document.getElementById('password'),
-		repeat = document.getElementById('repeat'),
-		noticeEl = document.getElementById('notice');
-
-	resetEl.addEventListener('click', function() {
-		if (password.value.length < 6) {
-			$('#error').hide();
-			noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
-			noticeEl.querySelector('p').innerHTML = 'The password entered it too short, please pick a different password!';
-			noticeEl.style.display = 'block';
-		} else if (password.value === repeat.value) {
-			socket.emit('user:reset.commit', { code: reset_code, password: password.value });
-		}
-	}, false);
-
-	// Enable the form if the code is valid
-	socket.emit('user:reset.valid', { code: reset_code });
-
-
-	ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
-	socket.on('user:reset.valid', function(data) {
-		if (!!data.valid) resetEl.disabled = false;
-		else {
-			var formEl = document.getElementById('reset-form');
-			// Show error message
-			$('#error').show();
-			formEl.parentNode.removeChild(formEl);
-		}
-	})
-
-	socket.on('user:reset.commit', function(data) {
-		if (data.status === 'ok') {
-			$('#error').hide();
-			$('#notice').hide();
-			$('#success').show();
-		}
-	});
-}());
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/reset_code.js"></script>
\ No newline at end of file
diff --git a/public/templates/topic.tpl b/public/templates/topic.tpl
index 66bd53fb9f..90f628fdce 100644
--- a/public/templates/topic.tpl
+++ b/public/templates/topic.tpl
@@ -123,608 +123,4 @@
 
 
 
-<script type="text/javascript">
-	(function() {
-		var	expose_tools = templates.get('expose_tools'),
-			tid = templates.get('topic_id'),
-			postListEl = document.getElementById('post-container'),
-			editBtns = document.querySelectorAll('#post-container .post-buttons .edit, #post-container .post-buttons .edit i'),
-			thread_state = {
-				locked: templates.get('locked'),
-				deleted: templates.get('deleted'),
-				pinned: templates.get('pinned')
-			},
-			topic_name = templates.get('topic_name');
-
-		function addCommasToNumbers() {
-			$('.formatted-number').each(function(index, element) {
-	        	$(element).html(app.addCommas($(element).html()));
-        	});
-		}
-
-		jQuery('document').ready(function() {
-
-			addCommasToNumbers();
-			
-			var	room = 'topic_' + tid,
-				adminTools = document.getElementById('thread-tools');
-
-			app.enter_room(room);
-			set_up_posts();
-
-			if (thread_state.locked === '1') set_locked_state(true);
-			if (thread_state.deleted === '1') set_delete_state(true);
-			if (thread_state.pinned === '1') set_pinned_state(true);
-
-			if (expose_tools === '1') {
-				var deleteThreadEl = document.getElementById('delete_thread'),
-					lockThreadEl = document.getElementById('lock_thread'),
-					pinThreadEl = document.getElementById('pin_thread'),
-					moveThreadEl = document.getElementById('move_thread'),
-					moveThreadModal = $('#move_thread_modal');
-
-				adminTools.style.visibility = 'inherit';
-
-				// Add events to the thread tools
-				deleteThreadEl.addEventListener('click', function(e) {
-					e.preventDefault();
-					if (thread_state.deleted !== '1') {
-						if (confirm('really delete thread? (THIS DIALOG TO BE REPLACED WITH BOOTBOX)')) {
-							socket.emit('api:topic.delete', { tid: tid });
-						}
-					} else {
-						if (confirm('really restore thread? (THIS DIALOG TO BE REPLACED WITH BOOTBOX)')) {
-							socket.emit('api:topic.restore', { tid: tid });
-						}
-					}
-				}, false);
-
-				lockThreadEl.addEventListener('click', function(e) {
-					e.preventDefault();
-					if (thread_state.locked !== '1') {
-						socket.emit('api:topic.lock', { tid: tid });
-					} else {
-						socket.emit('api:topic.unlock', { tid: tid });
-					}
-				}, false);
-
-				pinThreadEl.addEventListener('click', function(e) {
-					e.preventDefault();
-					if (thread_state.pinned !== '1') {
-						socket.emit('api:topic.pin', { tid: tid });
-					} else {
-						socket.emit('api:topic.unpin', { tid: tid });
-					}
-				}, false);
-
-				moveThreadEl.addEventListener('click', function(e) {
-					e.preventDefault();
-					moveThreadModal.modal('show');
-				}, false);
-
-				moveThreadModal.on('shown', function() {
-					var loadingEl = document.getElementById('categories-loading');
-					if (loadingEl) {
-						socket.once('api:categories.get', function(data) {
-							// Render categories
-							var	categoriesFrag = document.createDocumentFragment(),
-								categoryEl = document.createElement('li'),
-								numCategories = data.categories.length,
-								modalBody = moveThreadModal.find('.modal-body'),
-								categoriesEl = modalBody[0].getElementsByTagName('ul')[0],
-								confirmDiv = document.getElementById('move-confirm'),
-								confirmCat = confirmDiv.getElementsByTagName('span')[0],
-								commitEl = document.getElementById('move_thread_commit'),
-								cancelEl = document.getElementById('move_thread_cancel'),
-								x, info, targetCid, targetCatLabel;
-
-							categoriesEl.className = 'category-list';
-							for(x=0;x<numCategories;x++) {
-								info = data.categories[x];
-								categoryEl.className = info.blockclass;
-								categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name;
-								categoryEl.setAttribute('data-cid', info.cid);
-								categoriesFrag.appendChild(categoryEl.cloneNode(true));
-							}
-							categoriesEl.appendChild(categoriesFrag);
-							modalBody[0].removeChild(loadingEl);
-
-							categoriesEl.addEventListener('click', function(e) {
-								if (e.target.nodeName === 'LI') {
-									confirmCat.innerHTML = e.target.innerHTML;
-									confirmDiv.style.display = 'block';
-									targetCid = e.target.getAttribute('data-cid');
-									targetCatLabel = e.target.innerHTML;
-									commitEl.disabled = false;
-								}
-							}, false);
-
-							commitEl.addEventListener('click', function() {
-								if (!commitEl.disabled && targetCid) {
-									commitEl.disabled = true;
-									$(cancelEl).fadeOut(250);
-									$(moveThreadModal).find('.modal-header button').fadeOut(250);
-									commitEl.innerHTML = 'Moving <i class="icon-spin icon-refresh"></i>';
-
-									socket.once('api:topic.move', function(data) {
-										moveThreadModal.modal('hide');
-										if (data.status === 'ok') {
-											app.alert({
-												'alert_id': 'thread_move',
-												type: 'success',
-												title: 'Topic Successfully Moved',
-												message: 'This topic has been successfully moved to ' + targetCatLabel,
-												timeout: 5000
-											});
-										} else {
-											app.alert({
-												'alert_id': 'thread_move',
-												type: 'error',
-												title: 'Unable to Move Topic',
-												message: 'This topic could not be moved to ' + targetCatLabel + '.<br />Please try again later',
-												timeout: 5000
-											});
-										}
-									});
-									socket.emit('api:topic.move', { tid: tid, cid: targetCid });
-								}
-							});
-						});
-						socket.emit('api:categories.get');
-					}
-				});
-			}
-
-			// Fix delete state for this thread's posts
-			var	postEls = document.querySelectorAll('#post-container li[data-deleted]');
-			for(var x=0,numPosts=postEls.length;x<numPosts;x++) {
-				if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid'));
-				postEls[x].removeAttribute('data-deleted');
-			}
-		});
-
-
-		$('.post-container').delegate('.edit', 'click', function(e) {
-			var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1];
-
-			var main = $(this).parents('.main-post');
-			if(main.length > 0) 
-				app.open_post_window('edit', tid, topic_name, pid);
-			else 
-				app.open_post_window('edit', tid, "", pid);
-		
-		});
-
-		$('.post-container').delegate('.delete', 'click', function(e) {
-			var	pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1],
-			postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
-			deleteAction = !postEl.hasClass('deleted') ? true : false,
-			confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
-
-			if (confirmDel) {
-				deleteAction ?
-				socket.emit('api:posts.delete', { pid: pid }) :
-				socket.emit('api:posts.restore', { pid: pid });
-			}
-		}); 
-
-		// CHAT, move to chat.js later?
-
-		$('.post-container').delegate('.chat', 'click', function(e){
-
-			var username = $(this).parents('li').attr('data-username');
-			var touid = $(this).parents('li').attr('data-uid');
-
-			var chatModal = createModalIfDoesntExist(username, touid);
-
-			chatModal.show();
-			bringModalToTop(chatModal);
-			
-		});
-
-		function bringModalToTop(chatModal) {
-			var topZ = 0;
-			$('.modal').each(function(){
-			  var thisZ = parseInt($(this).css('zIndex'), 10);
-			  if (thisZ > topZ){
-			    topZ = thisZ;
-			  }
-			});
-			chatModal.css('zIndex', topZ+1);
-		}
-
-		function createModalIfDoesntExist(username, touid) {
-			var chatModal = $('#chat-modal-'+touid);
-
-			if(!chatModal.length) {
-				var chatModal = $('#chat-modal').clone();
-				chatModal.attr('id','chat-modal-'+touid);
-				chatModal.appendTo($('body'));
-				chatModal.draggable({
-					start:function(){
-						bringModalToTop(chatModal);
-					}
-				});
-				chatModal.find('#chat-with-name').html(username);
-
-				chatModal.find('.close').on('click',function(e){
-					chatModal.hide();
-				});
-
-				chatModal.on('click', function(e){
-					bringModalToTop(chatModal);
-				});
-
-				addSendHandler(chatModal, touid);	
-			}
-
-			return chatModal;
-		}
-
-		function addSendHandler(chatModal, touid) {
-			chatModal.find('#chat-message-input').off('keypress');
-			chatModal.find('#chat-message-input').on('keypress', function(e) {
-				if(e.which === 13) {
-					sendMessage(chatModal, touid);
-				}
-			});
-
-			chatModal.find('#chat-message-send-btn').off('click');
-			chatModal.find('#chat-message-send-btn').on('click', function(e){
-				sendMessage(chatModal, touid);
-				return false;
-			});
-		}
-
-		function sendMessage(chatModal, touid) {
-			var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
-			if(msg.length) {
-				msg = msg +'\n';
-				socket.emit('sendChatMessage', { touid:touid, message:msg});
-				chatModal.find('#chat-message-input').val('');
-				appendChatMessage(chatModal, 'You : ' + msg);
-			}
-		}
-
-		socket.on('chatMessage', function(data){
-			var username = data.username;
-			var fromuid = data.fromuid;
-			var message = data.message;
-
-			var chatModal = createModalIfDoesntExist(username, fromuid);
-			chatModal.show();
-			bringModalToTop(chatModal);
-		
-			appendChatMessage(chatModal, message)
-		});
-
-		function appendChatMessage(chatModal, message){
-			var chatContent = chatModal.find('#chat-content');
-			chatContent.append(message);
-    		chatContent.scrollTop(
-        		chatContent[0].scrollHeight - chatContent.height()
-    		);
-		}
-		//end of chat
-
-
-		ajaxify.register_events([
-			'event:rep_up', 'event:rep_down', 'event:new_post', 'api:get_users_in_room',
-			'event:topic_deleted', 'event:topic_restored', 'event:topic:locked',
-			'event:topic_unlocked', 'event:topic_pinned', 'event:topic_unpinned',
-			'event:topic_moved', 'event:post_edited', 'event:post_deleted', 'event:post_restored',
-			'api:posts.favourite', 'chatMessage'
-		]);
-
-
-		socket.on('api:get_users_in_room', function(users) {
-			var anonymous = users.anonymous,
-				usernames = users.usernames,
-				usercount = usernames.length;
-
-			for (var i = 0, ii=usercount; i<ii; i++) {
-				usernames[i] = '<strong>' + '<a href="/users/'+usernames[i]+'">' + usernames[i] + '</a></strong>';
-			}
-
-			// headexplosion.gif for fun, to see if I could do this in one line of code. feel free to refactor haha
-			var active =
-				((usercount === 1) ? usernames[0] : '')
-				+ ((usercount === 2 && anonymous === 0) ? usernames[0] + ' and ' + usernames[1] : '')
-				+ ((usercount > 2 && anonymous === 0) ? usernames.join(', ').replace(/,([^,]*)$/, ", and$1") : '')
-				+ (usercount > 1 && anonymous > 0 ? usernames.join(', ') : '')
-				+ ((anonymous > 0) ? (usercount > 0 ? ' and ': '') + anonymous + ' guest' + (anonymous > 1  ? 's are': ' is') : '')
-				+ (anonymous === 0 ? (usercount > 1 ? ' are' : ' is') : '') + ' browsing this thread';
-
-			document.getElementById('thread_active_users').innerHTML = active;
-		});
-
-		socket.on('event:rep_up', function(data) {
-			adjust_rep(1, data.pid, data.uid);
-		});
-
-		socket.on('event:rep_down', function(data) {
-			adjust_rep(-1, data.pid, data.uid);
-		});
-
-		socket.on('event:new_post', function(data) {
-			var html = templates.prepare(templates['topic'].blocks['posts']).parse(data),
-				uniqueid = new Date().getTime();
-				
-			jQuery('<div id="' + uniqueid + '"></div>')
-				.appendTo("#post-container")
-				.hide()
-				.append(html)
-				.fadeIn('slow');
-
-			set_up_posts(uniqueid);
-			
-			addCommasToNumbers();
-		});
-
-		socket.on('event:topic_deleted', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_locked_state(true);
-				set_delete_state(true);
-			}
-		});
-
-		socket.on('event:topic_restored', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_locked_state(false);
-				set_delete_state(false);
-			}
-		});
-
-		socket.on('event:topic_locked', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_locked_state(true, 1);
-			}
-		});
-
-		socket.on('event:topic_unlocked', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_locked_state(false, 1);
-			}
-		});
-
-		socket.on('event:topic_pinned', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_pinned_state(true, 1);
-			}
-		});
-
-		socket.on('event:topic_unpinned', function(data) {
-			if (data.tid === tid && data.status === 'ok') {
-				set_pinned_state(false, 1);
-			}
-		});
-
-		socket.on('event:topic_moved', function(data) {
-			if (data && data.tid > 0) ajaxify.go('topic/' + data.tid);
-		});
-
-		socket.on('event:post_edited', function(data) {
-			var editedPostEl = document.getElementById('content_' + data.pid);
-
-			var editedPostTitle = $('#topic_title_'+data.pid);
-			
-			if(editedPostTitle.length > 0) {
-				editedPostTitle.fadeOut(250, function() {
-					editedPostTitle.html(data.title);
-					editedPostTitle.fadeIn(250);
-				});
-			}
-
-			$(editedPostEl).fadeOut(250, function() {
-				this.innerHTML = data.content;
-				$(this).fadeIn(250);
-			});
-		});
-
-		socket.on('api:posts.favourite', function(data) {
-			if (data.status !== 'ok' && data.pid) {
-				var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
-				if (favEl) favEl.className = 'icon-star-empty';
-			}
-		});
-
-		socket.on('event:post_deleted', function(data) {
-			if (data.pid) toggle_post_delete_state(data.pid, true);
-		});
-
-		socket.on('event:post_restored', function(data) {
-			if (data.pid) toggle_post_delete_state(data.pid, true);
-		});
-
-		function adjust_rep(value, pid, uid) {
-			var post_rep = jQuery('.post_rep_' + pid),
-				user_rep = jQuery('.user_rep_' + uid);
-
-			var ptotal = parseInt(post_rep.html(), 10),
-				utotal = parseInt(user_rep.html(), 10);
-
-			ptotal += value;
-			utotal += value;
-
-			post_rep.html(ptotal);
-			user_rep.html(utotal);
-		}
-
-
-		function set_up_posts(div) {
-			if (div == null) div = '';
-			else div = '#' + div;
-
-			jQuery(div + ' .post_reply').click(function() {
-				if (thread_state.locked !== '1') app.open_post_window('reply', tid, topic_name);
-			});
-
-			jQuery(div + ' .quote').click(function() {
-				if (thread_state.locked !== '1') app.open_post_window('quote', tid, topic_name);
-				
-				var pid = $(this).parents('li').attr('data-pid');
-				
-				$('#post_content').val('> ' + $('#content_' + pid).html() + '\n');
-				console.log("POST ID "+pid);
-			});
-
-			jQuery(div + ' .edit, ' + div + ' .delete').each(function() {
-				var ids = this.id.replace('ids_', '').split('_'),
-					pid = ids[0],
-					uid = ids[1];
-
-			});
-
-			jQuery(div + ' .favourite').click(function() {
-				var ids = this.id.replace('favs_', '').split('_'),
-					pid = ids[0],
-					uid = ids[1];
-
-				if (thread_state.locked !== '1') {
-					if (this.children[1].className == 'icon-star-empty') {
-						this.children[1].className = 'icon-star';
-						socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room});
-					}
-					else {
-						this.children[1].className = 'icon-star-empty';
-						socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room});
-					}
-				}
-			});
-		}
-
-		function set_locked_state(locked, alert) {
-			var	threadReplyBtn = document.getElementById('post_reply'),
-				postReplyBtns = document.querySelectorAll('#post-container .post_reply'),
-				quoteBtns = document.querySelectorAll('#post-container .quote'),
-				editBtns = document.querySelectorAll('#post-container .edit'),
-				deleteBtns = document.querySelectorAll('#post-container .delete'),
-				numReplyBtns = postReplyBtns.length,
-				lockThreadEl = document.getElementById('lock_thread'),
-				x;
-
-			if (locked === true) {
-				lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread';
-				threadReplyBtn.disabled = true;
-				threadReplyBtn.innerHTML = 'Locked <i class="icon-lock"></i>';
-				for(x=0;x<numReplyBtns;x++) {
-					postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>';
-					quoteBtns[x].style.display = 'none';
-					editBtns[x].style.display = 'none';
-					deleteBtns[x].style.display = 'none';
-				}
-
-				if (alert) {
-					app.alert({
-						'alert_id': 'thread_lock',
-						type: 'success',
-						title: 'Thread Locked',
-						message: 'Thread has been successfully locked',
-						timeout: 5000
-					});
-				}
-
-				thread_state.locked = '1';
-			} else {
-				lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread';
-				threadReplyBtn.disabled = false;
-				threadReplyBtn.innerHTML = 'Reply';
-				for(x=0;x<numReplyBtns;x++) {
-					postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
-					quoteBtns[x].style.display = 'inline-block';
-					editBtns[x].style.display = 'inline-block';
-					deleteBtns[x].style.display = 'inline-block';
-				}
-
-				if (alert) {
-					app.alert({
-						'alert_id': 'thread_lock',
-						type: 'success',
-						title: 'Thread Unlocked',
-						message: 'Thread has been successfully unlocked',
-						timeout: 5000
-					});
-				}
-
-				thread_state.locked = '0';
-			}
-		}
-
-		function set_delete_state(deleted) {
-			var	deleteThreadEl = document.getElementById('delete_thread'),
-				deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0],
-				threadEl = document.querySelector('.post-container'),
-				deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
-
-			if (deleted) {
-				deleteTextEl.innerHTML = '<i class="icon-comment"></i> Restore Thread';
-				$(threadEl).addClass('deleted');
-
-				// Spawn a 'deleted' notice at the top of the page
-				deleteNotice.setAttribute('id', 'thread-deleted');
-				deleteNotice.className = 'alert';
-				deleteNotice.innerHTML = 'This thread has been deleted. Only users with thread management privileges can see it.';
-				document.getElementById('content').insertBefore(deleteNotice, threadEl);
-
-				thread_state.deleted = '1';
-			} else {
-				deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread';
-				$(threadEl).removeClass('deleted');
-				deleteNotice.parentNode.removeChild(deleteNotice);
-
-				thread_state.deleted = '0';
-			}
-		}
-
-		function set_pinned_state(pinned, alert) {
-			var pinEl = document.getElementById('pin_thread');
-
-			if (pinned) {
-				pinEl.innerHTML = '<i class="icon-pushpin"></i> Unpin Thread';
-				if (alert) {
-					app.alert({
-						'alert_id': 'thread_pin',
-						type: 'success',
-						title: 'Thread Pinned',
-						message: 'Thread has been successfully pinned',
-						timeout: 5000
-					});
-				}
-
-				thread_state.pinned = '1';
-			} else {
-				pinEl.innerHTML = '<i class="icon-pushpin"></i> Pin Thread';
-				if (alert) {
-					app.alert({
-						'alert_id': 'thread_pin',
-						type: 'success',
-						title: 'Thread Unpinned',
-						message: 'Thread has been successfully unpinned',
-						timeout: 5000
-					});
-				}
-
-				thread_state.pinned = '0';
-			}
-		}
-
-		function toggle_post_delete_state(pid) {
-			var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]'));
-				quoteEl = $(postEl[0].querySelector('.quote')),
-				favEl = $(postEl[0].querySelector('.favourite')),
-				replyEl = $(postEl[0].querySelector('.post_reply'));
-
-			if (!postEl.hasClass('deleted')) {
-				quoteEl.addClass('none');
-				favEl.addClass('none');
-				replyEl.addClass('none');
-			} else {
-				quoteEl.removeClass('none');
-				favEl.removeClass('none');
-				replyEl.removeClass('none');
-			}
-
-			postEl.toggleClass('deleted');
-		}
-	})();
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/topic.js"></script>
\ No newline at end of file
diff --git a/public/templates/users.tpl b/public/templates/users.tpl
index 684a28a875..65d64dad22 100644
--- a/public/templates/users.tpl
+++ b/public/templates/users.tpl
@@ -20,23 +20,4 @@
 	<!-- END users -->
 </div>
 
-<script>
-(function() {
-    
-   
-    
-    $(document).ready(function() {
-        
-        $('.reputation').each(function(index, element) {
-        	$(element).html(app.addCommas($(element).html()));
-        });
-        
-        $('.postcount').each(function(index, element) {
-        	$(element).html(app.addCommas($(element).html()));
-        });
-        
-    });
-    
-
-}());
-</script>
\ No newline at end of file
+<script type="text/javascript" src="/src/forum/users.js"></script>
\ No newline at end of file