Skip to content

Instantly share code, notes, and snippets.

@psaia
Created August 15, 2011 21:34
Show Gist options
  • Save psaia/1147926 to your computer and use it in GitHub Desktop.
Save psaia/1147926 to your computer and use it in GitHub Desktop.
A framework for creating single page JavaScript apps easily.
/*
Author: Pete Saia
*/
/* Configuration
The name of the property should match the view (<section>)
ID. The route is the desired hash URI. The controller will be
called upon displaying the section html. For the route do
not add a leading or trailing slash.
Example:
firstsegment/secondsegment
To link to a page just create an anchor with the "rel"
attribute set to "nofollow" and the href with the name
of the desired property which is also the div of the
desired section. You can make use of parameters by using
the pipe symbol "|". That's it.
Example:
<a href="roundtable_target_audiences" rel="internal">Foo</a>
Initiate with:
var BOOTSTRAP = {
'config': {
container_selector: '#main',
view_selector: 'section'
},
'default': {
default_view: 'home', // View to load when there is no hash.
route: '',
controller: function () {
// Some functions.
}
},
'controllers': {
'home': {
route: 'home',
controller: function () {
// Some functions.
}
},
'portfolio': {
route: 'portfolio',
controller: function () {
// Some other functions.
}
},
'portfolio_photography': {
route: 'portfolio/photography',
controller: function () {
// Some more functions.
}
},
}
};
// Start.
$(function () {
var heroApp = new Hero(BOOTSTRAP);
});
-----------------------------------------------------------*/
/* Hero
*
* Make single page JavaScript apps like a hero.
* Author: Pete Saia
*
* hero.js
------------------------------------------------*/
var Hero = function (bs) {
// Load bootstrap.
this.bootstrap = bs;
// Assign DOM elements.
this.container = this.bootstrap['config'].container_selector;
this.$views = $(this.container).find(this.bootstrap['config'].view_selector);
// Start.
this.init();
this.bind_links();
};
/*
* Initiate.
* @return void
*/
Hero.prototype.init = function (page) {
var that = this;
// Remove views from DOM.
this.$views.remove();
// This is using the hashchange plugin for browser history.
jQuery(window).hashchange(function(e) {
that.load(that._is_view(that._get_hash(), 'uri'));
});
// Manual trigger load.
this.load(that._is_view(that._get_hash(), 'uri'));
};
/*
* Handle links.
* @return void
*/
Hero.prototype.bind_links = function () {
var that = this;
$('a[rel="nofollow"]').live('click', function () {
var prop = $(this).attr('href');
// If the link is valid change the hash which will cause the page
// to change via hashchange().
if (that._is_view(prop, 'property')) {
that._set_hash(that.bootstrap['controllers'][prop].route);
} else {
if (window.console) {
console.log('Property name is not valid.');
}
}
return false;
});
};
/*
* Display a specific section based on route property.
* @return void
*/
Hero.prototype.load = function (page) {
if (page.length > 0 && page !== 'default') {
this._display_view(page, this.bootstrap['controllers'][page].controller);
} else {
// Show default page, load default_view and default controller.
this._display_view(this.bootstrap['default'].default_view, this.bootstrap['default'].controller);
}
};
/*
* Check if hash URI or property name is valid.
* @return false or the name of the page property (page id)
*/
Hero.prototype._is_view = function (str, type) {
var property_name = false,
route_str = '';
if (type === 'uri') {
for (prop in this.bootstrap['controllers']) {
route_str = this.bootstrap['controllers'][prop].route;
// Dealing with parameter, so remove it.
if (-str.indexOf('|')) {
str = str.replace(/\|.*/, '');
}
// Check if route exists in bootstrap.
if (route_str === str) {
property_name = prop;
break;
}
}
} else if (type === 'property') {
for (prop in this.bootstrap['controllers']) {
// Check if property exists in bootstrap.
if (prop === str) {
property_name = prop;
break;
}
}
}
return property_name;
};
/*
* Display HTML
* @return void
*/
Hero.prototype._display_view = function (div_id, callback) {
var that = this,
$current_div = $(this.container).find(this.bootstrap['config'].view_selector);
if ($current_div.length > 0 && $current_div.attr('id') !== div_id) {
// Remove the old, insert the new.
$current_div.fadeOut('fast', function () {
$(this).remove();
that.$views.each(function () {
if ($(this).attr('id') === div_id) {
$(that.container).append($(this));
$(this).fadeIn('fast');
$(this).css('opacity', '1');
$('body').attr('id', 'section_' + div_id);
callback();
}
});
});
} else {
// There is no old so just insert the new.
this.$views.each(function () {
if ($(this).attr('id') === div_id) {
$(that.container).append($(this));
$(this).fadeIn('fast');
$('body').attr('id', 'section_' + div_id);
callback();
}
});
}
};
/*
* Get the current hash uri.
* @return string
*/
Hero.prototype._get_hash = function () {
var str = '';
// If there is a hash.
if (window.location.hash) {
// Trim and remove hash.
str = $.trim(window.location.hash.replace('#', ''));
// Remove leading and trailing slash.
str = str.replace(/^\/|\/$/g, '');
}
return str;
};
/*
* Set new hash.
* @return void
*/
Hero.prototype._set_hash = function (str) {
// Add hash.
window.location.hash = '/' + str;
};
/* Hero
* Start sample app.
------------------------------------------------*/
var BOOTSTRAP = {
// Interface configurations.
'config': {
container_selector: '#main',
view_selector: 'section'
},
// The default landing page with no hash.
'default': {
default_view: 'home', // View to load when there is no hash.
route: '',
controller: function () {
splash_lbox();
cycle_diagram();
indicator('hide');
}
},
// Normal views. Methods coming from page-methods.js.
'controllers': {
'home': {
route: 'home',
controller: function () {
cycle_diagram();
indicator('hide');
}
},
'roundtable': {
route: 'roundtable',
controller: function () {
indicator('show');
}
},
'roundtable_target_audiences': {
route: 'roundtable/target-audiences',
controller: function () {
indicator('show');
}
},
'roundtable_validators': {
route: 'roundtable/network-of-validators',
controller: function () {
indicator('show');
}
},
'roundtable_topics': {
route: 'roundtable/topic-areas',
controller: function () {
indicator('show');
}
},
'roundtable_content_types': {
route: 'roundtable/content-types',
controller: function () {
indicator('show');
}
},
'news': {
route: 'news',
controller: function () {
indicator('show');
}
},
'news_nyt': {
route: 'news/nyt-study',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp': {
route: 'news/foreign_press-study',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp_1': {
route: 'news/foreign_press-study|1',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp_2': {
route: 'news/foreign_press-study|2',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp_3': {
route: 'news/foreign_press-study|3',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp_4': {
route: 'news/foreign_press-study|4',
controller: function () {
indicator('show');
carousel();
}
},
'news_fp_5': {
route: 'news/foreign_press-study|5',
controller: function () {
indicator('show');
carousel();
}
},
'sharing': {
route: 'sharing',
controller: function () {
indicator('show');
}
},
'sharing_content': {
route: 'sharing/content',
controller: function () {
indicator('show');
}
},
'sharing_nyt': {
route: 'sharing/nyt',
controller: function () {
indicator('show');
}
},
'sharing_google': {
route: 'sharing/google',
controller: function () {
indicator('show');
}
},
}
};
$(function () {
// Go.
new Hero(BOOTSTRAP);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment