added highlight.js support
This commit is contained in:
parent
d30c9d71a6
commit
9d3751f241
|
@ -222,7 +222,7 @@ def create_app(subdomain=None):
|
|||
cname = to_canonical(name)
|
||||
if request.method == 'POST':
|
||||
edit_cname = to_canonical(request.form['name'])
|
||||
if edit_cname != cname:
|
||||
if edit_cname.lower() != cname.lower():
|
||||
w.rename_page(cname, edit_cname)
|
||||
w.write_page(edit_cname, request.form['content'], message=request.form['message'],
|
||||
username=CurrentUser.get('username'))
|
||||
|
|
9
realms/static/css/cerulean.bootstrap.min.css
vendored
9
realms/static/css/cerulean.bootstrap.min.css
vendored
File diff suppressed because one or more lines are too long
1
realms/static/js/jquery-1.10.2.min.js
vendored
1
realms/static/js/jquery-1.10.2.min.js
vendored
File diff suppressed because one or more lines are too long
105
realms/static/js/showdown/extensions/table.js
Normal file
105
realms/static/js/showdown/extensions/table.js
Normal file
|
@ -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
|
||||
*/
|
||||
|
||||
|
||||
function escapeHtml(s) {
|
||||
s = ('' + s); /* Coerce to string */
|
||||
s = s.replace(/&/g, '&');
|
||||
s = s.replace(/</g, '<');
|
||||
s = s.replace(/>/g, '>');
|
||||
s = s.replace(/"/g, '"');
|
||||
s = s.replace(/'/g, ''');
|
||||
return s;
|
||||
}
|
||||
|
||||
/**
|
||||
* Main function for converting markdown to HTML.
|
||||
|
@ -87,6 +95,34 @@ WMD.preprocessors = {
|
|||
}
|
||||
doc.markdown = lines.join('\n');
|
||||
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 = {
|
||||
preprocessors: [
|
||||
WMD.preprocessors.metadata,
|
||||
WMD.preprocessors.underscores
|
||||
WMD.preprocessors.underscores,
|
||||
WMD.preprocessors.fencedCodeBlocksHighlightJS
|
||||
],
|
||||
postprocessors: []
|
||||
postprocessors: [
|
||||
]
|
||||
};
|
||||
for (var k in options) {
|
||||
obj[k] = options[k];
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
<link href="/static/css/bootstrap/spacelab.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">
|
||||
|
||||
<!-- 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/html-sanitizer-minified.js"></script>
|
||||
<script src="/static/js/showdown/wmd.js"></script>
|
||||
<script src="/static/js/highlight/highlight.pack.js"></script>
|
||||
<script>
|
||||
/*
|
||||
marked.setOptions({
|
||||
|
@ -118,6 +120,11 @@
|
|||
langPrefix: 'lang-'
|
||||
});
|
||||
*/
|
||||
|
||||
|
||||
hljs.initHighlightingOnLoad();
|
||||
|
||||
// Markdown Renderer
|
||||
MDR = {
|
||||
doc: null,
|
||||
callback: WMD,
|
||||
|
|
|
@ -65,22 +65,4 @@
|
|||
<div id="preview"></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 %}
|
|
@ -9,7 +9,7 @@
|
|||
{% block body %}
|
||||
|
||||
<div id="page-content" style="display:none">
|
||||
{{ page.data|safe }}
|
||||
{{ page.data }}
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -3,6 +3,37 @@ import os
|
|||
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("&", '&')
|
||||
s = s.replace("<", '<')
|
||||
s = s.replace(">", '>')
|
||||
s = s.replace('"', '"')
|
||||
s = s.replace("'", ''')
|
||||
return s
|
||||
|
||||
|
||||
def unescape_html(s):
|
||||
s = s.replace('&', "&")
|
||||
s = s.replace('<', "<")
|
||||
s = s.replace('>', ">")
|
||||
s = s.replace('"', '"')
|
||||
s = s.replace(''', "'")
|
||||
return s
|
||||
|
||||
def mkdir_safe(path):
|
||||
if path and not(os.path.exists(path)):
|
||||
os.makedirs(path)
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import os
|
||||
import re
|
||||
from lxml.html.clean import clean_html
|
||||
import lxml.html
|
||||
from lxml.html import clean
|
||||
import ghdiff
|
||||
|
||||
from gittle import Gittle
|
||||
from dulwich.repo import NotGitRepository
|
||||
|
||||
from util import to_canonical
|
||||
from util import to_canonical, escape_repl, unescape_repl
|
||||
from models import Site
|
||||
|
||||
|
||||
|
@ -68,11 +69,23 @@ class Wiki():
|
|||
return True if s.get_by_name(name) else False
|
||||
|
||||
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 = re.sub(r"(\n>)", "\n>", content)
|
||||
content = re.sub(r"(^>)", ">", content)
|
||||
content = re.sub(r"```(.*?)```", unescape_repl, content, flags=re.DOTALL)
|
||||
|
||||
filename = self.cname_to_filename(to_canonical(name))
|
||||
f = open(self.path + "/" + filename, 'w')
|
||||
|
|
|
@ -6,7 +6,7 @@ python-pkgs:
|
|||
- 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:
|
||||
pip:
|
||||
- name: {{ pkg }}
|
||||
|
|
Loading…
Reference in a new issue