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