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 +7 -0
- data/.gitignore +1 -0
- data/LICENSE +22 -0
- data/README.md +24 -0
- data/bin/github-markdown-server +51 -0
- data/contrib/github-markdown-server.el +73 -0
- data/contrib/p +116 -0
- data/data/image/favicon.ico +0 -0
- data/data/js/live.js +233 -0
- data/github-markdown-server.gemspec +25 -0
- data/lib/github-markdown-server.rb +7 -0
- data/lib/github-markdown-server/resources.rb +9 -0
- data/lib/github-markdown-server/server.rb +136 -0
- data/lib/github-markdown-server/version.rb +3 -0
- metadata +120 -0
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,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
|
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: []
|