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