Created
September 5, 2012 04:09
-
-
Save dstibrany/3630229 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"app", | |
"modules/document/documentmodels" | |
], | |
function(app, DocumentModels) { | |
var Views = {}; | |
// Documents - Documents List | |
// -------------------------- | |
Views.List = Backbone.View.extend({ | |
template: "document/list", | |
className: "span3", | |
tagName: "section", | |
id: "document-list", | |
documentOffsetLength: 15, | |
initialize: function() { | |
var self = this; | |
/*** Load Initial Batch of Documents ***/ | |
// fetch inital batch | |
app.on('search:getDocuments', function(query){ | |
self.collection.fetch({ | |
data: { | |
q: query ? query : 'null' | |
} | |
}); | |
}); | |
// render inital batch | |
this.collection.on('reset', function(collection, res) { | |
var rendering_list_view; | |
var query = res.data.q; | |
rendering_list_view = self.render(); | |
// initialize infinite scroll when the list view is done rendering | |
rendering_list_view.done(function() { | |
self.initInfiniteScroll(query); | |
}); | |
}); | |
/*** Load Subsequent Batches ***/ | |
// manually fetch next batch (ie. with a 'Load More' button) | |
app.on('loadMore', function() { | |
// invoke infiniteScroll's scroll event handler with the true option to manually trigger document fetching | |
self.getInfiniteScroll('target').trigger('scroll', true); | |
}); | |
// render next batch when new models are added to the collection | |
this.collection.on('add', function(model) { | |
self.insertView( | |
"#document-items", | |
new Views.ListItem({ | |
model: model | |
}) | |
).render(); | |
}); | |
}, | |
beforeRender: function() { | |
// Iterate over the passed collection and create a view for each item. | |
this.collection.each(function(model) { | |
// Pass the document model data to the new Document Item View. | |
this.insertView( | |
"#document-items", | |
new Views.ListItem({ | |
model: model | |
}) | |
); | |
}, this); | |
}, | |
// initialize infinite scrolling for this collection of documents | |
initInfiniteScroll: function(query) { | |
var $document_items = $('#document-items'); | |
var spinner_el = '<img id="infinte-scroll-spinner" src="assets/images/ajax-loader.gif" alt="" width="35" height="35">'; | |
var onFetch = function() { | |
$document_items.append(spinner_el); | |
}; | |
var success = function() { | |
$('#infinte-scroll-spinner').remove(); | |
}; | |
this.infiniScroll = new Backbone.InfiniScroll(this.collection, { | |
success: success, | |
onFetch: onFetch, | |
target: this.$('.overflow-scroll'), | |
includePage: true, | |
documentOffsetLength: this.documentOffsetLength, | |
scrollOffset: 100, | |
query: query | |
}); | |
}, | |
// return the infiniScroll object, or return a property in the infiniScroll's options | |
getInfiniteScroll: function(prop) { | |
var infiniScroll = this.infiniScroll; | |
if (!infiniScroll) return false; | |
return prop ? infiniScroll.options[prop] : infiniScroll; | |
} | |
}); | |
// Documents List Item - Individual Document View | |
// ---------------------------------------------- | |
Views.ListItem = Backbone.View.extend({ | |
template: 'document/listItem', | |
className: 'drag', | |
events: { | |
'click': 'showPreview' | |
}, | |
initialize: function() { | |
var baseUrl = app.root; | |
this.pdfUrl = baseUrl + 'examine/' + this.model.get('resource') + '?format=PDF'; | |
this.pdfThumbUrl = baseUrl + 'preview/' + this.model.get('resource') + '.sec'; | |
this.model.on('destroy', this.remove, this); | |
this.model.on('change', this.render, this); | |
}, | |
serialize: function() { | |
return _.extend(this.model.toJSON(), { | |
pdf_url: this.pdfUrl | |
}); | |
}, | |
afterRender: function() { | |
this.dragInit(); | |
}, | |
showPreview: function(e) { | |
var doc_tag_collection; | |
e.preventDefault(); | |
// store a reference to the model of the currently selected document | |
app.selectedDocument = this.model; | |
doc_tag_collection = new DocumentModels.Collections.DocumentTagList(); | |
doc_tag_collection.url += '/' + this.model.get('id'); | |
app.trigger('showPreview', this.model, doc_tag_collection); | |
}, | |
// TODO may want to optimize this to use event delegation rather than binding to each element | |
dragInit: function() { | |
var self = this; | |
var $el = this.$el; | |
// set proxy dimensions here | |
var proxy_width = 90; | |
var proxy_height = 20; | |
// drag start | |
$el.drag('start', function(e, dd) { | |
// augment drag callback with the model belonging to this document | |
dd.model = self.model; | |
return $('<div>') | |
.addClass('drag-proxy') | |
.css({ | |
'opacity': 0.75, | |
'background': 'rgba(34, 196, 248, 0.44)', | |
'width': proxy_width + 'px', | |
'height': proxy_height + 'px', | |
'left': '0', | |
'z-index': 2 | |
}) | |
.appendTo(document.body); | |
}); | |
// during drag | |
$el.drag(function(e, dd) { | |
$(dd.proxy).css({ | |
top: dd.offsetY + (dd.startY - dd.originalY) - (proxy_height / 2), | |
left: dd.offsetX + (dd.startX - dd.originalX) - (proxy_width / 2) | |
}); | |
}); | |
// drag end | |
$el.drag('end', function(e, dd) { | |
// revert proxy back to original location | |
var reverting_proxy = $(dd.proxy).animate({ | |
top: dd.startY - (proxy_height / 2), | |
left: dd.startX - (proxy_width / 2) | |
}, 420).promise(); | |
// remove proxy when revert animation is done | |
reverting_proxy.done(function() { | |
$(dd.proxy).remove(); | |
}); | |
}); | |
} | |
}); | |
return Views; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*global serverData:false */ | |
define([ | |
// Global | |
"app", | |
// modules | |
"modules/document/document", | |
"modules/header/header", | |
"modules/biller/biller", | |
"modules/billercred/billercred", | |
"modules/account/account", | |
"modules/user/user", | |
"modules/search/search" | |
], | |
function(app, Document, Header, Biller, BillerCred, Account, User, Search) { | |
// For DEBUGGING | |
window.app = app; | |
// Defining the application router, you can attach sub routers here. | |
var Router = Backbone.Router.extend({ | |
routes: { | |
"app": "app" | |
}, | |
app: function() { | |
// Create Collection/Model Instances | |
// --------------------------------- | |
// serverData is a global embedded in from Express | |
app.models.user = new User.Model( | |
// correct for the fact that user table in db doesn't | |
// have a "username" field | |
_.extend(serverData.user, { | |
username: serverData.user.email, | |
postalcode: serverData.user.postcode | |
}) | |
); | |
app.models.billers = new Biller.Collection(serverData.billers); | |
app.models.billerCreds = new BillerCred.Collection(serverData.billerCreds); | |
app.models.accounts = new Account.Collection(serverData.accounts); | |
var documents = app.models.documents = new Document.Collections.Documents(); | |
var search = app.models.search = new Search.Collections.Collection(); | |
// TODO make this cleaner | |
var folder_tree = app.models.folder_tree = new Document.Models.FolderTree(Document.Models.FolderTree.prototype.parse.call(null, serverData.folders)); | |
var tag_list = app.models.tag_list = new Document.Collections.TagList(serverData.tags); | |
// Create Main Layout | |
// ------------------ | |
var main = app.useLayout('main', {className: 'container'}); | |
// Set all the views. | |
main.insertViews({ | |
'#header': new Header.Views.Header({ | |
views: { | |
'#search-box': new Search.Views.Search({collection: search}) | |
} | |
}), | |
'#document-nav': [ | |
new Document.Views.FolderTree({model: folder_tree}), | |
new Document.Views.TagList({collection: tag_list}) | |
], | |
'#documents': [ | |
new Document.Views.List({collection: documents}), | |
new Document.Views.Preview({model: new Document.Models.Preview()}) | |
] | |
}).render().done(app.setHeight); | |
// Fetch Data from Server | |
// ---------------------- | |
// getting documents in root folder (i.e. 'null') | |
app.trigger('search:init', null); | |
} | |
}); | |
return Router; | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define([ | |
"app", | |
"modules/search/searchmodels" | |
], | |
function(app, SearchModels) { | |
var Views = {}; | |
// Utilities | |
// ---------------- | |
Views.util = { | |
resizeInput: function (input) { | |
var $input = $(input); | |
var length = $input.val().length; | |
var font_size = parseInt($input.css('fontSize'), 10); | |
var denominator = 1.3; | |
$input.css('width', ( length * (font_size / denominator) ) + 10 + 'px'); | |
} | |
}; | |
// Search View | |
// ----------- | |
Views.Search = Backbone.View.extend({ | |
template: 'search/search', | |
id: 'search', | |
className: 'input-append', | |
initialize: function() { | |
this.collection.on('add', function() { | |
this.render(); | |
}, this); | |
this.collection.on('destroy', function() { | |
this.render(); | |
this.search(); | |
}, this); | |
app.on('search:init', this.init, this); | |
// listen for change to folder/tag names so that the search bar can be updated | |
app.on('search:rename', this.rename, this); | |
// listen for deletions of folder/tags so that the search bar can be updated | |
app.on('search:destroy', this.destroy, this); | |
app.on('search:refresh', this.search, this); | |
}, | |
beforeRender: function() { | |
var type; | |
var self = this; | |
// helper function to allow us to set or append | |
function setView(type, model, append) { | |
var view; | |
if (type === 'text') { | |
view = new Views.SearchItemText({ | |
model: model, | |
collection: self.collection | |
}); | |
} else { | |
view = new Views.SearchItem({ | |
model: model | |
}); | |
} | |
self.setView('#' + type + '-search', view, append); | |
} | |
this.collection.each(function(model) { | |
type = model.get('type'); | |
// insert in a different DOM location, depending on whether the model is a tag or a folder | |
if (type === 'tid') { | |
setView('tags', model, true); | |
} else if (type === 'fid') { | |
setView('folders', model, true); | |
} else if (type === 'text') { | |
setView('text', model); | |
} | |
}); | |
}, | |
afterRender: function() { | |
// resize the text box on re-render of the search view | |
Views.util.resizeInput(this.$('input')); | |
}, | |
init: function(search_data) { | |
var model; | |
// convert search data into a model if it's not already a model | |
if (search_data && !(search_data instanceof Backbone.Model)) { | |
model = new SearchModels.Models.Model(search_data); | |
// a model of type textSearchModel was passed in | |
} else { | |
model = search_data; | |
} | |
// if no model exists then pass null to access all documents | |
if (!model) { | |
this.search(null); | |
// if the root folder was clicked then clear the search bar, trigger a search for all docs | |
} else if (model.get('id') === 'root') { | |
this.destroy(this.collection.where({type: 'fid'})[0]); | |
// if they are clicking on a non-selected item, continue the search | |
} else if (this.collection.add(model) !== false) { | |
this.search(); | |
// else they are clicking on an already selected tag/folder, so just remove the item from search bar | |
} else { | |
this.destroy(model); | |
} | |
}, | |
rename: function(id, name) { | |
var model = this.collection.get(id); | |
if (model) model.set('name', name); | |
}, | |
// destroy can take a model or an id - this.collection.get can handle both cases | |
destroy: function(id) { | |
var model = this.collection.get(id); | |
if (model) model.trigger('destroy', model); | |
// pass null to folder tree event listener to deselect the currently selected folder | |
app.trigger('deselect:folder', id); | |
}, | |
// perform the actual search by sending an event to the document list module | |
search: function(query) { | |
query = query !== null ? this.collection.toString() : null; | |
app.trigger('search:getDocuments', query); | |
} | |
}); | |
// Search Item View | |
// ---------------- | |
Views.SearchItem = Backbone.View.extend({ | |
template: 'search/searchitem', | |
className: 'searchitem', | |
events: { | |
"click": "removeItem" | |
}, | |
initialize: function() { | |
this.model.on('change', this.render, this); | |
}, | |
serialize: function() { | |
return this.model.toJSON(); | |
}, | |
removeItem: function(e) { | |
var model = this.model; | |
var type = this.model.get('type'); | |
var id = this.model.get('id'); | |
model.trigger('destroy', model); | |
// signal folder/tag tree event listener to deselect the currently selected folder. | |
if (type === 'tid') { | |
app.trigger('deselect:tag', id); | |
} else if (type === 'fid') { | |
app.trigger('deselect:folder', id); | |
} | |
} | |
}); | |
Views.SearchItemText = Backbone.View.extend({ | |
template: 'search/searchitemtext', | |
className: 'searchitemtext', | |
events: { | |
'keydown': 'handleKeydown', | |
'keyup': 'handleKeyup' | |
}, | |
serialize: function() { | |
return this.model.toJSON(); | |
}, | |
handleKeyup: function(e) { | |
// add the current value to the model | |
this.collection.textSearchModel.set('text', this.$('input').val()); | |
}, | |
handleKeydown: function(e) { | |
// resize the text box on re-render of the search view | |
Views.util.resizeInput(this.$('input')); | |
if (e.which === 13 || e.keyCode === 13) { | |
// user hit enter key, so perform a search | |
app.trigger('search:init', this.collection.textSearchModel); | |
} | |
} | |
}); | |
return Views; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment