index.js (view raw)
1const blob = new Blob(['importScripts("https://cdn.jsdelivr.net/npm/lzma@2.3.2/src/lzma_worker.min.js");']);
2const lzma = new LZMA(window.URL.createObjectURL(blob));
3
4let editor = null;
5let select = null;
6let clipboard = null;
7
8const init = () => {
9 initCodeEditor();
10 initLangSelector();
11 initCode();
12 initClipboard();
13};
14
15const initCodeEditor = () => {
16 const readOnly = new URLSearchParams(window.location.search).has('readonly');
17 CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.51.0/mode/%N/%N.js';
18 editor = new CodeMirror(document.getElementById('editor'), {
19 lineNumbers: true,
20 theme: 'dracula',
21 readOnly: readOnly
22 });
23 if (readOnly) {
24 document.body.classList.add('readonly');
25 }
26};
27
28const initLangSelector = () => {
29 select = new SlimSelect({
30 select: '#language',
31 data: CodeMirror.modeInfo.map(e => ({
32 text: e.name,
33 value: slugify(e.name),
34 data: { mime: e.mime, mode: e.mode }
35 })),
36 showContent: 'up',
37 onChange: e => {
38 const language = e.data || { mime: null, mode: null };
39 editor.setOption('mode', language.mime);
40 CodeMirror.autoLoadMode(editor, language.mode);
41 }
42 });
43
44 select.set(decodeURIComponent(new URLSearchParams(window.location.search).get('lang') || 'plain-text'));
45};
46
47const initCode = () => {
48 const base64 = location.hash.substr(1);
49 if (base64.length === 0) {
50 return;
51 }
52 decompress(base64, (code, err) => {
53 if (err) {
54 alert('Failed to decompress data: ' + err);
55 return;
56 }
57 editor.setValue(code);
58 });
59};
60
61const initClipboard = () => {
62 clipboard = new ClipboardJS('.clipboard');
63 clipboard.on('success', () => {
64 hideCopyBar(true);
65 });
66};
67
68const generateLink = mode => {
69 compress(editor.getValue(), (base64, err) => {
70 if (err) {
71 alert('Failed to compress data: ' + err);
72 return;
73 }
74 const url = buildUrl(base64, mode);
75 showCopyBar(url);
76 });
77};
78
79// Open the "Copy" bar and select the content
80const showCopyBar = dataToCopy => {
81 const linkInput = document.getElementById('copy-link');
82 linkInput.value = dataToCopy;
83 linkInput.setSelectionRange(0, dataToCopy.length);
84 document.getElementById('copy').style.display = 'flex';
85};
86
87// Close the "Copy" bar
88const hideCopyBar = success => {
89 const copyButton = document.getElementById('copy-btn');
90 const copyBar = document.getElementById('copy');
91 if (!success) {
92 copyBar.style.display = 'none';
93 return;
94 }
95 copyButton.innerText = 'Copied !';
96 setTimeout(() => {
97 copyBar.style.display = 'none';
98 copyButton.innerText = 'Copy';
99 }, 800);
100};
101
102// Build a shareable URL
103const buildUrl = (rawData, mode) => {
104 const url =
105 `${location.protocol}//${location.host}${location.pathname}` +
106 `?lang=${encodeURIComponent(select.selected())}` +
107 (mode === 'iframe' ? '&readonly' : '') +
108 `#${rawData}`;
109 if (mode === 'markdown') {
110 return `[paste](${url})`;
111 }
112 if (mode === 'iframe') {
113 const height = document.getElementsByClassName('CodeMirror-sizer')[0].scrollHeight;
114 return `<iframe width="100%" height="${height}" frameborder="0" src="${url}"></iframe>`;
115 }
116 return url;
117};
118
119// Transform a compressed base64 string into a plain text string
120const decompress = (base64, cb) => {
121 const progressBar = document.getElementById('progress');
122
123 const req = new XMLHttpRequest();
124 req.open('GET', 'data:application/octet;base64,' + base64);
125 req.responseType = 'arraybuffer';
126 req.onload = e => {
127 lzma.decompress(
128 new Uint8Array(e.target.response),
129 (result, err) => {
130 progressBar.style.width = '0';
131 cb(result, err);
132 },
133 progress => {
134 progressBar.style.width = 100 * progress + '%';
135 }
136 );
137 };
138 req.send();
139};
140
141// Transform a plain text string into a compressed base64 string
142const compress = (str, cb) => {
143 const progressBar = document.getElementById('progress');
144
145 lzma.compress(
146 str,
147 1,
148 (compressed, err) => {
149 if (err) {
150 progressBar.style.width = '0';
151 cb(compressed, err);
152 return;
153 }
154 const reader = new FileReader();
155 reader.onload = () => {
156 progressBar.style.width = '0';
157 cb(reader.result.substr(reader.result.indexOf(',') + 1));
158 };
159 reader.readAsDataURL(new Blob([new Uint8Array(compressed)]));
160 },
161 progress => {
162 progressBar.style.width = 100 * progress + '%';
163 }
164 );
165};
166
167const slugify = str =>
168 str
169 .toString()
170 .toLowerCase()
171 .replace(/\s+/g, '-')
172 .replace(/\+/g, '-p')
173 .replace(/#/g, '-sharp')
174 .replace(/[^\w\-]+/g, '');
175
176init();