Skip to content

Instantly share code, notes, and snippets.

@meowfreeze
Created January 22, 2014 21:33
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save meowfreeze/8567770 to your computer and use it in GitHub Desktop.
Save meowfreeze/8567770 to your computer and use it in GitHub Desktop.
A prototype of a backbone portfolio app with masonry.
// load the application once the DOM is ready
$(function() {
// research this:
// Backbone.pubSub = _.extend({}, Backbone.Events);
// TEMPLATES
var templates = {
portfolio:
"<% _.each(projects, function(project, index) { %>" +
"<a href='<%= project.slug %>' title='<%= project.title %>' class='item hide project'>" +
"<img src='<%= project.preview.url %>' width='<%= project.preview.width %>' height='<%= project.preview.height %>' onload='image_loaded(this)'/>" +
"<span class='caption'><%= project.title %></span>" +
"</a>" +
"<% }); %>",
project:
"<% _.each(project.images, function(image, index) { %>" +
"<div class='item hide'>" +
"<img src='<%= image.url %>' width='<%= image.width %>' height='<%= image.height %>' onload='image_loaded(this)'/>" +
"<span class='caption small'><%= image.caption %></span>" +
"</div>" +
"<% if (project.images.length < 2 && index + 1 == project.images.length) { %>" +
"<div class='description item'>" +
"<h2><%= project.title %></h2>" +
"<%= project.description %>" +
"</div>" +
"<% } %>" +
"<% if (index === 2) { %>" +
"<div class='description item'>" +
"<h2><%= project.title %></h2>" +
"<%= project.description %>" +
"</div>" +
"<% } %>" +
"<% }); %>",
prev_next:
"<div id='prev'><% if (prev) { %>" +
"<a href='/<%= prev.attributes.slug %>' title='<%= prev.attributes.title %>' class='next_prev'>< <%= prev.attributes.title %></a>" +
"<% } else { %><p>...</p><% } %></div>" +
"<div id='next'><% if (next) { %>" +
"<a href='/<%= next.attributes.slug %>' title='<%= next.attributes.title %>' class='next_prev next'><%= next.attributes.title %> ></a>" +
"<% } else { %><p>...</p><% } %></div>",
about:
"<div id='about' class='hide fadein'><%= page.content %></div>"
}
// MODEL
// slug is unique in the backend
var Project = Backbone.Model.extend({
idAttribute: 'slug'
});
// COLLECTION
var Portfolio = Backbone.Collection.extend({
model: Project,
// return all projects by category
// if no category is specified, return the collection unfiltered
get_projects_by_category: function(cat) {
if (!cat) { return this }
return _.filter(this.toJSON(), function(project) {
return _.contains(project.category, cat)
})
},
// return all projects by subcategory
// if no subcategory is specified, return the collection unfiltered
get_projects_by_subcategory: function(subcat) {
if (!subcat) { return this }
return _.filter(this.toJSON(), function(project) {
return _.contains(project.subcategory, subcat)
})
},
// return a project by slug (unique)
get_project_by_slug: function(slug) {
return this.where({'slug': slug});
},
// return a dict with next and previous projects
get_next_prev: function(order) {
return {
prev: this.where({'order': order - 1})[0],
next: this.where({'order': order + 1})[0]
}
}
});
var Page = Backbone.Model.extend({
idAttribute: 'slug'
});
var Pages = Backbone.Collection.extend({
model: Page,
// return a page by slug (unique)
get_page_by_slug: function(slug) {
return this.where({'slug': slug});
},
})
// VIEWS
var NavigationView = Backbone.View.extend({
el: 'nav',
events: {
'click a.category': 'category',
'click a.subcategory': 'subcategory',
'click a.page': 'page'
},
category: function(e) {
e.preventDefault()
var category = $(e.target).attr('href')
app.previous_url = app.parse_category_url(category)
app.portfolioRouter.navigate(category, {trigger: true})
$('nav a').removeClass('active')
$(e.currentTarget).addClass('active')
},
subcategory: function(e) {
e.preventDefault()
var subcategory = $(e.target).attr('href')
app.portfolioRouter.navigate(subcategory, {trigger: true})
$('nav a').removeClass('active')
$(e.currentTarget).addClass('active')
},
page: function(e) {
e.preventDefault()
var page = $(e.target).attr('href')
app.portfolioRouter.navigate(page, {trigger: true})
$('nav a').removeClass('active')
$(e.currentTarget).addClass('active')
}
})
var PortfolioView = Backbone.View.extend({
el: '#content',
template: _.template(templates.portfolio),
events: {
'click a.project': 'project',
'mouseenter a.project': 'highlight',
'mouseleave a.project': 'unhighlight'
},
project: function(e) {
e.preventDefault()
var title = $(e.currentTarget).attr('href')
app.portfolioRouter.navigate(title, {trigger: true})
},
render: function() {
this.$el.html(this.template({
projects: app.filtered.toJSON()
}));
this.postrender()
},
postrender: function() {
// remove category highlighting
$('nav a').removeClass('cat')
// reset masonry
app.masonry(this.el)
// position titles
app.position_captions()
},
highlight: function(e) {
var project = app.projects.get($(e.currentTarget).attr('href'))
app.project_cats(app.get_cats(project))
},
unhighlight: function() {
$('nav a').removeClass('cat')
}
});
var ProjectView = Backbone.View.extend({
el: '#content',
template: _.template(templates.project),
render: function() {
var project = app.project.toJSON()[0]
project.images = app.sort_images_by_category(project.images)
this.$el.html(this.template({
project: project,
}));
this.postrender()
},
postrender: function() {
// reset masonry
app.masonry(this.el)
// remove category highlighting
$('nav a').removeClass('cat')
// highlight categories for project
app.project_cats(app.cats)
// position titles
app.position_captions()
},
})
var AboutView = Backbone.View.extend({
el: '#content',
template: _.template(templates.about),
render: function() {
var page = app.pages.toJSON()[0]
this.$el.html(this.template({
page: page
}));
this.postrender()
},
postrender: function() {
// remove category highlighting
$('nav a').removeClass('cat')
}
})
var PrevNextView = Backbone.View.extend({
el: '#next_prev .inner',
template: _.template(templates.prev_next),
events: {
'click a.next_prev': 'next_prev',
},
render: function() {
var project = app.project.toJSON()[0]
app.previous_url = '/'
var next_prev = app.projects.get_next_prev(project.order)
this.$el.html(this.template({
prev: next_prev.prev,
next: next_prev.next
}));
},
next_prev: function(e) {
e.preventDefault()
var title = $(e.currentTarget).attr('href')
app.portfolioRouter.navigate(title, {trigger: true})
}
})
// ROUTER
var PortfolioRouter = Backbone.Router.extend({
routes: {
'': 'index',
'about': 'about',
'category/:category': 'category',
'subcategory/:subcategory': 'subcategory',
':project': 'project'
},
index: function() {
app.filtered.reset(projectsData)
},
category: function(category) {
app.filtered.reset(app.projects.get_projects_by_category(category))
},
subcategory: function(subcategory) {
app.filtered.reset(app.projects.get_projects_by_subcategory(subcategory))
},
project: function(project) {
app.cats = app.get_cats(project)
app.project.reset(app.projects.get_project_by_slug(project))
},
about: function() {
app.aboutView.render()
app.prevNextView.$el.empty()
}
})
var app = {
// collections
projects: new Portfolio(projectsData),
filtered: new Portfolio(),
project: new Portfolio(),
pages: new Pages(pagesData),
// views
navigationView: new NavigationView(),
portfolioView: new PortfolioView(),
projectView: new ProjectView(),
prevNextView: new PrevNextView(),
aboutView: new AboutView(),
// global categories object
cats: [],
// initialize/reset masonry
masonry: function(element) {
var masonry = new Masonry(element, {
columnWidth: 20,
itemSelector: '.item',
isAnimated: true
});
},
position_captions: function() {
_.each($('.caption'), function(caption) {
$(caption).css('marginBottom', -1 * ( $(caption).height() + 2) )
})
},
// categories
project_cats: function(cats) {
_.each($('nav a'), function(title) {
_.each(cats, function(cat) {
if ($(title).attr('title') === cat) {
$(title).addClass('cat')
}
})
})
},
// return a list of categories for a given project slug
get_cats: function(project) {
var project = this.projects.get(project)
return project.get('category').concat(project.get('subcategory'))
},
// if navigating to a project from a specific category,
// display images tagged with that category first
sort_images_by_category: function(images, category) {
return _.sortBy(images, function(image) {
return (image.category === app.previous_url) ? 0 : 1
})
},
// return category segment
parse_category_url: function(category) {
if (category !== '/') {
//console.log(category.match(/\/([A-Za-z0-9_\-]+)\/?$/)[1])
return category.match(/\/([A-Za-z0-9_-]+)\/?$/)[1]
}
return '/'
},
// initialize the app
initialize: function() {
this.masonry('#content')
this.position_captions()
this.previous_url = this.parse_category_url(window.location.pathname)
}
}
app.filtered.on('reset', function() {
app.portfolioView.render()
app.prevNextView.$el.empty()
})
app.project.on('reset', function() {
app.projectView.render()
app.prevNextView.render()
})
app.portfolioRouter = new PortfolioRouter();
Backbone.history.start(
{
pushState: true,
silent: true
});
app.initialize()
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment