use worker to prevent dos
This commit is contained in:
parent
3fe557209a
commit
acf9c82388
@ -62,7 +62,7 @@ header h1 {
|
|||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#options.badParse {
|
.error {
|
||||||
border-color: red;
|
border-color: red;
|
||||||
background-color: #FEE
|
background-color: #FEE
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,17 @@ if (!window.fetch) {
|
|||||||
window.fetch = unfetch;
|
window.fetch = unfetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onunhandledrejection = function (e) {
|
||||||
|
throw e.reason;
|
||||||
|
};
|
||||||
|
|
||||||
var $markdownElem = document.querySelector('#markdown');
|
var $markdownElem = document.querySelector('#markdown');
|
||||||
var $markedVerElem = document.querySelector('#markedVersion');
|
var $markedVerElem = document.querySelector('#markedVersion');
|
||||||
var $markedVer = document.querySelector('#markedCdn');
|
var $markedVer = document.querySelector('#markedCdn');
|
||||||
var $optionsElem = document.querySelector('#options');
|
var $optionsElem = document.querySelector('#options');
|
||||||
var $outputTypeElem = document.querySelector('#outputType');
|
var $outputTypeElem = document.querySelector('#outputType');
|
||||||
var $inputTypeElem = document.querySelector('#inputType');
|
var $inputTypeElem = document.querySelector('#inputType');
|
||||||
|
var $previewElem = document.querySelector('#preview');
|
||||||
var $previewIframe = document.querySelector('#preview iframe');
|
var $previewIframe = document.querySelector('#preview iframe');
|
||||||
var $permalinkElem = document.querySelector('#permalink');
|
var $permalinkElem = document.querySelector('#permalink');
|
||||||
var $clearElem = document.querySelector('#clear');
|
var $clearElem = document.querySelector('#clear');
|
||||||
@ -20,6 +25,7 @@ var $htmlElem = document.querySelector('#html');
|
|||||||
var $lexerElem = document.querySelector('#lexer');
|
var $lexerElem = document.querySelector('#lexer');
|
||||||
var $panes = document.querySelectorAll('.pane');
|
var $panes = document.querySelectorAll('.pane');
|
||||||
var $inputPanes = document.querySelectorAll('.inputPane');
|
var $inputPanes = document.querySelectorAll('.inputPane');
|
||||||
|
var lastInput = '';
|
||||||
var inputDirty = true;
|
var inputDirty = true;
|
||||||
var $activeOutputElem = null;
|
var $activeOutputElem = null;
|
||||||
var search = searchToObject();
|
var search = searchToObject();
|
||||||
@ -74,14 +80,7 @@ fetch('https://data.jsdelivr.com/v1/package/npm/marked')
|
|||||||
if ('options' in search && search.options) {
|
if ('options' in search && search.options) {
|
||||||
$optionsElem.value = search.options;
|
$optionsElem.value = search.options;
|
||||||
} else {
|
} else {
|
||||||
$optionsElem.value = JSON.stringify(
|
setDefaultOptions();
|
||||||
marked.getDefaults(),
|
|
||||||
function (key, value) {
|
|
||||||
if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}, ' ');
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -141,17 +140,31 @@ $optionsElem.addEventListener('keydown', handleInput, false);
|
|||||||
$clearElem.addEventListener('click', function () {
|
$clearElem.addEventListener('click', function () {
|
||||||
$markdownElem.value = '';
|
$markdownElem.value = '';
|
||||||
$markedVerElem.value = 'master';
|
$markedVerElem.value = 'master';
|
||||||
updateVersion().then(function () {
|
updateVersion().then(setDefaultOptions);
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
function setDefaultOptions() {
|
||||||
|
if (window.Worker) {
|
||||||
|
messageWorker({
|
||||||
|
task: 'defaults',
|
||||||
|
version: markedVersions[$markedVerElem.value]}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
var defaults = marked.getDefaults();
|
||||||
|
setOptions(defaults);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOptions(opts) {
|
||||||
$optionsElem.value = JSON.stringify(
|
$optionsElem.value = JSON.stringify(
|
||||||
marked.getDefaults(),
|
opts,
|
||||||
function (key, value) {
|
function (key, value) {
|
||||||
if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
|
if (value && typeof value === 'object' && Object.getPrototypeOf(value) !== Object.prototype) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}, ' ');
|
}, ' ');
|
||||||
});
|
}
|
||||||
}, false);
|
|
||||||
|
|
||||||
function searchToObject() {
|
function searchToObject() {
|
||||||
// modified from https://stackoverflow.com/a/7090123/806777
|
// modified from https://stackoverflow.com/a/7090123/806777
|
||||||
@ -213,6 +226,10 @@ function updateLink() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateVersion() {
|
function updateVersion() {
|
||||||
|
if (window.Worker) {
|
||||||
|
handleInput();
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
var promise;
|
var promise;
|
||||||
if ($markedVerElem.value in markedVersionCache) {
|
if ($markedVerElem.value in markedVersionCache) {
|
||||||
promise = Promise.resolve(markedVersionCache[$markedVerElem.value]);
|
promise = Promise.resolve(markedVersionCache[$markedVerElem.value]);
|
||||||
@ -234,30 +251,40 @@ function updateVersion() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var delayTime = 1;
|
var delayTime = 1;
|
||||||
var options = {};
|
var checkChangeTimeout = null;
|
||||||
function checkForChanges() {
|
function checkForChanges() {
|
||||||
if (inputDirty && typeof marked !== 'undefined') {
|
if (inputDirty && (typeof marked !== 'undefined' || window.Worker)) {
|
||||||
inputDirty = false;
|
inputDirty = false;
|
||||||
|
|
||||||
updateLink();
|
updateLink();
|
||||||
|
|
||||||
var startTime = new Date();
|
var options = {};
|
||||||
|
|
||||||
var scrollPercent = getScrollPercent();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var optionsString = $optionsElem.value || '{}';
|
var optionsString = $optionsElem.value || '{}';
|
||||||
|
try {
|
||||||
var newOptions = JSON.parse(optionsString);
|
var newOptions = JSON.parse(optionsString);
|
||||||
options = newOptions;
|
options = newOptions;
|
||||||
$optionsElem.classList.remove('badParse');
|
$optionsElem.classList.remove('error');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
$optionsElem.classList.add('badParse');
|
$optionsElem.classList.add('error');
|
||||||
}
|
}
|
||||||
|
|
||||||
var lexed = marked.lexer($markdownElem.value, options);
|
var version = markedVersions[$markedVerElem.value];
|
||||||
|
var markdown = $markdownElem.value;
|
||||||
|
var hash = version + markdown + optionsString;
|
||||||
|
if (lastInput !== hash) {
|
||||||
|
lastInput = hash;
|
||||||
|
if (window.Worker) {
|
||||||
|
delayTime = 100;
|
||||||
|
messageWorker({
|
||||||
|
task: 'parse',
|
||||||
|
version: version,
|
||||||
|
markdown: markdown,
|
||||||
|
options: options
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
var startTime = new Date();
|
||||||
|
var lexed = marked.lexer(markdown, options);
|
||||||
var lexedList = [];
|
var lexedList = [];
|
||||||
|
|
||||||
for (var i = 0; i < lexed.length; i++) {
|
for (var i = 0; i < lexed.length; i++) {
|
||||||
var lexedLine = [];
|
var lexedLine = [];
|
||||||
for (var j in lexed[i]) {
|
for (var j in lexed[i]) {
|
||||||
@ -265,17 +292,10 @@ function checkForChanges() {
|
|||||||
}
|
}
|
||||||
lexedList.push('{' + lexedLine.join(', ') + '}');
|
lexedList.push('{' + lexedLine.join(', ') + '}');
|
||||||
}
|
}
|
||||||
|
|
||||||
var parsed = marked.parser(lexed, options);
|
var parsed = marked.parser(lexed, options);
|
||||||
|
var scrollPercent = getScrollPercent();
|
||||||
if (iframeLoaded) {
|
setParsed(parsed, lexedList.join('\n'));
|
||||||
$previewIframe.contentDocument.body.innerHTML = (parsed);
|
|
||||||
}
|
|
||||||
$htmlElem.value = (parsed);
|
|
||||||
$lexerElem.value = (lexedList.join('\n'));
|
|
||||||
|
|
||||||
setScrollPercent(scrollPercent);
|
setScrollPercent(scrollPercent);
|
||||||
|
|
||||||
var endTime = new Date();
|
var endTime = new Date();
|
||||||
delayTime = endTime - startTime;
|
delayTime = endTime - startTime;
|
||||||
if (delayTime < 50) {
|
if (delayTime < 50) {
|
||||||
@ -284,7 +304,69 @@ function checkForChanges() {
|
|||||||
delayTime = 1000;
|
delayTime = 1000;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
window.setTimeout(checkForChanges, delayTime);
|
}
|
||||||
|
}
|
||||||
|
checkChangeTimeout = window.setTimeout(checkForChanges, delayTime);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function setParsed(parsed, lexed) {
|
||||||
|
if (iframeLoaded) {
|
||||||
|
$previewIframe.contentDocument.body.innerHTML = parsed;
|
||||||
|
}
|
||||||
|
$htmlElem.value = parsed;
|
||||||
|
$lexerElem.value = lexed;
|
||||||
|
}
|
||||||
|
|
||||||
|
var markedWorker;
|
||||||
|
function messageWorker(message) {
|
||||||
|
if (!markedWorker || markedWorker.working) {
|
||||||
|
if (markedWorker) {
|
||||||
|
clearTimeout(markedWorker.timeout);
|
||||||
|
markedWorker.terminate();
|
||||||
|
}
|
||||||
|
markedWorker = new Worker('worker.js');
|
||||||
|
markedWorker.onmessage = function (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');
|
||||||
|
var scrollPercent = getScrollPercent();
|
||||||
|
setParsed(e.data.parsed, e.data.lexed);
|
||||||
|
setScrollPercent(scrollPercent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
clearTimeout(checkChangeTimeout);
|
||||||
|
delayTime = 10;
|
||||||
|
checkForChanges();
|
||||||
|
};
|
||||||
|
markedWorker.onerror = markedWorker.onmessageerror = function (err) {
|
||||||
|
clearTimeout(markedWorker.timeout);
|
||||||
|
var error = 'There was an error in the Worker';
|
||||||
|
if (err) {
|
||||||
|
if (err.message) {
|
||||||
|
error = err.message;
|
||||||
|
} else {
|
||||||
|
error = err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$previewElem.classList.add('error');
|
||||||
|
$htmlElem.classList.add('error');
|
||||||
|
$lexerElem.classList.add('error');
|
||||||
|
setParsed(error, error);
|
||||||
|
setScrollPercent(0);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
markedWorker.working = true;
|
||||||
|
markedWorker.timeout = setTimeout(function () {
|
||||||
|
markedWorker.onerror('Marked is taking a while...');
|
||||||
|
}, 1000);
|
||||||
|
markedWorker.postMessage(message);
|
||||||
|
}
|
||||||
checkForChanges();
|
checkForChanges();
|
||||||
setScrollPercent(0);
|
setScrollPercent(0);
|
||||||
|
75
docs/demo/worker.js
Normal file
75
docs/demo/worker.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
/* global marked */
|
||||||
|
var versionCache = {};
|
||||||
|
var currentVersion;
|
||||||
|
onmessage = function (e) {
|
||||||
|
if (e.data.version === currentVersion) {
|
||||||
|
parse(e);
|
||||||
|
} else {
|
||||||
|
getVersion(e.data.version).then(function (text) {
|
||||||
|
// eslint-disable-next-line no-new-func
|
||||||
|
Function(text)();
|
||||||
|
currentVersion = e.data.version;
|
||||||
|
|
||||||
|
parse(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onunhandledrejection = function (e) {
|
||||||
|
throw e.reason;
|
||||||
|
};
|
||||||
|
|
||||||
|
function parse(e) {
|
||||||
|
switch (e.data.task) {
|
||||||
|
case 'defaults':
|
||||||
|
var defaults = marked.getDefaults();
|
||||||
|
defaults.renderer = null;
|
||||||
|
postMessage({
|
||||||
|
task: e.data.task,
|
||||||
|
defaults: defaults
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case 'parse':
|
||||||
|
var lexed = marked.lexer(e.data.markdown, e.data.options);
|
||||||
|
var lexedList = [];
|
||||||
|
for (var i = 0; i < lexed.length; i++) {
|
||||||
|
var lexedLine = [];
|
||||||
|
for (var j in lexed[i]) {
|
||||||
|
lexedLine.push(j + ':' + jsonString(lexed[i][j]));
|
||||||
|
}
|
||||||
|
lexedList.push('{' + lexedLine.join(', ') + '}');
|
||||||
|
}
|
||||||
|
var parsed = marked.parser(lexed, e.data.options);
|
||||||
|
|
||||||
|
postMessage({
|
||||||
|
task: e.data.task,
|
||||||
|
lexed: lexedList.join('\n'),
|
||||||
|
parsed: parsed
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function jsonString(input) {
|
||||||
|
var output = (input + '')
|
||||||
|
.replace(/\n/g, '\\n')
|
||||||
|
.replace(/\r/g, '\\r')
|
||||||
|
.replace(/\t/g, '\\t')
|
||||||
|
.replace(/\f/g, '\\f')
|
||||||
|
.replace(/[\\"']/g, '\\$&')
|
||||||
|
.replace(/\u0000/g, '\\0');
|
||||||
|
return '"' + output + '"';
|
||||||
|
};
|
||||||
|
|
||||||
|
function getVersion(ver) {
|
||||||
|
if (ver in versionCache) {
|
||||||
|
return Promise.resolve(versionCache[ver]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fetch(ver)
|
||||||
|
.then(function (res) { return res.text(); })
|
||||||
|
.then(function (text) {
|
||||||
|
versionCache[ver] = text;
|
||||||
|
return text;
|
||||||
|
});
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user