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