nitdoc: full rewrite of the nitdoc engine
[nit.git] / src / doc / static / static_html.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 # Render documentation pages as HTML
16 module static_html
17
18 import static::static_structure
19 import json
20
21 redef class DocPage
22 super Template
23
24 # Page url
25 var html_url: String is writable, noinit
26
27 # Directory where css, js and other assets can be found
28 var shareurl: String is writable, noinit
29
30 # Top menu template if any
31 var topmenu: DocTopMenu is writable, noinit
32
33 # Footer content if any
34 var footer: nullable Writable = null is writable
35
36 # Render the page as a html template
37 fun render(doc: DocModel): Writable do
38 # init page options
39 self.shareurl = doc.share_url or else "."
40 self.footer = doc.custom_footer
41
42 # build page
43 init_title(doc)
44 init_topmenu(doc)
45
46 # piwik tracking
47 var tracker_url = doc.tracker_url
48 var site_id = doc.piwik_site_id
49 if tracker_url != null and site_id != null then
50 piwik_script = new PiwikScript(tracker_url, site_id)
51 end
52 return self
53 end
54
55 # Build page title string
56 fun init_title(doc: DocModel) do end
57
58 # Build top menu template if any
59 fun init_topmenu(doc: DocModel) do
60 topmenu = new DocTopMenu
61
62 var home = new Link("index.html", "Nitdoc")
63
64 var custom_brand = doc.custom_brand
65 if custom_brand != null then
66 topmenu.brand = new Link("index.html", custom_brand)
67 topmenu.items.add new ListItem(home)
68 else
69 topmenu.brand = home
70 end
71 end
72
73 # Renders the html `<head>`
74 private fun render_head do
75 var css = (self.shareurl / "css").html_escape
76 var vendors = (self.shareurl / "vendors").html_escape
77
78 addn "<!DOCTYPE html>"
79 addn "<head>"
80 addn " <meta charset='utf-8'/>"
81 addn " <link rel='stylesheet' href='{vendors}/bootstrap/css/bootstrap.min.css'/>"
82 addn " <link rel='stylesheet' href='{css}/nitdoc.bootstrap.css'/>"
83 addn " <link rel='stylesheet' href='{css}/nitdoc.cards.css'/>"
84 addn " <link rel='stylesheet' href='{css}/nitdoc.code.css'/>"
85 addn " <link rel='stylesheet' href='{css}/nitdoc.css'/>"
86 addn " <link rel='stylesheet' href='{css}/nitdoc.quicksearch.css'/>"
87 addn " <title>{title.html_escape}</title>"
88 addn "</head>"
89 add "<body>"
90 end
91
92 # Renders the footer and content
93 private fun render_content do
94 if tabs.is_empty then return
95 if tabs.length == 1 then
96 addn tabs.first
97 return
98 end
99 addn "<ul class='nav nav-tabs'>"
100 for tab in tabs do
101 if tab.is_empty and not tab isa DocTabLink then continue
102 addn tab.tab_link
103 end
104 addn "</ul>"
105 addn "<div class='tab-content'>"
106 for tab in tabs do
107 if tab.is_empty then continue
108 addn tab
109 end
110 addn "</div>"
111 end
112
113 # Piwik script to append in the page scripts
114 var piwik_script: nullable PiwikScript = null is writable
115
116 # Render JS scripts
117 private fun render_footer do
118 if footer != null then
119 addn "<div class='footer'>"
120 add footer.as(not null)
121 addn "</div>"
122 end
123 var vendors = (self.shareurl / "vendors").html_escape
124 var js = (self.shareurl / "js").html_escape
125
126 addn "<script src='quicksearch-list.js'></script>"
127 addn "<script src='{vendors}/jquery/jquery-1.11.1.min.js'></script>"
128 addn "<script src='{vendors}/jquery/jquery-ui-1.10.4.custom.min.js'></script>"
129 addn "<script src='{vendors}/bootstrap/js/bootstrap.min.js'></script>"
130 addn "<script src='{js}/nitdoc.utils.js'></script>"
131 addn "<script src='{js}/nitdoc.quicksearch.js'></script>"
132
133 var piwik_script = self.piwik_script
134 if piwik_script != null then
135 add piwik_script
136 end
137 addn "</body>"
138 addn "</html>"
139 end
140
141 # Render the whole page
142 redef fun rendering do
143 render_head
144 add topmenu
145 addn "<div class='container-fluid'>"
146 render_content
147 addn "</div>"
148 render_footer
149 end
150 end
151
152 redef class PageHome
153 redef var html_url = "index.html"
154
155 redef fun render(doc) do
156 main_tab.show_sidebar = false
157 return super
158 end
159
160 redef fun init_title(doc) do
161 title = doc.custom_title or else "Nitdoc"
162 end
163
164 redef fun render_content do
165 addn "<div class='container'>"
166 if tabs.not_empty then
167 addn tabs.first
168 end
169 addn "</div>"
170 end
171 end
172
173 redef class PageMEntity
174 redef var html_url is lazy do return mentity.html_url
175 redef fun init_title(doc) do title = mentity.html_name
176
177 redef fun render_content do
178 addn new CardPageHeader(
179 mentity.html_declaration.write_to_string,
180 mentity.html_namespace.write_to_string)
181 super
182 end
183
184 redef fun init_topmenu(doc) do
185 super
186 for m in mentity.nitdoc_breadcrumbs do
187 topmenu.add_li new ListItem(new Link(m.html_url, m.html_name))
188 end
189 topmenu.active_item = topmenu.items.last
190 end
191 end
192
193 redef class PagePerson
194 redef var html_url is lazy do return person.html_url
195 end
196
197 redef class PageTag
198 redef var html_url is lazy do return "tag_{tag.to_cmangle}.html"
199 end
200
201 redef class HtmlightVisitor
202 redef fun hrefto(mentity) do return mentity.html_url
203 end
204
205 redef class DocTab
206 super Template
207
208 # Show sidebar for this page?
209 var show_sidebar = true is writable
210
211 # Tab link for tab headers
212 fun tab_link: Template do
213 var tpl = new Template
214 tpl.addn "<li class='{if is_active then "active" else ""}'>"
215 tpl.addn " <a data-toggle='tab' href='#{id}'>"
216
217 var icon = self.icon
218 if icon != null then
219 tpl.addn " <span class='glyphicon glyphicon-{icon}'></span>"
220 end
221 tpl.addn " {title}"
222 tpl.addn " </a>"
223 tpl.addn "</li>"
224 return tpl
225 end
226
227 redef fun rendering do
228 var has_left = show_sidebar and sidebar.cards.not_empty
229 var has_right = metadata.cards.not_empty
230
231 addn "<div class='tab-pane {if is_active then "active" else ""}' id='{id}'>"
232 if has_left then
233 addn " <div class='col-sm-3'>"
234 addn sidebar
235 addn " </div>"
236 end
237 var cols = 12
238 if has_left then cols -= 3
239 if has_right then cols -= 3
240 addn " <div class='col-sm-{cols}'>"
241 for card in content do addn card
242 addn " </div>"
243 if has_right then
244 addn " <div class='col-sm-3'>"
245 addn metadata
246 addn " </div>"
247 end
248 addn "</div>"
249 end
250 end
251
252 redef class DocTabLink
253
254 redef fun tab_link do
255 var tpl = new Template
256 tpl.addn "<li class='{if is_active then "active" else ""}'>"
257 tpl.addn " <a href='{url.html_escape}'>"
258
259 var icon = self.icon
260 if icon != null then
261 tpl.addn " <span class='glyphicon glyphicon-{icon}'></span>"
262 end
263 tpl.addn " {title}"
264 tpl.addn " </a>"
265 tpl.addn "</li>"
266 return tpl
267 end
268
269 redef fun rendering do end
270 end
271
272 # Top menu bar template
273 class DocTopMenu
274 super UnorderedList
275
276 # Brand link to display in first position of the top menu
277 #
278 # This is where you want to put your logo.
279 var brand: nullable Link is noinit, writable
280
281 # Active menu item
282 #
283 # Depends on the current page, this allows to hilighted the current item.
284 var active_item: nullable ListItem is noinit, writable
285
286 redef fun rendering do
287 addn "<nav class='navbar navbar-default navbar-fixed-top'>"
288 addn " <div class='container-fluid'>"
289 addn " <div class='navbar-header'>"
290 add " <button type='button' class='navbar-toggle' "
291 addn " data-toggle='collapse' data-target='#topmenu-collapse'>"
292 addn " <span class='sr-only'>Toggle menu</span>"
293 addn " <span class='icon-bar'></span>"
294 addn " <span class='icon-bar'></span>"
295 addn " <span class='icon-bar'></span>"
296 addn " </button>"
297 var brand = self.brand
298 if brand != null then
299 add "<span class='navbar-brand'>"
300 add brand
301 add "</span>"
302 end
303 addn " </div>"
304 addn " <div class='collapse navbar-collapse' id='topmenu-collapse'>"
305 addn " <ul class='nav navbar-nav'>"
306 for item in items do
307 if item == active_item then item.css_classes.add "active"
308 add item.write_to_string
309 end
310 addn " </ul>"
311 addn " <div id='search-placeholder'>"
312 addn " </div>"
313 addn " </div>"
314 addn " </div>"
315 addn "</nav>"
316 end
317 end
318
319 redef class DocSidebar
320 super Template
321
322 redef fun rendering do
323 if cards.is_empty then return
324 addn "<div id='sidebar'>"
325 for card in cards do addn card
326 addn "</div>"
327 end
328 end
329
330 # JS script for Piwik Tracker
331 class PiwikScript
332 super Template
333
334 # Piwik URL to use for this tracker
335 var tracker_url: String
336
337 # Site ID used on Piwik system
338 var site_id: String
339
340 redef fun rendering do
341 addn "<script>"
342
343 var site_id = self.site_id.to_json
344 var tracker_url = self.tracker_url.trim
345 if tracker_url.chars.last != '/' then tracker_url += "/"
346 tracker_url = "://{tracker_url}".to_json
347
348 addn "<!-- Piwik -->"
349 addn "var _paq = _paq || [];"
350 addn " _paq.push([\"trackPageView\"]);"
351 addn " _paq.push([\"enableLinkTracking\"]);"
352 addn "(function() \{"
353 addn " var u=((\"https:\" == document.location.protocol) ? \"https\" : \"http\") + {tracker_url};"
354 addn " _paq.push([\"setTrackerUrl\", u+\"piwik.php\"]);"
355 addn " _paq.push([\"setSiteId\", {site_id}]);"
356 addn " var d=document, g=d.createElement(\"script\"), s=d.getElementsByTagName(\"script\")[0]; g.type=\"text/javascript\";"
357 addn " g.defer=true; g.async=true; g.src=u+\"piwik.js\"; s.parentNode.insertBefore(g,s);"
358 addn "\})();"
359
360 addn "</script>"
361 end
362 end
363
364 # Model redefs
365
366 redef class MEntity
367 redef fun to_dot_node do
368 var node = super
369 node["URL"] = html_url
370 return node
371 end
372
373 redef var html_url = "{html_id}.html" is lazy
374 end
375
376 redef class MPackage
377 redef var html_url is lazy do return "package_{super}"
378 end
379
380 redef class MGroup
381 redef var html_url is lazy do return "group_{super}"
382 end
383
384 redef class MModule
385 redef var html_url is lazy do return "module_{super}"
386 end
387
388 redef class MClass
389 redef var html_url is lazy do return "class_{super}"
390 end
391
392 redef class MClassDef
393 redef var html_url is lazy do
394 if is_intro then return mclass.html_url
395 return "{mclass.html_url}?def=def_code_{html_id}#lin"
396 end
397 end
398
399 redef class MProperty
400 redef var html_url is lazy do return "property_{super}"
401 end
402
403 redef class MPropDef
404 redef var html_url is lazy do
405 if is_intro then return mproperty.html_url
406 return "{mproperty.html_url}?def=def_code_{html_id}#lin"
407 end
408 end
409
410 redef class Person
411 redef var html_url = "person_{html_id}.html" is lazy
412 end