var $ = require('jquery'); var url = require('url'); var events = require('../core').events; var state = require('../core').state; var loading = require('./loading'); var usePushState = (typeof history.pushState !== 'undefined'); function handleNavigation(relativeUrl, push) { var uri = url.resolve(window.location.pathname, relativeUrl); if (!usePushState) { // Refresh the page to the new URL if pushState not supported location.href = relativeUrl; return; } return loading.show($.get(uri) .done(function (html) { // Push url to history if (push) history.pushState({ path: uri }, null, uri); // Replace html content html = html.replace( /<(\/?)(html|head|body)([^>]*)>/ig, function(a,b,c,d){ return '<' + b + 'div' + ( b ? '' : ' data-element="' + c + '"' ) + d + '>'; }); var $page = $(html); var $pageHead = $page.find('[data-element=head]'); var $pageBody = $page.find('.book'); // Merge heads // !! Warning !!: we only update necessary portions to avoid strange behavior (page flickering etc ...) // Update title document.title = $pageHead.find('title').text(); // Reference to $('head'); var $head = $('head'); // Update next & prev tags // Remove old $head.find('link[rel=prev]').remove(); $head.find('link[rel=next]').remove(); // Add new next * prev tags $head.append($pageHead.find('link[rel=prev]')); $head.append($pageHead.find('link[rel=next]')); // Merge body var bodyClass = $('.book').attr('class'); var scrollPosition = $('.book-summary .summary').scrollTop(); $pageBody.toggleClass('with-summary', $('.book').hasClass('with-summary')); $('.book').replaceWith($pageBody); $('.book').attr('class', bodyClass); $('.book-summary .summary').scrollTop(scrollPosition); // Update state state.update($('html')); preparePage(); }) .fail(function (e) { location.href = relativeUrl; })); } function updateNavigationPosition() { var bodyInnerWidth, pageWrapperWidth; bodyInnerWidth = parseInt($('.body-inner').css('width'), 10); pageWrapperWidth = parseInt($('.page-wrapper').css('width'), 10); $('.navigation-next').css('margin-right', (bodyInnerWidth - pageWrapperWidth) + 'px'); } function notifyPageChange() { events.trigger('page.change'); } function preparePage(notify) { var $bookBody = $('.book-body'); var $bookInner = $bookBody.find('.body-inner'); var $pageWrapper = $bookInner.find('.page-wrapper'); // Update navigation position updateNavigationPosition(); // Focus on content $pageWrapper.focus(); // Reset scroll $bookInner.scrollTop(0); $bookBody.scrollTop(0); // Notify if (notify !== false) notifyPageChange(); } function isLeftClickEvent(e) { return e.button === 0; } function isModifiedEvent(e) { return !!(e.metaKey || e.altKey || e.ctrlKey || e.shiftKey); } function handlePagination(e) { if (isModifiedEvent(e) || !isLeftClickEvent(e)) { return; } e.stopPropagation(); e.preventDefault(); var url = $(this).attr('href'); if (url) handleNavigation(url, true); } function goNext() { var url = $('.navigation-next').attr('href'); if (url) handleNavigation(url, true); } function goPrev() { var url = $('.navigation-prev').attr('href'); if (url) handleNavigation(url, true); } function init() { // Prevent cache so that using the back button works // See: http://stackoverflow.com/a/15805399/983070 $.ajaxSetup({ cache: false }); // Recreate first page when the page loads. history.replaceState({ path: window.location.href }, ''); // Back Button Hijacking :( window.onpopstate = function (event) { if (event.state === null) { return; } return handleNavigation(event.state.path, false); }; $(document).on('click', '.navigation-prev', handlePagination); $(document).on('click', '.navigation-next', handlePagination); $(document).on('click', '.summary [data-path] a', handlePagination); $(window).resize(updateNavigationPosition); // Prepare current page preparePage(false); } module.exports = { init: init, goNext: goNext, goPrev: goPrev, notify: notifyPageChange };