patricia 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +22 -0
- data/README.md +77 -0
- data/Rakefile +2 -0
- data/bin/patricia +48 -0
- data/lib/patricia/app.rb +258 -0
- data/lib/patricia/assets/javascripts/app.js +170 -0
- data/lib/patricia/assets/javascripts/bootstrap.min.js +7 -0
- data/lib/patricia/assets/javascripts/jquery-1.11.0.min.js +4 -0
- data/lib/patricia/assets/javascripts/tooltips.js +10 -0
- data/lib/patricia/assets/stylesheets/app.css +22 -0
- data/lib/patricia/assets/stylesheets/bootstrap.min.css +7 -0
- data/lib/patricia/cli.rb +66 -0
- data/lib/patricia/patricia.rb +248 -0
- data/lib/patricia/version.rb +3 -0
- data/lib/patricia/views/404.haml +12 -0
- data/lib/patricia/views/application.haml +18 -0
- data/lib/patricia/views/search.haml +62 -0
- data/lib/patricia/views/wiki/page.haml +31 -0
- data/lib/patricia/views/wiki/welcome.md +101 -0
- data/lib/patricia.rb +3 -0
- data/patricia/.gitignore +22 -0
- data/patricia.gemspec +25 -0
- data/spec/app_spec.rb +195 -0
- data/spec/assets/javascripts/one.js +1 -0
- data/spec/assets/javascripts/two.js +1 -0
- data/spec/assets/stylesheets/green.css +3 -0
- data/spec/assets/stylesheets/red.css +3 -0
- data/spec/patricia_wiki_spec.rb +123 -0
- data/spec/random-test-wiki/amazing-animals/index.md +3 -0
- data/spec/random-test-wiki/amazing-animals/small/hamster.md +7 -0
- data/spec/random-test-wiki/amazing-animals/small/mouse.md +13 -0
- data/spec/random-test-wiki/amazing-animals/small/rat.md +7 -0
- data/spec/random-test-wiki/amazing-animals/tall/elephant.md +13 -0
- data/spec/random-test-wiki/amazing-animals/tall/giraffe.md +7 -0
- data/spec/random-test-wiki/colors/blue.md +9 -0
- data/spec/random-test-wiki/colors/bright-orange.org +19 -0
- data/spec/random-test-wiki/colors/dark-yellow.textile +14 -0
- data/spec/random-test-wiki/colors/file.txt +3 -0
- data/spec/random-test-wiki/colors/green.markdown +3 -0
- data/spec/random-test-wiki/colors/image.png +0 -0
- data/spec/random-test-wiki/colors/light-pink.rst +74 -0
- data/spec/random-test-wiki/colors/red.md +7 -0
- data/spec/random-test-wiki/overview.md +15 -0
- data/spec/test_helpers.rb +11 -0
- metadata +143 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ef479d8eff067d6a70b52846c246763987cb72e8
|
4
|
+
data.tar.gz: 538feff310e1df73d5f52c79d6da00e054bd268a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e22e0b7aa71306f9e4015a98acedc315adf3bf21a38141b1f0f073918c0db23aa202e5fc43fcb38e0731e79755068a5fc86a44d290bc521c5d15bafa74fb952e
|
7
|
+
data.tar.gz: d704365bc2c6e17f7966870f16f07905232899e4c185f4eeca0c7bfe623a49db1ba187fbbff1c4dd51960ff63261fc6dbcc4218ff46f11b9758419c0c248ad89
|
data/.gitignore
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
23
|
+
bin/app_config.yml
|
24
|
+
output
|
25
|
+
spec/output
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 nounch
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# Patricia
|
2
|
+
|
3
|
+
Patricia is a simple markup-based Wiki. You give it a directory full of
|
4
|
+
markup files and it either
|
5
|
+
|
6
|
+
- serves the markup files as dynamic HTML pages with a hierarchical list of
|
7
|
+
all pages in a handy sidebar (including a search box) or
|
8
|
+
- generates a directory full of static HTML pages that you can serve
|
9
|
+
yourself.
|
10
|
+
|
11
|
+
The first option is great for writing/previewing your markup files. You
|
12
|
+
simply refresh the page and Patricia picks up the current state of the
|
13
|
+
file. However, it is also perfectly fine to use this built-in server to
|
14
|
+
serve your Wiki as a real website.
|
15
|
+
|
16
|
+
Files of different supported markup languages can be wildly mixed in the
|
17
|
+
markup directory, Patricia is able to figure it out as long as you use the
|
18
|
+
right file extension. Static files (images/PDFs/video/...) in the markup
|
19
|
+
directory are served as is, so you can link to them from any page and the
|
20
|
+
links will not break.
|
21
|
+
|
22
|
+
## Installation
|
23
|
+
|
24
|
+
gem install patricia
|
25
|
+
|
26
|
+
## Quick Start
|
27
|
+
|
28
|
+
Run
|
29
|
+
|
30
|
+
patricia /path/to/markup/dir -p 4321
|
31
|
+
|
32
|
+
and point your browser to `http://localhost:4321/`. Patricia will tell you
|
33
|
+
what to do from that point on.
|
34
|
+
|
35
|
+
## Usage
|
36
|
+
|
37
|
+
To serve a directory full of markup files (`dir`), run this:
|
38
|
+
|
39
|
+
patricia /path/to/dir
|
40
|
+
|
41
|
+
To generate static HTML files in an output directory (`outputdir`), run
|
42
|
+
this:
|
43
|
+
|
44
|
+
patricia /path/to/dir /path/to/outputdir
|
45
|
+
|
46
|
+
To use your own stylesheets use the `--css` command line switch, to include
|
47
|
+
your own JavaScript, use `--js`. If you want to use a custom port, the
|
48
|
+
`--port` comes in handy.
|
49
|
+
|
50
|
+
Run `patricia --help` to see a list of all options.
|
51
|
+
|
52
|
+
## Markup Languages
|
53
|
+
|
54
|
+
Patricia supports the following markup languages (supported file
|
55
|
+
extensions in parentheses):
|
56
|
+
|
57
|
+
- Markdown (`.md`, `.markdown`) via `kramdown`
|
58
|
+
- Org (`.org`) via `org-ruby`
|
59
|
+
- Textile (`.textile`) via `redcloth`
|
60
|
+
- reStructuredText (`.rst`, `.rest`, `restx`) via `pandoc-ruby`
|
61
|
+
|
62
|
+
To add support for a new language do this:
|
63
|
+
|
64
|
+
0. *Write tests*
|
65
|
+
1. Add a method with the markup language name to `Patricia::Converter`
|
66
|
+
(This function takes a markup string as input and returns a HTML string
|
67
|
+
as output)
|
68
|
+
2. Associate this new method with the desired file extensions for the new
|
69
|
+
markup language by adding mappings to the
|
70
|
+
`Patricia::Converter.converters` hash.
|
71
|
+
3. Add those file extensions to `PatriciaApp::App`'s
|
72
|
+
`settings.app_markdown_glob` in its `configure` block.
|
73
|
+
4. Add the file extensions to the `settings.app_content_types` hash in
|
74
|
+
`PatriciaApp::App` and map them to the `text/plain` content type, if
|
75
|
+
possible, so that browsers will display them on a page instead of
|
76
|
+
prompting to download the file.
|
77
|
+
5. *Make sure the tests pass*
|
data/Rakefile
ADDED
data/bin/patricia
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
require 'rack'
|
5
|
+
require 'yaml'
|
6
|
+
require_relative '../lib/patricia'
|
7
|
+
|
8
|
+
|
9
|
+
config = {
|
10
|
+
:css_dir => CLI.options[:css],
|
11
|
+
:js_dir => CLI.options[:js],
|
12
|
+
:markup_dir => CLI.options[:markup_dir],
|
13
|
+
:tooltips => CLI.options[:tooltips],
|
14
|
+
}
|
15
|
+
config_file = File.dirname(__FILE__) + '/app_config.yml'
|
16
|
+
# The thread ensures that the config file is written before the Sinatra app
|
17
|
+
# starts.
|
18
|
+
config_writer = Thread.new do
|
19
|
+
begin
|
20
|
+
File.delete(config_file)
|
21
|
+
rescue
|
22
|
+
# Ignore.
|
23
|
+
end
|
24
|
+
File.open(config_file, 'w') do |f|
|
25
|
+
f.write(config.to_yaml)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Wait for the config file to be written.
|
30
|
+
config_writer.join
|
31
|
+
|
32
|
+
if CLI.options[:output_dir]
|
33
|
+
css_dir = CLI.options[:css]
|
34
|
+
js_dir = CLI.options[:js]
|
35
|
+
css = Dir[css_dir + '/**/*.css'].collect { |x| x.sub(/#{css_dir}/, '') }
|
36
|
+
js = Dir[js_dir + '/**/*.js'].collect { |x| x.sub(/#{js_dir}/, '') }
|
37
|
+
patricia = Patricia::Wiki.new(CLI.options[:markup_dir], :output_dir =>
|
38
|
+
CLI.options[:output_dir], :css =>
|
39
|
+
css, :js => js)
|
40
|
+
patricia.render
|
41
|
+
else
|
42
|
+
require_relative '../lib/patricia/app'
|
43
|
+
app = Rack::Builder.new do
|
44
|
+
use PatriciaApp::App
|
45
|
+
run Proc.new { |env| [404, {'Content-Type' => 'text/html'}, ['404']] }
|
46
|
+
end
|
47
|
+
Rack::Handler::WEBrick.run app, :Port => CLI.options[:port] || 4321
|
48
|
+
end
|
data/lib/patricia/app.rb
ADDED
@@ -0,0 +1,258 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
|
5
|
+
module PatriciaApp
|
6
|
+
class App < Sinatra::Base
|
7
|
+
|
8
|
+
def self.generate_routes
|
9
|
+
# CSS
|
10
|
+
settings.app_css.each do |dir|
|
11
|
+
get dir do
|
12
|
+
content_type 'text/css'
|
13
|
+
File.new(File.join(settings.app_css_dir, dir)).readlines
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# JS
|
18
|
+
settings.app_js.each do |dir|
|
19
|
+
get dir do
|
20
|
+
content_type 'text/js'
|
21
|
+
File.new(File.join(settings.app_js_dir, dir)).readlines
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
configure do
|
27
|
+
begin
|
28
|
+
config_file = File.expand_path('../../../bin/app_config.yml',
|
29
|
+
__FILE__)
|
30
|
+
app_configs = YAML.load_file(config_file)
|
31
|
+
markup_dir = app_configs[:markup_dir]
|
32
|
+
set :environment, 'production'
|
33
|
+
set :app_markup_dir, markup_dir
|
34
|
+
set :app_markdown_glob, '.{md,markdown,org,textile,rst,rest,restx}'
|
35
|
+
set :app_css_path, '/patricia.css'
|
36
|
+
set :app_js_path, '/patricia.js'
|
37
|
+
set :app_public_folder, app_configs[:public_folder]
|
38
|
+
set :app_tooltips, app_configs[:tooltips]
|
39
|
+
# CSS
|
40
|
+
if app_configs[:css_dir] != nil
|
41
|
+
set :app_css_dir, app_configs[:css_dir]
|
42
|
+
css_paths = Dir[app_configs[:css_dir] + '/**/*.css']
|
43
|
+
.collect do |path|
|
44
|
+
path.gsub(/#{app_css_dir}/, '')
|
45
|
+
end
|
46
|
+
set :app_css, css_paths
|
47
|
+
else
|
48
|
+
set :app_css_dir, ''
|
49
|
+
set :app_css, []
|
50
|
+
end
|
51
|
+
# JS
|
52
|
+
if app_configs[:js_dir] != nil
|
53
|
+
set :app_js_dir, app_configs[:js_dir]
|
54
|
+
js_paths = Dir[app_configs[:js_dir] + '/**/*.js']
|
55
|
+
.collect do |path|
|
56
|
+
path.gsub(/#{app_js_dir}/, '')
|
57
|
+
end
|
58
|
+
set :app_js, js_paths
|
59
|
+
else
|
60
|
+
set :app_js_dir, ''
|
61
|
+
set :app_js, []
|
62
|
+
end
|
63
|
+
# Content types
|
64
|
+
set :app_content_types, {
|
65
|
+
'png' => 'image/png',
|
66
|
+
'jpg' => 'image/jpeg',
|
67
|
+
'jpeg' => 'image/jpeg',
|
68
|
+
'gif' => 'image/gif',
|
69
|
+
'bmp' => 'image/bmp',
|
70
|
+
'torrent' => 'image/x-bittorrent',
|
71
|
+
'svg' => 'image/svg+xml',
|
72
|
+
'xml' => 'application/xml',
|
73
|
+
'atom' => 'application/atom-xml',
|
74
|
+
'zip' => 'application/zip',
|
75
|
+
'bz' => 'application/x-bzip',
|
76
|
+
'bz2' => 'application/x-bzip2',
|
77
|
+
'gzip' => 'application/gzip',
|
78
|
+
'pdf' => 'application/pdf',
|
79
|
+
'sh' => 'application/x-sh',
|
80
|
+
'7z' => 'application/x-7z-compressed',
|
81
|
+
'swf' => 'application/x-shockwave-flash',
|
82
|
+
'avi' => 'video/x-msvideo',
|
83
|
+
'txt' => 'text/plain',
|
84
|
+
'css' => 'text/css',
|
85
|
+
'csv' => 'text/csv',
|
86
|
+
# Send markup source files
|
87
|
+
#
|
88
|
+
# One can argue if this is semantically correct (alternative:
|
89
|
+
# `plain/x-markdown;charset=UTF-8'), but as there is no official
|
90
|
+
# final specification and this solution comes with the highest
|
91
|
+
# browser-compatibility, it is preferred.
|
92
|
+
'md' => 'text/plain',
|
93
|
+
'markdown' => 'text/plain',
|
94
|
+
'org' => 'text/plain',
|
95
|
+
'textile' => 'text/plain',
|
96
|
+
'rst' => 'text/plain',
|
97
|
+
'rest' => 'text/plain',
|
98
|
+
'rstx' => 'text/plain',
|
99
|
+
}
|
100
|
+
self.generate_routes
|
101
|
+
rescue
|
102
|
+
# Ignore.
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
helpers do
|
107
|
+
def build_toc
|
108
|
+
Patricia::Wiki.new(settings.app_markup_dir).build_toc
|
109
|
+
end
|
110
|
+
|
111
|
+
def generate_page_title
|
112
|
+
capitalize_all(File.basename(settings.app_markup_dir).gsub(/-/,
|
113
|
+
' '))
|
114
|
+
end
|
115
|
+
|
116
|
+
def capitalize_all(string)
|
117
|
+
string.split.inject([]) do |result, token|
|
118
|
+
result << token[0].capitalize + token[1..-1]
|
119
|
+
end.join(' ')
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
get '/' do
|
124
|
+
@html = Patricia::Converter .to_html(File.dirname(__FILE__) +
|
125
|
+
'/views/wiki/welcome.md')
|
126
|
+
@toc = build_toc
|
127
|
+
@title = generate_page_title
|
128
|
+
@page_title = ''
|
129
|
+
@breadcrumb = ''
|
130
|
+
@stylesheets = settings.app_css
|
131
|
+
@javascripts = settings.app_js
|
132
|
+
haml 'wiki/page'.to_sym, :layout => :application
|
133
|
+
end
|
134
|
+
|
135
|
+
get %r{/404/?} do
|
136
|
+
haml '404'.to_sym, :layout => :application
|
137
|
+
end
|
138
|
+
|
139
|
+
get %r{/patricia/search/?} do
|
140
|
+
haml :search, :layout => :application
|
141
|
+
end
|
142
|
+
|
143
|
+
post %r{/patricia/search/?} do
|
144
|
+
@previous_search_query = params[:search_query] || ''
|
145
|
+
if params[:case_sensitive]
|
146
|
+
search_query = %r{#{params[:search_query]}}
|
147
|
+
@previous_search_query_was_sensitive = true
|
148
|
+
else
|
149
|
+
search_query = %r{#{params[:search_query]}}i
|
150
|
+
@previous_search_query_was_sensitive = false
|
151
|
+
end
|
152
|
+
# Search all markup files
|
153
|
+
@results = []
|
154
|
+
paths = Dir[File.join(settings.app_markup_dir, '/**/*' +
|
155
|
+
settings.app_markdown_glob)]
|
156
|
+
paths.each do |path|
|
157
|
+
if File.read(path)
|
158
|
+
p = path.gsub(/#{settings.app_markup_dir}/, '')
|
159
|
+
file_name = File.basename(p, '.*')
|
160
|
+
no_ext = File.join(File.dirname(p), file_name).sub(/^\.\//, '')
|
161
|
+
beautiful_file_name = capitalize_all(file_name.gsub(/-/, ' '))
|
162
|
+
beautiful_path = no_ext.split('/').collect do |s|
|
163
|
+
capitalize_all(s.gsub(/-/, ' '))
|
164
|
+
end.join(' > ')
|
165
|
+
content = File.read(path)
|
166
|
+
lines = content.split("\n").length
|
167
|
+
if content =~ search_query
|
168
|
+
@results << [beautiful_file_name, '/' + no_ext, beautiful_path,
|
169
|
+
lines]
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
haml :search, :layout => :application
|
174
|
+
end
|
175
|
+
|
176
|
+
get settings.app_css_path do
|
177
|
+
pwd = File.dirname(__FILE__)
|
178
|
+
css = ''
|
179
|
+
[
|
180
|
+
'/assets/stylesheets/bootstrap.min.css',
|
181
|
+
'/assets/stylesheets/app.css',
|
182
|
+
].each do |path|
|
183
|
+
css << File.read(pwd + path) + "\n\n"
|
184
|
+
end
|
185
|
+
content_type 'text/css'
|
186
|
+
css
|
187
|
+
end
|
188
|
+
|
189
|
+
get settings.app_js_path do
|
190
|
+
pwd = File.dirname(__FILE__)
|
191
|
+
js = ''
|
192
|
+
files =
|
193
|
+
[
|
194
|
+
'/assets/javascripts/jquery-1.11.0.min.js',
|
195
|
+
'/assets/javascripts/bootstrap.min.js',
|
196
|
+
'/assets/javascripts/app.js',
|
197
|
+
]
|
198
|
+
files << '/assets/javascripts/tooltips.js' if settings.app_tooltips
|
199
|
+
files.each do |path|
|
200
|
+
js << File.read(pwd + path) + "\n\n"
|
201
|
+
end
|
202
|
+
content_type 'text/javascript'
|
203
|
+
js
|
204
|
+
end
|
205
|
+
|
206
|
+
get %r{/(.*)} do |path|
|
207
|
+
# This assumes that there are not two or more files with the same
|
208
|
+
# file name, but different extensions (e.g. `/smart/green/frog.md'
|
209
|
+
# and `/smart/green/frog.markdown'). If so, it will chose the one it
|
210
|
+
# finds first.
|
211
|
+
# If this becomes a problem in the future, one could send a
|
212
|
+
# disambiguation page with links to each of the files for this
|
213
|
+
# request.
|
214
|
+
begin
|
215
|
+
file_path = Dir[settings.app_markup_dir + '/' + path +
|
216
|
+
settings.app_markdown_glob].first
|
217
|
+
@markup_url = file_path.gsub(/#{settings.app_markup_dir}/, '')
|
218
|
+
@html = Patricia::Converter.to_html(file_path)
|
219
|
+
@toc = build_toc
|
220
|
+
arrow = ' > '
|
221
|
+
@title = generate_page_title
|
222
|
+
@breadcrumb = capitalize_all(File.dirname(path).gsub(/\//, ' '))
|
223
|
+
breadcrumb_array = @breadcrumb.split
|
224
|
+
if breadcrumb_array.length >= 1 && breadcrumb_array.first !~ /\./
|
225
|
+
@breadcrumb = breadcrumb_array.inject([]) do |result, token|
|
226
|
+
result << capitalize_all(token.gsub(/-/, ' '))
|
227
|
+
end.join(arrow) + arrow
|
228
|
+
else
|
229
|
+
@breadcrumb = ''
|
230
|
+
end
|
231
|
+
@stylesheets = settings.app_css
|
232
|
+
@javascripts = settings.app_js
|
233
|
+
@page_title = capitalize_all(File.basename(path).gsub(/-/, ' '))
|
234
|
+
.split.join(' ')
|
235
|
+
haml 'wiki/page'.to_sym, :layout => :application
|
236
|
+
rescue
|
237
|
+
file_path = File.join(settings.app_markup_dir, path)
|
238
|
+
if File.exists?(file_path)
|
239
|
+
ext = File.extname(path).gsub(/\./, '')
|
240
|
+
# Try to guess the correct content type for popular content
|
241
|
+
# types.
|
242
|
+
content_type settings.app_content_types[ext] ||
|
243
|
+
'application/octet-stream'
|
244
|
+
send_file(file_path)
|
245
|
+
else
|
246
|
+
redirect to('/404')
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
# `patricia.rb' already defines this, but redefining it here decouples
|
252
|
+
# the web app from the static file generator.
|
253
|
+
def _without_extension(path)
|
254
|
+
path.sub(/\..*$/, '')
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
$(document).ready(function() {
|
2
|
+
|
3
|
+
// Helpers
|
4
|
+
|
5
|
+
$.fn.elementText = function() {
|
6
|
+
var str = '';
|
7
|
+
$(this).contents().each(function() {
|
8
|
+
if ($(this).nodeType == 3) {
|
9
|
+
str += $(this).textContent || $(this).innerText || '';
|
10
|
+
}
|
11
|
+
});
|
12
|
+
return str;
|
13
|
+
};
|
14
|
+
|
15
|
+
|
16
|
+
// Sidebar expanding
|
17
|
+
|
18
|
+
var self = $(this);
|
19
|
+
self.sidebarExpanded = false;
|
20
|
+
self.originalSidebarStyle = {
|
21
|
+
'width': '',
|
22
|
+
'position': '',
|
23
|
+
'box-shadow': '',
|
24
|
+
};
|
25
|
+
self.originalSidebarLeftMargin = parseInt($('#p-sidebar').css(
|
26
|
+
'margin-left'));
|
27
|
+
self.sidebarLeftMarginAnimationWidth = 10;
|
28
|
+
self.sidebarExpandedStyle = {
|
29
|
+
'width': '80%',
|
30
|
+
'right': '20px',
|
31
|
+
'position': 'absolute',
|
32
|
+
'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.3)',
|
33
|
+
};
|
34
|
+
self.sidebarWidthToggleOriginalText =
|
35
|
+
$('#p-sidebar-width-toggle').text();
|
36
|
+
self.sidebarWidthToggleExpandedText = 'Narrow sidebar';
|
37
|
+
self.previousScrollPosition = null;
|
38
|
+
|
39
|
+
self.toggleSidebarWidth = function() {
|
40
|
+
if (self.sidebarExpanded) {
|
41
|
+
self.sidebarExpanded = false;
|
42
|
+
$('#p-sidebar-width-toggle')
|
43
|
+
.text(self.sidebarWidthToggleOriginalText);
|
44
|
+
$('#p-sidebar').css(self.originalSidebarStyle);
|
45
|
+
// Scroll to the previous scroll position.
|
46
|
+
$('html').scrollTop(self.previousScrollPosition);
|
47
|
+
} else {
|
48
|
+
self.sidebarExpanded = true;
|
49
|
+
$('#p-sidebar-width-toggle')
|
50
|
+
.text(self.sidebarWidthToggleExpandedText);
|
51
|
+
$('#p-sidebar').css(self.sidebarExpandedStyle);
|
52
|
+
// Scroll to thetop of the page.
|
53
|
+
self.previousScrollPosition = $('html').scrollTop();
|
54
|
+
$('html').scrollTop(0);
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
$('#p-sidebar-width-toggle').click(function(e) {
|
59
|
+
e.preventDefault();
|
60
|
+
self.toggleSidebarWidth();
|
61
|
+
});
|
62
|
+
|
63
|
+
// Collapse the sidebar when clicking on a link while the sidebar is
|
64
|
+
// expanded. So when hitting the back button the sidebar will be
|
65
|
+
// collapsed. This is moreintuitive.
|
66
|
+
$('#toc a').click(function() {
|
67
|
+
if (self.sidebarExpanded) {
|
68
|
+
self.toggleSidebarWidth();
|
69
|
+
}
|
70
|
+
});
|
71
|
+
|
72
|
+
// Keybinding
|
73
|
+
$('body').append(
|
74
|
+
'\
|
75
|
+
<div id="help-box">\
|
76
|
+
<p class="text-right"><a class="btn btn-default btn-xs help-box-toggle"\
|
77
|
+
href="/">Close</a></p>\
|
78
|
+
<h2 class="text-center">Key bindings</h2>\
|
79
|
+
<br/>\
|
80
|
+
<p><code>?</code><span>: Toggle key bindings help</span></p>\
|
81
|
+
<p><code>w</code><span>: Toggle sidebar width</span></p>\
|
82
|
+
<p><code>s</code><span>: Select sidebar search box</span></p>\
|
83
|
+
<p><code>Esc</code><span>: Unselect sidebar search box</span></p>\
|
84
|
+
<p><code>p</code><span>: Go to page search page</span></p>\
|
85
|
+
</div>\
|
86
|
+
'
|
87
|
+
);
|
88
|
+
self.helpBox = $('#help-box');
|
89
|
+
self.helpBox.hide();
|
90
|
+
self.helpBox.css({
|
91
|
+
'position': 'fixed',
|
92
|
+
'background-color': '#FAFAFA',
|
93
|
+
'color': '#686868',
|
94
|
+
'top': '50%',
|
95
|
+
'left': '50%',
|
96
|
+
'width': '500px',
|
97
|
+
'height': '400px',
|
98
|
+
'margin-left': '-200px',
|
99
|
+
'margin-top': '-300px',
|
100
|
+
'z-index': '9999',
|
101
|
+
'padding': '10px',
|
102
|
+
'box-shadow': '0 0 0 9999px rgba(0, 0, 0, 0.2)',
|
103
|
+
'border': '1px solid #ACACAC',
|
104
|
+
});
|
105
|
+
$('#p-sidebar-search-box').parent().find('a:first').parent().prepend('\
|
106
|
+
<a href="/" class="text-muted btn-xs help-box-toggle">Key bindings\
|
107
|
+
</a>');
|
108
|
+
$('.help-box-toggle').click(function(e) {
|
109
|
+
e.preventDefault();
|
110
|
+
self.helpBox.toggle();
|
111
|
+
})
|
112
|
+
$(document).keypress(function(e) {
|
113
|
+
// 119: w - Toggle sidebar width
|
114
|
+
// 115: s - Select sidebar search box
|
115
|
+
// 0: Esc - Unselect sidebar search box
|
116
|
+
// 112: s - Go to page search page
|
117
|
+
// 63: ? - Toggle key bindings help
|
118
|
+
if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA') {
|
119
|
+
if (e.which == 119) {
|
120
|
+
self.toggleSidebarWidth();
|
121
|
+
} else if (e.which == 115) {
|
122
|
+
$('#p-sidebar-search-box').select();
|
123
|
+
} else if (e.which == 112) {
|
124
|
+
window.location = $('#p-page-search-link').attr('href');
|
125
|
+
} else if (e.which == 63) {
|
126
|
+
$('#help-box').toggle();
|
127
|
+
}
|
128
|
+
} else if (e.target.id == 'sidebar-search-box') {
|
129
|
+
if (e.which == 0) {
|
130
|
+
$('#p-sidebar-search-box').blur();
|
131
|
+
}
|
132
|
+
}
|
133
|
+
});
|
134
|
+
|
135
|
+
|
136
|
+
// Skip `Key bindings' and `Widen sidebar' links when navigation using
|
137
|
+
// TAB.
|
138
|
+
$('#p-sidebar-search-box').parent().find('a').slice(0,2)
|
139
|
+
.each(function(index) {
|
140
|
+
$(this).attr('tabindex', '9999');
|
141
|
+
// Add tooltips
|
142
|
+
var title = '';
|
143
|
+
if (index == 0) {
|
144
|
+
title = 'Shortcut: "?"';
|
145
|
+
} else if (index == 1) {
|
146
|
+
title = 'Shortcut: "w"';
|
147
|
+
}
|
148
|
+
$(this).attr('title', title);
|
149
|
+
$(this).attr('data-toggle', 'tooltip');
|
150
|
+
$(this).attr('data-placement', 'top');
|
151
|
+
});
|
152
|
+
|
153
|
+
|
154
|
+
// Search
|
155
|
+
|
156
|
+
$('#p-sidebar-search-box').keyup(function() {
|
157
|
+
var filter = $(this).val().toLowerCase();
|
158
|
+
|
159
|
+
$('#p-sidebar li').each(function() {
|
160
|
+
var text = $(this).text().toLowerCase();
|
161
|
+
|
162
|
+
if (text.match(filter)) {
|
163
|
+
$(this).show()
|
164
|
+
} else {
|
165
|
+
$(this).hide();
|
166
|
+
}
|
167
|
+
});
|
168
|
+
});
|
169
|
+
|
170
|
+
});
|