module html_templates
import html_model
+import html::bootstrap
+import doc_phases::doc_structure
+import doc_phases::doc_hierarchies
+import doc_phases::doc_graphs
+import doc_phases::doc_intros_redefs
+import doc_phases::doc_lin
+import doc_phases::doc_readme
+intrude import doc_down
# Renders the page as HTML.
redef class DocPage
var body_attrs = new Array[TagAttribute]
# Top menu template if any.
- var topmenu: TplTopMenu is writable, noinit
+ var topmenu: DocTopMenu is writable, noinit
# Sidebar template if any.
- var sidebar: nullable TplSidebar = null is writable
-
- # Content of the page in form a TplSection.
- # TODO remove when other templates are migrated.
- var sections = new Array[TplSection]
+ var sidebar: nullable DocSideBar = null is writable
# Footer content if any.
var footer: nullable Writable = null is writable
# JS scripts to append at the end of the body
var scripts = new Array[TplScript]
- # Adds a section to this page.
- # TODO remove when other templates are migrated.
- fun add_section(section: TplSection) do
- sections.add section
- end
-
# Renders the html `<head>`.
private fun render_head do
var css = (self.shareurl / "css").html_escape
addn ">"
end
- # Renders the topmenu template.
- private fun render_topmenu do
- addn " <div class='row'>"
- add topmenu
- addn " </div>"
- end
-
- # Renders the sidebar template.
- #
- # Sidebar is automatically populated with a summary of all sections
- # TODO remove summary generation when other templates are migrated.
- private fun render_sidebar do
- if sidebar == null then return
- var summary = new TplSummary.with_order(0)
- for section in sections do
- section.render_summary summary
- end
- sidebar.boxes.add summary
- add sidebar.as(not null)
- end
-
# Renders the footer and content.
private fun render_content do
- for section in sections do add section
+ add root
if footer != null then
addn "<div class='well footer'>"
add footer.as(not null)
addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
- addn "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
+ addn "<script src='{js}/lib/utils.js'></script>"
+ addn "<script src='{js}/plugins/filtering.js'></script>"
+ addn "<script src='quicksearch-list.js'></script>"
+ addn "<script src='{js}/plugins/quicksearch.js'></script>"
for script in scripts do add script
addn """<script>
$(function () {
redef fun rendering do
render_head
addn "<div class='container-fluid'>"
- render_topmenu
+ addn " <div class='row'>"
+ add topmenu
+ addn " </div>"
addn " <div class='row' id='content'>"
+ var sidebar = self.sidebar
if sidebar != null then
addn "<div class='col col-xs-3 col-lg-2'>"
- render_sidebar
+ add sidebar
addn "</div>"
addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
render_content
addn "</div>"
render_footer
end
+
+ # Render table of content for this page.
+ fun html_toc: UnorderedList do
+ var lst = new UnorderedList
+ lst.css_classes.add "nav"
+ for child in root.children do
+ child.render_toc_item(lst)
+ end
+ return lst
+ end
+end
+
+# Top menu bar template.
+#
+# FIXME should be a Bootstrap component template
+# At this moment, the topmenu structure stills to specific to Nitdoc to use the
+# generic component.
+class DocTopMenu
+ super UnorderedList
+
+ # Brand link to display in first position of the top menu.
+ #
+ # This is where you want to put your logo.
+ var brand: nullable Writable is noinit, writable
+
+ # Active menu item.
+ #
+ # Depends on the current page, this allows to hilighted the current item.
+ #
+ # FIXME should be using Boostrap breadcrumbs component.
+ # This will still like this to avoid diff and be changed in further fixes
+ # when we will modify the output.
+ var active_item: nullable ListItem is noinit, writable
+
+ redef fun rendering do
+ addn "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
+ addn " <div class='container-fluid'>"
+ addn " <div class='navbar-header'>"
+ add " <button type='button' class='navbar-toggle' "
+ addn " data-toggle='collapse' data-target='#topmenu-collapse'>"
+ addn " <span class='sr-only'>Toggle menu</span>"
+ addn " <span class='icon-bar'></span>"
+ addn " <span class='icon-bar'></span>"
+ addn " <span class='icon-bar'></span>"
+ addn " </button>"
+ if brand != null then
+ add "<span class='navbar-brand'>"
+ add brand.write_to_string
+ add "</span>"
+ end
+ addn " </div>"
+ addn " <div class='collapse navbar-collapse' id='topmenu-collapse'>"
+ addn " <ul class='nav navbar-nav'>"
+ for item in items do
+ if item == active_item then item.css_classes.add "active"
+ add item.write_to_string
+ end
+ addn " </ul>"
+ addn " </div>"
+ addn " </div>"
+ addn "</nav>"
+ end
+end
+
+# Nitdoc sidebar template.
+class DocSideBar
+ super Template
+
+ # Sidebar contains `DocSideBox`.
+ var boxes = new Array[DocSideBox]
+
+ redef fun rendering do
+ if boxes.is_empty then return
+ addn "<div id='sidebar'>"
+ for box in boxes do add box
+ addn "</div>"
+ end
+end
+
+# Something that can be put in a DocSideBar.
+class DocSideBox
+ super Template
+
+ # Box HTML id, used for Bootstrap collapsing feature.
+ #
+ # Use `html_title.to_cmangle` by default.
+ var id: String is lazy do return title.write_to_string.to_cmangle
+
+ # Title of the box to display.
+ var title: Writable
+
+ # Content to display in the box.
+ var content: Writable
+
+ # Is the box opened by default?
+ #
+ # Otherwise, the user will have to clic on the title to display the content.
+ #
+ # Default is `true`.
+ var is_open = true is writable
+
+ redef fun rendering do
+ var open = ""
+ if is_open then open = "in"
+ addn "<div class='panel'>"
+ addn " <div class='panel-heading'>"
+ add " <a data-toggle='collapse' data-parent='#sidebar'"
+ add " data-target='#box_{id}' href='#'>"
+ add title
+ addn " </a>"
+ addn " </div>"
+ addn " <div id='box_{id}' class='summary panel-body collapse {open}'>"
+ add content
+ addn " </div>"
+ addn "</div>"
+ end
+end
+
+redef class DocComposite
+ super Template
+
+ # HTML anchor id
+ var html_id: String is writable, lazy do return id
+
+ # Title to display if any.
+ #
+ # This title can be decorated with HTML.
+ var html_title: nullable Writable is writable, lazy do return title
+
+ # Subtitle to display if any.
+ var html_subtitle: nullable Writable is noinit, writable
+
+ # Render the element title and subtitle.
+ private fun render_title do
+ if html_title != null then
+ var header = new Header(hlvl, html_title.write_to_string)
+ header.css_classes.add "signature"
+ addn header
+ end
+ if html_subtitle != null then
+ addn "<div class='info subtitle'>"
+ addn html_subtitle.write_to_string
+ addn "</div>"
+ end
+ end
+
+ # Render the element body.
+ private fun render_body do
+ for child in children do addn child.write_to_string
+ end
+
+ redef fun rendering do
+ if is_hidden then return
+ render_title
+ render_body
+ end
+
+ # Level <hX> for HTML heading.
+ private fun hlvl: Int do return depth
+
+ # A short, undecorated title that goes in the table of contents.
+ #
+ # By default, returns `html_title.to_s`, subclasses should redefine it.
+ var html_toc_title: nullable String is lazy, writable do
+ if html_title == null then return toc_title
+ return html_title.write_to_string
+ end
+
+ # Render this element in a table of contents.
+ private fun render_toc_item(lst: UnorderedList) do
+ if is_toc_hidden or html_toc_title == null then return
+
+ var content = new Template
+ content.add new Link("#{html_id}", html_toc_title.to_s)
+ if not children.is_empty then
+ var sublst = new UnorderedList
+ sublst.css_classes.add "nav"
+ for child in children do
+ child.render_toc_item(sublst)
+ end
+ content.add sublst
+ end
+ lst.add_li new ListItem(content)
+ end
+
+ # ID used in HTML tab labels.
+ #
+ # We sanitize it for Boostrap JS panels that do not like ":" and "." in ids.
+ var html_tab_id: String is lazy do
+ var id = html_id.replace(":", "")
+ id = id.replace(".", "")
+ return "{id}-tab"
+ end
+end
+
+redef class DocRoot
+ redef fun rendering do
+ for child in children do addn child.write_to_string
+ end
+end
+
+redef class DocSection
+ super BSComponent
+
+ redef fun rendering do
+ if is_hidden then
+ addn "<a id=\"{html_id}\"></a>"
+ return
+ end
+ addn "<section{render_css_classes} id=\"{html_id}\">"
+ render_title
+ render_body
+ addn "</section>"
+ end
+end
+
+redef class DocArticle
+ super BSComponent
+
+ redef fun rendering do
+ if is_hidden then return
+ addn "<article{render_css_classes} id=\"{html_id}\">"
+ render_title
+ render_body
+ addn "</article>"
+ end
+end
+
+redef class TabbedGroup
+ redef fun render_body do
+ var tabs = new DocTabs("{html_id}.tabs", "")
+ for child in children do
+ if child.is_hidden then continue
+ var title = child.html_toc_title or else child.toc_title or else ""
+ tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
+ end
+ addn tabs
+ end
+end
+
+redef class PanelGroup
+ redef var html_title = null
+ redef var toc_title is lazy do return title or else ""
+ redef var is_toc_hidden = true
+end
+
+redef class HomeArticle
+ redef var html_title = "Overview"
+
+ # HTML content to display on the home page.
+ #
+ # This attribute is set by the `doc_render` phase who knows the context.
+ var content: nullable String is noinit, writable
+
+ redef fun render_body do
+ var content = self.content
+ if content != null then add content
+ super
+ end
+end
+
+redef class IndexArticle
+ redef var html_title = "Index"
+
+ redef fun render_body do
+ addn "<div class='container-fluid'>"
+ addn " <div class='row'>"
+ render_list("Modules", mmodules)
+ render_list("Classes", mclasses)
+ render_list("Properties", mprops)
+ addn "</div>"
+ addn "</div>"
+ end
+
+ # Displays a list from the content of `mentities`.
+ private fun render_list(title: String, mentities: Array[MEntity]) do
+ if mentities.is_empty then return
+ addn "<div class='col-xs-4'>"
+ addn new Header(3, title)
+ var lst = new UnorderedList
+ for mentity in mentities do
+ if mentity isa MProperty then
+ var tpl = new Template
+ tpl.add mentity.intro.html_link
+ tpl.add " ("
+ tpl.add mentity.intro.mclassdef.mclass.html_link
+ tpl.add ")"
+ lst.add_li new ListItem(tpl)
+ else
+ lst.add_li new ListItem(mentity.html_link)
+ end
+ end
+ addn lst
+ addn "</div>"
+ end
+end
+
+redef class MEntityComposite
+ redef var html_title is lazy do return mentity.nitdoc_name
+end
+
+redef class MEntitySection
+ redef var html_title is lazy do return mentity.html_name
+ redef var html_subtitle is lazy do return mentity.html_declaration
+end
+
+redef class ConcernSection
+ redef var html_title is lazy do return "in {mentity.nitdoc_name}"
+end
+
+redef class IntroArticle
+ redef var html_title = null
+
+ # Link to source to display if any.
+ var html_source_link: nullable Writable is noinit, writable
+
+ redef fun render_body do
+ var tabs = new DocTabs("{html_id}.tabs", "")
+ var comment = mentity.html_documentation
+ if mentity isa MPackage then
+ comment = mentity.html_synopsis
+ end
+ if comment != null then
+ tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+ end
+ for child in children do
+ if child.is_hidden then continue
+ var title = child.html_toc_title or else child.toc_title or else ""
+ tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
+ end
+ var lnk = html_source_link
+ if lnk != null then
+ tabs.drop_list.items.add new ListItem(lnk)
+ end
+ addn tabs
+ end
+end
+
+redef class ConcernsArticle
+ redef var html_title = "Concerns"
+ redef fun render_body do add concerns.html_list
+end
+
+redef class DefinitionListArticle
+ redef var html_title is lazy do
+ var title = new Template
+ title.add mentity.html_icon
+ title.add mentity.html_link
+ return title
+ end
+
+ redef var html_subtitle is lazy do return mentity.html_namespace
+ redef var html_toc_title is lazy do return mentity.html_name
+end
+
+redef class DefinitionArticle
+ redef var html_title is lazy do return mentity.html_name
+ redef var html_subtitle is lazy do return mentity.html_declaration
+
+ # Does `self` display only it's title and no body?
+ #
+ # FIXME diff hack
+ var is_no_body: Bool = false is writable
+
+ # Does `self` display only the short content as definition?
+ #
+ # FIXME diff hack
+ var is_short_comment: Bool = false is writable
+
+ # Link to source to display if any.
+ var html_source_link: nullable Writable is noinit, writable
+
+ redef fun render_body do
+ var tabs = new DocTabs("{html_id}.tabs", "")
+ if not is_no_body then
+ var comment
+ if is_short_comment or mentity isa MPackage then
+ comment = mentity.html_synopsis
+ else
+ comment = mentity.html_documentation
+ end
+ if comment != null then
+ tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+ end
+ end
+ for child in children do
+ if child.is_hidden then continue
+ var title = child.html_toc_title or else child.toc_title or else ""
+ tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
+ end
+ var lnk = html_source_link
+ if lnk != null then
+ tabs.drop_list.items.add new ListItem(lnk)
+ end
+ addn tabs
+ end
+end
+
+redef class MEntitiesListArticle
+ redef fun render_body do
+ var lst = new UnorderedList
+ lst.css_classes.add "list-unstyled list-definition"
+ for mentity in mentities do
+ lst.add_li mentity.html_list_item
+ end
+ add lst
+ end
+end
+
+redef class DefinitionLinArticle
+ redef fun render_body do
+ var lst = new UnorderedList
+ lst.css_classes.add "list-unstyled list-labeled"
+ for mentity in mentities do
+ if not mentity isa MPropDef then continue # TODO handle all mentities
+ var tpl = new Template
+ tpl.add mentity.mclassdef.html_namespace
+ var comment = mentity.mclassdef.html_synopsis
+ if comment != null then
+ tpl.add ": "
+ tpl.add comment
+ end
+ var li = new ListItem(tpl)
+ li.css_classes.add "signature"
+ lst.add_li li
+ end
+ add lst
+ end
+end
+
+redef class GraphArticle
+ redef var html_title = null
+
+ # Graph in SVG with clickable map.
+ #
+ # This attribute is set by the `doc_render` phase who knows the context.
+ var svg: nullable String = null is writable
+
+ redef fun render_body do
+ addn "<div class=\"text-center\">"
+ var svg = self.svg
+ if svg != null then add svg
+ addn "</div>"
+ end
+end
+
+redef class ReadmeSection
+ redef var html_id is lazy do
+ return markdown_processor.emitter.decorator.strip_id(html_title.as(not null).to_s)
+ end
+
+ redef var html_title is lazy do
+ return markdown_processor.process(title.as(not null))
+ end
+end
+
+redef class ReadmeArticle
+ redef var html_id = ""
+ redef var html_title = null
+ redef var is_toc_hidden = true
+
+ redef fun render_body do
+ add markdown_processor.process(md.trim.write_to_string)
+ end
+end
+
+redef class DocumentationArticle
+ redef var html_title is lazy do
+ var synopsis = mentity.html_synopsis
+ if synopsis == null then return mentity.html_link
+ return "{mentity.html_link.write_to_string} – {synopsis.write_to_string}"
+ end
+
+ redef var html_subtitle is lazy do return null
+ redef var html_toc_title is lazy do return mentity.html_name
+ redef var is_toc_hidden is lazy do return depth > 3
+
+ redef fun render_body do
+ var tabs = new DocTabs("{html_id}.tabs", "")
+ var comment = mentity.html_comment
+ if comment != null then
+ tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
+ end
+ for child in children do
+ if child.is_hidden then continue
+ var title = child.html_toc_title or else child.toc_title or else ""
+ tabs.add_panel new DocTabPanel(child.html_tab_id, title, child)
+ end
+ addn tabs
+ end
end