templates
Version:
System for creating and managing template collections, and rendering templates with any node.js template engine. Can be used as the basis for creating a static site generator or blog framework.
251 lines (215 loc) • 5.83 kB
JavaScript
'use strict';
var Base = require('base-methods');
var utils = require('./utils');
/**
* Create an instance of `Views` with the given `options`.
*
* ```js
* var collection = new Views();
* collection.addView('foo', {content: 'bar'});
* ```
* @param {Object} `options`
* @api public
*/
function Views(options) {
Base.call(this);
options = options || {};
// decorate the instance
utils.renameKey(this);
utils.option(this);
// add constructors
this.define('List', options.List || require('./list'));
this.define('View', options.View || require('./view'));
this.define('loaded', false);
this.define('plugins', []);
this.isCollection = true;
this.queue = [];
this.views = {};
// if an instance of `List` of `Views` is passed, load it now
if (Array.isArray(options) || options instanceof this.List) {
this.options = options.options;
this.addList(options.items);
} else if (options instanceof Views) {
this.options = options.options;
this.addViews(options.views);
} else {
this.options = options;
}
}
Base.extend(Views);
/**
* Run a plugin on the collection instance. Plugins
* are invoked immediately upon creating the collection
* in the order in which they were defined.
*
* ```js
* collection.use(function(views) {
* // `views` is the instance, as is `this`
*
* // optionally return a function to be passed to
* // the `.use` method of each view created on the
* // instance
* return function(view) {
* // do stuff to each `view`
* };
* });
* ```
*
* @param {Function} `fn` Plugin function. If the plugin returns a function it will be passed to the `use` method of each view created on the instance.
* @return {Object} Returns the instance for chaining.
* @api public
*/
Views.prototype.use = function(fn) {
var plugin = fn.call(this, this, this.options);
if (typeof plugin === 'function') {
this.plugins.push(plugin);
}
this.emit('use');
return this;
};
/**
* Returns a new view, using the `View` class
* currently defined on the instance.
*
* ```js
* var view = app.view('foo', {conetent: '...'});
* // or
* var view = app.view({path: 'foo', conetent: '...'});
* ```
* @name .view
* @param {String|Object} `key` View key or object
* @param {Object} `value` If key is a string, value is the view object.
* @return {Object} returns the `view` object
* @api public
*/
utils.viewFactory(Views.prototype, 'view', 'View');
/**
* Set a view on the collection. This is identical to [addView](#addView)
* except `setView` does not emit an event for each view.
*
* ```js
* collection.setView('foo', {content: 'bar'});
* ```
*
* @param {String|Object} `key` View key or object
* @param {Object} `value` If key is a string, value is the view object.
* @developer This method is decorated onto the collection in the constructor using the `createView` utility method.
* @return {Object} returns the `view` instance.
* @api public
*/
Views.prototype.setView = function(key, value) {
var view = this.view(key, value);
this.views[view.key] = view;
return view;
};
/**
* Adds event emitting and custom loading to [setView](#setView).
*
* @param {String} `key`
* @param {Object} `value`
* @api public
*/
Views.prototype.addView = function(key, value) {
var args = [].slice.call(arguments);
this.emit.apply(this, ['addView'].concat(args));
var view = this.setView(key, value);
while (this.queue.length) {
this.setView(this.queue.shift());
}
return view;
};
/**
* Load multiple views onto the collection.
*
* ```js
* collection.addViews({
* 'a.html': {content: '...'},
* 'b.html': {content: '...'},
* 'c.html': {content: '...'}
* });
* ```
* @param {Object|Array} `views`
* @return {Object} returns the `collection` object
* @api public
*/
Views.prototype.addViews = function(views) {
if (Array.isArray(views)) {
return this.addList.apply(this, arguments);
}
if (arguments.length > 1 && utils.isView(arguments[1])) {
return this.addView.apply(this, arguments);
}
this.emit('addViews', views);
if (this.loaded) return this;
this.visit('addView', views);
return this;
};
/**
* Load an array of views onto the collection.
*
* ```js
* collection.addViews([
* {path: 'a.html', content: '...'},
* {path: 'b.html', content: '...'},
* {path: 'c.html', content: '...'}
* ]);
* ```
* @param {Object|Array} `views`
* @return {Object} returns the `collection` object
* @api public
*/
Views.prototype.addList = function(list) {
this.emit('addList', list);
if (this.loaded) return this;
if (!Array.isArray(list)) {
throw new TypeError('expected list to be an array.');
}
var len = list.length, i = -1;
while (++i < len) {
var view = list[i];
this.addView(view.path, view);
}
return this;
};
/**
* Get a view from the collection.
*
* ```js
* collection.getView('a.html');
* ```
* @param {String} `key` Key of the view to get.
* @return {Object}
* @api public
*/
Views.prototype.getView = function(key) {
return this.views[key] || this.views[this.renameKey(key)];
};
/**
* Set view types for the collection.
*
* @param {String} `plural` e.g. `pages`
* @param {Object} `options`
* @api private
*/
Views.prototype.viewType = function() {
this.options.viewType = utils.arrayify(this.options.viewType || []);
if (this.options.viewType.length === 0) {
this.options.viewType.push('renderable');
}
return this.options.viewType;
};
/**
* Get the number of views on the instance.
*/
utils.define(Views.prototype, 'count', {
get: function() {
return Object.keys(this.views).length;
},
set: function () {
throw new Error('count is a read-only getter and cannot be defined.');
}
});
/**
* Expose `Views`
*/
module.exports = Views;