For this blog application we need some pages and modules. The font we use must be placed in the fonts folder. Also, the images we will use must be included in the img folder.
This application have general and common informations that we need.
Pay attention
index.html
file.
index.html
.
index.html
.
Into index.html
should add the follow codes at the head section.
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
<title>Blog</title>
<link rel="stylesheet" type="text/css" href="lib/framework.css" />
<link rel="stylesheet" type="text/css" href="lib/mdtoast.css" />
<!-- add your modules styles here -->
<!-- <link rel="stylesheet" type="text/css" href="modules/" /> -->
<link rel="stylesheet" type="text/css" href="modules/ui/css/style.css" />
<!-- add your page styles here -->
<!-- <link rel="stylesheet" type="text/css" href="css/" /> -->
<link rel="stylesheet" type="text/css" href="css/common.css" />
</head>
Also at the end of this page, into body section we need to have some Script
as a below.
<script src="lib/jquery-2.2.0.min.js"></script>
<script src="lib/mdtoast.js"></script>
<script src="lib/cloadio.min.js"></script>
<script src="lib/framework-a.min.js"></script>
<script src="js/app.js"></script>
<script src="lib/framework-b.min.js"></script>
<!-- add your module scripts here -->
<!-- <script src="modules/"></script> -->
<script src="modules/ui/js/script.js"></script>
<script src="modules/controls/js/script.js"></script>
<script src="modules/form/js/script.js"></script>
<script src="modules/table/js/script.js"></script>
<!-- add your page scripts here -->
<!-- <script src="js/"></script> -->
<script src="js/home.js"></script>
We have a file that named common.css
, which should have the default stylesheet as follow.
/* font */
/* Webfont: poppins-regular */
@font-face {
font-family: 'poppins';
src: url('../font/poppins-regular.woff2') format('woff2'), /* Modern Browsers */
url('../font/poppins-regular.woff') format('woff'); /* Modern Browsers */
font-style: normal;
font-weight: 400;
}
/* Webfont: poppins-medium */
@font-face {
font-family: 'poppins';
src: url('../font/poppins-medium.woff2') format('woff2'), /* Modern Browsers */
url('../font/poppins-medium.woff') format('woff'); /* Modern Browsers */
font-style: normal;
font-weight: 500;
}
/* Webfont: poppins-semi-bold */
@font-face {
font-family: 'poppins';
src: url('../font/poppins-bold.woff2') format('woff2'), /* Modern Browsers */
url('../font/poppins-bold.woff') format('woff');
font-style: normal;
font-weight: 600;
}
/* general */
* {
box-sizing: border-box;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-tap-highlight-color: transparent;
}
*:focus {
outline: none;
}
*:not(input):not(textarea) {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
::-webkit-scrollbar {
display: none;
}
html {
height: 100%;
}
body {
background-color: #ffffff;
height: 100%;
font-family: 'poppins';
font-size: 14px;
}
input[type='text'], input[type='number'], input[type='password'] {
display: block;
width: 100%;
font-size: 16px;
font-family: 'poppins';
box-sizing: border-box;
border-radius: 8px;
}
input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
display: none;
}
input[type='password'] {
padding: 6px 62px 6px 14px;
}
input::placeholder {
font-size: 14px;
font-family: 'poppins';
font-weight: 400;
}
#overlay, #menuOverlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 471;
animation: opacity 0.5s ease;
display: none;
background-color: #0000001a;
}
#overlay:not(.active), #menuOverlay:not(.active) {
opacity: 0;
}
#overlay.active, #menuOverlay.active {
opacity: 1;
}
#text > textarea{
font-size: 16px;
font-family: 'poppins';
}
#text .button {
height: 54px;
background-color: #1a8917;
color: #ffffff;
line-height: 54px;
transition: opacity 0.7s ease;
text-align: center;
border-radius: 8px;
font-size: 16px;
font-weight: bold;
}
#box {
background-color: #ffffff;
}
#box > .buttons, #options > .buttons {
text-align: center;
font-weight: bold;
}
#box > .buttons > .confirm > div {
text-align: center;
width: 49%;
display: inline-block;
padding-top: 20px;
cursor: pointer;
}
#box > .buttons > .alert {
padding-top: 20px;
cursor: pointer;
}
#options > .buttons > .button {
margin-top: 22px;
}
.page {
height: 100%;
}
Into app.onDocumentReady
section in app.js
file, we should add the following codes in general. This codes are for handling the screen, connection config and something else.
app.onDocumentReady = function () {
// config connection
ref.config('ping', {interval: 20000, retries: 2});
if (platform.type === 'android') {
platform.device.screen.width = Math.ceil(platform.device.screen.width);
platform.device.screen.height = Math.ceil(platform.device.screen.height);
}
// apply screen safe area
var jHtml = $('html');
if (platform.device.screen.safeArea) {
if (platform.type === 'android') {
platform.device.screen.safeArea.width = Math.ceil(platform.device.screen.safeArea.width);
platform.device.screen.safeArea.height = Math.ceil(platform.device.screen.safeArea.height);
platform.device.screen.safeArea.origin.x = Math.ceil(platform.device.screen.safeArea.origin.x);
platform.device.screen.safeArea.origin.y = Math.ceil(platform.device.screen.safeArea.origin.y);
}
app.local.safeWindow = platform.device.screen.safeArea;
jHtml.css('margin-top', platform.device.screen.safeArea.origin.y + 'px');
jHtml.css('max-height', platform.device.screen.safeArea.height + 'px');
var hd = platform.device.screen.height - platform.device.screen.safeArea.height; // height difference
jHtml.css('height', 'calc(100% - ' + hd + 'px)');
app.local.sab = hd - platform.device.screen.safeArea.origin.y; // safe area bottom
}
else {
app.local.safeWindow = platform.device.screen;
jHtml.css('height', '100%');
}
};
The app.onSynced
codes are for after syncing user and CloadIO, which events and methods should be running.
// on sync event
// called when time synced with cloadio
app.onSynced = function () {
var next = function () {
if (app.local.onSyncCompleted.length) {
for (var i in app.local.onSyncCompleted)
app.local.onSyncCompleted[i]();
app.local.onSyncCompleted = [];
}
app.local.synced = true;
};
};
The app.auth.onCompleted
after user authenticated, this event will be called and the codes run.
// called when user authenticated
app.auth.onCompleted = function () {
if (app.auth.user.key)
homePage.show();
};
This page is when the app is loading and this page will be appear at first.
Into index.html
, at the body secction, should add the follow codes. This page is first page will be load in the application.
<div class="page loading">
<span>loading...</span>
</div>
Pay attention the page
class name is a FrameworkJS keyword and should be used for every new page that we want to have in the project. For more information about page
please see the Page API.
We should make a sepreted stylesheet file into CSS folder with loading
name and include that into index.html
file.
So we will add the loading styles into loading.css
file as you can see in the follow.
.page.loading {
position: absolute;
top: 50%;
right: 50%;
transform: translate(50%, -100%);
height: 23px;
}
.page.loading > span {
font-size: 16px;
font-weight: bold;
color: #1a8917;
}
There is no JS codes for this page, just we need to add default JS page codes.
var loadingPage = new Page('loading', function () {
// after page shown
}, function () {
// before page shown
}, function () {
// after page closed
});
Into app.onDocumentReady
section in app.js
file, we should add the following code. This code will shown the loading page after run the application for the first time.
app.onDocumentReady = function () {
loadingPage.show();
};
This page is for sign in with the email account in the app.
The adding HTML codes order is important, try to obey it.
In order to make sign in page, add the below HTML codes into body section of index.html
file.
<div class="page signIn">
<div class="header">
<div class="title">Sign In</div>
</div>
<div class="content">
<div class="email">
<input type="text" placeholder="Email Address">
<div class="message"></div>
</div>
<div class="pass">
<input type="password" placeholder="Password">
<div class="display">
<img src="img/showPass.svg">
</div>
<div class="message">
<div class="info">At least 4 characters password</div>
</div>
</div>
<div class="button">Sign in</div>
</div>
<div class="footer">
<div class="noAccount">Don't have an account?</div>
<div class="signUp">Sign Up</div>
</div>
</div>
Add the following CSS codes into signIn.css
file.
.page.signIn > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 68px;
}
.page.signIn > .header > .title {
line-height: 68px;
font-size: 18px;
font-weight: 600;
text-align: center;
}
.page.signIn > .content {
position: absolute;
top: 68px;
right: 0;
left: 0;
bottom: 80px;
padding-top: 32px;
padding-right: 32px;
padding-left: 32px;
overflow-y: auto;
}
.page.signIn > .content > .pass {
position: relative;
}
.page.signIn > .content > div > input {
margin-bottom: 16px;
padding: 13.5px 24px;
border: 1px solid #b6bcb6;
}
.page.signIn > .content > .pass > input {
padding-right: 68px;
}
.page.signIn > .content > div > input:focus {
box-shadow: 0px 0px 0px 1px #878b87 inset;
}
.page.signIn > .content > div > input::placeholder {
font-size: 16px;
color: #b6bcb6;
}
.page.signIn > .content > .pass > .display {
position: absolute;
top: 0;
right: 0;
padding: 19px 24px;
cursor: pointer;
}
.page.signIn > .content > .pass > .display > img {
width: 20px;
display: block;
}
.page.signIn > .content > div > .message {
text-align: left;
padding-left: 24px;
line-height: 22px;
font-size: 14px;
}
.page.signIn > .content > div > .message > div:first-child {
margin-top: -8px;
}
.page.signIn > .content > div > .message > div:last-child {
margin-bottom: 16px;
}
.page.signIn > .content > div > .message > .error {
color: #f23002;
}
.page.signIn > .content > div > .message > .info {
color: #c0c3d2;
}
.page.signIn > .content > .button {
height: 54px;
background-color: #1a8917;
color: #ffffff;
line-height: 54px;
transition: opacity 0.7s ease;
text-align: center;
border-radius: 8px;
font-weight: bold;
}
.page.signIn > .footer {
position: absolute;
right: 32px;
bottom: 32px;
left: 32px;
text-align: center;
}
.page.signIn > .footer > .noAccount {
margin-bottom: 8px;
}
.page.signIn > .footer > .signUp {
font-weight: bold;
}
Two suitable icons for hiding and showing the password should be added to images folder.
You can download them form here.
In this page we use the ui
module, it should be placed in the modules folder and then included in index.html
, as we did it in the General Information section.
For more information about ui module, visit the ui module.
Add the following JS codes into signIn.js
file. It is saved in the JS folder.
var signInPage = new Page('signIn', function () {
// after page shown
// define variables
var jPage = $('.page.signIn'),
jContent = jPage.children('.content'),
jEmail = jContent.children('.email',)
jPass = jContent.children('.pass'),
jFooter = jPage.children('.footer');
jPass.children('.display').click(function () {
var jInput = jPass.children('input');
if (jInput.attr('type') === 'password') {
jInput.attr('type', 'text');
jPass.find(' > .display > img').attr('src', 'img/hidePass.svg');
}
else {
jInput.attr('type', 'password');
jPass.find(' > .display > img').attr('src', 'img/showPass.svg');
}
});
jContent.find(' > .email > input').off('keyup paste').on('keyup paste', function () {
var jSelf = $(this);
setTimeout(function () {
if (jSelf.val() && jContent.find(' > .pass > input').val())
jContent.children('.button').addClass('active');
else
jContent.children('.button').removeClass('active');
}, 1);
});
jContent.find(' > .pass > input').off('keyup paste').on('keyup paste', function () {
var jSelf = $(this);
setTimeout(function () {
if (jSelf.val() && jContent.find(' > .email > input').val())
jContent.children('.button').addClass('active');
else
jContent.children('.button').removeClass('active');
}, 1);
});
// sign in button
app.ui.button(jContent, 'ripple', {
onPush: function (jButton) {
// reset
jContent.find('div > .message').html('');
var go = true, email = jEmail.children('input').val().toLowerCase().trim();
if (!email) {
jEmail.children('.message').html('<div class="error">Email address cannot be empty.</div>');
go = false;
}
var pass = jPass.children('input').val();
if (!pass) {
jPass.children('.message').html('<div class="error">Password cannot be empty.</div>');
go = false;
}
if (go)
ref.run('signIn', {appVersion: app.version.current, email: email, pass: pass}, function (error, credential) {
if (error) {
if (error === 'username or password isn’t correct')
mdtoast('Username or password isn’t correct.', {type: mdtoast.ERROR});
else {
console.error('running sign in cloud side function failed', error);
mdtoast('Sign in failed. Please try again.', {type: mdtoast.ERROR});
}
}
else
ref.authWithToken(credential.t, true, function (error) {
if (error) {
if (error === 'credential failed')
mdtoast('Email or password is incorrect. Please try again.', {type: mdtoast.ERROR});
else
mdtoast('An error occured. Please try again.', {type: mdtoast.ERROR});
}
else {
var userKey = credential.p.sub.split('@')[0];
app.auth.syncUser(userKey, function (error, value) {
if (error)
mdtoast('User synchronization failed.', {type: mdtoast.ERROR});
else {
// save email and username on device
var user = localStorage.user ? JSON.parse(localStorage.user) : {};
user.status = 'authenticated';
user.key = app.auth.user.key;
if (app.auth.user.email)
user.email = app.auth.user.email;
localStorage.user = JSON.stringify(user);
var user = $.extend(true, {}, app.auth.user);
delete user.key;
ref.child(app.cloadio.puip + '/' + app.auth.user.key).set(user, function (error) {
if (error)
console.error('set user failed', error);
else
app.auth.onCompleted && app.auth.onCompleted();
});
}
});
}
});
});
}
});
// sign up
jFooter.children('.signUp').click(function() {
signUpPage.show();
});
}, function () {
// before page shown
}, function () {
// after page closed
});
It should be placed in CloadIO side and the name of this is signIn.js, we can save it in the src
folder siblings the client
folder.
// sign in
database.connect(function (error, mongodb) {
if (error)
send('connect to database failed');
else {
var or = [{username: args.email}], o = {};
o['email.value'] = args.email;
or.push(o);
mongodb.collection('users').find({
$or: or,
pass: args.pass
}).toArray(function (error, users) {
if (error)
send('check users failed');
else if (users.length)
// get application secret value
settings.get('secret', function (error, secret) {
if (error)
send('sign user failed');
else {
// make payload
var payload = {iss: 'cloadio', sub: users[0]['#k'] + '@' + appName, iat: Date.now()};
// send token and payload to client
send(null, {t: jwt.encode(payload, secret), p: payload});
}
});
else
send('username or password isn’t correct');
});
}
});
For more information about CloadIO functions, You can see the CloadIO Function section.
This page is for sign up users intoapp.
In order to make sign up page, add the below HTML codes into body section of index.html
file.
<div class="page signUp">
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">Sign Up</div>
</div>
</div>
<div class="content">
<div class="email">
<input type="text" placeholder="Email Address">
<div class="message"></div>
</div>
<div class="pass">
<input type="password" placeholder="Password">
<div class="display">
<img src="img/showPass.svg">
</div>
<div class="message">
<div class="info">At least 4 characters password</div>
</div>
</div>
<div class="button">Sign up</div>
</div>
</div>
Add the following CSS codes into signUp.css
file.
.page.signUp > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 68px;
overflow: hidden;
}
.page.signUp > .header > .left {
float: left;
height: 68px;
}
.page.signUp > .header > .left > .button {
padding: 26px 25px;
}
.page.signUp > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.signUp > .header > .right {
padding-left: 68px;
padding-right: 68px;
}
.page.signUp > .header > .right > .title {
line-height: 68px;
font-size: 18px;
font-weight: 600;
text-align: center;
}
.page.signUp > .content {
position: absolute;
top: 68px;
right: 0;
left: 0;
bottom: 0;
padding: 32px;
overflow-y: auto;
}
.page.signUp > .content > .pass {
position: relative;
}
.page.signUp > .content > div > input {
margin-bottom: 16px;
padding: 13.5px 24px;
border: 1px solid #b6bcb6;
}
.page.signUp > .content > .pass > input {
padding-right: 68px;
}
.page.signUp > .content > div > input:focus {
box-shadow: 0px 0px 0px 1px #878b87 inset;
}
.page.signUp > .content > div > input::placeholder {
font-size: 16px;
color: #b6bcb6;
}
.page.signUp > .content > .pass > .display {
position: absolute;
top: 0;
right: 0;
padding: 19px 24px;
cursor: pointer;
}
.page.signUp > .content > .pass > .display > img {
width: 20px;
display: block;
}
.page.signUp > .content > div > .message {
text-align: left;
padding-left: 24px;
line-height: 22px;
font-size: 14px;
}
.page.signUp > .content > div > .message > div:first-child {
margin-top: -8px;
}
.page.signUp > .content > div > .message > div:last-child {
margin-bottom: 16px;
}
.page.signUp > .content > div > .message > .error {
color: #f23002;
}
.page.signUp > .content > div > .message > .info {
color: #c0c3d2;
}
.page.signUp > .content > .button {
height: 54px;
background-color: #1a8917;
color: #ffffff;
line-height: 54px;
transition: opacity 0.7s ease;
text-align: center;
border-radius: 8px;
font-weight: bold;
}
For make a back button, and show or hide password we need to add suitable icons in img folder.
You can download them form here.
In this page we use the ui
module, it should be placed in the modules folder and then included in index.html
, as we did it in the General Information section.
For more information about ui module, visit the ui module.
Add the following JS codes into signUp.js
file.
var signUpPage = new Page('signUp', function () {
// after page shown
// define variables
var jPage = $('.page.signUp'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
jPass = jContent.children('.pass'),
model = {
email: {
inputType:'email',
text:'Email',
syntax: {
value: '^\\\\S+@\\\\S+\\\\.\\\\S+$'
},
display: 'none'
},
pass: {
text:'Password',
display: 'none',
syntax: {
value: '^.{4,64}$',
message: 'Password must contain at least 4 characters.'
}
}
};
// back
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/backBlack.svg">',
color: '#b6bcb6',
onPush: function (jButton) {
history.back();
}
});
jPass.children('.display').click(function () {
var jInput = jPass.children('input');
if (jInput.attr('type') === 'password') {
jInput.attr('type', 'text');
jPass.find(' > .display > img').attr('src', 'img/hidePass.svg');
}
else {
jInput.attr('type', 'password');
jPass.find(' > .display > img').attr('src', 'img/showPass.svg');
}
});
jContent.find(' > .email > input').off('keyup paste').on('keyup paste', function () {
var jSelf = $(this);
setTimeout(function () {
if (jSelf.val() && jContent.find(' > .pass > input').val())
jContent.children('.button').addClass('active');
else
jContent.children('.button').removeClass('active');
}, 1);
});
jContent.find(' > .pass > input').off('keyup paste').on('keyup paste', function () {
var jSelf = $(this);
setTimeout(function () {
if (jSelf.val() && jContent.find(' > .email > input').val())
jContent.children('.button').addClass('active');
else
jContent.children('.button').removeClass('active');
}, 1);
});
// next button
app.ui.button(jContent, 'ripple', {
onPush: function (jButton) {
// reset
jContent.find(' > div > .message').html('');
var go = true, jEmail = jContent.children('.email'), email = jEmail.children('input').val().toLowerCase().trim();
// if (model.email.syntax && !new RegExp(tools.decodeHtml(model.email.syntax.value)).test(email)) {
// jEmail.children('.message').html('<div class="error">Incorrect email address.</div>\
// <div class="info">example: email@sample.com</div>');
// go = false;
// }
var pass = jPass.children('input').val();
if (model.pass.syntax && !new RegExp(tools.decodeHtml(model.pass.syntax.value)).test(pass)) {
jPass.children('.message').html('<div class="error">At least 4 characters password is required.</div>');
go = false;
}
if (go)
ref.run('signUp', {appVersion: app.version.current, email: email, pass: pass}, function (error, credential) {
if (error) {
if (error === 'email is taken before')
mdtoast('Entered email isn’t available. Please choose another one.', {type: mdtoast.ERROR});
else {
console.error('running sign in cloud side function failed', error);
mdtoast('Sign in failed. Please try again.', {type: mdtoast.ERROR});
}
}
else
ref.authWithToken(credential.t, true, function (error) {
if (error) {
if (error === 'credential failed')
mdtoast('Email or password is incorrect. Please try again.', {type: mdtoast.ERROR});
else
mdtoast('An error occured. Please try again.', {type: mdtoast.ERROR});
}
else {
var userKey = credential.p.sub.split('@')[0];
app.auth.syncUser(userKey, function (error, value) {
if (error)
mdtoast('User synchronization failed.', {type: mdtoast.ERROR});
else {
// save email on device
var user = localStorage.user ? JSON.parse(localStorage.user) : {};
user.status = 'authenticated';
user.key = app.auth.user.key;
if (app.auth.user.email)
user.email = app.auth.user.email;
localStorage.user = JSON.stringify(user);
// save user on cloadio
var user = $.extend(true, {}, app.auth.user);
delete user.key;
ref.child(app.cloadio.puip + '/' + app.auth.user.key).set(user, function (error) {
if (error)
console.error('set user failed', error);
else
app.auth.onCompleted && app.auth.onCompleted();
});
}
});
}
});
});
}
});
}, function () {
// before page shown
}, function () {
// after page closed
});
It should be placed in CloadIO side and the name of this is signUp.js, we can save it in the src
folder siblings the client
folder.
// sign up
database.connect(function (error, mongodb) {
if (error)
send('connect to database failed');
else {
// assign username
var u = args.email.split('@')[0], check = function (username) {
database.get(['clients'], {ob: 'v', c: 'username', e: username}, function (error, client) {
if (error)
send('get client failed');
else if (client)
check(u + (Math.floor(Math.random() * (9000) + 1000)));
else {
var query = {};
query['email.value'] = args.email;
mongodb.collection('users').find(query).count(function (error, count) {
if (error)
send('check email failed');
else if (count)
send('email is taken before');
else {
var verificationCode = Math.floor(1000 + Math.random() * 9000);
database.push(['users'], {
authType: 'username',
username: username,
pass: args.pass,
email: {
value: args.email
},
createdAt: Date.now(),
verificationCode: verificationCode
}, function (error, userKey) {
if (error)
send('push user failed');
else
database.set(['clients', userKey], {
username: username,
email: args.email
}, function (error) {
if (error)
send('push client failed');
else {
// get application secret value
settings.get('secret', function (error, secret) {
if (error)
send('sign user failed');
else {
// make payload
var payload = {iss: 'cloadio', sub: userKey + '@' + appName, iat: Date.now()};
// send token and payload to client
send(null, {t: jwt.encode(payload, secret), p: payload});
}
});
}
});
});
}
});
}
});
};
check(u);
}
});
For more information about how upload CloadIO functions, visit the CloadIO Function.
This page is first page of the app after user login, and also its contains the list of uploaded posts.
In order to make this page, add the below HTML codes into body section of index.html
file.
<div class="page home">
<div id="menuOverlay"></div>
<div class="menu">
<div class="header">
<img src="img/headerMenu.jpg">
</div>
<div class="content">
<ul>
<li class="button" key="profile">Profile</li>
<li class="button" key="addPost">Add Post</li>
<li class="button" key="signOut">Sign Out</li>
</ul>
</div>
<div class="footer">
<img src="img/codinicLogo.png">
</div>
</div>
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">All Posts</div>
</div>
</div>
<div class="content">
<div class="tableWrapper"></div>
</div>
<div class="footer">
<div class="button navi" key="home">Home</div>
<div class="button navi" key="addPost">Add Post</div>
<div class="button navi" key="profile">Profile</div>
</div>
</div>
In the home.css
file, we add the below styles and include it into index.html
file.
.page.home > .menu {
position: absolute;
top: 0;
bottom: 0;
width: 270px;
left: -280px;
background: #fff;
color: #6d6c6c;
z-index: 1003;
box-shadow: 0 0 11px 0 rgb(0 0 0 / 47%);
-webkit-box-shadow: 0 0 11px 0 rgb(0 0 0 / 47%);
-moz-box-shadow: 0 0 11px 0 rgba(0, 0, 0, 0.47);
transition: transform 400ms ease-out;
}
.page.home > .menu.active {
transform: translateX(280px);
}
.page.page.home > .menu > .header {
width: 270px;
height: 140px;
}
.page.page.home > .menu > .header > img {
width: 100%;
height: 100%;
object-fit: cover;
}
.page.page.home > .menu > .content {
position: absolute;
top: 140px;
right: 0;
bottom: 92px;
left: 0;
border-top: 1px solid #cdcdcd;
}
.page.page.home > .menu > .content > ul {
list-style: none;
padding: 0;
margin: 0;
}
.page.page.home > .menu > .content > ul > li {
font-weight: bold;
padding-right: 32px;
padding-left: 32px;
height: 53px;
line-height: 53px;
border-bottom: 1px solid #eeeeee;
}
.page.home > .menu > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
}
.page.home > .menu > .footer > img {
width: 150px;
object-fit: cover;
margin: 32px auto;
display: block;
}
.page.home > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0px 0px 6px 0px #ddd;
}
.page.home > .header > .left {
float: left;
height: 50px;
}
.page.home > .header > .left > .button {
padding: 17px 14.1px;
}
.page.home > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.home > .header > .right {
float: right;
padding-right: 22px;
}
.page.home > .header > .right > .title {
line-height: 50px;
font-size: 17px;
font-weight: 600;
text-align: center;
color: #1a8917;
}
.page.home > .content {
position: absolute;
top: 50px;
right: 0;
left: 0;
bottom: 0;
overflow-y: auto;
}
.page.home > .content > .tableWrapper > .fTable > .content > table {
padding-top: 7.5px;
padding-bottom: 57.5px;
}
.page.home > .content > .tableWrapper > .fTable > .content > table > thead {
display: none;
}
.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td {
padding: 7.5px 15px;
}
.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .left {
position: relative;
background-color: #efefef;
float: left;
width: 104px;
height: 104px;
border-radius: 8px;
}
.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .left > img[src='img/imageWhite.svg'] {
width: 24px;
margin: 61px auto;
display: block;
}
.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .left > img:not([src='img/imageWhite.svg']) {
object-fit: cover;
opacity: 0;
transition: opacity 0.6s ease;
width: 100%;
height: 100%;
border-radius: 8px;
}
.page.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .right {
margin-top: 9px;
margin-left: 16px;
float: left;
width: calc(100% - 120px);
}
.page.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .right > .title {
font-weight: bold;
font-size: 16px;
}
.page.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .right > .description {
color: #454545;
}
.page.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .right > .info {
display: table;
width: 100%;
font-size: 12px;
color: #454545;
}
.page.page.home > .content > .tableWrapper > .fTable > .content > table > tbody > tr > td > .right > .info > div {
display: table-cell;
}
.page.home > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0px 0px 6px 0px #ddd;
background-color: #ffffff;
}
.page.home > .footer > div {
float: left;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
font-weight: bold;
}
In this page we used the table
and maybe other added modules, these should be placed in the modules folder and then included in index.html
, as we did it in the General Information section.
For more information about table module, visit the table module.
Add the js codes into home.js
file and include in the script section in the index.html
file.
var homePage = new Page('home', function () {
// after page shown
var jPage = $('.page.home'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
jFooter = jPage.children('.footer'),
model = {
image: {
inputType: 'image',
text: 'Upload image'
},
title: {
inputType: 'text',
text: 'Title',
placeholder: 'Enter your title here',
required: true
},
description: {
inputType: 'longText',
text: 'Description',
placeholder: 'Enter your description here',
required: true
},
createdAt: {
inputType: 'time',
text: 'Date',
display: 'inTable'
},
createdBy: {
text: 'Author',
display: 'inTable'
}
};
// handle menu
var jMenu = jPage.children('.menu'), jOverlay = $('#menuOverlay');
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/menuBlack.svg">',
color: '#b6bcb6',
onPush: function () {
if (jMenu.hasClass('active')) {
jMenu.removeClass('active');
jOverlay.css('display', 'none');
jOverlay.css('opacity', '0');
}
else {
jMenu.addClass('active');
jOverlay.css('display', 'block');
jOverlay.css('opacity', '1');
var jMenuContent = jMenu.find(' > .content > ul');
if (!app.auth.user.key)
jMenuContent.children('li[key="signOut"]').hide();
jMenuContent.children('.button').each(function (i, o) {
var jSelf = $(o), key = jSelf.attr('key');
app.ui.button(jMenuContent, 'ripple', {
color: '#b6bcb6',
key: key,
onPush: function () {
jOverlay.click();
var key = jSelf.attr('key');
if (key === 'profile')
profilePage.show();
else if (key === 'addPost') {
if (app.auth.user.key)
newPostPage.show();
else {
app.controls.box('confirm', null, 'You are not signed in, do you want to sign up now?', 'center', function(result) {
if (result) {
signUpPage.show();
}
});
}
}
else if (key === 'signOut') {
app.controls.box('confirm', null, 'Are you sure?', 'center', function(result) {
if (result) {
mdtoast('Checking out...');
setTimeout(function () {
app.auth.clear();
}, 500);
}
});
}
}
});
});
}
}
});
jOverlay.click(function () {
jMenu.removeClass('active');
jOverlay.hide();
});
new Table(model, jContent.children('.tableWrapper'), '/posts', {
pageCapacity: 25,
filter: false,
onRecordsReceived: function (records) {
// cache
if (app.local.posts)
$.extend(app.local.posts, records);
else
app.local.posts = records;
},
onRecordRendered: function (recordKey, record, jRecord) {
jRecord.html('<td class="button">\
<div class="left">\
<img onload="this.style.opacity = 1">\
</div>\
<div class="right">\
<div class="title">' + record.title + '</div>\
<div class="description">' + (record.description.substr(0, 40) + '...') + '</div>\
<div class="info">\
<div class="createdBy"><strong>' + model['createdBy'].text + ': </strong><span>...</span></div>\
<div class="createdAt"><strong>' + model['createdAt'].text + ': </strong> ' + new Date(parseInt(record.createdAt)).toLocaleDateString() + '</div>\
</div>\
</div>\
</td>');
var jImg = jRecord.find(' > td > .left > img');
if (record.image) {
var url = 'posts/' + recordKey + '/image/binary.' + record.image.ext;
if (app.local.images[url])
jImg.attr('src', app.local.images[url]);
else {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
app.local.images[url] = reader.result;
jImg.attr('src', reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
}
var jCreatedBy = jRecord.find(' > td > .right > .info > .createdBy');
if (record.createdBy) {
ref.child('clients/' + record.createdBy).get(function (error, client) {
if (error)
console.error('get client value failed', error);
else if (client)
jCreatedBy.children('span').html(client.username.substr(0, 10) + '...');
});
}
app.ui.button(jRecord, 'ripple', {
color: '#9c9c9c',
onPush: function () {
if (record.createdBy === app.auth.user.key)
editPostPage.show(recordKey);
else
postDetailPage.show(recordKey);
}
});
}
}).render();
jFooter.children('.navi[key="home"]').css('color', '#1a8917');
}, function () {
// before page shown
}, function () {
// after page closed
});
Because the footer section is common in some pages, we are going to add the codes into app.js
file, on the onDocumentReady
section.
It is better to add the below codes after screen codes and before showing the loading page.
// render footer buttons
for (var pageKey in app.pages) {
var jPage = $('.page.' + app.pages[pageKey].name);
var jFooter = jPage.children('.footer'), l = jFooter.children('.navi').length, w = jFooter.find(' > .navi:first-child > img').width(), p = (app.local.safeWindow.width - (l * w)) / ((l * 2) + 2);
jPage.find(' > .footer > .navi').each(function (i, o) {
var jSelf = $(o), key = jSelf.attr('key');
if (jSelf.hasClass('button'))
app.ui.button(jFooter, 'none', {
classes: 'navi',
key: key,
color: '#b6bcb6',
onPush: function () {
if (key === 'home')
homePage.show();
else if (key === 'profile') {
if (app.auth.user.key)
profilePage.show();
else {
app.controls.box('confirm', null, 'You are not signed in, do you want to sign in now?', 'center', function(result) {
if (result) {
signInPage.show();
}
});
}
}
else if (key === 'addPost') {
if (app.auth.user.key)
newPostPage.show();
else {
app.controls.box('confirm', null, 'You are not signed in, do you want to sign up now?', 'center', function(result) {
if (result) {
signInPage.show();
}
});
}
}
}
});
var width;
if (i === 0) {
width = 3 * p;
jSelf.css('padding-left', p);
}
else if (i === (l - 1)) {
width = 3 * p;
jSelf.css('padding-right', p);
}
else
width = 2 * p;
width += w;
jSelf.css('width', width);
jSelf.children('.circle').css('width', jSelf.height());
});
}
This page is for viewing user information in the application.
In order to make profile page, add the below HTML codes into body section of index.html
file.
<div class="page profile">
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">Profile</div>
</div>
</div>
<div class="content">
<div class="formWrapper"></div>
</div>
<div class="footer">
<div class="button navi" key="home">Home</div>
<div class="button navi" key="addPost">Add Post</div>
<div class="button navi" key="profile">Profile</div>
</div>
</div>
This codes will add into profile.css
file, into CSS folder. Also it should be included into index.html
, at the head section.
.page.profile > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0px 0px 6px 0px #ddd;
}
.page.profile > .header > .left {
float: left;
height: 68px;
}
.page.profile > .header > .left > .button {
padding: 17px 16px;
}
.page.profile > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.profile > .header > .right {
float: right;
padding-right: 22px;
}
.page.profile > .header > .right > .title {
line-height: 50px;
font-size: 17px;
font-weight: 600;
text-align: center;
color: #1a8917;
}
.page.profile > .content {
position: absolute;
top: 50px;
right: 0;
left: 0;
bottom: 50px;
overflow-y: auto;
padding-top: 32px;
padding-right: 32px;
padding-left: 32px;
}
.page.profile > .content > .formWrapper > .fForm > .fields > .field > .key {
font-size: 16px;
display: inline-block;
padding-bottom: 15px;
font-weight: bold;
}
.page.profile > .content > .formWrapper > .fForm > .fields > .field > .value {
margin-bottom: 32px;
padding: 13.5px 24px;
border: 1px solid #ececec;
border-radius: 8px;
background-color: #ececec;
}
.page.profile > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0px 0px 6px 0px #ddd;
background-color: #ffffff;
}
.page.profile > .footer > div {
float: left;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
font-weight: bold;
}
The needed icons should be added to img folder. You can download them form here.
In this page we used the form
and ui
modules, these should be placed in the modules folder and then included in index.html
, as we did it in the General Information section.
This codes will add into profile.js
file, into JS folder. Also it should be included into index.html
, in the bottom section.
var profilePage = new Page('profile', function () {
// after page shown
// define variables
var jPage = $('.page.profile'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
jEmail = jContent.children('.email'),
jPass = jContent.children('.pass'),
jFooter = jPage.children('.footer'),
model = {
email: {
inputType:'email',
text:'Email',
syntax: {
value: '^\\\\S+@\\\\S+\\\\.\\\\S+$'
}
},
username: {
text:'user Name'
}
};
// back
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/backBlack.svg">',
color: '#b6bcb6',
onPush: function (jButton) {
history.back();
}
});
// render form
new Form ('view', model, jContent.children('.formWrapper'), app.auth.user, '/clients/' + app.auth.user.key).render();
jFooter.children('.navi[key="profile"]').css('color', '#1a8917');
}, function () {
// before page shown
}, function () {
// after page closed
});
This page is for add new post by user.
In order to make newPost page, add the below HTML codes into body section of index.html
file.
<div class="page newPost">
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">New Post</div>
</div>
</div>
<div class="content">
<div class="formWrapper"></div>
<div class="button" key="save">Save</div>
</div>
<div class="footer">
<div class="button navi" key="home">Home</div>
<div class="button navi" key="addPost">Add Post</div>
<div class="button navi" key="profile">Profile</div>
</div>
</div>
The styles should be added into newPost.css
file, and also add to head section of index.html
.
.page.newPost > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0px 0px 6px 0px #ddd;
}
.page.newPost > .header > .left {
float: left;
height: 68px;
}
.page.newPost > .header > .left > .button {
padding: 17px 16px;
}
.page.newPost > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.newPost > .header > .right {
float: right;
padding-right: 22px;
}
.page.newPost > .header > .right > .title {
line-height: 50px;
font-size: 17px;
font-weight: 600;
text-align: center;
color: #1a8917;
}
.page.newPost > .content {
position: absolute;
top: 50px;
right: 0;
left: 0;
bottom: 50px;
overflow-y: auto;
}
.page.newPost > .content > .formWrapper {
padding-top: 15px;
padding-bottom: 40px;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field {
padding-right: 32px;
padding-left: 32px;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field > img[src='img/imageWhite.svg'] {
width: 24px;
margin: 61px auto;
display: block;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field > img:not([src='img/imageWhite.svg']) {
object-fit: cover;
opacity: 0;
transition: opacity 0.6s ease;
width: 100%;
height: 100%;
border-radius: 8px;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field > label {
font-size: 16px;
display: inline-block;
padding-bottom: 15px;
font-weight: bold;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field > input {
margin-bottom: 32px;
padding: 13.5px 24px;
border: 1px solid #b6bcb6;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons {
margin-bottom: 32px;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons > .large {
position: relative;
}
.page.newPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons > .large > .remove {
color: #ffffff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
background-color: red;
border-radius: 5px;
padding: 2px 6px;
}
.page.newPost > .content > .button {
background-color: #1a8917;
height: 54px;
color: #ffffff;
line-height: 54px;
transition: opacity 0.7s ease;
text-align: center;
border-radius: 8px;
font-weight: bold;
margin-right: 32px;
margin-bottom: 32px;
margin-left: 32px;
}
.page.newPost > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0px 0px 6px 0px #ddd;
background-color: #ffffff;
}
.page.newPost > .footer > div {
float: left;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
font-weight: bold;
}
The JS codes should be added into newPost.js
file, and also add to bottom section of index.html
.
var newPostPage = new Page('newPost', function () {
// after page shown
// define variables
var path, data, jPage = $('.page.newPost'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
jFooter = jPage.children('.footer'),
model = {
image: {
inputType: 'image',
text: 'Upload image'
},
title: {
inputType: 'text',
text: 'Title',
placeholder: 'Enter your title here',
required: true
},
description: {
inputType: 'longText',
text: 'Description',
placeholder: 'Enter your description here',
required: true
},
createdAt: {
inputType: 'time',
text: 'Date',
display: 'inTable'
},
createdBy: {
text: 'Author',
display: 'inTable'
}
};
// back
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/backBlack.svg">',
color: '#b6bcb6',
onPush: function (jButton) {
history.back();
}
});
// render form
var form = new Form('create', model, jContent.children('.formWrapper'), null, '/posts');
form.render();
// save
app.ui.button(jContent, 'ripple', {
color: '#b6bcb6',
onPush: function (jButton) {
form.save(function (error) {
if (error) {
if (error.on === 'empty')
mdtoast(error.field.text + ' cannot be empty.', {type: mdtoast.ERROR});
else if (error.on === 'validation' || error.on === 'execution')
mdtoast(error.remote ? error.text : error.field.text + ' isn’t valid.', {type: mdtoast.ERROR});
else {
if (error.on === 'save')
console.error('save failed', error.text);
mdtoast('Save failed.', {type: mdtoast.ERROR});
}
}
else {
mdtoast('Save blog successful.', {type: mdtoast.SUCCESS});
homePage.show();
}
});
}
});
jFooter.children('.navi[key="addPost"]').css('color', '#1a8917');
}, function () {
// before page shown
}, function () {
// after page closed
});
This page is for editing user-added posts.
In order to make editPost page, add the below HTML codes into body section of index.html
file.
<div class="page editPost">
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">Edit Post</div>
</div>
</div>
<div class="content">
<div class="formWrapper"></div>
<div class="btns">
<div class="button" key="save">Save</div>
<div class="button" key="delete">Delete</div>
</div>
</div>
<div class="footer">
<div class="button navi" key="home">Home</div>
<div class="button navi" key="addPost">Add Post</div>
<div class="button navi" key="profile">Profile</div>
</div>
</div>
The styles should be added into editPost.css
file, and also add to head section of index.html
.
.page.editPost > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0px 0px 6px 0px #ddd;
}
.page.editPost > .header > .left {
float: left;
height: 68px;
}
.page.editPost > .header > .left > .button {
padding: 17px 16px;
}
.page.editPost > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.editPost > .header > .right {
float: right;
padding-right: 22px;
}
.page.editPost > .header > .right > .title {
line-height: 50px;
font-size: 17px;
font-weight: 600;
text-align: center;
color: #1a8917;
}
.page.editPost > .content {
position: absolute;
top: 50px;
right: 0;
left: 0;
bottom: 50px;
overflow-y: auto;
}
.page.editPost > .content > .formWrapper {
padding-top: 15px;
padding-bottom: 40px;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field {
padding-right: 32px;
padding-left: 32px;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field > img[src='img/imageWhite.svg'] {
width: 24px;
margin: 61px auto;
display: block;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field > img:not([src='img/imageWhite.svg']) {
object-fit: cover;
opacity: 0;
transition: opacity 0.6s ease;
width: 100%;
height: 100%;
border-radius: 8px;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field > label {
font-size: 16px;
display: inline-block;
padding-bottom: 15px;
font-weight: bold;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field > input {
margin-bottom: 32px;
padding: 13.5px 24px;
border: 1px solid #b6bcb6;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons {
margin-bottom: 32px;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons > .large {
position: relative;
}
.page.editPost > .content > .formWrapper > .fForm > .fields > .field.image > .buttons > .large > .remove {
color: #ffffff;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
background-color: red;
border-radius: 5px;
padding: 2px 6px;
}
.page.editPost > .content > .btns {
padding-right: 32px;
padding-left: 32px;
padding-bottom: 32px;
overflow: hidden;
}
.page.editPost > .content > .btns > .button {
display: table-cell;
height: 54px;
color: #ffffff;
line-height: 54px;
transition: opacity 0.7s ease;
text-align: center;
border-radius: 8px;
font-weight: bold;
width: 45%;
}
.page.editPost > .content > .btns > .button[key='save'] {
background-color: #1a8917;
float: left;
}
.page.editPost > .content > .btns > .button[key='delete'] {
background-color: #f44336;
float: right;
}
.page.editPost > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0px 0px 6px 0px #ddd;
background-color: #ffffff;
}
.page.editPost > .footer > div {
float: left;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
font-weight: bold;
}
The JS codes should be added into editPost.js
file, and also add to bottom section of index.html
.
var editPostPage = new Page('editPost', function (postKey) {
// after page shown
// define variables
var path, data, jPage = $('.page.editPost'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
jBtns = jContent.children('.btns'),
model = {
image: {
inputType: 'image',
text: 'Upload image'
},
title: {
inputType: 'text',
text: 'Title',
placeholder: 'Enter your title here',
required: true
},
description: {
inputType: 'longText',
text: 'Description',
placeholder: 'Enter your description here',
required: true
},
createdAt: {
inputType: 'time',
text: 'Date',
display: 'inTable'
},
createdBy: {
text: 'Author',
display: 'inTable'
}
};
// back
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/backBlack.svg">',
color: '#b6bcb6',
onPush: function (jButton) {
history.back();
}
});
// render form
var form = new Form('edit', model, jContent.children('.formWrapper'), app.local.posts[postKey], '/posts/' + postKey);
form.render();
// save
app.ui.button(jBtns, 'ripple', {
key: 'save',
color: '#b6bcb6',
onPush: function (jButton) {
form.save(function (error) {
if (error) {
if (error.on === 'empty')
mdtoast(error.field.text + ' cannot be empty.', {type: mdtoast.ERROR});
else if (error.on === 'validation' || error.on === 'execution')
mdtoast(error.remote ? error.text : error.field.text + ' isn’t valid.', {type: mdtoast.ERROR});
else {
if (error.on === 'save')
console.error('save failed', error.text);
mdtoast('Save failed.', {type: mdtoast.ERROR});
}
}
else {
mdtoast('Save blog successful.', {type: mdtoast.SUCCESS});
homePage.show();
}
});
}
});
// delete
app.ui.button(jBtns, 'ripple', {
key: 'delete',
color: '#b6bcb6',
onPush: function (jButton) {
app.controls.box('confirm', null, 'Are you sure you want to delete this post?', 'center', function(result) {
if (result) {
ref.child('posts/' + postKey).del(function (error) {
if (error) {
console.error('delete blog failed', error);
toast('Delete blog failed. Please try again.', {type: mdtoast.ERROR});
}
else {
delete app.local.posts[postKey];
mdtoast('Delete blog successful.', {type: mdtoast.SUCCESS});
homePage.show();
}
});
}
});
}
});
}, function () {
// before page shown
}, function () {
// after page closed
});
This page is to showing the detail of every added post.
In order to make postDetail page, add the below HTML codes into body section of index.html
file.
<div class="page postDetail">
<div class="header">
<div class="left">
<div class="button"></div>
</div>
<div class="right">
<div class="title">Post Detail</div>
</div>
</div>
<div class="content">
<div class="formWrapper"></div>
</div>
<div class="footer">
<div class="button navi" key="home">Home</div>
<div class="button navi" key="addPost">Add Post</div>
<div class="button navi" key="profile">Profile</div>
</div>
</div>
The styles should be added into postDetail.css
file, and also add to head section of index.html
.
.page.postDetail > .header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 50px;
overflow: hidden;
box-shadow: 0px 0px 6px 0px #ddd;
}
.page.postDetail > .header > .left {
float: left;
height: 68px;
}
.page.postDetail > .header > .left > .button {
padding: 17px 16px;
}
.page.postDetail > .header > .left > .button > img {
display: block;
height: 16px;
}
.page.postDetail > .header > .right {
float: right;
padding-right: 22px;
}
.page.postDetail > .header > .right > .title {
line-height: 50px;
font-size: 17px;
font-weight: 600;
text-align: center;
color: #1a8917;
}
.page.postDetail > .content {
position: absolute;
top: 50px;
right: 0;
left: 0;
bottom: 50px;
overflow-y: auto;
}
.page.postDetail > .content > .formWrapper {
padding-bottom: 32px;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field:not([key='image']) {
padding-right: 32px;
padding-left: 32px;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field > .key {
display: none;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field[key='title'] > .value {
font-size: 18px;
font-weight: bold;
padding-bottom: 15px;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field[key='description'] > .value {
color: #454545;
padding-bottom: 15px;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field[key='createdAt'],
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field[key='createdBy'] {
display: table-cell;
width: 50%;
font-weight: bold;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field[key='image'] {
background-color: #efefef;
height: 200px;
margin-bottom: 15px;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field > img[src='img/imageWhite.svg'] {
width: 24px;
margin: 61px auto;
display: block;
}
.page.postDetail > .content > .formWrapper > .fForm > .fields > .field > img:not([src='img/imageWhite.svg']) {
object-fit: cover;
opacity: 0;
transition: opacity 0.6s ease;
width: 100%;
height: 100%;
}
.page.postDetail > .footer {
position: absolute;
right: 0;
bottom: 0;
left: 0;
box-shadow: 0px 0px 6px 0px #ddd;
background-color: #ffffff;
}
.page.postDetail > .footer > div {
float: left;
text-align: center;
padding-top: 15px;
padding-bottom: 15px;
font-weight: bold;
}
The JS codes should be added into postDetail.js
file, and also add to bottom section of index.html
.
var postDetailPage = new Page('postDetail', function (postKey) {
// after page shown
// define variables
var jPage = $('.page.postDetail'),
jHeader = jPage.children('.header'),
jContent = jPage.children('.content'),
model = {
image: {
inputType: 'image',
text: 'Upload image'
},
title: {
inputType: 'text',
text: 'Title',
placeholder: 'Enter your title here',
required: true
},
description: {
inputType: 'longText',
text: 'Description',
placeholder: 'Enter your description here',
required: true
},
createdAt: {
inputType: 'time',
text: 'Date'
},
createdBy: {
text: 'Author'
}
};
// back
app.ui.button(jHeader.children('.left'), 'ripple', {
html: '<img src="img/backBlack.svg">',
color: '#b6bcb6',
onPush: function (jButton) {
history.back();
}
});
// render form
new Form('view', model, jContent.children('.formWrapper'), app.local.posts[postKey], '/posts/' + postKey, {
onItemRendered: function (fieldKey, field, jField, value) {
if (fieldKey === 'image') {
if (value) {
jField.html('<img onload="this.style.opacity = 1">');
var jImg = jField.children('img');
if (field.image) {
var url = 'posts/' + postKey + '/image/binary.' + field.image.ext;
if (app.local.images[url])
jImg.attr('src', app.local.images[url]);
else {
var xhr = new XMLHttpRequest();
xhr.onload = function () {
var reader = new FileReader();
reader.onloadend = function () {
app.local.images[url] = reader.result;
jImg.attr('src', reader.result);
};
reader.readAsDataURL(xhr.response);
};
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.send();
}
}
}
else
jField.remove();
}
else if (fieldKey === 'createdBy') {
ref.child('clients/' + value).get(function (error, client) {
if (error)
console.error('get client value failed', error);
else if (client)
jField.children('.value').val(client.username);
});
}
}
}).render();
}, function () {
// before page shown
}, function () {
// after page closed
});