memorack 0.0.1
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.
- data/.gitignore +28 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +108 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/memorack +9 -0
- data/etc/tools/update_version.rb +14 -0
- data/lib/memorack/cli.rb +263 -0
- data/lib/memorack/locales/en.yml +7 -0
- data/lib/memorack/locales/ja.yml +7 -0
- data/lib/memorack/mdmenu.rb +185 -0
- data/lib/memorack/memoapp.rb +350 -0
- data/lib/memorack/sinatra-mustache.rb +13 -0
- data/lib/memorack/template/.gitignore +12 -0
- data/lib/memorack/template/.powenv +1 -0
- data/lib/memorack/template/Gemfile +7 -0
- data/lib/memorack/template/config.ru +5 -0
- data/lib/memorack/template/content/README.md +3 -0
- data/lib/memorack/template/themes/custom/config.json +15 -0
- data/lib/memorack/template/themes/custom/index.md +3 -0
- data/lib/memorack/themes/basic/2-column.scss +60 -0
- data/lib/memorack/themes/basic/basic-styles.scss +82 -0
- data/lib/memorack/themes/basic/config.json +14 -0
- data/lib/memorack/themes/basic/index.html +45 -0
- data/lib/memorack/themes/basic/styles.scss +9 -0
- data/lib/memorack/themes/oreilly/config.json +3 -0
- data/lib/memorack/themes/oreilly/oreilly/base.scss +33 -0
- data/lib/memorack/themes/oreilly/oreilly/styles.scss +138 -0
- data/lib/memorack/themes/oreilly/styles.scss +13 -0
- data/lib/memorack/tilt-mustache.rb +29 -0
- data/lib/memorack/version.rb +6 -0
- data/lib/memorack.rb +6 -0
- data/memorack.gemspec +45 -0
- data/spec/memorack_spec.rb +239 -0
- data/spec/spec_helper.rb +9 -0
- metadata +230 -0
@@ -0,0 +1,350 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rack'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require 'memorack/tilt-mustache'
|
8
|
+
require 'memorack/mdmenu'
|
9
|
+
|
10
|
+
module MemoRack
|
11
|
+
class MemoApp
|
12
|
+
attr_reader :themes, :options_chain
|
13
|
+
|
14
|
+
DEFAULT_APP_OPTIONS = {
|
15
|
+
root: 'content/',
|
16
|
+
themes_folder: 'themes/',
|
17
|
+
tmpdir: 'tmp/',
|
18
|
+
theme: 'basic',
|
19
|
+
markdown: 'redcarpet',
|
20
|
+
formats: ['markdown'],
|
21
|
+
css: nil,
|
22
|
+
directory_watcher: false
|
23
|
+
}
|
24
|
+
|
25
|
+
# テンプレートエンジンのオプション
|
26
|
+
DEFAULT_TEMPLATE_OPTIONS = {
|
27
|
+
tables: true
|
28
|
+
}
|
29
|
+
|
30
|
+
# テンプレートで使用するローカル変数の初期値
|
31
|
+
DEFAULT_LOCALS = {
|
32
|
+
title: 'memo'
|
33
|
+
}
|
34
|
+
|
35
|
+
DEFAULT_OPTIONS = DEFAULT_APP_OPTIONS.merge(DEFAULT_TEMPLATE_OPTIONS).merge(DEFAULT_LOCALS)
|
36
|
+
|
37
|
+
def initialize(app, options={})
|
38
|
+
options = DEFAULT_OPTIONS.merge(to_sym_keys(options))
|
39
|
+
|
40
|
+
@themes_folders = [options[:themes_folder], File.expand_path('../themes/', __FILE__)]
|
41
|
+
read_config(options[:theme], options)
|
42
|
+
|
43
|
+
@app = app
|
44
|
+
@options = options
|
45
|
+
|
46
|
+
# DEFAULT_APP_OPTIONS に含まれるキーをすべてインスタンス変数に登録する
|
47
|
+
DEFAULT_APP_OPTIONS.each { |key, item|
|
48
|
+
instance_variable_set("@#{key}".to_sym, options[key])
|
49
|
+
|
50
|
+
# @options からテンプレートで使わないものを削除
|
51
|
+
@options.delete(key)
|
52
|
+
}
|
53
|
+
|
54
|
+
@locals = default_locals(@options)
|
55
|
+
|
56
|
+
use_engine(@markdown)
|
57
|
+
define_statics(@root, *@themes)
|
58
|
+
|
59
|
+
# ファイル監視を行う
|
60
|
+
watcher(@root) if @directory_watcher
|
61
|
+
end
|
62
|
+
|
63
|
+
def call(env)
|
64
|
+
content_type = 'text/html'
|
65
|
+
|
66
|
+
req = Rack::Request.new(env)
|
67
|
+
query = Rack::Utils.parse_query(req.query_string)
|
68
|
+
path_info = URI.unescape(req.path_info)
|
69
|
+
path, ext = split_extname(path_info)
|
70
|
+
|
71
|
+
case path_info
|
72
|
+
when '/'
|
73
|
+
content = render_with_mustache :index, :markdown
|
74
|
+
when /\.css$/
|
75
|
+
case @css
|
76
|
+
when 'scss', 'sass'
|
77
|
+
require 'sass'
|
78
|
+
|
79
|
+
result = pass(env, @statics)
|
80
|
+
return result unless result.first == 404
|
81
|
+
|
82
|
+
content_type = 'text/css'
|
83
|
+
cache_location = File.expand_path('sass-cache', @tmpdir)
|
84
|
+
content = render @css.to_sym, "#{path}.#{@css}", {views: @themes, cache_location: cache_location}
|
85
|
+
end
|
86
|
+
else
|
87
|
+
return pass(env) unless ext && Tilt.registered?(ext)
|
88
|
+
|
89
|
+
if query.has_key?('edit')
|
90
|
+
fullpath = File.expand_path(File.join(@root, path_info))
|
91
|
+
|
92
|
+
# @attention リダイレクトはうまく動作しない
|
93
|
+
#
|
94
|
+
# redirect_url = 'file://' + File.expand_path(File.join(@root, req.path_info))
|
95
|
+
# return redirect(redirect_url, 302) if File.exists?(fullpath)
|
96
|
+
end
|
97
|
+
|
98
|
+
content = render_with_mustache path.to_sym, ext
|
99
|
+
end
|
100
|
+
|
101
|
+
return pass(env) unless content
|
102
|
+
|
103
|
+
[200, {'Content-Type' => content_type}, [content.to_s]]
|
104
|
+
end
|
105
|
+
|
106
|
+
|
107
|
+
# リダイレクト
|
108
|
+
def redirect(url, code = 301)
|
109
|
+
# 301 = 恒久的, 302 = 一時的, 303, 410
|
110
|
+
[code, {'Content-Type' => 'text/html', 'Location' => url}, ['Redirect: ', url]]
|
111
|
+
end
|
112
|
+
|
113
|
+
# テンプレートエンジンを使用できるようにする
|
114
|
+
def use_engine(engine)
|
115
|
+
require engine if engine
|
116
|
+
|
117
|
+
# Tilt で Redcarpet 2.x を使うためのおまじない
|
118
|
+
Object.send(:remove_const, :RedcarpetCompat) if defined?(RedcarpetCompat) == 'constant'
|
119
|
+
end
|
120
|
+
|
121
|
+
# テーマのパスを取得する
|
122
|
+
def theme_path(theme)
|
123
|
+
return nil unless theme
|
124
|
+
|
125
|
+
@themes_folders.each { |folder|
|
126
|
+
path = theme && File.join(folder, theme)
|
127
|
+
return path if File.exists?(path) && FileTest::directory?(path)
|
128
|
+
}
|
129
|
+
|
130
|
+
nil
|
131
|
+
end
|
132
|
+
|
133
|
+
# デフォルトの locals を生成する
|
134
|
+
def default_locals(locals = {})
|
135
|
+
locals = locals.dup
|
136
|
+
|
137
|
+
locals[:page] ||= {}
|
138
|
+
locals[:page][:title] ||= locals[:title]
|
139
|
+
|
140
|
+
locals[:app] ||= {}
|
141
|
+
locals[:app][:name] ||= MemoRack::name
|
142
|
+
locals[:app][:version] ||= MemoRack::VERSION
|
143
|
+
locals[:app][:url] ||= MemoRack::HOMEPAGE
|
144
|
+
|
145
|
+
locals
|
146
|
+
end
|
147
|
+
|
148
|
+
# 設定ファイルを読込む
|
149
|
+
def read_config(theme, options = {})
|
150
|
+
@themes ||= []
|
151
|
+
@options_chain = []
|
152
|
+
@theme_chain = []
|
153
|
+
|
154
|
+
begin
|
155
|
+
require 'json'
|
156
|
+
|
157
|
+
while theme
|
158
|
+
dir = theme_path(theme)
|
159
|
+
break unless dir
|
160
|
+
break if @themes.member?(dir)
|
161
|
+
|
162
|
+
# テーマ・チェインに追加
|
163
|
+
@themes << File.join(dir, '')
|
164
|
+
|
165
|
+
# config の読込み
|
166
|
+
path = File.join(dir, 'config.json')
|
167
|
+
break unless File.readable?(path)
|
168
|
+
|
169
|
+
data = File.read(path)
|
170
|
+
@options_chain << to_sym_keys(JSON.parse(data))
|
171
|
+
|
172
|
+
theme = @options_chain.last[:theme]
|
173
|
+
end
|
174
|
+
rescue
|
175
|
+
end
|
176
|
+
|
177
|
+
# オプションをマージ
|
178
|
+
@options_chain.reverse.each { |opts| options.merge!(opts) }
|
179
|
+
options
|
180
|
+
end
|
181
|
+
|
182
|
+
# 静的ファイルの参照先を定義する
|
183
|
+
def define_statics(*args)
|
184
|
+
@statics = [] unless @statics
|
185
|
+
|
186
|
+
@statics |= args.collect { |root| Rack::File.new(root) }
|
187
|
+
end
|
188
|
+
|
189
|
+
# 次のアプリにパスする
|
190
|
+
def pass(env, apps = @statics + [@app])
|
191
|
+
apps.each { |app|
|
192
|
+
next unless app
|
193
|
+
|
194
|
+
result = app.call(env)
|
195
|
+
return result unless result.first == 404
|
196
|
+
}
|
197
|
+
|
198
|
+
[404, {'Content-Type' => 'text/plain'}, ['File not found: ', env['PATH_INFO']]]
|
199
|
+
end
|
200
|
+
|
201
|
+
# ファイル監視を行う
|
202
|
+
def watcher(path = '.')
|
203
|
+
require 'directory_watcher'
|
204
|
+
|
205
|
+
dw = DirectoryWatcher.new path, :pre_load => true
|
206
|
+
dw.interval = 1
|
207
|
+
dw.stable = 2
|
208
|
+
dw.glob = '**/*'
|
209
|
+
dw.add_observer { |*args|
|
210
|
+
t = Time.now.strftime("%Y-%m-%d %H:%M:%S")
|
211
|
+
puts "[#{t}] regeneration: #{args.size} files changed"
|
212
|
+
|
213
|
+
@menu = nil
|
214
|
+
}
|
215
|
+
|
216
|
+
dw.start
|
217
|
+
end
|
218
|
+
|
219
|
+
# テンプレートエンジンで render する
|
220
|
+
def render(engine, template, options = {}, locals = {})
|
221
|
+
options = {views: @root}.merge(options)
|
222
|
+
|
223
|
+
if options[:views].kind_of?(Array)
|
224
|
+
err = nil
|
225
|
+
|
226
|
+
options[:views].each { |views|
|
227
|
+
options[:views] = views
|
228
|
+
|
229
|
+
begin
|
230
|
+
return render(engine, template, options, locals)
|
231
|
+
rescue Errno::ENOENT => e
|
232
|
+
err = e
|
233
|
+
end
|
234
|
+
}
|
235
|
+
|
236
|
+
raise err
|
237
|
+
end
|
238
|
+
|
239
|
+
fname = template.kind_of?(String) ? template : "#{template}.#{engine}"
|
240
|
+
path = File.join(options[:views], fname)
|
241
|
+
|
242
|
+
engine = Tilt.new(File.join(File.dirname(path), ".#{engine}"), options) {
|
243
|
+
method = MemoApp.template_method(template)
|
244
|
+
|
245
|
+
if method && respond_to?(method)
|
246
|
+
data = send(method)
|
247
|
+
else
|
248
|
+
data = File.binread(path)
|
249
|
+
data.force_encoding('UTF-8')
|
250
|
+
end
|
251
|
+
|
252
|
+
data
|
253
|
+
}
|
254
|
+
engine.render(options, locals).force_encoding('UTF-8')
|
255
|
+
end
|
256
|
+
|
257
|
+
# レイアウトに mustache を適用してテンプレートエンジンでレンダリングする
|
258
|
+
def render_with_mustache(template, engine = :markdown, options = {}, locals = {})
|
259
|
+
begin
|
260
|
+
options = @options.merge(options)
|
261
|
+
|
262
|
+
@menu = nil unless @directory_watcher # ファイル監視していない場合はメニューを初期化
|
263
|
+
|
264
|
+
@menu ||= render :markdown, :menu, options
|
265
|
+
content = render engine, template, options
|
266
|
+
fname = template.to_s.force_encoding('UTF-8')
|
267
|
+
|
268
|
+
locals = @locals.merge(locals)
|
269
|
+
|
270
|
+
locals[:__menu__] = @menu
|
271
|
+
locals[:__content__] = content
|
272
|
+
locals[:page][:title] = [File.basename(fname), locals[:title]].join(' | ') unless template == :index
|
273
|
+
|
274
|
+
render :mustache, 'index.html', {views: @themes}, locals
|
275
|
+
rescue => e
|
276
|
+
e.to_s
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
# 拡張子を取出す
|
281
|
+
def split_extname(path)
|
282
|
+
return [$1, $2] if /^(.+)\.([^.]+)/ =~ path
|
283
|
+
|
284
|
+
[path]
|
285
|
+
end
|
286
|
+
|
287
|
+
# キーをシンボルに変換する
|
288
|
+
def to_sym_keys(hash)
|
289
|
+
hash.inject({}) { |memo, entry|
|
290
|
+
key, value = entry
|
291
|
+
memo[key.to_sym] = value
|
292
|
+
memo
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
# Tilt に登録されている拡張子を集める
|
297
|
+
def extnames(extname)
|
298
|
+
klass = Tilt[extname]
|
299
|
+
Tilt.mappings.select { |key, value| value.member?(klass) }.collect { |key, value| key }
|
300
|
+
end
|
301
|
+
|
302
|
+
# 対応フォーマットを取得する
|
303
|
+
def collect_formats
|
304
|
+
unless @collect_formats
|
305
|
+
@collect_formats = {}
|
306
|
+
|
307
|
+
@formats.each { |item|
|
308
|
+
if item.kind_of?(Array)
|
309
|
+
@collect_formats[item.first] = item
|
310
|
+
elsif item.kind_of?(Hash)
|
311
|
+
@collect_formats.merge!(item)
|
312
|
+
else
|
313
|
+
@collect_formats[item] = extnames(item)
|
314
|
+
end
|
315
|
+
}
|
316
|
+
end
|
317
|
+
|
318
|
+
@collect_formats
|
319
|
+
end
|
320
|
+
|
321
|
+
# テンプレート名
|
322
|
+
def self.template_method(name)
|
323
|
+
name.kind_of?(Symbol) && "template_#{name}".to_sym
|
324
|
+
end
|
325
|
+
|
326
|
+
# テンプレートを作成する
|
327
|
+
def self.template(name, &block)
|
328
|
+
define_method(self.template_method(name), &block)
|
329
|
+
end
|
330
|
+
|
331
|
+
#### テンプレート
|
332
|
+
|
333
|
+
# インデックスを作成
|
334
|
+
template :index do
|
335
|
+
begin
|
336
|
+
render :markdown, 'index.md', {views: @themes}
|
337
|
+
rescue
|
338
|
+
''
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
# メニューを作成
|
343
|
+
template :menu do
|
344
|
+
mdmenu = MdMenu.new({prefix: '/', uri_escape: true, formats: collect_formats})
|
345
|
+
Dir.chdir(@root) { |path| mdmenu.collection('.') }
|
346
|
+
mdmenu.generate(StringIO.new).string
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
export PATH="$HOME/.rbenv/shims:$PATH"
|
@@ -0,0 +1,15 @@
|
|
1
|
+
{
|
2
|
+
// "directory_watcher": true, // ファイルの変更を監視するか決める
|
3
|
+
"theme": "oreilly", // テーマ(省略すると basic )
|
4
|
+
// "markdown": "kramdown", // 使用する markdownライブラリ(省略すると redcarpet )
|
5
|
+
// "formats": ["markdown"], // "markdown", "rdoc", "textile" or "wiki" (要 Tilt に対応した gem ライブラリ)
|
6
|
+
|
7
|
+
// redcarpet の拡張構文
|
8
|
+
// "tables": false, // テーブル
|
9
|
+
// "strikethrough": false, // 例: ~~good~~
|
10
|
+
// "fenced_code_blocks": false, // 例: ```ruby
|
11
|
+
// "superscript": false, // 例: 2^10
|
12
|
+
// "autolink": true, // 例: http://0.0.0.0/
|
13
|
+
|
14
|
+
"title": "覚書き" // タイトル
|
15
|
+
}
|
@@ -0,0 +1,60 @@
|
|
1
|
+
/* 2カラム表示 */
|
2
|
+
#content-container {
|
3
|
+
width: 100%;
|
4
|
+
}
|
5
|
+
|
6
|
+
#menu {
|
7
|
+
width: 30%;
|
8
|
+
min-height: 100px;
|
9
|
+
float: left;
|
10
|
+
margin: 20px 0 20px 0;
|
11
|
+
padding: 0 10px 0 10px;
|
12
|
+
border: solid 1px #888;
|
13
|
+
border-radius: 10px;
|
14
|
+
|
15
|
+
ul {
|
16
|
+
padding-left: 20px;
|
17
|
+
}
|
18
|
+
|
19
|
+
a:link {
|
20
|
+
color: #369;
|
21
|
+
text-decoration: none;
|
22
|
+
}
|
23
|
+
|
24
|
+
a:visited {
|
25
|
+
color: #369;
|
26
|
+
}
|
27
|
+
|
28
|
+
a:hover {
|
29
|
+
color: #f00;
|
30
|
+
}
|
31
|
+
|
32
|
+
li {
|
33
|
+
padding-left: 4px;
|
34
|
+
margin-left: -4px;
|
35
|
+
}
|
36
|
+
|
37
|
+
li.selected {
|
38
|
+
background: #58b;
|
39
|
+
a {
|
40
|
+
color: #fff;
|
41
|
+
}
|
42
|
+
}
|
43
|
+
}
|
44
|
+
|
45
|
+
#content {
|
46
|
+
width: 65%;
|
47
|
+
float: right;
|
48
|
+
margin: 0;
|
49
|
+
position: relative;
|
50
|
+
}
|
51
|
+
|
52
|
+
.clear {
|
53
|
+
clear: both;
|
54
|
+
}
|
55
|
+
|
56
|
+
/* 文中の折返し */
|
57
|
+
pre {
|
58
|
+
white-space: pre-wrap;
|
59
|
+
word-break: break-all;
|
60
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
/* 基本 */
|
2
|
+
h1 {
|
3
|
+
font-size:1.8em;
|
4
|
+
}
|
5
|
+
|
6
|
+
h2 {
|
7
|
+
font-size:1.6em;
|
8
|
+
}
|
9
|
+
|
10
|
+
h3 {
|
11
|
+
font-size:1.2em;
|
12
|
+
}
|
13
|
+
|
14
|
+
h4, h5, h6 {
|
15
|
+
font-size:1em;
|
16
|
+
}
|
17
|
+
|
18
|
+
|
19
|
+
/* テーブル */
|
20
|
+
table {
|
21
|
+
width: 100%;
|
22
|
+
border-collapse: collapse;
|
23
|
+
margin: 10px 0px;
|
24
|
+
}
|
25
|
+
|
26
|
+
tr {
|
27
|
+
background: #FFF;
|
28
|
+
|
29
|
+
.altrow {
|
30
|
+
background: #F9F9F9;
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
th, caption {
|
35
|
+
text-align:left;
|
36
|
+
}
|
37
|
+
|
38
|
+
th, td {
|
39
|
+
padding: .5em;
|
40
|
+
line-height:1.5em;
|
41
|
+
border:1px solid #ddd;
|
42
|
+
text-align: left;
|
43
|
+
font-size: .9em
|
44
|
+
}
|
45
|
+
|
46
|
+
th {
|
47
|
+
color: #555;
|
48
|
+
background: #f2f2f2;
|
49
|
+
padding: .5em .5em;
|
50
|
+
font-weight:bold;
|
51
|
+
text-align: center;
|
52
|
+
}
|
53
|
+
|
54
|
+
td {
|
55
|
+
padding: .5em;
|
56
|
+
}
|
57
|
+
|
58
|
+
caption {
|
59
|
+
font-style: italic;
|
60
|
+
color: #777;
|
61
|
+
}
|
62
|
+
|
63
|
+
|
64
|
+
/* コード */
|
65
|
+
code {
|
66
|
+
font-family: monospace;
|
67
|
+
white-space: pre;
|
68
|
+
background: #F7F7F7;
|
69
|
+
border: 1px solid #D7D7D7;
|
70
|
+
|
71
|
+
display: inline;
|
72
|
+
padding: 0 4px 0;
|
73
|
+
margin: 0 2px 0;
|
74
|
+
}
|
75
|
+
|
76
|
+
pre code {
|
77
|
+
display: block;
|
78
|
+
padding: 4px 12px 4px;
|
79
|
+
white-space: pre-wrap;
|
80
|
+
word-break: break-all;
|
81
|
+
}
|
82
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
{
|
2
|
+
"directory_watcher": false, // ファイルの変更を監視するか決める
|
3
|
+
"css": "scss", // scss の自動変換を使用する
|
4
|
+
// "markdown": "kramdown", // 使用する markdownライブラリ
|
5
|
+
// "formats": ["markdown"], // "markdown", "rdoc", "textile" or "wiki" (要 Tilt に対応した gem ライブラリ)
|
6
|
+
|
7
|
+
// redcarpet の拡張構文
|
8
|
+
"strikethrough": true, // 例: ~~good~~
|
9
|
+
"fenced_code_blocks": true, // 例: ```ruby
|
10
|
+
"superscript": true, // 例: 2^10
|
11
|
+
// "autolink": true, // 例: http://0.0.0.0/
|
12
|
+
|
13
|
+
"title": "メモ"
|
14
|
+
}
|
@@ -0,0 +1,45 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8" />
|
5
|
+
|
6
|
+
<style>
|
7
|
+
article, aside, dialog, figure, footer, header,
|
8
|
+
hgroup, menu, nav, section { display: block; }
|
9
|
+
</style>
|
10
|
+
|
11
|
+
<meta name="keywords" content="" />
|
12
|
+
<meta name="description" content="" />
|
13
|
+
<title>{{page.title}}</title>
|
14
|
+
|
15
|
+
<link type="text/css" href="/styles.css" rel="stylesheet" media="all" />
|
16
|
+
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js"></script>
|
17
|
+
<script type="text/javascript">
|
18
|
+
$(function(){
|
19
|
+
// 表示しているメニューを選択
|
20
|
+
$('#menu a[href="' + document.location.pathname + '"]').parent().addClass('selected');
|
21
|
+
|
22
|
+
// 外部サイトのリンクに target='_blank' を追加
|
23
|
+
$('a[href^=http]').not('[href*=":' + location.hostname + '"]').attr('target', '_blank');
|
24
|
+
});
|
25
|
+
</script>
|
26
|
+
|
27
|
+
</head>
|
28
|
+
<body>
|
29
|
+
<div id="page">
|
30
|
+
<header>
|
31
|
+
<h1><a href="/">{{title}}</a></h1>
|
32
|
+
</header>
|
33
|
+
|
34
|
+
<div id="content-container">
|
35
|
+
<div id="menu" class="importdoc">{{{__menu__}}}</div>
|
36
|
+
<div id="content">{{{__content__}}}</div>
|
37
|
+
<div class="clear"></div>
|
38
|
+
</div>
|
39
|
+
|
40
|
+
<footer>
|
41
|
+
<p>Powered by <a href="{{app.url}}" target="_blank"> {{app.name}} {{app.version}}</a></p>
|
42
|
+
</footer>
|
43
|
+
</div>
|
44
|
+
</body>
|
45
|
+
</html>
|
@@ -0,0 +1,33 @@
|
|
1
|
+
/* mixin */
|
2
|
+
|
3
|
+
@mixin box-shadow($args) {
|
4
|
+
-moz-box-shadow: $args;
|
5
|
+
-webkit-box-shadow: $args;
|
6
|
+
box-shadow: $args;
|
7
|
+
}
|
8
|
+
|
9
|
+
|
10
|
+
@mixin transform($args) {
|
11
|
+
-moz-transform: $args;
|
12
|
+
-webkit-transform: $args;
|
13
|
+
transform: $args;
|
14
|
+
}
|
15
|
+
|
16
|
+
|
17
|
+
/* basic style */
|
18
|
+
|
19
|
+
body {
|
20
|
+
color:#333;
|
21
|
+
font-size: 14px;
|
22
|
+
font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif;
|
23
|
+
line-height: 1.5;
|
24
|
+
-webkit-font-smoothing: antialiased;
|
25
|
+
}
|
26
|
+
|
27
|
+
h1, h2, h3, h4, h5, h6 {
|
28
|
+
font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif;
|
29
|
+
font-weight: bold;
|
30
|
+
letter-spacing: -0.018em;
|
31
|
+
page-break-after: avoid;
|
32
|
+
}
|
33
|
+
|