2020-03-09 10:39:55 +00:00
|
|
|
const blob = new Blob(['importScripts("https://cdn.jsdelivr.net/npm/lzma@2.3.2/src/lzma_worker.min.js");']);
|
|
|
|
|
const lzma = new LZMA(window.URL.createObjectURL(blob));
|
|
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
let editor = null;
|
|
|
|
|
let select = null;
|
|
|
|
|
let clipboard = null;
|
2020-04-24 13:30:20 +00:00
|
|
|
let statsEl = null;
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-04-28 11:49:38 +00:00
|
|
|
const init = () => {
|
2020-05-14 16:32:34 +00:00
|
|
|
handleLegacyUrl();
|
2020-04-28 11:49:38 +00:00
|
|
|
initCodeEditor();
|
|
|
|
|
initLangSelector();
|
|
|
|
|
initCode();
|
|
|
|
|
initClipboard();
|
2020-06-02 14:03:00 +00:00
|
|
|
initModals();
|
2020-04-28 11:49:38 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const initCodeEditor = () => {
|
2020-03-09 14:54:31 +00:00
|
|
|
CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.52.0/mode/%N/%N.js';
|
2020-04-25 22:53:10 +00:00
|
|
|
editor = new CodeMirror(byId('editor'), {
|
2020-02-17 15:05:42 +00:00
|
|
|
lineNumbers: true,
|
2020-03-09 13:38:28 +00:00
|
|
|
theme: 'dracula',
|
2020-03-09 14:54:31 +00:00
|
|
|
readOnly: readOnly,
|
2020-04-27 15:03:02 +00:00
|
|
|
lineWrapping: false,
|
2020-04-22 16:50:44 +00:00
|
|
|
scrollbarStyle: 'simple',
|
2020-02-13 15:02:17 +00:00
|
|
|
});
|
2020-03-09 13:38:28 +00:00
|
|
|
if (readOnly) {
|
|
|
|
|
document.body.classList.add('readonly');
|
|
|
|
|
}
|
2020-04-24 13:30:20 +00:00
|
|
|
|
2020-04-25 22:53:10 +00:00
|
|
|
statsEl = byId('stats');
|
2020-04-24 13:30:20 +00:00
|
|
|
editor.on('change', () => {
|
|
|
|
|
statsEl.innerHTML = `Length: ${editor.getValue().length} | Lines: ${editor['doc'].size}`;
|
2020-04-28 13:46:29 +00:00
|
|
|
hideCopyBar();
|
2020-04-24 13:30:20 +00:00
|
|
|
});
|
2020-02-17 15:05:42 +00:00
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
const initLangSelector = () => {
|
2020-02-13 15:02:17 +00:00
|
|
|
select = new SlimSelect({
|
|
|
|
|
select: '#language',
|
2020-04-22 16:50:44 +00:00
|
|
|
data: CodeMirror.modeInfo.map((e) => ({
|
2020-02-21 14:23:26 +00:00
|
|
|
text: e.name,
|
2020-05-14 16:32:34 +00:00
|
|
|
value: shorten(e.name),
|
2020-04-22 16:50:44 +00:00
|
|
|
data: { mime: e.mime, mode: e.mode },
|
2020-02-21 14:23:26 +00:00
|
|
|
})),
|
2020-04-22 16:50:44 +00:00
|
|
|
showContent: 'down',
|
|
|
|
|
onChange: (e) => {
|
2020-02-21 14:23:26 +00:00
|
|
|
const language = e.data || { mime: null, mode: null };
|
|
|
|
|
editor.setOption('mode', language.mime);
|
|
|
|
|
CodeMirror.autoLoadMode(editor, language.mode);
|
2020-04-22 16:50:44 +00:00
|
|
|
},
|
2020-02-13 15:02:17 +00:00
|
|
|
});
|
|
|
|
|
|
2020-04-28 13:46:29 +00:00
|
|
|
// Set lang selector
|
2020-05-14 16:32:34 +00:00
|
|
|
const l = new URLSearchParams(window.location.search).get('l');
|
|
|
|
|
select.set(l ? decodeURIComponent(l) : shorten('Plain Text'));
|
2020-02-17 15:05:42 +00:00
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
const initCode = () => {
|
2020-05-14 16:32:34 +00:00
|
|
|
let base64 = location.hash.substr(1);
|
2020-02-13 15:02:17 +00:00
|
|
|
if (base64.length === 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-02-17 15:05:42 +00:00
|
|
|
decompress(base64, (code, err) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
alert('Failed to decompress data: ' + err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-28 11:49:38 +00:00
|
|
|
editor.setValue(code);
|
2020-02-17 15:05:42 +00:00
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-14 16:32:34 +00:00
|
|
|
const handleLegacyUrl = () => {
|
|
|
|
|
const lang = new URLSearchParams(window.location.search).get('lang');
|
|
|
|
|
const base = `${location.protocol}//${location.host}`;
|
|
|
|
|
if (location.hash.charAt(5) === '-') {
|
|
|
|
|
const hashedLang = location.hash.substr(1, 4);
|
|
|
|
|
const newLang = CodeMirror.modeInfo.find((e) => hash(e.name) === hashedLang);
|
|
|
|
|
const queryParams = newLang ? '?l=' + shorten(newLang.name) : '';
|
|
|
|
|
location.replace(`${base}/${queryParams}#${location.hash.substr(6)}`);
|
|
|
|
|
throw new Error('waiting for page to reload');
|
|
|
|
|
}
|
|
|
|
|
if (lang) {
|
|
|
|
|
location.replace(`${base}/${'?l=' + shorten(lang)}${location.hash}`);
|
|
|
|
|
throw new Error('waiting for page to reload');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
const initClipboard = () => {
|
|
|
|
|
clipboard = new ClipboardJS('.clipboard');
|
|
|
|
|
clipboard.on('success', () => {
|
|
|
|
|
hideCopyBar(true);
|
|
|
|
|
});
|
|
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-06-02 14:03:00 +00:00
|
|
|
const initModals = () => {
|
|
|
|
|
MicroModal.init({
|
|
|
|
|
onClose: () => editor.focus(),
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-22 16:50:44 +00:00
|
|
|
const generateLink = (mode) => {
|
2020-04-24 13:30:20 +00:00
|
|
|
const data = editor.getValue();
|
|
|
|
|
compress(data, (base64, err) => {
|
2020-02-17 15:05:42 +00:00
|
|
|
if (err) {
|
|
|
|
|
alert('Failed to compress data: ' + err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-03-05 13:33:21 +00:00
|
|
|
const url = buildUrl(base64, mode);
|
2020-05-15 13:05:59 +00:00
|
|
|
statsEl.innerHTML = `Data length: ${data.length} | Link length: ${url.length} | Compression ratio: ${Math.round(
|
|
|
|
|
(100 * url.length) / data.length
|
|
|
|
|
)}%`;
|
2020-04-24 13:30:20 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
showCopyBar(url);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Open the "Copy" bar and select the content
|
2020-04-22 16:50:44 +00:00
|
|
|
const showCopyBar = (dataToCopy) => {
|
2020-04-26 15:45:06 +00:00
|
|
|
byId('copy').classList.remove('hidden');
|
2020-04-25 22:53:10 +00:00
|
|
|
const linkInput = byId('copy-link');
|
2020-02-17 15:05:42 +00:00
|
|
|
linkInput.value = dataToCopy;
|
2020-04-26 15:45:06 +00:00
|
|
|
linkInput.focus();
|
2020-02-17 15:05:42 +00:00
|
|
|
linkInput.setSelectionRange(0, dataToCopy.length);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Close the "Copy" bar
|
2020-04-22 16:50:44 +00:00
|
|
|
const hideCopyBar = (success) => {
|
2020-04-25 22:53:10 +00:00
|
|
|
const copyButton = byId('copy-btn');
|
|
|
|
|
const copyBar = byId('copy');
|
2020-02-17 15:05:42 +00:00
|
|
|
if (!success) {
|
2020-04-24 10:24:08 +00:00
|
|
|
copyBar.classList.add('hidden');
|
2020-02-13 15:02:17 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2020-02-17 15:05:42 +00:00
|
|
|
copyButton.innerText = 'Copied !';
|
|
|
|
|
setTimeout(() => {
|
2020-04-24 10:24:08 +00:00
|
|
|
copyBar.classList.add('hidden');
|
2020-02-17 15:05:42 +00:00
|
|
|
copyButton.innerText = 'Copy';
|
|
|
|
|
}, 800);
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-26 15:45:06 +00:00
|
|
|
const disableLineWrapping = () => {
|
|
|
|
|
byId('disable-line-wrapping').classList.add('hidden');
|
|
|
|
|
byId('enable-line-wrapping').classList.remove('hidden');
|
|
|
|
|
editor.setOption('lineWrapping', false);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const enableLineWrapping = () => {
|
|
|
|
|
byId('enable-line-wrapping').classList.add('hidden');
|
|
|
|
|
byId('disable-line-wrapping').classList.remove('hidden');
|
|
|
|
|
editor.setOption('lineWrapping', true);
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-27 15:03:02 +00:00
|
|
|
const openInNewTab = () => {
|
2020-06-15 09:46:18 +00:00
|
|
|
window.open(location.href.replace(/[?&]readonly/, ''));
|
2020-04-27 15:03:02 +00:00
|
|
|
};
|
|
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
// Build a shareable URL
|
2020-03-05 13:33:21 +00:00
|
|
|
const buildUrl = (rawData, mode) => {
|
2020-05-15 13:05:59 +00:00
|
|
|
const base = `${location.protocol}//${location.host}${location.pathname}`;
|
2020-05-14 16:32:34 +00:00
|
|
|
const query = shorten('Plain Text') === select.selected() ? '' : `?l=${encodeURIComponent(select.selected())}`;
|
|
|
|
|
const url = base + query + '#' + rawData;
|
2020-03-09 13:38:28 +00:00
|
|
|
if (mode === 'markdown') {
|
2020-04-23 16:29:59 +00:00
|
|
|
return `[NoPaste snippet](${url})`;
|
2020-03-09 13:38:28 +00:00
|
|
|
}
|
|
|
|
|
if (mode === 'iframe') {
|
2020-05-14 16:32:34 +00:00
|
|
|
const height = editor['doc'].height + 45;
|
2020-04-27 15:03:02 +00:00
|
|
|
return `<iframe width="100%" height="${height}" frameborder="0" src="${url}"></iframe>`;
|
2020-03-09 13:38:28 +00:00
|
|
|
}
|
|
|
|
|
return url;
|
2020-02-17 15:05:42 +00:00
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
// Transform a compressed base64 string into a plain text string
|
|
|
|
|
const decompress = (base64, cb) => {
|
2020-04-25 22:53:10 +00:00
|
|
|
const progressBar = byId('progress');
|
2020-02-17 15:41:54 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
const req = new XMLHttpRequest();
|
|
|
|
|
req.open('GET', 'data:application/octet;base64,' + base64);
|
|
|
|
|
req.responseType = 'arraybuffer';
|
2020-04-22 16:50:44 +00:00
|
|
|
req.onload = (e) => {
|
2020-02-17 15:41:54 +00:00
|
|
|
lzma.decompress(
|
|
|
|
|
new Uint8Array(e.target.response),
|
|
|
|
|
(result, err) => {
|
|
|
|
|
progressBar.style.width = '0';
|
|
|
|
|
cb(result, err);
|
|
|
|
|
},
|
2020-04-22 16:50:44 +00:00
|
|
|
(progress) => {
|
2020-02-17 15:41:54 +00:00
|
|
|
progressBar.style.width = 100 * progress + '%';
|
|
|
|
|
}
|
|
|
|
|
);
|
2020-02-17 15:05:42 +00:00
|
|
|
};
|
|
|
|
|
req.send();
|
|
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-02-17 15:05:42 +00:00
|
|
|
// Transform a plain text string into a compressed base64 string
|
|
|
|
|
const compress = (str, cb) => {
|
2020-04-28 13:46:29 +00:00
|
|
|
if (str.length === 0) {
|
|
|
|
|
cb('');
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-04-25 22:53:10 +00:00
|
|
|
const progressBar = byId('progress');
|
2020-02-17 15:41:54 +00:00
|
|
|
|
|
|
|
|
lzma.compress(
|
|
|
|
|
str,
|
|
|
|
|
1,
|
|
|
|
|
(compressed, err) => {
|
|
|
|
|
if (err) {
|
|
|
|
|
progressBar.style.width = '0';
|
|
|
|
|
cb(compressed, err);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const reader = new FileReader();
|
|
|
|
|
reader.onload = () => {
|
|
|
|
|
progressBar.style.width = '0';
|
|
|
|
|
cb(reader.result.substr(reader.result.indexOf(',') + 1));
|
|
|
|
|
};
|
|
|
|
|
reader.readAsDataURL(new Blob([new Uint8Array(compressed)]));
|
|
|
|
|
},
|
2020-04-22 16:50:44 +00:00
|
|
|
(progress) => {
|
2020-02-17 15:41:54 +00:00
|
|
|
progressBar.style.width = 100 * progress + '%';
|
2020-02-13 15:02:17 +00:00
|
|
|
}
|
2020-02-17 15:41:54 +00:00
|
|
|
);
|
2020-02-17 15:05:42 +00:00
|
|
|
};
|
2020-02-13 15:02:17 +00:00
|
|
|
|
2020-04-22 16:50:44 +00:00
|
|
|
const slugify = (str) =>
|
2020-02-21 14:23:26 +00:00
|
|
|
str
|
2020-05-14 16:32:34 +00:00
|
|
|
.trim()
|
2020-02-21 14:23:26 +00:00
|
|
|
.toString()
|
|
|
|
|
.toLowerCase()
|
|
|
|
|
.replace(/\s+/g, '-')
|
|
|
|
|
.replace(/\+/g, '-p')
|
|
|
|
|
.replace(/#/g, '-sharp')
|
|
|
|
|
.replace(/[^\w\-]+/g, '');
|
|
|
|
|
|
2020-05-14 16:32:34 +00:00
|
|
|
const shorten = (name) => {
|
|
|
|
|
let n = slugify(name).replace('script', '-s').replace('python', 'py');
|
|
|
|
|
const nov = (s) => s[0] + s.substr(1).replace(/[aeiouy-]/g, '');
|
|
|
|
|
if (n.replace(/-/g, '').length <= 4) {
|
|
|
|
|
return n.replace(/-/g, '');
|
|
|
|
|
}
|
|
|
|
|
if (n.split('-').length >= 2) {
|
|
|
|
|
return n
|
|
|
|
|
.split('-')
|
|
|
|
|
.map((x) => nov(x.substr(0, 2)))
|
|
|
|
|
.join('')
|
|
|
|
|
.substr(0, 4);
|
|
|
|
|
}
|
|
|
|
|
n = nov(n);
|
|
|
|
|
if (n.length <= 4) {
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
return n.substr(0, 2) + n.substr(n.length - 2, 2);
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-25 22:53:10 +00:00
|
|
|
const byId = (id) => document.getElementById(id);
|
|
|
|
|
|
2020-05-14 16:32:34 +00:00
|
|
|
// Legacy code, only for retro-compatibility
|
2020-04-28 13:46:29 +00:00
|
|
|
const hash = function (str, seed = 0) {
|
|
|
|
|
let h1 = 0xdeadbeef ^ seed;
|
|
|
|
|
let h2 = 0x41c6ce57 ^ seed;
|
|
|
|
|
for (let i = 0, ch; i < str.length; i++) {
|
|
|
|
|
ch = str.charCodeAt(i);
|
|
|
|
|
h1 = Math.imul(h1 ^ ch, 2654435761);
|
|
|
|
|
h2 = Math.imul(h2 ^ ch, 1597334677);
|
|
|
|
|
}
|
|
|
|
|
h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
|
|
|
|
|
h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
|
|
|
|
|
const h = 4294967296 * (2097151 & h2) + (h1 >>> 0);
|
|
|
|
|
return h.toString(36).substr(0, 4).toUpperCase();
|
|
|
|
|
};
|
|
|
|
|
|
2020-05-14 16:32:34 +00:00
|
|
|
// Only for tests purposes
|
2020-03-09 14:54:31 +00:00
|
|
|
const testAllModes = () => {
|
|
|
|
|
for (const [index, language] of Object.entries(CodeMirror.modeInfo)) {
|
2020-05-14 16:32:34 +00:00
|
|
|
CodeMirror.autoLoadMode(editor, language.mode);
|
2020-03-09 14:54:31 +00:00
|
|
|
setTimeout(() => {
|
|
|
|
|
console.info(language.name);
|
2020-05-14 16:32:34 +00:00
|
|
|
select.set(shorten(language.name));
|
2020-03-09 14:54:31 +00:00
|
|
|
}, 1000 * index);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2020-04-28 11:49:38 +00:00
|
|
|
init();
|