Merge branch 'master' of github.com:psychobunny/node-forum

Conflicts:
	public/templates/header.tpl
	src/webserver.js
This commit is contained in:
Julian Lam
2013-05-02 09:23:11 -04:00
24 changed files with 518 additions and 218 deletions

View File

@@ -2,5 +2,6 @@
"socket" : {
"address" : "localhost",
"port" : "4567"
}
},
"api_url" : "http://localhost:4567/api/"
}

View File

@@ -22,8 +22,8 @@ var ajaxify = {};
ajaxify.go = function(url, callback) {
var url = url.replace(/\/$/, "");
var tpl_url = (url === '') ? 'home' : url;
var tpl_url = (url === '' || url === '/') ? 'home' : url.split('/')[0];
if (templates[tpl_url]) {
window.history.pushState({}, url, "/" + url);

View File

@@ -1,6 +1,8 @@
var socket,
config,
app = {};
app = {},
API_URL = null;
// todo: cleanup,etc
(function() {
@@ -8,6 +10,8 @@ var socket,
$.ajax({
url: '/config.json?v=' + new Date().getTime(),
success: function(data) {
API_URL = data.api_url;
config = data;
socket = io.connect('http://' + config.socket.address + config.socket.port? ':' + config.socket.port : '');
@@ -18,6 +22,10 @@ var socket,
socket.on('event:alert', function(data) {
app.alert(data);
});
socket.on('event:consolelog', function(data) {
console.log(data);
});
},
async: false
@@ -74,14 +82,63 @@ var socket,
}
}
var post_window = null;
app.open_post_window = function() {
var post_window = null,
submit_post_btn = null,
post_title = null,
reply_title = null,
post_content = null;
app.open_post_window = function(post_mode, id, title) {
submit_post_btn = submit_post_btn || document.getElementById('submit_post_btn');
post_title = post_title || document.getElementById('post_title');
reply_title = reply_title || document.getElementById('reply_title');
post_content = post_content || document.getElementById('post_content');
post_window = post_window || document.getElementById('post_window');
jQuery(post_window).slideToggle(250);
document.getElementById('post_title').focus();
if (post_mode == null || post_mode == 'topic') {
post_title.style.display = "block";
reply_title.style.display = "none";
post_title.focus();
submit_post_btn.onclick = function() {
app.post_topic();
}
} else {
post_title.style.display = "none";
reply_title.style.display = "block";
reply_title.innerHTML = 'You are replying to "' + title + '"';
post_content.focus();
submit_post_btn.onclick = function() {
app.post_reply(id)
}
}
};
app.post_reply = function(topic_id) {
var content = document.getElementById('post_content').value;
if (content.length < 5) {
app.alert({
title: 'Reply Failure',
message: 'You need to write more dude.',
type: 'error',
timeout: 2000
});
return;
}
socket.emit('api:posts.reply', {
'topic_id' : topic_id,
'content' : content
});
jQuery(post_window).slideToggle(250);
};
app.post_topic = function() {
var title = document.getElementById('post_title').value,
content = document.getElementById('post_content').value;
@@ -91,10 +148,7 @@ var socket,
title: 'Topic Post Failure',
message: 'You need to write more dude.',
type: 'error',
timeout: 2000,
clickfn: function() {
ajaxify.go('register');
}
timeout: 2000
});
return;

View File

@@ -1,9 +1,17 @@
var templates = {};
(function() {
var ready_callback;
templates.ready = function(callback) {
//quick implementation because introducing a lib to handle several async callbacks
if (callback == null) ready_callback();
else ready_callback = callback;
}
function loadTemplates(templatesToLoad) {
var timestamp = new Date().getTime();
var loaded = templatesToLoad.length;
for (var t in templatesToLoad) {
(function(file) {
@@ -18,6 +26,12 @@ var templates = {};
template.prototype.html = String(html);
templates[file] = new template;
loaded--;
if (loaded == 0) templates.ready();
}).fail(function() {
loaded--;
if (loaded == 0) templates.ready();
});
}(templatesToLoad[t]));
}
@@ -26,7 +40,7 @@ var templates = {};
function init() {
loadTemplates([
'header', 'footer', 'register', 'home',
'header', 'footer', 'register', 'home', 'topic',
'login', 'reset', 'reset_code', 'account_settings',
'emails/reset', 'emails/reset_plaintext'
]);
@@ -62,6 +76,10 @@ var templates = {};
var template = this.html, regex, block;
return (function parse(data, namespace, template) {
if (data.length == 0) {
regex = makeRegex('[^]*');
template = template.replace(regex, '');
}
for (var d in data) {
if (data.hasOwnProperty(d)) {
@@ -112,10 +130,10 @@ function load_template(callback) {
rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : '');
var url = location.href.replace(rootUrl +'/', '');
if (url == '') url = 'home';
jQuery.get('api/' + url, function(data) {
document.getElementById('content').innerHTML = templates[url].parse(JSON.parse(data));
url = (url === '' || url === '/') ? 'home' : url;
jQuery.get(API_URL + url, function(data) {
document.getElementById('content').innerHTML = templates[url.split('/')[0]].parse(JSON.parse(data));
if (callback) callback();
});
}

View File

@@ -1,18 +0,0 @@
<h1>Account Settings</h1>
<div class="well">
<div class="alert" id="message" style="display:none">
<button type="button" class="close" data-dismiss="message">&times;</button>
<strong></strong>
<p></p>
</div>
<!-- <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> -->
<p>
If you see this, you are logged in.
</p>
</div>
<script type="text/javascript">
(function() {
// ...
}());
</script>

View File

@@ -4,7 +4,7 @@
<!-- START Forum Info -->
<div id="footer" class="container" style="padding-top: 50px;">
<div class="alert alert-info">
<span id="active_users"></span><br />
<span id="active_users"></span>; <span id="active_record"></span><br />
<span id="number_of_users"></span><br />
<span id="latest_user"></span>
</div>
@@ -16,7 +16,9 @@
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');
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) {
@@ -24,22 +26,49 @@
});
socket.emit('user.latest', {});
socket.on('user.latest', function(data) {
latest_user.innerHTML = "The most recent user to register is <b>" + data.username + "</b>.";
if (data.username == '') {
latest_user.innerHTML = '';
} else {
latest_user.innerHTML = "The most recent user to register is <b>" + data.username + "</b>.";
}
});
socket.emit('api:user.active.get');
socket.on('api:user.active.get', function(data) {
active_users.innerHTML = 'There ' + (parseInt(data.users) !== 1 ? 'are' : 'is') + ' <strong>' + data.users + '</strong> user' + (parseInt(data.users) !== 1 ? 's' : '') + ' and <strong>' + data.anon + '</strong> guest' + (parseInt(data.anon) !== 1 ? 's' : '') + ' online';
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:user.get', { fields: ['username', 'picture'] });
socket.on('api:user.get', function(data) {
var gravatar = document.createElement('img'),
name = document.createElement('span');
if (data.uid > 0) {
var gravatar = document.createElement('img'),
name = document.createElement('span')
logoutEl = document.createElement('li');
name.innerHTML = data['username'];
gravatar.src = data['picture'];
logoutEl.innerHTML = '<a href="/logout">Log out</a>';
user_label.appendChild(gravatar);
user_label.appendChild(name);
name.innerHTML = data['username'];
gravatar.src = data['picture'];
user_label.innerHTML = '';
user_label.appendChild(gravatar);
user_label.appendChild(name);
right_menu.appendChild(logoutEl);
} else {
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);
}
});
}());
</script>

View File

@@ -90,33 +90,68 @@
border: 1px solid #eee;
margin-top: 50px;
}
.topic-container li.topic-row:nth-child(odd) {
.topic-container a:nth-child(odd) li.topic-row {
background-color:#fdfdfd;
}
.topic-container li.topic-row:nth-child(even) {
.topic-container a:nth-child(even) li.topic-row {
background-color:#fff;
}
.topic-container li.topic-row {
cursor: pointer;
border-bottom: 1px solid #eee;
padding: 10px;
}
.topic-container li:last-child {
border-bottom: 0;
}
.topic-container li.topic-row:hover {
background-color: #eee;
}
.post-container {
list-style-type: none;
padding: 0;
margin: 0;
border: 1px solid #eee;
}
.post-container li.post-row:nth-child(odd) {
background-color:#fdfdfd;
}
.post-container li.post-row:nth-child(even) {
background-color:#fff;
}
.post-container li.post-row {
cursor: pointer;
border-bottom: 1px solid #eee;
padding: 10px;
}
.post-container li:last-child {
border-bottom: 0;
}
.post-container li.post-row:hover {
background-color: #eee;
}
#user_label img {
border: 1px solid #999;
margin-right: 8px;
margin-top: -2px;
float: left;
}
#user_label span {
font-size: 12px;
font-weight: bold;
}
#reply_title {
font-size: 17px;
padding-top: 14px;
font-weight: 600;
}
</style>
</head>
@@ -132,13 +167,10 @@
</button>
<div class="nav-collapse collapse">
<ul class="nav">
<li class="active"><a href="/">Home</a></li>
<li><a href="/register">Register</a></li>
<li><a href="/login">Login</a></li>
<li class="active"><a href="/">Forum</a></li>
</ul>
<ul class="nav pull-right">
<li><a href="#" id="user_label"></a></li>
<li><a href="/logout">Log out</a></li>
<ul class="nav pull-right" id="right-menu">
<li><p class="navbar-text" id="user_label"></p></li>
</ul>
</div>
</div>
@@ -148,6 +180,7 @@
<div class="post-title-container">
<div class="container">
<input id="post_title" placeholder="Enter your topic title here." />
<span id="reply_title"></span>
</div>
</div>
<div class="post-content-container">
@@ -161,7 +194,7 @@
<a class="btn btn-link" href="#" tabindex="-1"><i class="icon-list"></i></a>
</div>
<div class="btn-group" style="float: right; margin-right: -12px">
<a class="btn" onclick="app.post_topic()"><i class="icon-ok"></i> Submit</a>
<a id="submit_post_btn" class="btn" onclick="app.post_topic()"><i class="icon-ok"></i> Submit</a>
<a class="btn" onclick="jQuery(post_window).slideToggle(250);"><i class="icon-remove"></i> Discard</a>
</div>
</div>
@@ -172,4 +205,5 @@
</div>
</div>
<div id="notification_window"></div>
<div class="container" id="content">

View File

@@ -1,9 +1,9 @@
<button id="new_post" class="btn btn-primary btn-large">New Post</button>
<ul class="topic-container">
<!-- BEGIN topics -->
<a href="topics/{topics.slug}"><li class="topic-row">
<a href="topic/{topics.slug}"><li class="topic-row">
<h4>{topics.title}</h4>
<p>Posted on {topics.timestamp} by user {topics.uid}. {topics.post_count} posts.</p>
<p>Posted {topics.relativeTime} by user {topics.uid}. {topics.post_count} posts.</p>
</li></a>
<!-- END topics -->
</ul>

View File

@@ -4,33 +4,10 @@
<button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Failed Login Attempt</strong> <p></p>
</div>
<label>Username</label><input type="text" placeholder="Enter Username" id="username" /><br />
<label>Password</label><input type="password" placeholder="Enter Password" id="password" /><br />
<button class="btn btn-primary" id="login" type="submit">Login</button> &nbsp;
<form method="post" action="/login">
<label>Username</label><input type="text" placeholder="Enter Username" name="username" id="username" /><br />
<label>Password</label><input type="password" placeholder="Enter Password" name="password" id="password" /><br />
<button class="btn btn-primary" id="login" type="submit">Login</button> &nbsp;
</form>
<a href="/reset">Forgot Password?</a>
</div>
<script type="text/javascript">
(function() {
var username = document.getElementById('username'),
password = document.getElementById('password'),
login = document.getElementById('login'),
error = document.getElementById('error');
login.onclick = function() {
socket.emit('user.login', {
username: username.value,
password: password.value
});
};
ajaxify.register_events(['user.login']);
socket.on('user.login', function(data) {
if (data.status === 0) {
jQuery('#error').show(50);
jQuery('#error p').html(data.message);
} else {
document.location.href = '/';
}
});
}());
</script>
</div>

View File

@@ -1,8 +1,23 @@
<ul class="topic-container">
<div class="container">
<ul class="breadcrumb">
<li><a href="/">Home</a> <span class="divider">/</span></li>
<li class="active">{TOPIC_NAME}</li>
</ul>
</div>
<ul class="post-container">
<!-- BEGIN posts -->
<li class="topic-row">
<li class="post-row">
<p>{posts.content}</p>
<p>Posted on {posts.timestamp} by user {posts.uid}.</p>
<p>Posted {posts.relativeTime} by user {posts.uid}.</p>
</li>
<!-- END posts -->
</ul>
</ul>
<hr />
<button id="post_reply" class="btn btn-primary btn-large">Reply</button>
<script type="text/javascript">
var post_reply = document.getElementById('post_reply');
post_reply.onclick = function() {
app.open_post_window('reply', "{TOPIC_ID}", "{TOPIC_NAME}");
}
</script>