codo
Version:
A CoffeeScript documentation generator.
240 lines (196 loc) • 7.45 kB
text/coffeescript
strftime = require 'strftime'
FS = require 'fs'
Path = require 'path'
Templater = require './templater'
TreeBuilder = require './tree_builder'
Theme = require './_theme'
Codo = require '../../../lib/codo'
module.exports = class Theme.Theme
options: [
{name: 'private', alias: 'p', describe: 'Show privates', boolean: true, default: false}
{name: 'analytics', alias: 'a', describe: 'The Google analytics ID', default: false}
{name: 'title', alias: 't', describe: 'HTML Title', default: 'CoffeeScript API Documentation'}
]
@compile: (environment) ->
theme = new @(environment)
theme.compile()
constructor: (@environment) ->
@templater = new Templater(@environment.options.output)
@referencer = new Codo.Tools.Referencer(@environment)
compile: ->
@templater.compileAsset('javascript/application.js')
@templater.compileAsset('stylesheets/application.css')
@renderAlphabeticalIndex()
@render 'method_list', 'method_list.html'
@renderClasses()
@renderMixins()
@renderFiles()
@renderExtras()
@renderIndex()
@renderFuzzySearchData()
#
# HELPERS
#
awareOf: (needle) ->
@environment.references[needle]?
reference: (needle, prefix) ->
@pathFor(@environment.reference(needle), undefined, prefix)
anchorFor: (entity) ->
if entity instanceof Codo.Meta.Method
"#{entity.name}-#{entity.kind}"
else if entity instanceof Codo.Entities.Property
"#{entity.name}-property"
else if entity instanceof Codo.Entities.Variable
"#{entity.name}-variable"
pathFor: (kind, entity, prefix='') ->
unless entity?
entity = kind
kind = 'class' if entity instanceof Codo.Entities.Class
kind = 'mixin' if entity instanceof Codo.Entities.Mixin
kind = 'file' if entity instanceof Codo.Entities.File
kind = 'extra' if entity instanceof Codo.Entities.Extra
kind = 'method' if entity.entity instanceof Codo.Meta.Method
kind = 'variable' if entity.entity instanceof Codo.Entities.Variable
kind = 'property' if entity.entity instanceof Codo.Entities.Property
switch kind
when 'file', 'extra'
prefix + kind + '/' + entity.name + '.html'
when 'class', 'mixin'
prefix + kind + '/' + entity.name.replace(/\./, '/') + '.html'
when 'method', 'variable', 'property'
@pathFor(entity.owner, undefined, prefix) + '#' + @anchorFor(entity.entity)
else
entity
activate: (text, prefix, limit=false) ->
text = @referencer.resolve text, (link, label) =>
"<a href='#{@pathFor link, undefined, prefix}'>#{label}</a>"
Codo.Tools.Markdown.convert(text, limit)
generateBreadcrumbs: (entries = []) ->
entries = [entries] unless Array.isArray(entries)
breadcrumbs = []
if @environment.options.readme
breadcrumbs.push
href: @pathFor('extra', @environment.findReadme())
title: @environment.options.name
breadcrumbs.push(href: 'alphabetical_index.html', title: 'Index')
for entry in entries
if entry instanceof Object
breadcrumbs.push entry
else
breadcrumbs.push {title: entry}
breadcrumbs
calculatePath: (filename) ->
dirname = Path.dirname(filename)
dirname.split(/[\/\\]/).map(-> '..').join('/')+'/' unless dirname == '.'
render: (source, destination, context={}) ->
globalContext =
environment: @environment
path: @calculatePath(destination)
strftime: strftime
anchorFor: @anchorFor
pathFor: @pathFor
reference: @reference
awareOf: @awareOf
activate: => @activate(arguments...)
render: (template, context={}) =>
context[key] = value for key, value of globalContext
@templater.render template, context
context[key] = value for key, value of globalContext
@templater.render source, context, destination
#
# RENDERERS
#
# Generate the alphabetical index of all classes and mixins.
#
renderAlphabeticalIndex: ->
classes = {}
mixins = {}
files = {}
# Sort in character group
for code in [97..122]
char = String.fromCharCode(code)
map = [
[@environment.visibleClasses(), classes],
[@environment.visibleMixins(), mixins],
[@environment.visibleFiles(), files]
]
for [list, storage] in map
for entry in list
if entry.basename.toLowerCase()[0] == char
storage[char] ?= []
storage[char].push(entry)
@render 'alphabetical_index', 'alphabetical_index.html',
classes: classes
mixins: mixins
files: files
renderIndex: ->
list = if @environment.visibleClasses().length > 0
'class_list.html'
else if @environment.visibleFiles().length > 0
'file_list.html'
else if @environment.visibleMixins().length > 0
'mixin_list.html'
else if @environment.visibleExtras().length > 0
'extra_list.html'
else
'method_list.html'
main = if @environment.options.readme
@pathFor('extra', @environment.findReadme())
else
'alphabetical_index.html'
@render 'frames', 'index.html',
list: list
main: main
renderClasses: ->
@render 'class_list', 'class_list.html',
tree: TreeBuilder.build @environment.visibleClasses(), (klass) ->
[klass.basename, klass.namespace.split('.')]
for klass in @environment.visibleClasses()
@render 'class', @pathFor('class', klass),
entity: klass,
breadcrumbs: @generateBreadcrumbs(klass.name.split '.')
renderMixins: ->
@render 'mixin_list', 'mixin_list.html',
tree: TreeBuilder.build @environment.visibleMixins(), (klass) ->
[klass.basename, klass.namespace.split('.')]
for mixin in @environment.visibleMixins()
@render 'mixin', @pathFor('mixin', mixin),
entity: mixin
breadcrumbs: @generateBreadcrumbs(mixin.name.split '.')
renderFiles: ->
@render 'file_list', 'file_list.html',
tree: TreeBuilder.build @environment.visibleFiles(), (file) ->
[file.basename, file.dirname.split('/')]
for file in @environment.visibleFiles()
@render 'file', @pathFor('file', file),
entity: file,
breadcrumbs: @generateBreadcrumbs(file.name.split '/')
renderExtras: ->
@render 'extra_list', 'extra_list.html',
tree: TreeBuilder.build @environment.visibleExtras(), (extra) ->
result = extra.name.split('/')
[result.pop(), result]
for extra in @environment.visibleExtras()
@render 'extra', @pathFor('extra', extra),
entity: extra
breadcrumbs: @generateBreadcrumbs(extra.name.split '/')
renderFuzzySearchData: ->
search = []
everything = [
@environment.visibleClasses(),
@environment.visibleMixins(),
@environment.visibleFiles(),
@environment.visibleExtras()
]
for basics in everything
for basic in basics
search.push
t: basic.name
p: @pathFor(basic)
for method in @environment.visibleMethods()
search.push
t: "#{method.owner.name}#{method.entity.shortSignature()}"
p: @pathFor(method)
content = 'window.searchData = ' + JSON.stringify(search)
output = Path.join(@environment.options.output, 'javascript', 'search.js')
FS.writeFileSync output, content