UNPKG

rendr

Version:

Render your Backbone.js apps on the client and the server.

149 lines (126 loc) 3.68 kB
/** * This is the app instance that is shared between client and server. * The client also subclasses it for client-specific stuff. */ var Backbone = require('backbone'), _ = require('underscore'), Fetcher = require('./fetcher'), ModelUtils = require('./modelUtils'), isServer = (typeof window === 'undefined'), ClientRouter; if (!isServer) { ClientRouter = require('app/router'); Backbone.$ = window.$ || require('jquery'); } module.exports = Backbone.Model.extend({ defaults: { loading: false, templateEngine: 'handlebars', templateAdapter: 'rendr-handlebars' }, // Set keys to undefined so runtime V8 is happier templateAdapter: undefined, req: undefined, modelUtils: undefined, fetcher: undefined, /** * @shared */ constructor: function(attributes, options) { attributes = attributes || {}; this.options = options || {}; var entryPath = this.options.entryPath || ''; if (!isServer) { // the entry path must always be empty for the client entryPath = ''; } this.modelUtils = this.options.modelUtils || new ModelUtils(entryPath); /** * On the server-side, you can access the Express request, `req`. */ if (this.options.req) { this.req = this.options.req; } this.initializeTemplateAdapter(entryPath, attributes); /** * Instantiate the `Fetcher`, which is used on client and server. */ this.fetcher = new Fetcher({ app: this }); /** * Initialize the `ClientRouter` on the client-side. */ if (!isServer) { if (this.options.ClientRouter) { ClientRouter = this.options.ClientRouter; } new ClientRouter({ app: this, entryPath: entryPath, appViewClass: this.getAppViewClass(), rootPath: attributes.rootPath }); } Backbone.Model.apply(this, arguments); }, /** * @shared * * Initialize the `templateAdapter`, allowing application developers to use whichever * templating system they want. * * We can't use `this.get('templateAdapter')` here because `Backbone.Model`'s * constructor has not yet been called. */ initializeTemplateAdapter: function(entryPath, attributes) { if (this.options.templateAdapterInstance) { this.templateAdapter = this.options.templateAdapterInstance; } else { var templateAdapterModule = attributes.templateAdapter || this.defaults.templateAdapter, templateAdapterOptions = {entryPath: entryPath}, templateEngine = require(attributes.templateEngine || this.defaults.templateEngine); templateAdapterOptions = this.setTemplateFinder(templateAdapterOptions); this.templateAdapter = require(templateAdapterModule)(templateAdapterOptions, templateEngine); } }, /** * @shared * Override this in app/app to return a custom template finder */ getTemplateFinder: _.noop, /** * @shared */ setTemplateFinder: function(templateAdapterOptions) { if (_.isFunction(this.getTemplateFinder) && this.getTemplateFinder !== _.noop) { templateAdapterOptions.templateFinder = this.getTemplateFinder(); } return templateAdapterOptions; }, /** * @shared */ fetch: function() { this.fetcher.fetch.apply(this.fetcher, arguments); }, /** * @client */ getAppViewClass: function () { return require('../client/app_view'); }, /** * @client */ bootstrapData: function(modelMap, callback) { this.fetcher.bootstrapData(modelMap, callback); }, /** * @client */ start: function() { this.router.start(); this.trigger('start'); } });