Implemented loading custom CSS and templates, implemented google analytics

This commit is contained in:
Mikko Ahlroth 2015-01-18 23:54:52 +02:00
parent 9fb8eada9f
commit ae5d23bd2c
11 changed files with 159 additions and 13 deletions

View file

@ -1,6 +1,6 @@
{ {
"name": "laine", "name": "laine",
"version": "0.0.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"components-bootstrap": "~3.1.1", "components-bootstrap": "~3.1.1",

View file

@ -38,7 +38,8 @@ var requireJsRuntimeConfig = vm.runInNewContext(fs.readFileSync('src/app/require
'components/year-page/year-page', 'components/year-page/year-page',
'components/month-page/month-page', 'components/month-page/month-page',
'components/post-page/post-page', 'components/post-page/post-page',
'components/config-service/config-service' 'components/config-service/config-service',
'components/template-service/template-service'
], ],
insertRequire: ['app/startup'], insertRequire: ['app/startup'],
bundles: { bundles: {

View file

@ -1,4 +1,12 @@
define(["knockout", "crossroads", "hasher", "./routes"], function(ko, crossroads, hasher, routes) { define(
[
"knockout",
"crossroads",
"hasher",
"./routes",
"../components/config-service/config-service",
"../components/ga-service/ga-service"
], function(ko, crossroads, hasher, routes, configService, gaService) {
// This module configures crossroads.js, a routing library. If you prefer, you // This module configures crossroads.js, a routing library. If you prefer, you
// can use any other routing library (or none at all) as Knockout is designed to // can use any other routing library (or none at all) as Knockout is designed to
@ -14,6 +22,8 @@ define(["knockout", "crossroads", "hasher", "./routes"], function(ko, crossroads
function Router(config) { function Router(config) {
var currentRoute = this.currentRoute = ko.observable({}); var currentRoute = this.currentRoute = ko.observable({});
this.crossroads = crossroads;
this.hasher = hasher;
ko.utils.arrayForEach(config.routes, function(route) { ko.utils.arrayForEach(config.routes, function(route) {
var addedRoute = crossroads.addRoute(route.url, function(requestParams) { var addedRoute = crossroads.addRoute(route.url, function(requestParams) {
@ -29,7 +39,15 @@ define(["knockout", "crossroads", "hasher", "./routes"], function(ko, crossroads
} }
function activateCrossroads() { function activateCrossroads() {
function parseHash(newHash, oldHash) { crossroads.parse(newHash); } function parseHash(newHash, oldHash) {
crossroads.parse(newHash);
// Send google analytics page event if it's enabled
if (configService.useGa) {
gaService.sendPageEvent();
}
}
crossroads.normalizeFn = crossroads.NORM_AS_OBJECT; crossroads.normalizeFn = crossroads.NORM_AS_OBJECT;
hasher.prependHash = '!'; hasher.prependHash = '!';
hasher.initialized.add(parseHash); hasher.initialized.add(parseHash);

View file

@ -61,7 +61,3 @@ define([], function() {
} }
]; ];
}); });

View file

@ -32,6 +32,10 @@ define(['jquery', 'knockout', './router', 'marked', 'bootstrap', 'knockout-proje
ko.components.register('config-service', { require: 'components/config-service/config-service' }); ko.components.register('config-service', { require: 'components/config-service/config-service' });
ko.components.register('template-service', { require: 'components/template-service/template-service' });
ko.components.register('ga-service', { require: 'components/ga-service/ga-service' });
// [Scaffolded component registrations will be inserted here. To retain this feature, don't remove this comment.] // [Scaffolded component registrations will be inserted here. To retain this feature, don't remove this comment.]
// Set Markdown parser options // Set Markdown parser options

View file

@ -1,5 +1,5 @@
define(['knockout', '../../app/routes', '../../app/router', 'hasher'], define(['knockout'],
function(ko, routes, router, hasher) { function(ko) {
function ConfigService() { function ConfigService() {
var self = this; var self = this;
@ -9,6 +9,18 @@ define(['knockout', '../../app/routes', '../../app/router', 'hasher'],
document.title = self.blogName; document.title = self.blogName;
self.baseTitle = self.blogName; self.baseTitle = self.blogName;
// Load custom CSS if specified
if (self.customCss) {
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = 'laine-custom-css';
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = self.customCss;
link.media = 'all';
head.appendChild(link);
}
} }
// This runs when the component is torn down. Put here any logic necessary to clean up, // This runs when the component is torn down. Put here any logic necessary to clean up,

View file

@ -0,0 +1,34 @@
define(
[
'knockout',
'../config-service/config-service'
], function(ko, configService) {
function GaService() {
var self = this;
// The GA object that can be used in other components
self.ga = null;
if (configService.useGa) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', configService.webPropertyID, 'auto');
self.ga = ga;
}
self.sendPageEvent = function() {
self.ga('send', 'pageview');
};
}
// This runs when the component is torn down. Put here any logic necessary to clean up,
// for example cancelling setTimeouts or disposing Knockout subscriptions/computeds.
GaService.prototype.dispose = function() { };
return new GaService();
});

View file

@ -1,4 +1,10 @@
define(['knockout', '../db/db'], function(ko, DB) { define(
[
'knockout',
'../db/db',
'../config-service/config-service',
'../template-service/template-service'
], function(ko, DB, configService, templateService) {
function GenericRoute() { function GenericRoute() {
var self = this; var self = this;
@ -14,6 +20,16 @@ define(['knockout', '../db/db'], function(ko, DB) {
page(parseInt(route.pageNumber, 10)); page(parseInt(route.pageNumber, 10));
} }
} }
self.resolveTemplate = function(originalTemplate, templateName) {
if (templateName in configService.customTemplates
&& configService.customTemplates[templateName]) {
return templateService.loadTemplate(templateName, configService.customTemplates[templateName]);
}
else {
return originalTemplate;
}
}
} }
// This runs when the component is torn down. Put here any logic necessary to clean up, // This runs when the component is torn down. Put here any logic necessary to clean up,

View file

@ -1,4 +1,10 @@
define(['knockout', 'text!./home.html', '../generic-route/generic-route', '../db/db'], define(
[
'knockout',
'text!./home.html',
'../generic-route/generic-route',
'../db/db'
],
function(ko, templateMarkup, GR, DB) { function(ko, templateMarkup, GR, DB) {
function HomePage(route) { function HomePage(route) {
@ -15,6 +21,8 @@ define(['knockout', 'text!./home.html', '../generic-route/generic-route', '../db
// for example cancelling setTimeouts or disposing Knockout subscriptions/computeds. // for example cancelling setTimeouts or disposing Knockout subscriptions/computeds.
HomePage.prototype.dispose = function() {}; HomePage.prototype.dispose = function() {};
templateMarkup = GR.resolveTemplate(templateMarkup, 'home');
return { viewModel: HomePage, template: templateMarkup }; return { viewModel: HomePage, template: templateMarkup };
}); });

View file

@ -0,0 +1,32 @@
define(['knockout'], function(ko) {
function TemplateService() {
var self = this;
self.templateStore = {};
self.loadTemplate = function(templateName, templateUrl) {
if (templateName in self.templateStore) {
return self.templateStore[templateName];
}
else {
var html = $.ajax(templateUrl, {
// We need to set async false to receive the template before rendering
// starts
async: false,
dataType: 'html'
}).responseText;
self.templateStore[templateName] = html;
return html;
}
};
}
// This runs when the component is torn down. Put here any logic necessary to clean up,
// for example cancelling setTimeouts or disposing Knockout subscriptions/computeds.
TemplateService.prototype.dispose = function() { };
return new TemplateService();
});

View file

@ -18,5 +18,30 @@ LAINE_CONFIG = {
pageCommenting: false, pageCommenting: false,
// Posts shown per page // Posts shown per page
postsPerPage: 5 postsPerPage: 5,
// GOOGLE ANALYTICS
useGa: false, // Set to true to use Google Analytics
webPropertyID: '', // Your Google Analytics UA-XXXX-Y code
// CUSTOM TEMPLATE AND CSS OVERRIDES
// To load a custom CSS file, insert the file name below
customCss: '',
// To replace builtin templates with custom teplates, insert the file names
// below in the correct places. The paths should be relative to the
// index.html path.
customTemplates: {
navbar: '', // Navigation bar
home: '', // Home page
post: '', // Single post/page
pagination: '', // A list of posts with included pagination
year: '', // Year archives
month: '', // Month archives
tag: '' // Tag archives
}
}; };