Skip to content

Instantly share code, notes, and snippets.

@simshanith
Last active December 12, 2015 06:08
Show Gist options
  • Save simshanith/4727003 to your computer and use it in GitHub Desktop.
Save simshanith/4727003 to your computer and use it in GitHub Desktop.
Example Backbone app JS skeleton
<!DOCTYPE html>
<html lang="en">
<head>
<title>Example Backbone Skeleto</title>
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
<meta content="utf-8" http-equiv="encoding">
<style>
.hidden{
display: none !important;
}
.invisible {
visibility: hidden !important;
}
</style>
</head>
<body>
<div id="blkblnkt"></div>
<div class="page hidden" id="exampleMixinView"></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.2/jquery.cookie.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/lodash.js/1.0.0-rc.2/lodash.underscore.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/backbone.js/0.9.2/backbone-min.js "></script>
<script src="assets/js/init.js"></script>
<script src="assets/js/views.js"></script>
<script src="assets/js/main.js"></script>
</body>
</html>
// select few global variables
window.app = _.extend({
views: {},
urls: {}, // normalized to no trailing slash; set below
queryStrings: {}, // params to detect as part of OAuth Flow
cache: {}, // set up an object for request caching
cookies: ['__my_cookie_key'], // list of cookie keys _.reduce'd into object from cookie values (using jQuery.cookie)
ajaxHistory: [],
}, Backbone.Events);
window.utils = {};
window.views = {};
window.api = {};
app.cookies = _.reduce(app.cookies, function(memo, cookie){memo[cookie] = $.cookie(cookie); return memo;}, {});
_.mixin({ // lo-dash / underscore mixins, available in _ passed into main
safeGet: function(obj, propName){ // Utility for safely retrieving values. Test cases:
if(obj && _.has(obj, propName)) // _.safeGet({foo: "bar"}, "foo") ==> "bar"
return obj[propName]; // _.safeGet({foo: "bar"}, "bar") ==> null
} // _.safeGet(undefined, "foo") ==> null
});
// utility functions
utils.getAjaxHistory = function(){
return _.reduce(app.ajaxHistory,
function(memo, item){
memo += [
item.url,
item.status,
item.responseHeaders,
'\n'
].join('\n');
return memo;
}, '');
};
utils.fetchRootUrl = function(){
var rootAnchor = document.createElement('a');
rootAnchor.setAttribute('href', '/');
var rootUrl = rootAnchor.href;
delete rootAnchor;
return rootUrl.slice(-1) == '/' ? rootUrl.slice(0, -1) : rootUrl; // remove trailing slash (if present)
}
utils.toProperCase = function (myString) {
return myString.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
};
utils.startRemoteDebugger = function(listenId){
if(!(app.debug || listenId))return;
if(listenId) app.debug = listenId;
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) return;
js = d.createElement(s); js.id = id;
js.src = "http://jsconsole.com/remote.js?"+app.debug;
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'remoteDebugger'));
};
utils.queryString = function(key) {
var re=new RegExp('(?:\\?|&)'+key+'=(.*?)(?=&|$)','gi');
var r=[], m;
while ((m=re.exec(document.location.search)) != null) r.push(m[1]);
return r;
};
// Function To Safely console.log
utils.safeLog = function(){
var logs = arguments;
if(window.console&&window.console.log){
for (var i=0; i<logs.length; i++) {
window.console.log(logs[i]);
}
}
};
// ajax logging for remote debugging
$(document).ajaxSuccess(function(e, xhr, settings){
utils.safeLog('ajax success', 'url: '+settings.url, xhr.getAllResponseHeaders());
app.ajaxHistory.push({
url: settings.url,
responseHeaders: xhr.getAllResponseHeaders(),
status: xhr.status+' '+xhr.statusText,
});
});
$(document).ajaxError(function(e, xhr, settings){
utils.safeLog('ajax error', 'url: '+settings.url, xhr.getAllResponseHeaders());
app.ajaxHistory.push({
url: settings.url,
responseHeaders: xhr.getAllResponseHeaders(),
status: xhr.status+' '+xhr.statusText,
});
});
//*****=====~~~~- environment config
// find app root url based on page context
app.rootUrl = app.urls.appRoot = utils.fetchRootUrl();
// detect environment based on root url
app.env = ~app.rootUrl.indexOf('local') ? 'local' :
~app.rootUrl.indexOf('vmldev') ? 'dev' :
~app.rootUrl.indexOf('vmlstage') ? 'stage' :
~app.rootUrl.indexOf('example.com') ? 'prod' : 'whoknows';
// set urls based on environment
switch(app.env){
case 'prod':
app.urls.api = 'http://example.com/api';
//break; // commented out to only allow qa urls until prod is ready
case 'stage':
case 'dev':
case 'local' :
app.urls.api = 'http://dev.example.com/api';
break;
default:
app.urls.api = '';
}
//*****=====~~~~- api functions
app.cache.data = {};
api.getData = function(term, callback){
callback = _.isFunction(callback) ? callback : function(){};
var results = app.cache.data[term];
if(results){
callback(results);
} else {
var url = app.urls.api+'/data/'+term;
$.ajax(url, {
type: 'get',
dataType: 'json',
success: function(resp){
app.cache.data[term] = resp;
callback(resp);
}
});
}
};
/////////////////////////////////////////////////////***=~-
// powerful AppView Backbone.View extension class //*****=====~~~~-
///////////////////////////////////////////////////***=~-
AppView = Backbone.View.extend({
isCurrentView: function(){return (views.currentView && this.cid == views.currentView.cid);},
initialize: function(options){
var view = this; // view setup
var combinedRender = options && options.render && // chained render functions
_.isFunction(options.render) && function(){
AppView.prototype.render.apply(view, [].slice.call(arguments)); // call app level render
options.render.apply(view, [].slice.call(arguments)); // call view level render
return view;
} || AppView.prototype.render; // fallback to app level render
view.id = view.$el.prop('id'); // set id on view to view's element id
view.data = view.data || {}; // initialize view.data as empty object (if not already initialized)
// call passed initialize function, if available
options && _.isFunction(options.initialize) &&
options.initialize.apply(view, [].slice.call(arguments));
// wrap it all up and redefine self
view = _.extend(view, options, {render: combinedRender});
},
render: function(){
// ALL VIEWS SHOULD DO THIS
var view = this; // local reference
// hide other views
$('div.page').not(view.el).addClass('hidden');
// update reference
views.currentView = view;
// reset skeleton template
view.$el.html(view.template(view.data));
// be sure to show this view
view.$el.removeClass('hidden');
},
});
AppView.MyViewMixin = function(){
return {
option: "defaultValue",
viewFunction: function(){
var view = this; // mixin handles binding
alert("View ID:"+ view.id);
},
events: { // you can pass in events object directly, tho it can be tricky to extend this with more events specific to the view
'click .mySelector': function(e){
utils.safeLog(e);
alert('Clicked!');
}
}
};
};
(function($, _, Backbone, window, undefined){
app.views.exampleMixinView = new AppView(_.extend(AppView.MyViewMixin(), {
el: $('#exampleMixinView'),
template: views.myTemplate,
render: function() {
var view = this;
view.viewFunction(); // available from mixin
}
}));
/* *****==~~~-
* APPLICATION ROUTER
* ******************* */
AppRouter = Backbone.Router.extend({
routes: {
'*path' : 'defaultRoute'
},
defaultRoute: function(){
app.views.exampleMixinView.data = {foo: '<button class="mySelector">Click Me</button>'};
app.views.exampleMixinView.render();
}
});
$(function(){ // on ready
app.router = new AppRouter();
Backbone.history.start();
utils.safeLog('Application Initialized', app);
api.getData('manbearpig');
});
})(jQuery, _, Backbone, this)
views.myTemplate = _.template('<div><%= foo %></div>');
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment