marked/docs/demo/demo.js

487 lines
13 KiB
JavaScript
Raw Normal View History

2019-04-26 20:58:04 -05:00
onunhandledrejection = function(e) {
2019-02-13 12:16:39 -06:00
throw e.reason;
};
const $loadingElem = document.querySelector('#loading');
const $mainElem = document.querySelector('#main');
const $markdownElem = document.querySelector('#markdown');
const $markedVerElem = document.querySelector('#markedVersion');
const $commitVerElem = document.querySelector('#commitVersion');
const $optionsElem = document.querySelector('#options');
const $outputTypeElem = document.querySelector('#outputType');
const $inputTypeElem = document.querySelector('#inputType');
const $responseTimeElem = document.querySelector('#responseTime');
const $previewElem = document.querySelector('#preview');
const $previewIframe = document.querySelector('#preview iframe');
const $permalinkElem = document.querySelector('#permalink');
const $clearElem = document.querySelector('#clear');
const $htmlElem = document.querySelector('#html');
const $lexerElem = document.querySelector('#lexer');
const $panes = document.querySelectorAll('.pane');
const $inputPanes = document.querySelectorAll('.inputPane');
let lastInput = '';
let inputDirty = true;
let $activeOutputElem = null;
const search = searchToObject();
const markedVersions = {
master: 'https://cdn.jsdelivr.net/gh/markedjs/marked'
2019-02-08 14:11:09 -06:00
};
let delayTime = 1;
let checkChangeTimeout = null;
let markedWorker;
2019-02-14 11:14:15 -06:00
$previewIframe.addEventListener('load', handleIframeLoad);
$outputTypeElem.addEventListener('change', handleOutputChange, false);
$inputTypeElem.addEventListener('change', handleInputChange, false);
$markedVerElem.addEventListener('change', handleVersionChange, false);
$markdownElem.addEventListener('change', handleInput, false);
$markdownElem.addEventListener('keyup', handleInput, false);
$markdownElem.addEventListener('keypress', handleInput, false);
$markdownElem.addEventListener('keydown', handleInput, false);
$optionsElem.addEventListener('change', handleInput, false);
$optionsElem.addEventListener('keyup', handleInput, false);
$optionsElem.addEventListener('keypress', handleInput, false);
$optionsElem.addEventListener('keydown', handleInput, false);
2019-02-08 14:11:09 -06:00
2019-02-14 10:43:03 -06:00
$commitVerElem.style.display = 'none';
2019-02-14 11:14:15 -06:00
$commitVerElem.addEventListener('keypress', handleAddVersion, false);
$clearElem.addEventListener('click', handleClearClick, false);
Promise.all([
setInitialQuickref(),
2019-02-14 11:18:47 -06:00
setInitialOutputType(),
2019-02-14 11:14:15 -06:00
setInitialText(),
setInitialVersion()
.then(setInitialOptions)
2019-04-26 20:58:04 -05:00
]).then(function() {
2019-02-14 11:18:47 -06:00
handleInputChange();
handleOutputChange();
2019-02-14 11:14:15 -06:00
checkForChanges();
setScrollPercent(0);
2019-02-14 11:25:01 -06:00
$loadingElem.style.display = 'none';
$mainElem.style.display = 'block';
2018-09-23 20:56:38 -04:00
});
2018-06-24 01:46:25 -05:00
2019-04-26 20:58:04 -05:00
function setInitialText() {
2019-02-14 11:52:14 -06:00
if ('text' in search) {
2019-02-14 11:14:15 -06:00
$markdownElem.value = search.text;
} else {
return fetch('./initial.md')
2019-04-26 20:58:04 -05:00
.then(function(res) { return res.text(); })
.then(function(text) {
2019-02-14 11:14:15 -06:00
if ($markdownElem.value === '') {
$markdownElem.value = text;
}
});
}
}
2019-04-26 20:58:04 -05:00
function setInitialQuickref() {
2019-02-14 11:14:15 -06:00
return fetch('./quickref.md')
2019-04-26 20:58:04 -05:00
.then(function(res) { return res.text(); })
.then(function(text) {
2019-02-14 11:14:15 -06:00
document.querySelector('#quickref').value = text;
2018-03-26 10:36:47 -05:00
});
}
2019-04-26 20:58:04 -05:00
function setInitialVersion() {
2019-02-14 11:14:15 -06:00
return fetch('https://data.jsdelivr.com/v1/package/npm/marked')
2019-04-26 20:58:04 -05:00
.then(function(res) {
2019-02-14 11:14:15 -06:00
return res.json();
})
2019-04-26 20:58:04 -05:00
.then(function(json) {
for (let i = 0; i < json.versions.length; i++) {
const ver = json.versions[i];
markedVersions[ver] = 'https://cdn.jsdelivr.net/npm/marked@' + ver;
const opt = document.createElement('option');
2019-02-14 11:14:15 -06:00
opt.textContent = ver;
opt.value = ver;
$markedVerElem.appendChild(opt);
}
})
2019-04-26 20:58:04 -05:00
.then(function() {
2019-04-01 09:08:42 -05:00
return fetch('https://api.github.com/repos/markedjs/marked/commits')
2019-04-26 20:58:04 -05:00
.then(function(res) {
2019-04-01 09:08:42 -05:00
return res.json();
})
2019-04-26 20:58:04 -05:00
.then(function(json) {
markedVersions.master = 'https://cdn.jsdelivr.net/gh/markedjs/marked@' + json[0].sha;
2019-04-01 09:08:42 -05:00
})
2019-04-26 20:58:04 -05:00
.catch(function() {
2019-04-01 09:08:42 -05:00
// do nothing
// uses url without commit
});
})
2019-04-26 20:58:04 -05:00
.then(function() {
2019-02-14 11:14:15 -06:00
if (search.version) {
2019-04-03 16:47:30 -05:00
if (markedVersions[search.version]) {
return search.version;
} else {
const match = search.version.match(/^(\w+):(.+)$/);
2019-02-14 11:14:15 -06:00
if (match) {
switch (match[1]) {
case 'commit':
addCommitVersion(search.version, match[2].substring(0, 7), match[2]);
return search.version;
case 'pr':
return getPrCommit(match[2])
2019-04-26 20:58:04 -05:00
.then(function(commit) {
2019-02-14 11:14:15 -06:00
if (!commit) {
return 'master';
}
addCommitVersion(search.version, 'PR #' + match[2], commit);
return search.version;
});
}
2019-02-14 10:43:03 -06:00
}
}
2019-02-13 18:24:44 -06:00
}
2019-02-08 14:11:09 -06:00
2019-02-14 11:14:15 -06:00
return 'master';
})
2019-04-26 20:58:04 -05:00
.then(function(version) {
2019-02-14 11:14:15 -06:00
$markedVerElem.value = version;
})
.then(updateVersion);
2018-04-03 11:21:25 -05:00
}
2019-04-26 20:58:04 -05:00
function setInitialOptions() {
2019-02-14 11:52:14 -06:00
if ('options' in search) {
2019-02-14 11:14:15 -06:00
$optionsElem.value = search.options;
} else {
return setDefaultOptions();
2019-02-14 00:08:33 -06:00
}
}
2019-04-26 20:58:04 -05:00
function setInitialOutputType() {
2019-02-14 11:14:15 -06:00
if (search.outputType) {
$outputTypeElem.value = search.outputType;
}
2018-10-18 00:19:48 -07:00
}
2018-04-03 11:21:25 -05:00
2019-04-26 20:58:04 -05:00
function handleIframeLoad() {
2019-02-14 11:14:15 -06:00
lastInput = '';
inputDirty = true;
2018-10-18 00:19:48 -07:00
}
2019-04-26 20:58:04 -05:00
function handleInput() {
2019-02-14 11:14:15 -06:00
inputDirty = true;
2020-04-01 21:08:44 -05:00
}
2018-03-26 10:36:47 -05:00
2019-04-26 20:58:04 -05:00
function handleVersionChange() {
2019-02-14 10:43:03 -06:00
if ($markedVerElem.value === 'commit' || $markedVerElem.value === 'pr') {
2019-02-14 00:08:33 -06:00
$commitVerElem.style.display = '';
} else {
$commitVerElem.style.display = 'none';
updateVersion();
}
2019-02-14 11:14:15 -06:00
}
2018-03-26 10:36:47 -05:00
2019-04-26 20:58:04 -05:00
function handleClearClick() {
2019-02-14 11:14:15 -06:00
$markdownElem.value = '';
$markedVerElem.value = 'master';
$commitVerElem.style.display = 'none';
updateVersion().then(setDefaultOptions);
}
2018-10-17 15:11:53 -07:00
2019-04-26 20:58:04 -05:00
function handleAddVersion(e) {
2019-02-14 00:08:33 -06:00
if (e.which === 13) {
2019-02-14 10:43:03 -06:00
switch ($markedVerElem.value) {
case 'commit': {
const commit = $commitVerElem.value.toLowerCase();
2019-02-14 10:43:03 -06:00
if (!commit.match(/^[0-9a-f]{40}$/)) {
alert('That is not a valid commit');
return;
}
addCommitVersion('commit:' + commit, commit.substring(0, 7), commit);
$markedVerElem.value = 'commit:' + commit;
$commitVerElem.style.display = 'none';
$commitVerElem.value = '';
updateVersion();
break;
}
case 'pr': {
2019-02-14 10:43:03 -06:00
$commitVerElem.disabled = true;
const pr = $commitVerElem.value.replace(/\D/g, '');
2019-02-14 10:43:03 -06:00
getPrCommit(pr)
2019-04-26 20:58:04 -05:00
.then(function(commit) {
2019-02-14 10:43:03 -06:00
$commitVerElem.disabled = false;
if (!commit) {
alert('That is not a valid PR');
return;
}
addCommitVersion('pr:' + pr, 'PR #' + pr, commit);
$markedVerElem.value = 'pr:' + pr;
$commitVerElem.style.display = 'none';
$commitVerElem.value = '';
updateVersion();
});
break;
}
2019-02-14 00:08:33 -06:00
}
}
2019-02-14 11:14:15 -06:00
}
2019-02-14 00:08:33 -06:00
2019-04-26 20:58:04 -05:00
function handleInputChange() {
2019-02-14 11:14:15 -06:00
handleChange($inputPanes, $inputTypeElem.value);
}
2019-04-26 20:58:04 -05:00
function handleOutputChange() {
2019-02-14 11:14:15 -06:00
$activeOutputElem = handleChange($panes, $outputTypeElem.value);
updateLink();
}
2019-04-26 20:58:04 -05:00
function handleChange(panes, visiblePane) {
let active = null;
for (let i = 0; i < panes.length; i++) {
2019-02-14 11:14:15 -06:00
if (panes[i].id === visiblePane) {
panes[i].style.display = '';
active = panes[i];
} else {
panes[i].style.display = 'none';
}
}
return active;
2020-04-01 21:08:44 -05:00
}
2019-02-14 11:14:15 -06:00
2019-04-26 20:58:04 -05:00
function addCommitVersion(value, text, commit) {
2019-02-14 11:14:15 -06:00
if (markedVersions[value]) {
return;
}
markedVersions[value] = 'https://cdn.jsdelivr.net/gh/markedjs/marked@' + commit;
const opt = document.createElement('option');
2019-02-14 11:14:15 -06:00
opt.textContent = text;
opt.value = value;
$markedVerElem.insertBefore(opt, $markedVerElem.firstChild);
}
2018-03-28 11:24:14 -05:00
2019-04-26 20:58:04 -05:00
function getPrCommit(pr) {
2019-02-14 10:43:03 -06:00
return fetch('https://api.github.com/repos/markedjs/marked/pulls/' + pr + '/commits')
2019-04-26 20:58:04 -05:00
.then(function(res) {
2019-02-14 10:43:03 -06:00
return res.json();
})
2019-04-26 20:58:04 -05:00
.then(function(json) {
2019-02-14 10:43:03 -06:00
return json[json.length - 1].sha;
2019-04-26 20:58:04 -05:00
}).catch(function() {
2019-02-14 10:43:03 -06:00
// return undefined
});
}
2019-04-26 20:58:04 -05:00
function setDefaultOptions() {
return messageWorker({
task: 'defaults',
version: markedVersions[$markedVerElem.value]
});
2019-02-13 12:16:39 -06:00
}
2019-04-26 20:58:04 -05:00
function setOptions(opts) {
2019-02-13 12:16:39 -06:00
$optionsElem.value = JSON.stringify(
opts,
2019-04-26 20:58:04 -05:00
function(key, value) {
2019-02-13 12:16:39 -06:00
if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
return undefined;
}
return value;
}, ' ');
}
2019-04-26 20:58:04 -05:00
function searchToObject() {
2018-04-03 11:21:25 -05:00
// modified from https://stackoverflow.com/a/7090123/806777
const pairs = location.search.slice(1).split('&');
const obj = {};
2018-04-03 11:21:25 -05:00
for (let i = 0; i < pairs.length; i++) {
2018-04-03 11:21:25 -05:00
if (pairs[i] === '') {
continue;
}
const pair = pairs[i].split('=');
2018-04-03 11:21:25 -05:00
obj[decodeURIComponent(pair.shift())] = decodeURIComponent(pair.join('='));
}
return obj;
}
2019-04-26 20:58:04 -05:00
function getScrollSize() {
const e = $activeOutputElem;
2018-03-26 10:36:47 -05:00
return e.scrollHeight - e.clientHeight;
2020-04-01 21:08:44 -05:00
}
2019-02-14 11:14:15 -06:00
2019-04-26 20:58:04 -05:00
function getScrollPercent() {
const size = getScrollSize();
2018-03-26 10:36:47 -05:00
if (size <= 0) {
return 1;
}
2018-10-18 00:19:48 -07:00
return $activeOutputElem.scrollTop / size;
2020-04-01 21:08:44 -05:00
}
2019-02-14 11:14:15 -06:00
2019-04-26 20:58:04 -05:00
function setScrollPercent(percent) {
2018-10-18 00:19:48 -07:00
$activeOutputElem.scrollTop = percent * getScrollSize();
2020-04-01 21:08:44 -05:00
}
2018-03-26 10:36:47 -05:00
2019-04-26 20:58:04 -05:00
function updateLink() {
let outputType = '';
2018-04-03 11:21:25 -05:00
if ($outputTypeElem.value !== 'preview') {
outputType = 'outputType=' + $outputTypeElem.value + '&';
}
2018-10-18 00:19:48 -07:00
$permalinkElem.href = '?' + outputType + 'text=' + encodeURIComponent($markdownElem.value)
2019-02-08 14:11:09 -06:00
+ '&options=' + encodeURIComponent($optionsElem.value)
+ '&version=' + encodeURIComponent($markedVerElem.value);
2018-04-03 11:21:25 -05:00
history.replaceState('', document.title, $permalinkElem.href);
}
2019-04-26 20:58:04 -05:00
function updateVersion() {
handleInput();
return Promise.resolve();
}
2019-04-26 20:58:04 -05:00
function checkForChanges() {
if (inputDirty && $markedVerElem.value !== 'commit' && $markedVerElem.value !== 'pr') {
2018-03-26 10:36:47 -05:00
inputDirty = false;
2018-03-28 11:24:14 -05:00
2018-04-03 11:21:25 -05:00
updateLink();
2018-03-28 11:24:14 -05:00
let options = {};
const optionsString = $optionsElem.value || '{}';
2018-10-17 15:11:53 -07:00
try {
const newOptions = JSON.parse(optionsString);
2018-10-17 15:27:25 -07:00
options = newOptions;
2019-02-13 12:16:39 -06:00
$optionsElem.classList.remove('error');
2018-10-17 15:27:25 -07:00
} catch (err) {
2019-02-13 12:16:39 -06:00
$optionsElem.classList.add('error');
2018-10-17 15:11:53 -07:00
}
const version = markedVersions[$markedVerElem.value];
const markdown = $markdownElem.value;
const hash = version + markdown + optionsString;
2019-02-13 12:16:39 -06:00
if (lastInput !== hash) {
lastInput = hash;
delayTime = 100;
messageWorker({
task: 'parse',
version,
markdown,
options
});
2018-06-24 01:46:25 -05:00
}
2019-02-13 12:16:39 -06:00
}
checkChangeTimeout = window.setTimeout(checkForChanges, delayTime);
2020-04-01 21:08:44 -05:00
}
2018-03-26 10:36:47 -05:00
2019-04-26 20:58:04 -05:00
function setResponseTime(ms) {
let amount = ms;
let suffix = 'ms';
2019-02-13 14:18:15 -06:00
if (ms > 1000 * 60 * 60) {
amount = 'Too Long';
suffix = '';
} else if (ms > 1000 * 60) {
amount = '>' + Math.floor(ms / (1000 * 60));
suffix = 'm';
} else if (ms > 1000) {
amount = '>' + Math.floor(ms / 1000);
suffix = 's';
}
$responseTimeElem.textContent = amount + suffix;
$responseTimeElem.animate([
{ transform: 'scale(1.2)' },
{ transform: 'scale(1)' }
], 200);
2019-02-13 14:18:15 -06:00
}
2019-04-26 20:58:04 -05:00
function setParsed(parsed, lexed) {
2019-02-14 09:19:14 -06:00
try {
2019-02-13 12:16:39 -06:00
$previewIframe.contentDocument.body.innerHTML = parsed;
2019-02-14 09:19:14 -06:00
} catch (ex) {}
2019-02-13 12:16:39 -06:00
$htmlElem.value = parsed;
$lexerElem.value = lexed;
}
2018-03-26 10:36:47 -05:00
const workerPromises = {};
2019-04-26 20:58:04 -05:00
function messageWorker(message) {
2019-02-13 12:16:39 -06:00
if (!markedWorker || markedWorker.working) {
if (markedWorker) {
clearTimeout(markedWorker.timeout);
markedWorker.terminate();
2018-03-26 10:36:47 -05:00
}
2019-02-13 12:16:39 -06:00
markedWorker = new Worker('worker.js');
2019-04-26 20:58:04 -05:00
markedWorker.onmessage = function(e) {
2019-02-13 12:16:39 -06:00
clearTimeout(markedWorker.timeout);
markedWorker.working = false;
switch (e.data.task) {
case 'defaults': {
2019-02-13 12:16:39 -06:00
setOptions(e.data.defaults);
break;
}
case 'parse': {
2019-02-13 12:16:39 -06:00
$previewElem.classList.remove('error');
$htmlElem.classList.remove('error');
$lexerElem.classList.remove('error');
const scrollPercent = getScrollPercent();
2019-02-13 12:16:39 -06:00
setParsed(e.data.parsed, e.data.lexed);
setScrollPercent(scrollPercent);
2019-02-13 14:18:15 -06:00
setResponseTime(e.data.time);
2019-02-13 12:16:39 -06:00
break;
}
2019-02-13 12:16:39 -06:00
}
clearTimeout(checkChangeTimeout);
delayTime = 10;
checkForChanges();
workerPromises[e.data.id]();
delete workerPromises[e.data.id];
2019-02-13 12:16:39 -06:00
};
2019-04-26 20:58:04 -05:00
markedWorker.onerror = markedWorker.onmessageerror = function(err) {
2019-02-13 12:16:39 -06:00
clearTimeout(markedWorker.timeout);
let error = 'There was an error in the Worker';
2019-02-13 12:16:39 -06:00
if (err) {
if (err.message) {
error = err.message;
} else {
error = err;
}
}
2019-02-14 00:08:33 -06:00
error = error.replace(/^Uncaught Error: /, '');
2019-02-13 12:16:39 -06:00
$previewElem.classList.add('error');
$htmlElem.classList.add('error');
$lexerElem.classList.add('error');
setParsed(error, error);
setScrollPercent(0);
};
2018-03-26 10:36:47 -05:00
}
2019-02-14 00:08:33 -06:00
if (message.task !== 'defaults') {
markedWorker.working = true;
workerTimeout(0);
}
return new Promise(function(resolve) {
message.id = uniqueWorkerMessageId();
workerPromises[message.id] = resolve;
markedWorker.postMessage(message);
});
}
function uniqueWorkerMessageId() {
let id;
do {
id = Math.random().toString(36);
} while (id in workerPromises);
return id;
2019-02-13 14:18:15 -06:00
}
2019-04-26 20:58:04 -05:00
function workerTimeout(seconds) {
markedWorker.timeout = setTimeout(function() {
2019-02-13 14:18:15 -06:00
seconds++;
markedWorker.onerror('Marked has taken longer than ' + seconds + ' second' + (seconds > 1 ? 's' : '') + ' to respond...');
workerTimeout(seconds);
2019-02-13 12:16:39 -06:00
}, 1000);
}