src/doc: migrate Index page rendering to `doc_phases`
[nit.git] / src / doc / html_templates / html_templates.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 # Introduces templates that compose the documentation HTML rendering.
16 module html_templates
17
18 import html_model
19 import html::bootstrap
20 import doc_phases::doc_structure
21 import doc_phases::doc_hierarchies
22 import doc_phases::doc_graphs
23 import doc_phases::doc_intros_redefs
24
25 # Renders the page as HTML.
26 redef class DocPage
27 super Template
28
29 # Page url.
30 var html_url: String is writable, noinit
31
32 # Directory where css, js and other assets can be found.
33 var shareurl: String is writable, noinit
34
35 # Attributes of the body tag element.
36 var body_attrs = new Array[TagAttribute]
37
38 # Top menu template if any.
39 var topmenu: DocTopMenu is writable, noinit
40
41 # Sidebar template if any.
42 var sidebar: nullable DocSideBar = null is writable
43
44 # Footer content if any.
45 var footer: nullable Writable = null is writable
46
47 # JS scripts to append at the end of the body
48 var scripts = new Array[TplScript]
49
50 # Renders the html `<head>`.
51 private fun render_head do
52 var css = (self.shareurl / "css").html_escape
53 var vendors = (self.shareurl / "vendors").html_escape
54
55 addn "<!DOCTYPE html>"
56 addn "<head>"
57 addn " <meta charset='utf-8'/>"
58 addn " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
59 addn " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
60 addn " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
61 addn " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
62 addn " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
63 addn " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
64 addn " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
65 addn " <title>{title.html_escape}</title>"
66 addn "</head>"
67 add "<body"
68 for attr in body_attrs do add attr
69 addn ">"
70 end
71
72 # Renders the footer and content.
73 private fun render_content do
74 add root
75 if footer != null then
76 addn "<div class='well footer'>"
77 add footer.as(not null)
78 addn "</div>"
79 end
80 end
81
82 # Render JS scripts
83 private fun render_footer do
84 var vendors = (self.shareurl / "vendors").html_escape
85 var js = (self.shareurl / "js").html_escape
86
87 addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
88 addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
89 addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
90 addn "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
91 for script in scripts do add script
92 addn """<script>
93 $(function () {
94 $("[data-toggle='tooltip']").tooltip();
95 $("[data-toggle='popover']").popover();
96 });
97 </script>"""
98 addn "</body>"
99 addn "</html>"
100 end
101
102 # Render the whole page
103 redef fun rendering do
104 render_head
105 addn "<div class='container-fluid'>"
106 addn " <div class='row'>"
107 add topmenu
108 addn " </div>"
109 addn " <div class='row' id='content'>"
110 var sidebar = self.sidebar
111 if sidebar != null then
112 addn "<div class='col col-xs-3 col-lg-2'>"
113 add sidebar
114 addn "</div>"
115 addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
116 render_content
117 addn "</div>"
118 else
119 addn "<div class='col col-xs-12'>"
120 render_content
121 addn "</div>"
122 end
123 addn " </div>"
124 addn "</div>"
125 render_footer
126 end
127
128 # Render table of content for this page.
129 fun html_toc: UnorderedList do
130 var lst = new UnorderedList
131 lst.css_classes.add "nav"
132 for child in root.children do
133 child.render_toc_item(lst)
134 end
135 return lst
136 end
137 end
138
139 # Top menu bar template.
140 #
141 # FIXME should be a Bootstrap component template
142 # At this moment, the topmenu structure stills to specific to Nitdoc to use the
143 # generic component.
144 class DocTopMenu
145 super UnorderedList
146
147 # Brand link to display in first position of the top menu.
148 #
149 # This is where you want to put your logo.
150 var brand: nullable Writable is noinit, writable
151
152 # Active menu item.
153 #
154 # Depends on the current page, this allows to hilighted the current item.
155 #
156 # FIXME should be using Boostrap breadcrumbs component.
157 # This will still like this to avoid diff and be changed in further fixes
158 # when we will modify the output.
159 var active_item: nullable ListItem is noinit, writable
160
161 redef fun rendering do
162 addn "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
163 addn " <div class='container-fluid'>"
164 addn " <div class='navbar-header'>"
165 add " <button type='button' class='navbar-toggle' "
166 addn " data-toggle='collapse' data-target='#topmenu-collapse'>"
167 addn " <span class='sr-only'>Toggle menu</span>"
168 addn " <span class='icon-bar'></span>"
169 addn " <span class='icon-bar'></span>"
170 addn " <span class='icon-bar'></span>"
171 addn " </button>"
172 if brand != null then
173 add "<span class='navbar-brand'>"
174 add brand.write_to_string
175 add "</span>"
176 end
177 addn " </div>"
178 addn " <div class='collapse navbar-collapse' id='topmenu-collapse'>"
179 addn " <ul class='nav navbar-nav'>"
180 for item in items do
181 if item == active_item then item.css_classes.add "active"
182 add item.write_to_string
183 end
184 addn " </ul>"
185 addn " </div>"
186 addn " </div>"
187 addn "</nav>"
188 end
189 end
190
191 # Nitdoc sidebar template.
192 class DocSideBar
193 super Template
194
195 # Sidebar contains `DocSideBox`.
196 var boxes = new Array[DocSideBox]
197
198 redef fun rendering do
199 if boxes.is_empty then return
200 addn "<div id='sidebar'>"
201 for box in boxes do add box
202 addn "</div>"
203 end
204 end
205
206 # Something that can be put in a DocSideBar.
207 class DocSideBox
208 super Template
209
210 # Box HTML id, used for Bootstrap collapsing feature.
211 #
212 # Use `html_title.to_cmangle` by default.
213 var id: String is lazy do return title.write_to_string.to_cmangle
214
215 # Title of the box to display.
216 var title: Writable
217
218 # Content to display in the box.
219 var content: Writable
220
221 # Is the box opened by default?
222 #
223 # Otherwise, the user will have to clic on the title to display the content.
224 #
225 # Default is `true`.
226 var is_open = true is writable
227
228 redef fun rendering do
229 var open = ""
230 if is_open then open = "in"
231 addn "<div class='panel'>"
232 addn " <div class='panel-heading'>"
233 add " <a data-toggle='collapse' data-parent='#sidebar'"
234 add " data-target='#box_{id}' href='#'>"
235 add title
236 addn " </a>"
237 addn " </div>"
238 addn " <div id='box_{id}' class='summary panel-body collapse {open}'>"
239 add content
240 addn " </div>"
241 addn "</div>"
242 end
243 end
244
245 redef class DocComposite
246 super Template
247
248 # HTML anchor id
249 var html_id: String is noinit, writable
250
251 # Title to display if any.
252 #
253 # This title can be decorated with HTML.
254 var html_title: nullable Writable is noinit, writable
255
256 # Subtitle to display if any.
257 var html_subtitle: nullable Writable is noinit, writable
258
259 # Render the element title and subtitle.
260 private fun render_title do
261 if html_title != null then
262 var header = new Header(hlvl, html_title.write_to_string)
263 header.css_classes.add "signature"
264 if hlvl == 2 then header.css_classes.add "well well-sm"
265 addn header
266 end
267 if html_subtitle != null then
268 addn "<div class='info subtitle'>"
269 addn html_subtitle.write_to_string
270 addn "</div>"
271 end
272 end
273
274 # Render the element body.
275 private fun render_body do
276 for child in children do addn child.write_to_string
277 end
278
279 redef fun rendering do
280 if is_hidden then return
281 render_title
282 render_body
283 end
284
285 # Level <hX> for HTML heading.
286 private fun hlvl: Int do
287 if parent == null then return 0
288 return parent.hlvl + 1
289 end
290
291 # Is `self` not displayed in the page.
292 #
293 # By default, empty elements are hidden.
294 fun is_hidden: Bool do return is_empty
295
296 # A short, undecorated title that goes in the table of contents.
297 #
298 # By default, returns `html_title.to_s`, subclasses should redefine it.
299 var toc_title: String is lazy, writable do return html_title.to_s
300
301 # Is `self` hidden in the table of content?
302 var is_toc_hidden = false is writable
303
304 # Render this element in a table of contents.
305 private fun render_toc_item(lst: UnorderedList) do
306 if is_toc_hidden then return
307
308 var content = new Template
309 content.add new Link("#{html_id}", toc_title)
310
311 if not children.is_empty then
312 var sublst = new UnorderedList
313 sublst.css_classes.add "nav"
314 for child in children do
315 child.render_toc_item(sublst)
316 end
317 content.add sublst
318 end
319 lst.add_li new ListItem(content)
320 end
321 end
322
323 redef class DocSection
324 super BSComponent
325
326 redef fun rendering do
327 if is_hidden then
328 addn "<a id=\"{html_id}\"></a>"
329 return
330 end
331 addn "<section{render_css_classes} id=\"{html_id}\">"
332 render_title
333 render_body
334 addn "</section>"
335 end
336 end
337
338 redef class DocArticle
339 super BSComponent
340
341 redef fun rendering do
342 if is_hidden then return
343 addn "<article{render_css_classes} id=\"{html_id}\">"
344 render_title
345 render_body
346 addn "</article>"
347 end
348 end
349
350 redef class HomeArticle
351 redef var html_id = "intro"
352 redef var html_title = "Overview"
353
354 # HTML content to display on the home page.
355 #
356 # This attribute is set by the `doc_render` phase who knows the context.
357 var content: nullable String is noinit, writable
358
359 redef fun render_body do
360 var content = self.content
361 if content != null then add content
362 super
363 end
364 end
365
366 redef class IndexArticle
367 redef var html_id = "index"
368 redef var html_title = "Index"
369 redef fun is_empty do
370 return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
371 end
372
373 redef fun render_body do
374 addn "<div class='container-fluid'>"
375 addn " <div class='row'>"
376 render_list("Modules", mmodules)
377 render_list("Classes", mclasses)
378 render_list("Properties", mprops)
379 addn "</div>"
380 addn "</div>"
381 end
382
383 # Displays a list from the content of `mentities`.
384 private fun render_list(title: String, mentities: Array[MEntity]) do
385 if mentities.is_empty then return
386 addn "<div class='col-xs-4'>"
387 addn new Header(3, title)
388 var lst = new UnorderedList
389 for mentity in mentities do
390 if mentity isa MProperty then
391 var tpl = new Template
392 tpl.add mentity.intro.html_link
393 tpl.add " ("
394 tpl.add mentity.intro.mclassdef.mclass.html_link
395 tpl.add ")"
396 lst.add_li new ListItem(tpl)
397 else
398 lst.add_li new ListItem(mentity.html_link)
399 end
400 end
401 addn lst
402 addn "</div>"
403 end
404 end
405
406 redef class ProjectsSection
407 redef var html_id = "projects"
408 redef var html_title = "Projects"
409 end
410
411 redef class MEntityComposite
412 redef var html_id is lazy do return mentity.nitdoc_id
413 redef var html_title is lazy do return mentity.nitdoc_name
414 end
415
416 redef class MEntitySection
417 redef var html_id is lazy do return "section_{mentity.nitdoc_name}"
418 redef var html_title is lazy do return mentity.html_name
419 redef var html_subtitle is lazy do return mentity.html_declaration
420 end
421
422 redef class ConcernSection
423 redef var html_id is lazy do return "section_concerns_{mentity.nitdoc_id}"
424 redef var html_title is lazy do return "in {mentity.nitdoc_name}"
425 redef fun is_toc_hidden do return is_empty
426 end
427
428 redef class ImportationListSection
429 redef var html_id is lazy do return "section_dependancies_{mentity.nitdoc_id}"
430 redef var html_title is lazy do return "Dependencies"
431 end
432
433 redef class InheritanceListSection
434 redef var html_id is lazy do return "section_inheritance_{mentity.nitdoc_id}"
435 redef var html_title is lazy do return "Inheritance"
436 end
437
438 redef class IntroArticle
439 redef var html_id is lazy do return "article_intro_{mentity.nitdoc_id}"
440 redef var html_title = null
441 redef var is_hidden = false
442 redef var is_toc_hidden = true
443
444 redef fun render_body do
445 var comment = mentity.html_comment
446 if comment != null then addn comment
447 super
448 end
449 end
450
451 redef class ConcernsArticle
452 redef var html_id is lazy do return "article_concerns_{mentity.nitdoc_id}"
453 redef var html_title = "Concerns"
454 redef fun is_hidden do return concerns.is_empty
455 redef fun render_body do add concerns.html_list
456 end
457
458 redef class DefinitionArticle
459 redef var html_id is lazy do return "article_definition_{mentity.nitdoc_id}"
460 redef var html_title is lazy do return mentity.html_name
461 redef var html_subtitle is lazy do return mentity.html_declaration
462 redef var is_hidden = false
463
464 # Does `self` display only it's title and no body?
465 #
466 # FIXME diff hack
467 var is_no_body: Bool = false is writable
468
469 # Does `self` display only the short content as definition?
470 #
471 # FIXME diff hack
472 var is_short_comment: Bool = false is writable
473
474 redef fun render_body do
475 if not is_no_body then
476 var comment
477 if is_short_comment then
478 comment = mentity.html_short_comment
479 else
480 comment = mentity.html_comment
481 end
482 if comment != null then addn comment
483 end
484 super
485 end
486 end
487
488 redef class HierarchyListArticle
489 redef var html_id is lazy do return "article_hierarchy_{list_title}_{mentity.nitdoc_id}"
490 redef var html_title is lazy do return list_title
491 redef fun is_empty do return mentities.is_empty
492 redef fun is_toc_hidden do return mentities.is_empty
493
494 redef fun render_body do
495 var lst = new UnorderedList
496 lst.css_classes.add "list-unstyled list-definition"
497 for mentity in mentities do
498 lst.add_li mentity.html_list_item
499 end
500 addn lst
501 end
502 end
503
504 redef class IntrosRedefsListArticle
505 redef var html_id is lazy do return "article_intros_redefs_{mentity.nitdoc_id}"
506 redef var html_title is lazy do return list_title
507 redef fun is_hidden do return mentities.is_empty
508 redef var is_toc_hidden = true
509
510 redef fun render_body do
511 var lst = new UnorderedList
512 lst.css_classes.add "list-unstyled list-labeled"
513 for mentity in mentities do
514 lst.add_li mentity.html_list_item
515 end
516 add lst
517 end
518 end
519
520 redef class GraphArticle
521 redef var html_id is lazy do return "article_graph_{mentity.nitdoc_id}"
522 redef var html_title = null
523 redef var toc_title do return "Graph"
524 redef var is_hidden = false
525 redef var is_toc_hidden = true
526
527 # HTML map used to display link.
528 #
529 # This attribute is set by the `doc_render` phase who knows the context.
530 var map: String is noinit, writable
531
532 redef fun render_body do
533 addn "<div class=\"text-center\">"
534 addn " <img src='{id}.png' usemap='#{id}' style='margin:auto'"
535 addn " alt='{graph_title}'/>"
536 add map
537 addn "</div>"
538 end
539 end