Skip to content

Fix viewing of pages using file:/// urls. #8

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion mkdocs_windmill/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
{%- block scripts %}
<script>
var base_url = '{{ base_url }}';
var home_url = '{{ nav.homepage.url }}';
var is_top_frame = {% if is_top %}(window === window.parent){% else %}false{% endif %};
{%- if page %}
{# Include the first two levels of TOC data as a JS object, to be rendered in top frame #}
Expand Down Expand Up @@ -132,7 +133,7 @@
<br>
{%- if config.extra.article_nav_bottom != False %}
{% include "article-nav.html" %}
<br>
<div class="wm-article-bottom"></div>
{%- endif %}
</div>

Expand Down
4 changes: 4 additions & 0 deletions mkdocs_windmill/css/base.css
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,10 @@ body {
right: 15px;
}

.wm-article-bottom {
height: 60px;
}

.wm-page-content img {
max-width: 100%;
display: inline-block;
Expand Down
121 changes: 69 additions & 52 deletions mkdocs_windmill/js/base.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
/* global window, document, $, hljs, elasticlunr, base_url, is_top_frame */
/* global window, document, $, hljs, elasticlunr, base_url, home_url, is_top_frame */
/* exported getParam, onIframeLoad */
"use strict";

Expand All @@ -13,7 +13,8 @@

var mainWindow = is_top_frame ? window : (window.parent !== window ? window.parent : null);
var iframeWindow = null;
var rootUrl = qualifyUrl(base_url);
var rootPath = qualifyUrl(home_url);
var rootDir = qualifyUrl(base_url);
var searchIndex = null;
var showPageToc = true;
var MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
Expand All @@ -26,7 +27,8 @@ var Keys = {
};

function startsWith(str, prefix) { return str.lastIndexOf(prefix, 0) === 0; }
function endsWith(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; }
//function endsWith(str, suffix) { return str.indexOf(suffix, str.length - suffix.length) !== -1; }
function stripPrefix(str, prefix) { return startsWith(str, prefix) ? str.slice(prefix.length) : null; }

/**
* Returns whether to use small-screen mode. Note that the same size is used in css @media block.
Expand All @@ -44,20 +46,15 @@ function qualifyUrl(url) {
return a.href;
}

/**
* Turns an absolute path to relative, stripping out rootUrl + separator.
*/
function getRelPath(separator, absUrl) {
var prefix = rootUrl + (endsWith(rootUrl, separator) ? '' : separator);
return startsWith(absUrl, prefix) ? absUrl.slice(prefix.length) : null;
// Functions to convert between full URLs used to load actual page, and hash-using URLs used for
// navigation of the main window.
function getFullUrl(hashUrl) {
let rel = stripPrefix(hashUrl, rootPath + '#');
return rel === null ? rootPath : rootDir + rel;
}

/**
* Turns a relative path to absolute, adding a prefix of rootUrl + separator.
*/
function getAbsUrl(separator, relPath) {
var sep = endsWith(rootUrl, separator) ? '' : separator;
return relPath === null ? null : rootUrl + sep + relPath;
function getHashUrl(fullUrl) {
let rel = stripPrefix(fullUrl, rootDir);
return rel === null ? null : rootPath + '#' + rel;
}

/**
Expand All @@ -69,19 +66,18 @@ function updateIframe(enableForwardNav) {
// Grey out the "forward" button if we don't expect 'forward' to work.
$('#hist-fwd').toggleClass('greybtn', !enableForwardNav);

var targetRelPath = getRelPath('#', mainWindow.location.href) || '';
var targetIframeUrl = getAbsUrl('/', targetRelPath);
var targetIframeUrl = getFullUrl(mainWindow.location.href);
var loc = iframeWindow.location;
var currentIframeUrl = _safeGetLocationHref(loc);

console.log("updateIframe: %s -> %s (%s)", currentIframeUrl, targetIframeUrl,
currentIframeUrl === targetIframeUrl ? "same" : "replacing");

if (currentIframeUrl !== targetIframeUrl) {
document.body.scrollTop = 0;
loc.replace(targetIframeUrl);
onIframeBeforeLoad(targetIframeUrl);
}
document.body.scrollTop = 0;
}

/**
Expand Down Expand Up @@ -126,9 +122,9 @@ function updateTocButtonState() {
* Update the height of the iframe container. On small screens, we adjust it to fit the iframe
* contents, so that the page scrolls as a whole rather than inside the iframe.
*/
function updateContentHeight() {
function updateContentHeight(iframeBodyHeight) {
if (isSmallScreen()) {
$('.wm-content-pane').height(iframeWindow.document.body.offsetHeight + 20);
$('.wm-content-pane').height(iframeBodyHeight + 20);
$('.wm-article').attr('scrolling', 'no');
} else {
$('.wm-content-pane').height('');
Expand All @@ -150,10 +146,9 @@ function closeTempItems() {
* path, and points the iframe to the new URL.
*/
function visitUrl(url, event) {
var relPath = getRelPath('/', url);
if (relPath !== null) {
var newUrl = getHashUrl(url);
if (newUrl !== null) {
event.preventDefault();
var newUrl = getAbsUrl('#', relPath);
if (newUrl !== mainWindow.location.href) {
mainWindow.history.pushState(null, '', newUrl);
updateIframe(false);
Expand All @@ -170,9 +165,8 @@ function visitUrl(url, event) {
function adjustLink(linkEl) {
if (!linkEl.hasAttribute('data-wm-adjusted')) {
linkEl.setAttribute('data-wm-adjusted', 'done');
var relPath = getRelPath('/', linkEl.href);
if (relPath !== null) {
var newUrl = getAbsUrl('#', relPath);
var newUrl = getHashUrl(linkEl.href);
if (newUrl !== null) {
linkEl.href = newUrl;
}
}
Expand Down Expand Up @@ -216,6 +210,18 @@ function initMainWindow() {
// When the side-pane is a dropdown, hide it on click-away.
$(window).on('blur', closeTempItems);

$(window).on('message', function(jevent) {
var ev = jevent.originalEvent;
if (ev.source !== iframeWindow) {
console.log("message event from unexpected source", ev.source);
return;
}
if (ev.data.name === 'iframeLoad') {
onIframeLoad(ev.data);
}
if (ev.data.height) { updateContentHeight(ev.data.height); }
});

// When we click on an opener in the table of contents, open it.
$('.wm-toc-pane').on('click', '.wm-toc-opener', function(e) {
$(this).toggleClass('wm-toc-open');
Expand All @@ -229,22 +235,10 @@ function initMainWindow() {
$(this).next('.wm-page-toc').collapse(showPageToc ? 'show' : 'hide');
});

// Once the article loads in the side-pane, close the dropdown.
$('.wm-article').on('load', function() {
document.title = iframeWindow.document.title;
updateContentHeight();

// We want to update content height whenever the height of the iframe's content changes.
// Using MutationObserver seems to be the best way to do that.
var observer = new MutationObserver(updateContentHeight);
observer.observe(iframeWindow.document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});

iframeWindow.focus();
setTimeout(function() {
iframeWindow.focus();
}, 0);
});

// Initialize search functionality.
Expand All @@ -269,19 +263,19 @@ function onIframeBeforeLoad(url) {
}

function getTocLi(url) {
var relPath = getAbsUrl('#', getRelPath('/', cleanUrlPath(url)));
var selector = '.wm-article-link[href="' + relPath + '"]';
var topUrl = getHashUrl(cleanUrlPath(url));
var selector = '.wm-article-link[href="' + topUrl + '"]';
return $(selector).closest('.wm-toc-li');
}

function onIframeLoad() {
var url = iframeWindow.location.href;
function onIframeLoad(iframeData) {
var url = iframeData.url;
onIframeBeforeLoad(url);

if (iframeWindow.pageToc) {
var relPath = getAbsUrl('#', getRelPath('/', cleanUrlPath(url)));
renderPageToc(getTocLi(url), relPath, iframeWindow.pageToc);
if (iframeData.pageToc) {
var topUrl = getHashUrl(cleanUrlPath(url));
renderPageToc(getTocLi(url), topUrl, iframeData.pageToc);
}
if (iframeData.title) { document.title = iframeData.title; }
iframeWindow.focus();
}

Expand Down Expand Up @@ -325,7 +319,7 @@ function renderPageToc(parentElem, pageUrl, pageToc) {

if (!mainWindow) {
// This is a page that ought to be in an iframe. Redirect to load the top page instead.
var topUrl = getAbsUrl('#', getRelPath('/', window.location.href));
var topUrl = getHashUrl(window.location.href);
if (topUrl) {
window.location.href = topUrl;
}
Expand All @@ -351,13 +345,36 @@ if (is_top_frame) {
// Article contents.
iframeWindow = window;
if (mainWindow) {
mainWindow.onIframeLoad();
mainWindow.postMessage({
name: 'iframeLoad',
url: iframeWindow.location.href,
pageToc: iframeWindow.pageToc,
title: iframeWindow.document.title
}, '*');
}

var updateHeight = function() {
mainWindow.postMessage({
height: document.body.offsetHeight
}, '*');
};


// Other initialization of iframe contents.
hljs.initHighlightingOnLoad();
$(document).ready(function() {
$('table').addClass('table table-striped table-hover table-bordered table-condensed');
updateHeight();

// We want to update content height whenever the height of the iframe's content changes.
// Using MutationObserver seems to be the best way to do that.
var observer = new MutationObserver(updateHeight);
observer.observe(document.body, {
attributes: true,
childList: true,
characterData: true,
subtree: true
});
});
}

Expand Down