cc865498154ffed71eb59a237d5ec48836c3dd19
[nit.git] / src / doc / doc_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 # HTML templates used by Nitdoc to generate API documentation
16 # Pages are assembled using `Template`
17 module doc_templates
18
19 import template
20
21 # A documentation page
22 class TplPage
23 super Template
24
25 # Page title in HTML header
26 var title: String writable
27
28 # Directory where css, js and other assets can be found
29 var shareurl: String writable
30
31 # Attributes of the body tag element
32 var body_attrs = new Array[TagAttribute]
33
34 # Top menu template if any
35 var topmenu: TplTopMenu writable
36
37 # Sidebar template if any
38 var sidebar: nullable TplSidebar writable
39
40 # Content of the page in form a TplSection
41 var sections = new Array[TplSection]
42
43 # Footer content if any
44 var footer: nullable Streamable writable
45
46 # JS scripts to append at the end of the body
47 var scripts = new Array[TplScript]
48
49 init do end
50
51 # Add a section to this page
52 fun add_section(section: TplSection) do
53 sections.add section
54 end
55
56 # Render the html header
57 private fun render_head do
58 add "<!DOCTYPE html>"
59 add "<head>"
60 add " <meta charset='utf-8'/>"
61 add " <!--link rel='stylesheet' href='{shareurl}/css/Nitdoc.UI.css' type='text/css'/-->"
62 add " <link rel='stylesheet' href='{shareurl}/vendors/bootstrap/css/bootstrap.min.css'/>"
63 add " <link rel='stylesheet' href='{shareurl}/css/nitdoc.bootstrap.css'/>"
64 add " <link rel='stylesheet' href='{shareurl}/css/nitdoc.css'/>"
65 add " <link rel='stylesheet' href='{shareurl}/css/Nitdoc.QuickSearch.css'/>"
66 add " <link rel='stylesheet' href='{shareurl}/css/Nitdoc.ModalBox.css'/>"
67 add " <link rel='stylesheet' href='{shareurl}/css/Nitdoc.GitHub.css'/>"
68 add " <title>{title}</title>"
69 add "</head>"
70 add "<body"
71 for attr in body_attrs do add attr
72 add ">"
73 end
74
75 # Render the topmenu template
76 private fun render_topmenu do
77 add " <div class='row'>"
78 add topmenu
79 add " </div>"
80 end
81
82 # Render the sidebar
83 # Sidebar is automatically populated with a summary of all sections
84 fun render_sidebar do
85 if sidebar == null then return
86 var summary = new TplSummary.with_order(0)
87 for section in sections do
88 section.render_summary summary
89 end
90 sidebar.boxes.add summary
91 add sidebar.as(not null)
92 end
93 # Render the footer and content
94 private fun render_content do
95 for section in sections do add section
96 if footer != null then
97 add "<div class='well footer'>"
98 add footer.as(not null)
99 add "</div>"
100 end
101 end
102
103 # Render JS scripts
104 private fun render_footer do
105 add "<script src='{shareurl}/vendors/jquery/jquery-1.11.1.min.js'></script>"
106 add "<script src='{shareurl}/vendors/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
107 add "<script src='{shareurl}/vendors/bootstrap/js/bootstrap.min.js'></script>"
108 add "<script data-main='{shareurl}/js/nitdoc' src='{shareurl}/js/lib/require.js'</script>"
109 for script in scripts do add script
110 add """<script>
111 $(function () {
112 $("[data-toggle='tooltip']").tooltip();
113 $("[data-toggle='popover']").popover();
114 });
115 </script>"""
116 add "</body>"
117 add "</html>"
118 end
119
120 # Render the whole page
121 redef fun rendering do
122 render_head
123 add "<div class='container-fluid'>"
124 render_topmenu
125 add " <div class='row' id='content'>"
126 if sidebar != null then
127 add "<div class='col col-xs-3 col-lg-2'>"
128 render_sidebar
129 add "</div>"
130 add "<div class='col col-xs-9 col-lg-10' data-spy='scroll' data-target='.summary'>"
131 render_content
132 add "</div>"
133 else
134 add "<div class='col col-xs-12'>"
135 render_content
136 add "</div>"
137 end
138 add " </div>"
139 add "</div>"
140 render_footer
141 end
142 end
143
144 #########################
145 # general layout elements
146 #########################
147
148 # Top menu bar template
149 class TplTopMenu
150 super Template
151
152 # Brand link to display in first position of the top menu
153 private var brand: nullable Streamable writable
154 # Elements of the topmenu
155 private var elts = new Array[Streamable]
156
157 init do end
158
159 # Add a content between `<li>` tags
160 fun add_item(content: Streamable, is_active: Bool) do
161 var tpl = new Template
162 tpl.add "<li"
163 if is_active then
164 tpl.add " class='active'"
165 end
166 tpl.add ">"
167 tpl.add content
168 tpl.add "</li>"
169 add_raw(tpl)
170 end
171
172 # Add a raw content to the menu
173 fun add_raw(content: Streamable) do
174 elts.add content
175 end
176
177 redef fun rendering do
178 if brand == null and elts.is_empty then return
179 add "<nav id='topmenu' class='navbar navbar-default navbar-fixed-top' role='navigation'>"
180 add " <div class='container-fluid'>"
181 add " <div class='navbar-header'>"
182 add " <button type='button' class='navbar-toggle' "
183 add " data-toggle='collapse' data-target='#topmenu-collapse'>"
184 add " <span class='sr-only'>Toggle menu</span>"
185 add " <span class='icon-bar'></span>"
186 add " <span class='icon-bar'></span>"
187 add " <span class='icon-bar'></span>"
188 add " </button>"
189 if brand != null then add brand.as(not null)
190 add " </div>"
191 add " <div class='collapse navbar-collapse' id='topmenu-collapse'>"
192 if not elts.is_empty then
193 add "<ul class='nav navbar-nav'>"
194 for elt in elts do add elt
195 add "</ul>"
196 end
197 add " </div>"
198 add " </div>"
199 add "</nav>"
200 end
201 end
202
203 # A sidebar template
204 class TplSidebar
205 super Template
206
207 # Sidebar contains sidebar element templates called boxes
208 var boxes = new Array[TplSidebarElt]
209
210 # Sort boxes by order priority
211 private fun order_boxes do
212 var sorter = new OrderComparator
213 sorter.sort(boxes)
214 end
215
216 redef fun rendering do
217 if boxes.is_empty then return
218 order_boxes
219 add "<div id='sidebar'>"
220 for box in boxes do add box
221 add "</div>"
222 end
223 end
224
225 # Comparator used to sort boxes by order
226 private class OrderComparator
227 super Comparator[TplSidebarElt]
228
229 redef fun compare(a, b) do
230 if a.order < b.order then return -1
231 if a.order > b.order then return 1
232 return 0
233 end
234 end
235
236 # Something that can be put in the sidebar
237 class TplSidebarElt
238 super Template
239
240 # Order of the box in the sidebar
241 var order: Int = 1
242
243 init with_order(order: Int) do self.order = order
244 end
245
246 # Agenericbox that can be added to sidebar
247 class TplSideBox
248 super TplSidebarElt
249
250 # Title of the box to display
251 # Title is also a placeholder for the collapse link
252 var title: String
253
254 # Box HTML id
255 # equals to `title.to_cmangle` by default
256 # Used for collapsing
257 var id: String
258
259 # Content to display in the box
260 # box will not be rendered if the content is null
261 var content: nullable Streamable writable
262
263 # Is the box opened by default
264 # otherwise, the user will have to clic on the title to display the content
265 var is_open writable = false
266
267 init(title: String) do
268 self.title = title
269 self.id = title.to_cmangle
270 end
271
272 init with_content(title: String, content: Streamable) do
273 init(title)
274 self.content = content
275 end
276
277 redef fun rendering do
278 if content == null then return
279 var open = ""
280 if is_open then open = "in"
281 add "<div class='panel'>"
282 add " <div class='panel-heading'>"
283 add " <a data-toggle='collapse' data-parent='#sidebar' data-target='#box_{id}' href='#'>"
284 add title
285 add " </a>"
286 add " </div>"
287 add " <div id='box_{id}' class='panel-body collapse {open}'>"
288 add content.as(not null)
289 add " </div>"
290 add "</div>"
291 end
292 end
293
294 # Something that can go on a summary template
295 class TplSummaryElt
296 super Template
297
298 # Add an element to the summary
299 fun add_child(child: TplSummaryElt) is abstract
300 end
301
302 # A summary that can go on the sidebar
303 # If the page contains a sidebar, the summary is automatically placed
304 # on top of the sidebarauto-generated
305 # summary contains anchors to all sections displayed in the page
306 class TplSummary
307 super TplSidebarElt
308 super TplSummaryElt
309
310 # Summary elements to display
311 var children = new Array[TplSummaryElt]
312
313 redef fun add_child(child) do children.add child
314
315 redef fun rendering do
316 if children.is_empty then return
317 add "<div class='panel'>"
318 add " <div class='panel-heading'>"
319 add " <a data-toggle='collapse' data-parent='#sidebar' data-target='#box-sum' href='#'>"
320 add "Summary"
321 add " </a>"
322 add " </div>"
323 add " <div id='box-sum' class='summary collapse in'>"
324 add " <ul class='nav'>"
325 for entry in children do add entry
326 add " </ul>"
327 add " </div>"
328 add "</div>"
329 end
330 end
331
332 # A summary entry
333 class TplSummaryEntry
334 super TplSummaryElt
335
336 # Text to display
337 var text: Streamable
338
339 # Children of this entry
340 # Will be displayed as a tree
341 var children = new Array[TplSummaryElt]
342
343 init(text: Streamable) do self.text = text
344
345 redef fun add_child(child) do children.add child
346
347 redef fun rendering do
348 add "<li>"
349 add text
350 if not children.is_empty then
351 add "<ul class='nav'>"
352 for entry in children do add entry
353 add "</ul>"
354 end
355 add "</li>"
356 end
357 end
358
359 # Something that can go in a section
360 # Sections are automatically collected to populate the menu
361 class TplSectionElt
362 super Template
363
364 # HTML anchor id
365 var id: String
366
367 # Title to display if any
368 # if both `title` and `summary_title` are null then
369 # the section will not appear in the summary
370 var title: nullable Streamable writable
371
372 # Subtitle to display if any
373 var subtitle: nullable Streamable writable
374
375 # Title that appear in the summary
376 # if null use `title` instead
377 var summary_title: nullable String writable
378
379 # Parent section of this section if any
380 var parent: nullable TplSection
381
382 init(id: String) do self.id = id
383
384 init with_title(id: String, title: Streamable) do
385 init(id)
386 self.title = title
387 end
388
389 # Level <hX> for HTML heading
390 protected fun hlvl: Int do
391 if parent == null then return 1
392 return parent.hlvl + 1
393 end
394
395 # Render this section in the summary
396 protected fun render_summary(parent: TplSummaryElt) is abstract
397
398 # Is the section empty (no content at all)
399 fun is_empty: Bool is abstract
400 end
401
402 # A HTML <section> element
403 class TplSection
404 super TplSectionElt
405
406 # Elements contained by this section
407 var children = new Array[TplSectionElt]
408
409 # Add an element in this section
410 fun add_child(child: TplSectionElt) do
411 child.parent = self
412 children.add child
413 end
414
415 redef fun is_empty: Bool do return children.is_empty
416
417 redef fun render_summary(parent) do
418 if is_empty then return
419 var title = summary_title
420 if title == null and self.title != null then title = self.title.write_to_string
421 if title == null then return
422 var lnk = new TplLink("#{id}", title)
423 var entry = new TplSummaryEntry(lnk)
424 for child in children do
425 child.render_summary(entry)
426 end
427 parent.add_child entry
428 end
429
430 redef fun rendering do
431 add "<section id='{id}'>"
432 if title != null then
433 var lvl = hlvl
434 add "<h{lvl}>"
435 add title.as(not null)
436 add "</h{lvl}>"
437 end
438 if subtitle != null then
439 add "<div class='info subtitle'>"
440 add subtitle.as(not null)
441 add "</div>"
442 end
443 for child in children do
444 add child
445 end
446 add "</section>"
447 end
448 end
449
450 # A page article that can go in a section
451 class TplArticle
452 super TplSectionElt
453
454 # Content for this article
455 var content: nullable Streamable writable = null
456 var source_link: nullable Streamable writable = null
457
458 init with_content(id: String, title: Streamable, content: Streamable) do
459 with_title(id, title)
460 self.content = content
461 end
462
463 # CSS classes to apply on the article title heading element
464 var title_classes = new Array[String]
465
466 redef fun render_summary(parent) do
467 if is_empty then return
468 var title = summary_title
469 if title == null and self.title != null then title = self.title.write_to_string
470 if title == null then return
471 var lnk = new TplLink("#{id}", title)
472 parent.add_child new TplSummaryEntry(lnk)
473 end
474
475 redef fun rendering do
476 if is_empty then return
477 add "<article id='{id}'>"
478 if title != null then
479 var lvl = hlvl
480 add "<h{lvl} class='{title_classes.join(" ")}'>"
481 add title.as(not null)
482 add "</h{lvl}>"
483 end
484 if subtitle != null then
485 add "<div class='info subtitle'>"
486 add subtitle.as(not null)
487 add "</div>"
488 end
489 if content != null then
490 add content.as(not null)
491 end
492 add """</article>"""
493 end
494
495 redef fun is_empty: Bool do
496 return title == null and subtitle == null and content == null
497 end
498 end
499
500 # A module / class / prop definition
501 class TplDefinition
502 super Template
503
504 # Comment to display
505 var comment: nullable Streamable writable
506
507 # Namespace for this definition
508 var namespace: nullable Streamable writable
509
510 # Location link to display
511 var location: nullable Streamable writable
512
513 init do end
514
515 private fun render_info do
516 add "<div class='info text-right'>"
517 if namespace != null then
518 if comment == null then
519 add "<span class=\"noComment\">no comment for </span>"
520 end
521 add namespace.as(not null)
522 end
523 if location != null then
524 add " "
525 add location.as(not null)
526 end
527 add "</div>"
528 end
529
530 private fun render_comment do
531 if comment != null then add comment.as(not null)
532 end
533
534 redef fun rendering do
535 add "<div class='definition'>"
536 render_comment
537 render_info
538 add "</div>"
539 end
540 end
541
542 # Class definition
543 class TplClassDefinition
544 super TplDefinition
545
546 var intros = new Array[TplListElt]
547 var redefs = new Array[TplListElt]
548
549 init do end
550
551 redef fun rendering do
552 add "<div class='definition'>"
553 render_comment
554 render_info
555 render_list("Introduces", intros)
556 render_list("Redefines", redefs)
557 add "</div>"
558 end
559
560 private fun render_list(name: String, elts: Array[TplListElt]) do
561 if elts.is_empty then return
562 add "<h5>{name}</h5>"
563 add "<ul class='list-unstyled list-definition'>"
564 for elt in elts do add elt
565 add "</ul>"
566 end
567 end
568
569 # Layout for Search page
570 class TplSearchPage
571 super TplSectionElt
572
573 var modules = new Array[Streamable]
574 var classes = new Array[Streamable]
575 var props = new Array[Streamable]
576
577 redef fun rendering do
578 if title != null then add "<h1>{title}</h1>"
579 add "<div class='container-fluid'>"
580 add " <div class='row'>"
581 if not modules.is_empty then
582 add "<div class='col-xs-4'>"
583 add "<h3>Modules</h3>"
584 add "<ul>"
585 for m in modules do
586 add "<li>"
587 add m
588 add "</li>"
589 end
590 add "</ul>"
591 add "</div>"
592 end
593 if not classes.is_empty then
594 add "<div class='col-xs-4'>"
595 add "<h3>Classes</h3>"
596 add "<ul>"
597 for c in classes do
598 add "<li>"
599 add c
600 add "</li>"
601 end
602 add "</ul>"
603 add "</div>"
604 end
605 if not props.is_empty then
606 add "<div class='col-xs-4'>"
607 add "<h3>Properties</h3>"
608 add "<ul>"
609 for p in props do
610 add "<li>"
611 add p
612 add "</li>"
613 end
614 add "</ul>"
615 add "</div>"
616 end
617 add " </div>"
618 add "</div>"
619 end
620 end
621
622 #####################
623 # Basiv HTML elements
624 #####################
625
626 # A html link <a>
627 class TplLink
628 super Template
629
630 # Link href
631 var href: String writable
632
633 # Text to display in the link
634 var text: String writable
635
636 # Optional title
637 var title: nullable String writable
638
639 init(href, text: String) do
640 self.href = href
641 self.text = text
642 end
643
644 init with_title(href, text, title: String) do
645 init(href, text)
646 self.title = title
647 end
648
649 redef fun rendering do
650 add "<a href=\""
651 add href
652 add "\""
653 if title != null then
654 add " title=\""
655 add title.as(not null)
656 add "\""
657 end
658 add ">"
659 add text
660 add "</a>"
661 end
662 end
663
664 # A <ul> list
665 class TplList
666 super TplListElt
667
668 # Elements contained in this list
669 # can be <li> or <ul> elements
670 var elts = new Array[TplListElt]
671
672 # CSS classes of the <ul> element
673 var css_classes = new Array[String]
674
675 # Add content wrapped in a <li> element
676 fun add_li(content: Streamable) do elts.add new TplListItem.with_content(content)
677
678 init do end
679
680 init with_classes(classes: Array[String]) do self.css_classes = classes
681
682 redef fun rendering do
683 if elts.is_empty then return
684 add "<ul class='{css_classes.join(" ")}'>"
685 for elt in elts do add elt
686 add "</ul>"
687 end
688 end
689
690 # Something that can be added to a TplList
691 class TplListElt
692 super Template
693 end
694
695 # A list item <li>
696 class TplListItem
697 super TplListElt
698
699 # Content of the list item
700 var content = new Template
701
702 # CSS classes of the <li> element
703 var css_classes = new Array[String]
704
705 init do end
706
707 init with_content(content: Streamable) do append(content)
708
709 init with_classes(content: Streamable, classes: Array[String]) do
710 with_content(content)
711 css_classes = classes
712 end
713
714 # Append `content` to the item
715 # similar to `self.content.add`
716 fun append(content: Streamable) do self.content.add content
717
718 redef fun rendering do
719 add "<li class='"
720 for cls in css_classes do add " {cls}"
721 add "'>"
722 add content
723 add "</li>"
724 end
725 end
726
727 # A label with a text content
728 class TplLabel
729 super Template
730
731 # Content of the label if any
732 var content: nullable Streamable
733
734 # CSS classes of the <span> element
735 var css_classes = new Array[String]
736
737 init do end
738 init with_content(content: Streamable) do self.content = content
739 init with_classes(classes: Array[String]) do self.css_classes = classes
740
741 redef fun rendering do
742 add "<span class='label {css_classes.join(" ")}'>"
743 if content != null then add content.as(not null)
744 add "</span>"
745 end
746 end
747
748 # A label with an icon
749 class TplIcon
750 super TplLabel
751
752 # Bootsrap icon name
753 # see: http://getbootstrap.com/components/#glyphicons
754 var icon: String
755
756 init with_icon(icon: String) do self.icon = icon
757
758 redef fun rendering do
759 add "<span class='glyphicon glyphicon-{icon} {css_classes.join(" ")}'>"
760 if content != null then add content.as(not null)
761 add "</span>"
762 end
763 end
764
765 # A HTML tag attribute
766 # `<tag attr="value">`
767 class TagAttribute
768 super Template
769
770 var name: String
771 var value: nullable String
772
773 init(name: String, value: nullable String) do
774 self.name = name
775 self.value = value
776 end
777
778 redef fun rendering do
779 if value == null then
780 add(" {name}")
781 else
782 add(" {name}=\"{value}\"")
783 end
784 end
785 end
786
787 # Javacript template
788 class TplScript
789 super Template
790
791 var attrs = new Array[TagAttribute]
792 var content: nullable Streamable writable
793
794 init do
795 attrs.add(new TagAttribute("type", "text/javascript"))
796 end
797
798 protected fun render_content do
799 if content != null then add content.as(not null)
800 end
801
802 redef fun rendering do
803 add "<script"
804 for attr in attrs do add attr
805 add ">"
806 render_content
807 add "</script>"
808 end
809 end
810
811 # JS script for Piwik Tracker
812 class TplPiwikScript
813 super TplScript
814
815 var tracker_url: String
816 var site_id: String
817
818 init(tracker_url, site_id: String) do
819 super
820 self.tracker_url = tracker_url
821 self.site_id = site_id
822 end
823
824 redef fun render_content do
825 add "<!-- Piwik -->"
826 add "var _paq = _paq || [];"
827 add " _paq.push([\"trackPageView\"]);"
828 add " _paq.push([\"enableLinkTracking\"]);"
829 add "(function() \{"
830 add " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + \"://{tracker_url}\";"
831 add " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
832 add " _paq.push([\"setSiteId\", \"{site_id}\"]);"
833 add " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
834 add " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
835 add "\})();"
836 end
837 end
838