API Docs for:
Show:

File: src\lib\docpad.coffee

##*
# The central module for DocPad
# @module DocPad
##

# =====================================
# This block *must* come first

# Important
pathUtil = require('path')
lazyRequire = require('lazy-require')
corePath = pathUtil.resolve(__dirname, '..', '..')

# Profile
if ('--profile' in process.argv)
	# Debug
	debugger

	# Nodetime
	if process.env.DOCPAD_PROFILER.indexOf('nodetime') isnt -1
		throw new Error('NODETIME_KEY environment variable is undefined')  unless process.env.NODETIME_KEY
		console.log 'Loading profiling tool: nodetime'
		require('lazy-require').sync 'nodetime', {cwd:corePath}, (err,nodetime) ->
			if err
				console.log 'Failed to load profiling tool: nodetime'
				console.log err.stack or err
			else
				nodetime.profile({
					accountKey: process.env.NODETIME_KEY
					appName: 'DocPad'
				})
				console.log 'Profiling with nodetime with account key:', process.env.NODETIME_KEY

	# Webkit Devtools
	if process.env.DOCPAD_PROFILER.indexOf('webkit-devtools-agent') isnt -1
		console.log 'Loading profiling tool: webkit-devtools-agent'
		require('lazy-require').sync 'webkit-devtools-agent', {cwd:corePath}, (err, agent) ->
			if err
				console.log 'Failed to load profiling tool: webkit-devtools-agent'
				console.log err.stack or err
			else
				agent.start()
				console.log "Profiling with webkit-devtools-agent on pid #{process.pid} at http://127.0.0.1:9999/"

	# V8 Profiler
	if process.env.DOCPAD_PROFILER.indexOf('v8-profiler') isnt -1
		console.log 'Loading profiling tool: v8-profiler'
		require('lazy-require').sync 'v8-profiler-helper', {cwd:corePath}, (err, profiler) ->
			if err
				console.log 'Failed to load profiling tool: v8-profiler'
				console.log err.stack or err
			else
				profiler.startProfile('docpad-profile')
				console.log "Profiling with v8-profiler"
			process.on 'exit', ->
				profiler.stopProfile('docpad-profile')


# =====================================
# Requires

# Standard Library
util     = require('util')

# External
queryEngine = require('query-engine')
{uniq, union, pick} = require('underscore')
CSON = require('cson')
balUtil = require('bal-util')
scandir = require('scandirectory')
extendr = require('extendr')
eachr = require('eachr')
typeChecker = require('typechecker')
ambi = require('ambi')
{TaskGroup} = require('taskgroup')
safefs = require('safefs')
safeps = require('safeps')
ignorefs = require('ignorefs')
rimraf = require('rimraf')
superAgent = require('superagent')
extractOptsAndCallback = require('extract-opts')
{EventEmitterGrouped} = require('event-emitter-grouped')

# Base
{Events,Model,Collection,QueryCollection} = require('./base')

# Utils
docpadUtil = require('./util')

# Models
FileModel = require('./models/file')
DocumentModel = require('./models/document')

# Collections
FilesCollection = require('./collections/files')
ElementsCollection = require('./collections/elements')
MetaCollection = require('./collections/meta')
ScriptsCollection = require('./collections/scripts')
StylesCollection = require('./collections/styles')

# Plugins
PluginLoader = require('./plugin-loader')
BasePlugin = require('./plugin')


# ---------------------------------
# Helpers

setImmediate = global?.setImmediate or process.nextTick  # node 0.8 b/c


# ---------------------------------
# Variables

isUser = docpadUtil.isUser()


###*
# Contains methods for managing the DocPad application.
# This includes managing a DocPad projects files and
# documents, watching directories, emitting events and
# managing the node.js/express.js web server.
# Extends https://github.com/bevry/event-emitter-grouped
#
# The class is instantiated in the docpad-server.js file
# which is the entry point for a DocPad application.
#
# 	new DocPad(docpadConfig, function(err, docpad) {
# 		if (err) {
# 			return docpadUtil.writeError(err);
# 		}
# 		return docpad.action(action, function(err) {
# 			if (err) {
# 				return docpadUtil.writeError(err);
# 			}
# 			return console.log('OK');
# 		});
# 	});
#
# @class Docpad
# @constructor
# @extends EventEmitterGrouped
###
class DocPad extends EventEmitterGrouped
	# Libraries
	# Here for legacy API reasons
	#@DocPad: DocPad
	#@Backbone: require('backbone')
	#@queryEngine: queryEngine

	# Allow for `DocPad.create()` as an alias for `new DocPad()`
	# Allow for `DocPad.createInstance()` as an alias for `new DocPad()` (legacy alias)
	@create: (args...) -> return new @(args...)
	@createInstance: (args...) -> return new @(args...)

	# Require a local DocPad file
	# Before v6.73.0 this allowed requiring of files inside src/lib, as well as files inside src
	# Now it only allows requiring of files inside src/lib as that makes more sense
	@require: (relativePath) ->
		# Absolute the path
		absolutePath = pathUtil.normalize(pathUtil.join(__dirname, relativePath))

		# Check if we are actually a local docpad file
		if absolutePath.replace(__dirname, '') is absolutePath
			throw new Error("docpad.require is limited to local docpad files only: #{relativePath}")

		# Require the path
		return require(absolutePath)


	# =================================
	# Variables

	# ---------------------------------
	# Modules

	# ---------------------------------
	# Base

	###*
	# Events class
	# https://github.com/docpad/docpad/blob/master/src/lib/base.coffee
	# @property {Object} Events
	###
	Events: Events
	###*
	# Model class
	# Extension of the Backbone Model class
	# http://backbonejs.org/#Model
	# https://github.com/docpad/docpad/blob/master/src/lib/base.coffee
	# @property {Object} Model
	###
	Model: Model

	###*
	# Collection class
	# Extension of the Backbone Collection class
	# https://github.com/docpad/docpad/blob/master/src/lib/base.coffee
	# http://backbonejs.org/#Collection
	# @property {Object} Collection
	###
	Collection: Collection

	###*
	# QueryCollection class
	# Extension of the Query Engine QueryCollection class
	# https://github.com/docpad/docpad/blob/master/src/lib/base.coffee
	# https://github.com/bevry/query-engine/blob/master/src/documents/lib/query-engine.js.coffee
	# @property {Object} QueryCollection
	###
	QueryCollection: QueryCollection

	# ---------------------------------
	# Models

	###*
	# File Model class
	# Extension of the Model class
	# https://github.com/docpad/docpad/blob/master/src/lib/models/file.coffee
	# @property {Object} FileModel
	###
	FileModel: FileModel

	###*
	# Document Model class
	# Extension of the File Model class
	# https://github.com/docpad/docpad/blob/master/src/lib/models/document.coffee
	# @property {Object} DocumentModel
	###
	DocumentModel: DocumentModel

	# ---------------------------------
	# Collections

	###*
	# Collection of files in a DocPad project
	# Extension of the QueryCollection class
	# https://github.com/docpad/docpad/blob/master/src/lib/collections/files.coffee
	# @property {Object} FilesCollection
	###
	FilesCollection: FilesCollection

	###*
	# Collection of elements in a DocPad project
	# Extension of the Collection class
	# https://github.com/docpad/docpad/blob/master/src/lib/collections/elements.coffee
	# @property {Object} ElementsCollection
	###
	ElementsCollection: ElementsCollection

	###*
	# Collection of metadata in a DocPad project
	# Extension of the ElementsCollection class
	# https://github.com/docpad/docpad/blob/master/src/lib/collections/meta.coffee
	# @property {Object} MetaCollection
	###
	MetaCollection: MetaCollection

	###*
	# Collection of JS script files in a DocPad project
	# Extension of the ElementsCollection class
	# https://github.com/docpad/docpad/blob/master/src/lib/collections/scripts.coffee
	# @property {Object} ScriptsCollection
	###
	ScriptsCollection: ScriptsCollection

	###*
	# Collection of CSS style files in a DocPad project
	# Extension of the ElementsCollection class
	# https://github.com/docpad/docpad/blob/master/src/lib/collections/styles.coffee
	# @property {Object} StylesCollection
	###
	StylesCollection: StylesCollection

	###*
	# Plugin Loader class
	# https://github.com/docpad/docpad/blob/master/src/lib/plugin-loader.coffee
	# Loads the DocPad plugins from the file system into
	# a DocPad project
	# @property {Object} PluginLoader
	###
	PluginLoader: PluginLoader

	###*
	# Base class for all DocPad plugins
	# https://github.com/docpad/docpad/blob/master/src/lib/plugin.coffee
	# @property {Object} BasePlugin
	###
	BasePlugin: BasePlugin

	# ---------------------------------
	# DocPad

	###*
	# DocPad's version number
	# @private
	# @property {Number} version
	###
	version: null

	###*
	# Get the DocPad version number
	# @method getVersion
	# @return {Number}
	###
	getVersion: ->
		@version ?= require(@packagePath).version
		return @version

	###*
	# Get the DocPad version string
	# @method getVersionString
	# @return {String}
	###
	getVersionString: ->
		if docpadUtil.isLocalDocPadExecutable()
			return util.format(@getLocale().versionLocal, @getVersion(), @corePath)
		else
			return util.format(@getLocale().versionGlobal, @getVersion(), @corePath)

	###*
	# The plugin version requirements
	# @property {String} pluginVersion
	###
	pluginVersion: '2'

	# Process getters
	###*
	# Get the process platform
	# @method getProcessPlatform
	# @return {Object}
	###
	getProcessPlatform: -> process.platform

	###*
	# Get the process version string
	# @method getProcessVersion
	# @return {String}
	###
	getProcessVersion: -> process.version.replace(/^v/,'')

	###*
	# The express.js server instance bound to DocPad.
	# http://expressjs.com
	# @private
	# @property {Object} serverExpress
	###
	serverExpress: null

	###*
	# The Node.js http server instance bound to DocPad
	# https://nodejs.org/api/http.html
	# @private
	# @property {Object} serverHttp
	###
	serverHttp: null

	###*
	# Get the DocPad express.js server instance and, optionally,
	# the node.js https server instance
	# @method getServer
	# @param {Boolean} [both=false]
	# @return {Object}
	###
	getServer: (both=false) ->
		{serverExpress,serverHttp} = @
		if both
			return {serverExpress, serverHttp}
		else
			return serverExpress

	###*
	# Set the express.js server and node.js http server
	# to bind to DocPad
	# @method setServer
	# @param {Object} servers
	###
	setServer: (servers) ->
		# Apply
		if servers.serverExpress and servers.serverHttp
			@serverExpress = servers.serverExpress
			@serverHttp = servers.serverHttp

		# Cleanup
		delete @config.serverHttp
		delete @config.serverExpress
		delete @config.server

	###*
	# Destructor. Close and destroy the node.js http server
	# @private
	# @method destroyServer
	###
	destroyServer: ->
		@serverHttp?.close()
		@serverHttp = null
		# @TODO figure out how to destroy the express server

	#
	###*
	# Internal property. The caterpillar logger instances bound to DocPad
	# @private
	# @property {Object} loggerInstances
	###
	loggerInstances: null

	###*
	# Get the caterpillar logger instance bound to DocPad
	# @method getLogger
	# @return {Object} caterpillar logger
	###
	getLogger: -> @loggerInstances?.logger

	###*
	# Get all the caterpillar logger instances bound to DocPad
	# @method getLoggers
	# @return {Object} collection of caterpillar loggers
	###
	getLoggers: -> @loggerInstances

	###*
	# Sets the caterpillar logger instances bound to DocPad
	# @method setLoggers
	# @param {Object} loggers
	# @return {Object} logger instances bound to DocPad
	###
	setLoggers: (loggers) ->
		if @loggerInstances
			@warn @getLocale().loggersAlreadyDefined
		else
			@loggerInstances = loggers
			@loggerInstances.logger.setConfig(dry:true)
			@loggerInstances.console.setConfig(dry:false).pipe(process.stdout)
		return loggers

	###*
	# Destructor. Destroy the caterpillar logger instances bound to DocPad
	# @private
	# @method {Object} destroyLoggers
	###
	destroyLoggers: ->
		if @loggerInstances
			for own key,value of @loggerInstances
				value.end()
		@

	###*
	# The action runner instance bound to docpad
	# @private
	# @property {Object} actionRunnerInstance
	###
	actionRunnerInstance: null

	###*
	# Get the action runner instance bound to docpad
	# @method getActionRunner
	# @return {Object} the action runner instance
	###
	getActionRunner: -> @actionRunnerInstance

	###*
	# Apply the passed DocPad action arguments
	# @method {Object} action
	# @param {Object} args
	# @return {Object}
	###
	action: (args...) -> docpadUtil.action.apply(@, args)


	###*
	# The error runner instance bound to DocPad
	# @property {Object} errorRunnerInstance
	###
	errorRunnerInstance: null

	###*
	# Get the error runner instance
	# @method {Object} getErrorRunner
	# @return {Object} the error runner instance
	###
	getErrorRunner: -> @errorRunnerInstance

	###*
	# The track runner instance bound to DocPad
	# @private
	# @property {Object} trackRunnerInstance
	###
	trackRunnerInstance: null

	###*
	# Get the track runner instance
	# @method getTrackRunner
	# @return {Object} the track runner instance
	###
	getTrackRunner: -> @trackRunnerInstance


	###*
	# Event Listing. String array of event names.
	# Whenever an event is created, it must be applied here to be available to plugins and configuration files
	# https://github.com/bevry/docpad/wiki/Events
	# @private
	# @property {Array} string array of event names
	###
	events: [
		'extendTemplateData'           # fired each load
		'extendCollections'            # fired each load
		'docpadLoaded'                 # fired multiple times, first time command line configuration hasn't been applied yet
		'docpadReady'                  # fired only once
		'docpadDestroy'                # fired once on shutdown
		'consoleSetup'                 # fired once
		'generateBefore'
		'populateCollectionsBefore'
		'populateCollections'
		'contextualizeBefore'
		'contextualizeAfter'
		'renderBefore'
		'renderCollectionBefore'
		'render'                       # fired for each extension conversion
		'renderDocument'               # fired for each document render, including layouts and render passes
		'renderCollectionAfter'
		'renderAfter'
		'writeBefore'
		'writeAfter'
		'generateAfter'
		'generated'
		'serverBefore'
		'serverExtend'
		'serverAfter'
		'notify'
	]

	###*
	# Get the list of available events
	# @method getEvents
	# @return {Object} string array of event names
	###
	getEvents: ->
		@events


	# ---------------------------------
	# Collections

	# Database collection

	###*
	# QueryEngine collection
	# @private
	# @property {Object} database
	###
	database: null

	###*
	# A FilesCollection of models updated
	# from the DocPad database after each regeneration.
	# @private
	# @property {Object} databaseTempCache FileCollection of models
	###
	databaseTempCache: null

	###*
	# Description for getDatabase
	# @method {Object} getDatabase
	###
	getDatabase: -> @database

	###*
	# Safe method for retrieving the database by
	# either returning the database itself or the temporary
	# database cache
	# @method getDatabaseSafe
	# @return {Object}
	###
	getDatabaseSafe: -> @databaseTempCache or @database

	###*
	# Destructor. Destroy the DocPad database
	# @private
	# @method destroyDatabase
	###
	destroyDatabase: ->
		if @database?
			@database.destroy()
			@database = null
		if @databaseTempCache?
			@databaseTempCache.destroy()
			@databaseTempCache = null
		@

	###*
	# Files by url. Used to speed up fetching
	# @private
	# @property {Object} filesByUrl
	###
	filesByUrl: null

	###*
	# Files by Selector. Used to speed up fetching
	# @private
	# @property {Object} filesBySelector
	###
	filesBySelector: null

	###*
	# Files by Out Path. Used to speed up conflict detection. Do not use for anything else
	# @private
	# @property {Object} filesByOutPath
	###
	filesByOutPath: null

	###*
	# Blocks
	# @private
	# @property {Object} blocks
	###
	blocks: null
	### {
		# A collection of meta elements
		meta: null  # Elements Collection

		# A collection of script elements
		scripts: null  # Scripts Collection

		# Collection of style elements
		styles: null  # Styles Collection
	} ###

	###*
	# Get a block by block name. Optionally clone block.
	# @method getBlock
	# @param {String} name
	# @param {Object} [clone]
	# @return {Object} block
	###
	getBlock: (name,clone) ->
		block = @blocks[name]
		if clone
			classname = name[0].toUpperCase()+name[1..]+'Collection'
			block = new @[classname](block.models)
		return block

	###*
	# Set a block by name and value
	# @method setBlock
	# @param {String} name
	# @param {Object} value
	###
	setBlock: (name,value) ->
		if @blocks[name]?
			@blocks[name].destroy()
			if value
				@blocks[name] = value
			else
				delete @blocks[name]
		else
			@blocks[name] = value
		@

	###*
	# Get all blocks
	# @method getBlocks
	# @return {Object} collection of blocks
	###
	getBlocks: -> @blocks

	###*
	# Set all blocks
	# @method setBlocks
	# @param {Object} blocks
	###
	setBlocks: (blocks) ->
		for own name,value of blocks
			@setBlock(name,value)
		@

	###*
	# Apply the passed function to each block
	# @method eachBlock
	# @param {Function} fn
	###
	eachBlock: (fn) ->
		eachr(@blocks, fn)
		@

	###*
	# Destructor. Destroy all blocks
	# @private
	# @method destroyBlocks
	###
	destroyBlocks: ->
		if @blocks
			for own name,block of @blocks
				block.destroy()
				@blocks[name] = null
		@

	###*
	# The DocPad collections
	# @private
	# @property {Object} collections
	###
	collections: null

	###*
	# Get a collection by collection name or key.
	# This is often accessed within the docpad.coffee
	# file or a layout/page via @getCollection.
	# Because getCollection returns a docpad collection,
	# a call to this method is often chained with a
	# QueryEngine style query.
	#
	# 	@getCollection('documents').findAllLive({relativeOutDirPath: 'posts'},[{date:-1}])
	#
	# @method getCollection
	# @param {String} value
	# @return {Object} collection
	###
	getCollection: (value) ->
		if value
			if typeof value is 'string'
				if value is 'database'
					return @getDatabase()

				else
					for collection in @collections
						if value in [collection.options.name, collection.options.key]
							return collection

			else
				for collection in @collections
					if value is collection
						return collection

		return null

	###*
	# Destroy a collection by collection name or key
	# @method destroyCollection
	# @param {String} value
	# @return {Object} description
	###
	destroyCollection: (value) ->
		if value
			if typeof value is 'string' and value isnt 'database'
				@collections = @collections.filter (collection) ->
					if value in [collection.options.name, collection.options.key]
						collection?.destroy()
						return false
					else
						return true

			else if value isnt @getDatabase()
				@collections = @collections.filter (collection) ->
					if value is collection
						collection?.destroy()
						return false
					else
						return true

		return null

	###*
	# Add a collection
	# @method addCollection
	# @param {Object} collection
	###
	addCollection: (collection) ->
		if collection and collection not in [@getDatabase(), @getCollection(collection)]
			@collections.push(collection)
		@

	###*
	# Set a name for a collection.
	# A collection can have multiple names
	#
	# The partials plugin (https://github.com/docpad/docpad-plugin-partials)
	# creates a live collection and passes this to setCollection with
	# the name 'partials'.
	#
	# 	# Add our partials collection
	#	docpad.setCollection('partials', database.createLiveChildCollection()
	#		.setQuery('isPartial', {
	#				$or:
	#					isPartial: true
	#					fullPath: $startsWith: config.partialsPath
	#		})
	#		.on('add', (model) ->
	#			docpad.log('debug', util.format(locale.addingPartial, model.getFilePath()))
	#			model.setDefaults(
	#				isPartial: true
	#				render: false
	#				write: false
	#			)
	#		)
	#	)
	#
	#
	# @method setCollection
	# @param {String} name the name to give to the collection
	# @param {Object} collection a DocPad collection
	###
	setCollection: (name, collection) ->
		if collection
			if name
				collection.options.name = name
				if @getCollection(name) isnt collection
					@destroyCollection(name)
			@addCollection(collection)
		else
			@destroyCollection(name)

	###*
	# Get the DocPad project's collections
	# @method getCollections
	# @return {Object} the collections
	###
	getCollections: ->
		return @collections

	###*
	# Set the DocPad project's collections
	# @method setCollections
	###
	setCollections: (collections) ->
		if Array.isArray(collections)
			for value in collections
				@addCollection(value)
		else
			for own name,value of collections
				@setCollection(name, value)
		@

	###*
	# Apply the passed function to each collection
	# @method eachCollection
	# @param {Function} fn
	###
	eachCollection: (fn) ->
		fn(@getDatabase(), 'database')
		for collection,index in @collections
			fn(collection, collection.options.name or collection.options.key or index)
		@

	###*
	# Destructor. Destroy the DocPad project's collections.
	# @private
	# @method destroyCollections
	###
	destroyCollections: ->
		if @collections
			for collection in @collections
				collection.destroy()
			@collections = []
		@


	# ---------------------------------
	# Collection Helpers

	###*
	# Get all the files in the DocPad database (will use live collections)
	# @method getFiles
	# @param {Object} query
	# @param {Object} sorting
	# @param {Object} paging
	# @return {Object} collection
	###
	getFiles: (query,sorting,paging) ->
		key = JSON.stringify({query, sorting, paging})
		collection = @getCollection(key)
		unless collection
			collection = @getDatabase().findAllLive(query, sorting, paging)
			collection.options.key = key
			@addCollection(collection)
		return collection


	###*
	# Get a single file based on a query
	# @method getFile
	# @param {Object} query
	# @param {Object} sorting
	# @param {Object} paging
	# @return {Object} a file
	###
	getFile: (query,sorting,paging) ->
		file = @getDatabase().findOne(query, sorting, paging)
		return file

	###*
	# Get files at a path
	# @method getFilesAtPath
	# @param {String} path
	# @param {Object} sorting
	# @param {Object} paging
	# @return {Object} files
	###
	getFilesAtPath: (path,sorting,paging) ->
		query = $or: [{relativePath: $startsWith: path}, {fullPath: $startsWith: path}]
		files = @getFiles(query, sorting, paging)
		return files

	###*
	# Get a file at a relative or absolute path or url
	# @method getFileAtPath
	# @param {String} path
	# @param {Object} sorting
	# @param {Object} paging
	# @return {Object} a file
	###
	getFileAtPath: (path,sorting,paging) ->
		file = @getDatabase().fuzzyFindOne(path, sorting, paging)
		return file


	# TODO: Does this still work???
	###*
	# Get a file by its url
	# @method getFileByUrl
	# @param {String} url
	# @param {Object} [opts={}]
	# @return {Object} a file
	###
	getFileByUrl: (url,opts={}) ->
		opts.collection ?= @getDatabase()
		file = opts.collection.get(@filesByUrl[url])
		return file


	###*
	# Get a file by its id
	# @method getFileById
	# @param {String} id
	# @param {Object} [opts={}]
	# @return {Object} a file
	###
	getFileById: (id,opts={}) ->
		opts.collection ?= @getDatabase()
		file = opts.collection.get(id)
		return file


	###*
	# Remove the query string from a url
	# Pathname convention taken from document.location.pathname
	# @method getUrlPathname
	# @param {String} url
	# @return {String}
	###
	getUrlPathname: (url) ->
		return url.replace(/\?.*/,'')

	###*
	# Get a file by its route and return
	# it to the supplied callback.
	# @method getFileByRoute
	# @param {String} url
	# @param {Object} next
	# @param {Error} next.err
	# @param {String} next.file
	###
	getFileByRoute: (url,next) ->
		# Prepare
		docpad = @

		# If we have not performed a generation yet then wait until the initial generation has completed
		if docpad.generated is false
			# Wait until generation has completed and recall ourselves
			docpad.once 'generated', ->
				return docpad.getFileByRoute(url, next)

			# hain
			return @

		# @TODO the above causes a signifcant delay when importing external documents (like tumblr data) into the database
		# we need to figure out a better way of doing this
		# perhaps it is via `writeSource: once` for imported documents
		# or providing an option to disable this so it forward onto the static handler instead

		# Prepare
		database = docpad.getDatabaseSafe()

		# Fetch
		cleanUrl = docpad.getUrlPathname(url)
		file = docpad.getFileByUrl(url, {collection:database}) or docpad.getFileByUrl(cleanUrl, {collection:database})

		# Forward
		next(null, file)

		# Chain
		@


	# TODO: What on earth is a selector?
	###*
	# Get a file by its selector
	# @method getFileBySelector
	# @param {Object} selector
	# @param {Object} [opts={}]
	# @return {Object} a file
	###
	getFileBySelector: (selector,opts={}) ->
		opts.collection ?= @getDatabase()
		file = opts.collection.get(@filesBySelector[selector])
		unless file
			file = opts.collection.fuzzyFindOne(selector)
			if file
				@filesBySelector[selector] = file.id
		return file


	# ---------------------------------
	# Skeletons


	###*
	# Skeletons Collection
	# @private
	# @property {Object} skeletonsCollection
	###
	skeletonsCollection: null

	###*
	# Get Skeletons
	# Get all the available skeletons with their details and
	# return this collection to the supplied callback.
	# @method getSkeletons
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.skeletonsCollection DocPad collection of skeletons
	# @return {Object} DocPad skeleton collection
	###
	getSkeletons: (next) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Check if we have cached locally
		if @skeletonsCollection?
			return next(null, @skeletonsCollection)

		# Fetch the skeletons from the exchange
		@skeletonsCollection = new Collection()
		@skeletonsCollection.comparator = queryEngine.generateComparator(position:1, name:1)
		@getExchange (err,exchange) ->
			# Check
			return next(err)  if err

			# Prepare
			index = 0

			# If we have the exchange data, then add the skeletons from it
			if exchange
				eachr exchange.skeletons, (skeleton, skeletonKey) ->
					skeleton.id ?= skeletonKey
					skeleton.name ?= skeletonKey
					skeleton.position ?= index
					docpad.skeletonsCollection.add(new Model(skeleton))
					++index

			# Add No Skeleton Option
			docpad.skeletonsCollection.add(new Model(
				id: 'none'
				name: locale.skeletonNoneName
				description: locale.skeletonNoneDescription
				position: index
			))

			# Return Collection
			return next(null, docpad.skeletonsCollection)
		@


	# ---------------------------------
	# Plugins


	###*
	# Plugins that are loading really slow
	# @property {Object} slowPlugins
	###
	slowPlugins: null  # {}

	###*
	# Loaded plugins indexed by name
	# @property {Object} loadedPlugins
	###
	loadedPlugins: null  # {}

	###*
	# A listing of all the available extensions for DocPad
	# @property {Object} exchange
	###
	exchange: null  # {}

	# -----------------------------
	# Paths

	###*
	# The DocPad directory
	# @property {String} corePath
	###
	corePath: corePath

	###*
	# The DocPad library directory
	# @private
	# @property {String} libPath
	###
	libPath: __dirname

	###*
	# The main DocPad file
	# @property {String} mainPath
	###
	mainPath: pathUtil.resolve(__dirname, 'docpad')

	###*
	# The DocPad package.json path
	# @property {String} packagePath
	###
	packagePath: pathUtil.resolve(__dirname, '..', '..', 'package.json')

	###*
	# The DocPad locale path
	# @property {String} localePath
	###
	localePath: pathUtil.resolve(__dirname, '..', '..', 'locale')

	###*
	# The DocPad debug log path (docpad-debug.log)
	# @property {String} debugLogPath
	###
	debugLogPath: pathUtil.join(process.cwd(), 'docpad-debug.log')

	###*
	# The User's configuration path (.docpad.cson)
	# @property {String} userConfigPath
	###
	userConfigPath: '.docpad.cson'

	# -----------------------------
	# Template Data


	###*
	# Description for initialTemplateData
	# @private
	# @property {Object} initialTemplateData
	###
	initialTemplateData: null  # {}

	###*
	# Plugin's Extended Template Data
	# @private
	# @property {Object} pluginsTemplateData
	###
	pluginsTemplateData: null  # {}

	###*
	# Get Complete Template Data
	# @method getTemplateData
	# @param {Object} userTemplateData
	# @return {Object} templateData
	###
	getTemplateData: (userTemplateData) ->
		# Prepare
		userTemplateData or= {}
		docpad = @
		locale = @getLocale()

		# Set the initial docpad template data
		@initialTemplateData ?=
			# Site Properties
			site: {}

			# Environment
			getEnvironment: ->
				return docpad.getEnvironment()

			# Environments
			getEnvironments: ->
				return docpad.getEnvironments()

			# Set that we reference other files
			referencesOthers: (flag) ->
				document = @getDocument()
				document.referencesOthers()
				return null

			# Get the Document
			getDocument: ->
				return @documentModel

			# Get a Path in respect to the current document
			getPath: (path,parentPath) ->
				document = @getDocument()
				path = document.getPath(path, parentPath)
				return path

			# Get Files
			getFiles: (query,sorting,paging) ->
				@referencesOthers()
				result = docpad.getFiles(query, sorting, paging)
				return result

			# Get another file's URL based on a relative path
			getFile: (query,sorting,paging) ->
				@referencesOthers()
				result = docpad.getFile(query,sorting,paging)
				return result

			# Get Files At Path
			getFilesAtPath: (path,sorting,paging) ->
				@referencesOthers()
				path = @getPath(path)
				result = docpad.getFilesAtPath(path, sorting, paging)
				return result

			# Get another file's model based on a relative path
			getFileAtPath: (relativePath) ->
				@referencesOthers()
				path = @getPath(relativePath)
				result = docpad.getFileAtPath(path)
				return result

			# Get a specific file by its id
			getFileById: (id) ->
				@referencesOthers()
				result = docpad.getFileById(id)
				return result

			# Get the entire database
			getDatabase: ->
				@referencesOthers()
				return docpad.getDatabase()

			# Get a pre-defined collection
			getCollection: (name) ->
				@referencesOthers()
				return docpad.getCollection(name)

			# Get a block
			getBlock: (name) ->
				return docpad.getBlock(name,true)

			# Include another file taking in a relative path
			include: (subRelativePath,strict=true) ->
				file = @getFileAtPath(subRelativePath)
				if file
					if strict and file.get('rendered') is false
						if docpad.getConfig().renderPasses is 1
							docpad.warn util.format(locale.renderedEarlyViaInclude, subRelativePath)
						return null
					return file.getOutContent()
				else
					err = new Error(util.format(locale.includeFailed, subRelativePath))
					throw err

		# Fetch our result template data
		templateData = extendr.extend({}, @initialTemplateData, @pluginsTemplateData, @getConfig().templateData, userTemplateData)

		# Add site data
		templateData.site.url or= @getSimpleServerUrl()
		templateData.site.date or= new Date()
		templateData.site.keywords or= []
		if typeChecker.isString(templateData.site.keywords)
			templateData.site.keywords = templateData.site.keywords.split(/,\s*/g)

		# Return
		templateData


	# -----------------------------
	# Locales

	###*
	# Determined locale
	# @private
	# @property {Object} locale
	###
	locale: null


	###*
	# Get the locale (language code and locale code)
	# @method getLocale
	# @return {Object} locale
	###
	getLocale: ->
		if @locale? is false
			config = @getConfig()
			codes = uniq [
				'en'
				safeps.getLanguageCode config.localeCode
				safeps.getLanguageCode safeps.getLocaleCode()
				safeps.getLocaleCode   config.localeCode
				safeps.getLocaleCode   safeps.getLocaleCode()
			]
			locales = (@loadLocale(code)  for code in codes)
			@locale = extendr.extend(locales...)

		return @locale

	###*
	# Load the locale
	# @method loadLocale
	# @param {String} code
	# @return {Object} locale
	###
	loadLocale: (code) ->
		# Check if it exists
		localeFilename = "#{code}.cson"
		localePath = pathUtil.join(@localePath, localeFilename)
		return null  unless safefs.existsSync(localePath)

		# Load it
		locale = CSON.parseCSONFile(localePath)

		# Log the error in the background and continue
		if locale instanceof Error
			locale.context = "Failed to parse the CSON locale file: #{localePath}"
			docpad.error(locale)  # @TODO: should this be a fatal error instead?
			return null

		# Success
		return locale


	# -----------------------------
	# Environments


	###*
	# Get the DocPad environment, eg: development,
	# production or static
	# @method getEnvironment
	# @return {String} the environment
	###
	getEnvironment: ->
		env = @getConfig().env or 'development'
		return env

	###*
	# Get the environments
	# @method getEnvironments
	# @return {Array} array of environment strings
	###
	getEnvironments: ->
		env = @getEnvironment()
		envs = env.split(/[, ]+/)
		return envs


	# -----------------------------
	# Configuration

	###*
	# Hash Key
	# The key that we use to hash some data before sending it to our statistic server
	# @private
	# @property {String} string constant
	###
	hashKey: '7>9}$3hP86o,4=@T'  # const

	###*
	# Website Package Configuration
	# @private
	# @property {Object} websitePackageConfig
	###
	websitePackageConfig: null  # {}

	###*
	# Merged Configuration
	# Merged in the order of:
	# - initialConfig
	# - userConfig
	# - websiteConfig
	# - instanceConfig
	# - environmentConfig
	# Use getConfig to retrieve this value
	# @private
	# @property {Object} config
	###
	config: null  # {}


	###*
	# Instance Configuration

	# @private
	# @property {Object} instanceConfig
	###
	instanceConfig: null  # {}

	###*
	# Website Configuration
	# Merged into the config property
	# @private
	# @property {Object} websiteConfig
	###
	websiteConfig: null  # {}

	###*
	# User Configuraiton
	# Merged into the config property
	# @private
	# @property {Object} userConfig
	###
	userConfig:
		# Name
		name: null

		# Email
		email: null

		# Username
		username: null

		# Subscribed
		subscribed: null

		# Subcribe Try Again
		# If our subscription has failed, when should we try again?
		subscribeTryAgain: null

		# Terms of Service
		tos: null

		# Identified
		identified: null

	###*
	# Initial Configuration. The default docpadConfig
	# settings that can be overridden in a project's docpad.coffee file.
	# Merged into the config property
	# @private
	# @property {Object} initialConfig
	###
	initialConfig:

		# -----------------------------
		# Plugins

		# Force re-install of all plugin dependencies
		force: false

		# Whether or not we should use the global docpad instance
		global: false

		# Whether or not we should enable plugins that have not been listed or not
		enableUnlistedPlugins: true

		# Plugins which should be enabled or not pluginName: pluginEnabled
		enabledPlugins: {}

		# Whether or not we should skip unsupported plugins
		skipUnsupportedPlugins: true

		# Whether or not to warn about uncompiled private plugins
		warnUncompiledPrivatePlugins: true

		# Configuration to pass to any plugins pluginName: pluginConfiguration
		plugins: {}


		# -----------------------------
		# Project Paths

		# The project directory
		rootPath: process.cwd()

		# The project's database cache path
		databaseCachePath: '.docpad.db'

		# The project's package.json path
		packagePath: 'package.json'

		# The project's configuration paths
		# Reads only the first one that exists
		# If you want to read multiple configuration paths, then point it to a coffee|js file that requires
		# the other paths you want and exports the merged config
		configPaths: [
			'docpad.js'
			'docpad.coffee'
			'docpad.json'
			'docpad.cson'
		]

		# Plugin directories to load
		pluginPaths: []

		# The project's plugins directory
		pluginsPaths: [
			'node_modules'
			'plugins'
		]

		# Paths that we should watch for reload changes in
		reloadPaths: []

		# Paths that we should watch for regeneration changes in
		regeneratePaths: []

		# The time to wait after a source file has changed before using it to regenerate
		regenerateDelay: 100

		# The time to wait before outputting the files we are waiting on
		slowFilesDelay: 20*1000

		# The project's out directory
		outPath: 'out'

		# The project's src directory
		srcPath: 'src'

		# The project's documents directories
		# relative to the srcPath
		documentsPaths: [
			'documents'
			'render'
		]

		# The project's files directories
		# relative to the srcPath
		filesPaths: [
			'files'
			'static'
			'public'
		]

		# The project's layouts directory
		# relative to the srcPath
		layoutsPaths: [
			'layouts'
		]

		# Ignored file patterns during directory parsing
		ignorePaths: false
		ignoreHiddenFiles: false
		ignoreCommonPatterns: true
		ignoreCustomPatterns: false

		# Watch options
		watchOptions: null


		# -----------------------------
		# Server

		# Port
		# The port that the server should use
		# Defaults to these environment variables:
		# - PORT — Heroku, Nodejitsu, Custom
		# - VCAP_APP_PORT — AppFog
		# - VMC_APP_PORT — CloudFoundry
		port: null

		# Hostname
		# The hostname we wish to listen to
		# Defaults to these environment variables:
		# HOSTNAME — Generic
		# Do not set to "localhost" it does not work on heroku
		hostname: null

		# Max Age
		# The caching time limit that is sent to the client
		maxAge: 86400000

		# Server
		# The Express.js server that we want docpad to use
		serverExpress: null
		# The HTTP server that we want docpad to use
		serverHttp: null

		# Extend Server
		# Whether or not we should extend the server with extra middleware and routing
		extendServer: true

		# Which middlewares would you like us to activate
		# The standard middlewares (bodyParser, methodOverride, express router)
		middlewareStandard: true
		# The standard bodyParser middleware
		middlewareBodyParser: true
		# The standard methodOverride middleware
		middlewareMethodOverride: true
		# The standard express router middleware
		middlewareExpressRouter: true
		# Our own 404 middleware
		middleware404: true
		# Our own 500 middleware
		middleware500: true


		# -----------------------------
		# Logging

		# Log Level
		# Which level of logging should we actually output
		logLevel: (if ('-d' in process.argv) then 7 else 6)

		# Catch uncaught exceptions
		catchExceptions: true

		# Report Errors
		# Whether or not we should report our errors back to DocPad
		# By default it is only enabled if we are not running inside a test
		reportErrors: process.argv.join('').indexOf('test') is -1

		# Report Statistics
		# Whether or not we should report statistics back to DocPad
		# By default it is only enabled if we are not running inside a test
		reportStatistics: process.argv.join('').indexOf('test') is -1

		# Color
		# Whether or not our terminal output should have color
		# `null` will default to what the terminal supports
		color: null


		# -----------------------------
		# Other

		# Utilise the database cache
		databaseCache: false  # [false, true, 'write']

		# Detect Encoding
		# Should we attempt to auto detect the encoding of our files?
		# Useful when you are using foreign encoding (e.g. GBK) for your files
		detectEncoding: false

		# Render Single Extensions
		# Whether or not we should render single extensions by default
		renderSingleExtensions: false

		# Render Passes
		# How many times should we render documents that reference other documents?
		renderPasses: 1

		# Offline
		# Whether or not we should run in offline mode
		# Offline will disable the following:
		# - checkVersion
		# - reportErrors
		# - reportStatistics
		offline: false

		# Check Version
		# Whether or not to check for newer versions of DocPad
		checkVersion: false

		# Welcome
		# Whether or not we should display any custom welcome callbacks
		welcome: false

		# Prompts
		# Whether or not we should display any prompts
		prompts: false

		# Progress
		# Whether or not we should display any progress bars
		# Requires prompts being true, and log level 6 or above
		progress: true

		# Powered By DocPad
		# Whether or not we should include DocPad in the Powered-By meta header
		# Please leave this enabled as it is a standard practice and promotes DocPad in the web eco-system
		poweredByDocPad: true

		# Helper Url
		# Used for subscribing to newsletter, account information, and statistics etc
		# Helper's source-code can be found at: https://github.com/docpad/helper
		helperUrl: if true then 'http://helper.docpad.org/' else 'http://localhost:8000/'

		# Safe Mode
		# If enabled, we will try our best to sandbox our template rendering so that they cannot modify things outside of them
		# Not yet implemented
		safeMode: false

		# Template Data
		# What data would you like to expose to your templates
		templateData: {}

		# Collections
		# A hash of functions that create collections
		collections: {}

		# Events
		# A hash of event handlers
		events: {}

		# Regenerate Every
		# Performs a regenerate every x milliseconds, useful for always having the latest data
		regenerateEvery: false

		# Regerenate Every Options
		# The generate options to use on the regenerate every call
		regenerateEveryOptions:
			populate: true
			partial:  false


		# -----------------------------
		# Environment Configuration

		# Locale Code
		# The code we shall use for our locale (e.g. en, fr, etc)
		localeCode: null

		# Environment
		# Whether or not we are in production or development
		# Separate environments using a comma or a space
		env: null

		# Environments
		# Environment specific configuration to over-ride the global configuration
		environments:
			development:
				# Always refresh from server
				maxAge: false

				# Only do these if we are running standalone (aka not included in a module)
				checkVersion: isUser
				welcome: isUser
				prompts: isUser

	###*
	# Regenerate Timer
	# When config.regenerateEvery is set to a value, we create a timer here
	# @private
	# @property {Object} regenerateTimer
	###
	regenerateTimer: null

	###*
	# Get the DocPad configuration. Commonly
	# called within the docpad.coffee file or within
	# plugins to access application specific configurations.
	# 	serverExtend: (opts) ->
			# Extract the server from the options
			{server} = opts
			docpad = @docpad

			# As we are now running in an event,
			# ensure we are using the latest copy of the docpad configuraiton
			# and fetch our urls from it
			latestConfig = docpad.getConfig()
			oldUrls = latestConfig.templateData.site.oldUrls or []
			newUrl = latestConfig.templateData.site.url

			# Redirect any requests accessing one of our sites oldUrls to the new site url
			server.use (req,res,next) ->
				...
	# @method getConfig
	# @return {Object} the DocPad configuration object
	###
	getConfig: ->
		return @config or {}

	###*
	# Get the port that DocPad is listening on (eg 9778)
	# @method getPort
	# @return {Number} the port number
	###
	getPort: ->
		return @getConfig().port ? require('hostenv').PORT ? 9778

	###*
	# Get the Hostname
	# @method getHostname
	# @return {String}
	###
	getHostname: ->
		return @getConfig().hostname ? require('hostenv').HOSTNAME ? '0.0.0.0'

	###*
	# Get address
	# @method getServerUrl
	# @param {Object} [opts={}]
	# @return {String}
	###
	getServerUrl: (opts={}) ->
		opts.hostname ?= @getHostname()
		opts.port ?= @getPort()
		opts.simple ?= false
		if opts.simple is true and opts.hostname in ['0.0.0.0', '::', '::1']
			return "http://127.0.0.1:#{opts.port}"
		else
			return "http://#{opts.hostname}:#{opts.port}"

	###*
	# Get simple server URL (changes 0.0.0.0, ::, and ::1 to 127.0.0.1)
	# @method getSimpleServerUrl
	# @param {Object} [opts={}]
	# @param {Boolean} [opts.simple=true]
	# @return {String}
	###
	getSimpleServerUrl: (opts={}) ->
		opts.simple = true
		return @getServerUrl(opts)


	# =================================
	# Initialization Functions

	###*
	# Constructor method. Sets up the DocPad instance.
	# next(err)
	# @method constructor
	# @param {Object} instanceConfig
	# @param {Function} next callback
	# @param {Error} next.err
	###
	constructor: (instanceConfig,next) ->
		# Prepare
		[instanceConfig,next] = extractOptsAndCallback(instanceConfig, next)
		docpad = @

		# Create our own custom TaskGroup class for DocPad
		# That will listen to tasks as they execute and provide debugging information
		@TaskGroup = class extends TaskGroup
			constructor: ->
				# Prepare
				super
				tasks = @

				# Listen to executing tasks and output their progress
				tasks.on 'started', ->
					config = tasks.getConfig()
					name = tasks.getNames()
					progress = config.progress
					if progress
						totals = tasks.getItemTotals()
						progress.step(name).total(totals.total).setTick(totals.completed)
					else
						docpad.log('debug', name+' > started')

				# Listen to executing tasks and output their progress
				tasks.on 'item.add', (item) ->
					config = tasks.getConfig()
					name = item.getNames()
					progress = config.progress
					if progress
						totals = tasks.getItemTotals()
						progress.step(name).total(totals.total).setTick(totals.completed)
					else
						docpad.log('debug', name+' > added')

				# Listen to executing tasks and output their progress
				tasks.on 'item.started', (item) ->
					config = tasks.getConfig()
					name = item.getNames()
					progress = config.progress
					if progress
						totals = tasks.getItemTotals()
						progress.step(name).total(totals.total).setTick(totals.completed)
					else
						docpad.log('debug', name+' > started')

				# Listen to executing tasks and output their progress
				tasks.on 'item.done', (item, err) ->
					config = tasks.getConfig()
					name = item.getNames()
					progress = config.progress
					if progress
						totals = tasks.getItemTotals()
						progress.step(name).total(totals.total).setTick(totals.completed)
					else
						docpad.log('debug', name+' > done')

				# Chain
				@

		# Binders
		# Using this over coffescript's => on class methods, ensures that the method length is kept
		for methodName in """
			action
			log warn error fatal inspector notify track identify subscribe checkRequest
			serverMiddlewareRouter serverMiddlewareHeader serverMiddleware404 serverMiddleware500
			destroyWatchers
			""".split(/\s+/)
			@[methodName] = @[methodName].bind(@)

		# Allow DocPad to have unlimited event listeners
		@setMaxListeners(0)

		# Setup configuration event wrappers
		configEventContext = {docpad}  # here to allow the config event context to persist between event calls
		@getEvents().forEach (eventName) ->
			# Bind to the event
			docpad.on eventName, (opts,next) ->
				eventHandler = docpad.getConfig().events?[eventName]
				# Fire the config event handler for this event, if it exists
				if typeChecker.isFunction(eventHandler)
					args = [opts,next]
					ambi(eventHandler.bind(configEventContext), args...)
				# It doesn't exist, so lets continue
				else
					next()

		# Create our action runner
		@actionRunnerInstance = @TaskGroup.create('action runner').whenDone (err) ->
			docpad.error(err)  if err

		# Create our track runner
		@trackRunnerInstance = @TaskGroup.create('track runner').whenDone (err) ->
			if err and docpad.getDebugging()
				locale = docpad.getLocale()
				docpad.warn(locale.trackError, err)

		# Initialize the loggers
		if (loggers = instanceConfig.loggers)
			delete instanceConfig.loggers
		else
			# Create
			logger = new (require('caterpillar').Logger)(lineOffset: 2)

			# console
			loggerConsole = logger
				.pipe(
					new (require('caterpillar-filter').Filter)
				)
				.pipe(
					new (require('caterpillar-human').Human)
				)

			# Apply
			loggers = {logger, console:loggerConsole}

		# Apply the loggers
		safefs.unlink(@debugLogPath, -> )  # Remove the old debug log file
		@setLoggers(loggers)  # Apply the logger streams
		@setLogLevel(instanceConfig.logLevel ? @initialConfig.logLevel)  # Set the default log level

		# Log to bubbled events
		@on 'log', (args...) ->
			docpad.log.apply(@,args)

		# Dereference and initialise advanced variables
		# we deliberately ommit initialTemplateData here, as it is setup in getTemplateData
		@slowPlugins = {}
		@loadedPlugins = {}
		@exchange = {}
		@pluginsTemplateData = {}
		@instanceConfig = {}
		@collections = []
		@blocks = {}
		@filesByUrl = {}
		@filesBySelector = {}
		@filesByOutPath = {}
		@database = new FilesCollection(null, {name:'database'})
			.on('remove', (model,options) ->
				# Skip if we are not a writeable file
				return  if model.get('write') is false

				# Delete the urls
				for url in model.get('urls') or []
					delete docpad.filesByUrl[url]

				# Ensure we regenerate anything (on the next regeneration) that was using the same outPath
				outPath = model.get('outPath')
				if outPath
					updatedModels = docpad.database.findAll({outPath})
					updatedModels.remove(model)
					updatedModels.each (model) ->
						model.set('mtime': new Date())

					# Log
					docpad.log('debug', 'Updated mtime for these models due to remove of a similar one', updatedModels.pluck('relativePath'))

				# Return safely
				return true
			)
			.on('add change:urls', (model) ->
				# Skip if we are not a writeable file
				return  if model.get('write') is false

				# Delete the old urls
				for url in model.previous('urls') or []
					delete docpad.filesByUrl[url]

				# Add the new urls
				for url in model.get('urls')
					docpad.filesByUrl[url] = model.cid

				# Return safely
				return true
			)
			.on('add change:outPath', (model) ->
				# Skip if we are not a writeable file
				return  if model.get('write') is false

				# Check if we have changed our outPath
				previousOutPath = model.previous('outPath')
				if previousOutPath
					# Ensure we regenerate anything (on the next regeneration) that was using the same outPath
					previousModels = docpad.database.findAll(outPath:previousOutPath)
					previousModels.remove(model)
					previousModels.each (model) ->
						model.set('mtime': new Date())

					# Log
					docpad.log('debug', 'Updated mtime for these models due to addition of a similar one', previousModels.pluck('relativePath'))

					# Update the cache entry with another file that has the same outPath or delete it if there aren't any others
					previousModelId = docpad.filesByOutPath[previousOutPath]
					if previousModelId is model.id
						if previousModels.length
							docpad.filesByOutPath[previousOutPath] = previousModelId
						else
							delete docpad.filesByOutPath[previousOutPath]

				# Update the cache entry and fetch the latest if it was already set
				if (outPath = model.get('outPath'))
					existingModelId = docpad.filesByOutPath[outPath] ?= model.id
					if existingModelId isnt model.id
						existingModel = docpad.database.get(existingModelId)
						if existingModel
							# We have a conflict, let the user know
							modelPath = model.get('fullPath') or (model.get('relativePath')+':'+model.id)
							existingModelPath = existingModel.get('fullPath') or (existingModel.get('relativePath')+':'+existingModel.id)
							docpad.warn util.format(docpad.getLocale().outPathConflict, outPath, modelPath, existingModelPath)
						else
							# There reference was old, update it with our new one
							docpad.filesByOutPath[outPath] = model.id

				# Return safely
				return true
			)
		@userConfig = extendr.dereference(@userConfig)
		@initialConfig = extendr.dereference(@initialConfig)

		# Extract action
		if instanceConfig.action?
			action = instanceConfig.action
		else
			action = 'load ready'

		# Check if we want to perform an action
		if action
			@action action, instanceConfig, (err) ->
				if next?
					next(err, docpad)
				else if err
					docpad.fatal(err)
		else
			next?(null, docpad)

		# Chain
		@

	###*
	# Destructor. Destroy the DocPad instance
	# This is an action, and should be called as such
	# E.g. docpad.action('destroy', next)
	# @method destroy
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	destroy: (opts, next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @

		# Destroy Regenerate Timer
		docpad.destroyRegenerateTimer()

		# Wait one second to wait for any logging to complete
		docpadUtil.wait 1000, ->

			# Destroy Plugins
			docpad.emitSerial 'docpadDestroy', (err) ->
				# Check
				return next?(err)  if err

				# Destroy Plugins
				docpad.destroyPlugins()

				# Destroy Server
				docpad.destroyServer()

				# Destroy Watchers
				docpad.destroyWatchers()

				# Destroy Blocks
				docpad.destroyBlocks()

				# Destroy Collections
				docpad.destroyCollections()

				# Destroy Database
				docpad.destroyDatabase()

				# Destroy Logging
				docpad.destroyLoggers()

				# Destroy Process Listners
				process.removeListener('uncaughtException', docpad.error)

				# Destroy DocPad Listeners
				docpad.removeAllListeners()

				# Forward
				return next?()

		# Chain
		@

	###*
	# Emit event, serial
	# @private
	# @method emitSerial
	# @param {String} eventName
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	emitSerial: (eventName, opts, next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		locale = docpad.getLocale()

		# Log
		docpad.log 'debug', util.format(locale.emittingEvent, eventName)

		# Emit
		super eventName, opts, (err) ->
			# Check
			return next(err)  if err

			# Log
			docpad.log 'debug', util.format(locale.emittedEvent, eventName)

			# Forward
			return next(err)

		# Chain
		@

	###*
	# Emit event, parallel
	# @private
	# @method emitParallel
	# @param {String} eventName
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	emitParallel: (eventName, opts, next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		locale = docpad.getLocale()

		# Log
		docpad.log 'debug', util.format(locale.emittingEvent, eventName)

		# Emit
		super eventName, opts, (err) ->
			# Check
			return next(err)  if err

			# Log
			docpad.log 'debug', util.format(locale.emittedEvent, eventName)

			# Forward
			return next(err)

		# Chain
		@


	# =================================
	# Helpers

	###*
	# Get the ignore options for the DocPad project
	# @method getIgnoreOpts
	# @return {Array} string array of ignore options
	###
	getIgnoreOpts: ->
		return pick(@config, ['ignorePaths', 'ignoreHiddenFiles', 'ignoreCommonPatterns', 'ignoreCustomPatterns'])

	###*
	# Is the supplied path ignored?
	# @method isIgnoredPath
	# @param {String} path
	# @param {Object} [opts={}]
	# @return {Boolean}
	###
	isIgnoredPath: (path,opts={}) ->
		opts = extendr.extend(@getIgnoreOpts(), opts)
		return ignorefs.isIgnoredPath(path, opts)

	###*
	# Scan directory
	# @method scandir
	# @param {Object} [opts={}]
	###
	#NB: How does this work? What is returned?
	#Does it require a callback (next) passed as
	#one of the options
	scandir: (opts={}) ->
		opts = extendr.extend(@getIgnoreOpts(), opts)
		return scandir(opts)

	###*
	# Watch Directory. Wrapper around the Bevry watchr
	# module (https://github.com/bevry/watchr). Used
	# internally by DocPad to watch project documents
	# and files and then activate the regeneration process
	# when any of those items are updated.
	#
	# Although it is possible to pass a range of options to watchdir
	# in practice these options are provided as part of
	# the DocPad config object with a number of default options
	# specified in the DocPad config.
	# @method watchdir
	# @param {Object} [opts={}]
	# @param {String} [opts.path] a single path to watch.
	# @param {Array} [opts.paths] an array of paths to watch.
	# @param {Function} [opts.listener] a single change listener to fire when a change occurs.
	# @param {Array} [opts.listeners] an array of listeners.
	# @param {Function} [opts.next] callback.
	# @param {Object} [opts.stat] a file stat object to use for the path, instead of fetching a new one.
	# @param {Number} [opts.interval=5007] for systems that poll to detect file changes, how often should it poll in millseconds.
	# @param {Number} [opts.catupDelay=200] handles system swap file deletions and renaming
	# @param {Array} [opts.preferredMethods=['watch','watchFile'] which order should we prefer our watching methods to be tried?.
	# @param {Boolean} [opts.followLinks=true] follow symlinks, i.e. use stat rather than lstat.
	# @param {Boolean|Array} [opts.ignorePaths=false] an array of full paths to ignore.
	# @param {Boolean|Array} [opts.ignoreHiddenFiles=false] whether or not to ignored files which filename starts with a ".".
	# @param {Boolean} [opts.ignoreCommonPatterns=true] whether or not to ignore common undesirable file patterns (e.g. .svn, .git, .DS_Store, thumbs.db, etc).
	# @param {Boolean|Array} [opts.ignoreCustomPatterns=null] any custom ignore patterns that you would also like to ignore along with the common patterns.
	# @return {Object} the watcher
	###
	watchdir: (opts={}) ->
		opts = extendr.extend(@getIgnoreOpts(), opts, @config.watchOptions)
		return require('watchr').watch(opts)


	# =================================
	# Setup and Loading

	###*
	# DocPad is ready. Peforms the tasks needed after DocPad construction
	# and DocPad has loaded. Triggers the docpadReady event.
	# next(err,docpadInstance)
	# @private
	# @method ready
	# @param {Object} [opts]
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.docpadInstance
	###
	ready: (opts,next) ->
		# Prepare
		[instanceConfig,next] = extractOptsAndCallback(instanceConfig,next)
		docpad = @
		config = @getConfig()
		locale = @getLocale()

		# Render Single Extensions
		@DocumentModel::defaults.renderSingleExtensions = config.renderSingleExtensions

		# Version Check
		@compareVersion()

		# Welcome Prepare
		if @getDebugging()
			pluginsList = ("#{pluginName} v#{@loadedPlugins[pluginName].version}"  for pluginName in Object.keys(@loadedPlugins).sort()).join(', ')
		else
			pluginsList = Object.keys(@loadedPlugins).sort().join(', ')

		# Welcome Output
		docpad.log 'info', util.format(locale.welcome, @getVersionString())
		docpad.log 'notice', locale.welcomeDonate
		docpad.log 'info', locale.welcomeContribute
		docpad.log 'info', util.format(locale.welcomePlugins, pluginsList)
		docpad.log 'info', util.format(locale.welcomeEnvironment, @getEnvironment())

		# Prepare
		tasks = new @TaskGroup 'ready tasks', next:(err) ->
			# Error?
			return docpad.error(err)  if err

			# All done, forward our DocPad instance onto our creator
			return next?(null,docpad)

		tasks.addTask 'welcome event', (complete) ->
			# No welcome
			return complete()  unless config.welcome

			# Welcome
			docpad.emitSerial('welcome', {docpad}, complete)

		tasks.addTask 'track', (complete) ->
			# Identify
			return docpad.identify(complete)

		tasks.addTask 'emit docpadReady', (complete) ->
			docpad.emitSerial('docpadReady', {docpad}, complete)

		# Run tasks
		tasks.run()

		# Chain
		@

	###*
	# Performs the merging of the passed configuration objects
	# @private
	# @method mergeConfigurations
	# @param {Object} configPackages
	# @param {Object} configsToMerge
	###
	mergeConfigurations: (configPackages,configsToMerge) ->
		# Prepare
		envs = @getEnvironments()

		# Figure out merging
		for configPackage in configPackages
			continue  unless configPackage
			configsToMerge.push(configPackage)
			for env in envs
				envConfig = configPackage.environments?[env]
				configsToMerge.push(envConfig)  if envConfig

		# Merge
		extendr.safeDeepExtendPlainObjects(configsToMerge...)

		# Chain
		@

	###*
	# Set the instance configuration
	# by merging the properties of the passed object
	# with the existing DocPad instanceConfig object
	# @private
	# @method setInstanceConfig
	# @param {Object} instanceConfig
	###
	setInstanceConfig: (instanceConfig) ->
		# Merge in the instance configurations
		if instanceConfig
			logLevel = @getLogLevel()
			extendr.safeDeepExtendPlainObjects(@instanceConfig, instanceConfig)
			extendr.safeDeepExtendPlainObjects(@config, instanceConfig)  if @config  # @TODO document why there is the if
			@setLogLevel(instanceConfig.logLevel)  if instanceConfig.logLevel and instanceConfig.logLevel isnt logLevel
		@

	###*
	# Set the DocPad configuration object.
	# Performs a number of tasks, including
	# merging the pass instanceConfig with DocPad's
	# other config objects.
	# next(err,config)
	# @private
	# @method setConfig
	# @param {Object} instanceConfig
	# @param {Object} next
	# @param {Error} next.err
	# @param {Object} next.config
	###
	setConfig: (instanceConfig,next) ->
		# Prepare
		[instanceConfig,next] = extractOptsAndCallback(instanceConfig,next)
		docpad = @
		locale = @getLocale()

		# Apply the instance configuration, generally we won't have it at this level
		# as it would have been applied earlier the load step
		@setInstanceConfig(instanceConfig)  if instanceConfig

		# Apply the environment
		# websitePackageConfig.env is left out of the detection here as it is usually an object
		# that is already merged with our process.env by the environment runner
		# rather than a string which is the docpad convention
		@config.env = @instanceConfig.env or @websiteConfig.env or @initialConfig.env or process.env.NODE_ENV

		# Merge configurations
		configPackages = [@initialConfig, @userConfig, @websiteConfig, @instanceConfig]
		configsToMerge = [@config]
		docpad.mergeConfigurations(configPackages, configsToMerge)

		# Extract and apply the server
		@setServer extendr.safeShallowExtendPlainObjects({
			serverHttp: @config.serverHttp
			serverExpress: @config.serverExpress
		},  @config.server)

		# Extract and apply the logger
		@setLogLevel(@config.logLevel)

		# Resolve any paths
		@config.rootPath = pathUtil.resolve(@config.rootPath)
		@config.outPath = pathUtil.resolve(@config.rootPath, @config.outPath)
		@config.srcPath = pathUtil.resolve(@config.rootPath, @config.srcPath)
		@config.databaseCachePath = pathUtil.resolve(@config.rootPath, @config.databaseCachePath)
		@config.packagePath = pathUtil.resolve(@config.rootPath, @config.packagePath)

		# Resolve Documents, Files, Layouts paths
		for type in ['documents','files','layouts']
			typePaths = @config[type+'Paths']
			for typePath,key in typePaths
				typePaths[key] = pathUtil.resolve(@config.srcPath, typePath)

		# Resolve Plugins paths
		for type in ['plugins']
			typePaths = @config[type+'Paths']
			for typePath,key in typePaths
				typePaths[key] = pathUtil.resolve(@config.rootPath, typePath)

		# Bind the error handler, so we don't crash on errors
		process.removeListener('uncaughtException', @error)
		@removeListener('error', @error)
		if @config.catchExceptions
			process.setMaxListeners(0)
			process.on('uncaughtException', @error)
			@on('error', @error)

		# Prepare the Post Tasks
		postTasks = new @TaskGroup 'setConfig post tasks', next:(err) ->
			return next(err, docpad.config)

		###
		postTasks.addTask 'lazy depedencnies: encoding', (complete) =>
			return complete()  unless @config.detectEncoding
			return lazyRequire 'encoding', {cwd:corePath, stdio:'inherit'}, (err) ->
				docpad.warn(locale.encodingLoadFailed)  if err
				return complete()
		###

		postTasks.addTask 'load plugins', (complete) ->
			docpad.loadPlugins(complete)

		postTasks.addTask 'extend collections', (complete) ->
			docpad.extendCollections(complete)

		postTasks.addTask 'fetch plugins templateData', (complete) ->
			docpad.emitSerial('extendTemplateData', {templateData:docpad.pluginsTemplateData}, complete)

		postTasks.addTask 'fire the docpadLoaded event', (complete) ->
			docpad.emitSerial('docpadLoaded', complete)

		# Fire post tasks
		postTasks.run()

		# Chain
		@


	###*
	# Load the various configuration files from the
	# file system. Set the instanceConfig.
	# next(err,config)
	# @private
	# @method load
	# @param {Object} instanceConfig
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.config
	###
	load: (instanceConfig,next) ->
		# Prepare
		[instanceConfig,next] = extractOptsAndCallback(instanceConfig,next)
		docpad = @
		locale = @getLocale()
		instanceConfig or= {}

		# Reset non persistant configurations
		@websitePackageConfig = {}
		@websiteConfig = {}
		@config = {}

		# Merge in the instance configurations
		@setInstanceConfig(instanceConfig)

		# Prepare the Load Tasks
		preTasks = new @TaskGroup 'load tasks', next:(err) =>
			return next(err)  if err
			return @setConfig(next)

		preTasks.addTask 'normalize the userConfigPath', (complete) =>
			safeps.getHomePath (err,homePath) =>
				return complete(err)  if err
				dropboxPath = pathUtil.resolve(homePath, 'Dropbox')
				safefs.exists dropboxPath, (dropboxPathExists) =>
					# @TODO: Implement checks here for
					# https://github.com/bevry/docpad/issues/799
					userConfigDirPath = if dropboxPathExists then dropboxPath else homePath
					@userConfigPath = pathUtil.resolve(userConfigDirPath, @userConfigPath)
					return complete()

		preTasks.addTask "load the user's configuration", (complete) =>
			configPath = @userConfigPath
			docpad.log 'debug', util.format(locale.loadingUserConfig, configPath)
			@loadConfigPath {configPath}, (err,data) =>
				return complete(err)  if err

				# Apply loaded data
				extendr.extend(@userConfig, data or {})

				# Done
				docpad.log 'debug', util.format(locale.loadingUserConfig, configPath)
				return complete()

		preTasks.addTask "load the anonymous user's configuration", (complete) =>
			# Ignore if username is already identified
			return complete()  if @userConfig.username

			# User is anonymous, set their username to the hashed and salted mac address
			require('getmac').getMac (err,macAddress) =>
				if err or !macAddress
					docpad.warn(locale.macError, err)
					return complete()

				# Hash with salt
				try
					macAddressHash = require('crypto').createHmac('sha1', docpad.hashKey).update(macAddress).digest('hex')
				catch err
					return complete()  if err

				# Apply
				if macAddressHash
					@userConfig.name ?= "MAC #{macAddressHash}"
					@userConfig.username ?= macAddressHash

				# Next
				return complete()

		preTasks.addTask "load the website's package data", (complete) =>
			rootPath = pathUtil.resolve(@instanceConfig.rootPath or @initialConfig.rootPath)
			configPath = pathUtil.resolve(rootPath, @instanceConfig.packagePath or @initialConfig.packagePath)
			docpad.log 'debug', util.format(locale.loadingWebsitePackageConfig, configPath)
			@loadConfigPath {configPath}, (err,data) =>
				return complete(err)  if err
				data or= {}

				# Apply loaded data
				@websitePackageConfig = data

				# Done
				docpad.log 'debug', util.format(locale.loadedWebsitePackageConfig, configPath)
				return complete()

		preTasks.addTask "read the .env file if it exists", (complete) =>
			rootPath = pathUtil.resolve(@instanceConfig.rootPath or @websitePackageConfig.rootPath or @initialConfig.rootPath)
			configPath = pathUtil.resolve(rootPath, '.env')
			docpad.log 'debug', util.format(locale.loadingEnvConfig, configPath)
			safefs.exists configPath, (exists) ->
				return complete()  unless exists
				require('envfile').parseFile configPath, (err,data) ->
					return complete(err)  if err
					for own key,value of data
						process.env[key] = value
					docpad.log 'debug', util.format(locale.loadingEnvConfig, configPath)
					return complete()

		preTasks.addTask "load the website's configuration", (complete) =>
			docpad.log 'debug', util.format(locale.loadingWebsiteConfig)
			rootPath = pathUtil.resolve(@instanceConfig.rootPath or @initialConfig.rootPath)
			configPaths = @instanceConfig.configPaths or @initialConfig.configPaths
			for configPath, index in configPaths
				configPaths[index] = pathUtil.resolve(rootPath, configPath)
			@loadConfigPath {configPaths}, (err,data) =>
				return complete(err)  if err
				data or= {}

				# Apply loaded data
				extendr.extend(@websiteConfig, data)

				# Done
				docpad.log 'debug', util.format(locale.loadedWebsiteConfig)
				return complete()

		# Run the load tasks synchronously
		preTasks.run()

		# Chain
		@


	# =================================
	# Configuration

	###*
	# Update user configuration with the passed data
	# @method updateUserConfig
	# @param {Object} [data={}]
	# @param {Function} next
	# @param {Error} next.err
	###
	updateUserConfig: (data={},next) ->
		# Prepare
		[data,next] = extractOptsAndCallback(data,next)
		docpad = @
		userConfigPath = @userConfigPath

		# Apply back to our loaded configuration
		# does not apply to @config as we would have to reparse everything
		# and that appears to be an imaginary problem
		extendr.extend(@userConfig, data)  if data

		# Convert to CSON
		CSON.createCSONString @userConfig, (err, userConfigString) ->
			if err
				err.context = "Failed to create the CSON string for the user configuration"
				return next(err)

			# Write it
			safefs.writeFile userConfigPath, userConfigString, 'utf8', (err) ->
				# Forward
				return next(err)

		# Chain
		@

	###*
	# Load a configuration url.
	# @method loadConfigUrl
	# @param {String} configUrl
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.parsedData
	###
	loadConfigUrl: (configUrl,next) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Log
		docpad.log 'debug', util.format(locale.loadingConfigUrl, configUrl)

		# Read the URL
		superAgent
			.get(configUrl)
			.timeout(30*1000)
			.end (err,res) ->
				# Check
				return next(err)  if err

				# Read the string using CSON
				CSON.parseCSONString(res.text, next)

		# Chain
		@


	###*
	# Load the configuration from a file path
	# passed as one of the options (opts.configPath) or
	# from DocPad's configPaths
	# @private
	# @method loadConfigPath
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.parsedData
	###
	loadConfigPath: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		locale = @getLocale()

		# Prepare
		load = (configPath) ->
			# Check
			return next()  unless configPath

			# Log
			docpad.log 'debug', util.format(locale.loadingConfigPath, configPath)

			# Check that it exists
			safefs.exists configPath, (exists) ->
				return next()  unless exists

				# Prepare CSON Options
				csonOptions =
					cson: true
					json: true
					coffeescript: true
					javascript: true

				# Read the path using CSON
				CSON.requireFile configPath, csonOptions, (err, data) ->
					if err
						err.context = util.format(locale.loadingConfigPathFailed, configPath)
						return next(err)

					# Check if the data is a function, if so, then execute it as one
					while typeChecker.isFunction(data)
						try
							data = data(docpad)
						catch err
							return next(err)
					unless typeChecker.isObject(data)
						err = new Error("Loading the configuration #{docpad.inspector configPath} returned an invalid result #{docpad.inspector data}")
						return next(err)  if err

					# Return the data
					return next(null, data)

		# Check
		if opts.configPath
			load(opts.configPath)
		else
			@getConfigPath opts, (err,configPath) ->
				load(configPath)

		# Chain
		@

	###*
	# Get config paths and check that those
	# paths exist
	# @private
	# @method getConfigPath
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @param {String} next.path
	###
	getConfigPath: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()
		result = null

		# Ensure array
		opts.configPaths ?= config.configPaths
		opts.configPaths = [opts.configPaths]  unless typeChecker.isArray(opts.configPaths)

		tasks = new @TaskGroup 'getConfigPath tasks', next:(err) ->
			return next(err, result)

		# Determine our configuration path
		opts.configPaths.forEach (configPath) ->
			tasks.addTask "Checking if [#{configPath}] exists", (complete) ->
				return complete()  if result
				safefs.exists configPath, (exists) ->
					if exists
						result = configPath
						tasks.clear()
						complete()
					else
						complete()

		# Run them synchronously
		tasks.run()

		# Chain
		@


	###*
	# Extend collecitons. Create DocPad's
	# standard (documents, files
	# layouts) and special (generate, referencesOthers,
	# hasLayout, html, stylesheet) collections. Set blocks
	# @private
	# @method extendCollections
	# @param {Function} next
	# @param {Error} next.err
	###
	extendCollections: (next) ->
		# Prepare
		docpad = @
		docpadConfig = @getConfig()
		locale = @getLocale()
		database = @getDatabase()

		# Standard Collections
		@setCollections({
			# Standard Collections
			documents: database.createLiveChildCollection()
				.setQuery('isDocument', {
					render: true
					write: true
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingDocument, model.getFilePath()))
				)
			files: database.createLiveChildCollection()
				.setQuery('isFile', {
					render: false
					write: true
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingFile, model.getFilePath()))
				)
			layouts: database.createLiveChildCollection()
				.setQuery('isLayout', {
					$or:
						isLayout: true
						fullPath: $startsWith: docpadConfig.layoutsPaths
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingLayout, model.getFilePath()))
					model.setDefaults({
						isLayout: true
						render: false
						write: false
					})
				)

			# Special Collections
			generate: database.createLiveChildCollection()
				.setQuery('generate', {
					dynamic: false
					ignored: false
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingGenerate, model.getFilePath()))
				)
			referencesOthers: database.createLiveChildCollection()
				.setQuery('referencesOthers', {
					dynamic: false
					ignored: false
					referencesOthers: true
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingReferencesOthers, model.getFilePath()))
				)
			hasLayout: database.createLiveChildCollection()
				.setQuery('hasLayout', {
					dynamic: false
					ignored: false
					layout: $exists: true
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingHasLayout, model.getFilePath()))
				)
			html: database.createLiveChildCollection()
				.setQuery('isHTML', {
					write: true
					outExtension: 'html'
				})
				.on('add', (model) ->
					docpad.log('debug', util.format(locale.addingHtml, model.getFilePath()))
				)
			stylesheet: database.createLiveChildCollection()
				.setQuery('isStylesheet', {
					write: true
					outExtension: 'css'
				})
		})

		# Blocks
		@setBlocks({
			meta: new MetaCollection()
			scripts: new ScriptsCollection()
			styles: new StylesCollection()
		})

		# Custom Collections Group
		tasks = new @TaskGroup "extendCollections tasks", concurrency:0, next:(err) ->
			docpad.error(err)  if err
			docpad.emitSerial('extendCollections', next)

		# Cycle through Custom Collections
		eachr docpadConfig.collections or {}, (fn,name) ->
			if !name or !typeChecker.isString(name)
				err = new Error("Inside your DocPad configuration you have a custom collection with an invalid name of: #{docpad.inspector name}")
				docpad.error(err)
				return

			if !fn or !typeChecker.isFunction(fn)
				err = new Error("Inside your DocPad configuration you have a custom collection called #{docpad.inspector name} with an invalid method of: #{docpad.inspector fn}")
				docpad.error(err)
				return

			tasks.addTask "creating the custom collection: #{name}", (complete) ->
				# Init
				ambi [fn.bind(docpad), fn], database, (err, collection) ->
					# Check for error
					if err
						docpad.error(err)
						return complete()

					# Check the type of the collection
					else unless collection instanceof QueryCollection
						docpad.warn util.format(locale.errorInvalidCollection, name)
						return complete()

					# Make it a live collection
					collection.live(true)  if collection

					# Apply the collection
					docpad.setCollection(name, collection)
					return complete()

		# Run Custom collections
		tasks.run()

		# Chain
		@


	###*
	# Reset collections. Perform a complete clean of our collections
	# @private
	# @method resetCollections
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	resetCollections: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		database = docpad.getDatabase()

		# Make it as if we have never generated before
		docpad.generated = false

		# Perform a complete clean of our collections
		database.reset([])
		meta = @getBlock('meta').reset([])
		scripts = @getBlock('scripts').reset([])
		styles = @getBlock('styles').reset([])
		# ^ Backbone.js v1.1 changes the return values of these, however we change that in our Element class
		# because if we didn't, all our skeletons would fail

		# Add default block entries
		meta.add("""<meta name="generator" content="DocPad v#{docpad.getVersion()}" />""")  if docpad.getConfig().poweredByDocPad isnt false

		# Reset caches
		@filesByUrl = {}
		@filesBySelector = {}
		@filesByOutPath = {}

		# Chain
		next()
		@


	###*
	# Initialise git repo
	# @private
	# @method initGitRepo
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.results
	###
	initGitRepo: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()

		# Extract
		opts.cwd ?= config.rootPath
		opts.output ?= @getDebugging()

		# Forward
		safeps.initGitRepo(opts, next)

		# Chain
		@

	###*
	# Init node modules
	# @private
	# @method initNodeModules
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.results
	###
	initNodeModules: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()

		# Extract
		opts.cwd ?= config.rootPath
		opts.output ?= docpad.getDebugging()
		opts.force ?= if config.offline then false else true
		# ^ @todo this line causes --force to be added, when it shouldn't be
		opts.args ?= []
		opts.args.push('--force')  if config.force
		opts.args.push('--no-registry')  if config.offline

		# Log
		docpad.log('info', 'npm install')  if opts.output

		# Forward
		safeps.initNodeModules(opts, next)

		# Chain
		@

	###*
	# Fix node package versions
	# Combat to https://github.com/npm/npm/issues/4587#issuecomment-35370453
	# @private
	# @method fixNodePackageVersions
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	fixNodePackageVersions: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()

		# Extract
		opts.packagePath ?= config.packagePath

		# Read and replace
		safefs.readFile opts.packagePath, (err,buffer) ->
			data = buffer.toString()
			data = data.replace(/("docpad(?:.*?)": ")\^/g, '$1~')
			safefs.writeFile opts.packagePath, data, (err) ->
				return next(err)

		# Chain
		@


	###*
	# Install node module. Same as running
	# 'npm install' through the command line
	# @private
	# @method installNodeModule
	# @param {Array} names
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.result
	###
	installNodeModule: (names,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()

		# Extract
		opts.cwd ?= config.rootPath
		opts.args ?= []
		if docpad.getDebugging()
			opts.stdio ?= 'inherit'

		opts.global ?= false
		opts.global = ['--global']             if opts.global is true
		opts.global = [opts.global]            if opts.global and Array.isArray(opts.global) is false

		opts.save ?= !opts.global
		opts.save = ['--save']                 if opts.save is true
		opts.save = [opts.save]                if opts.save and Array.isArray(opts.save) is false

		# Command
		command = ['npm', 'install']

		# Names
		names = names.split(/[,\s]+/)  unless typeChecker.isArray(names)
		names.forEach (name) ->
			# Check
			return  unless name

			# Ensure latest if version isn't specfied
			name += '@latest'  if name.indexOf('@') is -1

			# Push the name to the commands
			command.push(name)

		# Arguments
		command.push(opts.args...)
		command.push('--force')           if config.force
		command.push('--no-registry')     if config.offline
		command.push(opts.save...)        if opts.save
		command.push(opts.global...)      if opts.global

		# Log
		docpad.log('info', command.join(' '))  if opts.output

		# Forward
		safeps.spawn(command, opts, next)

		# Chain
		@


	###*
	# Uninstall node module. Same as running
	# 'npm uninstall' through the command line
	# @private
	# @method uninstallNodeModule
	# @param {Array} names
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.result
	###
	uninstallNodeModule: (names,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		config = @getConfig()

		# Extract
		opts.cwd ?= config.rootPath
		opts.output ?= docpad.getDebugging()
		opts.args ?= []

		opts.global ?= false
		opts.global = ['--global']             if opts.global is true
		opts.global = [opts.global]            if opts.global and Array.isArray(opts.global) is false

		opts.save ?= !opts.global
		opts.save = ['--save', '--save-dev']   if opts.save is true
		opts.save = [opts.save]                if opts.save and Array.isArray(opts.save) is false

		# Command
		command = ['npm', 'uninstall']

		# Names
		names = names.split(/[,\s]+/)  unless typeChecker.isArray(names)
		command.push(names...)

		# Arguments
		command.push(opts.args...)
		command.push(opts.save...)        if opts.save
		command.push(opts.global...)      if opts.global

		# Log
		docpad.log('info', command.join(' '))  if opts.output

		# Forward
		safeps.spawn(command, opts, next)

		# Chain
		@



	# =================================
	# Logging

	###*
	# Set the log level
	# @private
	# @method setLogLevel
	# @param {Number} level
	###
	setLogLevel: (level) ->
		@getLogger().setConfig({level})
		if level is 7
			loggers = @getLoggers()
			if loggers.debug? is false
				loggers.debug = loggers.logger
					.pipe(
						new (require('caterpillar-human').Human)(color:false)
					)
					.pipe(
						require('fs').createWriteStream(@debugLogPath)
					)
		@

	###*
	# Get the log level
	# @method getLogLevel
	# @return {Number} the log level
	###
	getLogLevel: ->
		return @getConfig().logLevel

	###*
	# Are we debugging?
	# @method getDebugging
	# @return {Boolean}
	###
	getDebugging: ->
		return @getLogLevel() is 7


	###*
	# Handle a fatal error
	# @private
	# @method fatal
	# @param {Object} err
	###
	fatal: (err) ->
		docpad = @
		config = @getConfig()

		# Check
		return @  unless err

		# Handle
		@error(err)

		# Even though the error would have already been logged by the above
		# Ensure it is definitely outputted in the case the above fails
		docpadUtil.writeError(err)

		# Destroy DocPad
		@destroy()

		# Chain
		@


	###*
	# Inspect. Converts object to JSON string. Wrapper around nodes util.inspect method.
	# Can't use the inspect namespace as for some silly reason it destroys everything
	# @method inspector
	# @param {Object} obj
	# @param {Object} opts
	# @return {String} JSON string of passed object
	###
	inspector: (obj, opts) ->
		opts ?= {}
		opts.colors ?= @getConfig().color
		return docpadUtil.inspect(obj, opts)

	###*
	# Log arguments
	# @property {Object} log
	# @param {Mixed} args...
	###
	log: (args...) ->
		# Log
		logger = @getLogger() or console
		logger.log.apply(logger, args)

		# Chain
		@


	###*
	# Create an error object
	# @method createError
	# @param {Object} err
	# @param {Object} opts
	# @return {Object} the error
	###
	# @TODO: Decide whether or not we should track warnings
	# Previously we didn't, but perhaps it would be useful
	# If the statistics gets polluted after a while, we will remove it
	# Ask @balupton to check the stats after March 30th 2015
	createError: (err, opts) ->
		# Prepare
		opts ?= {}
		opts.level ?= err.level ? 'error'
		opts.track ?= err.track ? true
		opts.tracked ?= err.tracked ? false
		opts.log ?= err.log ? true
		opts.logged ?= err.logged ? false
		opts.notify ?= err.notify ? true
		opts.notified ?= err.notified ? false
		opts.context ?= err.context  if err.context?

		# Ensure we have an error object
		err = new Error(err)  unless err.stack

		# Add our options to the error object
		for own key,value of opts
			err[key] ?= value

		# Return the error
		return err


	###*
	# Create an error (tracks it) and log it
	# @method error
	# @param {Object} err
	# @param {Object} [level='err']
	###
	error: (err, level='err') ->
		# Prepare
		docpad = @

		# Create the error and track it
		err = @createError(err, {level})

		# Track the error
		@trackError(err)

		# Log the error
		@logError(err)

		# Notify the error
		@notifyError(err)

		# Chain
		@

	###*
	# Log an error
	# @method logError
	# @param {Object} err
	###
	logError: (err) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Track
		if err and err.log isnt false and err.logged isnt true
			err = @createError(err, {logged:true})
			occured =
				if err.level in ['warn', 'warning']
					locale.warnOccured
				else
					locale.errorOccured
			message =
				if err.context
					err.context+locale.errorFollows
				else
					occured
			message += '\n\n'+err.stack.toString().trim()
			message += '\n\n'+locale.errorSubmission
			docpad.log(err.level, message)

		# Chain
		@


	###*
	# Track an error in the background
	# @private
	# @method trackError
	# @param {Object} err
	###
	trackError: (err) ->
		# Prepare
		docpad = @
		config = @getConfig()

		# Track
		if err and err.track isnt false and err.tracked isnt true and config.offline is false and config.reportErrors is true
			err = @createError(err, {tracked:true})
			data = {}
			data.message = err.message
			data.stack = err.stack.toString().trim()  if err.stack
			data.config = config
			data.env = process.env
			docpad.track('error', data)

		# Chain
		@

	###*
	# Notify error
	# @private
	# @method notifyError
	# @param {Object} err
	###
	notifyError: (err) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Check
		if err.notify isnt false and err.notified isnt true
			err.notified = true
			occured =
				if err.level in ['warn', 'warning']
					locale.warnOccured
				else
					locale.errorOccured
			docpad.notify(err.message, {title:occured})

		# Chain
		@

	###*
	# Log an error of level 'warn'
	# @method warn
	# @param {String} message
	# @param {Object} err
	# @return {Object} description
	###
	warn: (message, err) ->
		# Handle
		if err
			err.context = message
			err.level = 'warn'
			@error(err)
		else
			err =
				if message instanceof Error
					message
				else
					new Error(message)
			err.level = 'warn'
			@error(err)

		# Chain
		@


	###*
	# Send a notify event to plugins (like growl)
	# @method notify
	# @param {String} message
	# @param {Object} [opts={}]
	###
	notify: (message,opts={}) ->
		# Prepare
		docpad = @

		# Emit
		docpad.emitSerial 'notify', {message,opts}, (err) ->
			docpad.error(err)  if err

		# Chain
		@


	###*
	# Check Request
	# @private
	# @method checkRequest
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.res
	###
	checkRequest: (next) ->
		next ?= @error.bind(@)
		return (err,res) ->
			# Check
			return next(err, res)  if err

			# Check
			if res.body?.success is false or res.body?.error
				err = new Error(res.body.error or 'unknown request error')  # @TODO localise this
				return next(err, res)

			# Success
			return next(null, res)


	###*
	# Subscribe to the DocPad email list.
	# @private
	# @method subscribe
	# @param {Function} next
	# @param {Error} next.err
	###
	subscribe: (next) ->
		# Prepare
		config = @getConfig()

		# Check
		if config.offline is false
			if @userConfig?.email
				# Data
				data = {}
				data.email = @userConfig.email  # required
				data.name = @userConfig.name or null
				data.username = @userConfig.username or null

				# Apply
				superAgent
					.post(config.helperUrl)
					.type('json').set('Accept', 'application/json')
					.query(
						method: 'add-subscriber'
					)
					.send(data)
					.timeout(30*1000)
					.end @checkRequest next
			else
				err = new Error('Email not provided')  # @TODO localise this
				next?(err)
		else
			next?()

		# Chain
		@

	###*
	# Track
	# @private
	# @method track
	# @param {String} name
	# @param {Object} [things={}]
	# @param {Function} next
	# @param {Error} next.err
	###
	track: (name,things={},next) ->
		# Prepare
		docpad = @
		config = @getConfig()

		# Check
		if config.offline is false and config.reportStatistics
			# Data
			data = {}
			data.userId = @userConfig.username or null
			data.event = name
			data.properties = things

			# Things
			things.websiteName = @websitePackageConfig.name  if @websitePackageConfig?.name
			things.platform = @getProcessPlatform()
			things.environment = @getEnvironment()
			things.version = @getVersion()
			things.nodeVersion = @getProcessVersion()

			# Plugins
			eachr docpad.loadedPlugins, (value,key) ->
				things['plugin-'+key] = value.version or true

			# Apply
			trackRunner = docpad.getTrackRunner()
			trackRunner.addTask 'track task', (complete) ->
				superAgent
					.post(config.helperUrl)
					.type('json').set('Accept', 'application/json')
					.query(
						method: 'analytics'
						action: 'track'
					)
					.send(data)
					.timeout(30*1000)
					.end docpad.checkRequest (err) ->
						next?(err)
						complete(err)  # we pass the error here, as if we error, we want to stop all tracking

			# Execute the tracker tasks
			trackRunner.run()
		else
			next?()

		# Chain
		@

	###*
	# Identify DocPad user
	# @private
	# @method identify
	# @param {Function} next
	# @param {Error} next.err
	###
	identify: (next) ->
		# Prepare
		docpad = @
		config = @getConfig()

		# Check
		if config.offline is false and config.reportStatistics and @userConfig?.username
			# Data
			data = {}
			data.userId = @userConfig.username  # required
			data.traits = things = {}

			# Things
			now = new Date()
			things.username = @userConfig.username  # required
			things.email = @userConfig.email or null
			things.name = @userConfig.name or null
			things.lastLogin = now.toISOString()
			things.lastSeen = now.toISOString()
			things.countryCode = safeps.getCountryCode()
			things.languageCode = safeps.getLanguageCode()
			things.platform = @getProcessPlatform()
			things.version = @getVersion()
			things.nodeVersion = @getProcessVersion()

			# Is this a new user?
			if docpad.userConfig.identified isnt true
				# Update
				things.created = now.toISOString()

				# Create the new user
				docpad.getTrackRunner().addTask 'create new user', (complete) ->
					superAgent
						.post(config.helperUrl)
						.type('json').set('Accept', 'application/json')
						.query(
							method: 'analytics'
							action: 'identify'
						)
						.send(data)
						.timeout(30*1000)
						.end docpad.checkRequest (err) ->
							# Save the changes with these
							docpad.updateUserConfig({identified:true}, complete)

			# Or an existing user?
			else
				# Update the existing user's information witht he latest
				docpad.getTrackRunner().addTask 'update user', (complete) ->
					superAgent
						.post(config.helperUrl)
						.type('json').set('Accept', 'application/json')
						.query(
							method: 'analytics'
							action: 'identify'
						)
						.send(data)
						.timeout(30*1000)
						.end docpad.checkRequest complete

		# Chain
		next?()
		@


	# =================================
	# Models and Collections

	# ---------------------------------
	# b/c compat functions

	###*
	# Create file model. Calls
	# {{#crossLink "DocPad/createModel:method"}}{{/crossLink}}
	# with the 'file' modelType.
	# @method createFile
	# @param {Object} [attrs={}]
	# @param {Object} [opts={}]
	# @return {Object} FileModel
	###
	createFile: (attrs={},opts={}) ->
		opts.modelType = 'file'
		return @createModel(attrs, opts)

	###*
	# Create document model. Calls
	# {{#crossLink "DocPad/createModel:method"}}{{/crossLink}}
	# with the 'document' modelType.
	# @method createDocument
	# @param {Object} [attrs={}]
	# @param {Object} [opts={}]
	# @return {Object} DocumentModel
	###
	createDocument: (attrs={},opts={}) ->
		opts.modelType = 'document'
		return @createModel(attrs, opts)


	###*
	# Parse the files directory and
	# return a files collection to
	# the passed callback
	# @method parseFileDirectory
	# @param {Object} [opts={}]
	# @param {Function} next callback
	# @param {Error} next.err
	# @param {Object} next.files files collection
	###
	parseFileDirectory: (opts={},next) ->
		opts.modelType ?= 'file'
		opts.collection ?= @getDatabase()
		return @parseDirectory(opts, next)

	###*
	# Parse the documents directory and
	# return a documents collection to
	# the passed callback.
	#
	# The partials plugin (https://github.com/docpad/docpad-plugin-partials)
	# uses this method to load a collection of
	# files from the partials directory.
	#
	# 	docpad.parseDocumentDirectory({path: config.partialsPath}, next)
	#
	# @method parseDocumentDirectory
	# @param {Object} [opts={}]
	# @param {String} [opts.modelType='document']
	# @param {Object} [opts.collection=docpad.database]
	# @param {Object} [opts.path]
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.files files collection of documents
	###
	parseDocumentDirectory: (opts={},next) ->
		opts.modelType ?= 'document'
		opts.collection ?= @getDatabase()
		return @parseDirectory(opts, next)


	# ---------------------------------
	# Standard functions


	###*
	# Attach events to a document model.
	# @private
	# @method attachModelEvents
	# @param {Object} model
	###
	attachModelEvents: (model) ->
		# Prepare
		docpad = @

		# Only attach events if we haven't already done so
		if model.attachedDocumentEvents isnt true
			model.attachedDocumentEvents = true

			# Attach document events
			if model.type is 'document'
				# Clone
				model.on 'clone', (clonedModel) ->
					docpad.attachModelEvents(clonedModel)

				# Render
				model.on 'render', (args...) ->
					docpad.emitSerial('render', args...)

				# Render document
				model.on 'renderDocument', (args...) ->
					docpad.emitSerial('renderDocument', args...)

				# Fetch a layout
				model.on 'getLayout', (opts={},next) ->
					opts.collection = docpad.getCollection('layouts')
					layout = docpad.getFileBySelector(opts.selector, opts)
					next(null, {layout})

			# Remove
			#model.on 'remove', (file) ->
			#	docpad.getDatabase().remove(file)
			# ^ Commented out as for some reason this stops layouts from working

			# Error
			model.on 'error', (args...) ->
				docpad.error(args...)

			# Log
			model.on 'log', (args...) ->
				if args.length is 2
					if args[0] in ['err', 'error']
						docpad.error(args[1])
						return

					if args[0] in ['warn', 'warning']
						docpad.warn(args[1])
						return

				docpad.log(args...)

		# Chain
		@

	###*
	# Add supplied model to the DocPad database. If the passed
	# model definition is a plain object of properties, a new
	# model will be created prior to adding to the database.
	# Calls {{#crossLink "DocPad/createModel:method"}}{{/crossLink}}
	# before adding the model to the database.
	#
	#	# Override the stat's mtime to now
	#	# This is because renames will not update the mtime
	#	fileCurrentStat?.mtime = new Date()
	#
	#	# Create the file object
	#	file = docpad.addModel({fullPath:filePath, stat:fileCurrentStat})
	#
	# @method addModel
	# @param {Object} model either a plain object defining the required properties, in particular
	# the file path or an actual model object
	# @param {Object} opts
	# @return {Object} the model
	###
	addModel: (model, opts) ->
		model = @createModel(model, opts)
		@getDatabase().add(model)
		return model

	###*
	# Add the supplied collection of models to the DocPad database.
	# Calls {{#crossLink "DocPad/createModels:method"}}{{/crossLink}}
	# before adding the models to the database.
	#
	# 	databaseData = JSON.parse data.toString()
	#	models = docpad.addModels(databaseData.models)
	#
	# @method addModels
	# @param {Object} models DocPad collection of models
	# @param {Object} opts
	# @return {Object} the models
	###
	addModels: (models, opts) ->
		models = @createModels(models, opts)
		@getDatabase().add(models)
		return models

	###*
	# Create a collection of models from the supplied collection
	# ensuring that the collection is suitable for adding to the
	# DocPad database. The method calls {{#crossLink "DocPad/createModel"}}{{/crossLink}}
	# for each model in the models array.
	# @private
	# @method createModels
	# @param {Object} models DocPad collection of models
	# @param {Object} opts
	# @return {Object} the models
	###
	createModels: (models, opts) ->
		for model in models
			@createModel(model, opts)
		# return the for loop results

	###*
	# Creates either a file or document model.
	# The model type to be created can be passed
	# as an opts property, if not, the method will
	# attempt to determing the model type by checking
	# if the file is in one of the documents or
	# layout paths.
	#
	# Ensures a duplicate model is not created
	# and all required attributes are present and
	# events attached.
	#
	# Generally it is not necessary for an application
	# to manually create a model via creatModel as DocPad
	# will handle this process when watching a project's
	# file and document directories. However, it is possible
	# that a plugin might have a requirement to do so.
	#
	# 	model = @docpad.createModel({fullPath:fullPath})
    #   model.load()
    #   @docpad.getDatabase().add(model)
	#
	# @method createModel
	# @param {Object} [attrs={}]
	# @param {String} attrs.fullPath the full path to the file
	# @param {Object} [opts={}]
	# @param {String} opts.modelType either 'file' or 'document'
	# @return {Object} the file or document model
	###
	createModel: (attrs={},opts={}) ->
		# Check
		if attrs instanceof FileModel
			return attrs

		# Prepare
		docpad = @
		config = @getConfig()
		database = @getDatabase()
		fileFullPath = attrs.fullPath or null


		# Find or create
		# This functionality use to be inside ensureModel
		# But that caused duplicates in some instances
		# So now we will always check
		if attrs.fullPath
			result = database.findOne(fullPath: attrs.fullPath)
			if result
				return result


		# -----------------------------
		# Try and determine the model type

		# If the type hasn't been specified try and detemrine it based on the full path
		if fileFullPath
			# Check if we have a document or layout
			unless opts.modelType
				for dirPath in config.documentsPaths.concat(config.layoutsPaths)
					if fileFullPath.indexOf(dirPath) is 0
						attrs.relativePath or= fileFullPath.replace(dirPath, '').replace(/^[\/\\]/,'')
						opts.modelType = 'document'
						break

			# Check if we have a file
			unless opts.modelType
				for dirPath in config.filesPaths
					if fileFullPath.indexOf(dirPath) is 0
						attrs.relativePath or= fileFullPath.replace(dirPath, '').replace(/^[\/\\]/,'')
						opts.modelType = 'file'
						break

		# -----------------------------
		# Create the appropriate emodel

		# Extend the opts with things we need
		opts = extendr.extend({
			detectEncoding: config.detectEncoding
			rootOutDirPath: config.outPath
			locale: @getLocale()
			TaskGroup: @TaskGroup
		}, opts)

		if opts.modelType is 'file'
			# Create a file model
			model = new FileModel(attrs, opts)
		else
			# Create document model
			model = new DocumentModel(attrs, opts)

		# -----------------------------
		# Finish up

		# Attach Events
		@attachModelEvents(model)

		# Return
		return model

	###*
	# Parse a directory and return a
	# files collection
	# @method parseDirectory
	# @param {Object} [opts={}]
	# @param {Object} next
	# @param {Error} next.err
	# @param {Object} next.files files collection
	###
	parseDirectory: (opts={},next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		locale = @getLocale()

		# Extract
		{path,createFunction} = opts
		createFunction ?= @createModel
		files = opts.collection or new FilesCollection()

		# Check if the directory exists
		safefs.exists path, (exists) ->
			# Check
			unless exists
				# Log
				docpad.log 'debug', util.format(locale.renderDirectoryNonexistant, path)

				# Forward
				return next()

			# Log
			docpad.log 'debug', util.format(locale.renderDirectoryParsing, path)

			# Files
			docpad.scandir(
				# Path
				path: path

				# File Action
				fileAction: (fileFullPath,fileRelativePath,nextFile,fileStat) ->
					# Prepare
					data =
						fullPath: fileFullPath
						relativePath: fileRelativePath
						stat: fileStat

					# Create file
					file = createFunction.call(docpad, data, opts)

					# Update the file's stat
					# To ensure changes files are handled correctly in generation
					file.action 'load', (err) ->
						# Error?
						return nextFile(err)  if err

						# Add the file to the collection
						files.add(file)

						# Next
						nextFile()

				# Next
				next: (err) ->
					# Check
					return next(err)  if err

					# Log
					docpad.log 'debug', util.format(locale.renderDirectoryParsed, path)

					# Forward
					return next(null, files)
			)

		# Chain
		@


	# =================================
	# Plugins

	###*
	# Get a plugin by it's name
	# @method getPlugin
	# @param {Object} pluginName
	# @return {Object} a DocPad plugin
	###
	getPlugin: (pluginName) ->
		@loadedPlugins[pluginName]


	###*
	# Check if we have any plugins
	# @method hasPlugins
	# @return {Boolean}
	###
	hasPlugins: ->
		return typeChecker.isEmptyObject(@loadedPlugins) is false

	###*
	# Destructor. Destroy plugins
	# @private
	# @method destroyPlugins
	###
	destroyPlugins: ->
		for own name,plugin of @loadedPlugins
			plugin.destroy()
			@loadedPlugins[name] = null
		@

	###*
	# Load plugins from the file system
	# next(err)
	# @private
	# @method loadPlugins
	# @param {Function} next
	# @param {Error} next.err
	###
	loadPlugins: (next) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Snore
		@slowPlugins = {}
		snore = balUtil.createSnore ->
			docpad.log 'notice', util.format(locale.pluginsSlow, Object.keys(docpad.slowPlugins).join(', '))

		# Async
		tasks = new @TaskGroup "loadPlugins tasks", concurrency:0, next:(err) ->
			docpad.slowPlugins = {}
			snore.clear()
			return next(err)

		# Load website plugins
		(@config.pluginsPaths or []).forEach (pluginsPath) ->
			tasks.addTask "load the website's plugins at: #{pluginsPath}", (complete) ->
				safefs.exists pluginsPath, (exists) ->
					return complete()  unless exists
					docpad.loadPluginsIn(pluginsPath, complete)

		# Load specific plugins
		(@config.pluginPaths or []).forEach (pluginPath) ->
			tasks.addTask "load custom plugins at: #{pluginPath}", (complete) ->
				safefs.exists pluginPath, (exists) ->
					return complete()  unless exists
					docpad.loadPlugin(pluginPath, complete)

		# Execute the loading asynchronously
		tasks.run()

		# Chain
		@

	###*
	# Checks if a plugin was loaded succesfully.
	# @method loadedPlugin
	# @param {String} pluginName
	# @param {Function} next
	# @param {Error} next.err
	# @param {Boolean} next.loaded
	###
	loadedPlugin: (pluginName,next) ->
		# Prepare
		docpad = @

		# Check
		loaded = docpad.loadedPlugins[pluginName]?
		next(null,loaded)

		# Chain
		@

	###*
	# Load a plugin from its full file path
	# _next(err)
	# @private
	# @method loadPlugin
	# @param {String} fileFullPath
	# @param {Function} _next
	# @param {Error} _next.err
	# @return {Object} description
	###
	loadPlugin: (fileFullPath,_next) ->
		# Prepare
		docpad = @
		config = @getConfig()
		locale = @getLocale()
		next = (err) ->
			# Remove from slow plugins
			delete docpad.slowPlugins[pluginName]
			# Forward
			return _next(err)

		# Prepare variables
		loader = new PluginLoader(
			dirPath: fileFullPath
			docpad: @
			BasePlugin: BasePlugin
		)
		pluginName = loader.pluginName
		enabled = (
			(config.enableUnlistedPlugins  and  config.enabledPlugins[pluginName]? is false)  or
			config.enabledPlugins[pluginName] is true
		)

		# If we've already been loaded, then exit early as there is no use for us to load again
		if docpad.loadedPlugins[pluginName]?
			# However we probably want to reload the configuration as perhaps the user or environment configuration has changed
			docpad.loadedPlugins[pluginName].setConfig()
			# Complete
			return _next()

		# Add to loading stores
		docpad.slowPlugins[pluginName] = true

		# Check
		unless enabled
			# Skip
			docpad.log 'debug', util.format(locale.pluginSkipped, pluginName)
			return next()
		else
			# Load
			docpad.log 'debug', util.format(locale.pluginLoading, pluginName)

			# Check existance
			loader.exists (err,exists) ->
				# Error or doesn't exist?
				return next(err)  if err or not exists

				# Check support
				loader.unsupported (err,unsupported) ->
					# Error?
					return next(err)  if err

					# Unsupported?
					if unsupported
						# Version?
						if unsupported in ['version-docpad','version-plugin'] and config.skipUnsupportedPlugins is false
							docpad.log 'warn', util.format(locale.pluginContinued, pluginName)
						else
							# Type?
							if unsupported is 'type'
								docpad.log 'debug', util.format(locale.pluginSkippedDueTo, pluginName, unsupported)

							# Something else?
							else
								docpad.log 'warn', util.format(locale.pluginSkippedDueTo, pluginName, unsupported)
							return next()

					# Load the class
					loader.load (err) ->
						return next(err)  if err

						# Create an instance
						loader.create {}, (err,pluginInstance) ->
							return next(err)  if err

							# Add to plugin stores
							docpad.loadedPlugins[loader.pluginName] = pluginInstance

							# Log completion
							docpad.log 'debug', util.format(locale.pluginLoaded, pluginName)

							# Forward
							return next()

		# Chain
		@

	###*
	# Load plugins from a directory path
	# @private
	# @method loadPluginsIn
	# @param {String} pluginsPath
	# @param {Function} next
	# @param {Error} next.err
	###
	loadPluginsIn: (pluginsPath, next) ->
		# Prepare
		docpad = @
		locale = @getLocale()

		# Load Plugins
		docpad.log 'debug', util.format(locale.pluginsLoadingFor, pluginsPath)
		@scandir(
			# Path
			path: pluginsPath

			# Skip files
			fileAction: false

			# Handle directories
			dirAction: (fileFullPath,fileRelativePath,nextFile) ->
				# Prepare
				pluginName = pathUtil.basename(fileFullPath)

				# Delve deeper into the directory if it is a direcotry of plugins
				return nextFile(null, false)  if fileFullPath is pluginsPath

				# Otherwise, it is a plugin directory, so load the plugin
				docpad.loadPlugin fileFullPath, (err) ->
					# Warn about the plugin load error if there is one
					if err
						docpad.warn util.format(locale.pluginFailedToLoad, pluginName, fileFullPath), err

					# All done and don't recurse into this directory
					return nextFile(null, true)

			# Next
			next: (err) ->
				docpad.log 'debug', util.format(locale.pluginsLoadedFor, pluginsPath)
				return next(err)
		)

		# Chain
		@


	# =================================
	# Utilities

	# ---------------------------------
	# Utilities: Misc

	###*
	# Compare current DocPad version to the latest
	# and print out the result to the console.
	# Used at startup.
	# @private
	# @method compareVersion
	###
	compareVersion: ->
		# Prepare
		docpad = @
		config = @getConfig()
		locale = @getLocale()

		# Check
		return @  if config.offline or !config.checkVersion

		# Check
		balUtil.packageCompare(
			local: @packagePath
			remote: config.helperUrl+'latest'
			newVersionCallback: (details) ->
				isLocalInstallation = docpadUtil.isLocalDocPadExecutable()
				message = (if isLocalInstallation then locale.versionOutdatedLocal else locale.versionOutdatedGlobal)
				currentVersion = 'v'+details.local.version
				latestVersion = 'v'+details.remote.version
				upgradeUrl = details.local.upgradeUrl or details.remote.installUrl or details.remote.homepage
				messageFilled = util.format(message, currentVersion, latestVersion, upgradeUrl)
				docpad.notify(latestVersion, title:locale.versionOutdatedNotification)
				docpad.log('notice', messageFilled)
		)

		# Chain
		@


	# ---------------------------------
	# Utilities: Exchange


	###*
	# Get DocPad's exchange data
	# Requires internet access
	# next(err,exchange)
	# @private
	# @method getExchange
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.exchange docpad.exchange
	###
	getExchange: (next) ->
		# Prepare
		docpad = @
		config = @getConfig()
		locale = @getLocale()

		# Check if it is stored locally
		return next(null, docpad.exchange)  if typeChecker.isEmptyObject(docpad.exchange) is false

		# Offline?
		return next(null, null)  if config.offline

		# Log
		docpad.log('info', locale.exchangeUpdate+' '+locale.pleaseWait)

		# Otherwise fetch it from the exchangeUrl
		exchangeUrl = config.helperUrl+'?method=exchange&version='+@version
		docpad.loadConfigUrl exchangeUrl, (err,parsedData) ->
			# Check
			if err
				locale = docpad.getLocale()
				docpad.warn(locale.exchangeError, err)
				return next()

			# Log
			docpad.log('info', locale.exchangeUpdated)

			# Success
			docpad.exchange = parsedData
			return next(null, parsedData)

		# Chain
		@


	# ---------------------------------
	# Utilities: Files

	###*
	# Contextualize files.
	# Contextualizing is the process of adding layouts and
	# awareness of other documents to our document. The
	# contextualizeBefore and contextualizeAfter events
	# are emitted here.
	# @private
	# @method contextualizeFiles
	# @param {Object} [opts={}]
	# @param {Function} next
	# @param {Error} next.err
	###
	contextualizeFiles: (opts={},next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		{collection,templateData} = opts
		docpad = @
		config = @getConfig()
		locale = @getLocale()
		slowFilesObject = {}
		slowFilesTimer = null

		# Update progress
		opts.progress?.step("contextualizeFiles (preparing)").total(1).setTick(0)

		# Log
		docpad.log 'debug', util.format(locale.contextualizingFiles, collection.length)

		# Start contextualizing
		docpad.emitSerial 'contextualizeBefore', {collection,templateData}, (err) ->
			# Prepare
			return next(err)  if err

			# Completion callback
			tasks = new docpad.TaskGroup "contextualizeFiles tasks", concurrency:0, next:(err) ->
				# Kill the timer
				clearInterval(slowFilesTimer)
				slowFilesTimer = null

				# Check
				return next(err)  if err

				# Update progress
				opts.progress?.step("contextualizeFiles (postparing)").total(1).setTick(0)

				# After
				docpad.emitSerial 'contextualizeAfter', {collection}, (err) ->
					# Check
					return next(err)  if err

					# Log
					docpad.log 'debug', util.format(locale.contextualizedFiles, collection.length)

					# Forward
					return next()

			# Add contextualize tasks
			opts.progress?.step('contextualizeFiles').total(collection.length).setTick(0)
			collection.forEach (file,index) ->
				filePath = file.getFilePath()
				slowFilesObject[file.id] = file.get('relativePath') or file.id
				tasks.addTask "conextualizing: #{filePath}", (complete) ->
					file.action 'contextualize', (err) ->
						delete slowFilesObject[file.id]
						opts.progress?.tick()
						return complete(err)

			# Setup the timer
			slowFilesTimer = setInterval(
				->
					slowFilesArray = (value or key  for own key,value of slowFilesObject)
					docpad.log('info', util.format(locale.slowFiles, 'contextualizeFiles')+' \n'+slowFilesArray.join('\n'))
				config.slowFilesDelay
			)

			# Run tasks
			tasks.run()

		# Chain
		@

	###*
	# Render the DocPad project's files.
	# The renderCollectionBefore, renderCollectionAfter,
	# renderBefore, renderAfter events are all emitted here.
	# @private
	# @method renderFiles
	# @param {Object} [opts={}]
	# @param {Function} next
	# @param {Error} next.err
	###
	renderFiles: (opts={},next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		{collection,templateData,renderPasses} = opts
		docpad = @
		config = @getConfig()
		locale = @getLocale()
		slowFilesObject = {}
		slowFilesTimer = null

		# Update progress
		opts.progress?.step("renderFiles (preparing)").total(1).setTick(0)

		# Log
		docpad.log 'debug', util.format(locale.renderingFiles, collection.length)

		# Render File
		# next(null, outContent, file)
		renderFile = (file,next) ->
			# Render
			if file.get('render') is false or !file.get('relativePath')
				file.attributes.rtime = new Date()
				next(null, file.getOutContent(), file)
			else
				file.action('render', {templateData}, next)

			# Return
			return file

		# Render Collection
		renderCollection = (collectionToRender,{renderPass},next) ->
			# Plugin Event
			docpad.emitSerial 'renderCollectionBefore', {collection:collectionToRender,renderPass}, (err) ->
				# Prepare
				return next(err)  if err

				subTasks = new docpad.TaskGroup "renderCollection: #{collectionToRender.options.name}", concurrency:0, next:(err) ->
					# Prepare
					return next(err)  if err

					# Plugin Event
					docpad.emitSerial('renderCollectionAfter', {collection:collectionToRender,renderPass}, next)

				# Cycle
				opts.progress?.step("renderFiles (pass #{renderPass})").total(collectionToRender.length).setTick(0)
				collectionToRender.forEach (file) ->
					filePath = file.getFilePath()
					slowFilesObject[file.id] = file.get('relativePath')
					subTasks.addTask "rendering: #{filePath}", (complete) ->
						renderFile file, (err) ->
							delete slowFilesObject[file.id] or file.id
							opts.progress?.tick()
							return complete(err)

				# Return
				subTasks.run()
				return collectionToRender

		# Plugin Event
		docpad.emitSerial 'renderBefore', {collection,templateData}, (err) ->
			# Prepare
			return next(err)  if err

			# Async
			tasks = new docpad.TaskGroup "renderCollection: renderBefore tasks", next:(err) ->
				# Kill the timer
				clearInterval(slowFilesTimer)
				slowFilesTimer = null

				# Check
				return next(err)  if err

				# Update progress
				opts.progress?.step("renderFiles (postparing)").total(1).setTick(0)

				# After
				docpad.emitSerial 'renderAfter', {collection}, (err) ->
					# Check
					return next(err)  if err

					# Log
					docpad.log 'debug', util.format(locale.renderedFiles, collection.length)

					# Forward
					return next()

			# Queue the initial render
			initialCollection = collection.findAll('referencesOthers':false)
			subsequentCollection = null
			tasks.addTask "rendering the initial collection", (complete) ->
				renderCollection initialCollection, {renderPass:1}, (err) ->
					return complete(err)  if err
					subsequentCollection = collection.findAll('referencesOthers':true)
					renderCollection(subsequentCollection, {renderPass:2}, complete)

			# Queue the subsequent renders
			if renderPasses > 1
				[3..renderPasses].forEach (renderPass) ->  tasks.addTask "rendering the subsequent collection index #{renderPass}", (complete) ->
					renderCollection(subsequentCollection, {renderPass}, complete)

			# Setup the timer
			slowFilesTimer = setInterval(
				->
					slowFilesArray = (value or key  for own key,value of slowFilesObject)
					docpad.log('info', util.format(locale.slowFiles, 'renderFiles')+' \n'+slowFilesArray.join('\n'))
				config.slowFilesDelay
			)

			# Run tasks
			tasks.run()

		# Chain
		@

	###*
	# Write rendered files to the DocPad out directory.
	# The writeBefore and writeAfter events are emitted here.
	# @private
	# @method writeFiles
	# @param {Object} [opts={}]
	# @param {Function} next
	# @param {Error} next.err
	###
	writeFiles: (opts={},next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		{collection,templateData} = opts
		docpad = @
		config = @getConfig()
		locale = @getLocale()
		slowFilesObject = {}
		slowFilesTimer = null

		# Update progress
		opts.progress?.step("writeFiles (preparing)").total(1).setTick(0)

		# Log
		docpad.log 'debug', util.format(locale.writingFiles, collection.length)

		# Plugin Event
		docpad.emitSerial 'writeBefore', {collection,templateData}, (err) ->
			# Prepare
			return next(err)  if err

			# Completion callback
			tasks = new docpad.TaskGroup "writeFiles tasks", concurrency:0, next:(err) ->
				# Kill the timer
				clearInterval(slowFilesTimer)
				slowFilesTimer = null

				# Check
				return next(err)  if err

				# Update progress
				opts.progress?.step("writeFiles (postparing)").total(1).setTick(0)

				# After
				docpad.emitSerial 'writeAfter', {collection}, (err) ->
					# Check
					return next(err)  if err

					# docpad.log 'debug', util.format(locale.wroteFiles, collection.length)
					return next()

			# Add write tasks
			opts.progress?.step('writeFiles').total(collection.length).setTick(0)
			collection.forEach (file,index) ->
				filePath = file.getFilePath()
				tasks.addTask "writing the file: #{filePath}", (complete) ->
					# Prepare
					slowFilesObject[file.id] = file.get('relativePath')

					# Create sub tasks
					fileTasks = new docpad.TaskGroup "tasks for file write: #{filePath}", concurrency:0, next:(err) ->
						delete slowFilesObject[file.id]
						opts.progress?.tick()
						return complete(err)

					# Write out
					if file.get('write') isnt false and file.get('dynamic') isnt true and file.get('outPath')
						fileTasks.addTask "write out", (complete) ->
							file.action('write', complete)

					# Write source
					if file.get('writeSource') is true and file.get('fullPath')
						fileTasks.addTask "write source", (complete) ->
							file.action('writeSource', complete)

					# Run sub tasks
					fileTasks.run()

			# Setup the timer
			slowFilesTimer = setInterval(
				->
					slowFilesArray = (value or key  for own key,value of slowFilesObject)
					docpad.log('info', util.format(locale.slowFiles, 'writeFiles')+' \n'+slowFilesArray.join('\n'))
				config.slowFilesDelay
			)

			# Run tasks
			tasks.run()

		# Chain
		@


	# ---------------------------------
	# Generate

	# Generate Helpers
	###*
	# Has DocPad's generation process started?
	# @private
	# @property {Boolean} generateStarted
	###
	generateStarted: null

	###*
	# Has DocPad's generation process ended?
	# @private
	# @property {Boolean} generateEnded
	###
	generateEnded: null

	###*
	# Is DocPad currently generating?
	# @private
	# @property {Boolean} generating
	###
	generating: false

	###*
	# Has DocPad done at least one generation?
	# True once the first generation has occured.
	# @private
	# @property {Object} generated
	###
	generated: false

	###*
	# Create the console progress bar.
	# Progress only shown if the DocPad config 'progress'
	# option is true, the DocPad config 'prompts' option is true
	# and the log level is 6 (default)
	# @private
	# @method createProgress
	# @return {Object} the progress object
	###
	createProgress: ->
		# Prepare
		docpad = @
		config = docpad.getConfig()

		# Only show progress if
		# - progress is true
		# - prompts are supported (so no servers)
		# - and we are log level 6 (the default level)
		progress = null
		if config.progress and config.prompts and @getLogLevel() is 6
			progress = require('progressbar').create()
			@getLoggers().console.unpipe(process.stdout)
			@getLogger().once 'log', progress.logListener ?= (data) ->
				if data.levelNumber <= 5  # notice or higher
					docpad.destroyProgress(progress)

		# Return
		return progress

	###*
	# Destructor. Destroy the progress object
	# @private
	# @method destroyProgress
	# @param {Object} progress
	# @return {Object} the progress object
	###
	destroyProgress: (progress) ->
		# Fetch
		if progress
			progress.finish()
			@getLoggers().console.unpipe(process.stdout).pipe(process.stdout)

		# Return
		return progress

	###*
	# Destructor. Destroy the regeneration timer.
	# @private
	# @method destroyRegenerateTimer
	###
	destroyRegenerateTimer: ->
		# Prepare
		docpad = @

		# Clear Regenerate Timer
		if docpad.regenerateTimer
			clearTimeout(docpad.regenerateTimer)
			docpad.regenerateTimer = null

		# Chain
		@

	###*
	# Create the regeneration timer
	# @private
	# @method createRegenerateTimer
	###
	createRegenerateTimer: ->
		# Prepare
		docpad = @
		locale = docpad.getLocale()
		config = docpad.getConfig()

		# Create Regenerate Timer
		if config.regenerateEvery
			docpad.regenerateTimer = setTimeout(
				->
					docpad.log('info', locale.renderInterval)
					docpad.action('generate', config.regenerateEveryOptions)
				config.regenerateEvery
			)

		# Chain
		@

	###*
	# Set off DocPad's generation process.
	# The generated, populateCollectionsBefore, populateCollections, populateCollections
	# generateBefore and generateAfter events are emitted here
	# @method generate
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	generate: (opts, next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = docpad.getConfig()
		locale = docpad.getLocale()
		database = docpad.getDatabase()

		# Check
		return next()  if opts.collection?.length is 0


		# Update generating flag
		lastGenerateStarted = docpad.generateStarted
		docpad.generateStarted = new Date()
		docpad.generateEnded = null
		docpad.generating = true

		# Update the cached database
		docpad.databaseTempCache = new FilesCollection(database.models)  if database.models.length

		# Create Progress
		# Can be over-written by API calls
		opts.progress ?= docpad.createProgress()

		# Grab the template data we will use for rendering
		opts.templateData = docpad.getTemplateData(opts.templateData or {})

		# How many render passes will we require?
		# Can be over-written by API calls
		opts.renderPasses or= config.renderPasses


		# Destroy Regenerate Timer
		docpad.destroyRegenerateTimer()

		# Check plugin count
		docpad.log('notice', locale.renderNoPlugins)  unless docpad.hasPlugins()

		# Log
		docpad.log('info', locale.renderGenerating)
		docpad.notify (new Date()).toLocaleTimeString(), {title: locale.renderGeneratingNotification}

		# Tasks
		tasks = new @TaskGroup("generate tasks", {progress: opts.progress}).done (err) ->
			# Update generating flag
			docpad.generating = false
			docpad.generateEnded = new Date()

			# Update caches
			docpad.databaseTempCache = null

			# Create Regenerate Timer
			docpad.createRegenerateTimer()

			# Clear Progress
			if opts.progress
				docpad.destroyProgress(opts.progress)
				opts.progress = null

			# Error?
			return next(err)  if err

			# Log success message
			seconds = (docpad.generateEnded - docpad.generateStarted) / 1000
			howMany = "#{opts.collection?.length or 0}/#{database.length}"
			docpad.log 'info', util.format(locale.renderGenerated, howMany, seconds)
			docpad.notify (new Date()).toLocaleTimeString(), {title: locale.renderGeneratedNotification}

			# Generated
			if opts.initial is true
				docpad.generated = true
				return docpad.emitSerial('generated', opts, next)

			# Safety check if generated is false but initial was false too
			# https://github.com/bevry/docpad/issues/811
			else if docpad.generated is false
				return next(
					new Error('DocPad is in an invalid state, please report this on the github issue tracker. Reference 3360')
				)

			else
				return next()

		# Extract functions from tasks for simplicity
		# when dealing with nested tasks/groups
		addGroup = tasks.addGroup.bind(tasks)
		addTask = tasks.addTask.bind(tasks)


		# Setup a clean database
		addTask 'Reset our collections', (complete) ->
			# Skip if we are not a reset generation, or an initial generation (generated is false)
			return complete()  unless opts.reset is true or docpad.generated is false
			return docpad.resetCollections(opts, complete)


		# Figure out the options
		# This is here as resetCollections could change our state
		# https://github.com/bevry/docpad/issues/811
		addTask 'Figure out options', ->
			# Mode: Cache
			# Shall we write to the database cache?
			# Set to true if the configuration option says we can, and we are the initial generation
			opts.cache     ?= config.databaseCache

			# Mode: Initial
			# Shall we do some basic initial checks
			# Set to the opts.reset value if specified, or whether are the initial generation
			opts.initial   ?= !(docpad.generated)

			# Mode: Reset
			# Shall we reset the database
			# Set to true if we are the initial generation
			opts.reset     ?= opts.initial

			# Mode: Populate
			# Shall we fetch in new data?
			# Set to the opts.reset value if specified, or the opts.initial value
			opts.populate  ?= opts.reset

			# Mode: Reload
			# Shall we rescan the file system for changes?
			# Set to the opts.reset value if specified, or the opts.initial value
			opts.reload    ?= opts.reset

			# Mode: Partial
			# Shall we perform a partial generation (false) or a completion generation (true)?
			# Set to false if we are the initial generation
			opts.partial   ?= !(opts.reset)

			# Log our opts
			docpad.log(
				'debug'
				'Generate options:'
				pick(opts, ['cache', 'initial', 'reset', 'populate', 'reload', 'partial', 'renderPasses'])
			)


		# Check directory structure
		addTask 'check source directory exists', (complete) ->
			# Skip if we are not the initial generation
			return complete()  unless opts.initial is true

			# Continue if we are the initial generation
			safefs.exists config.srcPath, (exists) ->
				# Check
				unless exists
					err = new Error(locale.renderNonexistant)
					return complete(err)

				# Forward
				return complete()


		addGroup 'fetch data to render', (addGroup, addTask) ->
			# Fetch new data
			# If we are a populate generation (by default an initial generation)
			if opts.populate is true
				# This will pull in new data from plugins
				addTask 'populateCollectionsBefore', (complete) ->
					docpad.emitSerial('populateCollectionsBefore', opts, complete)

				# Import the cached data
				# If we are the initial generation, and we have caching enabled
				if opts.initial is true and opts.cache in [true, 'read']
					addTask 'import data from cache', (complete) ->
						# Check if we do have a databae cache
						safefs.exists config.databaseCachePath, (exists) ->
							return complete()  if exists is false

							# Read the database cache if it exists
							safefs.readFile config.databaseCachePath, (err, data) ->
								return complete(err)  if err

								# Parse it and apply the data values
								databaseData = JSON.parse data.toString()
								opts.cache     = true
								opts.initial   = true
								opts.reset     = false
								opts.populate  = true
								opts.reload    = true
								opts.partial   = true

								lastGenerateStarted = new Date(databaseData.generateStarted)
								addedModels = docpad.addModels(databaseData.models)
								docpad.log 'info', util.format(locale.databaseCacheRead, database.length, databaseData.models.length)

								# @TODO we need a way of detecting deleted files between generations

								return complete()

				# Rescan the file system
				# If we are a reload generation (by default an initial generation)
				# This is useful when the database is out of sync with the source files
				# For instance, someone shut down docpad, and made some changes, then ran docpad again
				# See https://github.com/bevry/docpad/issues/705#issuecomment-29243666 for details
				if opts.reload is true
					addGroup 'import data from file system', (addGroup, addTask) ->
						# Documents
						config.documentsPaths.forEach (documentsPath) ->
							addTask 'import documents', (complete) ->
								docpad.parseDirectory({
									modelType: 'document'
									collection: database
									path: documentsPath
									next: complete
								})

						# Files
						config.filesPaths.forEach (filesPath) ->
							addTask 'import files', (complete) ->
								docpad.parseDirectory({
									modelType: 'file'
									collection: database
									path: filesPath
									next: complete
								})

						# Layouts
						config.layoutsPaths.forEach (layoutsPath) ->
							addTask 'import layouts', (complete) ->
								docpad.parseDirectory({
									modelType: 'document'
									collection: database
									path: layoutsPath
									next: complete
								})

				# This will pull in new data from plugins
				addTask 'populateCollections', (complete) ->
					docpad.emitSerial('populateCollections', opts, complete)


		addGroup 'determine files to render', (addGroup, addTask) ->
			# Perform a complete regeneration
			# If we are a reset generation (by default an initial non-cached generation)
			if opts.partial is false
				# Use Entire Collection
				addTask 'Add all database models to render queue', ->
					opts.collection ?= new FilesCollection().add(docpad.getCollection('generate').models)

			# Perform a partial regeneration
			# If we are not a reset generation (by default any non-initial generation)
			else
				# Use Partial Collection
				addTask 'Add only changed models to render queue', ->
					changedQuery =
						$or:
							# Get changed files
							mtime: $gte: lastGenerateStarted

							# Get new files
							$and:
								wtime: null
								write: true
					opts.collection ?= new FilesCollection().add(docpad.getCollection('generate').findAll(changedQuery).models)


		addTask 'generateBefore', (complete) ->
			# If we have nothing to generate
			if opts.collection.length is 0
				# then there is no need to execute further tasks
				tasks.clear()
				complete()

			# Otherwise continue down the task loop
			else
				docpad.emitSerial('generateBefore', opts, complete)


		addTask 'prepare files', (complete) ->
			# Log the files to generate if we are in debug mode
			docpad.log 'debug', 'Files to generate at', (lastGenerateStarted), '\n', (
				{
					id: model.id
					path: model.getFilePath()
					mtime: model.get('mtime')
					wtime: model.get('wtime')
					dynamic: model.get('dynamic')
					ignored: model.get('ignored')
					write: model.get('write')
				}  for model in opts.collection.models
			)

			# Add anything that references other documents (e.g. partials, listing, etc)
			# This could eventually be way better
			standalones = opts.collection.pluck('standalone')
			allStandalone = standalones.indexOf(false) is -1
			if allStandalone is false
				opts.collection.add(docpad.getCollection('referencesOthers').models)

			# Deeply/recursively add the layout children
			addLayoutChildren = (collection) ->
				collection.forEach (file) ->
					if file.get('isLayout') is true
						# Find
						layoutChildrenQuery =
							layoutRelativePath: file.get('relativePath')
						layoutChildrenCollection = docpad.getCollection('hasLayout').findAll(layoutChildrenQuery)

						# Log the files to generate if we are in debug mode
						docpad.log 'debug', 'Layout children to generate at', (lastGenerateStarted), '\n', (
							{
								id: model.id
								path: model.getFilePath()
								mtime: model.get('mtime')
								wtime: model.get('wtime')
								write: model.get('write')
							}  for model in layoutChildrenCollection.models
						), '\n', layoutChildrenQuery

						# Recurse
						addLayoutChildren(layoutChildrenCollection)

						# Add
						opts.collection.add(layoutChildrenCollection.models)
			addLayoutChildren(opts.collection)

			# Filter out ignored, and no-render no-write files
			opts.collection.reset opts.collection.reject (file) ->
				return (file.get('render') is false and file.get('write') is false)

			# Log the files to generate if we are in debug mode
			docpad.log 'debug', 'Files to generate at', (lastGenerateStarted), '\n', (
				{
					id: model.id
					path: model.getFilePath()
					mtime: model.get('mtime')
					wtime: model.get('wtime')
					dynamic: model.get('dynamic')
					ignored: model.get('ignored')
					write: model.get('write')
				}  for model in opts.collection.models
			)

			# Forward
			return complete()


		addGroup 'process file', (addGroup, addTask) ->
			addTask 'contextualizeFiles', {args:[opts]}, docpad.contextualizeFiles.bind(docpad)
			addTask 'renderFiles', {args:[opts]}, docpad.renderFiles.bind(docpad)
			addTask 'writeFiles', {args:[opts]}, docpad.writeFiles.bind(docpad)


		addTask 'generateAfter', (complete) ->
			docpad.emitSerial('generateAfter', opts, complete)


		# Write the cache file
		addTask 'Write the database cache', (complete) ->
			# Skip if we do not care for writing the cache
			return complete()  unless opts.cache in [true, 'write']

			# Write the cache
			databaseData =
				generateStarted: docpad.generateStarted
				generateEnded: docpad.generateEnded
				models: (model.getAttributes()  for model in database.models)
			databaseDataDump = JSON.stringify(databaseData, null, '  ')
			docpad.log 'info', util.format(locale.databaseCacheWrite, databaseData.models.length)
			return safefs.writeFile(config.databaseCachePath, databaseDataDump, complete)


		# Run
		tasks.run()

		# Chain
		@


	# ---------------------------------
	# Render

	###*
	# Load a document
	# @private
	# @method loadDocument
	# @param {Object} document
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.document
	###
	loadDocument: (document,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)

		# Load
		# @TODO: don't load if already loaded
		document.action('load contextualize', opts, next)

		# Chain
		@

	###*
	# Load and render a document
	# @method loadAndRenderDocument
	# @param {Object} document
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.document
	###
	loadAndRenderDocument: (document,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @

		# Load
		docpad.loadDocument document, opts, (err) ->
			return next(err)  if err

			# Render
			docpad.renderDocument(document, opts, next)

		# Chain
		@

	###*
	# Render a document
	# @method renderDocument
	# @param {Object} document
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @param {Object} next.result
	# @param {Object} next.document
	###
	renderDocument: (document,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)

		# Render
		clone = document.clone().action 'render', opts, (err) ->
			result = clone.getOutContent()
			return next(err, result, document)

		# Chain
		@

	###*
	# Render a document at a file path
	# next(err,result)
	# @method renderPath
	# @param {String} path
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.result the rendered document
	###
	renderPath: (path,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		attributes = extendr.extend({
			fullPath: path
		},opts.attributes)

		# Handle
		document = @createDocument(attributes)
		@loadAndRenderDocument(document, opts, next)

		# Chain
		@

	###*
	# Render the passed content data as a
	# document. Required option, filename
	# (opts.filename)
	# next(err,result)
	# @method renderData
	# @param {String} content
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.result the rendered document
	###
	renderData: (content,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		attributes = extendr.extend({
			filename: opts.filename
			data: content
		}, opts.attributes)

		# Handle
		document = @createDocument(attributes)
		@loadAndRenderDocument(document, opts, next)

		# Chain
		@

	# Render Text
	# Doesn't extract meta information, or render layouts
	# TODO: Why not? Why not just have renderData?

	###*
	# Render the passed text data as a
	# document. Required option, filename
	# (opts.filename)
	# next(err,result)
	# @private
	# @method renderText
	# @param {String} text
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Object} next.result the rendered content
	# @param {Object} next.document the rendered document model
	###
	renderText: (text,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		opts.actions ?= ['renderExtensions', 'renderDocument']
		attributes = extendr.extend({
			filename: opts.filename
			data: text
			body: text
			content: text
		}, opts.attributes)

		# Handle
		document = @createDocument(attributes)

		# Render
		clone = document.clone().action 'normalize contextualize render', opts, (err) ->
			result = clone.getOutContent()
			return next(err, result, document)

		# Chain
		@

	###*
	# Render action
	# next(err,document,result)
	# @method render
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	render: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		locale = @getLocale()

		# Extract document
		if opts.document
			@renderDocument(opts.document, opts, next)
		else if opts.data
			@renderData(opts.data, opts, next)
		else if opts.text
			@renderText(opts.text, opts, next)
		else
			path = opts.path or opts.fullPath or opts.filename or null
			if path
				@renderPath(path, opts, next)
			else
				# Check
				err = new Error(locale.renderInvalidOptions)
				return next(err)

		# Chain
		@


	# ---------------------------------
	# Watch

	###*
	# Array of file watchers
	# @private
	# @property {Array} watchers
	###
	watchers: null

	###*
	# Destructor. Destroy the watchers used
	# by DocPad
	# @private
	# @method destroyWatchers
	###
	destroyWatchers: ->
		# Prepare
		docpad = @

		# Check
		if docpad.watchers
			# Close each of them
			for watcher in docpad.watchers
				watcher.close()

			# Reset the array
			docpad.watchers = []

		# Chain
		@

	###*
	# Start up file watchers used by DocPad
	# @private
	# @method watch
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	watch: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()
		locale = @getLocale()
		database = @getDatabase()
		@watchers ?= []

		# Restart our watchers
		restartWatchers = (next) ->
			# Close our watchers
			docpad.destroyWatchers()

			# Start a group
			tasks = new docpad.TaskGroup("watch tasks", {concurrency:0, next})

			# Watch reload paths
			reloadPaths = union(config.reloadPaths, config.configPaths)
			tasks.addTask "watch reload paths", (complete) -> docpad.watchdir(
				paths: reloadPaths
				listeners:
					'log': docpad.log
					'error': docpad.error
					'change': ->
						docpad.log 'info', util.format(locale.watchReloadChange, new Date().toLocaleTimeString())
						docpad.action 'load', (err) ->
							return docpad.fatal(err)  if err
							performGenerate(reset:true)
				next: (err,_watchers) ->
					if err
						docpad.warn("Watching the reload paths has failed:\n"+docpad.inspector(reloadPaths), err)
						return complete()
					for watcher in _watchers
						docpad.watchers.push(watcher)
					return complete()
			)

			# Watch regenerate paths
			regeneratePaths = config.regeneratePaths
			tasks.addTask "watch regenerate paths", (complete) -> docpad.watchdir(
				paths: regeneratePaths
				listeners:
					'log': docpad.log
					'error': docpad.error
					'change': -> performGenerate(reset:true)
				next: (err,_watchers) ->
					if err
						docpad.warn("Watching the regenerate paths has failed:\n"+docpad.inspector(regeneratePaths), err)
						return complete()
					for watcher in _watchers
						docpad.watchers.push(watcher)
					return complete()
			)

			# Watch the source
			srcPath = config.srcPath
			tasks.addTask "watch the source path", (complete) -> docpad.watchdir(
				path: srcPath
				listeners:
					'log': docpad.log
					'error': docpad.error
					'change': changeHandler
				next: (err,watcher) ->
					if err
						docpad.warn("Watching the src path has failed: "+srcPath, err)
						return complete()
					docpad.watchers.push(watcher)
					return complete()
			)

			# Run
			tasks.run()

			# Chain
			@

		# Timer
		regenerateTimer = null
		queueRegeneration = ->
			# Reset the wait
			if regenerateTimer
				clearTimeout(regenerateTimer)
				regenerateTimer = null

			# Regenerat after a while
			regenerateTimer = setTimeout(performGenerate, config.regenerateDelay)

		performGenerate = (opts={}) ->
			# Q: Should we also pass over the collection?
			# A: No, doing the mtime query in generate is more robust

			# Log
			docpad.log util.format(locale.watchRegenerating, new Date().toLocaleTimeString())

			# Afterwards, re-render anything that should always re-render
			docpad.action 'generate', opts, (err) ->
				docpad.error(err)  if err
				docpad.log util.format(locale.watchRegenerated, new Date().toLocaleTimeString())

		# Change event handler
		changeHandler = (changeType,filePath,fileCurrentStat,filePreviousStat) ->
			# Prepare
			fileEitherStat = (fileCurrentStat or filePreviousStat)

			# For some reason neither of the stats may exist, this will cause errors as this is an invalid state
			# as we depend on at least one stat existing, otherwise, what on earth is going on?
			# Whatever the case, this should be fixed within watchr, not docpad
			# as watchr should not be giving us invalid data
			# https://github.com/bevry/docpad/issues/792
			unless fileEitherStat
				err = new Error("""
						DocPad has encountered an invalid state while detecting changes for your files.
						So the DocPad team can fix this right away, please provide any information you can to:
						https://github.com/bevry/docpad/issues/792
						""")
				return docpad.error(err)

			# Log the change
			docpad.log 'info', util.format(locale.watchChange, new Date().toLocaleTimeString()), changeType, filePath

			# Check if we are a file we don't care about
			# This check should not be needed with v2.3.3 of watchr
			# however we've still got it here as it may still be an issue
			isIgnored = docpad.isIgnoredPath(filePath)
			if isIgnored
				docpad.log 'debug', util.format(locale.watchIgnoredChange, new Date().toLocaleTimeString()), filePath
				return

			# Don't care if we are a directory
			isDirectory = fileEitherStat.isDirectory()
			if isDirectory
				docpad.log 'debug', util.format(locale.watchDirectoryChange, new Date().toLocaleTimeString()), filePath
				return

			# Override the stat's mtime to now
			# This is because renames will not update the mtime
			fileCurrentStat?.mtime = new Date()

			# Create the file object
			file = docpad.addModel({fullPath:filePath, stat:fileCurrentStat})
			file.setStat(fileCurrentStat)  if changeType is 'update'

			# File was deleted, delete the rendered file, and remove it from the database
			if changeType is 'delete'
				database.remove(file)
				file.action 'delete', (err) ->
					return docpad.error(err)  if err
					queueRegeneration()

			# File is new or was changed, update it's mtime by setting the stat
			else if changeType in ['create', 'update']
				file.action 'load', (err) ->
					return docpad.error(err)  if err
					queueRegeneration()

		# Watch
		docpad.log(locale.watchStart)
		restartWatchers (err) ->
			return next(err)  if err
			docpad.log(locale.watchStarted)
			return next()

		# Chain
		@


	# ---------------------------------
	# Run Action

	###*
	# Run an action
	# @method run
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	run: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts, next)
		docpad = @
		locale = @getLocale()
		config = @getConfig()
		{srcPath, rootPath} = config

		# Prepare
		run = (complete) ->
			balUtil.flow(
				object: docpad
				action: 'server generate watch'
				args: [opts]
				next: complete
			)

		# Check if we have the docpad structure
		safefs.exists srcPath, (exists) ->
			# Check if have the correct structure, if so let's proceed with DocPad
			return run(next)  if exists

			# We don't have the correct structure
			# Check if we are running on an empty directory
			safefs.readdir rootPath, (err,files) ->
				return next(err)  if err

				# Check if our directory is empty
				if files.length
					# It isn't empty, display a warning
					docpad.warn util.format(locale.skeletonNonexistant, rootPath)
					return next()
				else
					docpad.skeleton opts, (err) ->
						# Check
						return next(err)  if err

						# Keep in global?
						return run(next)  if opts.global is true or docpad.getConfig().global is true

						# Log
						docpad.log('notice', locale.startLocal)

						# Destroy our DocPad instance so we can boot the local one
						docpad.destroy (err) ->
							# Check
							return next(err)  if err

							# Forward onto the local DocPad Instance now that it has been installed
							return docpadUtil.startLocalDocPadExecutable(next)

		# Chain
		@


	# ---------------------------------
	# Skeleton

	###*
	# Initialize the skeleton install process.
	# @private
	# @method initInstall
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	initInstall: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()

		# Tasks
		tasks = new @TaskGroup("initInstall tasks", {concurrency:0, next})

		tasks.addTask "node modules", (complete) ->
			path = pathUtil.join(config.rootPath, 'node_modules')
			safefs.ensurePath(path, complete)

		tasks.addTask "package", (complete) ->
			# Exists?
			path = pathUtil.join(config.rootPath, 'package.json')
			safefs.exists path, (exists) ->
				# Check
				return complete()  if exists

				# Write
				data = JSON.stringify({
					name: 'no-skeleton.docpad'
					version: '0.1.0'
					description: 'New DocPad project without using a skeleton'
					dependencies:
						docpad: '~'+docpad.getVersion()
					main: 'node_modules/.bin/docpad-server'
					scripts:
						start: 'node_modules/.bin/docpad-server'
				}, null, '  ')
				safefs.writeFile(path, data, complete)

		# Run
		tasks.run()

		# Chain
		@

	###*
	# Uninstall a plugin.
	# @private
	# @method uninstall
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	uninstall: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()

		# Tasks
		tasks = new @TaskGroup("uninstall tasks", {next})

		# Uninstall a plugin
		if opts.plugin
			tasks.addTask "uninstall the plugin: #{opts.plugin}", (complete) ->
				plugins =
					for plugin in opts.plugin.split(/[,\s]+/)
						plugin = "docpad-plugin-#{plugin}"  if plugin.indexOf('docpad-plugin-') isnt 0
						plugin
				docpad.uninstallNodeModule(plugins, {
					stdio: 'inherit'
					next: complete
				})

		# Re-load configuration
		tasks.addTask "re-load configuration", (complete) ->
			docpad.load(complete)

		# Run
		tasks.run()

		# Chain
		@

	###*
	# Install a plugin
	# @private
	# @method install
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	install: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()

		# Tasks
		tasks = new @TaskGroup("install tasks", {next})

		tasks.addTask "init the installation", (complete) ->
			docpad.initInstall(opts, complete)

		# Install a plugin
		if opts.plugin
			tasks.addTask "install the plugin: #{opts.plugin}", (complete) ->
				plugins =
					for plugin in opts.plugin.split(/[,\s]+/)
						plugin = "docpad-plugin-#{plugin}"  if plugin.indexOf('docpad-plugin-') isnt 0
						plugin += '@'+docpad.pluginVersion  if plugin.indexOf('@') is -1
						plugin
				docpad.installNodeModule(plugins, {
					stdio: 'inherit'
					next: complete
				})

		tasks.addTask "re-initialize the website's modules", (complete) ->
			docpad.initNodeModules({
				stdio: 'inherit'
				next: complete
			})

		tasks.addTask "fix node package versions", (complete) ->
			docpad.fixNodePackageVersions(complete)

		tasks.addTask "re-load the configuration", (complete) ->
			docpad.load(complete)

		# Run
		tasks.run()

		# Chain
		@

	###*
	# Update global NPM and DocPad
	# @private
	# @method upgrade
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @return {Object} description
	###
	upgrade: (opts,next) ->
		# Update Global NPM and DocPad
		@installNodeModule('npm docpad@6', {
			global: true
			stdio: 'inherit'
			next: next
		})

		# Chain
		@

	###*
	# Update the local DocPad and plugin dependencies
	# @private
	# @method update
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	###
	update: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()

		# Tasks
		tasks = new @TaskGroup("update tasks", {next})

		tasks.addTask "init the install", (complete) ->
			docpad.initInstall(opts, complete)

		# Update the local docpad and plugin dependencies
		# Grouped together to avoid npm dependency shortcuts that can cause missing dependencies
		# But don't update git/http/https dependencies, those are special for some reason
		# > https://github.com/bevry/docpad/pull/701
		dependencies = []
		eachr docpad.websitePackageConfig.dependencies, (version,name) ->
			return  if /^docpad-plugin-/.test(name) is false or /// :// ///.test(version) is true
			dependencies.push(name+'@'+docpad.pluginVersion)
		if dependencies.length isnt 0
			tasks.addTask "update plugins that are dependencies", (complete) ->
				docpad.installNodeModule('docpad@6 '+dependencies, {
					stdio: 'inherit'
					next: complete
				})

		# Update the plugin dev dependencies
		devDependencies = []
		eachr docpad.websitePackageConfig.devDependencies, (version,name) ->
			return  if /^docpad-plugin-/.test(name) is false
			devDependencies.push(name+'@'+docpad.pluginVersion)
		if devDependencies.length isnt 0
			tasks.addTask "update plugins that are dev dependencies", (complete) ->
				docpad.installNodeModule(devDependencies, {
					save: '--save-dev'
					stdio: 'inherit'
					next: complete
				})

		tasks.addTask "fix node package versions", (complete) ->
			docpad.fixNodePackageVersions(complete)

		tasks.addTask "re-initialize the rest of the website's modules", (complete) ->
			docpad.initNodeModules({
				stdio: 'inherit'
				next: complete
			})

		# Run
		tasks.run()

		# Chain
		@

	###*
	# DocPad cleanup tasks.
	# @private
	# @method clean
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @return {Object} description
	###
	clean: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = docpad.getConfig()
		locale = @getLocale()

		# Log
		docpad.log('info', locale.renderCleaning)

		# Tasks
		tasks = new @TaskGroup "clean tasks", {concurrency:0}, next:(err) ->
			# Error?
			return next(err)  if err

			# Log
			docpad.log('info', locale.renderCleaned)

			# Forward
			return next()

		tasks.addTask 'reset the collecitons', (complete) ->
			docpad.resetCollections(opts, complete)

		# Delete out path
		# but only if our outPath is not a parent of our rootPath
		tasks.addTask 'delete out path', (complete) ->
			# Check if our outPath is higher than our root path, so do not remove files
			return complete()  if config.rootPath.indexOf(config.outPath) isnt -1

			# Our outPath is not related or lower than our root path, so do remove it
			rimraf(config.outPath, complete)

		# Delete database cache
		tasks.addTask 'delete database cache file', (complete) ->
			safefs.unlink(config.databaseCachePath, complete)

		# Run tasks
		tasks.run()

		# Chain
		@



	###*
	# Initialize a Skeleton into to a Directory
	# @private
	# @method initSkeleton
	# @param {Object} skeletonModel
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	initSkeleton: (skeletonModel,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @getConfig()

		# Defaults
		opts.destinationPath ?= config.rootPath

		# Tasks
		tasks = new @TaskGroup("initSkeleton tasks", {next})

		tasks.addTask "ensure the path we are writing to exists", (complete) ->
			safefs.ensurePath(opts.destinationPath, complete)

		# Clone out the repository if applicable
		if skeletonModel? and skeletonModel.id isnt 'none'
			tasks.addTask "clone out the git repo", (complete) ->
				docpad.initGitRepo({
					cwd: opts.destinationPath
					url: skeletonModel.get('repo')
					branch: skeletonModel.get('branch')
					remote: 'skeleton'
					stdio: 'inherit'
					next: complete
				})
		else
			tasks.addTask "ensure src path exists", (complete) ->
				safefs.ensurePath(config.srcPath, complete)

			tasks.addGroup "initialize the website directory files", ->
				@setConfig(concurrency:0)

				# README
				@addTask "README.md", (complete) ->
					# Exists?
					path = pathUtil.join(config.rootPath, 'README.md')
					safefs.exists path, (exists) ->
						# Check
						return complete()  if exists

						# Write
						data = """
							# Your [DocPad](http://docpad.org) Project

							## License
							Copyright &copy; #{(new Date()).getFullYear()}+ All rights reserved.
							"""
						safefs.writeFile(path, data, complete)

				# Config
				@addTask "docpad.coffee configuration file", (complete) ->
					# Exists?
					docpad.getConfigPath (err,path) ->
						# Check
						return complete(err)  if err or path
						path = pathUtil.join(config.rootPath, 'docpad.coffee')

						# Write
						data = """
							# DocPad Configuration File
							# http://docpad.org/docs/config

							# Define the DocPad Configuration
							docpadConfig = {
								# ...
							}

							# Export the DocPad Configuration
							module.exports = docpadConfig
							"""
						safefs.writeFile(path, data, complete)

				# Documents
				@addTask "documents directory", (complete) ->
					safefs.ensurePath(config.documentsPaths[0], complete)

				# Layouts
				@addTask "layouts directory", (complete) ->
					safefs.ensurePath(config.layoutsPaths[0], complete)

				# Files
				@addTask "files directory", (complete) ->
					safefs.ensurePath(config.filesPaths[0], complete)

		# Run
		tasks.run()

		# Chain
		@

	###*
	# Install a Skeleton into a Directory
	# @private
	# @method installSkeleton
	# @param {Object} skeletonModel
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	installSkeleton: (skeletonModel,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @

		# Defaults
		opts.destinationPath ?= @getConfig().rootPath

		# Initialize and install the skeleton
		docpad.initSkeleton skeletonModel, opts, (err) ->
			# Check
			return next(err)  if err

			# Forward
			docpad.install(opts, next)

		# Chain
		@

	###*
	# Use a Skeleton
	# @private
	# @method useSkeleton
	# @param {Object} skeletonModel
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @return {Object} description
	###
	useSkeleton: (skeletonModel,opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		locale = @getLocale()

		# Defaults
		opts.destinationPath ?= @getConfig().rootPath

		# Extract
		skeletonId = skeletonModel?.id or 'none'
		skeletonName = skeletonModel?.get('name') or locale.skeletonNoneName

		# Track
		docpad.track('skeleton-use', {skeletonId})

		# Log
		docpad.log('info', util.format(locale.skeletonInstall, skeletonName, opts.destinationPath)+' '+locale.pleaseWait)

		# Install Skeleton
		docpad.installSkeleton skeletonModel, opts, (err) ->
			# Error?
			return next(err)  if err

			# Log
			docpad.log('info', locale.skeletonInstalled)

			# Forward
			return next(err)

		# Chain
		@


	###*
	# Select a Skeleton
	# @private
	# @method selectSkeleton
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	# @param {Error} next.skeletonModel
	###
	selectSkeleton: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		opts.selectSkeletonCallback ?= null

		# Track
		docpad.track('skeleton-ask')

		# Get the available skeletons
		docpad.getSkeletons (err,skeletonsCollection) ->
			# Check
			return next(err)  if err

			# Provide selection to the interface
			opts.selectSkeletonCallback(skeletonsCollection, next)

		# Chain
		@

	###*
	# Skeleton Empty?
	# @private
	# @method skeletonEmpty
	# @param {Object} path
	# @param {Function} next
	# @param {Error} next.err
	###
	skeletonEmpty: (path, next) ->
		# Prepare
		locale = @getLocale()

		# Defaults
		path ?= @getConfig().rootPath

		# Check the destination path is empty
		safefs.exists pathUtil.join(path, 'package.json'), (exists) ->
			# Check
			if exists
				err = new Error(locale.skeletonExists)
				return next(err)

			# Success
			return next()

		# Chain
		@

	###*
	# Initialize the project directory
	# with the basic skeleton.
	# @private
	# @method skeleton
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	skeleton: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		opts.selectSkeletonCallback ?= null

		# Init the directory with the basic skeleton
		@skeletonEmpty null, (err) ->
			# Check
			return next(err)  if err

			# Select Skeleton
			docpad.selectSkeleton opts, (err,skeletonModel) ->
				# Check
				return next(err)  if err

				# Use Skeleton
				docpad.useSkeleton(skeletonModel, next)

		# Chain
		@

	###*
	# Initialize the project directory
	# with the basic skeleton.
	# @private
	# @method init
	# @param {Object} opts
	# @param {Object} next
	# @param {Error} next.err
	# @return {Object} description
	###
	init: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @

		# Init the directory with the basic skeleton
		@skeletonEmpty null, (err) ->
			# Check
			return next(err)  if err

			# Basic Skeleton
			docpad.useSkeleton(null, next)

		# Chain
		@


	# ---------------------------------
	# Server

	###*
	# Serve a document
	# @private
	# @method serveDocument
	# @param {Object} opts
	# @param {Function} next
	# @param {Error} next.err
	###
	serveDocument: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		{document,err,req,res} = opts
		docpad = @
		config = @getConfig()

		# If no document, then exit early
		unless document
			if opts.statusCode?
				return res.send(opts.statusCode)
			else
				return next()

		# Prepare
		res.setHeaderIfMissing ?= (name, value) ->
			res.setHeader(name, value)  unless res.getHeader(name)

		# Content Type + Encoding/Charset
		encoding = document.get('encoding')
		charset = 'utf-8'  if encoding in ['utf8', 'utf-8']
		contentType = document.get('outContentType') or document.get('contentType')
		res.setHeaderIfMissing('Content-Type', contentType + (if charset then "; charset=#{charset}" else ''))

		# Cache-Control (max-age)
		res.setHeaderIfMissing('Cache-Control', "public, max-age=#{config.maxAge}")  if config.maxAge

		# Send
		dynamic = document.get('dynamic')
		if dynamic
			# If you are debugging why a dynamic document isn't rendering
			# it could be that you don't have cleanurls installed
			# e.g. if index.html is dynamic, and you are accessing it via /
			# then this code will not be reached, as we don't register that url
			# where if we have the cleanurls plugin installed, then do register that url
			# against the document, so this is reached
			collection = new FilesCollection([document], {name:'dynamic collection'})
			templateData = extendr.extend({}, req.templateData or {}, {req,err})
			docpad.action 'generate', {collection, templateData}, (err) ->
				content = document.getOutContent()
				if err
					docpad.error(err)
					return next(err)
				else
					if opts.statusCode?
						return res.send(opts.statusCode, content)
					else
						return res.send(content)

		else
			# ETag: `"<size>-<mtime>"`
			ctime = document.get('date')    # use the date or mtime, it should always exist
			mtime = document.get('wtime')   # use the last generate time, it may not exist though
			stat = document.getStat()
			etag = stat.size + '-' + Number(mtime)   if mtime and stat
			res.setHeaderIfMissing('ETag', '"' + etag + '"')  if etag

			# Date
			res.setHeaderIfMissing('Date', ctime.toUTCString())  if ctime?.toUTCString?
			res.setHeaderIfMissing('Last-Modified', mtime.toUTCString())  if mtime?.toUTCString?
			# @TODO:
			# The above .toUTCString? check is a workaround because sometimes the date object
			# isn't really a date object, this needs to be fixed properly
			# https://github.com/bevry/docpad/pull/781

			# Send
			if etag and etag is (req.get('If-None-Match') or '').replace(/^"|"$/g, '')
				res.send(304)  # not modified
			else
				content = document.getOutContent()
				if content
					if opts.statusCode?
						res.send(opts.statusCode, content)
					else
						res.send(content)
				else
					if opts.statusCode?
						res.send(opts.statusCode)
					else
						next()

		# Chain
		@


	###*
	# Server Middleware: Header
	# @private
	# @method serverMiddlewareHeader
	# @param {Object} req
	# @param {Object} res
	# @param {Object} next
	###
	serverMiddlewareHeader: (req,res,next) ->
		# Prepare
		docpad = @

		# Handle
		# Always enable this until we get a complaint about not having it
		# For instance, Express.js also forces this
		tools = res.get('X-Powered-By').split(/[,\s]+/g)
		tools.push("DocPad v#{docpad.getVersion()}")
		tools = tools.join(', ')
		res.set('X-Powered-By', tools)

		# Forward
		next()

		# Chain
		@


	###*
	# Server Middleware: Router
	# @private
	# @method serverMiddlewareRouter
	# @param {Object} req
	# @param {Object} res
	# @param {Function} next
	# @param {Error} next.err
	###
	serverMiddlewareRouter: (req,res,next) ->
		# Prepare
		docpad = @

		# Get the file
		docpad.getFileByRoute req.url, (err,file) ->
			# Check
			return next(err)  if err or file? is false

			# Check if we are the desired url
			# if we aren't do a permanent redirect
			url = file.get('url')
			cleanUrl = docpad.getUrlPathname(req.url)
			if (url isnt cleanUrl) and (url isnt req.url)
				return res.redirect(301, url)

			# Serve the file to the user
			docpad.serveDocument({document:file, req, res, next})

		# Chain
		@


	###*
	# Server Middleware: 404
	# @private
	# @method serverMiddleware404
	# @param {Object} req
	# @param {Object} res
	# @param {Object} next
	###
	serverMiddleware404: (req,res,next) ->
		# Prepare
		docpad = @
		database = docpad.getDatabaseSafe()

		# Notify the user of a 404
		docpad.log('notice', "404 Not Found:", req.url)

		# Check
		return res.send(500)  unless database

		# Serve the document to the user
		document = database.findOne({relativeOutPath: '404.html'})
		docpad.serveDocument({document, req, res, next, statusCode:404})

		# Chain
		@


	###*
	# Server Middleware: 500
	# @private
	# @method serverMiddleware500
	# @param {Object} err
	# @param {Object} req
	# @param {Object} res
	# @param {Function} next
	###
	serverMiddleware500: (err,req,res,next) ->
		# Prepare
		docpad = @
		database = docpad.getDatabaseSafe()

		# Check
		return res.send(500)  unless database

		# Serve the document to the user
		document = database.findOne({relativeOutPath: '500.html'})
		docpad.serveDocument({document,err,req,res,next,statusCode:500})

		# Chain
		@

	###*
	# Configure and start up the DocPad web server.
	# Http and express server is created, extended with
	# middleware, started up and begins listening.
	# The events serverBefore, serverExtend and
	# serverAfter emitted here.
	# @private
	# @method server
	# @param {Object} opts
	# @param {Function} next
	###
	server: (opts,next) ->
		# Prepare
		[opts,next] = extractOptsAndCallback(opts,next)
		docpad = @
		config = @config
		locale = @getLocale()
		port = @getPort()
		hostname = @getHostname()

		# Require
		http = require('http')
		express = require('express')

		# Config
		servers = @getServer(true)
		opts.serverExpress ?= servers.serverExpress
		opts.serverHttp ?= servers.serverHttp
		opts.middlewareBodyParser ?= config.middlewareBodyParser ? config.middlewareStandard
		opts.middlewareMethodOverride ?= config.middlewareMethodOverride ? config.middlewareStandard
		opts.middlewareExpressRouter ?= config.middlewareExpressRouter ? config.middlewareStandard
		opts.middleware404 ?= config.middleware404
		opts.middleware500 ?= config.middleware500
		# @TODO: Why do we do opts here instead of config???

		# Tasks
		tasks = new @TaskGroup("server tasks", {next})

		# Before Plugin Event
		tasks.addTask "emit serverBefore", (complete) ->
			docpad.emitSerial('serverBefore', complete)

		# Create server when none is defined
		if !opts.serverExpress or !opts.serverHttp
			tasks.addTask "create server", ->
				opts.serverExpress or= express()
				opts.serverHttp or= http.createServer(opts.serverExpress)
				docpad.setServer(opts)

		# Extend the server with our middlewares
		if config.extendServer is true
			tasks.addTask "extend the server", (complete) ->
				# Parse url-encoded and json encoded form data
				if opts.middlewareBodyParser isnt false
					opts.serverExpress.use(express.urlencoded())
					opts.serverExpress.use(express.json())

				# Allow over-riding of the request type (e.g. GET, POST, PUT, DELETE)
				if opts.middlewareMethodOverride isnt false
					if typeChecker.isString(opts.middlewareMethodOverride)
						opts.serverExpress.use(require('method-override')(opts.middlewareMethodOverride))
					else
						opts.serverExpress.use(require('method-override')())

				# Emit the serverExtend event
				# So plugins can define their routes earlier than the DocPad routes
				docpad.emitSerial 'serverExtend', {
					server: opts.serverExpress # b/c
					express: opts.serverExpress # b/c
					serverHttp: opts.serverHttp
					serverExpress: opts.serverExpress
				}, (err) ->
					return next(err)  if err

					# DocPad Header Middleware
					# Keep it after the serverExtend event
					opts.serverExpress.use(docpad.serverMiddlewareHeader)

					# Router Middleware
					# Keep it after the serverExtend event
					opts.serverExpress.use(opts.serverExpress.router)  if opts.middlewareExpressRouter isnt false

					# DocPad Router Middleware
					# Keep it after the serverExtend event
					opts.serverExpress.use(docpad.serverMiddlewareRouter)

					# Static
					# Keep it after the serverExtend event
					if config.maxAge
						opts.serverExpress.use(express.static(config.outPath, {maxAge:config.maxAge}))
					else
						opts.serverExpress.use(express.static(config.outPath))

					# DocPad 404 Middleware
					# Keep it after the serverExtend event
					opts.serverExpress.use(docpad.serverMiddleware404)  if opts.middleware404 isnt false

					# DocPad 500 Middleware
					# Keep it after the serverExtend event
					opts.serverExpress.use(docpad.serverMiddleware500)  if opts.middleware500 isnt false

					# Done
					return complete()

		# Start Server
		tasks.addTask "start the server", (complete) ->
			# Catch
			opts.serverHttp.once 'error', (err) ->
				# Friendlify the error message if it is what we suspect it is
				if err.message.indexOf('EADDRINUSE') isnt -1
					err = new Error(util.format(locale.serverInUse, port))

				# Done
				return complete(err)

			# Listen
			docpad.log 'debug', util.format(locale.serverStart, hostname, port)
			opts.serverHttp.listen port, hostname,  ->
				# Log
				address = opts.serverHttp.address()
				serverUrl = docpad.getServerUrl(
					hostname: address.hostname
					port: address.port
				)
				simpleServerUrl = docpad.getSimpleServerUrl(
					hostname: address.hostname
					port: address.port
				)
				docpad.log 'info', util.format(locale.serverStarted, serverUrl)
				if serverUrl isnt simpleServerUrl
					docpad.log 'info', util.format(locale.serverBrowse, simpleServerUrl)

				# Done
				return complete()

		# After Plugin Event
		tasks.addTask "emit serverAfter", (complete) ->
			docpad.emitSerial('serverAfter', {
				server: opts.serverExpress # b/c
				express: opts.serverExpress # b/c
				serverHttp: opts.serverHttp
				serverExpress: opts.serverExpress
			}, complete)

		# Run the tasks
		tasks.run()

		# Chain
		@


# ---------------------------------
# Export

module.exports = DocPad