added highlight.js support

This commit is contained in:
Matthew Scragg 2013-10-08 14:47:49 -05:00
부모 d30c9d71a6
커밋 9d3751f241
11개의 변경된 파일204개의 추가작업 그리고 38개의 파일을 삭제

파일 보기

@ -222,7 +222,7 @@ def create_app(subdomain=None):
cname = to_canonical(name) cname = to_canonical(name)
if request.method == 'POST': if request.method == 'POST':
edit_cname = to_canonical(request.form['name']) edit_cname = to_canonical(request.form['name'])
if edit_cname != cname: if edit_cname.lower() != cname.lower():
w.rename_page(cname, edit_cname) w.rename_page(cname, edit_cname)
w.write_page(edit_cname, request.form['content'], message=request.form['message'], w.write_page(edit_cname, request.form['content'], message=request.form['message'],
username=CurrentUser.get('username')) username=CurrentUser.get('username'))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

파일 보기

@ -0,0 +1,105 @@
/*global module:true*/
/*
* Basic table support with re-entrant parsing, where cell content
* can also specify markdown.
*
* Tables
* ======
*
* | Col 1 | Col 2 |
* |======== |====================================================|
* |**bold** | ![Valid XHTML] (http://w3.org/Icons/valid-xhtml10) |
* | Plain | Value |
*
*/
(function(){
var table = function(converter) {
var tables = {}, style = 'text-align:left;', filter;
tables.th = function(header){
if (header.trim() === "") { return "";}
var id = header.trim().replace(/ /g, '_').toLowerCase();
return '<th id="' + id + '" style="'+style+'">' + header + '</th>';
};
tables.td = function(cell) {
return '<td style="'+style+'">' + converter.makeHtml(cell) + '</td>';
};
tables.ths = function(){
var out = "", i = 0, hs = [].slice.apply(arguments);
for (i;i<hs.length;i+=1) {
out += tables.th(hs[i]) + '\n';
}
return out;
};
tables.tds = function(){
var out = "", i = 0, ds = [].slice.apply(arguments);
for (i;i<ds.length;i+=1) {
out += tables.td(ds[i]) + '\n';
}
return out;
};
tables.thead = function() {
var out, i = 0, hs = [].slice.apply(arguments);
out = "<thead>\n";
out += "<tr>\n";
out += tables.ths.apply(this, hs);
out += "</tr>\n";
out += "</thead>\n";
return out;
};
tables.tr = function() {
var out, i = 0, cs = [].slice.apply(arguments);
out = "<tr>\n";
out += tables.tds.apply(this, cs);
out += "</tr>\n";
return out;
};
filter = function(text) {
var i=0, lines = text.split('\n'), tbl = [], line, hs, rows, out = [];
for (i; i<lines.length;i+=1) {
line = lines[i];
// looks like a table heading
if (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl.push('<table>');
hs = line.substring(1, line.length -1).split('|');
tbl.push(tables.thead.apply(this, hs));
line = lines[++i];
if (!line.trim().match(/^[|]{1}[-=| ]+[|]{1}$/)) {
// not a table rolling back
line = lines[--i];
}
else {
line = lines[++i];
tbl.push('<tbody>');
while (line.trim().match(/^[|]{1}.*[|]{1}$/)) {
line = line.trim();
tbl.push(tables.tr.apply(this, line.substring(1, line.length -1).split('|')));
line = lines[++i];
}
tbl.push('</tbody>');
tbl.push('</table>');
// we are done with this table and we move along
out.push(tbl.join('\n'));
continue;
}
}
out.push(line);
}
return out.join('\n');
};
return [
{
type: 'lang',
filter: filter
}
];
};
// Client-side export
if (typeof window !== 'undefined' && window.Showdown && window.Showdown.extensions) { window.Showdown.extensions.table = table; }
// Server-side export
if (typeof module !== 'undefined') {
module.exports = table;
}
}());

파일 보기

@ -3,7 +3,15 @@
* Copyright (c) 2010 Caolan McMahon * Copyright (c) 2010 Caolan McMahon
*/ */
function escapeHtml(s) {
s = ('' + s); /* Coerce to string */
s = s.replace(/&/g, '&amp;');
s = s.replace(/</g, '&lt;');
s = s.replace(/>/g, '&gt;');
s = s.replace(/"/g, '&quot;');
s = s.replace(/'/g, '&#39;');
return s;
}
/** /**
* Main function for converting markdown to HTML. * Main function for converting markdown to HTML.
@ -87,6 +95,34 @@ WMD.preprocessors = {
} }
doc.markdown = lines.join('\n'); doc.markdown = lines.join('\n');
return doc; return doc;
},
fencedCodeBlocksHighlightJS: function (doc) {
var re1 = /```([A-Za-z]+)\s*([\s\S]+?)```/; // with syntax highlighting
var re2 = /```\s*([\s\S]+?)```/; // without syntax highlighting
var block;
while (block = re1.exec(doc.markdown) || re2.exec(doc.markdown)) {
var pre;
if (block.length === 3) {
// we have a code format
pre = '<pre style="padding:0;"><code class="' + escapeHtml(block[1]) + '">';
if (block[1] in hljs.LANGUAGES) {
pre += hljs.highlight(block[1], block[2]).value;
}
else {
pre += escapeHtml(block[2]);
}
pre += '</code></pre>';
}
else {
// no syntax highlighting
pre = '<pre style="padding:0;"><code class="no-highlight">' +
escapeHtml(block[1]) + '</code></pre>';
}
doc.markdown = doc.markdown.substr(0, block.index) +
pre + doc.markdown.substr(block.index + block[0].length);
}
return doc;
} }
}; };
@ -104,9 +140,11 @@ WMD.readOptions = function (options) {
var obj = { var obj = {
preprocessors: [ preprocessors: [
WMD.preprocessors.metadata, WMD.preprocessors.metadata,
WMD.preprocessors.underscores WMD.preprocessors.underscores,
WMD.preprocessors.fencedCodeBlocksHighlightJS
], ],
postprocessors: [] postprocessors: [
]
}; };
for (var k in options) { for (var k in options) {
obj[k] = options[k]; obj[k] = options[k];

파일 보기

@ -10,6 +10,7 @@
<link href="/static/css/bootstrap/spacelab.css" rel="stylesheet"> <link href="/static/css/bootstrap/spacelab.css" rel="stylesheet">
<link href="/static/css/font-awesome.min.css" rel="stylesheet"> <link href="/static/css/font-awesome.min.css" rel="stylesheet">
<link href="/static/js/highlight/styles/github.css" rel="stylesheet">
<link href="/static/css/style.css" rel="stylesheet"> <link href="/static/css/style.css" rel="stylesheet">
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries --> <!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
@ -106,6 +107,7 @@
<script src="/static/js/showdown/showdown.js"></script> <script src="/static/js/showdown/showdown.js"></script>
<script src="/static/js/html-sanitizer-minified.js"></script> <script src="/static/js/html-sanitizer-minified.js"></script>
<script src="/static/js/showdown/wmd.js"></script> <script src="/static/js/showdown/wmd.js"></script>
<script src="/static/js/highlight/highlight.pack.js"></script>
<script> <script>
/* /*
marked.setOptions({ marked.setOptions({
@ -118,6 +120,11 @@
langPrefix: 'lang-' langPrefix: 'lang-'
}); });
*/ */
hljs.initHighlightingOnLoad();
// Markdown Renderer
MDR = { MDR = {
doc: null, doc: null,
callback: WMD, callback: WMD,

파일 보기

@ -65,22 +65,4 @@
<div id="preview"></div> <div id="preview"></div>
</div> </div>
<!--
<form role="form" method="post">
<div class="form-group">
<label for="name"></label>
<input type="text" class="form-control" id="page" name="name" placeholder="Name" value="{{- name -}}" />
</div>
<div class="form-group">
<label for="content"></label>
<div id="epiceditor"></div>
<textarea name="content" id="content" class="form-control" placeholder="Content" style="display:none;">{{- content -}}</textarea>
</div>
<input type="submit" class="btn btn-primary" value="Save" />
</form>
-->
{% endblock %} {% endblock %}

파일 보기

@ -9,7 +9,7 @@
{% block body %} {% block body %}
<div id="page-content" style="display:none"> <div id="page-content" style="display:none">
{{ page.data|safe }} {{ page.data }}
</div> </div>
{% endblock %} {% endblock %}

파일 보기

@ -3,6 +3,37 @@ import os
import hashlib import hashlib
def escape_repl(m):
print "group 0"
print m.group(0)
print "group 1"
print m.group(1)
if m.group(1):
return "```" + escape_html(m.group(1)) + "```"
def unescape_repl(m):
if m.group(1):
return "```" + unescape_html(m.group(1)) + "```"
def escape_html(s):
s = s.replace("&", '&amp;')
s = s.replace("<", '&lt;')
s = s.replace(">", '&gt;')
s = s.replace('"', '&quot;')
s = s.replace("'", '&#39;')
return s
def unescape_html(s):
s = s.replace('&amp;', "&")
s = s.replace('&lt;', "<")
s = s.replace('&gt;', ">")
s = s.replace('&quot;', '"')
s = s.replace('&#39;', "'")
return s
def mkdir_safe(path): def mkdir_safe(path):
if path and not(os.path.exists(path)): if path and not(os.path.exists(path)):
os.makedirs(path) os.makedirs(path)

파일 보기

@ -1,12 +1,13 @@
import os import os
import re import re
from lxml.html.clean import clean_html import lxml.html
from lxml.html import clean
import ghdiff import ghdiff
from gittle import Gittle from gittle import Gittle
from dulwich.repo import NotGitRepository from dulwich.repo import NotGitRepository
from util import to_canonical from util import to_canonical, escape_repl, unescape_repl
from models import Site from models import Site
@ -68,11 +69,23 @@ class Wiki():
return True if s.get_by_name(name) else False return True if s.get_by_name(name) else False
def write_page(self, name, content, message=None, create=False, username=None, email=None): def write_page(self, name, content, message=None, create=False, username=None, email=None):
# adding the div wrapper apparently fixes anomalies with the lxml parser with certain markdown
content = clean_html('<div>' + content + '</div>') # prevents p tag from being added, we remove this later
content = '<div>' + content + '</div>'
content = re.sub(r"```(.*?)```", escape_repl, content, flags=re.DOTALL)
tree = lxml.html.fromstring(content)
cleaner = clean.Cleaner(remove_unknown_tags=False)
tree = cleaner.clean_html(tree)
content = lxml.html.tostring(tree, encoding='utf-8', method='html')
# post processing to fix errors
content = content[5:-6] content = content[5:-6]
content = re.sub(r"(\n&gt;)", "\n>", content) content = re.sub(r"(\n&gt;)", "\n>", content)
content = re.sub(r"(^&gt;)", ">", content) content = re.sub(r"(^&gt;)", ">", content)
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
filename = self.cname_to_filename(to_canonical(name)) filename = self.cname_to_filename(to_canonical(name))
f = open(self.path + "/" + filename, 'w') f = open(self.path + "/" + filename, 'w')

파일 보기

@ -6,7 +6,7 @@ python-pkgs:
- build-essential - build-essential
{% for pkg in ['ghdiff', 'tornado', 'pyzmq', 'itsdangerous', 'boto', 'redis', 'simplejson', 'sockjs-tornado', 'flask', 'flask-bcrypt', 'flask-login', 'flask-assets', 'gittle', 'gevent', 'lxml', 'markdown2', 'recaptcha-client', 'RethinkORM' ] %} {% for pkg in ['BeautifulSoup', 'html5lib', 'ghdiff', 'tornado', 'pyzmq', 'itsdangerous', 'boto', 'redis', 'simplejson', 'sockjs-tornado', 'flask', 'flask-bcrypt', 'flask-login', 'flask-assets', 'gittle', 'gevent', 'lxml', 'markdown2', 'recaptcha-client', 'RethinkORM' ] %}
{{ pkg }}-pip: {{ pkg }}-pip:
pip: pip:
- name: {{ pkg }} - name: {{ pkg }}