instiki 0.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. data/README +172 -0
  2. data/app/controllers/wiki.rb +389 -0
  3. data/app/models/author.rb +4 -0
  4. data/app/models/chunks/category.rb +31 -0
  5. data/app/models/chunks/chunk.rb +20 -0
  6. data/app/models/chunks/engines.rb +38 -0
  7. data/app/models/chunks/include.rb +29 -0
  8. data/app/models/chunks/literal.rb +19 -0
  9. data/app/models/chunks/match.rb +19 -0
  10. data/app/models/chunks/nowiki.rb +31 -0
  11. data/app/models/chunks/test.rb +18 -0
  12. data/app/models/chunks/uri.rb +97 -0
  13. data/app/models/chunks/wiki.rb +82 -0
  14. data/app/models/page.rb +86 -0
  15. data/app/models/page_lock.rb +24 -0
  16. data/app/models/page_set.rb +64 -0
  17. data/app/models/revision.rb +90 -0
  18. data/app/models/web.rb +89 -0
  19. data/app/models/wiki_content.rb +105 -0
  20. data/app/models/wiki_service.rb +83 -0
  21. data/app/models/wiki_words.rb +25 -0
  22. data/app/views/bottom.rhtml +4 -0
  23. data/app/views/markdown_help.rhtml +16 -0
  24. data/app/views/navigation.rhtml +19 -0
  25. data/app/views/rdoc_help.rhtml +16 -0
  26. data/app/views/static_style_sheet.rhtml +199 -0
  27. data/app/views/textile_help.rhtml +28 -0
  28. data/app/views/top.rhtml +49 -0
  29. data/app/views/wiki/authors.rhtml +13 -0
  30. data/app/views/wiki/edit.rhtml +31 -0
  31. data/app/views/wiki/edit_web.rhtml +138 -0
  32. data/app/views/wiki/export.rhtml +14 -0
  33. data/app/views/wiki/feeds.rhtml +10 -0
  34. data/app/views/wiki/list.rhtml +57 -0
  35. data/app/views/wiki/locked.rhtml +14 -0
  36. data/app/views/wiki/login.rhtml +11 -0
  37. data/app/views/wiki/new.rhtml +27 -0
  38. data/app/views/wiki/new_system.rhtml +78 -0
  39. data/app/views/wiki/new_web.rhtml +64 -0
  40. data/app/views/wiki/page.rhtml +81 -0
  41. data/app/views/wiki/print.rhtml +16 -0
  42. data/app/views/wiki/published.rhtml +10 -0
  43. data/app/views/wiki/recently_revised.rhtml +30 -0
  44. data/app/views/wiki/revision.rhtml +81 -0
  45. data/app/views/wiki/rollback.rhtml +31 -0
  46. data/app/views/wiki/rss_feed.rhtml +22 -0
  47. data/app/views/wiki/search.rhtml +15 -0
  48. data/app/views/wiki/tex.rhtml +23 -0
  49. data/app/views/wiki/tex_web.rhtml +35 -0
  50. data/app/views/wiki/web_list.rhtml +13 -0
  51. data/app/views/wiki_words_help.rhtml +8 -0
  52. data/instiki +67 -0
  53. data/libraries/action_controller_servlet.rb +177 -0
  54. data/libraries/diff/diff.rb +475 -0
  55. data/libraries/erb.rb +490 -0
  56. data/libraries/madeleine_service.rb +68 -0
  57. data/libraries/rdocsupport.rb +156 -0
  58. data/libraries/redcloth_for_tex.rb +869 -0
  59. data/libraries/view_helper.rb +33 -0
  60. data/libraries/web_controller_server.rb +81 -0
  61. metadata +142 -0
@@ -0,0 +1,14 @@
1
+ <% @title = "Export" %><%= sub_template "top" %>
2
+
3
+ <p>You can export all the pages in this web as a zip file in either HTML (with working links and all) or the pure markup (to import in another wiki).</p>
4
+
5
+ <ul id="feedsList">
6
+ <li><a href="../export_html">HTML</a>
7
+ <li><a href="../export_markup">Markup (<%= @web.markup %>)</a>
8
+ <% if OPTIONS[:pdflatex] && @web.markup == :textile %>
9
+ <li><a href="../export_tex">TeX</a>
10
+ <li><a href="../export_pdf">PDF</a>
11
+ <% end %>
12
+ </ul>
13
+
14
+ <%= sub_template "bottom" %>
@@ -0,0 +1,10 @@
1
+ <% @title = "Feeds" %><%= sub_template "top" %>
2
+
3
+ <p>You can subscribe to this wiki by RSS and get either just the headlines of the pages that change or the entire page.</p>
4
+
5
+ <ul id="feedsList">
6
+ <li><a href="../rss_with_content<%= "?password=#{web.password}" if web.password %>">Full content (RSS 2.0)</a>
7
+ <li><a href="../rss_with_headlines<%= "?password=#{web.password}" if web.password %>">Headlines (RSS 2.0)</a>
8
+ </ul>
9
+
10
+ <%= sub_template "bottom" %>
@@ -0,0 +1,57 @@
1
+ <% @title = "All Pages" %><%= sub_template "top" %>
2
+
3
+ <% unless @categories.empty? %>
4
+ <div id="categories">
5
+ <strong>Categories</strong>:
6
+ [<a href=".">Any</a>]
7
+ <%= @category_links.join(', ') %>
8
+ </div>
9
+ <% end %>
10
+
11
+ <div id="allPages" style="float: left; width: 280px; margin-right: 30px">
12
+ <% unless @pages_that_are_orphaned.empty? && @page_names_that_are_wanted.empty? %>
13
+ <h2>
14
+ All Pages
15
+ <br/><small style="font-size: 12px"><i>All pages in <%= @set_name %> listed alphabetically</i></small>
16
+ </h2>
17
+ <% end %>
18
+
19
+ <ul><% for page in @pages_by_name %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %></ul>
20
+
21
+ <% if @web.count_pages %>
22
+ <% total_chars = @pages_in_category.characters %>
23
+ <p><small>All content: <%= total_chars %> chars / <%= sprintf("%-.1f", (total_chars / 2275 )) %> pages</small></p>
24
+ <% end %>
25
+ </div>
26
+
27
+ <div style="float: left; width: 280px">
28
+ <% unless @page_names_that_are_wanted.empty? %>
29
+ <h2>
30
+ Wanted Pages
31
+ <br/><small style="font-size: 12px"><i>Unexisting pages that other pages in <%= @set_name %> reference</i></small>
32
+ </h2>
33
+
34
+ <ul style="margin-bottom: 10px">
35
+ <% for page_name in @page_names_that_are_wanted %>
36
+ <li>
37
+ <a href="../show/<%= page_name %>"><%= truncate(WikiWords.separate(page_name), 35) %></a>
38
+ wanted by
39
+ <%= web.select.pages_that_reference(page_name).collect { |page| page.link }.join(", ") %>
40
+ </li>
41
+ <% end %>
42
+ </ul>
43
+ <% end %>
44
+
45
+ <% unless @pages_that_are_orphaned.empty? %>
46
+ <h2>
47
+ Orphaned Pages
48
+ <br/><small style="font-size: 12px"><i>Pages in <%= @set_name %> that no other page reference</i></small>
49
+ </h2>
50
+
51
+ <ul style="margin-bottom: 35px">
52
+ <% for page in @pages_that_are_orphaned %><li><a href="../show/<%= page.name %>"><%= truncate(page.plain_name, 35) %></a></li><% end %>
53
+ </ul>
54
+ <% end %>
55
+ </div>
56
+
57
+ <%= sub_template "bottom" %>
@@ -0,0 +1,14 @@
1
+ <% @title = "#{@page.plain_name} is locked" %><%= sub_template "top" %>
2
+
3
+ <% if @page.lock_duration(Time.now) == 0 %>
4
+ <p><%= @page.locked_by_link %> just started editing this page.</p>
5
+ <% else %>
6
+ <p><%= @page.locked_by_link %> has been editing this page for <%= @page.lock_duration(Time.now) %> minutes.</p>
7
+ <% end %>
8
+
9
+ <p>
10
+ <a href="<%= @page.name %>?break_lock=1">Edit the page anyway</a> |
11
+ <a href="../show/<%= @page.name %>">Cancel</a>
12
+ </p>
13
+
14
+ <%= sub_template "bottom" %>
@@ -0,0 +1,11 @@
1
+ <% @title = "#{@web_name} Login" %><% @hide_navigation = true %><%= sub_template "top" %>
2
+
3
+ <form action="authenticate" method="post">
4
+ <p>
5
+ <b>Password</b><br />
6
+ <input type="password" name="password" />
7
+ </p>
8
+ </form>
9
+
10
+
11
+ <%= sub_template "bottom" %>
@@ -0,0 +1,27 @@
1
+ <%
2
+ @title = "Creating #{WikiWords.separate(CGI.unescape(@page_name))}"
3
+ @content_width = 720
4
+ @hide_navigation = true
5
+ %><%= sub_template "top" %>
6
+
7
+ <%= render_markup_help %>
8
+
9
+ <form action="../save/<%= @page_name %>" method="post" onSubmit="cleanAuthorName();">
10
+ <p>
11
+ <textarea name="content" style="width: 450px; height: 500px"></textarea>
12
+ </p>
13
+ <p>
14
+ <input type="submit" value="Create"> as
15
+ <input type="text" name="author" id="authorName" value="<%= @author %>" onClick="this.value == 'AnonymousCoward' ? this.value = '' : true">
16
+ </p>
17
+ </form>
18
+
19
+ <script language="JavaScript1.2">
20
+ function cleanAuthorName() {
21
+ if (document.getElementById('authorName').value == "") {
22
+ document.getElementById('authorName').value = 'AnonymousCoward';
23
+ }
24
+ }
25
+ </script>
26
+
27
+ <%= sub_template "bottom" %>
@@ -0,0 +1,78 @@
1
+ <% @title = "Instiki Setup"; @content_width = 500 %><%= sub_template "top" %>
2
+
3
+ <p>
4
+ Congratulations on succesfully installing and starting Instiki.
5
+ Since this is the first time Instiki has been run on this port, you'll need to do a brief one-time setup.
6
+ </p>
7
+
8
+ <form action="../create_system" id="setup" method="post" onSubmit="return validateSetup()">
9
+ <ol class="setup">
10
+ <li>
11
+
12
+ <h2 style="margin-bottom: 3px">Name and address for your first web</h2>
13
+ <div class="help">
14
+ The name of the web is included in the title on all pages. The address is the base path that all pages within the web live beneath. Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>. The address can only consist of letters & digits.
15
+ </div>
16
+ <div class="inputBox">
17
+ Name: <input type="text" id="web_name" name="web_name" value="Wiki" onChange="proposeAddress();"
18
+ onClick="this.value == 'Wiki' ? this.value = '' : true"> &nbsp;&nbsp;
19
+ Address: <input type="text" id="web_address" name="web_address" onChange="cleanAddress();" value="wiki">
20
+ </div>
21
+ </li>
22
+
23
+ <li>
24
+ <h2 style="margin-bottom: 3px">Password for creating and changing webs</h2>
25
+ <div class="help">
26
+ Administrative access allows you to make new webs and change existing ones.<br/>
27
+ Everyone with this password will be able to do this, so pick it carefully.
28
+ </div>
29
+ <div class="inputBox">
30
+ Password: <input type="password" id="password" name="password"> &nbsp;&nbsp;
31
+ Verify: <input type="password" id="password_check" name="password_check">
32
+ </div>
33
+ </li>
34
+ </ol>
35
+
36
+ <p align="right">
37
+ <input type="submit" value="Setup" style="margin-left: 40px">
38
+ </p>
39
+ </form>
40
+
41
+ <script>
42
+ function proposeAddress() {
43
+ document.getElementById('web_address').value =
44
+ document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
45
+ }
46
+
47
+ function cleanAddress() {
48
+ document.getElementById('web_address').value =
49
+ document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
50
+ }
51
+
52
+ function validateSetup() {
53
+ if (document.getElementById('web_name').value == "") {
54
+ alert("You must pick a name for the first web");
55
+ return false;
56
+ }
57
+
58
+ if (document.getElementById('web_address').value == "") {
59
+ alert("You must pick an address for the first web");
60
+ return false;
61
+ }
62
+
63
+ if (document.getElementById('password').value == "") {
64
+ alert("You must pick a system password");
65
+ return false;
66
+ }
67
+
68
+ if (document.getElementById('password_check').value == "" ||
69
+ document.getElementById('password').value != document.getElementById('password_check').value) {
70
+ alert("The password and its verification doesn't match");
71
+ return false;
72
+ }
73
+
74
+ return true;
75
+ }
76
+ </script>
77
+
78
+ <%= sub_template "bottom" %>
@@ -0,0 +1,64 @@
1
+ <% @title = "New Wiki Web"; @content_width = 500 %><%= sub_template "top" %>
2
+
3
+ <p>
4
+ Each web serves as an isolated name space for wiki pages, so different subjects or projects can write about different <i>MuppetShows</i>.
5
+ </p>
6
+
7
+ <form action="../create_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
8
+ <ol class="setup">
9
+ <li>
10
+ <h2 style="margin-bottom: 3px">Name and address for your new web</h2>
11
+ <div class="help">
12
+ The name of the web is included in the title on all pages. The address is the base path that all pages within the web live beneath. Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>. The address can only consist of letters & digits.
13
+ </div>
14
+ <div class="inputBox">
15
+ Name: <input type="text" id="web_name" name="name" onChange="proposeAddress();" /> &nbsp;&nbsp;
16
+ Address: <input type="text" id="web_address" name="address" onChange="cleanAddress();" />
17
+ </div>
18
+ </li>
19
+ </ol>
20
+
21
+
22
+ <p align="right">
23
+ <small>
24
+ Enter system password
25
+ <input type="password" id="system_password" name="system_password">
26
+ and
27
+ <input type="submit" value="Create Web">
28
+ </small>
29
+ </p>
30
+
31
+ </form>
32
+
33
+ <script>
34
+ function proposeAddress() {
35
+ document.getElementById('web_address').value =
36
+ document.getElementById('web_name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
37
+ }
38
+
39
+ function cleanAddress() {
40
+ document.getElementById('web_address').value =
41
+ document.getElementById('web_address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
42
+ }
43
+
44
+ function validateSetup() {
45
+ if (document.getElementById('web_name').value == "") {
46
+ alert("You must pick a name for the new web");
47
+ return false;
48
+ }
49
+
50
+ if (document.getElementById('web_address').value == "") {
51
+ alert("You must pick an address for the new web");
52
+ return false;
53
+ }
54
+
55
+ if (document.getElementById('system_password').value == "") {
56
+ alert("You must enter the system password");
57
+ return false;
58
+ }
59
+
60
+ return true;
61
+ }
62
+ </script>
63
+
64
+ <%= sub_template "bottom" %>
@@ -0,0 +1,81 @@
1
+ <% @title = @page.plain_name %><%= sub_template "top" %>
2
+
3
+ <div id="revision">
4
+ <%= @page.display_content %>
5
+ </div>
6
+
7
+ <div id="changes" style="display: none">
8
+ <p style="background: #eee; padding: 3px; border: 1px solid silver">
9
+ <small>
10
+ Showing changes from revision #<%= @page.number - 1 %> to #<%= @page.number %>:
11
+ <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
12
+ </small>
13
+ </p>
14
+
15
+ <%= @page.display_diff %>
16
+ </div>
17
+
18
+
19
+ <div class="byline">
20
+ <%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
21
+ by <%= @page.author_link %>
22
+ <%= "(#{@page.author.ip})" if @page.author.respond_to?(:ip) %>
23
+ <% if @web.count_pages %>
24
+ <% total_chars = @page.content.length %>
25
+ (<%= total_chars %> characters / <%= sprintf("%-.1f", (total_chars / 2275 rescue 0)) %> pages)
26
+ <% end %>
27
+ </div>
28
+
29
+ <div class="navigation">
30
+ <% if @page.name == "HomePage" %>
31
+ <a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit Page</a>
32
+ | <a href="../edit_web/" class="navlink">Edit Web</a>
33
+ <% else %>
34
+ <a href="../edit/<%= @page.name %>" class="navlink" accesskey="E">Edit</a>
35
+ <% end %>
36
+
37
+ <% if @page.revisions.length > 1 %>
38
+ | <a href="../revision/<%= @page.name %>?rev=<%= @page.revisions.length - 2 %>" class="navlink" accesskey="R">Back in time</a>
39
+ <small>(<%= @page.revisions.length - 1 %> revisions)</small>
40
+ <% end %>
41
+
42
+ <% if @page.revisions.length > 1 %>
43
+ <span id="show_changes">
44
+ | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
45
+ </span>
46
+ <span id="hide_changes" style="display: none">
47
+ | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
48
+ </span>
49
+ <% end %>
50
+
51
+ <small>
52
+ | Views: <a href="../print/<%= @page.name %>">Print</a>
53
+ <% if OPTIONS[:pdflatex] && @web.markup == :textile %>
54
+ | <a href="../tex/<%= @page.name %>">TeX</a> | <a href="../pdf/<%= @page.name %>">PDF</a>
55
+ <% end %>
56
+ </small>
57
+
58
+ <% if @page.references.length > 0 %>
59
+ <small>
60
+ | Linked from: <%= @page.references.collect { |ref| ref.link }.join(", ") %>
61
+ </small>
62
+ <% end %>
63
+ </div>
64
+
65
+ <script language="Javascript">
66
+ function toggleChanges() {
67
+ if (document.getElementById("changes").style.display == "none") {
68
+ document.getElementById("changes").style.display = "block";
69
+ document.getElementById("revision").style.display = "none";
70
+ document.getElementById("show_changes").style.display = "none";
71
+ document.getElementById("hide_changes").style.display = "inline";
72
+ } else {
73
+ document.getElementById("changes").style.display = "none";
74
+ document.getElementById("revision").style.display = "block";
75
+ document.getElementById("show_changes").style.display = "inline";
76
+ document.getElementById("hide_changes").style.display = "none";
77
+ }
78
+ }
79
+ </script>
80
+
81
+ <%= sub_template "bottom" %>
@@ -0,0 +1,16 @@
1
+ <%
2
+ @title = @page.plain_name
3
+ @hide_navigation = true
4
+ @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
5
+ @inline_style = true
6
+ %><%= sub_template "top" %>
7
+
8
+ <%= @page.display_content_for_export %>
9
+
10
+ <div class="byline">
11
+ <%= @page.revisions? ? "Revised" : "Created" %> on <%= @page.pretty_created_at %>
12
+ by
13
+ <%= @page.author_link({ :mode => :export }) %>
14
+ </div>
15
+
16
+ <%= sub_template "bottom" %>
@@ -0,0 +1,10 @@
1
+ <%
2
+ @title = @page.plain_name
3
+ @hide_navigation = true
4
+ @style_additions = ".newWikiWord { background-color: white; font-style: italic; }"
5
+ @inline_style = true
6
+ %><%= sub_template "top" %>
7
+
8
+ <%= @page.display_published %>
9
+
10
+ <%= sub_template "bottom" %>
@@ -0,0 +1,30 @@
1
+ <% @title = "Recently Revised" %><%= sub_template "top" %>
2
+
3
+ <% unless @categories.empty? %>
4
+ <div id="categories">
5
+ <strong>Categories</strong>:
6
+ [<a href=".">Any</a>]
7
+ <%= @category_links.join(', ') %>
8
+ </div>
9
+ <% end %>
10
+
11
+ <% revision_date = Date.new(2100) %>
12
+ <ul>
13
+ <% for page in @pages_by_revision %>
14
+ <% if page.revised_on < revision_date %>
15
+ </ul><b><%= page.pretty_revised_on %></b><ul>
16
+ <% end %>
17
+
18
+ <li>
19
+ <a href="../show/<%= page.name %>"><%= page.plain_name %></a>
20
+ <div class="byline" style="margin-bottom: 0px">
21
+ by <%= page.author_link %>
22
+ at <%= page.created_at.strftime "%H:%M" %>
23
+ <%= "from #{page.author.ip}" if page.author.respond_to?(:ip) %>
24
+ </div>
25
+ </li>
26
+
27
+ <% revision_date = page.revised_on %>
28
+ <% end %>
29
+
30
+ <%= sub_template "bottom" %>
@@ -0,0 +1,81 @@
1
+ <% @title = "#{@page.plain_name} (Rev ##{@revision.number})" %><%= sub_template "top" %>
2
+
3
+ <div id="revision">
4
+ <%= @revision.display_content %>
5
+ </div>
6
+
7
+ <div id="changes" style="display: none">
8
+ <p style="background: #eee; padding: 3px; border: 1px solid silver">
9
+ <small>
10
+ Showing changes from revision #<%= @revision.number - 1 %> to #<%= @revision.number %>:
11
+ <ins class="diffins">Added</ins> | <del class="diffdel">Removed</del>
12
+ </small>
13
+ </p>
14
+
15
+ <%= @revision.display_diff %>
16
+ </div>
17
+
18
+
19
+ <div class="byline">
20
+ <%= "Revision from #{@revision.pretty_created_at} by" %>
21
+ <%= @page.web.make_link(@revision.author) %>
22
+ </div>
23
+
24
+ <div class="navigation">
25
+
26
+ <% if @revision.next_revision %>
27
+ <% if @revision.next_revision.number < (@page.revisions.length - 1) %>
28
+ <a href="../revision/<%= @page.name %>?rev=<%= @revision.next_revision.number %>" class="navlink">
29
+ <% else %>
30
+ <a href="../show/<%= @page.name %>" class="navlink">
31
+ <% end %>
32
+ Forward in time</a>
33
+ (<%= @revision.page.revisions.length - @revision.next_revision.number %> more)
34
+ <% end %>
35
+
36
+ <% if @revision.next_revision && @revision.previous_revision %>
37
+ |
38
+ <% end %>
39
+
40
+ <% if @revision.previous_revision %>
41
+ <a href="../revision/<%= @page.name %>?rev=<%= @revision.previous_revision.number %>" class="navlink">Back in time</a>
42
+ (<%= @revision.previous_revision.number + 1 %> more)
43
+ <% end %>
44
+
45
+ | <a href="../show/<%= @page.name %>" class="navlink">See current</a>
46
+
47
+ <% if @revision.previous_revision %>
48
+ <span id="show_changes">
49
+ | <a href="#" onClick="toggleChanges(); return false;">See changes</a>
50
+ </span>
51
+ <span id="hide_changes" style="display: none">
52
+ | <a href="#" onClick="toggleChanges(); return false;">Hide changes</a>
53
+ </span>
54
+ <% end %>
55
+
56
+ | <a href="../rollback/<%= @page.name %>?rev=<%= @revision.number %>" class="navlink">Rollback</a>
57
+
58
+ <% if @page.references.length > 0 %>
59
+ <small>
60
+ | Linked from: <%= @page.references.collect { |ref| "<a href='#{ref.name}'>#{ref.name}</a>" }.join(", ") %>
61
+ </small>
62
+ <% end %>
63
+ </div>
64
+
65
+ <script language="Javascript">
66
+ function toggleChanges() {
67
+ if (document.getElementById("changes").style.display == "none") {
68
+ document.getElementById("changes").style.display = "block";
69
+ document.getElementById("revision").style.display = "none";
70
+ document.getElementById("show_changes").style.display = "none";
71
+ document.getElementById("hide_changes").style.display = "inline";
72
+ } else {
73
+ document.getElementById("changes").style.display = "none";
74
+ document.getElementById("revision").style.display = "block";
75
+ document.getElementById("show_changes").style.display = "inline";
76
+ document.getElementById("hide_changes").style.display = "none";
77
+ }
78
+ }
79
+ </script>
80
+
81
+ <%= sub_template "bottom" %>