Skip to content
This repository has been archived by the owner on Mar 20, 2021. It is now read-only.

Commit

Permalink
allow {{view 'Nested.Obj'}}
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryan Eastridge committed Aug 29, 2012
1 parent 3f763c4 commit d1d97ad
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 6 deletions.
19 changes: 16 additions & 3 deletions dist/thorax.js
Expand Up @@ -74,17 +74,30 @@ Thorax.Util = {
name = pathPrefix + name;
}
}
if (!object[type][name] && !ignoreErrors) {
var target = object[type],
value;
if (name.match(/\./)) {
var bits = name.split(/\./);
name = bits.pop();
bits.forEach(function(key) {
target = target[key];
});
} else {
value = target[name];
}
if (!target && !ignoreErrors) {
throw new Error(type + ': ' + name + ' does not exist.');
} else {
var value = object[type][name];
var value = target[name];
if (type === 'templates' && typeof value === 'string') {
value = object[type][name] = Handlebars.compile(value);
value = target[name] = Handlebars.compile(value);
}
return value;
}
},
getViewInstance: function(name, attributes) {
attributes['class'] && (attributes.className = attributes['class']);
attributes.tag && (attributes.tagName = attributes.tag);
if (typeof name === 'string') {
var klass = Thorax.Util.registryGet(Thorax, 'Views', name, false);
return klass.cid ? _.extend(klass, attributes || {}) : new klass(attributes);
Expand Down
128 changes: 128 additions & 0 deletions src/mobile.js
@@ -0,0 +1,128 @@
//Router
Thorax.Router = Backbone.Router.extend({
initialize: function() {
Backbone.history || (Backbone.history = new Backbone.History);
Backbone.history.on('route', onRoute, this);
//router does not have a built in destroy event
//but ViewController does
this.on('destroyed', function() {
Backbone.history.off('route', onRoute, this);
});
},
route: function(route, name, callback) {
//add a route:before event that is fired before the callback is called
return Backbone.Router.prototype.route.call(this, route, name, function() {
this.trigger.apply(this, ['route:before', name].concat(Array.prototype.slice.call(arguments)));
return callback.apply(this, arguments);
});
}
});

Thorax.Routers = {};
Thorax.Util.createRegistryWrapper(Thorax.Router, Thorax.Routers);

function onRoute(router, name) {
if (this === router) {
this.trigger.apply(this, ['route'].concat(Array.prototype.slice.call(arguments, 1)));
}
}

//layout
var layoutCidAttributeName = 'data-layout-cid';

Thorax.LayoutView = Thorax.View.extend({
render: function(output) {
//TODO: fixme, lumbar inserts templates after JS, most of the time this is fine
//but Application will be created in init.js (unlike most views)
//so need to put this here so the template will be picked up
var layoutTemplate;
if (this.name) {
layoutTemplate = Thorax.Util.registryGet(Thorax, 'templates', this.name, true);
}
//a template is optional in a layout
if (output || this.template || layoutTemplate) {
//but if present, it must have embedded an element containing layoutCidAttributeName
var response = Thorax.View.prototype.render.call(this, output || this.template || layoutTemplate);
ensureLayoutViewsTargetElement.call(this);
return response;
} else {
ensureLayoutCid.call(this);
}
},
setView: function(view, options) {
options = _.extend({
scroll: true,
destroy: true
}, options || {});
if (typeof view === 'string') {
view = new (Thorax.Util.registryGet(Thorax, 'Views', view, false));
}
this.ensureRendered();
var oldView = this._view;
if (view == oldView){
return false;
}
if (options.destroy) {
view._shouldDestroyOnNextSetView = true;
}
this.trigger('change:view:start', view, oldView, options);
oldView && oldView.trigger('deactivated', options);
view && view.trigger('activated', options);
if (oldView && oldView.el && oldView.el.parentNode) {
oldView.$el.remove();
}
//make sure the view has been rendered at least once
view && view.ensureRendered();
view && getLayoutViewsTargetElement.call(this).appendChild(view.el);
this._view = view;
oldView && oldView._shouldDestroyOnNextSetView && oldView.destroy();
this._view && this._view.trigger('ready', options);
this.trigger('change:view:end', view, oldView, options);
return view;
},

getView: function() {
return this._view;
}
});

Handlebars.registerHelper('layout', function(options) {
options.hash[layoutCidAttributeName] = this._view.cid;
return new Handlebars.SafeString(Thorax.Util.tag.call(this, options.hash, '', this));
});

function ensureLayoutCid() {
++this._renderCount;
//set the layoutCidAttributeName on this.$el if there was no template
this.$el.attr(layoutCidAttributeName, this.cid);
}

function ensureLayoutViewsTargetElement() {
if (!this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0]) {
throw new Error('No layout element found in ' + (this.name || this.cid));
}
}

function getLayoutViewsTargetElement() {
return this.$('[' + layoutCidAttributeName + '="' + this.cid + '"]')[0] || this.el[0] || this.el;
}

//ViewController
Thorax.ViewController = Thorax.LayoutView.extend();
_.extend(Thorax.ViewController.prototype, Thorax.Router.prototype, {
initialize: function() {
Thorax.Router.prototype.initialize.call(this);
//set the ViewController as the view on the parent
//if a parent was specified
this.on('route:before', function(router, name) {
if (this.parent && this.parent.getView) {
if (this.parent.getView() !== this) {
this.parent.setView(this, {
destroy: false
});
}
}
}, this);
this._bindRoutes();
}
});
19 changes: 16 additions & 3 deletions src/thorax.js
Expand Up @@ -52,17 +52,30 @@ Thorax.Util = {
name = pathPrefix + name;
}
}
if (!object[type][name] && !ignoreErrors) {
var target = object[type],
value;
if (name.match(/\./)) {
var bits = name.split(/\./);
name = bits.pop();
bits.forEach(function(key) {
target = target[key];
});
} else {
value = target[name];
}
if (!target && !ignoreErrors) {
throw new Error(type + ': ' + name + ' does not exist.');
} else {
var value = object[type][name];
var value = target[name];
if (type === 'templates' && typeof value === 'string') {
value = object[type][name] = Handlebars.compile(value);
value = target[name] = Handlebars.compile(value);
}
return value;
}
},
getViewInstance: function(name, attributes) {
attributes['class'] && (attributes.className = attributes['class']);
attributes.tag && (attributes.tagName = attributes.tag);
if (typeof name === 'string') {
var klass = Thorax.Util.registryGet(Thorax, 'Views', name, false);
return klass.cid ? _.extend(klass, attributes || {}) : new klass(attributes);
Expand Down
19 changes: 19 additions & 0 deletions test/src/test.js
Expand Up @@ -26,6 +26,25 @@ $(function() {
key: 'value'
});
equal(Thorax.Views['a-name'].prototype.key, 'value', 'registry will extend an existing class prototype');

//test nested
Thorax.Views.Outer = {
Inner: Thorax.View.extend({
template: 'inner'
}),
More: {
Nested: Thorax.View.extend({
template: 'nested'
})
}
};

var view = new Thorax.View({
template: '<p>{{view "Outer.Inner" tag="span"}}</p><div>{{view "Outer.More.Nested" tag="span"}}</div>'
});
view.render();
equal(view.$('p > span').html(), 'inner', 'test nested registryGet');
equal(view.$('div > span').html(), 'nested', 'test nested registryGet');
});

test("shouldFetch", function() {
Expand Down

0 comments on commit d1d97ad

Please sign in to comment.