markedly 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d54d644c1ee9d9be6259fbe45e2c8f83799bb06a
4
+ data.tar.gz: 90c261ae3447a3f8fa3381c1a01139683114c5d6
5
+ SHA512:
6
+ metadata.gz: 8e13d2bfe20e3538f17a24b178c1e761cff6eb1de1077cf42b6d2d5d9aa9386f06828d33e542c76b13cc2fb6405609680fdd025db6ddfb9f1a6508af0866fa04
7
+ data.tar.gz: d34b03f5c1677d4bf26cd2fd4d480de32bdeffd9feaa7f6369eec73d43bde496e73d64ebd69f24b0df722f19ca0b83fdcadfe79904f65d2d272aaaeb0a7dfcf1
data/.gitignore ADDED
@@ -0,0 +1,18 @@
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
+ vendor/bundle
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in markedly.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Yoshihiro Hara
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,53 @@
1
+ # Markedly
2
+
3
+ [![Build Status](https://travis-ci.org/hara/markedly.png)](https://travis-ci.org/hara/markedly)
4
+ [![Coverage Status](https://coveralls.io/repos/hara/markedly/badge.png)](https://coveralls.io/r/hara/markedly)
5
+ [![Code Climate](https://codeclimate.com/github/hara/markedly.png)](https://codeclimate.com/github/hara/markedly)
6
+
7
+
8
+ Markedly is live-reloadable markdown previewer.
9
+
10
+ Markedly converts markdown document and opens it with default browser.
11
+ Browser automatically reloads using WebSocket when you modify the document.
12
+
13
+ ## Installation
14
+
15
+ ```
16
+ $ gem install markedly
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Preview using default github css:
22
+
23
+ ```
24
+ $ markedly README.md
25
+ ```
26
+
27
+ Preview using custom css:
28
+
29
+ ```
30
+ $ markedly README.md -c /path/to/custom.css
31
+ $ markedly README.md -c http://example.com/custom.css
32
+ ```
33
+
34
+ Show options:
35
+
36
+ ```
37
+ $ markedly -h
38
+ ```
39
+
40
+ ## Special Thanks
41
+
42
+ Github style css is originally from:
43
+
44
+ * [Github Markdown CSS](https://gist.github.com/andyferra/2554919) of Andy Ferra
45
+ * [pytgments-style-github](https://pypi.python.org/pypi/pygments-style-github) of Hugo Maia Vieira
46
+
47
+ ## Contributing
48
+
49
+ 1. Fork it
50
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
51
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
52
+ 4. Push to the branch (`git push origin my-new-feature`)
53
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,24 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <%- unless css.nil? -%>
5
+ <link rel="stylesheet" href="<%= css.href %>">
6
+ <%- end -%>
7
+ <script>
8
+ var ws = new WebSocket('ws://localhost:<%= port %>');
9
+
10
+ ws.onmessage = function(event) {
11
+ if (event.data == 'updated') {
12
+ window.location.reload();
13
+ }
14
+ };
15
+
16
+ window.onunload = function() {
17
+ ws.close();
18
+ };
19
+ </script>
20
+ </head>
21
+ <body>
22
+ <%= body %>
23
+ </body>
24
+ </html>
data/assets/github.css ADDED
@@ -0,0 +1,339 @@
1
+ body {
2
+ font-family: Helvetica, arial, sans-serif;
3
+ font-size: 14px;
4
+ line-height: 1.6;
5
+ padding-top: 10px;
6
+ padding-bottom: 10px;
7
+ background-color: white;
8
+ padding: 30px; }
9
+
10
+ body > *:first-child {
11
+ margin-top: 0 !important; }
12
+ body > *:last-child {
13
+ margin-bottom: 0 !important; }
14
+
15
+ a {
16
+ color: #4183C4; }
17
+ a.absent {
18
+ color: #cc0000; }
19
+ a.anchor {
20
+ display: block;
21
+ padding-left: 30px;
22
+ margin-left: -30px;
23
+ cursor: pointer;
24
+ position: absolute;
25
+ top: 0;
26
+ left: 0;
27
+ bottom: 0; }
28
+
29
+ h1, h2, h3, h4, h5, h6 {
30
+ margin: 20px 0 10px;
31
+ padding: 0;
32
+ font-weight: bold;
33
+ -webkit-font-smoothing: antialiased;
34
+ cursor: text;
35
+ position: relative; }
36
+
37
+ h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
38
+ background: url("https://github.com/images/modules/styleguide/para.png") no-repeat 10px center;
39
+ text-decoration: none; }
40
+
41
+ h1 tt, h1 code {
42
+ font-size: inherit; }
43
+
44
+ h2 tt, h2 code {
45
+ font-size: inherit; }
46
+
47
+ h3 tt, h3 code {
48
+ font-size: inherit; }
49
+
50
+ h4 tt, h4 code {
51
+ font-size: inherit; }
52
+
53
+ h5 tt, h5 code {
54
+ font-size: inherit; }
55
+
56
+ h6 tt, h6 code {
57
+ font-size: inherit; }
58
+
59
+ h1 {
60
+ font-size: 28px;
61
+ color: black; }
62
+
63
+ h2 {
64
+ font-size: 24px;
65
+ border-bottom: 1px solid #cccccc;
66
+ color: black; }
67
+
68
+ h3 {
69
+ font-size: 18px; }
70
+
71
+ h4 {
72
+ font-size: 16px; }
73
+
74
+ h5 {
75
+ font-size: 14px; }
76
+
77
+ h6 {
78
+ color: #777777;
79
+ font-size: 14px; }
80
+
81
+ p, blockquote, ul, ol, dl, li, table, pre {
82
+ margin: 15px 0; }
83
+
84
+ hr {
85
+ background: transparent url("https://github.com/images/modules/pulls/dirty-shade.png") repeat-x 0 0;
86
+ border: 0 none;
87
+ color: #cccccc;
88
+ height: 4px;
89
+ padding: 0; }
90
+
91
+ body > h2:first-child {
92
+ margin-top: 0;
93
+ padding-top: 0; }
94
+ body > h1:first-child {
95
+ margin-top: 0;
96
+ padding-top: 0; }
97
+ body > h1:first-child + h2 {
98
+ margin-top: 0;
99
+ padding-top: 0; }
100
+ body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
101
+ margin-top: 0;
102
+ padding-top: 0; }
103
+
104
+ a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
105
+ margin-top: 0;
106
+ padding-top: 0; }
107
+
108
+ h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
109
+ margin-top: 0; }
110
+
111
+ li p.first {
112
+ display: inline-block; }
113
+
114
+ ul, ol {
115
+ padding-left: 30px; }
116
+
117
+ ul :first-child, ol :first-child {
118
+ margin-top: 0; }
119
+
120
+ ul :last-child, ol :last-child {
121
+ margin-bottom: 0; }
122
+
123
+ dl {
124
+ padding: 0; }
125
+ dl dt {
126
+ font-size: 14px;
127
+ font-weight: bold;
128
+ font-style: italic;
129
+ padding: 0;
130
+ margin: 15px 0 5px; }
131
+ dl dt:first-child {
132
+ padding: 0; }
133
+ dl dt > :first-child {
134
+ margin-top: 0; }
135
+ dl dt > :last-child {
136
+ margin-bottom: 0; }
137
+ dl dd {
138
+ margin: 0 0 15px;
139
+ padding: 0 15px; }
140
+ dl dd > :first-child {
141
+ margin-top: 0; }
142
+ dl dd > :last-child {
143
+ margin-bottom: 0; }
144
+
145
+ blockquote {
146
+ border-left: 4px solid #dddddd;
147
+ padding: 0 15px;
148
+ color: #777777; }
149
+ blockquote > :first-child {
150
+ margin-top: 0; }
151
+ blockquote > :last-child {
152
+ margin-bottom: 0; }
153
+
154
+ table {
155
+ padding: 0; }
156
+ table tr {
157
+ border-top: 1px solid #cccccc;
158
+ background-color: white;
159
+ margin: 0;
160
+ padding: 0; }
161
+ table tr:nth-child(2n) {
162
+ background-color: #f8f8f8; }
163
+ table tr th {
164
+ font-weight: bold;
165
+ border: 1px solid #cccccc;
166
+ text-align: left;
167
+ margin: 0;
168
+ padding: 6px 13px; }
169
+ table tr td {
170
+ border: 1px solid #cccccc;
171
+ text-align: left;
172
+ margin: 0;
173
+ padding: 6px 13px; }
174
+ table tr th :first-child, table tr td :first-child {
175
+ margin-top: 0; }
176
+ table tr th :last-child, table tr td :last-child {
177
+ margin-bottom: 0; }
178
+
179
+ img {
180
+ max-width: 100%; }
181
+
182
+ span.frame {
183
+ display: block;
184
+ overflow: hidden; }
185
+ span.frame > span {
186
+ border: 1px solid #dddddd;
187
+ display: block;
188
+ float: left;
189
+ overflow: hidden;
190
+ margin: 13px 0 0;
191
+ padding: 7px;
192
+ width: auto; }
193
+ span.frame span img {
194
+ display: block;
195
+ float: left; }
196
+ span.frame span span {
197
+ clear: both;
198
+ color: #333333;
199
+ display: block;
200
+ padding: 5px 0 0; }
201
+ span.align-center {
202
+ display: block;
203
+ overflow: hidden;
204
+ clear: both; }
205
+ span.align-center > span {
206
+ display: block;
207
+ overflow: hidden;
208
+ margin: 13px auto 0;
209
+ text-align: center; }
210
+ span.align-center span img {
211
+ margin: 0 auto;
212
+ text-align: center; }
213
+ span.align-right {
214
+ display: block;
215
+ overflow: hidden;
216
+ clear: both; }
217
+ span.align-right > span {
218
+ display: block;
219
+ overflow: hidden;
220
+ margin: 13px 0 0;
221
+ text-align: right; }
222
+ span.align-right span img {
223
+ margin: 0;
224
+ text-align: right; }
225
+ span.float-left {
226
+ display: block;
227
+ margin-right: 13px;
228
+ overflow: hidden;
229
+ float: left; }
230
+ span.float-left span {
231
+ margin: 13px 0 0; }
232
+ span.float-right {
233
+ display: block;
234
+ margin-left: 13px;
235
+ overflow: hidden;
236
+ float: right; }
237
+ span.float-right > span {
238
+ display: block;
239
+ overflow: hidden;
240
+ margin: 13px auto 0;
241
+ text-align: right; }
242
+
243
+ code, tt {
244
+ margin: 0 2px;
245
+ padding: 0 5px;
246
+ white-space: nowrap;
247
+ border: 1px solid #eaeaea;
248
+ background-color: #f8f8f8;
249
+ border-radius: 3px; }
250
+
251
+ pre code {
252
+ margin: 0;
253
+ padding: 0;
254
+ white-space: pre;
255
+ border: none;
256
+ background: transparent; }
257
+
258
+ .highlight pre {
259
+ background-color: #f8f8f8;
260
+ border: 1px solid #cccccc;
261
+ font-size: 13px;
262
+ line-height: 19px;
263
+ overflow: auto;
264
+ padding: 6px 10px;
265
+ border-radius: 3px; }
266
+
267
+ pre {
268
+ background-color: #f8f8f8;
269
+ border: 1px solid #cccccc;
270
+ font-size: 13px;
271
+ line-height: 19px;
272
+ overflow: auto;
273
+ padding: 6px 10px;
274
+ border-radius: 3px; }
275
+ pre code, pre tt {
276
+ background-color: transparent;
277
+ border: none; }
278
+
279
+ .hll { background-color: #ffffcc }
280
+ .c { color: #999988; font-style: italic } /* Comment */
281
+ .err { color: #a61717; background-color: #e3d2d2 } /* Error */
282
+ .k { color: #000000; font-weight: bold } /* Keyword */
283
+ .o { color: #000000; font-weight: bold } /* Operator */
284
+ .cm { color: #999988; font-style: italic } /* Comment.Multiline */
285
+ .cp { color: #999999; font-weight: bold; font-style: italic } /* Comment.Preproc */
286
+ .c1 { color: #999988; font-style: italic } /* Comment.Single */
287
+ .cs { color: #999999; font-weight: bold; font-style: italic } /* Comment.Special */
288
+ .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */
289
+ .ge { color: #000000; font-style: italic } /* Generic.Emph */
290
+ .gr { color: #aa0000 } /* Generic.Error */
291
+ .gh { color: #999999 } /* Generic.Heading */
292
+ .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */
293
+ .go { color: #888888 } /* Generic.Output */
294
+ .gp { color: #555555 } /* Generic.Prompt */
295
+ .gs { font-weight: bold } /* Generic.Strong */
296
+ .gu { color: #aaaaaa } /* Generic.Subheading */
297
+ .gt { color: #aa0000 } /* Generic.Traceback */
298
+ .kc { color: #000000; font-weight: bold } /* Keyword.Constant */
299
+ .kd { color: #000000; font-weight: bold } /* Keyword.Declaration */
300
+ .kn { color: #000000; font-weight: bold } /* Keyword.Namespace */
301
+ .kp { color: #000000; font-weight: bold } /* Keyword.Pseudo */
302
+ .kr { color: #000000; font-weight: bold } /* Keyword.Reserved */
303
+ .kt { color: #445588; font-weight: bold } /* Keyword.Type */
304
+ .m { color: #009999 } /* Literal.Number */
305
+ .s { color: #dd1144 } /* Literal.String */
306
+ .na { color: #008080 } /* Name.Attribute */
307
+ .nb { color: #0086B3 } /* Name.Builtin */
308
+ .nc { color: #445588; font-weight: bold } /* Name.Class */
309
+ .no { color: #008080 } /* Name.Constant */
310
+ .nd { color: #3c5d5d; font-weight: bold } /* Name.Decorator */
311
+ .ni { color: #800080 } /* Name.Entity */
312
+ .ne { color: #990000; font-weight: bold } /* Name.Exception */
313
+ .nf { color: #990000; font-weight: bold } /* Name.Function */
314
+ .nl { color: #990000; font-weight: bold } /* Name.Label */
315
+ .nn { color: #555555 } /* Name.Namespace */
316
+ .nt { color: #000080 } /* Name.Tag */
317
+ .nv { color: #008080 } /* Name.Variable */
318
+ .ow { color: #000000; font-weight: bold } /* Operator.Word */
319
+ .w { color: #bbbbbb } /* Text.Whitespace */
320
+ .mf { color: #009999 } /* Literal.Number.Float */
321
+ .mh { color: #009999 } /* Literal.Number.Hex */
322
+ .mi { color: #009999 } /* Literal.Number.Integer */
323
+ .mo { color: #009999 } /* Literal.Number.Oct */
324
+ .sb { color: #dd1144 } /* Literal.String.Backtick */
325
+ .sc { color: #dd1144 } /* Literal.String.Char */
326
+ .sd { color: #dd1144 } /* Literal.String.Doc */
327
+ .s2 { color: #dd1144 } /* Literal.String.Double */
328
+ .se { color: #dd1144 } /* Literal.String.Escape */
329
+ .sh { color: #dd1144 } /* Literal.String.Heredoc */
330
+ .si { color: #dd1144 } /* Literal.String.Interpol */
331
+ .sx { color: #dd1144 } /* Literal.String.Other */
332
+ .sr { color: #009926 } /* Literal.String.Regex */
333
+ .s1 { color: #dd1144 } /* Literal.String.Single */
334
+ .ss { color: #990073 } /* Literal.String.Symbol */
335
+ .bp { color: #999999 } /* Name.Builtin.Pseudo */
336
+ .vc { color: #008080 } /* Name.Variable.Class */
337
+ .vg { color: #008080 } /* Name.Variable.Global */
338
+ .vi { color: #008080 } /* Name.Variable.Instance */
339
+ .il { color: #009999 } /* Literal.Number.Integer.Long */
data/bin/markedly ADDED
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'markedly'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ default_options = {
8
+ css: 'github',
9
+ debug: false,
10
+ interval: 5,
11
+ port: Markedly::Document::DEFAULT_PORT,
12
+ parse: Markedly::Markdown::DEFAULT_EXTENSIONS,
13
+ render: Markedly::Markdown::DEFAULT_RENDERER_OPTIONS,
14
+ }
15
+
16
+ ARGV.options do |parser|
17
+ Version = Markedly::VERSION
18
+
19
+ parser.banner = 'Usage: markedly [redcarpet options] [markedly options] document.md'
20
+
21
+ parser.separator ''
22
+ parser.separator 'Redcarpet Options:'
23
+
24
+ Markedly::Markdown::AVAILABLE_EXTENSIONS.each do |ext|
25
+ parser.on('--parse-' + ext.to_s.gsub('_', '-')) { (options[:parse] ||= {})[ext] = true }
26
+ end
27
+ Markedly::Markdown::RENDERER_OPTIONS.each do |render|
28
+ parser.on('--render-' + render.to_s.gsub('_', '-')) { (options[:render] ||= {})[render] = true }
29
+ end
30
+
31
+ parser.separator ''
32
+ parser.separator 'Markedly Options:'
33
+
34
+ parser.on('-c', '--css CSS',
35
+ "use predefined or custom CSS (default: %s)" % default_options[:css]) do |css|
36
+ options[:css] = css
37
+ end
38
+
39
+ parser.on('-d','--debug','output debug message') { options[:debug] = true }
40
+
41
+ parser.on('-i', '--interval INTERVAL', Integer,
42
+ "watch every INTERVAL seconds (default: %d)" % default_options[:interval]) do |interval|
43
+ options[:interval] = interval
44
+ end
45
+
46
+ parser.on('-p', '--port PORT', "use PORT (default: %d)" % default_options[:port]) do |port|
47
+ options[:port] = port
48
+ end
49
+
50
+ parser.on('-h','--help','show this message') { puts parser.to_s; exit }
51
+ parser.on('-v','--version','show version') { puts parser.ver; exit }
52
+ end
53
+
54
+ begin
55
+ ARGV.permute!
56
+ rescue OptionParser::InvalidOption => e
57
+ warn e.message
58
+ abort ARGV.options.to_s
59
+ end
60
+
61
+ abort ARGV.options.to_s if ARGV.empty?
62
+
63
+ source = ARGV.first
64
+ options = default_options.merge(options)
65
+
66
+ abort "%s not found" % source unless File.file?(source)
67
+
68
+ Markedly::App.run!(source, options)
@@ -0,0 +1,64 @@
1
+ # coding: utf-8
2
+ require 'eventmachine'
3
+ require 'em-websocket'
4
+ require 'launchy'
5
+
6
+ module Markedly
7
+ class App
8
+ def self.run!(source, options = {})
9
+ App.new(source, options).run!
10
+ end
11
+
12
+ def initialize(source, options = {})
13
+ @document = Document.new(source, options)
14
+ @options = options
15
+ end
16
+
17
+ def run!
18
+ @document.convert
19
+ puts "Open '%s'" % @document.uri
20
+ Launchy.open(@document.uri)
21
+
22
+ EM.run do
23
+
24
+ @channel = EM::Channel.new
25
+
26
+ debug "Start WebSocket Server (ws://localhost:#{@document.port})"
27
+ EM::WebSocket.start(host: '0.0.0.0', port: @document.port) do |ws|
28
+ ws.onopen do
29
+ subscriber = @channel.subscribe do |message|
30
+ ws.send(message)
31
+ end
32
+
33
+ debug "client #{subscriber} connected"
34
+
35
+ ws.onclose do
36
+ @channel.unsubscribe(subscriber)
37
+ debug "client #{subscriber} closed"
38
+ end
39
+ end
40
+ end
41
+
42
+ EM.defer do
43
+ mtime = last_mtime = File.mtime(@document.source)
44
+ loop do
45
+ mtime = File.mtime(@document.source)
46
+ if mtime > last_mtime
47
+ puts "file modified"
48
+ @document.convert
49
+ @channel.push 'updated'
50
+ end
51
+ last_mtime = mtime
52
+ sleep @options[:interval]
53
+ end
54
+ end
55
+ end
56
+ end
57
+
58
+ private
59
+
60
+ def debug(message)
61
+ puts message if @options[:debug]
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,54 @@
1
+ # coding: utf-8
2
+ require 'addressable/uri'
3
+
4
+ module Markedly
5
+
6
+ class Asset
7
+ ASSETS_DIR = File.expand_path('../../../assets', __FILE__)
8
+
9
+ attr_reader :name
10
+ attr_reader :path
11
+ attr_reader :href
12
+
13
+ def initialize(path)
14
+ @path = path
15
+ ext = File.extname(@path)
16
+ @name = File.basename(path, ext)
17
+ prepare_href
18
+ end
19
+
20
+ def remote?
21
+ @remote
22
+ end
23
+
24
+ private
25
+
26
+ def prepare_href
27
+ if @path =~ /^https?:\/\//
28
+ @href = @path
29
+ @remote = true
30
+ else
31
+ @href = Addressable::URI.convert_path(@path).to_s
32
+ @remote = false
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ class CssAsset < Asset
39
+
40
+ def self.assets
41
+ Dir["#{Asset::ASSETS_DIR}/*.css"].map {|path| CssAsset.new(path) }
42
+ end
43
+
44
+ def self.asset(name)
45
+ assets.find { |asset| asset.name == name }
46
+ end
47
+
48
+ def initialize(path)
49
+ super
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,75 @@
1
+ # coding: utf-8
2
+ require 'erb'
3
+ require 'tempfile'
4
+ require 'addressable/uri'
5
+
6
+ module Markedly
7
+
8
+ class Document
9
+ DEFAULT_PORT = 8080
10
+
11
+ attr_reader :source
12
+ attr_reader :destination
13
+ attr_reader :path
14
+ attr_reader :uri
15
+ attr_reader :css
16
+ attr_reader :port
17
+
18
+ def initialize(source, options = {})
19
+ @source = File.expand_path(source)
20
+ @path = File.basename(source)
21
+ @port = options[:port] || DEFAULT_PORT
22
+ @markdown = Markdown.new(options[:parse], options[:render])
23
+ initialize_template
24
+ initialize_assets(options)
25
+ initialize_destination
26
+ end
27
+
28
+ def convert
29
+ @dest_file.rewind
30
+ @dest_file.write render(body_html, @css, @port)
31
+ @dest_file.truncate(@dest_file.pos)
32
+ @dest_file.flush
33
+ end
34
+
35
+ private
36
+
37
+ def initialize_assets(options = {})
38
+ css = options[:css]
39
+ if css.nil?
40
+ @css = nil
41
+ elsif File.file?(css)
42
+ @css = CssAsset.new(File.expand_path(css))
43
+ elsif css =~ /^https?:\/\//
44
+ @css = CssAsset.new(css)
45
+ else
46
+ @css = CssAsset.asset(css)
47
+ end
48
+ end
49
+
50
+ def initialize_template(options = {})
51
+ asset_dir = File.expand_path('../../../assets', __FILE__)
52
+ @template = File.join(asset_dir, 'document.html.erb')
53
+ end
54
+
55
+ def initialize_destination
56
+ basename = [ File.basename(self.source), '.html' ]
57
+ @dest_file = Tempfile.open(basename)
58
+ @destination = @dest_file.path
59
+ @uri = Addressable::URI.convert_path(@destination).to_s
60
+ end
61
+
62
+ def body_html
63
+ content = File.read(@source)
64
+ @markdown.render(content)
65
+ end
66
+
67
+ def render(body, css, port)
68
+ erb = ERB.new(File.read(@template), nil, '-')
69
+ erb.result(binding)
70
+ end
71
+
72
+ end
73
+
74
+ end
75
+
@@ -0,0 +1,87 @@
1
+ # coding: utf-8
2
+ require 'redcarpet'
3
+
4
+ module Markedly
5
+
6
+ class Markdown
7
+
8
+ AVAILABLE_EXTENSIONS = [
9
+ :no_intra_emphasis,
10
+ :tables,
11
+ :fenced_code_blocks,
12
+ :autolink,
13
+ :disable_indented_code_blocks,
14
+ :strikethrough,
15
+ :lax_spacing,
16
+ :space_after_headers,
17
+ :superscript,
18
+ :underline,
19
+ :highlight,
20
+ ]
21
+
22
+ DEFAULT_EXTENSIONS = {
23
+ tables: true,
24
+ fenced_code_blocks: true,
25
+ autolink: true,
26
+ }
27
+
28
+ RENDERER_OPTIONS = [
29
+ :filter_html,
30
+ :no_images,
31
+ :no_links,
32
+ :no_styles,
33
+ :safe_links_only,
34
+ :with_toc_data,
35
+ :hard_wrap,
36
+ :xhtml,
37
+ :prettify,
38
+ :link_attributes
39
+ ]
40
+
41
+ DEFAULT_RENDERER_OPTIONS = {}
42
+
43
+ module WithPygments
44
+ require 'pygments'
45
+
46
+ def block_code(code, language)
47
+ Pygments.highlight(code, lexer: language)
48
+ end
49
+ end
50
+
51
+ def initialize(extensions = {}, renderer_options = {})
52
+ extensions ||= DEFAULT_EXTENSIONS
53
+ renderer_options ||= DEFAULT_RENDERER_OPTIONS
54
+ renderer_class = Class.new(Redcarpet::Render::HTML) do
55
+ include WithPygments if Markdown.python_available?
56
+ end
57
+ renderer = renderer_class.new(renderer_options)
58
+ @markdown = Redcarpet::Markdown.new(renderer, extensions)
59
+ end
60
+
61
+ def render(text)
62
+ @markdown.render(text)
63
+ end
64
+
65
+ private
66
+
67
+ def self.python_available?
68
+ executable?('python2') || executable?('python')
69
+ end
70
+
71
+ def self.executable?(command)
72
+ if RUBY_PLATFORM =~ /mswin|mingw/
73
+ exts = ENV['PATHEXT'].split(';')
74
+ exts.map {|ext| command + ext }.each do |exe|
75
+ ENV['PATH'].gsub('\\', '/').split(';').map {|dir| File.join(dir, exe) }.each do |path|
76
+ return true if File.executable?(path)
77
+ end
78
+ end
79
+ return false
80
+ end
81
+
82
+ system("which #{command} > /dev/null 2>&1")
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,3 @@
1
+ module Markedly
2
+ VERSION = "0.0.1"
3
+ end
data/lib/markedly.rb ADDED
@@ -0,0 +1,5 @@
1
+ require "markedly/version"
2
+ require 'markedly/markdown'
3
+ require 'markedly/asset'
4
+ require 'markedly/document'
5
+ require 'markedly/app'
data/markedly.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'markedly/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "markedly"
8
+ spec.version = Markedly::VERSION
9
+ spec.authors = ["Yoshihiro Hara"]
10
+ spec.description = %q{Markedly offers live preview feature to your favorite browser.}
11
+ spec.summary = %q{A live-reloadable markdown previewer}
12
+ spec.homepage = "https://github.com/hara/markedly"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'addressable'
21
+ spec.add_dependency 'em-websocket'
22
+ spec.add_dependency 'launchy'
23
+ spec.add_dependency 'redcarpet'
24
+ spec.add_dependency 'pygments.rb'
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "rspec"
28
+ spec.add_development_dependency "coveralls"
29
+ end
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <link rel="stylesheet" href="<%= css %>">
5
+ <script>
6
+ var ws = new WebSocket('ws://localhost:8080');
7
+
8
+ ws.onmessage = function(event) {
9
+ if (event.data == 'updated') {
10
+ window.location.reload();
11
+ }
12
+ };
13
+
14
+ window.onunload = function() {
15
+ ws.close();
16
+ };
17
+ </script>
18
+ </head>
19
+ <body>
20
+ <h1>Markedly</h1>
21
+
22
+ </body>
23
+ </html>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <script>
5
+ var ws = new WebSocket('ws://localhost:8080');
6
+
7
+ ws.onmessage = function(event) {
8
+ if (event.data == 'updated') {
9
+ window.location.reload();
10
+ }
11
+ };
12
+
13
+ window.onunload = function() {
14
+ ws.close();
15
+ };
16
+ </script>
17
+ </head>
18
+ <body>
19
+ <h1>Markedly</h1>
20
+
21
+ </body>
22
+ </html>
@@ -0,0 +1,2 @@
1
+ Markedly
2
+ ========
@@ -0,0 +1,106 @@
1
+ # coding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ module Markedly
5
+ describe Asset do
6
+ end
7
+
8
+ describe CssAsset do
9
+ shared_context 'remote' do
10
+ let(:url) { 'http://example.com/css/main.css' }
11
+ let(:css) { CssAsset.new(url) }
12
+ end
13
+
14
+ shared_context 'predefined' do
15
+ let(:name) { 'github' }
16
+ let(:css) { CssAsset.asset(name) }
17
+ end
18
+
19
+ shared_context 'custom' do
20
+ let(:path) { docs('custom.css') }
21
+ let(:css) { CssAsset.new(path) }
22
+ end
23
+
24
+ describe '#name' do
25
+ context 'when remote css' do
26
+ include_context 'remote'
27
+ it { expect(css.name).to eq('main') }
28
+ end
29
+
30
+ context 'when predefined css' do
31
+ include_context 'predefined'
32
+ it { expect(css.name).to eq('github') }
33
+ end
34
+
35
+ context 'when custom css' do
36
+ include_context 'custom'
37
+ it { expect(css.name).to eq('custom') }
38
+ end
39
+ end
40
+
41
+ describe '#path' do
42
+ context 'when remote css' do
43
+ include_context 'remote'
44
+ it { expect(css.path).to eq(url) }
45
+ end
46
+
47
+ context 'when predefined css' do
48
+ include_context 'predefined'
49
+ it { expect(css.path).to eq(assets('github.css')) }
50
+ end
51
+
52
+ context 'when custom css' do
53
+ include_context 'custom'
54
+ it { expect(css.path).to eq(docs('custom.css')) }
55
+ end
56
+ end
57
+
58
+ describe '#href' do
59
+
60
+ context 'when remote css' do
61
+ include_context 'remote'
62
+ it 'returns http/https scheme url' do
63
+ expect(css.href).to eq(url)
64
+ end
65
+ end
66
+
67
+ context 'when predefined css' do
68
+ include_context 'predefined'
69
+ it 'returns file scheme url' do
70
+ expected = Addressable::URI.convert_path(assets('github.css')).to_s
71
+ expect(css.href).to eq(expected)
72
+ end
73
+ end
74
+
75
+ context 'when custom css' do
76
+ include_context 'custom'
77
+ it 'returns file scheme url' do
78
+ expected = Addressable::URI.convert_path(docs('custom.css')).to_s
79
+ expect(css.href).to eq(expected)
80
+ end
81
+ end
82
+ end
83
+
84
+ describe '#remote' do
85
+
86
+ context 'when remote css' do
87
+ include_context 'remote'
88
+ it { expect(css.remote?).to be_true }
89
+ end
90
+
91
+ context 'when predefined css' do
92
+ include_context 'predefined'
93
+ it { expect(css.remote?).to be_false }
94
+ end
95
+
96
+ context 'when custom css' do
97
+ include_context 'custom'
98
+ it { expect(css.remote?).to be_false }
99
+ end
100
+
101
+ end
102
+
103
+ end
104
+
105
+ end
106
+
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ require_relative '../spec_helper'
3
+
4
+ module Markedly
5
+ describe Document do
6
+
7
+ describe '#initialize' do
8
+ let(:document) { Document.new(docs('markdown.md')) }
9
+
10
+ it 'creates destination file' do
11
+ expect(File.exist?(document.destination)).to be_true
12
+ end
13
+
14
+ end
15
+
16
+ describe '#path' do
17
+ let(:document) { Document.new(docs('markdown.md')) }
18
+
19
+ it 'returns the URL path' do
20
+ expect(document.path).to eq('markdown.md')
21
+ end
22
+ end
23
+
24
+ describe '#convert' do
25
+ let(:source) { docs('markdown.md') }
26
+
27
+ context 'when does not have css' do
28
+ let(:destination) { docs('markdown.html') }
29
+ let(:document) { Document.new(source) }
30
+
31
+ it 'outputs HTML' do
32
+ document.convert
33
+ expected = File.read(destination)
34
+ expect(File.read(document.destination)).to eq(expected)
35
+ end
36
+ end
37
+
38
+ context 'when has css' do
39
+ let(:destination) { docs('markdown.github.html.erb') }
40
+ let(:document) { Document.new(source, css: 'github') }
41
+
42
+ it 'outputs HTML' do
43
+ document.convert
44
+ css = CssAsset.asset('github').href
45
+ expected = ERB.new(File.read(destination)).result(binding)
46
+ expect(File.read(document.destination)).to eq(expected)
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
55
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe Markedly do
4
+ it 'should have a version number' do
5
+ Markedly::VERSION.should_not be_nil
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'simplecov'
5
+ SimpleCov.start do
6
+ add_filter "/assets/"
7
+ add_filter "/spec/doc/"
8
+ add_filter "/vendor/"
9
+ end
10
+
11
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
12
+ require 'markedly'
13
+
14
+ def docs(name)
15
+ File.expand_path(name, File.join(File.dirname(__FILE__), 'docs'))
16
+ end
17
+
18
+ def assets(name)
19
+ File.expand_path(name, File.join(File.dirname(__FILE__), '../assets'))
20
+ end
21
+
metadata ADDED
@@ -0,0 +1,203 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: markedly
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Yoshihiro Hara
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: addressable
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: em-websocket
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: launchy
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pygments.rb
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: bundler
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '1.3'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '1.3'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: coveralls
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ description: Markedly offers live preview feature to your favorite browser.
140
+ email:
141
+ executables:
142
+ - markedly
143
+ extensions: []
144
+ extra_rdoc_files: []
145
+ files:
146
+ - .gitignore
147
+ - .rspec
148
+ - .travis.yml
149
+ - Gemfile
150
+ - LICENSE.txt
151
+ - README.md
152
+ - Rakefile
153
+ - assets/document.html.erb
154
+ - assets/github.css
155
+ - bin/markedly
156
+ - lib/markedly.rb
157
+ - lib/markedly/app.rb
158
+ - lib/markedly/asset.rb
159
+ - lib/markedly/document.rb
160
+ - lib/markedly/markdown.rb
161
+ - lib/markedly/version.rb
162
+ - markedly.gemspec
163
+ - spec/docs/custom.css
164
+ - spec/docs/markdown.github.html.erb
165
+ - spec/docs/markdown.html
166
+ - spec/docs/markdown.md
167
+ - spec/markedly/asset_spec.rb
168
+ - spec/markedly/document_spec.rb
169
+ - spec/markedly_spec.rb
170
+ - spec/spec_helper.rb
171
+ homepage: https://github.com/hara/markedly
172
+ licenses:
173
+ - MIT
174
+ metadata: {}
175
+ post_install_message:
176
+ rdoc_options: []
177
+ require_paths:
178
+ - lib
179
+ required_ruby_version: !ruby/object:Gem::Requirement
180
+ requirements:
181
+ - - '>='
182
+ - !ruby/object:Gem::Version
183
+ version: '0'
184
+ required_rubygems_version: !ruby/object:Gem::Requirement
185
+ requirements:
186
+ - - '>='
187
+ - !ruby/object:Gem::Version
188
+ version: '0'
189
+ requirements: []
190
+ rubyforge_project:
191
+ rubygems_version: 2.0.3
192
+ signing_key:
193
+ specification_version: 4
194
+ summary: A live-reloadable markdown previewer
195
+ test_files:
196
+ - spec/docs/custom.css
197
+ - spec/docs/markdown.github.html.erb
198
+ - spec/docs/markdown.html
199
+ - spec/docs/markdown.md
200
+ - spec/markedly/asset_spec.rb
201
+ - spec/markedly/document_spec.rb
202
+ - spec/markedly_spec.rb
203
+ - spec/spec_helper.rb