res/script.js

This is the script file that gets copied into the output. It mainly manages the display of the folder tree. The idea of this script file is to be minimal and standalone. So that means no jQuery.


        

Use localStorage to store data about the tree's state: whether or not the tree is visible and which directories are expanded. Unless the state

var sidebarVisible = (window.localStorage && window.localStorage.docker_showSidebar) ?
                        window.localStorage.docker_showSidebar == 'yes' :
                        defaultSidebar;

makeTree

Consructs the folder tree view

Params
treeData object Folder structure as in queueFile
root string Path from current file to root (ie '../../' etc.)
filename string The current file name
function makeTree(treeData, root, filename) {
  var treeNode = document.getElementById('tree');
  var treeHandle = document.getElementById('sidebar-toggle');
  treeHandle.addEventListener('click', toggleTree, false);

Build the html and add it to the container.

  treeNode.innerHTML = nodeHtml('', treeData, '', root);

Root folder (whole tree) should always be open

  treeNode.childNodes[0].className += ' open';

Attach click event handler

  treeNode.addEventListener('click', nodeClicked, false);

  if (sidebarVisible) document.body.className += ' sidebar';

Restore scroll position from localStorage if set. And attach scroll handler

  if (window.localStorage && window.localStorage.docker_treeScroll) treeNode.scrollTop = window.localStorage.docker_treeScroll;
  treeNode.onscroll = treeScrolled;

Only set a class to allow CSS transitions after the tree state has been painted

  setTimeout(function() { document.body.className += ' slidey'; }, 100);
}

treeScrolled

Called when the tree is scrolled. Stores the scroll position in localStorage so it can be restored on the next pageview.

function treeScrolled() {
  var tree = document.getElementById('tree');
  if (window.localStorage) window.localStorage.docker_treeScroll = tree.scrollTop;
}

nodeClicked

Called when a directory is clicked. Toggles open state of the directory

Params
e Event The click event
function nodeClicked(e) {

Find the target

  var t = e.target;

If the click target is actually a file (rather than a directory), ignore it

  if (t.tagName.toLowerCase() !== 'div' || t.className === 'children') return;

Recurse upwards until we find the actual directory node

  while (t && t.className.substring(0, 3) != 'dir') t = t.parentNode;

If we're at the root node, then do nothing (we don't allow collapsing of the whole tree)

  if (!t || t.parentNode.id == 'tree') return;

Find the path and toggle the state, saving the state in the localStorage variable

  var path = t.getAttribute('rel');
  if (t.className.indexOf('open') !== -1) {
    t.className = t.className.replace(/\s*open/g, '');
    if (window.localStorage) window.localStorage.removeItem('docker_openPath:' + path);
  } else {
    t.className += ' open';
    if (window.localStorage) window.localStorage['docker_openPath:' + path] = 'yes';
  }
}


nodeHtml

Constructs the markup for a directory in the tree

Params
nodename string The node name.
node object Node object of same format as whole tree.
path string The path form the base to this node
root string Relative path from current page to root
function nodeHtml(nodename, node, path, root) {

Firstly, figure out whether or not the directory is expanded from localStorage

  var isOpen = window.localStorage && window.localStorage['docker_openPath:' + path] == 'yes';
  var out = '<div class="dir' + (isOpen ? ' open' : '') + '" rel="' + path + '">';
  out += '<div class="nodename">' + nodename + '</div>';
  out += '<div class="children">';

Loop through all child directories first

  if (node.dirs) {
    var dirs = [];
    for (var i in node.dirs) {
      if (node.dirs.hasOwnProperty(i)) dirs.push({ name: i, html: nodeHtml(i, node.dirs[i], path + i + '/', root) });
    }

Have to store them in an array first and then sort them alphabetically here

    dirs.sort(function(a, b) { return (a.name > b.name) ? 1 : (a.name == b.name) ? 0 : -1; });

    for (var k = 0; k < dirs.length; k += 1) out += dirs[k].html;
  }

Now loop through all the child files alphabetically

  if (node.files) {
    node.files.sort();
    for (var j = 0; j < node.files.length; j += 1) {
      out += '<a class="file" href="' + root + path + node.files[j] + '.html">' + node.files[j] + '</a>';
    }
  }

Close things off

  out += '</div></div>';

  return out;
}

toggleTree

Toggles the visibility of the folder tree

function toggleTree() {

Do the actual toggling by modifying the class on the body element. That way we can get some nice CSS transitions going.

  if (sidebarVisible) {
    document.body.className = document.body.className.replace(/\s*sidebar/g, '');
    sidebarVisible = false;
  } else {
    document.body.className += ' sidebar';
    sidebarVisible = true;
  }
  if (window.localStorage) {
    if (sidebarVisible) {
      window.localStorage.docker_showSidebar = 'yes';
    } else {
      window.localStorage.docker_showSidebar = 'no';
    }
  }
}

wireUpTabs

Wires up events on the sidebar tabe

function wireUpTabs() {
  var tabEl = document.getElementById('sidebar_switch');
  var children = tabEl.childNodes;

Each tab has a class corresponding of the id of its tab pane

  for (var i = 0, l = children.length; i < l; i += 1) {

Ignore text nodes

    if (children[i].nodeType !== 1) continue;
    children[i].addEventListener('click', function(c) {
      return function() { switchTab(c); };
    }(children[i].className));
  }
}

switchTab

Switches tabs in the sidebar

Params
tab string The ID of the tab to switch to
function switchTab(tab) {
  var tabEl = document.getElementById('sidebar_switch');
  var children = tabEl.childNodes;

Easiest way to go through tabs without any kind of selector is just to look at the tab bar

  for (var i = 0, l = children.length; i < l; i += 1) {

Ignore text nodes

    if (children[i].nodeType !== 1) continue;

Figure out what tab pane this tab button corresponts to

    var t = children[i].className.replace(/\s.*$/, '');
    if (t === tab) {

Show the tab pane, select the tab button

      document.getElementById(t).style.display = 'block';
      if (children[i].className.indexOf('selected') === -1) children[i].className += ' selected';
    } else {

Hide the tab pane, deselect the tab button

      document.getElementById(t).style.display = 'none';
      children[i].className = children[i].className.replace(/\sselected/, '');
    }
  }

Store the last open tab in localStorage

  if (window.localStorage) window.localStorage.docker_sidebarTab = tab;
}

window.onload

When the document is ready, make the sidebar and all that jazz

(function(init) {
  if (window.addEventListener) {
    window.addEventListener('DOMContentLoaded', init);
  } else { // IE8 and below
    window.onload = init;
  }
}(function() {
  makeTree(tree, relativeDir, thisFile);
  wireUpTabs();

Switch to the last viewed sidebar tab if stored, otherwise default to folder tree

  if (window.localStorage && window.localStorage.docker_sidebarTab) {
    switchTab(window.localStorage.docker_sidebarTab);
  } else {
    switchTab('tree');
  }
}));