c9-core/plugins/c9.ide.preview.markdown/markdown.html

302 wiersze
12 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<script src="remarkable.min.js"></script>
<link rel="stylesheet" type="text/css" href="github-markdown.css">
<style>
body, html{
margin: 0;
padding: 0;
}
#preview{
overflow: auto;
color: rgb(0, 0, 0);
min-width: 200px;
max-width: 790px;
margin: 0 auto;
padding: 30px;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
#preview pre code div {
background: transparent !important;
}
#preview .ace_line {
white-space: pre;
}
#preview .ace_static_highlight {
font-family: inherit;
font-size: inherit;
}
</style>
</head>
<body>
<div class="markdown-body" id="preview">
</div>
<script>
(function(){
/*global Remarkable*/
var preview = document.getElementById("preview");
var host = location.href.match(/host=(.*?)(\&|$)/)[1];
var id = location.href.match(/id=(.*)/)[1];
var parent = window.opener || window.parent;
var count = 0;
var md = new Remarkable({
html: true, // Enable HTML tags in source
xhtmlOut: false, // Use '/' to close single tags (<br />)
breaks: false, // Convert '\n' in paragraphs into <br>
langPrefix: 'language-', // CSS language prefix for fenced blocks
linkify: true, // Autoconvert URL-like text to links
// Enable some language-neutral replacement + quotes beautification
typographer: true,
// Double + single quotes replacement pairs, when typographer enabled,
// and smartquotes on. Set doubles to '«»' for Russian, '„“' for German.
quotes: '“”‘’',
// Highlighter function. Should return escaped HTML,
// or '' if the source string is not changed
highlight: function (str, lang) {
var id = count++;
send({
message: "highlight",
content: str,
lang: lang,
hid: id
});
return '<span id="findme' + id + '"></span>' + str;
}
});
md.renderer.rules.paragraph_open = function (tokens, idx) {
var line;
if (tokens[idx].lines && tokens[idx].level === 0) {
line = tokens[idx].lines[0];
return '<p class="line" data-line="' + line + '">';
}
return '<p>';
};
md.renderer.rules.heading_open = function (tokens, idx) {
var line;
if (tokens[idx].lines && tokens[idx].level === 0) {
line = tokens[idx].lines[0];
return '<h' + tokens[idx].hLevel + ' class="line" data-line="' + line + '">';
}
return '<h' + tokens[idx].hLevel + '>';
};
md.renderer.rules.list_item_open = function (tokens, idx) {
var line;
if (tokens[idx].lines) {
line = tokens[idx].lines[0];
return '<li class="line" data-line="' + line + '">';
}
return '<li>';
};
md.renderer.rules.blockquote_open = function (tokens, idx) {
var line;
if (tokens[idx].lines) {
line = tokens[idx].lines[0];
return '<blockquote class="line" data-line="' + line + '">';
}
return '<blockquote>';
};
// Build offsets for each line (lines can be wrapped)
// That's a bit dirty to process each line everytime, but ok for demo.
// Optimizations are required only for big texts.
var sourceLikeDiv, cStyle;
function buildScrollMap(value) {
var i, offset, nonEmptyList, pos, a, b, lineHeightMap, linesCount,
acc, _scrollMap;
if (!sourceLikeDiv) {
sourceLikeDiv = document.createElement("div");
sourceLikeDiv.style.position = 'absolute';
sourceLikeDiv.style.visibility = 'hidden';
sourceLikeDiv.style.height = 'auto';
sourceLikeDiv.style.width = preview.clientWidth;
sourceLikeDiv.style.fontSize = cStyle.fontSize;
sourceLikeDiv.style.fontFamily = cStyle.fontFamily;
// sourceLikeDiv.style.lineHeight = cStyle.lineHeight;
sourceLikeDiv.style.whiteSpace = "pre";
}
document.body.appendChild(sourceLikeDiv);
offset = 0;
_scrollMap = [];
nonEmptyList = [];
lineHeightMap = [];
acc = 0;
value.split('\n').forEach(function(str) {
var h, lh;
lineHeightMap.push(acc);
if (str.length === 0) {
acc++;
return;
}
sourceLikeDiv.innerHTML = str;
var sStyle = getComputedStyle(sourceLikeDiv);
h = parseFloat(sStyle.height);
lh = parseFloat(sStyle.fontSize);
acc += Math.round(h / lh);
});
sourceLikeDiv.remove();
lineHeightMap.push(acc);
linesCount = acc;
for (i = 0; i < linesCount; i++) { _scrollMap.push(-1); }
nonEmptyList.push(0);
_scrollMap[0] = 0;
var lines = preview.querySelectorAll(".line");
for (var i = 0, l = lines.length; i < l; i++) {
var t = lines[i].getAttribute("data-line");
if (t === '') { return; }
t = lineHeightMap[t];
if (t !== 0) { nonEmptyList.push(t); }
_scrollMap[t] = Math.round(lines[i].offsetTop + offset);
}
nonEmptyList.push(linesCount);
_scrollMap[linesCount] = preview.scrollHeight;
pos = 0;
for (i = 1; i < linesCount; i++) {
if (_scrollMap[i] !== -1) {
pos++;
continue;
}
a = nonEmptyList[pos];
b = nonEmptyList[pos + 1];
_scrollMap[i] = Math.round((_scrollMap[b] * (i - a)
+ _scrollMap[a] * (b - i)) / (b - a));
}
_scrollMap[0] = 0;
return _scrollMap;
}
var ckb;
function setKeys(list){
ckb = {};
list.forEach(function(item) {
var binding = item.binding;
var command = item.command;
var hashId = binding.hashId;
var hash = (ckb[hashId] || (ckb[hashId] = {}));
if (!hash[binding.key]) {
hash[binding.key] = command;
} else {
if (!Array.isArray(hash[binding.key]))
hash[binding.key] = [hash[binding.key]];
hash[binding.key].push(command);
}
}, this);
}
var timer;
function scrollTo(posTo){
clearInterval(timer);
var curPos = document.body.scrollTop;
var dist = posTo - curPos;
var incr = dist / 10;
timer = setInterval(function(){
if (incr < 0
? document.body.scrollTop + incr < posTo
: document.body.scrollTop + incr > posTo) {
document.body.scrollTop = posTo;
clearInterval(timer);
}
else
document.body.scrollTop += incr;
}, 10);
}
document.addEventListener("keydown", function(e){
var hashId = 0 | (e.ctrlKey ? 1 : 0) | (e.altKey ? 2 : 0)
| (e.shiftKey ? 4 : 0) | (e.metaKey ? 8 : 0);
var keys = ckb[hashId];
var cmd = keys && keys[e.keyCode];
if (cmd) {
send({ message: "exec", command: cmd });
e.preventDefault();
e.stopPropagation();
}
});
var scrollMap, lastValue, highlightStyle;
window.addEventListener("message", function(e){
if (host != "local" && e.origin !== host)
return;
if (e.data.type == "document") {
lastValue = e.data.content;
preview.innerHTML = md.render(lastValue);
cStyle = e.data;
scrollMap = null;
}
else if (e.data.type == "keys")
setKeys(e.data.keys);
else if (e.data.type == "scroll") {
if (!scrollMap)
scrollMap = buildScrollMap(lastValue);
var line = Math.max(0, e.data.lineNumber - 2);
var posTo = scrollMap[line];
scrollTo(posTo);
}
else if (e.data.type == "highlight") {
var el = document.getElementById("findme" + e.data.hid);
if (!el) return;
if (!highlightStyle) {
highlightStyle = document.head.appendChild(document.createElement("style"));
highlightStyle.innerText = e.data.css;
}
el.parentNode.innerHTML = e.data.html;
}
}, false);
window.addEventListener("focus", function(){
send({ message: "focus" });
});
function send(message){
message.id = id;
parent.postMessage(message,
host == "local" ? "*" : host);
}
window.start = function(win){
parent = win || window.parent;
send({ message: "stream.document" });
}
if (parent != window)
window.start(parent);
})();
</script>
</body>
</html>