Add FAQ functionality and some style tweaks

Switched to HTML5-style routing
Reworked Express backend to accommodate HTML5-style routing
Links are now highlighted with a black background and white foreground
Reworked spacing on H1s
Added classes to nav buttons
This commit is contained in:
Thomas Wade 2018-10-30 17:33:24 +10:30
parent e1eecfdd45
commit a205dc9c7e
25 changed files with 125 additions and 33 deletions

View File

@ -1,6 +1,42 @@
[ [
{ {
"question":"What's a 'yeet'?", "title":"What happened to the old Lemonblog?",
"answer":"Well you see, Billy..." "content":"Long story short, the old Lemonblog is dead. *Super* dead. It's since been redesigned for better cross-platform usability and features a newer (and far better in my opinion) aesthetic."
},
{
"title":"Lorem ipsum dolor sit amet, consectetur adipiscing elit?",
"content":"Vestibulum iaculis, eros accumsan tincidunt dictum, velit nunc laoreet ipsum, quis varius risus urna sit amet turpis. Vivamus fermentum aliquam maximus. In porta eleifend lectus in fermentum. Aenean placerat mi libero, malesuada maximus mi faucibus vel. Aliquam vitae lacus sagittis, porttitor urna in, fringilla nibh. Quisque nec viverra ipsum, sed tincidunt quam. Quisque vulputate vel lectus ac varius. Aenean vehicula lectus eu arcu pharetra eleifend. Aenean ornare elit sit amet vehicula fringilla. Fusce non accumsan odio. Curabitur elit libero, pellentesque sit amet egestas quis, vehicula sit amet mi."
},
{
"title":"Etiam tempus ipsum eu nunc posuere suscipit?",
"content":"Sed sit amet nisl ac sapien faucibus lacinia vel sed tortor. Donec tincidunt nulla in arcu condimentum, eget scelerisque nulla euismod. Proin eu est vitae libero ullamcorper convallis quis sed enim. Nunc non quam hendrerit, scelerisque mauris vel, suscipit sem. Nam mattis diam diam, non blandit ante molestie at. Nunc egestas lacus nec placerat mollis. Mauris egestas neque varius consectetur ultricies. Vestibulum ut interdum dui. Pellentesque eu nisl viverra, placerat risus sed, faucibus justo."
},
{
"title":"Nulla elit dui, luctus ac interdum ut, dignissim sed ante?",
"content":"Maecenas interdum turpis non ligula porttitor pulvinar. Integer at tempor ex. In finibus leo eu commodo convallis. Etiam molestie nisl id ligula vulputate varius. Aenean placerat, erat non placerat iaculis, ipsum nibh luctus ipsum, ac vehicula diam mauris ut purus. Donec nec euismod felis. Sed sagittis eleifend nisi id porttitor. Aliquam erat volutpat. Sed ultricies velit purus, nec suscipit felis sagittis id. Nullam fermentum dictum purus, nec cursus ligula commodo in. Donec commodo a lectus a sagittis."
},
{
"title":"Pellentesque at ullamcorper velit?",
"content":"Aliquam eros orci, blandit ac nisl sed, convallis eleifend mauris. Sed pretium mollis ante. Suspendisse dictum massa ut libero fringilla sagittis. Donec at sapien bibendum, iaculis quam nec, eleifend nisl. Curabitur ac euismod mi, in gravida arcu. Maecenas nunc urna, ornare eget enim quis, rhoncus euismod tellus. Praesent eget sem placerat, euismod tellus eu, porttitor elit. Donec tempus, nunc id dictum auctor, metus ipsum ornare justo, et vestibulum nulla sem eu turpis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce fermentum, lacus at consequat facilisis, mauris odio euismod turpis, sed semper turpis mauris quis dui."
},
{
"title":"Vestibulum id sagittis mauris?",
"content":"Praesent id justo auctor, egestas enim vel, auctor erat. Nunc ullamcorper ac enim a blandit. Maecenas arcu ligula, pretium lacinia imperdiet ac, interdum ac nisl. Aliquam erat volutpat. Nulla commodo malesuada accumsan. Fusce eros quam, viverra sit amet sollicitudin id, pulvinar molestie diam. Nunc et ante vitae risus tempor dapibus et quis enim. Sed sodales urna non semper tristique. In eget elit pharetra, efficitur nisi in, tincidunt nunc. Donec eu mauris lorem. Nulla maximus sodales velit nec ornare."
},
{
"title":"Sed ipsum metus, pellentesque at enim eget, lacinia consectetur ex?",
"content":"Cras dapibus consequat tempor. Nunc justo mi, tempor nec sodales a, accumsan ac nunc. In volutpat iaculis risus, a ultricies quam egestas nec. Morbi fringilla neque a posuere semper. Suspendisse eget felis in risus elementum rutrum et eu ipsum. Donec sit amet ante elementum, vulputate ante eu, interdum dolor. Mauris consectetur ac elit mollis porttitor. Nam a ex enim. Ut eu massa at dui condimentum viverra."
},
{
"title":"Curabitur viverra sem ac fringilla egestas?",
"content":"Nunc id diam eleifend, accumsan erat ac, dapibus libero. Donec consectetur id nunc at facilisis. Vestibulum pretium leo in lectus pulvinar, eget imperdiet ex blandit. Praesent in lorem ac velit gravida venenatis scelerisque vel purus. Sed ornare quam non quam volutpat, nec ornare urna finibus. Aliquam sodales dui eu risus mattis, a pellentesque turpis eleifend. Donec leo ex, vulputate sit amet tempus sit amet, sodales et purus. Cras blandit a orci quis maximus."
},
{
"title":"Aliquam vehicula mauris enim, nec placerat massa pellentesque venenatis?",
"content":"Vivamus fermentum odio magna, et porttitor diam varius sodales. Aenean sit amet ipsum gravida, porta nisi et, convallis turpis. Proin nec felis finibus, vestibulum sapien vitae, posuere nisl. Cras tempus lacinia tincidunt. Mauris placerat elit et sollicitudin interdum. Aenean vehicula maximus euismod. Vivamus mollis, nisl in aliquam rhoncus, mauris metus tincidunt enim, ut finibus lectus nisi vel arcu. Proin pulvinar ipsum nisl, et dignissim justo sodales eu. Proin et lorem augue. Praesent fermentum erat nec est auctor, ac rhoncus nisl lobortis. Duis pellentesque nisi at urna tincidunt cursus. Curabitur aliquam mi ac pulvinar imperdiet. Proin sem leo, aliquam ut aliquam ac, scelerisque nec enim. Curabitur felis velit, varius ac ex ut, ultricies efficitur lorem."
},
{
"title":"Pellentesque porta tellus in velit suscipit dignissim?",
"content":"Morbi non lectus luctus, dictum leo vitae, finibus nulla. Cras rhoncus diam eu diam porta, id dapibus tortor vulputate. Aliquam dapibus neque sit amet dui aliquam venenatis. Nullam vitae magna tellus. Maecenas faucibus pretium libero, at tempor lectus varius non. Praesent turpis lorem, ultricies ut eleifend nec, dignissim a neque. Duis gravida mollis purus quis blandit. Praesent dui nibh, mattis elementum auctor nec, congue non dolor. Etiam lorem elit, convallis eu dignissim a, imperdiet quis ex. Mauris et lorem blandit, congue dolor sit amet, laoreet leo. Maecenas congue eu nibh et porta. Donec a varius lacus."
} }
] ]

View File

@ -5,27 +5,21 @@ var bodyParser = require('body-parser'); // Data conversion, JSON parsing
// Set up Express // Set up Express
var app = express(); var app = express();
app.use(express.static("./")); // Serve static files from root app.use(express.static("./static/")); // Serve static files from /static/
app.use(express.json()); // Parse JSON in requests app.use(express.json()); // Parse JSON in requests
// app.use("/", (req, res, next) => // Finally send a 404 page // app.use("/", (req, res, next) => // Finally send a 404 page
// { // {
// res.status(404).sendFile("404.html", {"root": "./"}); // res.status(404).sendFile("404.html", {"root": "./"});
// }); // });
// Root route, respond with with index.html
app.get("/", function(req, res)
{
res.sendFile("./index.html");
});
// Posts route, return posts.json // Posts route, return posts.json
app.get("/posts", function(req, res) app.get("/api/posts", function(req, res)
{ {
res.json(JSON.parse(fs.readFileSync('./data/posts.json'))); res.json(JSON.parse(fs.readFileSync('./data/posts.json')));
}); });
// Posts route, digest received post // Posts route, digest received post
app.post("/posts", function(req, res) app.post("/api/posts", function(req, res)
{ {
var postsFile = fs.readFileSync('./data/posts.json'); var postsFile = fs.readFileSync('./data/posts.json');
var posts = JSON.parse(postsFile); var posts = JSON.parse(postsFile);
@ -54,11 +48,23 @@ app.post("/posts", function(req, res)
}); });
// About route, return about.json // About route, return about.json
app.get("/about", (req, res) => app.get("/api/about", (req, res) =>
{ {
res.json(JSON.parse(fs.readFileSync('./data/about.json'))); res.json(JSON.parse(fs.readFileSync('./data/about.json')));
}); });
// FAQ route, return faq.json
app.get("/api/faq", (req, res) =>
{
res.json(JSON.parse(fs.readFileSync('./data/faq.json')));
});
// Default route, serve static index.html and let AngularJS handle the remainder
app.get("/*", (req, res) =>
{
res.sendFile("./static/index.html", {root: __dirname})
});
// Start listening // Start listening
app.listen(8080, function() app.listen(8080, function()
{ {

View File

@ -14,18 +14,17 @@
<script src="scripts/marked.min.js" type="application/javascript"></script> <script src="scripts/marked.min.js" type="application/javascript"></script>
<script src="scripts/ng-tags-input.min.js" type="application/javascript"></script> <script src="scripts/ng-tags-input.min.js" type="application/javascript"></script>
<script src="scripts/app.js" type="application/javascript"></script> <script src="scripts/app.js" type="application/javascript"></script>
<base href="/">
</head> </head>
<body> <body>
<div class="rootContainer"> <div class="rootContainer">
<ng-include src="'views/header.html'"></ng-include> <ng-include src="'partials/header.html'"></ng-include>
<div class="centreColumn"> <div class="centreColumn">
<div class="contentContainer"> <div class="contentContainer">
<ng-view></ng-view> <ng-view></ng-view>
</div> </div>
<ng-include src="'views/sidebar.html'"></ng-include> <ng-include src="'partials/sidebar.html'"></ng-include>
</div> </div>
<ng-include src="'views/footer.html'"></ng-include> <ng-include src="'partials/footer.html'"></ng-include>
</div> </div>
</body> </body>
</html> </html>

View File

@ -8,6 +8,6 @@
adds zest to your day adds zest to your day
</span> </span>
</a> </a>
<ng-include src="'views/navbuttons.html'"></ng-include> <ng-include src="'partials/navbuttons.html'"></ng-include>
</div> </div>
</div> </div>

View File

@ -0,0 +1,6 @@
<div class="navButtons" ng-controller="navButtons">
<a class="navButton" ng-click="navClick('/')" ng-class="isCurrent('/')">Blog</a>
<a class="navButton" ng-click="navClick('/about')" ng-class="isCurrent('/about')">About</a>
<a class="navButton" ng-click="navClick('/faq')" ng-class="isCurrent('/faq')">FAQ</a>
<a class="navButton" ng-click="navClick('/create')" ng-class="isCurrent('/create')">Create</a>
</div>

View File

@ -1,7 +1,7 @@
var app = angular.module("app", ["ngRoute", "ngSanitize", "ngTagsInput"]); var app = angular.module("app", ["ngRoute", "ngSanitize", "ngTagsInput"]);
app.config( app.config(
function ($routeProvider) function ($routeProvider, $locationProvider)
{ {
$routeProvider $routeProvider
.when("/", {templateUrl: "views/blog.html", controller: "blog"}) .when("/", {templateUrl: "views/blog.html", controller: "blog"})
@ -9,15 +9,22 @@ app.config(
.when("/about", {templateUrl: "views/about.html", controller: "about"}) .when("/about", {templateUrl: "views/about.html", controller: "about"})
.when("/faq", {templateUrl: "views/faq.html", controller: "faq"}) .when("/faq", {templateUrl: "views/faq.html", controller: "faq"})
.otherwise({templateUrl: "404.html"}); .otherwise({templateUrl: "404.html"});
$locationProvider.html5Mode({enabled: true, requireBase: false});
} }
); );
app.run(($anchorScroll) =>
{
$anchorScroll.yOffset = 100;
})
app.controller("blog", ["$scope", "$http", app.controller("blog", ["$scope", "$http",
function($scope, $http) function($scope, $http)
{ {
$scope.posts = []; $scope.posts = [];
$http.get("/posts") $http.get("/api/posts")
.then((r) => { $scope.posts = angular.fromJson(r.data); }); .then((r) => { $scope.posts = angular.fromJson(r.data); });
} }
]); ]);
@ -44,7 +51,7 @@ app.controller("create", ["$scope", "$http",
}; };
// POST it to the server // POST it to the server
$http.post("/posts", post); $http.post("/api/posts", post);
// Clear the fields // Clear the fields
$scope.title = ""; $scope.title = "";
@ -83,15 +90,23 @@ app.controller("about", ["$scope", "$http",
{ {
$scope.aboutContent = ""; $scope.aboutContent = "";
$http.get("about") $http.get("/api/about")
.then((r) => { $scope.aboutContent = angular.fromJson(r.data).content; }); .then((r) => { $scope.aboutContent = angular.fromJson(r.data).content; });
} }
]); ]);
app.controller("faq", ["$scope", app.controller("faq", ["$scope", "$http", "$location", "$anchorScroll",
function($scope) function($scope, $http, $location, $anchorScroll)
{ {
$scope.entries = []
$http.get("/api/faq")
.then((r) => { $scope.entries = angular.fromJson(r.data) });
$scope.scrollTo = (hash) =>
{
$anchorScroll(hash);
};
} }
]); ]);

View File

@ -28,7 +28,16 @@ a
text-decoration: none; text-decoration: none;
color: #000; color: #fff;
background-color: #000;
}
a:not(.navButton)
{
margin-right: 2px;
margin-left: 2px;
box-shadow: 2px 0 0 0 #000, -2px 0 0 0 #000;
} }
input, input,
@ -59,6 +68,17 @@ button:disabled
background-color: #888; background-color: #888;
} }
h1,
h2,
h3,
h4,
h5,
h6
{
margin-top: 20px;
margin-bottom: 5px;
}
h1 h1
{ {
font-size: 45px; font-size: 45px;
@ -145,7 +165,7 @@ h1
float: right; float: right;
} }
.navButtons a .navButtons .navButton
{ {
display: block; display: block;
float: left; float: left;
@ -163,12 +183,12 @@ h1
color: #fff; color: #fff;
background-color: #000; background-color: #000;
} }
.navButtons a.current .navButtons .navButton.current
{ {
color: #000; color: #000;
box-shadow: inset 0 60px 0 0 #fff; /* Box shadow hack to get an inset border */ box-shadow: inset 0 60px 0 0 #fff; /* Box shadow hack to get an inset border */
} }
.navButtons a:hover:not(.current) .navButtons .navButton:hover:not(.current)
{ {
box-shadow: inset 0 10px 0 0 #fff; box-shadow: inset 0 10px 0 0 #fff;
} }
@ -387,3 +407,8 @@ h1
/* color: #000; /* color: #000;
background-color: #fff; */ background-color: #fff; */
} }
.faqEntry
{
margin-bottom: 30px;
}

View File

@ -0,0 +1,4 @@
<div class="faqEntry" ng-repeat="entry in entries track by $index" id="{{$index+1}}">
<h2 class="title">{{$index+1}}. {{entry.title}}</h2>
<div class="content" ng-bind-html="entry.content | markdown"></div>
</div>

7
static/views/faq.html Normal file
View File

@ -0,0 +1,7 @@
<h1>FAQ</h1>
<ol>
<li ng-repeat="entry in entries track by $index">
<a href="" ng-click="scrollTo($index+1)">{{entry.title}}</a>
</li>
</ol>
<ng-include src="'templates/faqentry.html'"></ng-include>

View File

View File

@ -1,6 +0,0 @@
<div class="navButtons" ng-controller="navButtons">
<a ng-click="navClick('/')" ng-class="isCurrent('/')">Blog</a>
<a ng-click="navClick('/about')" ng-class="isCurrent('/about')">About</a>
<a ng-click="navClick('/faq')" ng-class="isCurrent('/faq')">FAQ</a>
<a ng-click="navClick('/create')" ng-class="isCurrent('/create')">Create</a>
</div>