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