Merge: nitx: start migration 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 import doc_phases::doc_lin
25
26 # Renders the page as HTML.
27 redef class DocPage
28 super Template
29
30 # Page url.
31 var html_url: String is writable, noinit
32
33 # Directory where css, js and other assets can be found.
34 var shareurl: String is writable, noinit
35
36 # Attributes of the body tag element.
37 var body_attrs = new Array[TagAttribute]
38
39 # Top menu template if any.
40 var topmenu: DocTopMenu is writable, noinit
41
42 # Sidebar template if any.
43 var sidebar: nullable DocSideBar = null is writable
44
45 # Footer content if any.
46 var footer: nullable Writable = null is writable
47
48 # JS scripts to append at the end of the body
49 var scripts = new Array[TplScript]
50
51 # Renders the html `<head>`.
52 private fun render_head do
53 var css = (self.shareurl / "css").html_escape
54 var vendors = (self.shareurl / "vendors").html_escape
55
56 addn "<!DOCTYPE html>"
57 addn "<head>"
58 addn " <meta charset='utf-8'/>"
59 addn " <!--link rel='stylesheet' href='{css}/Nitdoc.UI.css' type='text/css'/-->"
60 addn " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
61 addn " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
62 addn " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
63 addn " <link rel='stylesheet' href='{css}/Nitdoc.QuickSearch.css'/>"
64 addn " <link rel='stylesheet' href='{css}/Nitdoc.ModalBox.css'/>"
65 addn " <link rel='stylesheet' href='{css}/Nitdoc.GitHub.css'/>"
66 addn " <title>{title.html_escape}</title>"
67 addn "</head>"
68 add "<body"
69 for attr in body_attrs do add attr
70 addn ">"
71 end
72
73 # Renders the footer and content.
74 private fun render_content do
75 add root
76 if footer != null then
77 addn "<div class='well footer'>"
78 add footer.as(not null)
79 addn "</div>"
80 end
81 end
82
83 # Render JS scripts
84 private fun render_footer do
85 var vendors = (self.shareurl / "vendors").html_escape
86 var js = (self.shareurl / "js").html_escape
87
88 addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
89 addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
90 addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
91 addn "<script data-main='{js}/nitdoc' src='{js}/lib/require.js'></script>"
92 for script in scripts do add script
93 addn """<script>
94 $(function () {
95 $("[data-toggle='tooltip']").tooltip();
96 $("[data-toggle='popover']").popover();
97 });
98 </script>"""
99 addn "</body>"
100 addn "</html>"
101 end
102
103 # Render the whole page
104 redef fun rendering do
105 render_head
106 addn "<div class='container-fluid'>"
107 addn " <div class='row'>"
108 add topmenu
109 addn " </div>"
110 addn " <div class='row' id='content'>"
111 var sidebar = self.sidebar
112 if sidebar != null then
113 addn "<div class='col col-xs-3 col-lg-2'>"
114 add sidebar
115 addn "</div>"
116 addn "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
117 render_content
118 addn "</div>"
119 else
120 addn "<div class='col col-xs-12'>"
121 render_content
122 addn "</div>"
123 end
124 addn " </div>"
125 addn "</div>"
126 render_footer
127 end
128
129 # Render table of content for this page.
130 fun html_toc: UnorderedList do
131 var lst = new UnorderedList
132 lst.css_classes.add "nav"
133 for child in root.children do
134 child.render_toc_item(lst)
135 end
136 return lst
137 end
138 end
139
140 # Top menu bar template.
141 #
142 # FIXME should be a Bootstrap component template
143 # At this moment, the topmenu structure stills to specific to Nitdoc to use the
144 # generic component.
145 class DocTopMenu
146 super UnorderedList
147
148 # Brand link to display in first position of the top menu.
149 #
150 # This is where you want to put your logo.
151 var brand: nullable Writable is noinit, writable
152
153 # Active menu item.
154 #
155 # Depends on the current page, this allows to hilighted the current item.
156 #
157 # FIXME should be using Boostrap breadcrumbs component.
158 # This will still like this to avoid diff and be changed in further fixes
159 # when we will modify the output.
160 var active_item: nullable ListItem is noinit, writable
161
162 redef fun rendering do
163 addn "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
164 addn " <div class='container-fluid'>"
165 addn " <div class='navbar-header'>"
166 add " <button type='button' class='navbar-toggle' "
167 addn " data-toggle='collapse' data-target='#topmenu-collapse'>"
168 addn " <span class='sr-only'>Toggle menu</span>"
169 addn " <span class='icon-bar'></span>"
170 addn " <span class='icon-bar'></span>"
171 addn " <span class='icon-bar'></span>"
172 addn " </button>"
173 if brand != null then
174 add "<span class='navbar-brand'>"
175 add brand.write_to_string
176 add "</span>"
177 end
178 addn " </div>"
179 addn " <div class='collapse navbar-collapse' id='topmenu-collapse'>"
180 addn " <ul class='nav navbar-nav'>"
181 for item in items do
182 if item == active_item then item.css_classes.add "active"
183 add item.write_to_string
184 end
185 addn " </ul>"
186 addn " </div>"
187 addn " </div>"
188 addn "</nav>"
189 end
190 end
191
192 # Nitdoc sidebar template.
193 class DocSideBar
194 super Template
195
196 # Sidebar contains `DocSideBox`.
197 var boxes = new Array[DocSideBox]
198
199 redef fun rendering do
200 if boxes.is_empty then return
201 addn "<div id='sidebar'>"
202 for box in boxes do add box
203 addn "</div>"
204 end
205 end
206
207 # Something that can be put in a DocSideBar.
208 class DocSideBox
209 super Template
210
211 # Box HTML id, used for Bootstrap collapsing feature.
212 #
213 # Use `html_title.to_cmangle` by default.
214 var id: String is lazy do return title.write_to_string.to_cmangle
215
216 # Title of the box to display.
217 var title: Writable
218
219 # Content to display in the box.
220 var content: Writable
221
222 # Is the box opened by default?
223 #
224 # Otherwise, the user will have to clic on the title to display the content.
225 #
226 # Default is `true`.
227 var is_open = true is writable
228
229 redef fun rendering do
230 var open = ""
231 if is_open then open = "in"
232 addn "<div class='panel'>"
233 addn " <div class='panel-heading'>"
234 add " <a data-toggle='collapse' data-parent='#sidebar'"
235 add " data-target='#box_{id}' href='#'>"
236 add title
237 addn " </a>"
238 addn " </div>"
239 addn " <div id='box_{id}' class='summary panel-body collapse {open}'>"
240 add content
241 addn " </div>"
242 addn "</div>"
243 end
244 end
245
246 redef class DocComposite
247 super Template
248
249 # HTML anchor id
250 var html_id: String is noinit, writable
251
252 # Title to display if any.
253 #
254 # This title can be decorated with HTML.
255 var html_title: nullable Writable is noinit, writable
256
257 # Subtitle to display if any.
258 var html_subtitle: nullable Writable is noinit, writable
259
260 # Render the element title and subtitle.
261 private fun render_title do
262 if html_title != null then
263 var header = new Header(hlvl, html_title.write_to_string)
264 header.css_classes.add "signature"
265 if hlvl == 2 then header.css_classes.add "well well-sm"
266 addn header
267 end
268 if html_subtitle != null then
269 addn "<div class='info subtitle'>"
270 addn html_subtitle.write_to_string
271 addn "</div>"
272 end
273 end
274
275 # Render the element body.
276 private fun render_body do
277 for child in children do addn child.write_to_string
278 end
279
280 redef fun rendering do
281 if is_hidden then return
282 render_title
283 render_body
284 end
285
286 # Level <hX> for HTML heading.
287 private fun hlvl: Int do return depth
288
289 # Is `self` not displayed in the page.
290 #
291 # By default, empty elements are hidden.
292 fun is_hidden: Bool do return is_empty
293
294 # A short, undecorated title that goes in the table of contents.
295 #
296 # By default, returns `html_title.to_s`, subclasses should redefine it.
297 var toc_title: String is lazy, writable do return html_title.to_s
298
299 # Is `self` hidden in the table of content?
300 var is_toc_hidden = false is writable
301
302 # Render this element in a table of contents.
303 private fun render_toc_item(lst: UnorderedList) do
304 if is_toc_hidden then return
305
306 var content = new Template
307 content.add new Link("#{html_id}", toc_title)
308
309 if not children.is_empty then
310 var sublst = new UnorderedList
311 sublst.css_classes.add "nav"
312 for child in children do
313 child.render_toc_item(sublst)
314 end
315 content.add sublst
316 end
317 lst.add_li new ListItem(content)
318 end
319
320 # ID used in HTML tab labels.
321 #
322 # We sanitize it for Boostrap JS panels that do not like ":" and "." in ids.
323 var html_tab_id: String is lazy do
324 var id = html_id.replace(":", "")
325 id = id.replace(".", "")
326 return "{id}-tab"
327 end
328 end
329
330 redef class DocSection
331 super BSComponent
332
333 redef fun rendering do
334 if is_hidden then
335 addn "<a id=\"{html_id}\"></a>"
336 return
337 end
338 addn "<section{render_css_classes} id=\"{html_id}\">"
339 render_title
340 render_body
341 addn "</section>"
342 end
343 end
344
345 redef class DocArticle
346 super BSComponent
347
348 redef fun rendering do
349 if is_hidden then return
350 addn "<article{render_css_classes} id=\"{html_id}\">"
351 render_title
352 render_body
353 addn "</article>"
354 end
355 end
356
357 redef class TabbedGroup
358 redef fun render_body do
359 var tabs = new DocTabs("{html_id}.tabs", "")
360 for child in children do
361 if child.is_hidden then continue
362 tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
363 end
364 addn tabs
365 end
366 end
367
368 redef class PanelGroup
369 redef var html_id is lazy do return "group:{group_title.to_lower.to_snake_case}"
370 redef var html_title = null
371 redef var toc_title is lazy do return group_title
372 redef var is_toc_hidden = true
373 end
374
375 redef class HomeArticle
376 redef var html_id = "article:home"
377 redef var html_title = "Overview"
378
379 # HTML content to display on the home page.
380 #
381 # This attribute is set by the `doc_render` phase who knows the context.
382 var content: nullable String is noinit, writable
383
384 redef fun render_body do
385 var content = self.content
386 if content != null then add content
387 super
388 end
389 end
390
391 redef class IndexArticle
392 redef var html_id = "article:index"
393 redef var html_title = "Index"
394 redef fun is_empty do
395 return mmodules.is_empty and mclasses.is_empty and mprops.is_empty
396 end
397
398 redef fun render_body do
399 addn "<div class='container-fluid'>"
400 addn " <div class='row'>"
401 render_list("Modules", mmodules)
402 render_list("Classes", mclasses)
403 render_list("Properties", mprops)
404 addn "</div>"
405 addn "</div>"
406 end
407
408 # Displays a list from the content of `mentities`.
409 private fun render_list(title: String, mentities: Array[MEntity]) do
410 if mentities.is_empty then return
411 addn "<div class='col-xs-4'>"
412 addn new Header(3, title)
413 var lst = new UnorderedList
414 for mentity in mentities do
415 if mentity isa MProperty then
416 var tpl = new Template
417 tpl.add mentity.intro.html_link
418 tpl.add " ("
419 tpl.add mentity.intro.mclassdef.mclass.html_link
420 tpl.add ")"
421 lst.add_li new ListItem(tpl)
422 else
423 lst.add_li new ListItem(mentity.html_link)
424 end
425 end
426 addn lst
427 addn "</div>"
428 end
429 end
430
431 redef class ProjectsSection
432 redef var html_id = "section:projects"
433 redef var html_title = "Projects"
434 end
435
436 redef class MEntityComposite
437 redef var html_id is lazy do return mentity.nitdoc_id
438 redef var html_title is lazy do return mentity.nitdoc_name
439 end
440
441 redef class MEntitySection
442 redef var html_id is lazy do return "section:{mentity.nitdoc_name}"
443 redef var html_title is lazy do return mentity.html_name
444 redef var html_subtitle is lazy do return mentity.html_declaration
445 end
446
447 redef class ConstructorsSection
448 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.constructors"
449 redef var html_title = "Constructors"
450 redef var html_subtitle = null
451 redef fun is_toc_hidden do return is_empty
452 end
453
454 redef class ConcernSection
455 redef var html_id is lazy do return "concern:{mentity.nitdoc_id}"
456 redef var html_title is lazy do return "in {mentity.nitdoc_name}"
457 redef fun is_toc_hidden do return is_empty
458 end
459
460 redef class ImportationListSection
461 redef var html_id is lazy do return "section:{mentity.nitdoc_id}.importation"
462 redef var html_title is lazy do return "Dependencies"
463 end
464
465 redef class InheritanceListSection
466 redef var html_id is lazy do return "section:{mentity.nitdoc_id}.inheritance"
467 redef var html_title is lazy do return "Inheritance"
468 end
469
470 redef class IntroArticle
471 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intro"
472 redef var html_title = null
473 redef var is_hidden = false
474 redef var is_toc_hidden = true
475
476 # Link to source to display if any.
477 var html_source_link: nullable Writable is noinit, writable
478
479 redef fun render_body do
480 var tabs = new DocTabs("{html_id}.tabs", "")
481 var comment = mentity.html_comment
482 if comment != null then
483 tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
484 end
485 for child in children do
486 if child.is_hidden then continue
487 tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
488 end
489 var lnk = html_source_link
490 if lnk != null then
491 tabs.drop_list.items.add new ListItem(lnk)
492 end
493 addn tabs
494 end
495 end
496
497 redef class ConcernsArticle
498 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.concerns"
499 redef var html_title = "Concerns"
500 redef fun is_hidden do return concerns.is_empty
501 redef fun render_body do add concerns.html_list
502 end
503
504 redef class DefinitionListArticle
505 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.definition-list"
506
507 redef var html_title is lazy do
508 var title = new Template
509 title.add mentity.html_icon
510 title.add mentity.html_link
511 return title
512 end
513
514 redef var html_subtitle is lazy do return mentity.html_namespace
515 redef var toc_title is lazy do return mentity.html_name
516 end
517
518 redef class DefinitionArticle
519 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.definition"
520 redef var html_title is lazy do return mentity.html_name
521 redef var html_subtitle is lazy do return mentity.html_declaration
522 redef var is_hidden = false
523
524 # Does `self` display only it's title and no body?
525 #
526 # FIXME diff hack
527 var is_no_body: Bool = false is writable
528
529 # Does `self` display only the short content as definition?
530 #
531 # FIXME diff hack
532 var is_short_comment: Bool = false is writable
533
534 # Link to source to display if any.
535 var html_source_link: nullable Writable is noinit, writable
536
537 redef fun render_body do
538 var tabs = new DocTabs("{html_id}.tabs", "")
539 if not is_no_body then
540 var comment
541 if is_short_comment then
542 comment = mentity.html_short_comment
543 else
544 comment = mentity.html_comment
545 end
546 if comment != null then
547 tabs.add_panel new DocTabPanel("{html_tab_id}-comment", "Comment", comment)
548 end
549 end
550 for child in children do
551 if child.is_hidden then continue
552 tabs.add_panel new DocTabPanel(child.html_tab_id, child.toc_title, child)
553 end
554 var lnk = html_source_link
555 if lnk != null then
556 tabs.drop_list.items.add new ListItem(lnk)
557 end
558 addn tabs
559 end
560 end
561
562 redef class HierarchyListArticle
563 redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.hierarchy"
564 redef var html_title is lazy do return list_title
565 redef fun is_empty do return mentities.is_empty
566 redef var is_toc_hidden = true
567
568 redef fun render_body do
569 var lst = new UnorderedList
570 lst.css_classes.add "list-unstyled list-definition"
571 for mentity in mentities do
572 lst.add_li mentity.html_list_item
573 end
574 addn lst
575 end
576 end
577
578 redef class IntrosRedefsSection
579 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.intros_redefs"
580 redef var toc_title do return "Intros / Redefs"
581 redef var html_title = null
582 redef var html_subtitle = null
583 redef var is_toc_hidden = true
584 end
585
586 redef class IntrosRedefsListArticle
587 redef var html_id is lazy do return "article:{list_title}_{mentity.nitdoc_id}.intros_redefs"
588 redef var html_title is lazy do return list_title
589 redef fun is_hidden do return mentities.is_empty
590 redef var is_toc_hidden = true
591
592 redef fun render_body do
593 var lst = new UnorderedList
594 lst.css_classes.add "list-unstyled list-labeled"
595 for mentity in mentities do
596 lst.add_li mentity.html_list_item
597 end
598 add lst
599 end
600 end
601
602 redef class DefinitionLinArticle
603 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.lin"
604 redef var html_title is lazy do return "Linearization"
605 redef fun is_hidden do return mentities.is_empty
606 redef var is_toc_hidden = true
607
608 redef fun render_body do
609 var lst = new UnorderedList
610 lst.css_classes.add "list-unstyled list-labeled"
611 for mentity in mentities do
612 if not mentity isa MPropDef then continue # TODO handle all mentities
613 var tpl = new Template
614 tpl.add mentity.mclassdef.html_namespace
615 var comment = mentity.mclassdef.html_short_comment
616 if comment != null then
617 tpl.add ": "
618 tpl.add comment
619 end
620 var li = new ListItem(tpl)
621 li.css_classes.add "signature"
622 lst.add_li li
623 end
624 add lst
625 end
626 end
627
628 redef class GraphArticle
629 redef var html_id is lazy do return "article:{mentity.nitdoc_id}.graph"
630 redef var html_title = null
631 redef var toc_title do return "Graph"
632 redef var is_hidden = false
633 redef var is_toc_hidden = true
634
635 # HTML map used to display link.
636 #
637 # This attribute is set by the `doc_render` phase who knows the context.
638 var map: String is noinit, writable
639
640 redef fun render_body do
641 addn "<div class=\"text-center\">"
642 addn " <img src='{id}.png' usemap='#{id}' style='margin:auto'"
643 addn " alt='{graph_title}'/>"
644 add map
645 addn "</div>"
646 end
647 end