webapp.pl 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. use strict;
  2. use v5.10;
  3. =head1 NAME
  4. webapp - an Oddmuse module that provides offline wiki browsing
  5. =head1 SYNOPSIS
  6. This module makes a number of files available. These files make up a
  7. web application which will then download the rest of the wiki. The
  8. B<Administration> menu will contain a link to the web application.
  9. =head1 INSTALLATION
  10. Installing a module is easy: Create a modules subdirectory in your
  11. data directory, and put the Perl file in there. It will be loaded
  12. automatically.
  13. =cut
  14. AddModuleDescription('webapp.pl', 'Offline Extension');
  15. our ($q, %IndexHash, $ScriptName, $FullUrl, $StyleSheet, $StyleSheetPage, $SurgeProtection, @MyAdminCode, @MyInitVariables, $LastUpdate);
  16. push(@MyAdminCode, \&WebAppMenu);
  17. sub WebAppMenu {
  18. my ($id, $menuref, $restref) = @_;
  19. push(@$menuref,
  20. ScriptLink('webapp/index.html',
  21. T('Web application for offline browsing'),
  22. 'webapp'));
  23. }
  24. push(@MyInitVariables, \&InitWebApp);
  25. my $jquery = 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';
  26. sub InitWebApp {
  27. if ($q->path_info =~ /^\/webapp'/) {
  28. # HACK ALERT: In order to allow the app to cache all the pages, we
  29. # need to disable surge protection for the offline pages.
  30. $SurgeProtection = 0;
  31. }
  32. my $manifest = ScriptUrl('webapp/MANIFEST');
  33. my $oddmuse = ScriptUrl('webapp/oddmuse.js');
  34. if ($q->path_info eq '/webapp/index.html') {
  35. # Switch to HTML5 add link to the manifest listing all the pages
  36. print GetHttpHeader('text/html', $LastUpdate);
  37. print <<EOT;
  38. <!doctype html>
  39. <html lang="en" manifest="$manifest">
  40. <head>
  41. <meta name="apple-mobile-web-app-capable" content="yes" />
  42. <meta name="apple-mobile-web-app-status-bar-style" content="black" />
  43. <meta charset="utf-8">
  44. <title>HomePage</title>
  45. <script type="text/javascript" charset="utf-8" src="$jquery" ></script>
  46. <script type="text/javascript" charset="utf-8" src="$oddmuse" ></script>
  47. </head>
  48. <body>
  49. <h1>HomePage</h1>
  50. <p>Caching the local wiki!</p>
  51. <p id="cache">Caching <span id="page">0</span>/<span id="total">0</span>.</p>
  52. <p id="status">Enable Javascript and reload this page in order to get started.</p>
  53. </body>
  54. </html>
  55. EOT
  56. exit;
  57. } elsif ($q->path_info eq '/webapp/MANIFEST') {
  58. # List all the pages necessary for the offline application.
  59. print GetHttpHeader('text/cache-manifest');
  60. print "CACHE MANIFEST\n";
  61. # index.html
  62. print ScriptUrl('webapp/index.html') . "\n";
  63. # CSS file
  64. if ($StyleSheet) {
  65. print ref $StyleSheet ? join("\n", @$StyleSheet) . "\n" : "$StyleSheet\n";
  66. } elsif ($IndexHash{$StyleSheetPage}) {
  67. print "$ScriptName?action=browse;id=" . UrlEncode($StyleSheetPage)
  68. . ";raw=1;mime-type=text/css\n";
  69. } else {
  70. print "http://www.oddmuse.org/oddmuse.css\n";
  71. }
  72. # javascript
  73. print "$jquery\n";
  74. print "$oddmuse\n";
  75. # default
  76. print "NETWORK:\n";
  77. print "*\n";
  78. exit;
  79. } elsif ($q->path_info eq '/webapp/oddmuse.js') {
  80. print GetHttpHeader('application/javascript', $LastUpdate);
  81. my $quoted_script = quotemeta($ScriptName);
  82. print <<EOT;
  83. var wiki = (function() {
  84. var pages;
  85. var log = function(msg) {
  86. \$("#status").text(msg);
  87. }
  88. var log_html = function(msg) {
  89. \$("#status").html(msg);
  90. }
  91. var log_node = function(msg) {
  92. \$("#status").empty();
  93. \$("#status").append(msg);
  94. }
  95. var get_pages = function(data) {
  96. pages = data.split("\\n");
  97. var count = 1;
  98. pages.pop(); // after the last newline there is nothing
  99. \$("#total").text(pages.length);
  100. var get_page = function(i, id) {
  101. if (id != "") {
  102. var store_page = function(data) {
  103. window.localStorage.setItem(id, data);
  104. \$("#page").text(count++);
  105. }
  106. \$.get("$FullUrl",
  107. {action: "browse", id: id},
  108. store_page);
  109. }
  110. }
  111. \$.each(pages, get_page);
  112. window.localStorage.setItem(" pages", pages.join(" "));
  113. }
  114. var download = function() {
  115. log("Getting list of pages...");
  116. \$.get("$FullUrl",
  117. {action: "index", raw: "1"},
  118. get_pages);
  119. log_html('<p><a href="javascript:wiki.list()">List</a> the pages in local storage.');
  120. }
  121. var initialize = function() {
  122. pages = window.localStorage.getItem(" pages");
  123. if (pages) {
  124. pages = pages.split(" ");
  125. log_html('<p>Found pages in local storage. <a href="javascript:wiki.list()">List</a> the pages in local storage. <a href="javascript:wiki.download()">Download</a> a fresh copy.');
  126. } else {
  127. download();
  128. }
  129. };
  130. var list = function() {
  131. var ul = document.createElement('ul');
  132. \$.each(pages, function(i, id) {
  133. var li = document.createElement('li');
  134. var a = document.createElement('a');
  135. \$(a).attr({href: "javascript:wiki.browse('" + id + "')"});
  136. \$(a).text(id);
  137. \$(li).append(a);
  138. \$(ul).append(li);
  139. });
  140. log_node(ul);
  141. }
  142. var browse = function(id) {
  143. var re = /$quoted_script\\/([^\\/?]+)/;
  144. \$('*').html(window.localStorage.getItem(id));
  145. \$('a[href^="$ScriptName"]').each(function(i, a) {
  146. var match = re.exec(\$(a).attr('href'));
  147. if (match) {
  148. var id = unescape(match[1]);
  149. if (pages.indexOf(id) >= 0) {
  150. \$(a).attr('href', "javascript:wiki.browse('" + id + "')");
  151. }
  152. }
  153. });
  154. }
  155. return {
  156. initialize: initialize,
  157. download: download,
  158. list: list,
  159. browse: browse,
  160. };
  161. }());
  162. \$(document).ready(function(evt) {
  163. wiki.initialize();
  164. });
  165. EOT
  166. exit;
  167. }
  168. };
  169. =head1 COPYRIGHT AND LICENSE
  170. Copyright (C) 2011 Alex Schroeder <alex@gnu.org>
  171. This program is free software: you can redistribute it and/or modify it under
  172. the terms of the GNU General Public License as published by the Free Software
  173. Foundation, either version 3 of the License, or (at your option) any later
  174. version.
  175. This program is distributed in the hope that it will be useful, but WITHOUT ANY
  176. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  177. PARTICULAR PURPOSE. See the GNU General Public License for more details.
  178. You should have received a copy of the GNU General Public License along with
  179. this program. If not, see <http://www.gnu.org/licenses/>.
  180. =cut