Skip to content

Instantly share code, notes, and snippets.

@thejefflarson
Created October 13, 2011 17:55
Show Gist options
  • Save thejefflarson/1284941 to your computer and use it in GitHub Desktop.
Save thejefflarson/1284941 to your computer and use it in GitHub Desktop.
// The inspiration for this class is grounded in Jeremy Ashkenas's work with
// DocumentCloud. The concept of **bindings** is especially brilliant and is a
// large portion of what makes **Glass.js** tick.
//
propublica.View = Base.extend({
// jquery object for our target
el : null,
// list of bindings to listen in on
bindings : {},
tag : 'div',
cssClass : '',
toggleClass : 'active',
id : '',
scope : '',
// return if there's an empty query string for abstract views.
init : function(){
if(this.query().string.length == 0) return;
this._ensureElement();
},
_ensureElement : function(){
this.el = $(this.query().string);
this.cid = _.uniqueId();
this.setBindings();
this.el.bind("_render", _.bind(this._render, this));
},
// Delegate to jquery scoped within this.el
$ : function(query){
return $(query, this.el);
},
// Hidden method that delegates to **render** and is triggered by the initializer.
// You probably shouldn't need to overwrite this.
_render : function(){
this.render();
return true;
},
// override this method with the actual rendering bits
render : function(){
return this;
},
query : function(){
var self = this;
var string = _.reduce([['', 'scope'], [' ', 'tag'], ['#', 'id'], ['.', 'cssClass']], function(memo, attr){
return memo + (self[attr[1]].length > 0 ? attr[0] + self[attr[1]] : '');
}, '');
string = string.replace(/^\s*/, '');
return {
string: string,
scope: this.scope,
tag: this.tag,
id: this.id,
cssClass: this.cssClass
};
},
// ## DOM Manipulation Helpers ##
// Although it's possible to directly manipulate the **dom** in a view, its much
// more efficient to pass off processing to an associated model.
// For example, **munge** takes an **Array** of dom elements, not a jquery object,
// and replaces the **View** element's children with the new **collection**.
munge : function(collection){
var c = $('<div></div>');
_.each(collection, function(child){
c.append(child);
});
this.el.html(c.children());
},
// ## Generic Event Handlers ##
// **toggle** adds the **toggleClass** class (by default '.active') or removes it,
// in an on and off fashion. Returns the **toggleClass** if it was added. Relies on
// the **currentTarget** of the event object rather than **this.el** so you can pre-process
// and adjust to the correct target if need be.
toggle : function(e){
e.preventDefault();
e.stopPropagation();
var el = $(e.currentTarget);
el.toggleClass(this.toggleClass);
return el.hasClass(this.toggleClass) ? this.toggleClass : false;
},
// gaq tracking for events. You can pass in an array or arguments for the event you
// need to track.
track : function(){
var args = _.toArray(arguments).slice(0);
window._gaq.push((['_trackEvent']).concat(args));
},
// ## Event Bindings ##
// sets the bindings and set 'this' back to the the view object
setBindings : function(){
var self = this;
this.el.unbind(".delegate-" + this.cid);
_.each(this.bindings, function(fn, key){
self.el.live(key + ".delegate-" + this.cid, _.bind(self[fn], self));
});
},
// ## Model Communication ##
//
// bindTo allows you to subscribe to a particular attribute's change event using
// simple [Key Value Observing](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html).
// So for example if you wanted to listen to events on the title attribute
// of a model you would write:
//
// var view = new propublica.View.extend({
// title_changed : function(e, model, key, old, value){
// console.log(model.read(key));
// }
// });
// view.bindTo("title", model);
// model.write("title", "New Title");
// >>> "New Title"
//
// which would automatically call "title_changed" on the view.
//
// If you want to use a different function than the default -- "{key}_changed"
// -- then pass in a *targetKey*.
bindTo : function(keyPath, object, targetKey){
keyPath = object.keyPath(keyPath);
var cb = targetKey ? this[targetKey] : this[keyPath];
object.bind(keyPath, _.bind(cb, this));
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment