docs: function nav(path, query, hash, forceReload) {
  
      var reload = forceReload || !path;
  
      path =  path  || location.pathname;
      query = query || (reload && location.search) || '';
      hash =  hash  || (reload && location.hash)   || '';
  
      var newpage = generator.findPage(path);
      if (!newpage) return generator.emit('notify', 'Oops, jqueryview cannot find new page object ' + path);
  
      var oldpath = u.unPrefix(location.pathname, opts.staticRoot);
      var oldpage = generator.findPage(oldpath);
      if (!oldpage) return generator.emit('notify', 'Oops, jqueryview cannot find current page object ' + oldpath);
  
      if (!reload && newpage === oldpage) return; // hash navigation doesn't require repaint
  
      // simulate server-side request
      generator.req = { query: query ? require('querystring').parse(query.slice(1)) : {} };
  
      if ($layout.length && (reload || layoutChanged(oldpage, newpage))) {
        updateLayout();
        return;
      }
  
      // else just update page
      updatePage();
  
      ///// helper functions /////
  
      function updateLayout() {
        var layout = generator.layoutTemplate(newpage);
        $layout.html(generator.renderLayout(newpage));
        $layout.attr('data-render-layout', layout);
        log('jqueryview updateLayout', path, query, hash);
        generator.emit('update-view', path, query, hash, window, $layout);
      }
  
      function updatePage() {
        var $page = $('[data-render-page]');
        if (!$page.length) return generator.emit('notify', 'Oops, jqueryview cannot update page ' + path);
  
        $page.html(generator.renderPage(newpage));
        $page.attr('data-render-page', newpage._href);
        log('jqueryview updatePage:', path, query, hash)
        generator.emit('update-view', path, query, hash, window, $page);
      }
  
      // return true if newpage layout is different from current layout
      function layoutChanged(oldpage, newpage) {
        if (oldpage && oldpage.fixlayout) return true;
        if (lang(oldpage) !== lang(newpage)) return true;
        var currentlayout = $layout.attr('data-render-layout') || 'main-layout';
        var newlayout = generator.layoutTemplate(newpage);
        return (newlayout !== currentlayout);
      }
  
    }