Skip to content

Commit

Permalink
Add pushstate functionality: includes redone routes, difference cache…
Browse files Browse the repository at this point in the history
…s based on template and layout, and JS to handle pushstate
  • Loading branch information
lukekarrys committed Feb 25, 2012
1 parent 2db7466 commit 069f0eb
Show file tree
Hide file tree
Showing 14 changed files with 213 additions and 103 deletions.
15 changes: 10 additions & 5 deletions lib/plugins/handlebars.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -46,14 +46,19 @@ function getTemplate(filePath) {
} }


function renderTemplate(bodyPath, layoutPath, context) { function renderTemplate(bodyPath, layoutPath, context) {


var layoutName = layoutPath || defaultLayoutPath,
// Create a cacheId because the same bodyPath can call multiple layouts
cacheId = bodyPath+"|"+layoutName;

if(app.cacheTemplates) { if(app.cacheTemplates) {
if(cache.hasOwnProperty(bodyPath)) { if(cache.hasOwnProperty(bodyPath)) {
return cache[bodyPath]; return cache[cacheId];
} }
} }


var layoutSrc = getTemplate((layoutPath || defaultLayoutPath)),
var layoutSrc = getTemplate(layoutName),
layout = handlebars.compile(layoutSrc), layout = handlebars.compile(layoutSrc),
bodySrc = getTemplate(bodyPath), bodySrc = getTemplate(bodyPath),
body, ret; body, ret;
Expand Down Expand Up @@ -81,7 +86,7 @@ function renderTemplate(bodyPath, layoutPath, context) {
ret = layout(context); ret = layout(context);


if(app.cacheTemplates) { if(app.cacheTemplates) {
cache[bodyPath] = ret; cache[cacheId] = ret;
} }


return ret; return ret;
Expand Down
60 changes: 17 additions & 43 deletions lib/plugins/routes.js
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,54 +2,28 @@ var path = require('path'),
director = require('director'), director = require('director'),
routes = {}, routes = {},
router, app; router, app;


function getRoot() { function renderRouteTemplate(ajaxIdentifier, token1, token2) {
this.res.writeHead(200, {"Content-Type": "text/html"}); var
this.res.end(app.render('index')); // If our route starts with "_" we are using an empty layout for ajax purposes
} layoutName = (ajaxIdentifier === "_") ? "emptyLayout" : "",

// If we dont have a first token, then we use the index template
function getDetails() { path1 = (typeof token1 === 'string' && token1 !== '') ? token1 : 'index',
this.res.writeHead(200, {"Content-Type": "text/html"}); // Our second token is optional, but if we have one prepend a /
this.res.end(app.render('details')); path2 = (typeof token2 === 'string' && token2 !== '') ? '/' + token2 : '';
}

function getSchedule() {
this.res.writeHead(200, {"Content-Type": "text/html"});
this.res.end(app.render('schedule'));
}

function getRegister() {
this.res.writeHead(200, {"Content-Type": "text/html"});
this.res.end(app.render('register'));
}

function getRegisterSponsor() {
this.res.writeHead(200, {"Content-Type": "text/html"});
this.res.end(app.render('register/sponsor'));
}

function getRegisterVolunteer() {
this.res.writeHead(200, {"Content-Type": "text/html"}); this.res.writeHead(200, {"Content-Type": "text/html"});
this.res.end(app.render('register/volunteer')); this.res.end(app.render(path1 + path2, layoutName));
}

function getRegisterSpeaker() {
this.res.writeHead(200, {"Content-Type": "text/html"});
this.res.end(app.render('register/speaker'));
} }


exports.name = "routes"; exports.name = "routes";


exports.attach = function(options) { exports.attach = function(options) {
app = this; app = this;


app.router.get('/', getRoot); // Whitelist the routes that we render a template

// Route path must match the path to the template in the templates dir
app.router.get('/details', getDetails); app.router.get(/\/(_?)\/?/, renderRouteTemplate);
app.router.get('/schedule', getSchedule); app.router.get(/\/(_?)\/?(404|details|schedule|register)/, renderRouteTemplate);
app.router.get('/register', getRegister); app.router.get(/\/(_?)\/?(register)\/(sponsor|volunteer|speaker)/, renderRouteTemplate);
app.router.get('/register/sponsor', getRegisterSponsor);
app.router.get('/register/volunteer', getRegisterVolunteer);
app.router.get('/register/speaker', getRegisterSpeaker);

}; };
158 changes: 158 additions & 0 deletions public/js/history-app.js
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,158 @@
var Site = {};
(function($) {

var hasHistory = function() {
return !!(window.history && history.pushState);
};

Site.Wufoo = {
'base': {
'userName':'meltmedia',
'autoResize':true,
'header':'show',
'ssl':true
},
'/register' : {
'formHash':'z7x0w7',
'height':'520'
},
'/register/sponsor' : {
'formHash':'z7x0r3',
'height':'576'
},
'/register/speaker' : {
'formHash':'z7x1z5',
'height':'746'
},
'/register/volunteer' : {
'formHash':'z7x0k1',
'height':'703'
}
};

document.write = function() {
Site.currentFormHolder.append($(arguments[0]));
};

$(function() {
var $contentArea = $('#content .section-content'),
$initForm = $contentArea.find('.wufoo-form-holder'),
rootUrl = History.getRootUrl(),
$body = $('body'),
$title = $('title'),
$scrollTo = $('#navigation'),
addForm = function($c, pageUrl) {
var formSpecifics = Site.Wufoo['/'+pageUrl.replace(rootUrl, '')],
$formHolder = $c.find('.wufoo-form-holder');

if (typeof formSpecifics !== 'undefined' && $formHolder.length > 0) {
var formObj = $.extend(Site.Wufoo.base, formSpecifics);
Site.currentForm = new WufooForm();
Site.currentForm.initialize(formObj);
Site.currentFormHolder = $formHolder;
$formHolder.append("<scr"+"ipt>Site.currentForm.display();</scr"+"ipt>");
}
};

addForm($contentArea, window.location.href);

if (!hasHistory()) {
return;
}

$.expr[':'].internal = function(obj, index, meta, stack) {
// Prepare
var $link = $(obj),
url = $link.attr('href') || '';

// It is internal if it begins with the root url or if it has no :, also ignore named anchors
return ((url.substring(0, rootUrl.length) === rootUrl || url.indexOf(':') === -1) && url.charAt(0) !== "#");
};

// Ajaxify Helper
$.fn.ajaxify = function() {
// Prepare
var $this = $(this);

// Ajaxify
$this.find('a:internal').click(function(event) {
// Prepare
var $link = $(this),
url = $link.attr('href');

// Continue as normal for cmd clicks etc
if (event.which == 2 || event.metaKey) {
return true;
}

// Ajaxify this link
History.pushState(null, null, url);
event.preventDefault();
return false;
});

// Chain
return $this;
};

$body.ajaxify();

$(window).bind('statechange', function() {

var State = History.getState(),
url = State.url,
relativeUrl = '/' + url.replace(rootUrl, ''),
successFn = function(data) {
var $data = $(data),
$content = $data.filter('section.section-content').ajaxify(),
title = "NotConf - " + $content.data('title');

// Append any wufoo scripts back into their rightful parent
addForm($content, url);

$contentArea.fadeOut(400, function() {
$(this).replaceWith($content).fadeIn(400);
$contentArea = $content;
});

$title.text(title);
_gaq.push(['_trackPageview', relativeUrl]);

var currentTopPx = $body.scrollTop(),
scrollTopPx = $scrollTo.offset().top,
distance = (currentTopPx > scrollTopPx) ? currentTopPx - scrollTopPx : 0;

if (distance > $scrollTo.outerHeight()) {
$('html, body').animate({
scrollTop: scrollTopPx
}, distance * 2);
}
},
errorFn = function(jqXHR, textStatus, errorThrown) {
document.location.href = url;
return false;
};

$.ajax({
url: '/_' + relativeUrl,
dataType: 'html',
success: successFn,
error: function(jqXHR, textStatus, errorThrown) {
if (jqXHR.status === 404) {
$.ajax({
url: '/_/404',
dataType: 'html',
success: successFn,
error: errorFn
});
} else {
document.location.href = url;
}
}
});

return false;
});

});
})(jQuery);
1 change: 1 addition & 0 deletions public/js/history.adapter.jquery.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 069f0eb

Please sign in to comment.