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