********************************************************************
Register handlebars helpers.
function() {
    var self = this
    // -- generic --

    // indent
    handlebars.registerHelper('indent', function(options) {
      var spaces = options.hash.spaces || 4
      var text = options.fn(this).split('\n')
      text = text.map(function(line) {
        return Array(spaces + 1).join(' ') + line
      })
      return text.join('\n') + '\n'
    })

    // if is object
    handlebars.registerHelper('ifisobject', function(_var, options) {
      if (_var !== null && typeof _var === 'object') {
        return options.fn(this)
      }
      return options.inverse(this)
    })

    // if not empty
    handlebars.registerHelper('ifnotempty', function(collection, options) {
      if (((collection instanceof Array) && collection.length > 0 ||
        ((collection instanceof Object) && _.keys(collection).length > 0))) {
        return options.fn(this)
      }
    })

    // slugify
    handlebars.registerHelper('slugify', function(options) {
      var str = options.fn(this).toLowerCase()
      return str.replace(/^\/|\/$/g, '').replace(/\/| |:/g, '-')
    })

    // defined or null
    handlebars.registerHelper('definedornull', function(value, options) {
      if (value == null) {
        return 'null'
      }
      return options.fn(this)
    })

    // ===
    handlebars.registerHelper('if_eq', function(a, b, options) {
      if (a === b) {
        return options.fn(this)
      }
      return options.inverse(this)
    })

    // !=
    handlebars.registerHelper('if_ne', function(a, b, options) {
      if (a != b) {
        return options.fn(this)
      }
      return options.inverse(this)
    })

    // !=
    handlebars.registerHelper('if_nea', function(string, obj, options) {
      var split = string.split(',')
      if (split.length > 1) {
        if (split.indexOf(obj) < 0) {
          return options.fn(this)
        }
      }
      return options.inverse(this)
    })

    // ||
    handlebars.registerHelper('or', function(a, b) {
      return a || b
    })

    // &&
    handlebars.registerHelper('and', function(a, b) {
      return a && b
    })

    var falsey = function(value) {
      return (
        _.isString(value) && value.length == 0 ||
        _.isArray(value) && value.length == 0 ||
        _.isNaN(value) ||
        _.isNull(value) ||
        _.isUndefined(value) ||
        _.isObject(value) && _.keys(value).length == 0
      )
    }

    // truthy
    handlebars.registerHelper('truthy', function(value, options) {
      if (!falsey(value)) {
        return options.fn(this)
      }
      return options.inverse(this)
    })

    // falsey
    handlebars.registerHelper('falsey', function(value, options) {
      if (falsey(value)) {
        return options.fn(this)
      }
      return options.inverse(this)
    })

    // -- markdown --

    // body (formatted schema)
    handlebars.registerHelper('MDRenderBody', function(schema) {
      return new handlebars.SafeString(self._renderBody(schema))
    })

    // schema
    handlebars.registerHelper('MDRenderSchema', function(schema) {
      return new handlebars.SafeString(self._renderSchema(schema))
    })

    // code block
    handlebars.registerHelper('MDCodeBlock', function(options) {
      return '```\n' + options.fn(this) + '\n```\n'
    })

    // headers
    handlebars.registerHelper('MDHeading', function(options) {
      var level = options.hash.level || 1
      return Array(level + 1).join('#') + ' ' + options.fn(this)
    })

    // block quote
    handlebars.registerHelper('MDBlockQuote', function(options) {
      var level = options.hash.level || 1
      var text = options.fn(this).split('\n')
      text = text.map(function(line) {
        return Array(level + 1).join('> ') + line
      })
      return text.join('\n') + '\n'
    })
  }