instiki 0.9.2 → 0.10.0

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 (102) hide show
  1. data/CHANGELOG +165 -0
  2. data/README +68 -172
  3. data/app/controllers/admin_controller.rb +94 -0
  4. data/app/controllers/application.rb +131 -0
  5. data/app/controllers/file_controller.rb +129 -0
  6. data/app/controllers/wiki_controller.rb +354 -0
  7. data/{libraries/view_helper.rb → app/helpers/application_helper.rb} +68 -33
  8. data/app/models/author.rb +3 -3
  9. data/app/models/chunks/category.rb +33 -31
  10. data/app/models/chunks/chunk.rb +86 -20
  11. data/app/models/chunks/engines.rb +54 -38
  12. data/app/models/chunks/include.rb +41 -29
  13. data/app/models/chunks/literal.rb +31 -19
  14. data/app/models/chunks/nowiki.rb +28 -31
  15. data/app/models/chunks/test.rb +18 -18
  16. data/app/models/chunks/uri.rb +182 -97
  17. data/app/models/chunks/wiki.rb +141 -82
  18. data/app/models/file_yard.rb +58 -0
  19. data/app/models/page.rb +112 -86
  20. data/app/models/page_lock.rb +22 -23
  21. data/app/models/page_set.rb +89 -64
  22. data/app/models/revision.rb +123 -90
  23. data/app/models/web.rb +176 -89
  24. data/app/models/wiki_content.rb +207 -105
  25. data/app/models/wiki_service.rb +233 -83
  26. data/app/models/wiki_words.rb +23 -25
  27. data/app/views/{wiki/new_system.rhtml → admin/create_system.rhtml} +83 -78
  28. data/app/views/{wiki/new_web.rhtml → admin/create_web.rhtml} +69 -64
  29. data/app/views/admin/edit_web.rhtml +136 -0
  30. data/app/views/file/file.rhtml +19 -0
  31. data/app/views/file/import.rhtml +23 -0
  32. data/app/views/layouts/default.rhtml +85 -0
  33. data/app/views/markdown_help.rhtml +12 -16
  34. data/app/views/mixed_help.rhtml +7 -0
  35. data/app/views/navigation.rhtml +30 -19
  36. data/app/views/rdoc_help.rhtml +12 -16
  37. data/app/views/textile_help.rhtml +24 -28
  38. data/app/views/wiki/authors.rhtml +11 -13
  39. data/app/views/wiki/edit.rhtml +39 -31
  40. data/app/views/wiki/export.rhtml +12 -14
  41. data/app/views/wiki/feeds.rhtml +14 -10
  42. data/app/views/wiki/list.rhtml +64 -57
  43. data/app/views/wiki/locked.rhtml +23 -14
  44. data/app/views/wiki/login.rhtml +14 -11
  45. data/app/views/wiki/new.rhtml +31 -27
  46. data/app/views/wiki/page.rhtml +115 -81
  47. data/app/views/wiki/print.rhtml +14 -16
  48. data/app/views/wiki/published.rhtml +9 -10
  49. data/app/views/wiki/recently_revised.rhtml +27 -30
  50. data/app/views/wiki/revision.rhtml +103 -81
  51. data/app/views/wiki/rollback.rhtml +14 -9
  52. data/app/views/wiki/rss_feed.rhtml +22 -22
  53. data/app/views/wiki/search.rhtml +38 -15
  54. data/app/views/wiki/tex.rhtml +22 -22
  55. data/app/views/wiki/tex_web.rhtml +34 -34
  56. data/app/views/wiki/web_list.rhtml +18 -13
  57. data/app/views/wiki_words_help.rhtml +9 -8
  58. data/config/environment.rb +82 -0
  59. data/config/environments/development.rb +5 -0
  60. data/config/environments/production.rb +4 -0
  61. data/config/environments/test.rb +17 -0
  62. data/config/routes.rb +18 -0
  63. data/instiki +6 -67
  64. data/instiki.rb +3 -0
  65. data/lib/active_record_stub.rb +31 -0
  66. data/{libraries/diff → lib}/diff.rb +444 -475
  67. data/lib/instiki_errors.rb +15 -0
  68. data/{libraries → lib}/rdocsupport.rb +151 -155
  69. data/lib/redcloth_for_tex.rb +736 -0
  70. data/natives/osx/desktop_launcher/AppDelegate.h +18 -0
  71. data/natives/osx/desktop_launcher/AppDelegate.mm +109 -0
  72. data/natives/osx/desktop_launcher/Credits.html +16 -0
  73. data/natives/osx/desktop_launcher/English.lproj/InfoPlist.strings +0 -0
  74. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/classes.nib +13 -0
  75. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/info.nib +24 -0
  76. data/natives/osx/desktop_launcher/English.lproj/MainMenu.nib/objects.nib +0 -0
  77. data/natives/osx/desktop_launcher/Info.plist +13 -0
  78. data/natives/osx/desktop_launcher/Instiki.xcode/project.pbxproj +592 -0
  79. data/natives/osx/desktop_launcher/Instiki_Prefix.pch +7 -0
  80. data/natives/osx/desktop_launcher/MakeDMG.sh +9 -0
  81. data/natives/osx/desktop_launcher/main.mm +14 -0
  82. data/natives/osx/desktop_launcher/version.plist +16 -0
  83. data/public/404.html +6 -0
  84. data/public/500.html +6 -0
  85. data/public/dispatch.rb +10 -0
  86. data/public/favicon.ico +0 -0
  87. data/public/javascripts/edit_web.js +52 -0
  88. data/public/javascripts/prototype.js +336 -0
  89. data/{app/views/static_style_sheet.rhtml → public/stylesheets/instiki.css} +221 -198
  90. data/script/breakpointer +4 -0
  91. data/script/server +93 -0
  92. metadata +59 -32
  93. data/app/controllers/wiki.rb +0 -389
  94. data/app/models/chunks/match.rb +0 -19
  95. data/app/views/bottom.rhtml +0 -4
  96. data/app/views/top.rhtml +0 -49
  97. data/app/views/wiki/edit_web.rhtml +0 -138
  98. data/libraries/action_controller_servlet.rb +0 -177
  99. data/libraries/erb.rb +0 -490
  100. data/libraries/madeleine_service.rb +0 -68
  101. data/libraries/redcloth_for_tex.rb +0 -869
  102. data/libraries/web_controller_server.rb +0 -81
@@ -1,19 +0,0 @@
1
- # This module is to be included in unit tests that involve matching chunks.
2
- # It provides a easy way to test whether a chunk matches a particular string
3
- # and any the values of any fields that should be set after a match.
4
- module ChunkMatch
5
-
6
- # Asserts a number of tests for the given type and text.
7
- def match(type, test_text, expected)
8
- pattern = type.pattern
9
- assert_match(pattern, test_text)
10
- pattern =~ test_text # Previous assertion guarantees match
11
- chunk = type.new($~)
12
-
13
- # Test if requested parts are correct.
14
- for method_sym, value in expected do
15
- assert_respond_to(chunk, method_sym)
16
- assert_equal(value, chunk.method(method_sym).call, "Checking value of '#{method_sym}'")
17
- end
18
- end
19
- end
@@ -1,4 +0,0 @@
1
- </div>
2
- </div>
3
- </body>
4
- </html>
@@ -1,49 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <html xmlns="http://www.w3.org/1999/xhtml">
3
- <head>
4
- <title>
5
- <% if @web && @page && @page.name == "HomePage" && %( show published print ).include?(@action_name) %>
6
- <%= @web.name %>
7
- <% elsif @web %>
8
- <%= @title %> in <%= @web.name %>
9
- <% else %>
10
- <%= @title %>
11
- <% end %>
12
- </title>
13
-
14
- <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
15
-
16
- <style type="text/css">
17
- h1#pageName, .newWikiWord a, a.existingWikiWord, .newWikiWord a:hover, #TextileHelp h3 {
18
- color: #<%= @web ? @web.color : "393" %>;
19
- }
20
-
21
- #Container, #Content {
22
- width: <%= @content_width || "600" %>px;
23
- }
24
- <%= sub_template("static_style_sheet") if @inline_style %>
25
- </style>
26
-
27
- <link rel="Stylesheet" href="../static_style_sheet/" type="text/css" media="screen" />
28
-
29
- <style type="text/css">
30
- <%= @style_additions %>
31
- <%= @web ? web.additional_style : "" %>
32
- </style>
33
- </head>
34
- <body>
35
- <div id="Container">
36
- <div id="Content">
37
-
38
- <h1 id="pageName">
39
- <% if @web && @page && @page.name == "HomePage" && %( show published print ).include?(@action_name) %>
40
- <%= @web.name %>
41
- <% elsif @web %>
42
- <small><%= @web.name %></small><br />
43
- <%= @title %>
44
- <% else %>
45
- <%= @title %>
46
- <% end %>
47
- </h1>
48
-
49
- <%= sub_template "navigation" unless @web.nil? || @hide_navigation %>
@@ -1,138 +0,0 @@
1
- <% @title = "Edit Web" %><%= sub_template "top" %>
2
-
3
- <form action="../update_web" id="setup" method="post" onSubmit="cleanAddress(); return validateSetup()">
4
- <h2 style="margin-bottom: 3px">Name and address</h2>
5
- <div class="help">
6
- 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.
7
- Ex: the address "rails" gives URLs like <i>/rails/show/HomePage</i>.
8
- </div>
9
-
10
- <div class="inputBox">
11
- Name: <input type="text" id="name" name="name" value="<%= @web.name %>" onChange="proposeAddress();"> &nbsp;&nbsp;
12
- Address: <input type="text" id="address" name="address" value="<%= @web.address %>" onChange="cleanAddress();">
13
- <i>(Letters & digits only)</i>
14
- </div>
15
-
16
- <h2 style="margin-bottom: 3px">Specialize</h2>
17
- <div class="help">
18
- Turning safe mode on will strip HTML tags and stylesheet options from the content of all pages.
19
- Turning on "brackets only" will require all wiki words to be as [[wiki word]] and WikiWord won't work.
20
- Additions to the stylesheet take precedence over the existing styles. <i>Hint:</i> View source on a page you want to
21
- style to find ID names on individual tags. <a href="#" onClick="document.getElementById('additionalStyle').style.display='block';return false;">See styles >></a>
22
- </div>
23
- <div class="inputBox">
24
- Markup:
25
- <select name="markup">
26
- <%= html_options({ "Textile" => :textile, "Markdown" => :markdown, "RDoc" => :rdoc }, @web.markup) %>
27
- </select>
28
-
29
- &nbsp;&nbsp;
30
-
31
- Color:
32
- <select name="color">
33
- <%= html_options({ "Green" => "008B26", "Purple" => "504685", "Red" => "DA0006", "Orange" => "FA6F00", "Grey" => "8BA2B0" },
34
- @web.color) %>
35
- </select>
36
-
37
- &nbsp;&nbsp;
38
-
39
- <small>
40
-
41
- <input type="checkbox" name="safe_mode"<%= " CHECKED" if @web.safe_mode %>> Safe mode
42
-
43
- &nbsp;&nbsp;
44
-
45
- <input type="checkbox" name="brackets_only"<%= " CHECKED" if @web.brackets_only %>> Brackets only
46
-
47
- &nbsp;&nbsp;
48
-
49
- <input type="checkbox" name="count_pages"<%= " CHECKED" if @web.count_pages %>> Count pages
50
-
51
- </small>
52
-
53
- <textarea id="additionalStyle" style="display: none; margin-top: 10px; margin-bottom: 5px; width: 560px; height: 200px" name="additional_style"><%= @web.additional_style %></textarea>
54
- </div>
55
-
56
- <h2 style="margin-bottom: 3px">Password protection for this web (<%= @web.name %>)</h2>
57
- <div class="help">
58
- This is the password that visitors need to view and edit this web. Setting the password to nothing will remove the password protection.
59
- </div>
60
- <div class="inputBox">
61
- Password: <input type="password" id="password" name="password" value="<%= @web.password %>"> &nbsp;&nbsp;
62
- Verify: <input type="password" id="password_check" value="<%= @web.password %>" name="password_check">
63
- </div>
64
-
65
- <h2 style="margin-bottom: 3px">Publish read-only version of this web (<%= @web.name %>)</h2>
66
- <div class="help">
67
- You can turn on a read-only version of this web that's accessible even when the regular web is password protected.
68
- The published version is accessible through URLs like /wiki/published/HomePage.
69
- </div>
70
- <div class="inputBox">
71
- <input type="checkbox" name="published"<%= " CHECKED" if @web.published %>> Publish this web
72
- </div>
73
-
74
- <p align="right">
75
- <small>
76
- Enter system password
77
- <input type="password" id="system_password" name="system_password">
78
- and
79
- <input type="submit" value="Update Web">
80
- <br/><br/>
81
- ...or forget changes and <a href="/new_web/">create a new web</a>
82
- </small>
83
- </p>
84
-
85
- </form>
86
-
87
- <br/>
88
- <h1>Other administrative tasks</h1>
89
-
90
- <form action="../remove_orphaned_pages" id="remove_orphaned_pages" method="post">
91
-
92
- <p align="right">
93
- <small>
94
- Clean up by entering system password
95
- <input type="password" id="system_password" name="system_password">
96
- and
97
- <input type="submit" value="Delete Orphan Pages">
98
- </small>
99
- </p>
100
-
101
- <script>
102
- function proposeAddress() {
103
- document.getElementById('address').value =
104
- document.getElementById('name').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
105
- }
106
-
107
- function cleanAddress() {
108
- document.getElementById('address').value =
109
- document.getElementById('address').value.replace(/[^a-zA-Z0-9]/g, "").toLowerCase();
110
- }
111
-
112
- function validateSetup() {
113
- if (document.getElementById('system_password').value == "") {
114
- alert("You must enter the system password");
115
- return false;
116
- }
117
-
118
- if (document.getElementById('name').value == "") {
119
- alert("You must pick a name for the web");
120
- return false;
121
- }
122
-
123
- if (document.getElementById('address').value == "") {
124
- alert("You must pick an address for the web");
125
- return false;
126
- }
127
-
128
- if (document.getElementById('password').value != "" &&
129
- document.getElementById('password').value != document.getElementById('password_check').value) {
130
- alert("The password and its verification doesn't match");
131
- return false;
132
- }
133
-
134
- return true;
135
- }
136
- </script>
137
-
138
- <%= sub_template "bottom" %>
@@ -1,177 +0,0 @@
1
- require 'erb'
2
- require 'cgi'
3
- require 'webrick'
4
- include WEBrick
5
-
6
- require 'view_helper'
7
-
8
- class ActionControllerServlet < HTTPServlet::AbstractServlet
9
- @@template_root = "./views"
10
- def self.template_root() @@template_root end
11
- def self.template_root=(template_root) @@template_root = template_root end
12
-
13
- include ViewHelper
14
-
15
- def do_POST(req, res) do_GET(req, res) end
16
-
17
- def do_GET(req, res)
18
- @req, @res = req, res
19
-
20
- @res['Content-Type'] = "text/html; charset=utf-8"
21
- @res['Pragma'] = "no-cache"
22
- @res['Cache-Control'] = "no-cache"
23
-
24
- @params = @req.query
25
- @assigns = {}
26
- @performed_render = @performed_redirect = false
27
-
28
- @logger.info "Performing #{action_name}"
29
- @logger.info " Parameters: #{@params.inspect}"
30
- @logger.info " Cookies: #{@req.cookies.collect { |c| "#{c.name} => #{c.value}" }.join(", ") }"
31
-
32
- perform_action
33
- @res
34
- end
35
-
36
- protected
37
- def template_root() self.class.template_root end
38
-
39
- # Issues a HTTP 302 (location) redirect to the specified <tt>path</tt>. Query string
40
- # parameters can be specified in the <tt>params</tt> hash and are automatically URL escaped.
41
- # An <tt>anchor</tt> can also be specified (as a string).
42
- def redirect_path(path, params = nil, anchor = nil)
43
- path << build_query_string(params) unless params.nil?
44
- path << "\##{anchor}" unless anchor.nil?
45
- @res.set_redirect WEBrick::HTTPStatus::MovedPermanently, path
46
- @performed_redirect = true
47
- end
48
-
49
- # Redirects the browser to another action within the current controller, so redirect_path "pages"
50
- # within a controller called WikiController would take the user to "http://www.example.com/wiki/pages"
51
- def redirect_action(action, params = nil, anchor = nil)
52
- redirect_path "/#{controller_name}/#{action}", params, anchor
53
- end
54
-
55
-
56
- # Compiles the template response and adds it to the response. If no template_name has been
57
- # supplied, the action parameter parsed to the controller is used. So invoking
58
- # render on a object initialized with myController.new("show_person") will compile the template
59
- # located at "../views/show_person.rhtml". Notice that the ".rhtml" extension is postfixed
60
- # to the template_name regardless of one was passed or that the action parameter was used.
61
- def render(template_name = "#{controller_name}/#{action_name}")
62
- @performed_render = true
63
- @logger.info "Rendering: #{template_name}"
64
- add_instance_variables_to_assigns
65
- @res.body = template_result "#{template_root}/#{template_name}.rhtml"
66
- end
67
-
68
- def render_text(text)
69
- @performed_render = true
70
- @logger.info "Rendering in text"
71
- @res.body = text
72
- end
73
-
74
- # Wrapper around render that presumes the current controller is used as a base for the action,
75
- # so render_action("page") in WikiController will be equal to render("wiki/page")
76
- def render_action(action_name)
77
- render "#{controller_name}/#{action_name}"
78
- end
79
-
80
- def sub_template(template_name)
81
- template_result "#{template_root}/#{template_name}.rhtml"
82
- end
83
-
84
- # Returns the value of the cookie matching +name+ (or nil if none is found).
85
- def read_cookie(key)
86
- cookies = @req.cookies.select { |cookie| cookie.name == key }
87
- cookies.empty? ? nil : cookies.first.value
88
- end
89
-
90
- def write_cookie(key, value, permanent = false)
91
- @logger.info "Writing cookie: #{key} => #{value}"
92
-
93
- cookie = WEBrick::Cookie.new(key, value)
94
- cookie.path = "/"
95
- cookie.expires = Time.local(2030) if permanent
96
- @res.cookies << cookie
97
- end
98
-
99
-
100
- # Returns the last part of the uri path ("page" in "/wiki/page") by default, but
101
- # can be overwritten to implement mod_rewrite-like behaviour, such as "page" in "/wiki/page/home"
102
- def action_name
103
- @req.path.to_s.split(/\//).last
104
- end
105
-
106
- # Returns an array with each of the parts in the request as an element. So /something/cool/dude
107
- # returns ["something", "cool", "dude"]
108
- def request_path
109
- request_path_parts = @req.path.to_s.split(/\//)
110
- request_path_parts.length > 1 ? request_path_parts[1..-1] : []
111
- end
112
-
113
- # Can be overwritten by a controller class to implement shared behaviour for all the actions in
114
- # the class, such as security measures
115
- def before_action() end
116
-
117
-
118
- private
119
- # Wraps around all action calls to ensure the session is properly updated and closed.
120
- # Figures out whether an action, such as "list", is implemented as show_list or do_list.
121
- # The show_* type has precedence and automatically calls render after execution.
122
- def perform_action
123
- if before_action == false then return end
124
-
125
- if action_methods.include?(action_name)
126
- send(action_name)
127
- render unless @performed_render || @performed_redirect || !@res.body.empty?
128
- elsif template_exists_for_action
129
- render
130
- else
131
- raise "No action responded to #{action_name}", caller
132
- end
133
- end
134
-
135
- def add_instance_variables_to_assigns
136
- instance_variables.each { |var|
137
- next if protected_instance_variables.include?(var)
138
- @assigns[var[1..-1]] = instance_variable_get(var)
139
- }
140
- end
141
-
142
- def protected_instance_variables
143
- [ "@assigns", "@performed_redirect", "@performed_render" ]
144
- end
145
-
146
- def action_methods
147
- methods - Object.instance_methods
148
- end
149
-
150
- # Returns true if a template exists in the controller directory for the specified <tt>action_name</tt>,
151
- # which would mean true if called for WikiController#changes and "/views/wiki/changes.rhtml" existed.
152
- def template_exists_for_action
153
- File.exist? action_template_path
154
- end
155
-
156
- def action_template_path(action = action_name)
157
- "#{template_root}/#{controller_name}/#{action}.rhtml"
158
- end
159
-
160
- def template_result(template_path)
161
- @assigns.each { |key, value| instance_variable_set "@#{key}", value }
162
- ERB.new(IO.readlines(template_path).join).result(binding)
163
- end
164
-
165
- # Converts the class name from something like "OneModule::TwoModule::NeatController"
166
- # to "neat".
167
- def controller_name
168
- self.class.to_s.split("::").last.sub(/Controller/, "").downcase
169
- end
170
-
171
- # Returns a query string with escaped keys and values from the passed hash.
172
- def build_query_string(hash)
173
- elements = []
174
- hash.each { |key, value| elements << "#{CGI.escape(key)}=#{CGI.escape(value.to_s)}" }
175
- "?" + elements.join("&")
176
- end
177
- end
@@ -1,490 +0,0 @@
1
- # Tiny eRuby --- ERB2
2
- # Copyright (c) 1999-2000,2002,2003 Masatoshi SEKI
3
- # You can redistribute it and/or modify it under the same terms as Ruby.
4
-
5
- class ERB
6
- Revision = '$Date: 2004/02/23 23:40:09 $' #'
7
-
8
- def self.version
9
- "erb.rb [2.0.4 #{ERB::Revision.split[1]}]"
10
- end
11
- end
12
-
13
- # ERB::Compiler
14
- class ERB
15
- class Compiler
16
- class PercentLine
17
- def initialize(str)
18
- @value = str
19
- end
20
- attr_reader :value
21
- alias :to_s :value
22
- end
23
-
24
- class Scanner
25
- SplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
26
-
27
- @scanner_map = {}
28
- def self.regist_scanner(klass, trim_mode, percent)
29
- @scanner_map[[trim_mode, percent]] = klass
30
- end
31
-
32
- def self.default_scanner=(klass)
33
- @default_scanner = klass
34
- end
35
-
36
- def self.make_scanner(src, trim_mode, percent)
37
- klass = @scanner_map.fetch([trim_mode, percent], @default_scanner)
38
- klass.new(src, trim_mode, percent)
39
- end
40
-
41
- def initialize(src, trim_mode, percent)
42
- @src = src
43
- @stag = nil
44
- end
45
- attr_accessor :stag
46
-
47
- def scan; end
48
- end
49
-
50
- class TrimScanner < Scanner
51
- TrimSplitRegexp = /(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>\n)|(%>)|(\n)/
52
-
53
- def initialize(src, trim_mode, percent)
54
- super
55
- @trim_mode = trim_mode
56
- @percent = percent
57
- if @trim_mode == '>'
58
- @scan_line = self.method(:trim_line1)
59
- elsif @trim_mode == '<>'
60
- @scan_line = self.method(:trim_line2)
61
- elsif @trim_mode == '-'
62
- @scan_line = self.method(:explicit_trim_line)
63
- else
64
- @scan_line = self.method(:scan_line)
65
- end
66
- end
67
- attr_accessor :stag
68
-
69
- def scan(&block)
70
- @stag = nil
71
- if @percent
72
- @src.each do |line|
73
- percent_line(line, &block)
74
- end
75
- else
76
- @src.each do |line|
77
- @scan_line.call(line, &block)
78
- end
79
- end
80
- nil
81
- end
82
-
83
- def percent_line(line, &block)
84
- if @stag || line[0] != ?%
85
- return @scan_line.call(line, &block)
86
- end
87
-
88
- line[0] = ''
89
- if line[0] == ?%
90
- @scan_line.call(line, &block)
91
- else
92
- yield(PercentLine.new(line.chomp))
93
- end
94
- end
95
-
96
- def scan_line(line)
97
- line.split(SplitRegexp).each do |token|
98
- next if token.empty?
99
- yield(token)
100
- end
101
- end
102
-
103
- def trim_line1(line)
104
- line.split(TrimSplitRegexp).each do |token|
105
- next if token.empty?
106
- if token == "%>\n"
107
- yield('%>')
108
- yield(:cr)
109
- break
110
- end
111
- yield(token)
112
- end
113
- end
114
-
115
- def trim_line2(line)
116
- head = nil
117
- line.split(TrimSplitRegexp).each do |token|
118
- next if token.empty?
119
- head = token unless head
120
- if token == "%>\n"
121
- yield('%>')
122
- if is_erb_stag?(head)
123
- yield(:cr)
124
- else
125
- yield("\n")
126
- end
127
- break
128
- end
129
- yield(token)
130
- end
131
- end
132
-
133
- ExplicitTrimRegexp = /(^[ \t]*<%-)|(-%>\n?\z)|(<%-)|(-%>)|(<%%)|(%%>)|(<%=)|(<%#)|(<%)|(%>)|(\n)/
134
- def explicit_trim_line(line)
135
- line.split(ExplicitTrimRegexp).each do |token|
136
- next if token.empty?
137
- if @stag.nil? && /[ \t]*<%-/ =~ token
138
- yield('<%')
139
- elsif @stag && /-%>\n/ =~ token
140
- yield('%>')
141
- yield(:cr)
142
- elsif @stag && token == '-%>'
143
- yield('%>')
144
- else
145
- yield(token)
146
- end
147
- end
148
- end
149
-
150
- ERB_STAG = %w(<%= <%# <%)
151
- def is_erb_stag?(s)
152
- ERB_STAG.member?(s)
153
- end
154
- end
155
-
156
- Scanner.default_scanner = TrimScanner
157
-
158
- class SimpleScanner < Scanner
159
- def scan
160
- @src.each do |line|
161
- line.split(SplitRegexp).each do |token|
162
- next if token.empty?
163
- yield(token)
164
- end
165
- end
166
- end
167
- end
168
-
169
- Scanner.regist_scanner(SimpleScanner, nil, false)
170
-
171
- begin
172
- require 'strscan'
173
- class SimpleScanner2 < Scanner
174
- def scan
175
- stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
176
- etag_reg = /(.*?)(%%>|%>|\n|\z)/
177
- scanner = StringScanner.new(@src)
178
- while ! scanner.eos?
179
- scanner.scan(@stag ? etag_reg : stag_reg)
180
- text = scanner[1]
181
- elem = scanner[2]
182
- yield(text) unless text.empty?
183
- yield(elem) unless elem.empty?
184
- end
185
- end
186
- end
187
- Scanner.regist_scanner(SimpleScanner2, nil, false)
188
-
189
- class PercentScanner < Scanner
190
- def scan
191
- new_line = true
192
- stag_reg = /(.*?)(<%%|<%=|<%#|<%|\n|\z)/
193
- etag_reg = /(.*?)(%%>|%>|\n|\z)/
194
- scanner = StringScanner.new(@src)
195
- while ! scanner.eos?
196
- if new_line && @stag.nil?
197
- if scanner.scan(/%%/)
198
- yield('%')
199
- new_line = false
200
- next
201
- elsif scanner.scan(/%/)
202
- yield(PercentLine.new(scanner.scan(/.*?(\n|\z)/).chomp))
203
- next
204
- end
205
- end
206
- scanner.scan(@stag ? etag_reg : stag_reg)
207
- text = scanner[1]
208
- elem = scanner[2]
209
- yield(text) unless text.empty?
210
- yield(elem) unless elem.empty?
211
- new_line = (elem == "\n")
212
- end
213
- end
214
- end
215
- Scanner.regist_scanner(PercentScanner, nil, true)
216
-
217
- class ExplicitScanner < Scanner
218
- def scan
219
- new_line = true
220
- stag_reg = /(.*?)(<%%|<%=|<%#|<%-|<%|\n|\z)/
221
- etag_reg = /(.*?)(%%>|-%>|%>|\n|\z)/
222
- scanner = StringScanner.new(@src)
223
- while ! scanner.eos?
224
- if new_line && @stag.nil? && scanner.scan(/[ \t]*<%-/)
225
- yield('<%')
226
- new_line = false
227
- next
228
- end
229
- scanner.scan(@stag ? etag_reg : stag_reg)
230
- text = scanner[1]
231
- elem = scanner[2]
232
- new_line = (elem == "\n")
233
- yield(text) unless text.empty?
234
- if elem == '-%>'
235
- yield('%>')
236
- if scanner.scan(/(\n|\z)/)
237
- yield(:cr)
238
- new_line = true
239
- end
240
- elsif elem == '<%-'
241
- yield('<%')
242
- else
243
- yield(elem) unless elem.empty?
244
- end
245
- end
246
- end
247
- end
248
- Scanner.regist_scanner(ExplicitScanner, '-', false)
249
-
250
- rescue LoadError
251
- end
252
-
253
- class Buffer
254
- def initialize(compiler)
255
- @compiler = compiler
256
- @line = []
257
- @script = ''
258
- @compiler.pre_cmd.each do |x|
259
- push(x)
260
- end
261
- end
262
- attr_reader :script
263
-
264
- def push(cmd)
265
- @line << cmd
266
- end
267
-
268
- def cr
269
- @script << (@line.join('; '))
270
- @line = []
271
- @script << "\n"
272
- end
273
-
274
- def close
275
- return unless @line
276
- @compiler.post_cmd.each do |x|
277
- push(x)
278
- end
279
- @script << (@line.join('; '))
280
- @line = nil
281
- end
282
- end
283
-
284
- class Buffer16 < Buffer
285
- def initialize(compiler)
286
- super(compiler)
287
- @script = []
288
- end
289
-
290
- def script
291
- @script.join('')
292
- end
293
- end
294
-
295
- def make_buffer
296
- if RUBY_VERSION > '1.8'
297
- Buffer.new(self)
298
- else
299
- Buffer16.new(self)
300
- end
301
- end
302
-
303
- def compile(s)
304
- out = make_buffer
305
-
306
- content = ''
307
- scanner = make_scanner(s)
308
- scanner.scan do |token|
309
- if scanner.stag.nil?
310
- case token
311
- when PercentLine
312
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
313
- content = ''
314
- out.push(token.to_s)
315
- out.cr
316
- when :cr
317
- out.cr
318
- when '<%', '<%=', '<%#'
319
- scanner.stag = token
320
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
321
- content = ''
322
- when "\n"
323
- content << "\n"
324
- out.push("#{@put_cmd} #{content.dump}")
325
- out.cr
326
- content = ''
327
- when '<%%'
328
- content << '<%'
329
- else
330
- content << token
331
- end
332
- else
333
- case token
334
- when '%>'
335
- case scanner.stag
336
- when '<%'
337
- if content[-1] == ?\n
338
- content.chop!
339
- out.push(content)
340
- out.cr
341
- else
342
- out.push(content)
343
- end
344
- when '<%='
345
- out.push("#{@put_cmd}((#{content}).to_s)")
346
- when '<%#'
347
- # out.push("# #{content.dump}")
348
- end
349
- scanner.stag = nil
350
- content = ''
351
- when '%%>'
352
- content << '%>'
353
- else
354
- content << token
355
- end
356
- end
357
- end
358
- out.push("#{@put_cmd} #{content.dump}") if content.size > 0
359
- out.close
360
- out.script
361
- end
362
-
363
- def prepare_trim_mode(mode)
364
- case mode
365
- when 1
366
- return [false, '>']
367
- when 2
368
- return [false, '<>']
369
- when 0
370
- return [false, nil]
371
- when String
372
- perc = mode.include?('%')
373
- if mode.include?('-')
374
- return [perc, '-']
375
- elsif mode.include?('<>')
376
- return [perc, '<>']
377
- elsif mode.include?('>')
378
- return [perc, '>']
379
- else
380
- [perc, nil]
381
- end
382
- else
383
- return [false, nil]
384
- end
385
- end
386
-
387
- def make_scanner(src)
388
- Scanner.make_scanner(src, @trim_mode, @percent)
389
- end
390
-
391
- def initialize(trim_mode)
392
- @percent, @trim_mode = prepare_trim_mode(trim_mode)
393
- @put_cmd = 'print'
394
- @pre_cmd = []
395
- @post_cmd = []
396
- end
397
- attr_reader :percent, :trim_mode
398
- attr_accessor :put_cmd, :pre_cmd, :post_cmd
399
- end
400
- end
401
-
402
- # ERB
403
- class ERB
404
- def initialize(str, safe_level=nil, trim_mode=nil, eoutvar='_erbout')
405
- @safe_level = safe_level
406
- compiler = ERB::Compiler.new(trim_mode)
407
- set_eoutvar(compiler, eoutvar)
408
- @src = compiler.compile(str)
409
- end
410
- attr :src
411
-
412
- def set_eoutvar(compiler, eoutvar = '_erbout')
413
- compiler.put_cmd = "#{eoutvar}.concat"
414
-
415
- cmd = []
416
- cmd.push "#{eoutvar} = ''"
417
-
418
- compiler.pre_cmd = cmd
419
-
420
- cmd = []
421
- cmd.push(eoutvar)
422
-
423
- compiler.post_cmd = cmd
424
- end
425
-
426
- def run(b=TOPLEVEL_BINDING)
427
- print self.result(b)
428
- end
429
-
430
- def result(b=TOPLEVEL_BINDING)
431
- if @safe_level
432
- th = Thread.start {
433
- $SAFE = @safe_level
434
- eval(@src, b)
435
- }
436
- return th.value
437
- else
438
- return eval(@src, b)
439
- end
440
- end
441
-
442
- def def_method(mod, methodname, fname='(ERB)')
443
- mod.module_eval("def #{methodname}\n" + self.src + "\nend\n", fname, 0)
444
- end
445
-
446
- def def_module(methodname='erb')
447
- mod = Module.new
448
- def_method(mod, methodname)
449
- mod
450
- end
451
-
452
- def def_class(superklass=Object, methodname='result')
453
- cls = Class.new(superklass)
454
- def_method(cls, methodname)
455
- cls
456
- end
457
- end
458
-
459
- # ERB::Util
460
- class ERB
461
- module Util
462
- public
463
- def html_escape(s)
464
- s.to_s.gsub(/&/, "&amp;").gsub(/\"/, "&quot;").gsub(/>/, "&gt;").gsub(/</, "&lt;")
465
- end
466
- alias h html_escape
467
-
468
- def url_encode(s)
469
- s.to_s.gsub(/[^a-zA-Z0-9_\-.]/n){ sprintf("%%%02X", $&.unpack("C")[0]) }
470
- end
471
- alias u url_encode
472
- end
473
- end
474
-
475
- # ERB::DefMethod
476
- class ERB
477
- module DefMethod
478
- public
479
- def def_erb_method(methodname, erb)
480
- if erb.kind_of? String
481
- fname = erb
482
- File.open(fname) {|f| erb = ERB.new(f.read) }
483
- erb.def_method(self, methodname, fname)
484
- else
485
- erb.def_method(self, methodname)
486
- end
487
- end
488
- module_function :def_erb_method
489
- end
490
- end