Collin J. Doering
de27657196
The navigation was working correctly before the javascript refactor, and seemed to be working correctly after as well, except when clicking on links to tag pages or posts from within #page-content. When an internal link to a post or tag page is clicked the navigation should set the "blog" menuitem to be active. Unfortunately, this was not the case before this commit but luckily this commit rectifies this. Signed-off-by: Collin J. Doering <collin.doering@rekahsoft.ca>
474 lines
17 KiB
JavaScript
474 lines
17 KiB
JavaScript
/**
|
|
* (C) Copyright Collin Doering 2013
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
/**
|
|
* File:. default.js
|
|
* Author: Collin J. Doering
|
|
* Date: Sep 6, 2013
|
|
* Description: Client-side logic for blog-rekahsoft-ca
|
|
*/
|
|
|
|
//------------------------
|
|
/*jslint browser: true*/
|
|
/*global jQuery, MathJax*/
|
|
//------------------------
|
|
|
|
// Global array for processing piwik analytics commands
|
|
var _paq = _paq || [];
|
|
|
|
(function ($, mj) {
|
|
"use strict";
|
|
|
|
var router = (function () {
|
|
var routes = [],
|
|
routeDefault = { acceptUrls: /.*/ },
|
|
initCallbacks = [],
|
|
onChangeCallbacks = [],
|
|
beforeSends = [],
|
|
onSuccesss = [],
|
|
onErrors = [],
|
|
|
|
spec = {
|
|
runRouter: function runRouter(url) {
|
|
var i;
|
|
for (i = 0; i < routes.length; i += 1) {
|
|
if (routes[i].acceptUrls.test(url)) {
|
|
load(url, routes[i]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// No route ran; run default route
|
|
if (i === routes.length) {
|
|
load(url, routeDefault);
|
|
}
|
|
},
|
|
onInit: function onInit(cb) {
|
|
if (typeof cb === "function") {
|
|
initCallbacks.push(cb);
|
|
}
|
|
},
|
|
onChange: function onChange(cb) {
|
|
if (typeof cb === 'function') {
|
|
onChangeCallbacks.push(cb);
|
|
}
|
|
},
|
|
beforeSend: function beforeSend(cb) {
|
|
if (typeof cb === "function") {
|
|
beforeSends.push(cb);
|
|
}
|
|
},
|
|
onSuccess: function onSuccess(cb) {
|
|
if (typeof cb === "function") {
|
|
onSuccesss.push(cb);
|
|
}
|
|
},
|
|
onError: function onError(cb) {
|
|
if (typeof cb === "function") {
|
|
onErrors.push(cb);
|
|
}
|
|
},
|
|
route: function route(r) {
|
|
if (typeof r === "object" && r.acceptUrls instanceof RegExp) {
|
|
routes.push(r);
|
|
} else {
|
|
throw "Error invalid route";
|
|
}
|
|
},
|
|
defaultRoute: function defaultRoute(r) {
|
|
if (typeof r === "object") {
|
|
r.acceptUrls = /.*/;
|
|
routeDefault = r;
|
|
}
|
|
},
|
|
jsUrls: function (sel) {
|
|
$(sel).each(function (i) {
|
|
var href = $(this).attr('href'),
|
|
external_url_regexp = /https?:\/\/.*/,
|
|
mailto_regexp = /mailto:.*/,
|
|
files_regexp = /files\/.*/,
|
|
images_regexp = /images\/.*/;
|
|
|
|
if (!(external_url_regexp.test(href) || mailto_regexp.test(href) || files_regexp.test(href) || images_regexp.test(href))) {
|
|
$(this).click(function (e) {
|
|
e.preventDefault();
|
|
spec.runRouter(href);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
function setUrl(url) {
|
|
if (url === "/index.html") {
|
|
history.pushState(null, "Home", "/");
|
|
} else {
|
|
// TODO: strip url into title
|
|
history.pushState(null, "Title", url);
|
|
}
|
|
runCallbacks(url);
|
|
}
|
|
|
|
function load(url, r) {
|
|
// Set url in navigation bar
|
|
setUrl(url);
|
|
|
|
// Load route using ajax
|
|
$.ajax({
|
|
url: url,
|
|
type: 'GET',
|
|
dataType: 'html',
|
|
beforeSend: function (xhr, settings) {
|
|
// Run router beforeSend handlers
|
|
beforeSends.forEach(function (cb) {
|
|
cb(url);
|
|
});
|
|
|
|
// Run current handlers onSuccess callback (if it exists)
|
|
if (r.hasOwnProperty('beforeSend') && typeof r.beforeSend === 'function') {
|
|
r.beforeSend(url);
|
|
}
|
|
},
|
|
success: function (dta) {
|
|
// Parse script tags out of html
|
|
dta = $($.parseHTML(dta)).filter('#page-content').html();
|
|
|
|
// Add anchor click handlers for internal links in new content
|
|
spec.jsUrls('#page-content a');
|
|
|
|
// Run router onSuccess handlers
|
|
onSuccesss.forEach(function (cb) {
|
|
cb(url, dta);
|
|
});
|
|
|
|
// Run current handles onSuccess callback (if it exists)
|
|
if (r.hasOwnProperty('onSuccess') && typeof r.onSuccess === 'function') {
|
|
r.onSuccess(url, dta);
|
|
}
|
|
},
|
|
error: function (xhr, status) {
|
|
// Run router onError handlers
|
|
onErrors.forEach(function (cb) {
|
|
cb(url, status);
|
|
});
|
|
|
|
// Run current handles onError callback (if it exists)
|
|
if (r.hasOwnProperty('onError') && typeof r.onError === 'function') {
|
|
r.onError(url, status);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function runCallbacks(url) {
|
|
// Run each of the onChangeCallbacks
|
|
onChangeCallbacks.forEach(function (cb) {
|
|
cb(url);
|
|
});
|
|
}
|
|
|
|
// Listen for load event and run initCallbacks
|
|
window.addEventListener("load", function () {
|
|
initCallbacks.forEach(function (cb) {
|
|
cb(location.pathname);
|
|
});
|
|
});
|
|
|
|
// Listen for popstate event and call router with the new url
|
|
window.addEventListener("popstate", function (e) {
|
|
router.runRouter(location.pathname);
|
|
});
|
|
|
|
return spec;
|
|
}()),
|
|
|
|
analytics = (function () {
|
|
var inited = false,
|
|
spec = {
|
|
trackPageView: trackPageView,
|
|
debugEnable: function () {
|
|
init();
|
|
}
|
|
};
|
|
|
|
function trackPageView (href) {
|
|
if (inited) {
|
|
_paq.push(["setDocumentTitle", document.domain + href]);
|
|
_paq.push(["trackPageView"]);
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
if (!inited) {
|
|
_paq.push(["setDoNotTrack", true]);
|
|
_paq.push(["enableLinkTracking"]);
|
|
_paq.push(["setTrackerUrl", "//analytics.rekahsoft.ca/piwik.php"]);
|
|
_paq.push(["setSiteId", 1]);
|
|
|
|
inited = true;
|
|
}
|
|
}
|
|
|
|
// Initialize piwik.js when site is initially loaded
|
|
router.onInit(function () {
|
|
if (document.domain != "localhost") {
|
|
init();
|
|
trackPageView('/');
|
|
}
|
|
});
|
|
|
|
// Track page views with piwik each time the url changes
|
|
router.onChange(function (url, dta) {
|
|
trackPageView(url);
|
|
});
|
|
|
|
return spec
|
|
}()),
|
|
|
|
site = (function () {
|
|
var status = (function () {
|
|
var messages = [],
|
|
validIndicators = ["error", "success", "info"],
|
|
spec = {
|
|
setMessage: function setMessage(indicator, msg) {
|
|
var message = {}, hasValidIndicator = false;
|
|
for (var i = 0; i < validIndicators.length; i++) {
|
|
if (indicator === validIndicators[i]) {
|
|
hasValidIndicator = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hasValidIndicator) {
|
|
throw "The indicator given '" + indicator + "' is not know";
|
|
} else {
|
|
message.indicator = indicator;
|
|
message.msg = msg;
|
|
messages.push(message);
|
|
// TODO: Set the current message and appropriate class on #status given the indicator
|
|
// parameter, which can be one of "error", "success", or "info".
|
|
}
|
|
},
|
|
hasMessage: function hasMessage() {
|
|
if (messages.length === 0) {
|
|
return false;
|
|
} else {
|
|
return true;
|
|
}
|
|
},
|
|
validIndicators: validIndicators,
|
|
clear: function clear() {
|
|
// clear the status messages
|
|
messages = [];
|
|
$('#status').html('');
|
|
},
|
|
hide: function hide(indication) {
|
|
$('#status').slideUp('normal', function () {
|
|
$('#status').removeClass('error').removeClass('success').children('p.message').remove();
|
|
});
|
|
}
|
|
};
|
|
|
|
router.onSuccess(function (url, dta) {
|
|
status.hide();
|
|
});
|
|
|
|
$(document).ready(function () {
|
|
$('#status a.close-button').click(function () {
|
|
$(this).parent().slideUp(function () {
|
|
$(this).removeClass('error').removeClass('success');
|
|
$(this).children('p.message').remove();
|
|
});
|
|
});
|
|
});
|
|
|
|
return spec;
|
|
}()),
|
|
|
|
nav = (function () {
|
|
var spec = {
|
|
setActive: function setActive(url) {
|
|
if (url === "/") {
|
|
url = "/index.html";
|
|
}
|
|
|
|
// Initially set the active menuitem in the nav
|
|
$('a.menuitem[href="' + url + '"]').closest('ul').find('li.active').removeClass('active');
|
|
$('a.menuitem[href="' + url + '"]').closest('li').addClass('active');
|
|
},
|
|
toggleLoading: function toggleLoading() {
|
|
$('#nav').toggleClass('loading');
|
|
},
|
|
loadingOff: function loadingOff() {
|
|
$('#nav').removeClass('loading');
|
|
},
|
|
loadingOn: function loadingOn() {
|
|
$('#nav').addClass('loading');
|
|
}
|
|
};
|
|
|
|
router.onChange(function (url, dta) {
|
|
nav.setActive(url);
|
|
});
|
|
|
|
router.beforeSend(function (url) {
|
|
nav.toggleLoading();
|
|
});
|
|
|
|
$(document).ready(function () {
|
|
$('#nav-menu a.menuitem').click(function () {
|
|
$(this).closest('ul').find('li.active').removeClass('active');
|
|
$(this).closest('li').addClass('active');
|
|
});
|
|
});
|
|
|
|
return spec;
|
|
}()),
|
|
|
|
spec = {
|
|
init: init,
|
|
nav: nav,
|
|
status: status
|
|
};
|
|
|
|
function appCacheUpdateReady () {
|
|
window.applicationCache.swapCache();
|
|
// TODO: find what resource is loaded currently and reload it if it has changed
|
|
}
|
|
|
|
function init() {
|
|
window.addEventListener("updateready", appCacheUpdateReady);
|
|
if (window.applicationCache.status === window.applicationCache.UPDATEREADY) {
|
|
appCacheUpdateReady();
|
|
}
|
|
|
|
// TODO: deal with jsUrls function which is moved to the router
|
|
$(document).ready(function () {
|
|
// Add anchor click handlers for internal links
|
|
router.jsUrls('#page-content a, #nav-menu a.menuitem');
|
|
});
|
|
}
|
|
|
|
return spec;
|
|
}());
|
|
|
|
// Initialize routes
|
|
router.route({ // Post pages handler
|
|
acceptUrls: /posts\/.*\.html/,
|
|
onSuccess: function (url, dta) {
|
|
site.nav.setActive('/blog1.html');
|
|
}
|
|
});
|
|
|
|
router.route({ // Tag pages handler
|
|
acceptUrls: /tags\/.*(\d*)\.html/,
|
|
onSuccess: function (url, dta) {
|
|
site.nav.setActive('/blog1.html');
|
|
}
|
|
});
|
|
|
|
router.route({ // Blog pages handler
|
|
acceptUrls: /blog\d*\.html/
|
|
});
|
|
|
|
router.defaultRoute({ // Default page handler
|
|
beforeSend: undefined,
|
|
onSuccess: undefined,
|
|
onError: undefined
|
|
});
|
|
|
|
router.beforeSend(function (url) {
|
|
// Add .loading to #page-content and #nav to facilitate a loading animation
|
|
$('#page-content').addClass('loading');
|
|
});
|
|
|
|
router.onSuccess(function (url, dta) {
|
|
// Stop animations in the nav and page-content and scroll to the top of the page in a set amount of time
|
|
setTimeout(function () {
|
|
// Replace old page-content with new page-content
|
|
$('#page-content').html(dta);
|
|
|
|
// Add anchor click handlers for internal links in new content loaded into #page-content
|
|
router.jsUrls('#page-content a');
|
|
|
|
// Stop page loading
|
|
$('#page-content, #nav').removeClass('loading');
|
|
|
|
// Reload any new maths using MathJax
|
|
$('#page-content .math').each(function (math_elem) {
|
|
mj.Hub.Queue(["Typeset", mj.Hub, math_elem[0]]);
|
|
});
|
|
|
|
// Add fullscreen functionality to inline-images and figures
|
|
$('article.post p > img').click(function () {
|
|
$(this).get(0).toggleFullScreen();
|
|
});
|
|
$('figure').click(function () {
|
|
$(this).children('img').get(0).toggleFullScreen();
|
|
});
|
|
|
|
// Scroll to top of the page
|
|
if ($('body').scrollTop() > $('#nav').offset().top - 15) {
|
|
$('html, body').animate({
|
|
scrollTop: $('#nav').offset().top - 15
|
|
}, 'fast');
|
|
}
|
|
}, 250);
|
|
});
|
|
|
|
router.onError(function (url, status) {
|
|
/* Remove .loading from #page-content and #nav to stop the loading
|
|
* animation. Finally, display an error message in #status.
|
|
*/
|
|
$('#page-content, #nav').removeClass('loading');
|
|
|
|
// TODO: instead of immediately displaying error, check if the content is stored in local storage
|
|
$('#status').prepend('<p class="message">Error retrieving page ' + url + '</p>');
|
|
$('#status').addClass('error').slideDown();
|
|
});
|
|
|
|
site.init();
|
|
})(jQuery, MathJax);
|
|
|
|
// Modified from: https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Using_full_screen_mode
|
|
function toggleFullScreen() {
|
|
if (!document.fullscreenElement && // alternative standard method
|
|
!document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement ) { // current working methods
|
|
if (document.documentElement.requestFullscreen) {
|
|
this.requestFullscreen();
|
|
} else if (document.documentElement.msRequestFullscreen) {
|
|
this.msRequestFullscreen();
|
|
} else if (document.documentElement.mozRequestFullScreen) {
|
|
this.mozRequestFullScreen();
|
|
} else if (document.documentElement.webkitRequestFullscreen) {
|
|
this.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
|
|
}
|
|
} else {
|
|
if (document.exitFullscreen) {
|
|
document.exitFullscreen();
|
|
} else if (document.msExitFullscreen) {
|
|
document.msExitFullscreen();
|
|
} else if (document.mozCancelFullScreen) {
|
|
document.mozCancelFullScreen();
|
|
} else if (document.webkitExitFullscreen) {
|
|
document.webkitExitFullscreen();
|
|
}
|
|
}
|
|
}
|
|
|
|
Element.prototype.toggleFullScreen = toggleFullScreen;
|