<!DOCTYPE html>
<html>

  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <link rel="stylesheet" href="style.css" />
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.2.0/require.js"></script>
    <script src="script.js"></script>
  </head>

  <body spa-domain="menu">
    <header><nav><ul><li spa-repeat="item in menu"></ul></nav></header>
    <section spa-insert="content"></section>
  </body>

</html>
require.config({
    paths : { 
    jquery : '//cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery',
  }
});

require(['app'], function(app) { 
  app.init(); // <==== this starts your website build
});
a:link {
  color: cadetblue;
  text-decoration: none;
}
a:hover {
  color: cornflowerblue;
  text-decoration: underline;
}

ul {
  list-style: none;
}

header > nav.navbar  {  
  background-color: gainsboro;
}
header > nav .nav-header > a.navbar-brand:not(:hover),
header > nav .nav-header > a.navbar-brand:hover {
  font-size: 2rem;
  font-weight: 900;
  letter-spacing: 2px;
  color:white;
}
header > nav ul > li {
  cursor: pointer;
  height: 2rem;
  font-size: 1.5rem;

}
header > nav ul > li a:hover {
  text-decoration: none;
}

.route {
  text-align:center;
  display: block;
  margin: 0 auto;  
}

.rt-i {
  margin-left: 1.3rem;
  zoom: 400%;
  display: inline-block;
}
.rt { 
  text-align: center;
  display: block;
}
.rt-info {
  width: 8rem;
  margin-left: 7rem;
  opacity: 0.3;
}
define('app', ['require', 'jquery', 'menu', 'content', 'otherstuff', 'route'], 
  function(require, $, menu, content, otherstuff, route) {
    return {
      init : function() {  
        console.info('\'app\' loading')
        $('[spa-domain]').hide()
        
        otherstuff.load_css('https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css')
        
        route.init();
        menu.init();
        content.init();
        otherstuff.init();
        
        $('[spa-domain]').show()
      }
    }
  }
);
define('menu', ['jquery', 'content'], function ($, content) {
  return {
    init: function () {
      console.info('[controller: menu] loaded')
      $('[spa-insert]').each(e => {
        let i = $(e).attr('spa-insert')
        if( i in content ) {
          $(e).insert(content[i])
        }
      })
      
      $('nav').addClass('navbar navbar-default navbar-static-top')
      $('nav > ul').addClass('nav navbar-nav')
      
      $('[spa-repeat]').each(function () {
        switch ($(this).attr('spa-repeat')) {
          case "item in menu":
            $(this).parent().before('<div class="nav-header" class="col-md-2"><a class="navbar-brand" href="#">requireJS-SPA</a></div>')
            $('.nav-header').append($(this).parent())
            let lis = ''
            for (k in content.menu) {
              lis = `${lis}<li><a href="${content.menu[k].href}">${content.menu[k].innerText}</a>`
            }

            $(this).replaceWith(lis)
        }
      })
    }
  }
});
define('content', ['jquery'], function ($) {
  return {
    page_content: 'As requested in comments to the answer of the stackoverflow question <a href="http://stackoverflow.com/questions/32238659/how-to-make-spa-using-javascript-jquery-and-require-js/32238951#32238951">How to make spa using javascript, jquery and require,js</a>. This page should be previewed in windowed mode! So click the little pop-out button if you haven\'t already.',
    menu: {
      stackoverflow: {innerText: 'stackoverflow', href:'//stackoverflow.com'}, 
      github: {innerText: 'github', href: '//github.com'}
    },
    init: function () {
      console.info('[model: content] loaded')
    }
  }
});
define('otherstuff', ['jquery', 'content', 'route'], 
  function($, content, route) {
    /* This load CSS stuff would definitely fit better in the app.js, 
       but for clarity of the example I am keeping it here. */
    
    return {
      load_css: function(url) {
        var link = document.createElement("link");
        link.type = "text/css";
        link.rel = "stylesheet";
        link.href = url;
        document.getElementsByTagName("head")[0].appendChild(link);
      },
      init: function() {
        console.info('[controller: otherstuff] loaded')
        
        $('<a class="route" href="#vis">Show me the money!</a><img class="route" src="http://i.imgur.com/Ssns07U.jpg" />')
          .insertBefore('section[spa-insert]')
        $('img.route').css('visibility', 'hidden')
        
        $('section[spa-insert=content]').addClass('col-md-6 col-md-offset-3').html(content.page_content)
        
        $('<div class="rt-info"><span class="rt-i glyphicon glyphicon-chevron-up" aria-hidden="true"></span> <span class="rt glyphicon-class">this is a route too!</span></div>').insertAfter('header > nav')
  
        route.when('vis',
          function() {
            console.log('#vis called')
            $('img.route').css('visibility', 'visible')
          },
          function() {
            console.log('destroy on #vis called')
            $('img.route').css('visibility', 'hidden')
          }
        )
      }
    }
  }
);
define('route', ['jquery'], function ($) {
  return {
    routes: {},
    when: function (hash, callback, destroy) {
      this.routes[hash] = {callback:callback, destroy:destroy}
    },
    current_destroy: function(){},
    init: function () {
      route = this
      $(document).ready(function() {
        $(window).bind( 'hashchange', function(e) { 
          var anchor = document.location.hash.substring(1)
          if(typeof route.current_destroy === 'function') {
            route.current_destroy()
          }
          if (anchor in route.routes) {
            
            route.routes[anchor].callback()
            route.current_destroy = route.routes[anchor].destroy
          }
        })
      })
    }
  }
})