/**
* Use HTML5 history.pushState to make the browser's back and forward buttons work as expected.
*/
function CBrowserHistory() {
var m_inPopState = false;
var m_curBaseUrl = document.location.pathname;
var m_prevTitle = '__UNDEFINED__';
var m_loadPageFunc = null; //TODO: Not great that this is a member var...
var self = this;
//TODO: Test with doku?id urls.
function base(url, withId) {
if (withId) {
var id = url.replace(/.*id=([^&]+).*/, '$1');
if (id.match(/^[a-zA-Z0-9_\-:]+$/))
return url.replace(/\?.*/, '') + '?id='+id;
}
return url.replace(/\?.*/, '');
}
/**
* Get the expected title of the wiki page.
*
* @private
* @return {String} the title.
*/
function _getWikiTitle() {
var titleElt;
$('h1, h2, h3, h4, h5, h6', $('.content_initial')).each(function(idx, elt) {
if (elt.className.indexOf('sectionedit') >= 0) {
titleElt = elt;
return false; // Break out of each().
}
});
return titleElt ? $(titleElt).text() : '';
}
/**
* Initialize this class
*
* @param {Function} loadFunc - The function to call after a new page is loaded.
*/
this.init = function(loadFunc) {
m_prevTitle = _getWikiTitle() || '__UNDEFINED__';
m_loadPageFunc = loadFunc;
window.addEventListener('popstate', function(e) {
document.title = e.state.title;
m_inPopState = true;
self.switchBasePath(e.state.url);
//TODO: Set m_viewMode=null with a callback. Put current view mode in the state. Generalize with a getPageState() and pageStateCallback()
});
};
/**
* Get the id of the page, or null if switching to that page doesn't support fastshow.
*
* @param {String} newpage - The new page URL.
* @param {Boolean} force - Ignore fastshow rules.
* @return {Object} with two members: id (page id) and ns (namespace).
*/
this.getSwitchId = function(newpage, force) {
//TODO Bug: Doesn't work with httpd mode unless doku is in the base directory. Could fix by assuming same namespace.
var pageid = newpage.substr(1).replace(/.*doku.php(\?id=|\/)/, '').replace(/\//g, ':');
var ns = pageid.replace(/:[^:]+$/, '');
if (!force) {
if (JSINFO.fastwiki.fastshow_same_ns && ns != JSINFO.namespace)
return false;
var incl = JSINFO.fastwiki.fastshow_include, excl = JSINFO.fastwiki.fastshow_exclude;
// Include namespaces and pages
if (incl && !pageid.match('^(' + incl.split(/\s*,\s*/).join('|') + ')'))
return false;
// Exclude namespaces and pages
if (excl && pageid.match('^(' + excl.split(/\s*,\s*/).join('|') + ')'))
return false;
}
return {id:pageid, ns:ns};
};
/**
* Get a regex which matches the current page id in a url.
*
* @returns {RegExp}
*/
this.getSelfRefRegex = function() {
var path = document.location.pathname;
var idPath = JSINFO.id.replace(/:/g, '/');
// The non-httpd version can be tested unambiguously.
if (path.indexOf('doku.php') >= 0)
return new RegExp('doku\\.php\\?id='+JSINFO.id+'$|doku\\.php/'+idPath+'$');
// Absolute path with and without domain.
// TODO: While it won't apply to doku-generated links, for completeness we should also look for relative paths. This would require
// knowing the configured base path, and knowing about any tags.
return new RegExp('^'+path+'$|://'+document.location.host+path+'$');
};
/**
* @return {String} the current base url.
*/
this.getBaseUrl = function() {
return m_curBaseUrl;
};
/**
* Switch to a different page id (fastshow feature).
*
* @param {String} newpage - The URL of the new page.
*/
this.switchBasePath = function(newpage) {
//TODO Bug: Doesn't work with httpd mode unless doku is in the base directory. Could fix by assuming same namespace.
var pageinfo = this.getSwitchId(newpage);
if (!pageinfo)
return false;
// Update JSINFO
var oldid = JSINFO.id;
JSINFO.id = pageinfo.id;
JSINFO.namespace = pageinfo.ns;
// Replace 'id' fields.
$('form').each(function(idx, form) {
if ($(form).find('input[name="do"]').length > 0) {
var input = $('input[name="id"]', form);
if (input.val() == oldid)
input.val(pageinfo.id);
}
});
// TODO: Need to have newpage in non-switch case to get history for other actions.
m_curBaseUrl = base(newpage); //TODO: Always?
var prevpage = document.location.href;
m_loadPageFunc('show', null, {fastwiki_compareid:oldid}, true, function() {
setTimeout(function() {
if (!m_inPopState) {
// If we do things like save and subscribe, we end up back on 'show'.
//if (m_viewMode == 'show' && newpage == prevpage)
// window.history.back();
//else {
// When switching modes, just replace the url. When changing to a new page or in or out of show, push.
history.replaceState({url: prevpage, title: document.title}, "", prevpage);
// if (m_viewMode == 'show' || m_prevViewMode == 'show') { //TODO
history.pushState({url: newpage, title: document.title}, "", newpage);
$(window).trigger('fastwiki:afterIdChange', [prevpage, m_curBaseUrl]);
// }
//}
}
// Set this here instead of in the popstate listener, so that callbacks and setTimeout will work.
m_inPopState = false;
self.refreshPageTitle(true);
}, 1); // setTimeout so it happens after all other page manipulations. This won't be needed if I do history in _action().
});
return true;
};
/**
* Refresh the page title based on the top heading.
*
* @param {Boolean} fromIdSwitch - Is this refresh triggered by an id switch?
*/
this.refreshPageTitle = function(fromIdSwitch) {
var title = _getWikiTitle();
document.title = title;
//TODO: Close, but I need to get the prevTitle from h1,h2,etc like above. Or better, return it from the server.
if (!fromIdSwitch) {
$('a').each(function(idx, elt) {
var $this = $(this);
var href = $this.attr('href');
if (href && href.match(self.getSelfRefRegex()) && $this.text() == m_prevTitle)
$this.text(document.title);
});
}
m_prevTitle = title;
};
}