all repos — NoPaste @ 83abec6015edce47355ec0b122af3bea37ec0ee8

Resurrected - The PussTheCat.org fork of NoPaste

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;
  7let statsEl = null;
  8
  9const initCodeEditor = (initialValue) => {
 10    CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.52.0/mode/%N/%N.js';
 11    editor = new CodeMirror(byId('editor'), {
 12        lineNumbers: true,
 13        theme: 'dracula',
 14        readOnly: readOnly,
 15        lineWrapping: false,
 16        scrollbarStyle: 'simple',
 17        value: initialValue,
 18    });
 19    if (readOnly) {
 20        document.body.classList.add('readonly');
 21    }
 22
 23    statsEl = byId('stats');
 24    statsEl.innerHTML = `Length: ${initialValue.length} |  Lines: ${editor['doc'].size}`;
 25    editor.on('change', () => {
 26        statsEl.innerHTML = `Length: ${editor.getValue().length} |  Lines: ${editor['doc'].size}`;
 27    });
 28    initLangSelector();
 29};
 30
 31const initLangSelector = () => {
 32    select = new SlimSelect({
 33        select: '#language',
 34        data: CodeMirror.modeInfo.map((e) => ({
 35            text: e.name,
 36            value: slugify(e.name),
 37            data: { mime: e.mime, mode: e.mode },
 38        })),
 39        showContent: 'down',
 40        onChange: (e) => {
 41            const language = e.data || { mime: null, mode: null };
 42            editor.setOption('mode', language.mime);
 43            CodeMirror.autoLoadMode(editor, language.mode);
 44        },
 45    });
 46
 47    select.set(decodeURIComponent(new URLSearchParams(window.location.search).get('lang') || 'plain-text'));
 48};
 49
 50const initCode = () => {
 51    const base64 = location.pathname.substr(1) || location.hash.substr(1);
 52    if (base64.length === 0) {
 53        initCodeEditor('');
 54        return;
 55    }
 56    decompress(base64, (code, err) => {
 57        if (err) {
 58            alert('Failed to decompress data: ' + err);
 59            initCodeEditor('');
 60            return;
 61        }
 62        initCodeEditor(code);
 63    });
 64};
 65
 66const initClipboard = () => {
 67    clipboard = new ClipboardJS('.clipboard');
 68    clipboard.on('success', () => {
 69        hideCopyBar(true);
 70    });
 71};
 72
 73const generateLink = (mode) => {
 74    const data = editor.getValue();
 75    compress(data, (base64, err) => {
 76        if (err) {
 77            alert('Failed to compress data: ' + err);
 78            return;
 79        }
 80        const url = buildUrl(base64, mode);
 81        statsEl.innerHTML = `Data length: ${data.length} |  Link length: ${
 82            url.length
 83        } | Compression ratio: ${Math.round((100 * url.length) / data.length)}%`;
 84
 85        showCopyBar(url);
 86    });
 87};
 88
 89// Open the "Copy" bar and select the content
 90const showCopyBar = (dataToCopy) => {
 91    byId('copy').classList.remove('hidden');
 92    const linkInput = byId('copy-link');
 93    linkInput.value = dataToCopy;
 94    linkInput.focus();
 95    linkInput.setSelectionRange(0, dataToCopy.length);
 96};
 97
 98// Close the "Copy" bar
 99const hideCopyBar = (success) => {
100    const copyButton = byId('copy-btn');
101    const copyBar = byId('copy');
102    if (!success) {
103        copyBar.classList.add('hidden');
104        return;
105    }
106    copyButton.innerText = 'Copied !';
107    setTimeout(() => {
108        copyBar.classList.add('hidden');
109        copyButton.innerText = 'Copy';
110    }, 800);
111};
112
113const disableLineWrapping = () => {
114    byId('disable-line-wrapping').classList.add('hidden');
115    byId('enable-line-wrapping').classList.remove('hidden');
116    editor.setOption('lineWrapping', false);
117};
118
119const enableLineWrapping = () => {
120    byId('enable-line-wrapping').classList.add('hidden');
121    byId('disable-line-wrapping').classList.remove('hidden');
122    editor.setOption('lineWrapping', true);
123};
124
125const openInNewTab = () => {
126    window.open(location.href.replace('&readonly', ''));
127};
128
129// Build a shareable URL
130const buildUrl = (rawData, mode) => {
131    const base = `${location.protocol}//${location.host}/`;
132    const query = `?lang=${encodeURIComponent(select.selected())}`;
133    const url = rawData.length <= 4000 ? base + rawData + query : base + query + '#' + rawData;
134    if (mode === 'markdown') {
135        return `[NoPaste snippet](${url})`;
136    }
137    if (mode === 'iframe') {
138        const height = Math.min(editor['doc'].height + 45, 800);
139        return `<iframe width="100%" height="${height}" frameborder="0" src="${url}"></iframe>`;
140    }
141    return url;
142};
143
144// Transform a compressed base64 string into a plain text string
145const decompress = (base64, cb) => {
146    const progressBar = byId('progress');
147
148    const req = new XMLHttpRequest();
149    req.open('GET', 'data:application/octet;base64,' + base64);
150    req.responseType = 'arraybuffer';
151    req.onload = (e) => {
152        lzma.decompress(
153            new Uint8Array(e.target.response),
154            (result, err) => {
155                progressBar.style.width = '0';
156                cb(result, err);
157            },
158            (progress) => {
159                progressBar.style.width = 100 * progress + '%';
160            }
161        );
162    };
163    req.send();
164};
165
166// Transform a plain text string into a compressed base64 string
167const compress = (str, cb) => {
168    const progressBar = byId('progress');
169
170    lzma.compress(
171        str,
172        1,
173        (compressed, err) => {
174            if (err) {
175                progressBar.style.width = '0';
176                cb(compressed, err);
177                return;
178            }
179            const reader = new FileReader();
180            reader.onload = () => {
181                progressBar.style.width = '0';
182                cb(reader.result.substr(reader.result.indexOf(',') + 1));
183            };
184            reader.readAsDataURL(new Blob([new Uint8Array(compressed)]));
185        },
186        (progress) => {
187            progressBar.style.width = 100 * progress + '%';
188        }
189    );
190};
191
192const slugify = (str) =>
193    str
194        .toString()
195        .toLowerCase()
196        .replace(/\s+/g, '-')
197        .replace(/\+/g, '-p')
198        .replace(/#/g, '-sharp')
199        .replace(/[^\w\-]+/g, '');
200
201const byId = (id) => document.getElementById(id);
202
203/* Only for tests purposes */
204const testAllModes = () => {
205    for (const [index, language] of Object.entries(CodeMirror.modeInfo)) {
206        setTimeout(() => {
207            console.info(language.name);
208            select.set(slugify(language.name));
209        }, 1000 * index);
210    }
211};
212
213initCode(); // Will decode URL, create code editor, and language selector
214initClipboard();