github-markdown-server 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5f1f3e1b6c0b783128c49522e43ed9a0adad443c
4
+ data.tar.gz: 9b81373d11efa51e007e87e495ad1a65fecc640a
5
+ SHA512:
6
+ metadata.gz: 6c6de876abe5323c3fcb788827e2a76c9c244da3afa336ea5fdb649c5aeaee12bb28dec649a43563ed95df8724f3c733c27a7228631ca016eb63b3c01e884b95
7
+ data.tar.gz: a998e171e149d42f06941a553335791c7faeb70f20570616e61f87f18c7863678d12f3c36d3d1dd977ce3d8df18b968814e8d73f6e46a8e24ac143c51fb1b0f9
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 ark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # Github Markdown Server
2
+
3
+ Use your favorite editor to edit a markdown file, Run the server and open the file.
4
+ Saving in your editor updates instantly in the browser.
5
+
6
+ github-markdown-server is built on top of [github-markdown-preview](https://github.com/dmarcotte/github-markdown-preview).
7
+
8
+ ## Usage
9
+
10
+ ```shell
11
+ github-markdown-server README.md
12
+ ```
13
+
14
+ This will start a server serving in the current directory and all child directories and open a browser (on a mac) showing README.md converted to html. If you navigate to a directory name only child directories and .md files are shown in a directory listing. If there is a README.md file in that directory it will be appended to the directory listing.
15
+
16
+ ## Contrib
17
+
18
+ There is a [contrib](contrib/) directory with an emacs lisp file which will start a server and open a file for you. It'll keep track of which servers it has started and reuse an existing server if a file you want to preview is under that server's serving directory.
19
+
20
+ There is also a small python script (called `p`) which will work with files and produce paths and urls.
21
+
22
+ ## Contributing
23
+
24
+ Please feel free to send me pull requests! This is my first Ruby project and it always feels like my first time when I write Emacs Lisp.
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift("#{File.dirname(File.dirname(__FILE__))}/lib")
4
+
5
+ require 'github-markdown-server'
6
+ require 'optparse'
7
+
8
+ comment_mode = false
9
+ server_port = 8000
10
+ browser = true
11
+ directory = nil
12
+ opt_parser = OptionParser.new do |opt|
13
+ opt.banner = "Usage: github-markdown-server DIRECTORY"
14
+ opt.separator ""
15
+ opt.separator "Options"
16
+
17
+ opt.on("-b", "--[no-]browser", "don't open initial file in browser when you start a server") do |b|
18
+ browser = b
19
+ end
20
+
21
+ opt.on("-c", "--comment-mode", "renders a preview for Github comments/issues") do
22
+ comment_mode = true
23
+ end
24
+
25
+ opt.on("-d=", "--directory=", "start webserver listening on this port") do |dir|
26
+ directory = dir
27
+ end
28
+
29
+ opt.on("-p=", "--port=", "start webserver listening on this port") do |port|
30
+ server_port = port unless port.nil?
31
+ end
32
+
33
+ opt.on("-v", "--version", "print the version") do
34
+ $stdout.puts 'github-markdown-server version ' + GithubMarkdownServer::VERSION
35
+ Kernel.exit
36
+ end
37
+ end
38
+
39
+ opt_parser.parse!
40
+
41
+ file_name = ARGV.at(0) || '.'
42
+ directory = directory || file_name
43
+ options = {
44
+ :comment_mode => comment_mode,
45
+ :server_port => server_port,
46
+ :browser => browser,
47
+ :directory => directory,
48
+ :file_name => file_name,
49
+ }
50
+ p "go on #{server_port}"
51
+ GithubMarkdownServer::Server.new(options)
@@ -0,0 +1,73 @@
1
+ ;;; github-markdown-server -- start a server and open a markdown file in it
2
+ ;;; Created by: Alex K (wtwf.com) Thu Nov 27 02:48:00 2014
3
+
4
+ ;;; Commentary:
5
+
6
+ ;;; Code:
7
+
8
+ (require 'cl-lib)
9
+
10
+ (defvar github-markdown-servers '() "The servers we have running.")
11
+ (defvar github-markdown-server-port 7494 "The next available server port.")
12
+
13
+ (defun git-base (&optional file-name)
14
+ "Get the base of a git repository for FILE-NAME (or the current buffer)."
15
+ (interactive (buffer-file-name))
16
+ (shell-command-to-string (concat "p -t -g " (shell-quote-argument buffer-file-name))))
17
+
18
+ (defun github-markdown-serve (&optional file-name)
19
+ "Get the base of a git repository for FILE-NAME (or the current buffer)."
20
+ (interactive (list (buffer-file-name)))
21
+ (let ((base (git-base file-name)) (server nil) (server-running nil))
22
+ ;;;
23
+ (progn
24
+ (if (< (length base) 1) (setq base (directory-file-name file-name)))
25
+ ;; find a server that might still be running
26
+ (setq server (cl-reduce (lambda (a b)
27
+ (if (and (string= (car b) (substring base 0 (length (car b))))
28
+ (or (not (car a)) (< (length (car b)) (length (car a)))))
29
+ b a)) (cons nil github-markdown-servers)))
30
+ (if (and server
31
+ (progn
32
+ (message (concat "Checking if server running at" (prin1-to-string server)))
33
+ (setq server-running
34
+ (shell-command-to-string
35
+ (concat "lsof -n -i4TCP:" (number-to-string (cdr server)) " | grep LISTEN")))
36
+ (> (length server-running) 0)))
37
+ (progn
38
+ (message "reusing server")
39
+ (call-process-shell-command
40
+ (concat "open "
41
+ (shell-quote-argument
42
+ (concat "http://localhost:"
43
+ (number-to-string (cdr server))
44
+ (substring file-name (length (car server))))))))
45
+ (progn
46
+ (if server
47
+ (progn (message (concat "re-starting server on port " (number-to-string (cdr server)))))
48
+ (progn
49
+ (message "starting server"))
50
+ (setq server (cons base github-markdown-server-port))
51
+ ))
52
+ (call-process-shell-command (concat "github-markdown-server"
53
+ " --directory=" (shell-quote-argument (car server))
54
+ " --port=" (number-to-string (cdr server))
55
+ " " (shell-quote-argument file-name)) nil 0)
56
+ (if (not server)
57
+ (progn (add-to-list 'github-markdown-servers server)
58
+ (setq github-markdown-server-port (+ github-markdown-server-port 1))))))))
59
+
60
+ (defun view-file-in-browser (&optional file-name)
61
+ "Open up file on github or via a local markdown server.
62
+ FILE-NAME will be current buffer if not specified.
63
+ Prefix arg \[universal-argument] to not run local server."
64
+ (interactive (list (buffer-file-name)))
65
+ (if (and (not current-prefix-arg) (string-match "\.md$" file-name))
66
+ (github-markdown-serve file-name)
67
+ (call-process-shell-command
68
+ (concat "open "
69
+ (shell-quote-argument
70
+ (shell-command-to-string (concat "p -u -t " (shell-quote-argument file-name))))) nil 0)))
71
+
72
+ (provide 'github-markdown-server)
73
+ ;;; github-markdown-server ends here
data/contrib/p ADDED
@@ -0,0 +1,116 @@
1
+ #!/usr/bin/python
2
+ # Created by: Alex K (wtwf.com)Tue Dec 7 16:50:41 2004
3
+ # Last Modified: Time-stamp: <Thu Nov 27 03:08:53 PST 2014>
4
+
5
+ """
6
+ Turn a path into an absolute path or url.
7
+
8
+ -g, --git print out the root directory of this git repository
9
+ -h, --help print out this message
10
+ -p, --path show a full absolute path to the file
11
+ -t, --trim do not append a newline to the file
12
+ -u, --url show a url to the file (https://github.com/... or file:///)
13
+ -s, --server show a utl to a local github-markdown-server
14
+ --port use this port for github-markdown-server url (default 7474)
15
+ --host use this host for github-markdown-server url (default localhost)
16
+ """
17
+
18
+ __author__ = 'wtwf.com (Alex K)'
19
+
20
+ import getopt
21
+ import logging
22
+ import os
23
+ import re
24
+ import sys
25
+
26
+ def Usage(code, msg=''):
27
+ """Show a usage message."""
28
+ if code:
29
+ fd = sys.stderr
30
+ else:
31
+ fd = sys.stdout
32
+ PROGRAM = os.path.basename(sys.argv[0]) # pylint: disable=invalid-name,unused-variable
33
+ print >> fd, __doc__ % locals()
34
+ if msg:
35
+ print >> fd, msg
36
+ sys.exit(code)
37
+
38
+ def Main():
39
+ """Run."""
40
+ logging.basicConfig()
41
+ logging.getLogger().setLevel(logging.DEBUG)
42
+ try:
43
+ opts, args = getopt.getopt(sys.argv[1:], 'ghpstu',
44
+ 'git,help,host=,path,port=,server,trim,url'.split(','))
45
+ except getopt.error, msg:
46
+ Usage(1, msg)
47
+
48
+ if not args:
49
+ Usage(1)
50
+
51
+ output = 'path'
52
+ trim = False
53
+ port = 7474
54
+ host = 'localhost'
55
+ for opt, arg in opts:
56
+ if opt in ('-g', '--git'):
57
+ output = 'git'
58
+ if opt in ('-h', '--help'):
59
+ Usage(0)
60
+ if opt in ('--host',):
61
+ host = arg
62
+ if opt in ('-p', '--path'):
63
+ output = 'path'
64
+ if opt in ('--port',):
65
+ port = int(arg)
66
+ if opt in ('-s', '--server'):
67
+ output = 'github-markdown-server'
68
+ if opt in ('-t', '--trim'):
69
+ trim = True
70
+ if opt in ('-u', '--url'):
71
+ output = 'url'
72
+
73
+ for arg in args:
74
+ arg = os.path.abspath(arg)
75
+ if output != 'path':
76
+ arg = ConvertToUrl(arg, output, port, host)
77
+ if trim:
78
+ sys.stdout.write(arg) # for python 3 we could do print(arg, end='',flush=True)
79
+ else:
80
+ print arg
81
+
82
+ def ConvertToUrl(arg, output, port, host):
83
+ """Try to turn this into a url."""
84
+ directory = arg
85
+ base_url = None
86
+ while directory != '/' and not base_url:
87
+ directory = os.path.dirname(directory)
88
+ config_filename = os.path.join(directory, '.git', 'config')
89
+ if os.path.exists(config_filename):
90
+ if output == 'git':
91
+ return directory
92
+ for line in open(config_filename):
93
+ match = re.search(r'url = git@github.com:(.*)\.git', line)
94
+ if match:
95
+ base_url = 'https://github.com/%s/blob/master/' % match.group(1)
96
+ break
97
+
98
+ if output == 'git':
99
+ return ''
100
+ if output == 'github-markdown-server':
101
+ if base_url:
102
+ basename = arg[len(directory) + 1:]
103
+ arg = 'http://%s:%d/%s' % (host, port, basename)
104
+ else:
105
+ basename = os.path.basename(arg)
106
+ arg = 'http://%s:%d/%s' % (host, port, basename)
107
+ else:
108
+ if base_url:
109
+ arg = base_url + arg[len(directory) + 1:]
110
+ else:
111
+ arg = 'file://' + arg
112
+
113
+ return arg
114
+
115
+ if __name__ == '__main__':
116
+ Main()
Binary file
data/data/js/live.js ADDED
@@ -0,0 +1,233 @@
1
+ /*
2
+ Live.js - One script closer to Designing in the Browser
3
+ Written for Handcraft.com by Martin Kool (@mrtnkl).
4
+
5
+ Version 4.
6
+ Recent change: Made stylesheet and mimetype checks case insensitive.
7
+
8
+ http://livejs.com
9
+ http://livejs.com/license (MIT)
10
+ @livejs
11
+
12
+ Include live.js#css to monitor css changes only.
13
+ Include live.js#js to monitor js changes only.
14
+ Include live.js#html to monitor html changes only.
15
+ Mix and match to monitor a preferred combination such as live.js#html,css
16
+
17
+ By default, just include live.js to monitor all css, js and html changes.
18
+
19
+ Live.js can also be loaded as a bookmarklet. It is best to only use it for CSS then,
20
+ as a page reload due to a change in html or css would not re-include the bookmarklet.
21
+ To monitor CSS and be notified that it has loaded, include it as: live.js#css,notify
22
+ */
23
+ (function () {
24
+
25
+ var headers = { "Etag": 1, "Last-Modified": 1, "Content-Length": 1, "Content-Type": 1 },
26
+ resources = {},
27
+ pendingRequests = {},
28
+ currentLinkElements = {},
29
+ oldLinkElements = {},
30
+ interval = 1000,
31
+ loaded = false,
32
+ active = { "html": 1, "css": 1, "js": 1 };
33
+
34
+ var Live = {
35
+
36
+ // performs a cycle per interval
37
+ heartbeat: function () {
38
+ if (document.body) {
39
+ // make sure all resources are loaded on first activation
40
+ if (!loaded) Live.loadresources();
41
+ Live.checkForChanges();
42
+ }
43
+ setTimeout(Live.heartbeat, interval);
44
+ },
45
+
46
+ // loads all local css and js resources upon first activation
47
+ loadresources: function () {
48
+
49
+ // helper method to assert if a given url is local
50
+ function isLocal(url) {
51
+ var loc = document.location,
52
+ reg = new RegExp("^\\.|^\/(?!\/)|^[\\w]((?!://).)*$|" + loc.protocol + "//" + loc.host);
53
+ return url.match(reg);
54
+ }
55
+
56
+ // gather all resources
57
+ var scripts = document.getElementsByTagName("script"),
58
+ links = document.getElementsByTagName("link"),
59
+ uris = [];
60
+
61
+ // track local js urls
62
+ for (var i = 0; i < scripts.length; i++) {
63
+ var script = scripts[i], src = script.getAttribute("src");
64
+ if (src && isLocal(src))
65
+ uris.push(src);
66
+ if (src && src.match(/\blive.js#/)) {
67
+ for (var type in active)
68
+ active[type] = src.match("[#,|]" + type) != null
69
+ if (src.match("notify"))
70
+ alert("Live.js is loaded.");
71
+ }
72
+ }
73
+ if (!active.js) uris = [];
74
+ if (active.html) uris.push(document.location.href);
75
+
76
+ // track local css urls
77
+ for (var i = 0; i < links.length && active.css; i++) {
78
+ var link = links[i], rel = link.getAttribute("rel"), href = link.getAttribute("href", 2);
79
+ if (href && rel && rel.match(new RegExp("stylesheet", "i")) && isLocal(href)) {
80
+ uris.push(href);
81
+ currentLinkElements[href] = link;
82
+ }
83
+ }
84
+
85
+ // initialize the resources info
86
+ for (var i = 0; i < uris.length; i++) {
87
+ var url = uris[i];
88
+ Live.getHead(url, function (url, info) {
89
+ resources[url] = info;
90
+ });
91
+ }
92
+
93
+ // add rule for morphing between old and new css files
94
+ var head = document.getElementsByTagName("head")[0],
95
+ style = document.createElement("style"),
96
+ rule = "transition: all .3s ease-out;"
97
+ css = [".livejs-loading * { ", rule, " -webkit-", rule, "-moz-", rule, "-o-", rule, "}"].join('');
98
+ style.setAttribute("type", "text/css");
99
+ head.appendChild(style);
100
+ style.styleSheet ? style.styleSheet.cssText = css : style.appendChild(document.createTextNode(css));
101
+
102
+ // yep
103
+ loaded = true;
104
+ },
105
+
106
+ // check all tracking resources for changes
107
+ checkForChanges: function () {
108
+ for (var url in resources) {
109
+ if (pendingRequests[url])
110
+ continue;
111
+
112
+ Live.getHead(url, function (url, newInfo) {
113
+ var oldInfo = resources[url],
114
+ hasChanged = false;
115
+ resources[url] = newInfo;
116
+ for (var header in oldInfo) {
117
+ // do verification based on the header type
118
+ var oldValue = oldInfo[header],
119
+ newValue = newInfo[header],
120
+ contentType = newInfo["Content-Type"];
121
+ switch (header.toLowerCase()) {
122
+ case "etag":
123
+ if (!newValue) break;
124
+ // fall through to default
125
+ default:
126
+ hasChanged = oldValue != newValue;
127
+ break;
128
+ }
129
+ // if changed, act
130
+ if (hasChanged) {
131
+ Live.refreshResource(url, contentType);
132
+ break;
133
+ }
134
+ }
135
+ });
136
+ }
137
+ },
138
+
139
+ // act upon a changed url of certain content type
140
+ refreshResource: function (url, type) {
141
+ switch (type.toLowerCase()) {
142
+ // css files can be reloaded dynamically by replacing the link element
143
+ case "text/css":
144
+ var link = currentLinkElements[url],
145
+ html = document.body.parentNode,
146
+ head = link.parentNode,
147
+ next = link.nextSibling,
148
+ newLink = document.createElement("link");
149
+
150
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '') + ' livejs-loading';
151
+ newLink.setAttribute("type", "text/css");
152
+ newLink.setAttribute("rel", "stylesheet");
153
+ newLink.setAttribute("href", url + "?now=" + new Date() * 1);
154
+ next ? head.insertBefore(newLink, next) : head.appendChild(newLink);
155
+ currentLinkElements[url] = newLink;
156
+ oldLinkElements[url] = link;
157
+
158
+ // schedule removal of the old link
159
+ Live.removeoldLinkElements();
160
+ break;
161
+
162
+ // check if an html resource is our current url, then reload
163
+ case "text/html":
164
+ if (url != document.location.href)
165
+ return;
166
+
167
+ // local javascript changes cause a reload as well
168
+ case "text/javascript":
169
+ case "application/javascript":
170
+ case "application/x-javascript":
171
+ document.location.reload();
172
+ }
173
+ },
174
+
175
+ // removes the old stylesheet rules only once the new one has finished loading
176
+ removeoldLinkElements: function () {
177
+ var pending = 0;
178
+ for (var url in oldLinkElements) {
179
+ // if this sheet has any cssRules, delete the old link
180
+ try {
181
+ var link = currentLinkElements[url],
182
+ oldLink = oldLinkElements[url],
183
+ html = document.body.parentNode,
184
+ sheet = link.sheet || link.styleSheet,
185
+ rules = sheet.rules || sheet.cssRules;
186
+ if (rules.length >= 0) {
187
+ oldLink.parentNode.removeChild(oldLink);
188
+ delete oldLinkElements[url];
189
+ setTimeout(function () {
190
+ html.className = html.className.replace(/\s*livejs\-loading/gi, '');
191
+ }, 100);
192
+ }
193
+ } catch (e) {
194
+ pending++;
195
+ }
196
+ if (pending) setTimeout(Live.removeoldLinkElements, 50);
197
+ }
198
+ },
199
+
200
+ // performs a HEAD request and passes the header info to the given callback
201
+ getHead: function (url, callback) {
202
+ pendingRequests[url] = true;
203
+ var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XmlHttp");
204
+ xhr.open("HEAD", url, true);
205
+ xhr.onreadystatechange = function () {
206
+ delete pendingRequests[url];
207
+ if (xhr.readyState == 4 && xhr.status != 304) {
208
+ xhr.getAllResponseHeaders();
209
+ var info = {};
210
+ for (var h in headers) {
211
+ var value = xhr.getResponseHeader(h);
212
+ // adjust the simple Etag variant to match on its significant part
213
+ if (h.toLowerCase() == "etag" && value) value = value.replace(/^W\//, '');
214
+ if (h.toLowerCase() == "content-type" && value) value = value.replace(/^(.*?);.*?$/i, "$1");
215
+ info[h] = value;
216
+ }
217
+ callback(url, info);
218
+ }
219
+ }
220
+ xhr.send();
221
+ }
222
+ };
223
+
224
+ // start listening
225
+ if (document.location.protocol != "file:") {
226
+ if (!window.liveJsLoaded)
227
+ Live.heartbeat();
228
+
229
+ window.liveJsLoaded = true;
230
+ }
231
+ else if (window.console)
232
+ console.log("Live.js doesn't support the file protocol. It needs http.");
233
+ })();
@@ -0,0 +1,25 @@
1
+ $:.push File.expand_path('../lib', __FILE__)
2
+ require 'github-markdown-server'
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = 'github-markdown-server'
6
+ s.version = GithubMarkdownServer::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ['Alex K (wtwf.com)']
9
+ s.email = 'ruby@mail.wtwf.com'
10
+ s.homepage = 'https://github.com/arkarkark/github-markdown-server'
11
+ s.summary = %q{Use your favorite editor to edit a markdown file, Run the server and open the file. Saving in your editor updates instantly in the browser.}
12
+ s.description = %q{Runs a webserver to preview Github markdown with live.js updating.}
13
+ s.license = 'MIT'
14
+
15
+ s.add_dependency 'github-markdown-preview', '~> 3.1', '>= 3.1.3'
16
+
17
+ s.add_development_dependency 'minitest', '~> 5.4'
18
+ s.add_development_dependency 'bundler', '~> 1.7'
19
+ s.add_development_dependency 'rake', '~> 10.3'
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = %w(lib)
25
+ end
@@ -0,0 +1,7 @@
1
+ module GithubMarkdownServer
2
+ class FileNotFoundError < StandardError; end
3
+
4
+ require 'github-markdown-server/version'
5
+ require 'github-markdown-server/resources'
6
+ require 'github-markdown-server/server'
7
+ end
@@ -0,0 +1,9 @@
1
+ module GithubMarkdownServer
2
+ class Resources
3
+ ##
4
+ # Transforms a resource_path in data/ into an absolute path
5
+ def self.expand_path(resource_path)
6
+ File.join(File.dirname(File.expand_path(__FILE__)), '..', '..', 'data', resource_path)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,136 @@
1
+ require 'github-markdown-preview'
2
+ require 'pathname'
3
+ require 'tempfile'
4
+ require 'webrick'
5
+
6
+
7
+ module GithubMarkdownServer
8
+
9
+ ##
10
+ # Serves files and converts markdown to github like html (with live.js as well).
11
+ class Server
12
+ def initialize(options = {})
13
+ port = options[:server_port] || 8000
14
+ @directory = File.expand_path(options[:directory])
15
+ @file_name = File.expand_path(options[:file_name])
16
+ url = "http://localhost:#{port}/"
17
+
18
+ if !File.directory?(@directory)
19
+ @directory = File.dirname(@directory)
20
+ end
21
+
22
+ url += @file_name[@directory.length + 1..-1]
23
+
24
+ server_options = {
25
+ :Port => port,
26
+ :DocumentRoot => @directory,
27
+ Logger: WEBrick::Log.new("/dev/null"),
28
+ AccessLog: [],
29
+ }
30
+ if options[:browser]
31
+ server_options[:StartCallback] = Proc.new {
32
+ system('open', url)
33
+ }
34
+ end
35
+ server = WEBrick::HTTPServer.new(server_options)
36
+
37
+ trap 'INT' do server.shutdown end
38
+ server.mount_proc '/' do |req, res|
39
+ if req.path == '/favicon.ico'
40
+ source_file = Resources.expand_path(File.join('image','favicon.ico'))
41
+ else
42
+ source_file = File.join(@directory, req.path)
43
+ end
44
+ case req.request_method
45
+ when 'HEAD'
46
+ res['last-modified'] = File.mtime(source_file).to_s
47
+ else
48
+ livejs = "<script>\n#{IO.read(Resources.expand_path(File.join('js','live.js')))}\n</script>"
49
+
50
+ if source_file.end_with? '.md'
51
+ res.body = md2html(source_file)
52
+ res.body += livejs
53
+ elsif File.directory?(source_file)
54
+ directory = source_file
55
+ for index in ['index.md', 'index.html', 'index.htm']
56
+ source_file = File.join(directory, index)
57
+ if File.exists?(source_file)
58
+ res.body = IO.read(source_file)
59
+ res.body += livejs
60
+ break
61
+ end
62
+ end
63
+ if !File.exists?(source_file)
64
+ bonus = nil
65
+ readme = File.join(directory, '/README.md')
66
+ if File.exists?(readme)
67
+ bonus = md2html(readme)
68
+ else
69
+ bonus = emptystyles
70
+ end
71
+ res.body = directory_listing(directory, req.path == '/', bonus)
72
+ end
73
+ else
74
+ res.body = IO.read(source_file)
75
+ end
76
+ end
77
+ res['content-type'] = mime_type(File.extname(source_file))
78
+ end
79
+
80
+ puts "Starting server #{url}"
81
+ server.start
82
+ end
83
+
84
+
85
+ def md2html(file)
86
+ out = Tempfile.new(File.basename(file))
87
+ GithubMarkdownPreview::HtmlPreview.new(file, {:preview_file => out.path})
88
+ IO.read(out.path)
89
+ end
90
+
91
+ def emptystyles
92
+ file = Tempfile.new('')
93
+ out = Tempfile.new('')
94
+ GithubMarkdownPreview::HtmlPreview.new(file, {:preview_file => out.path}).wrap_preview('')
95
+ end
96
+
97
+ def directory_listing(dir, root, bonus)
98
+ body = '<ul>'
99
+ body += '<li><a href="../">..</a>' unless root
100
+ dirs = Pathname.glob(File.join(dir, '/*/')).map do |x|
101
+ d = x.basename.to_s
102
+ "<li><a href=\"#{d}/\">#{d}</a></li>"
103
+ end
104
+ mds = Pathname.glob(File.join(dir, '*.md')).map do |x|
105
+ md = x.basename.to_s
106
+ "<li><a href=\"#{md}\">#{md}</a></li>"
107
+ end
108
+
109
+ body += dirs.join('') unless dirs.empty?
110
+ body += mds.join('') unless mds.empty?
111
+
112
+ body += '</ul>'
113
+ if bonus
114
+ sep = '<div class="readme-content">'
115
+ body = bonus.sub!(sep, (body + sep))
116
+ end
117
+ body
118
+ end
119
+
120
+ def mime_type(ext)
121
+ ext = ext.downcase.sub(/^\./, '')
122
+ case ext
123
+ when 'gif', 'jpg', 'png'
124
+ "image/#{ext}"
125
+ when 'md', 'html', 'htm'
126
+ 'text/html'
127
+ when 'js', 'css'
128
+ "text/#{ext}"
129
+ when 'ico'
130
+ 'image/vnd.microsoft.icon'
131
+ else
132
+ 'application/octet-stream'
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,3 @@
1
+ module GithubMarkdownServer
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: github-markdown-server
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex K (wtwf.com)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: github-markdown-preview
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 3.1.3
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '3.1'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 3.1.3
33
+ - !ruby/object:Gem::Dependency
34
+ name: minitest
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '5.4'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '5.4'
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.3'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '10.3'
75
+ description: Runs a webserver to preview Github markdown with live.js updating.
76
+ email: ruby@mail.wtwf.com
77
+ executables:
78
+ - github-markdown-server
79
+ extensions: []
80
+ extra_rdoc_files: []
81
+ files:
82
+ - ".gitignore"
83
+ - LICENSE
84
+ - README.md
85
+ - bin/github-markdown-server
86
+ - contrib/github-markdown-server.el
87
+ - contrib/p
88
+ - data/image/favicon.ico
89
+ - data/js/live.js
90
+ - github-markdown-server.gemspec
91
+ - lib/github-markdown-server.rb
92
+ - lib/github-markdown-server/resources.rb
93
+ - lib/github-markdown-server/server.rb
94
+ - lib/github-markdown-server/version.rb
95
+ homepage: https://github.com/arkarkark/github-markdown-server
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.2.2
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Use your favorite editor to edit a markdown file, Run the server and open
119
+ the file. Saving in your editor updates instantly in the browser.
120
+ test_files: []