123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 |
- onunhandledrejection = (e) => {
- throw e.reason;
- };
- const $loadingElem = document.querySelector('#loading');
- const $mainElem = document.querySelector('#main');
- const $markdownElem = document.querySelector('#markdown');
- const $markedVerElem = document.querySelector('#markedVersion');
- 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;
- let latestVersion = 'master';
- const search = searchToObject();
- const markedVersions = {
- master: '../'
- };
- let delayTime = 1;
- let checkChangeTimeout = null;
- let markedWorker;
- $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);
- $clearElem.addEventListener('click', handleClearClick, false);
- Promise.all([
- setInitialQuickref(),
- setInitialOutputType(),
- setInitialText(),
- setInitialVersion()
- .then(setInitialOptions)
- ]).then(() => {
- handleInputChange();
- handleOutputChange();
- checkForChanges();
- setScrollPercent(0);
- $loadingElem.style.display = 'none';
- $mainElem.style.display = 'block';
- }).catch(() => {
- $loadingElem.classList.add('loadingError');
- $loadingElem.textContent = 'Failed to load marked. Refresh the page to try again.';
- });
- function setInitialText() {
- if ('text' in search) {
- $markdownElem.value = search.text;
- } else {
- return fetch('./initial.md')
- .then(res => res.text())
- .then(text => {
- if ($markdownElem.value === '') {
- $markdownElem.value = text;
- }
- });
- }
- }
- function setInitialQuickref() {
- return fetch('./quickref.md')
- .then(res => res.text())
- .then(text => {
- document.querySelector('#quickref').value = text;
- });
- }
- function setInitialVersion() {
- return fetch('https://data.jsdelivr.com/v1/package/npm/marked')
- .then(res => res.json())
- .then(json => {
- for (const ver of json.versions) {
- markedVersions[ver] = 'https://cdn.jsdelivr.net/npm/marked@' + ver;
- const opt = document.createElement('option');
- opt.textContent = ver;
- opt.value = ver;
- $markedVerElem.appendChild(opt);
- }
- if (location.host === 'marked.js.org') {
- latestVersion = json.tags.latest;
- } else {
- $markedVerElem.querySelector('option[value="master"]').textContent = 'This Build';
- }
- if (search.version && markedVersions[search.version]) {
- $markedVerElem.value = search.version;
- return;
- }
- $markedVerElem.value = latestVersion;
- })
- .then(updateVersion);
- }
- function setInitialOptions() {
- if ('options' in search) {
- $optionsElem.value = search.options;
- } else {
- return setDefaultOptions();
- }
- }
- function setInitialOutputType() {
- if (search.outputType) {
- $outputTypeElem.value = search.outputType;
- }
- }
- function handleIframeLoad() {
- lastInput = '';
- inputDirty = true;
- }
- function handleInput() {
- inputDirty = true;
- }
- function handleVersionChange() {
- updateVersion();
- }
- function handleClearClick() {
- $markdownElem.value = '';
- $markedVerElem.value = latestVersion;
- updateVersion();
- setDefaultOptions();
- }
- function handleInputChange() {
- handleChange($inputPanes, $inputTypeElem.value);
- }
- function handleOutputChange() {
- $activeOutputElem = handleChange($panes, $outputTypeElem.value);
- updateLink();
- }
- function handleChange(panes, visiblePane) {
- let active = null;
- for (let i = 0; i < panes.length; i++) {
- if (panes[i].id === visiblePane) {
- panes[i].style.display = '';
- active = panes[i];
- } else {
- panes[i].style.display = 'none';
- }
- }
- return active;
- }
- function setDefaultOptions() {
- return messageWorker({
- task: 'defaults',
- version: markedVersions[$markedVerElem.value]
- });
- }
- function setOptions(opts) {
- $optionsElem.value = JSON.stringify(
- opts,
- (key, value) => {
- if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
- return undefined;
- }
- return value;
- }, ' ');
- }
- function searchToObject() {
- // modified from https://stackoverflow.com/a/7090123/806777
- const pairs = location.search.slice(1).split('&');
- const obj = {};
- for (let i = 0; i < pairs.length; i++) {
- if (pairs[i] === '') {
- continue;
- }
- const pair = pairs[i].split('=');
- obj[decodeURIComponent(pair.shift())] = decodeURIComponent(pair.join('='));
- }
- return obj;
- }
- function getScrollSize() {
- if (!$activeOutputElem) {
- return 0;
- }
- const e = $activeOutputElem;
- return e.scrollHeight - e.clientHeight;
- }
- function getScrollPercent() {
- if (!$activeOutputElem) {
- return 1;
- }
- const size = getScrollSize();
- if (size <= 0) {
- return 1;
- }
- return $activeOutputElem.scrollTop / size;
- }
- function setScrollPercent(percent) {
- if ($activeOutputElem) {
- $activeOutputElem.scrollTop = percent * getScrollSize();
- }
- }
- function updateLink() {
- let outputType = '';
- if ($outputTypeElem.value !== 'preview') {
- outputType = 'outputType=' + $outputTypeElem.value + '&';
- }
- $permalinkElem.href = '?' + outputType + 'text=' + encodeURIComponent($markdownElem.value)
- + '&options=' + encodeURIComponent($optionsElem.value)
- + '&version=' + encodeURIComponent($markedVerElem.value);
- history.replaceState('', document.title, $permalinkElem.href);
- }
- function updateVersion() {
- handleInput();
- }
- function checkForChanges() {
- if (inputDirty && $markedVerElem.value !== 'pr') {
- inputDirty = false;
- updateLink();
- let options = {};
- const optionsString = $optionsElem.value || '{}';
- try {
- const newOptions = JSON.parse(optionsString);
- options = newOptions;
- $optionsElem.classList.remove('error');
- } catch {
- $optionsElem.classList.add('error');
- }
- const version = markedVersions[$markedVerElem.value];
- const markdown = $markdownElem.value;
- const hash = version + markdown + optionsString;
- if (lastInput !== hash) {
- lastInput = hash;
- delayTime = 100;
- messageWorker({
- task: 'parse',
- version,
- markdown,
- options
- });
- }
- }
- checkChangeTimeout = window.setTimeout(checkForChanges, delayTime);
- }
- function setResponseTime(ms) {
- let amount = ms;
- let suffix = 'ms';
- 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);
- }
- function setParsed(parsed, lexed) {
- try {
- $previewIframe.contentDocument.body.innerHTML = parsed;
- } catch {}
- $htmlElem.value = parsed;
- $lexerElem.value = lexed;
- }
- const workerPromises = {};
- function messageWorker(message) {
- if (!markedWorker || markedWorker.working) {
- if (markedWorker) {
- clearTimeout(markedWorker.timeout);
- markedWorker.terminate();
- }
- markedWorker = new Worker('worker.js');
- markedWorker.onmessage = (e) => {
- clearTimeout(markedWorker.timeout);
- markedWorker.working = false;
- switch (e.data.task) {
- case 'defaults': {
- setOptions(e.data.defaults);
- break;
- }
- case 'parse': {
- $previewElem.classList.remove('error');
- $htmlElem.classList.remove('error');
- $lexerElem.classList.remove('error');
- const scrollPercent = getScrollPercent();
- setParsed(e.data.parsed, e.data.lexed);
- setScrollPercent(scrollPercent);
- setResponseTime(e.data.time);
- break;
- }
- }
- clearTimeout(checkChangeTimeout);
- delayTime = 10;
- checkForChanges();
- workerPromises[e.data.id]();
- delete workerPromises[e.data.id];
- };
- markedWorker.onerror = markedWorker.onmessageerror = (err) => {
- clearTimeout(markedWorker.timeout);
- let error = 'There was an error in the Worker';
- if (err) {
- if (err.message) {
- error = err.message;
- } else {
- error = err;
- }
- }
- error = error.replace(/^Uncaught Error: /, '');
- $previewElem.classList.add('error');
- $htmlElem.classList.add('error');
- $lexerElem.classList.add('error');
- setParsed(error, error);
- setScrollPercent(0);
- };
- }
- if (message.task !== 'defaults') {
- markedWorker.working = true;
- workerTimeout(0);
- }
- return new Promise(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;
- }
- function workerTimeout(seconds) {
- markedWorker.timeout = setTimeout(() => {
- seconds++;
- markedWorker.onerror('Marked has taken longer than ' + seconds + ' second' + (seconds > 1 ? 's' : '') + ' to respond...');
- workerTimeout(seconds);
- }, 1000);
- }
|