mamemose 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +216 -0
- data/Rakefile +2 -0
- data/bin/mamemose +7 -0
- data/lib/mamemose/version.rb +3 -0
- data/lib/mamemose.rb +349 -0
- data/mamemose.gemspec +21 -0
- data/sample.png +0 -0
- metadata +104 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/
|
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 daimatz
|
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,216 @@
|
|
1
|
+
mamemose: Markdown memo server
|
2
|
+
====
|
3
|
+
|
4
|
+
概要
|
5
|
+
----
|
6
|
+
|
7
|
+
理想の Markdown メモツールを探したがなかったので自分で作った。要件としては
|
8
|
+
|
9
|
+
- Markdown など軽量マークアップ言語で書かれたドキュメントを
|
10
|
+
コマンド操作無しで勝手に HTML に変換してくれる
|
11
|
+
- Sphinx などをそのまま使うのはコマンドを打つ手間があるので嫌だった
|
12
|
+
- 複数マシンで内容を共有できる (Dropbox 等を使ってもよい)
|
13
|
+
- 使い慣れたエディタを使える
|
14
|
+
- 検索できる
|
15
|
+
- できれば GitHub Flavored Markdown でシンタックスハイライトもしてほしい
|
16
|
+
- できれば LaTeX で数式も書きたい
|
17
|
+
|
18
|
+
環境
|
19
|
+
----
|
20
|
+
|
21
|
+
- おもに Mac OS X 10.8 上の Ruby 1.9.3 でテスト
|
22
|
+
- Ruby 1.8.7 でも動くようにした
|
23
|
+
- Linux はおそらく大丈夫
|
24
|
+
- Windows も、こないだ Cygwin 上で試したら動いたのでたぶん動くと思う
|
25
|
+
|
26
|
+
インストール方法
|
27
|
+
----
|
28
|
+
|
29
|
+
```bash
|
30
|
+
$ gem install mamemose
|
31
|
+
```
|
32
|
+
|
33
|
+
使い方
|
34
|
+
----
|
35
|
+
|
36
|
+
設定ファイル (後述) を書いたら以下で起動
|
37
|
+
|
38
|
+
```bash
|
39
|
+
$ mamemose &> /dev/null &
|
40
|
+
```
|
41
|
+
|
42
|
+
ブラウザから
|
43
|
+
|
44
|
+
```
|
45
|
+
http://localhost:PORT/
|
46
|
+
```
|
47
|
+
|
48
|
+
にアクセスすればおk
|
49
|
+
|
50
|
+
`DOCUMENT_ROOT` 以下の Markdown で書かれたテキストを勝手にHTMLに変換して表示します。
|
51
|
+
一覧ページでは Markdown ドキュメントの1行目をタイトルとして読み込みます。
|
52
|
+
|
53
|
+
`DOCUMENT_ROOT` を Dropbox 以下のディレクトリに指定しておけば、どのマシンからでも
|
54
|
+
メモにアクセスできるようになります。
|
55
|
+
|
56
|
+
### シンタックスハイライト
|
57
|
+
|
58
|
+
[コード部分に GitHub Flavored Markdown の記法](http://github.github.com/github-flavored-markdown/)
|
59
|
+
を使い、シンタックスハイライトに
|
60
|
+
[SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/)
|
61
|
+
を使うことができます。設定例を参照。
|
62
|
+
|
63
|
+
### 数式
|
64
|
+
|
65
|
+
[MathJax](http://www.mathjax.org/) を使うと数式も書けます。設定例を参照。
|
66
|
+
|
67
|
+
設定
|
68
|
+
----
|
69
|
+
|
70
|
+
ホームディレクトリに `.mamemose.rb` という設定ファイルを置くとそれを読みます。
|
71
|
+
設定項目は以下の通り
|
72
|
+
|
73
|
+
- `DOCUMENT_ROOT`
|
74
|
+
- ドキュメントルート
|
75
|
+
- `PORT`
|
76
|
+
- ポート。 http://localhost:PORT/ にアクセス
|
77
|
+
- `MARKDOWN_PATTERN`
|
78
|
+
- Markdown ドキュメントと見なすファイルパターンを正規表現で
|
79
|
+
- `IGNORE_FILES`
|
80
|
+
- 無視するファイル・ディレクトリのリスト。
|
81
|
+
文字列の場合はそのものを、正規表現の場合はそれにマッチするものを無視する
|
82
|
+
- `RECENT_NUM`
|
83
|
+
- 「最近更新したファイル」を表示する数
|
84
|
+
- `RECENT_PATTERN`
|
85
|
+
- 「最近更新したファイル」に表示するファイルパターンを正規表現で
|
86
|
+
- `CUSTOM_HEADER`
|
87
|
+
- カスタムヘッダ。 `head` タグの最後に入る
|
88
|
+
- `CUSTOM_BODY`
|
89
|
+
- カスタムボディ。 `body` タグの最初に入る
|
90
|
+
- `CUSTOM_FOOTER`
|
91
|
+
- カスタムフッタ。 `body` タグの最後に入る
|
92
|
+
|
93
|
+
設定されなかったらデフォルト値を使います。
|
94
|
+
|
95
|
+
### 設定ファイル例
|
96
|
+
|
97
|
+
`~/.mamemose.rb`
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
DOCUMENT_ROOT = "~/memo"
|
101
|
+
|
102
|
+
PORT = 8888
|
103
|
+
|
104
|
+
# 通常の Markdown ファイルに加えて .txt ファイルも Markdown と見なす
|
105
|
+
MARKDOWN_PATTERN = /\.(md|markdown|txt)$/
|
106
|
+
|
107
|
+
# 最近更新したファイル一覧がジャマ
|
108
|
+
RECENT_NUM = 0
|
109
|
+
|
110
|
+
# 最近更新したファイル一覧に出すものを Markdown ドキュメントだけにする
|
111
|
+
# RECENT_PATTERN = MARKDOWN_PATTERN
|
112
|
+
|
113
|
+
# すべてのページで MathJax が使えるように
|
114
|
+
CUSTOM_HEADER = <<HEADER
|
115
|
+
<script type="text/javascript"
|
116
|
+
src="http://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS_HTML">
|
117
|
+
</script>
|
118
|
+
HEADER
|
119
|
+
|
120
|
+
# すべてのページで SyntaxHighlighter が使えるように
|
121
|
+
host = "http://alexgorbatchev.com/pub/sh/current" # 変数も使えます
|
122
|
+
CUSTOM_FOOTER = <<FOOTER
|
123
|
+
<link href="#{host}/styles/shCoreDefault.css" rel="stylesheet" type="text/css" />
|
124
|
+
<script src="#{host}/scripts/shCore.js" type="text/javascript"></script>
|
125
|
+
<script src="#{host}/scripts/shAutoloader.js" type="text/javascript"></script>
|
126
|
+
<script type="text/javascript">
|
127
|
+
SyntaxHighlighter.autoloader(
|
128
|
+
'AS3 as3 #{host}/scripts/shBrushAS3.js',
|
129
|
+
'AppleScript applescript #{host}/scripts/shBrushAppleScript.js',
|
130
|
+
'Bash bash #{host}/scripts/shBrushBash.js',
|
131
|
+
'CSharp csharp #{host}/scripts/shBrushCSharp.js',
|
132
|
+
'ColdFusion coldfusion #{host}/scripts/shBrushColdFusion.js',
|
133
|
+
'Cpp cpp #{host}/scripts/shBrushCpp.js',
|
134
|
+
'Css css #{host}/scripts/shBrushCss.js',
|
135
|
+
'Delphi delphi #{host}/scripts/shBrushDelphi.js',
|
136
|
+
'Diff diff #{host}/scripts/shBrushDiff.js',
|
137
|
+
'Erlang erlang #{host}/scripts/shBrushErlang.js',
|
138
|
+
'Groovy groovy #{host}/scripts/shBrushGroovy.js',
|
139
|
+
'JScript jscript #{host}/scripts/shBrushJScript.js',
|
140
|
+
'Java java #{host}/scripts/shBrushJava.js',
|
141
|
+
'JavaFX javafx #{host}/scripts/shBrushJavaFX.js',
|
142
|
+
'Perl perl #{host}/scripts/shBrushPerl.js',
|
143
|
+
'Php php #{host}/scripts/shBrushPhp.js',
|
144
|
+
'Plain plain #{host}/scripts/shBrushPlain.js',
|
145
|
+
'PowerShell powershell #{host}/scripts/shBrushPowerShell.js',
|
146
|
+
'Python python #{host}/scripts/shBrushPython.js',
|
147
|
+
'Ruby ruby #{host}/scripts/shBrushRuby.js',
|
148
|
+
'Sass sass #{host}/scripts/shBrushSass.js',
|
149
|
+
'Scala scala #{host}/scripts/shBrushScala.js',
|
150
|
+
'Sql sql #{host}/scripts/shBrushSql.js',
|
151
|
+
'Vb vb #{host}/scripts/shBrushVb.js',
|
152
|
+
'Xml xml #{host}/scripts/shBrushXml.js'
|
153
|
+
);
|
154
|
+
SyntaxHighlighter.all();
|
155
|
+
</script>
|
156
|
+
FOOTER
|
157
|
+
```
|
158
|
+
|
159
|
+
### 使用例
|
160
|
+
|
161
|
+
上記のように設定すると、次のような Markdown ファイル `~/memo/sample.md` は
|
162
|
+
|
163
|
+
数列
|
164
|
+
====
|
165
|
+
|
166
|
+
問題
|
167
|
+
----
|
168
|
+
|
169
|
+
和の公式
|
170
|
+
$$ \sum\_{k=1}^n k = \frac{1}{2}n(n+1) $$
|
171
|
+
を計算する関数を C++ で実装せよ。
|
172
|
+
|
173
|
+
解答
|
174
|
+
----
|
175
|
+
|
176
|
+
```cpp
|
177
|
+
int f(int n) {
|
178
|
+
int ret = 0;
|
179
|
+
for (int k = 1; k <= n; k++) {
|
180
|
+
ret += k;
|
181
|
+
}
|
182
|
+
return ret;
|
183
|
+
}
|
184
|
+
```
|
185
|
+
|
186
|
+
次のように表示される。
|
187
|
+
|
188
|
+
![](https://raw.github.com/daimatz/mamemose/master/sample.png)
|
189
|
+
|
190
|
+
MathJax と SyntaxHighlighter はローカルにダウンロードして使うのが親切だと思います。
|
191
|
+
|
192
|
+
検索
|
193
|
+
----
|
194
|
+
|
195
|
+
Markdown ドキュメントを全文検索して一致したものを表示します。
|
196
|
+
Markdown ドキュメントでないものはファイル名に一致したものを表示します。
|
197
|
+
|
198
|
+
FAQ と予想されるもの
|
199
|
+
----
|
200
|
+
|
201
|
+
- 遅いよ
|
202
|
+
- 一覧ページでは「最近更新したファイル」を表示するために
|
203
|
+
そのディレクトリ以下の全ファイルを舐めているので遅いです。
|
204
|
+
`RECENT_NUM = 0` にしてください。
|
205
|
+
- 検索が遅いのはどうしようもないです。ファイル数 3000 くらいまでなら
|
206
|
+
まあ使えるかなというのは確認したつもりですが
|
207
|
+
- SSD 積んでますか?
|
208
|
+
- 他の言語もシンタックスハイライトしたいんだけど
|
209
|
+
- SyntaxHighlighter の構文ファイルを自分で書いて読み込むようにしましょう
|
210
|
+
- Haskell 用のは書きました [gist](https://gist.github.com/3969549)
|
211
|
+
- reStructuredText 対応して
|
212
|
+
- Python で書いてください
|
213
|
+
- 表を書きたいんだけど
|
214
|
+
- 無理。 table タグ書いてください
|
215
|
+
- 定義リストを書きたいんだけど
|
216
|
+
- 同上
|
data/Rakefile
ADDED
data/bin/mamemose
ADDED
data/lib/mamemose.rb
ADDED
@@ -0,0 +1,349 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'webrick'
|
3
|
+
require 'find'
|
4
|
+
require 'uri'
|
5
|
+
require 'redcarpet'
|
6
|
+
require 'htmlentities'
|
7
|
+
|
8
|
+
require "mamemose/version"
|
9
|
+
|
10
|
+
conf = File.expand_path("~" + File::SEPARATOR + ".mamemose.rb")
|
11
|
+
load conf if File.exists?(conf)
|
12
|
+
|
13
|
+
DOCUMENT_ROOT = "~/Dropbox/memo" if !defined?(DOCUMENT_ROOT)
|
14
|
+
PORT = 20000 if !defined?(PORT)
|
15
|
+
RECENT_NUM = 10 if !defined?(RECENT_NUM)
|
16
|
+
RECENT_PATTERN = /.*/ if !defined?(RECENT_PATTERN)
|
17
|
+
IGNORE_FILES = ['.DS_Store','.AppleDouble','.LSOverride','Icon',/^\./,/~$/,
|
18
|
+
'.Spotlight-V100','.Trashes','Thumbs.db','ehthumbs.db',
|
19
|
+
'Desktop.ini','$RECYCLE.BIN',/^#/,'MathJax','syntaxhighlighter'] if !defined?(IGNORE_FILES)
|
20
|
+
MARKDOWN_PATTERN = /\.(md|markdown)$/ if !defined?(MARKDOWN_PATTERN)
|
21
|
+
CUSTOM_HEADER = '' if !defined?(CUSTOM_HEADER)
|
22
|
+
CUSTOM_BODY = '' if !defined?(CUSTOM_BODY)
|
23
|
+
CUSTOM_FOOTER = '' if !defined?(CUSTOM_FOOTER)
|
24
|
+
|
25
|
+
CONTENT_TYPE = "text/html; charset=utf-8"
|
26
|
+
DIR = File::expand_path(DOCUMENT_ROOT, '/')
|
27
|
+
|
28
|
+
class HTMLwithSyntaxHighlighter < Redcarpet::Render::XHTML
|
29
|
+
def block_code(code, lang)
|
30
|
+
code = HTMLEntities.new.encode(code)
|
31
|
+
lang ||= "plain"
|
32
|
+
return "<pre class='brush: #{lang}'>#{code}</pre>"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Mamemose::Server
|
37
|
+
def initialize
|
38
|
+
@server = WEBrick::HTTPServer.new({ :Port => PORT })
|
39
|
+
@server.mount_proc('/') do |req, res|
|
40
|
+
res['Cache-Control'] = 'no-cache, no-store, must-revalidate'
|
41
|
+
res['Pragma'] = 'no-cache'
|
42
|
+
res['Expires'] = '0'
|
43
|
+
if req.path =~ /^\/search/
|
44
|
+
query = req.query
|
45
|
+
path = path(query["path"])
|
46
|
+
q = URI.decode(query["q"])
|
47
|
+
q = q.force_encoding('utf-8') if q.respond_to?(:force_encoding)
|
48
|
+
|
49
|
+
found = {}
|
50
|
+
Find.find(path) do |file|
|
51
|
+
Find.prune if ignore?(file)
|
52
|
+
dir = File::dirname(file)
|
53
|
+
found[dir] = [] if !found[dir]
|
54
|
+
if markdown?(file)
|
55
|
+
open(file) do |f|
|
56
|
+
c = f.read + "\n" + file
|
57
|
+
found[dir] << [get_title(file,c), uri(file)] if !q.split(' ').map{|s| /#{s}/mi =~ c }.include?(nil)
|
58
|
+
end
|
59
|
+
elsif !q.split(' ').map{|s| /#{s}/ =~ File.basename(file)}.include?(nil)
|
60
|
+
found[dir] << [get_title(file),uri(file)]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
title = "Search #{q} in #{docpath(query['path'])}"
|
65
|
+
title = title.force_encoding('utf-8') if title.respond_to?(:force_encoding)
|
66
|
+
body = title + "\n====\n"
|
67
|
+
found.reject{|key, value| value == []}.sort.each do |key, value|
|
68
|
+
body += "\n### in <a href='#{uri(key)}'>#{uri(key)}\n"
|
69
|
+
value.each do |v|
|
70
|
+
body += link_list(v[0], v[1])
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
res.body = header_html(title, uri(path), q) + markdown(body) + footer_html
|
75
|
+
res.content_type = CONTENT_TYPE
|
76
|
+
|
77
|
+
else
|
78
|
+
|
79
|
+
filename = path(req.path)
|
80
|
+
|
81
|
+
if File.directory?(filename) then
|
82
|
+
title = "Index of #{docpath(req.path)}"
|
83
|
+
body = title + "\n====\n"
|
84
|
+
|
85
|
+
recent = []
|
86
|
+
dirs = []
|
87
|
+
markdowns = []
|
88
|
+
files = []
|
89
|
+
|
90
|
+
if RECENT_NUM > 0 then
|
91
|
+
Find.find(filename) do |file|
|
92
|
+
Find.prune if ignore?(file)
|
93
|
+
recent << file if File.file?(file) && file =~ RECENT_PATTERN
|
94
|
+
end
|
95
|
+
recent = recent.sort_by{|file| File.mtime(file)}.reverse.slice(0,RECENT_NUM)
|
96
|
+
recent = recent.map{|file|
|
97
|
+
if markdown?(file) then
|
98
|
+
[get_title(file, open(file).read), uri(file)]
|
99
|
+
else [File::basename(file), uri(file)]
|
100
|
+
end
|
101
|
+
}
|
102
|
+
else
|
103
|
+
recent = []
|
104
|
+
end
|
105
|
+
|
106
|
+
Dir.entries(filename).each do |i|
|
107
|
+
next if ignore?(i)
|
108
|
+
link = uri(File.join(filename, i))
|
109
|
+
if File.directory?(path(link)) then
|
110
|
+
dirs << [File.basename(link) + File::SEPARATOR, link]
|
111
|
+
elsif markdown?(link)
|
112
|
+
File.open(path(link)) do |f|
|
113
|
+
markdowns << [get_title(link, f.read), link]
|
114
|
+
end
|
115
|
+
else
|
116
|
+
files << [File::basename(link), link]
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
body += "\nRecent:\n---\n" if RECENT_NUM > 0
|
121
|
+
recent.each {|i| body += link_list(i[0], i[1])}
|
122
|
+
|
123
|
+
body += "\nDirectories:\n----\n"
|
124
|
+
dirs.each {|i| body += link_list(i[0], i[1])}
|
125
|
+
|
126
|
+
body += "\nMarkdown documents:\n----\n"
|
127
|
+
markdowns.each {|i| body += link_list(i[0], i[1])}
|
128
|
+
|
129
|
+
body += "\nOther files:\n----\n"
|
130
|
+
files.each {|i| body += link_list(i[0], i[1])}
|
131
|
+
|
132
|
+
res.body = header_html(title, req.path) + markdown(body) + footer_html
|
133
|
+
res.content_type = CONTENT_TYPE
|
134
|
+
|
135
|
+
elsif File.exists?(filename)
|
136
|
+
open(filename) do |file|
|
137
|
+
if markdown?(req.path)
|
138
|
+
str = file.read
|
139
|
+
title = get_title(filename, str)
|
140
|
+
res.body = header_html(title, req.path) + markdown(str) + footer_html
|
141
|
+
res.content_type = CONTENT_TYPE
|
142
|
+
else
|
143
|
+
res.body = file.read
|
144
|
+
res.content_type = WEBrick::HTTPUtils.mime_type(req.path, WEBrick::HTTPUtils::DefaultMimeTypes)
|
145
|
+
res.content_length = File.stat(filename).size
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
else
|
150
|
+
res.status = WEBrick::HTTPStatus::RC_NOT_FOUND
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
trap(:INT){@server.shutdown}
|
157
|
+
trap(:TERM){@server.shutdown}
|
158
|
+
end
|
159
|
+
|
160
|
+
def start
|
161
|
+
@server.start
|
162
|
+
end
|
163
|
+
|
164
|
+
private
|
165
|
+
|
166
|
+
def header_html(title, path, q="")
|
167
|
+
html = <<HTML
|
168
|
+
<!DOCTYPE HTML>
|
169
|
+
<html>
|
170
|
+
<head>
|
171
|
+
<meta http-equiv="Content-Type" content="#{CONTENT_TYPE}" />
|
172
|
+
<title>#{title}</title>
|
173
|
+
<style type="text/css"><!--
|
174
|
+
body {
|
175
|
+
margin: auto;
|
176
|
+
padding: 0 2em;
|
177
|
+
max-width: 80%;
|
178
|
+
border-left: 1px solid black;
|
179
|
+
border-right: 1px solid black;
|
180
|
+
font-size: 100%;
|
181
|
+
line-height: 140%;
|
182
|
+
}
|
183
|
+
pre {
|
184
|
+
border: 1px solid #090909;
|
185
|
+
background-color: #f8f8f8;
|
186
|
+
padding: 0.5em;
|
187
|
+
margin: 0.5em 1em;
|
188
|
+
}
|
189
|
+
code {
|
190
|
+
border: 1px solid #cccccc;
|
191
|
+
background-color: #f8f8f8;
|
192
|
+
padding: 2px 0.5em;
|
193
|
+
margin: 0 0.5em;
|
194
|
+
}
|
195
|
+
a {
|
196
|
+
text-decoration: none;
|
197
|
+
}
|
198
|
+
a:link, a:visited, a:hover {
|
199
|
+
color: #4444cc;
|
200
|
+
}
|
201
|
+
a:hover {
|
202
|
+
text-decoration: underline;
|
203
|
+
}
|
204
|
+
h1, h2, h3 {
|
205
|
+
font-weight: bold;
|
206
|
+
color: #2f4f4f;
|
207
|
+
}
|
208
|
+
h1 {
|
209
|
+
font-size: 200%;
|
210
|
+
line-height: 100%;
|
211
|
+
margin: 1em 0;
|
212
|
+
border-bottom: 1px solid #2f4f4f;
|
213
|
+
}
|
214
|
+
h2 {
|
215
|
+
font-size: 175%;
|
216
|
+
line-height: 100%;
|
217
|
+
margin: 1em 0;
|
218
|
+
padding-left: 0.5em;
|
219
|
+
border-left: 0.5em solid #2f4f4f;
|
220
|
+
}
|
221
|
+
h3 {
|
222
|
+
font-size: 150%;
|
223
|
+
line-height: 100%;
|
224
|
+
margin: 1em 0;
|
225
|
+
}
|
226
|
+
h4, h5 {
|
227
|
+
font-weight: bold;
|
228
|
+
color: #000000;
|
229
|
+
margin: 1em 0 0.5em;
|
230
|
+
}
|
231
|
+
h4 { font-size: 125% }
|
232
|
+
h5 { font-size: 100% }
|
233
|
+
p {
|
234
|
+
margin: 0.7em 1em;
|
235
|
+
text-indent: 1em;
|
236
|
+
}
|
237
|
+
div.footnotes {
|
238
|
+
padding-top: 1em;
|
239
|
+
color: #090909;
|
240
|
+
}
|
241
|
+
div#header {
|
242
|
+
margin-top: 1em;
|
243
|
+
padding-bottom: 1em;
|
244
|
+
border-bottom: 1px dotted black;
|
245
|
+
}
|
246
|
+
div#header > form {
|
247
|
+
display: float;
|
248
|
+
float: right;
|
249
|
+
text-align: right;
|
250
|
+
}
|
251
|
+
a.filename {
|
252
|
+
color: #666666;
|
253
|
+
}
|
254
|
+
footer {
|
255
|
+
border-top: 1px dotted black;
|
256
|
+
padding: 0.5em;
|
257
|
+
font-size: 80%;
|
258
|
+
text-align: right;
|
259
|
+
margin: 5em 0 1em;
|
260
|
+
}
|
261
|
+
--></style>
|
262
|
+
<script>
|
263
|
+
function copy(text) {
|
264
|
+
prompt("Copy filepath below:", text);
|
265
|
+
}
|
266
|
+
</script>
|
267
|
+
#{CUSTOM_HEADER}
|
268
|
+
</head>
|
269
|
+
<body>
|
270
|
+
#{CUSTOM_BODY}
|
271
|
+
HTML
|
272
|
+
link_str = ""
|
273
|
+
uri = ""
|
274
|
+
path.split('/').each do |s|
|
275
|
+
next if s == ''
|
276
|
+
uri += "/" + s
|
277
|
+
link_str += File::SEPARATOR + "<a href='#{uri}'>#{s}</a>"
|
278
|
+
end
|
279
|
+
link_str += " <a class='filename' href=\"javascript:copy('#{docpath(uri)}');\">[copy]</a>"
|
280
|
+
uri.gsub!('/'+File::basename(uri), "") if File.file?(path(uri))
|
281
|
+
link_str = "<a href='/'>#{DOCUMENT_ROOT}</a>" + link_str
|
282
|
+
search_form = <<HTML
|
283
|
+
<form action="/search" method="get">
|
284
|
+
<input name="path" type="hidden" value="#{uri}" />
|
285
|
+
<input name="q" type="text" value="#{q}" size="24" />
|
286
|
+
<input type="submit" value="search" />
|
287
|
+
</form>
|
288
|
+
HTML
|
289
|
+
return html + "<div id=\"header\">#{link_str}#{search_form}</div>"
|
290
|
+
end
|
291
|
+
|
292
|
+
def footer_html
|
293
|
+
html = <<HTML
|
294
|
+
#{CUSTOM_FOOTER}
|
295
|
+
<footer>
|
296
|
+
<a href="https://github.com/daimatz/mamemose">mamemose: Markdown memo server</a>
|
297
|
+
</footer>
|
298
|
+
</body>
|
299
|
+
</html>
|
300
|
+
HTML
|
301
|
+
return html
|
302
|
+
end
|
303
|
+
|
304
|
+
def uri(path)
|
305
|
+
s = File::expand_path(path).gsub(DIR, "").gsub(File::SEPARATOR, '/')
|
306
|
+
return s == '' ? '/' : s
|
307
|
+
end
|
308
|
+
|
309
|
+
def path(uri)
|
310
|
+
return File.join(DIR, uri.gsub('/', File::SEPARATOR))
|
311
|
+
end
|
312
|
+
|
313
|
+
def docpath(uri)
|
314
|
+
return File.join(DOCUMENT_ROOT, uri.gsub('/', File::SEPARATOR)).gsub(/#{File::SEPARATOR}$/, "")
|
315
|
+
end
|
316
|
+
|
317
|
+
def link_list(title, link)
|
318
|
+
file = path(link)
|
319
|
+
str = File.file?(file) ? sprintf("%.1fKB", File.size(file) / 1024.0) : "dir"
|
320
|
+
return "- [#{title}](#{link}) <a class='filename' href=\"javascript:copy('#{docpath(link)}');\">[#{File.basename(link)}, #{str}]</a>\n"
|
321
|
+
end
|
322
|
+
|
323
|
+
def markdown?(file)
|
324
|
+
return file =~ MARKDOWN_PATTERN
|
325
|
+
end
|
326
|
+
|
327
|
+
def ignore?(file)
|
328
|
+
file = File::basename(file)
|
329
|
+
IGNORE_FILES.each do |s|
|
330
|
+
return true if s.class == String && s == file
|
331
|
+
return true if s.class == Regexp && s =~ file
|
332
|
+
end
|
333
|
+
return false
|
334
|
+
end
|
335
|
+
|
336
|
+
def get_title(filename, str="")
|
337
|
+
return File::basename(filename) if !markdown?(filename)
|
338
|
+
title = str.split(/$/)[0]
|
339
|
+
return title =~ /^\s*$/ ? File::basename(filename) : title
|
340
|
+
end
|
341
|
+
|
342
|
+
def markdown(text)
|
343
|
+
markdown = Redcarpet::Markdown.new(HTMLwithSyntaxHighlighter,
|
344
|
+
{:strikethrough => true,
|
345
|
+
:autolink => true,
|
346
|
+
:fenced_code_blocks => true})
|
347
|
+
markdown.render(text)
|
348
|
+
end
|
349
|
+
end
|
data/mamemose.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mamemose/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["daimatz"]
|
6
|
+
gem.email = ["dai@daimatz.net"]
|
7
|
+
gem.description = %q{Markdown memo server}
|
8
|
+
gem.summary = %q{Markdown memo server}
|
9
|
+
gem.homepage = "https://github.com/daimatz/mamemose"
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($/)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "mamemose"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Mamemose::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "redcarpet", ">= 2.2.0"
|
19
|
+
gem.add_dependency "htmlentities", ">= 4.3.0"
|
20
|
+
gem.add_dependency 'thor', '>= 0.13.6'
|
21
|
+
end
|
data/sample.png
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mamemose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- daimatz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-11-30 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: redcarpet
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 2.2.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 2.2.0
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: htmlentities
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 4.3.0
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 4.3.0
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: thor
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 0.13.6
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.13.6
|
62
|
+
description: Markdown memo server
|
63
|
+
email:
|
64
|
+
- dai@daimatz.net
|
65
|
+
executables:
|
66
|
+
- mamemose
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- .gitignore
|
71
|
+
- Gemfile
|
72
|
+
- LICENSE
|
73
|
+
- README.md
|
74
|
+
- Rakefile
|
75
|
+
- bin/mamemose
|
76
|
+
- lib/mamemose.rb
|
77
|
+
- lib/mamemose/version.rb
|
78
|
+
- mamemose.gemspec
|
79
|
+
- sample.png
|
80
|
+
homepage: https://github.com/daimatz/mamemose
|
81
|
+
licenses: []
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
none: false
|
88
|
+
requirements:
|
89
|
+
- - ! '>='
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: '0'
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
requirements: []
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.8.24
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Markdown memo server
|
104
|
+
test_files: []
|