Added frontend outline
This commit is contained in:
parent
03b65c196c
commit
16dec4894f
19 changed files with 905 additions and 0 deletions
25
public_html/css/core.css
Normal file
25
public_html/css/core.css
Normal file
|
@ -0,0 +1,25 @@
|
|||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
background: #fff;
|
||||
color: #000;
|
||||
font-family: 'Droid Sans', sans-serif;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
#middle {
|
||||
padding: 0 2em;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin: 1.5em 0;
|
||||
padding: 0;
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #6a2;
|
||||
text-decoration: none;
|
||||
}
|
46
public_html/css/forms.css
Normal file
46
public_html/css/forms.css
Normal file
|
@ -0,0 +1,46 @@
|
|||
.form-wrapper {
|
||||
display: table;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
.form-label,
|
||||
.form-input {
|
||||
display: table-cell;
|
||||
line-height: 37px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
|
||||
.form-input {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.form-row label {
|
||||
padding-right: 1em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
textarea,
|
||||
input[type=text],
|
||||
input[type=password] {
|
||||
padding: 2px 4px;
|
||||
border: 1px solid #eee;
|
||||
background: #fafafa;
|
||||
font-family: monospace;
|
||||
font-size: 17px;
|
||||
text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
button,
|
||||
input[type=button] {
|
||||
padding: 0.1em 0.5em;
|
||||
border: 1px solid #eee;
|
||||
background: #eee;
|
||||
font-family: 'Droid Sans', sans-serif;
|
||||
font-size: 17px;
|
||||
}
|
3
public_html/css/login-form.css
Normal file
3
public_html/css/login-form.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
#login-form p {
|
||||
text-align: center;
|
||||
}
|
15
public_html/css/messages.css
Normal file
15
public_html/css/messages.css
Normal file
|
@ -0,0 +1,15 @@
|
|||
.message {
|
||||
margin-bottom: 0.2em;
|
||||
padding: 0.4em 0.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.message.error {
|
||||
background: #fdd;
|
||||
box-shadow: 0 0 0 1px #fcc inset;
|
||||
}
|
||||
|
||||
.message.info {
|
||||
background: #def;
|
||||
box-shadow: 0 0 0 1px #cdf inset;
|
||||
}
|
3
public_html/css/registration-form.css
Normal file
3
public_html/css/registration-form.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
#registration-form p {
|
||||
text-align: center;
|
||||
}
|
38
public_html/css/top-navigation.css
Normal file
38
public_html/css/top-navigation.css
Normal file
|
@ -0,0 +1,38 @@
|
|||
#top-navigation {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#top-navigation ul {
|
||||
list-style-type: none;
|
||||
padding: 0 2em;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#top-navigation li {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#top-navigation li a {
|
||||
display: inline-block;
|
||||
padding: 0.5em 1em;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#top-navigation li.active a {
|
||||
background: #faffca;
|
||||
}
|
||||
|
||||
#top-navigation li a:focus,
|
||||
#top-navigation li a:hover {
|
||||
outline: 0;
|
||||
background: #efa;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
#top-navigation li:first-child a {
|
||||
margin-left: -1em;
|
||||
}
|
||||
|
||||
#top-navigation li:last-child a {
|
||||
margin-right: -1em;
|
||||
}
|
166
public_html/index.html
Normal file
166
public_html/index.html
Normal file
|
@ -0,0 +1,166 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>szurubooru</title>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/path.js/0.8.4/path.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.6.0/underscore-min.js"></script>
|
||||
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.min.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/core.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/forms.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/messages.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/top-navigation.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/login-form.css"/>
|
||||
<link rel="stylesheet" type="text/css" href="/css/registration-form.css"/>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="top-navigation"></div>
|
||||
<div id="middle">
|
||||
<div id="sidebar"></div>
|
||||
<div id="content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/template" id="top-navigation-template">
|
||||
<ul>
|
||||
<!-- todo: check privileges -->
|
||||
<li class="users">
|
||||
<a href="#/users">Users</a>
|
||||
</li>
|
||||
<% if (!loggedIn) { %>
|
||||
<li class="login">
|
||||
<a href="#/login">Login</a>
|
||||
</li>
|
||||
<li class="register">
|
||||
<a href="#/register">Register</a>
|
||||
</li>
|
||||
<% } else { %>
|
||||
<li class="logout">
|
||||
<a href="#/logout">Logout</a>
|
||||
</li>
|
||||
<% } %>
|
||||
</ul>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="login-form-template">
|
||||
<div id="login-form">
|
||||
<p>
|
||||
If you don't have an account yet,<br/>
|
||||
<a href="#/register">click here</a> to create a new one.
|
||||
</p>
|
||||
|
||||
<div class="messages"></div>
|
||||
|
||||
<form method="post" class="form-wrapper">
|
||||
<div class="form-row">
|
||||
<label for="login-user" class="form-label">User name:</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="text" name="user" id="login-user"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="login-password" class="form-label">Password:</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="password" name="password" id="login-password"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label class="form-label"></label>
|
||||
<div class="form-input">
|
||||
<button class="submit" type="submit">Log in</button>
|
||||
|
||||
<input type="hidden" name="remember" value="0"/>
|
||||
<label class="checkbox-wrapper">
|
||||
<input type="checkbox" name="remember" value="1"/>
|
||||
<span></span>
|
||||
Remember me
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="help">
|
||||
<p>Problems logging in?</p>
|
||||
<ul>
|
||||
<li><a href="#/password-reset">I don't remember my password</a></li>
|
||||
<li><a href="#/activate-account">I haven't received activation e-mail</a></li>
|
||||
<li><a href="#/register">I don't have an account</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/template" id="registration-form-template">
|
||||
<div id="registration-form">
|
||||
<p>
|
||||
Registered users can view more content,<br/>
|
||||
upload files and add posts to favorites.
|
||||
</p>
|
||||
|
||||
<div class="messages"></div>
|
||||
|
||||
<form method="post" class="form-wrapper">
|
||||
<div class="form-row">
|
||||
<label for="registration-user" class="form-label">User name:</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="text" name="user" id="registration-user" placeholder="e.g. darth_vader" value=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="registration-password" class="form-label">Password:</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="password" name="password1" id="registration-password" placeholder="e.g. ●●●●●●●●" value=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="registration-password-confirm" class="form-label">Password (repeat):</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="password" name="password2" id="registration-password-confirm" placeholder="e.g. ●●●●●●●●" value=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label for="registration-email" class="form-label">E-mail address:</label>
|
||||
<div class="form-input">
|
||||
<input autocomplete="off" type="text" name="email" id="registration-email" placeholder="e.g. vader@empire.gov" value=""/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<label class="form-label"></label>
|
||||
<div class="form-input">
|
||||
<button class="submit" type="submit">Register</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<p id="email-info">
|
||||
Your e-mail will be used to show your <a href="http://gravatar.com/">Gravatar</a>.<br/>
|
||||
Leave blank for random Gravatar.
|
||||
</p>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="/js/DI.js"></script>
|
||||
<script type="text/javascript" src="/js/State.js"></script>
|
||||
<script type="text/javascript" src="/js/Api.js"></script>
|
||||
<script type="text/javascript" src="/js/Auth.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/TopNavigationPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/LoginPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/LogoutPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/MessagePresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/RegistrationPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Presenters/UserListPresenter.js"></script>
|
||||
<script type="text/javascript" src="/js/Router.js"></script>
|
||||
<script type="text/javascript" src="/js/Bootstrap.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
57
public_html/js/Api.js
Normal file
57
public_html/js/Api.js
Normal file
|
@ -0,0 +1,57 @@
|
|||
var App = App || {};
|
||||
|
||||
App.API = function() {
|
||||
|
||||
var baseUrl = '/api/';
|
||||
|
||||
function get(url, data) {
|
||||
return request('GET', url, data);
|
||||
};
|
||||
|
||||
function post(url, data) {
|
||||
return request('POST', url, data);
|
||||
};
|
||||
|
||||
function put(url, data) {
|
||||
return request('PUT', url, data);
|
||||
};
|
||||
|
||||
function _delete(url, data) {
|
||||
return request('DELETE', url, data);
|
||||
};
|
||||
|
||||
function request(method, url, data) {
|
||||
var fullUrl = baseUrl + '/' + url;
|
||||
fullUrl = fullUrl.replace(/\/{2,}/, '/');
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
$.ajax({
|
||||
success: function(data, textStatus, xhr) {
|
||||
resolve({
|
||||
status: xhr.status,
|
||||
json: data});
|
||||
},
|
||||
error: function(xhr, textStatus, errorThrown) {
|
||||
reject({
|
||||
status: xhr.status,
|
||||
json: xhr.responseJSON
|
||||
? xhr.responseJSON
|
||||
: {error: errorThrown}});
|
||||
},
|
||||
type: method,
|
||||
url: fullUrl,
|
||||
data: data,
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
get: get,
|
||||
post: post,
|
||||
put: put,
|
||||
delete: _delete
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('api', App.API);
|
92
public_html/js/Auth.js
Normal file
92
public_html/js/Auth.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
var App = App || {};
|
||||
|
||||
App.Auth = function(jQuery, api, appState) {
|
||||
|
||||
function loginFromCredentials(userName, password, remember) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
api.post('/login', {userName: userName, password: password})
|
||||
.then(function(response) {
|
||||
appState.set('loggedIn', true);
|
||||
appState.set('loggedInUser', response.json.user);
|
||||
appState.set('loginToken', response.json.token);
|
||||
jQuery.cookie(
|
||||
'auth',
|
||||
response.json.token.name,
|
||||
remember ? { expires: 365 } : {});
|
||||
resolve(response);
|
||||
}).catch(function(response) {
|
||||
reject(response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function loginFromToken(token) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
api.post('/login', {token: token})
|
||||
.then(function(response) {
|
||||
appState.set('loggedIn', response.json.user && response.json.user.id);
|
||||
appState.set('loggedInUser', response.json.user);
|
||||
appState.set('loginToken', response.json.token.name);
|
||||
resolve(response);
|
||||
}).catch(function(response) {
|
||||
reject(response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function loginAnonymous() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
api.post('/login')
|
||||
.then(function(response) {
|
||||
appState.set('loggedIn', false);
|
||||
appState.set('loggedInUser', response.json.user);
|
||||
appState.set('loginToken', null);
|
||||
resolve(response);
|
||||
}).catch(function(response) {
|
||||
reject(response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function logout() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
appState.set('loggedIn', false);
|
||||
appState.set('loginToken', null);
|
||||
jQuery.removeCookie('auth');
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
function tryLoginFromCookie() {
|
||||
return new Promise(function(resolve, reject) {
|
||||
if (appState.get('loggedIn')) {
|
||||
resolve();
|
||||
return;
|
||||
}
|
||||
|
||||
var authCookie = jQuery.cookie('auth');
|
||||
if (!authCookie) {
|
||||
reject();
|
||||
return;
|
||||
}
|
||||
|
||||
loginFromToken(authCookie).then(function(response) {
|
||||
resolve();
|
||||
}).catch(function(response) {
|
||||
jQuery.removeCookie('auth');
|
||||
reject();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
loginFromCredentials: loginFromCredentials,
|
||||
loginFromToken: loginFromToken,
|
||||
loginAnonymous: loginAnonymous,
|
||||
tryLoginFromCookie: tryLoginFromCookie,
|
||||
logout: logout,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('auth', App.Auth);
|
33
public_html/js/Bootstrap.js
Normal file
33
public_html/js/Bootstrap.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
var App = App || {};
|
||||
|
||||
App.Bootstrap = function(auth, router) {
|
||||
|
||||
auth.tryLoginFromCookie()
|
||||
.then(startRouting)
|
||||
.catch(function(error) {
|
||||
auth.loginAnonymous()
|
||||
.then(startRouting)
|
||||
.catch(function(response) {
|
||||
console.log(response);
|
||||
alert('Fatal authentication error: ' + response.json.error);
|
||||
});
|
||||
});
|
||||
|
||||
function startRouting() {
|
||||
try {
|
||||
router.start();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
startRouting: startRouting,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('bootstrap', App.Bootstrap);
|
||||
App.DI.registerManual('jQuery', function() { return $; });
|
||||
|
||||
var bootstrap = App.DI.get('bootstrap');
|
65
public_html/js/DI.js
Normal file
65
public_html/js/DI.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
var App = App || {};
|
||||
|
||||
App.DI = (function() {
|
||||
|
||||
var STRIP_COMMENTS = /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/mg;
|
||||
var ARGUMENT_NAMES = /([^\s,]+)/g;
|
||||
|
||||
var factories = {};
|
||||
var instances = {};
|
||||
|
||||
function get(key) {
|
||||
var instance = instances[key];
|
||||
if (!instance) {
|
||||
var factory = factories[key];
|
||||
if (!factory)
|
||||
throw new Error('Unregistered key: ' + key);
|
||||
var objectInitializer = factory.initializer;
|
||||
var singleton = factory.singleton;
|
||||
var deps = resolveDependencies(objectInitializer);
|
||||
var instance = {};
|
||||
instance = objectInitializer.apply(instance, deps);
|
||||
if (singleton)
|
||||
instances[key] = instance;
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
function resolveDependencies(objectIntializer) {
|
||||
var deps = [];
|
||||
var depKeys = getFunctionParameterNames(objectIntializer);
|
||||
for (var i = 0; i < depKeys.length; i ++) {
|
||||
deps[i] = get(depKeys[i]);
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
function register(key, objectInitializer) {
|
||||
factories[key] = {initializer: objectInitializer, singleton: false};
|
||||
};
|
||||
|
||||
function registerSingleton(key, objectInitializer) {
|
||||
factories[key] = {initializer: objectInitializer, singleton: true};
|
||||
};
|
||||
|
||||
function registerManual(key, objectInitializer) {
|
||||
instances[key] = objectInitializer();
|
||||
};
|
||||
|
||||
function getFunctionParameterNames(func) {
|
||||
var fnStr = func.toString().replace(STRIP_COMMENTS, '');
|
||||
var result = fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
|
||||
if (result === null) {
|
||||
result = [];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
return {
|
||||
get: get,
|
||||
register: register,
|
||||
registerManual: registerManual,
|
||||
registerSingleton: registerSingleton,
|
||||
};
|
||||
|
||||
})();
|
59
public_html/js/Presenters/LoginPresenter.js
Normal file
59
public_html/js/Presenters/LoginPresenter.js
Normal file
|
@ -0,0 +1,59 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.LoginPresenter = function(
|
||||
jQuery,
|
||||
topNavigationPresenter,
|
||||
messagePresenter,
|
||||
auth,
|
||||
router,
|
||||
appState) {
|
||||
|
||||
topNavigationPresenter.select('login');
|
||||
|
||||
var $el = jQuery('#content');
|
||||
var $messages;
|
||||
var template = _.template(jQuery('#login-form-template').html());
|
||||
|
||||
var eventHandlers = {
|
||||
|
||||
loginFormSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
messagePresenter.hideMessages($messages);
|
||||
|
||||
var userName = $el.find('[name=user]').val();
|
||||
var password = $el.find('[name=password]').val();
|
||||
var remember = $el.find('[name=remember]').val();
|
||||
|
||||
//todo: client side error reporting
|
||||
|
||||
auth.loginFromCredentials(userName, password, remember)
|
||||
.then(function(response) {
|
||||
router.navigateToMainPage();
|
||||
//todo: "redirect" to main page
|
||||
}).catch(function(response) {
|
||||
messagePresenter.showError($messages, response.json && response.json.error || response);
|
||||
});
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
if (appState.get('loggedIn'))
|
||||
router.navigateToMainPage();
|
||||
|
||||
render();
|
||||
|
||||
function render() {
|
||||
$el.html(template());
|
||||
$el.find('form').submit(eventHandlers.loginFormSubmit);
|
||||
$messages = $el.find('.messages');
|
||||
$messages.width($el.find('form').width());
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('loginPresenter', App.Presenters.LoginPresenter);
|
34
public_html/js/Presenters/LogoutPresenter.js
Normal file
34
public_html/js/Presenters/LogoutPresenter.js
Normal file
|
@ -0,0 +1,34 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.LogoutPresenter = function(
|
||||
jQuery,
|
||||
topNavigationPresenter,
|
||||
messagePresenter,
|
||||
auth,
|
||||
router) {
|
||||
|
||||
topNavigationPresenter.select('logout');
|
||||
|
||||
var $messages = jQuery('#content');
|
||||
|
||||
var eventHandlers = {
|
||||
mainPageLinkClick: function(e) {
|
||||
e.preventDefault();
|
||||
router.navigateToMainPage();
|
||||
},
|
||||
};
|
||||
|
||||
auth.logout().then(function() {
|
||||
var $messageDiv = messagePresenter.showInfo($messages, 'Logged out. <a href="">Back to main page</a>');
|
||||
$messageDiv.find('a').click(eventHandlers.mainPageLinkClick);
|
||||
}).catch(function(response) {
|
||||
messagePresenter.showError($messages, response.json && response.json.error || response);
|
||||
});
|
||||
|
||||
return {
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('logoutPresenter', App.Presenters.LogoutPresenter);
|
41
public_html/js/Presenters/MessagePresenter.js
Normal file
41
public_html/js/Presenters/MessagePresenter.js
Normal file
|
@ -0,0 +1,41 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.MessagePresenter = function(jQuery) {
|
||||
|
||||
function showInfo($el, message) {
|
||||
return showMessage($el, 'info', message);
|
||||
};
|
||||
|
||||
function showError($el, message) {
|
||||
return showMessage($el, 'error', message);
|
||||
};
|
||||
|
||||
function hideMessages($el) {
|
||||
$el.children('.message').each(function() {
|
||||
$(this).slideUp('fast', function() {
|
||||
$(this).remove();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function showMessage($el, className, message) {
|
||||
var $messageDiv = $('<div>');
|
||||
$messageDiv.addClass('message');
|
||||
$messageDiv.addClass(className);
|
||||
$messageDiv.html(message);
|
||||
$messageDiv.hide();
|
||||
$el.append($messageDiv);
|
||||
$messageDiv.slideDown('fast');
|
||||
return $messageDiv;
|
||||
};
|
||||
|
||||
return {
|
||||
showInfo: showInfo,
|
||||
showError: showError,
|
||||
hideMessages: hideMessages,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('messagePresenter', App.Presenters.MessagePresenter);
|
68
public_html/js/Presenters/RegistrationPresenter.js
Normal file
68
public_html/js/Presenters/RegistrationPresenter.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.RegistrationPresenter = function(
|
||||
jQuery,
|
||||
topNavigationPresenter,
|
||||
messagePresenter,
|
||||
api) {
|
||||
|
||||
topNavigationPresenter.select('register');
|
||||
|
||||
var $el = jQuery('#content');
|
||||
var template = _.template(jQuery('#registration-form-template').html());
|
||||
|
||||
var eventHandlers = {
|
||||
|
||||
registrationFormSubmit: function(e) {
|
||||
e.preventDefault();
|
||||
messagePresenter.hideMessages($messages);
|
||||
|
||||
var userName = $el.find('[name=user]').val();
|
||||
var password = $el.find('[name=password1]').val();
|
||||
var passwordConfirmation = $el.find('[name=password2]').val();
|
||||
var email = $el.find('[name=email]').val();
|
||||
|
||||
if (userName.length == 0) {
|
||||
messagePresenter.showError($messages, 'User name cannot be empty.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password.length == 0) {
|
||||
messagePresenter.showError($messages, 'Password cannot be empty.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (password != passwordConfirmation) {
|
||||
messagePresenter.showError($messages, 'Passwords must be the same.');
|
||||
return;
|
||||
}
|
||||
|
||||
api.post('/users', {userName: userName, password: password, email: email})
|
||||
.then(function(response) {
|
||||
//todo: show message about registration success
|
||||
//if it turned out that user needs to confirm his e-mail, notify about it
|
||||
//also, show link to login
|
||||
}).catch(function(response) {
|
||||
messagePresenter.showError($messages, response.json && response.json.error || response);
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
render();
|
||||
|
||||
function render() {
|
||||
$el.html(template());
|
||||
$el.find('form').submit(eventHandlers.registrationFormSubmit);
|
||||
$messages = $el.find('.messages');
|
||||
$messages.width($el.find('form').width());
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('registrationPresenter', App.Presenters.RegistrationPresenter);
|
37
public_html/js/Presenters/TopNavigationPresenter.js
Normal file
37
public_html/js/Presenters/TopNavigationPresenter.js
Normal file
|
@ -0,0 +1,37 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.TopNavigationPresenter = function(jQuery, appState) {
|
||||
|
||||
var selectedElement = null;
|
||||
var template = _.template(jQuery('#top-navigation-template').html());
|
||||
var $el = jQuery('#top-navigation');
|
||||
|
||||
var eventHandlers = {
|
||||
loginStateChanged: function() {
|
||||
render();
|
||||
},
|
||||
};
|
||||
|
||||
appState.startObserving('loggedIn', 'top-navigation', eventHandlers.loginStateChanged);
|
||||
render();
|
||||
|
||||
function select(newSelectedElement) {
|
||||
selectedElement = newSelectedElement;
|
||||
$el.find('li').removeClass('active');
|
||||
$el.find('li.' + selectedElement).addClass('active');
|
||||
};
|
||||
|
||||
function render() {
|
||||
$el.html(template({loggedIn: appState.get('loggedIn')}));
|
||||
$el.find('li.' + selectedElement).addClass('active');
|
||||
};
|
||||
|
||||
return {
|
||||
render: render,
|
||||
select: select,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('topNavigationPresenter', App.Presenters.TopNavigationPresenter);
|
22
public_html/js/Presenters/UserListPresenter.js
Normal file
22
public_html/js/Presenters/UserListPresenter.js
Normal file
|
@ -0,0 +1,22 @@
|
|||
var App = App || {};
|
||||
App.Presenters = App.Presenters || {};
|
||||
|
||||
App.Presenters.UserListPresenter = function(jQuery, topNavigationPresenter, appState) {
|
||||
|
||||
topNavigationPresenter.select('users');
|
||||
|
||||
var $el = jQuery('#content');
|
||||
|
||||
render();
|
||||
|
||||
function render() {
|
||||
$el.html('Logged in: ' + appState.get('loggedIn'));
|
||||
};
|
||||
|
||||
return {
|
||||
render: render
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.register('userListPresenter', App.Presenters.UserListPresenter);
|
53
public_html/js/Router.js
Normal file
53
public_html/js/Router.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
var App = App || {};
|
||||
|
||||
App.Router = function(jQuery) {
|
||||
|
||||
var root = '#/';
|
||||
|
||||
injectRoutes();
|
||||
|
||||
function navigateToMainPage() {
|
||||
window.location.href = root;
|
||||
};
|
||||
|
||||
function navigate(url) {
|
||||
window.location.href = url;
|
||||
};
|
||||
|
||||
function start() {
|
||||
Path.listen();
|
||||
};
|
||||
|
||||
function changePresenter(presenterGetter) {
|
||||
jQuery('#content').empty();
|
||||
var presenter = presenterGetter();
|
||||
};
|
||||
|
||||
function injectRoutes() {
|
||||
inject('#/login', function() { return new App.DI.get('loginPresenter'); });
|
||||
inject('#/logout', function() { return new App.DI.get('logoutPresenter'); });
|
||||
inject('#/register', function() { return new App.DI.get('registrationPresenter'); });
|
||||
inject('#/users', function() { return App.DI.get('userListPresenter'); });
|
||||
setRoot('#/users');
|
||||
};
|
||||
|
||||
function setRoot(newRoot) {
|
||||
root = newRoot;
|
||||
Path.root(newRoot);
|
||||
};
|
||||
|
||||
function inject(path, presenterGetter) {
|
||||
Path.map(path).to(function() {
|
||||
changePresenter(presenterGetter);
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
start: start,
|
||||
navigate: navigate,
|
||||
navigateToMainPage: navigateToMainPage,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('router', App.Router);
|
48
public_html/js/State.js
Normal file
48
public_html/js/State.js
Normal file
|
@ -0,0 +1,48 @@
|
|||
var App = App || {};
|
||||
|
||||
App.State = function() {
|
||||
|
||||
var properties = {};
|
||||
var observers = {};
|
||||
|
||||
function get(key) {
|
||||
return properties[key];
|
||||
};
|
||||
|
||||
function set(key, value) {
|
||||
properties[key] = value;
|
||||
if (key in observers) {
|
||||
for (observerName in observers[key]) {
|
||||
if (observers[key].hasOwnProperty(observerName)) {
|
||||
observers[key][observerName](key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function startObserving(key, observerName, callback) {
|
||||
if (!(key in observers))
|
||||
observers[key] = {};
|
||||
if (!(observerName in observers[key]))
|
||||
observers[key][observerName] = {};
|
||||
observers[key][observerName] = callback;
|
||||
};
|
||||
|
||||
function stopObserving(key, observerName) {
|
||||
if (!(key in observers))
|
||||
return;
|
||||
if (!(observerName in observers[key]))
|
||||
return;
|
||||
delete observers[key][observerName];
|
||||
};
|
||||
|
||||
return {
|
||||
get: get,
|
||||
set: set,
|
||||
startObserving: startObserving,
|
||||
stopObserving: stopObserving,
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
App.DI.registerSingleton('appState', App.State);
|
Loading…
Reference in a new issue