all repos — NoPaste @ 14c60a1a3ee6872f7e4d5d03e506571af38c3901

Resurrected - The PussTheCat.org fork of NoPaste

script.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 init = () => {
 10    handleLegacyUrl();
 11    initCodeEditor();
 12    initLangSelector();
 13    initCode();
 14    initClipboard();
 15    initModals();
 16};
 17
 18const initCodeEditor = () => {
 19    CodeMirror.modeURL = 'https://cdn.jsdelivr.net/npm/codemirror@5.52.0/mode/%N/%N.js';
 20    editor = new CodeMirror(byId('editor'), {
 21        lineNumbers: true,
 22        theme: 'dracula',
 23        readOnly: readOnly,
 24        lineWrapping: false,
 25        scrollbarStyle: 'simple',
 26    });
 27    if (readOnly) {
 28        document.body.classList.add('readonly');
 29    }
 30
 31    statsEl = byId('stats');
 32    editor.on('change', () => {
 33        statsEl.innerHTML = `Length: ${editor.getValue().length} |  Lines: ${editor['doc'].size}`;
 34        hideCopyBar();
 35    });
 36};
 37
 38const initLangSelector = () => {
 39    select = new SlimSelect({
 40        select: '#language',
 41        data: CodeMirror.modeInfo.map((e) => ({
 42            text: e.name,
 43            value: shorten(e.name),
 44            data: { mime: e.mime, mode: e.mode },
 45        })),
 46        showContent: 'down',
 47        onChange: (e) => {
 48            const language = e.data || { mime: null, mode: null };
 49            editor.setOption('mode', language.mime);
 50            CodeMirror.autoLoadMode(editor, language.mode);
 51        },
 52    });
 53
 54    // Set lang selector
 55    const l = new URLSearchParams(window.location.search).get('l');
 56    select.set(l ? decodeURIComponent(l) : shorten('Plain Text'));
 57};
 58
 59const initCode = () => {
 60    let base64 = location.hash.substr(1);
 61    if (base64.length === 0) {
 62        return;
 63    }
 64    decompress(base64, (code, err) => {
 65        if (err) {
 66            alert('Failed to decompress data: ' + err);
 67            return;
 68        }
 69        editor.setValue(code);
 70    });
 71};
 72
 73const handleLegacyUrl = () => {
 74    const lang = new URLSearchParams(window.location.search).get('lang');
 75    const base = `${location.protocol}//${location.host}`;
 76    if (location.hash.charAt(5) === '-') {
 77        const hashedLang = location.hash.substr(1, 4);
 78        const newLang = CodeMirror.modeInfo.find((e) => hash(e.name) === hashedLang);
 79        const queryParams = newLang ? '?l=' + shorten(newLang.name) : '';
 80        location.replace(`${base}/${queryParams}#${location.hash.substr(6)}`);
 81        throw new Error('waiting for page to reload');
 82    }
 83    if (lang) {
 84        location.replace(`${base}/${'?l=' + shorten(lang)}${location.hash}`);
 85        throw new Error('waiting for page to reload');
 86    }
 87};
 88
 89const initClipboard = () => {
 90    clipboard = new ClipboardJS('.clipboard');
 91    clipboard.on('success', () => {
 92        hideCopyBar(true);
 93    });
 94};
 95
 96const initModals = () => {
 97    MicroModal.init({
 98        onClose: () => editor.focus(),
 99    });
100};
101
102const generateLink = (mode) => {
103    const data = editor.getValue();
104    compress(data, (base64, err) => {
105        if (err) {
106            alert('Failed to compress data: ' + err);
107            return;
108        }
109        const url = buildUrl(base64, mode);
110        statsEl.innerHTML = `Data length: ${data.length} |  Link length: ${url.length} | Compression ratio: ${Math.round(
111            (100 * url.length) / data.length
112        )}%`;
113
114        showCopyBar(url);
115    });
116};
117
118// Open the "Copy" bar and select the content
119const showCopyBar = (dataToCopy) => {
120    byId('copy').classList.remove('hidden');
121    const linkInput = byId('copy-link');
122    linkInput.value = dataToCopy;
123    linkInput.focus();
124    linkInput.setSelectionRange(0, dataToCopy.length);
125};
126
127// Close the "Copy" bar
128const hideCopyBar = (success) => {
129    const copyButton = byId('copy-btn');
130    const copyBar = byId('copy');
131    if (!success) {
132        copyBar.classList.add('hidden');
133        return;
134    }
135    copyButton.innerText = 'Copied !';
136    setTimeout(() => {
137        copyBar.classList.add('hidden');
138        copyButton.innerText = 'Copy';
139    }, 800);
140};
141
142const disableLineWrapping = () => {
143    byId('disable-line-wrapping').classList.add('hidden');
144    byId('enable-line-wrapping').classList.remove('hidden');
145    editor.setOption('lineWrapping', false);
146};
147
148const enableLineWrapping = () => {
149    byId('enable-line-wrapping').classList.add('hidden');
150    byId('disable-line-wrapping').classList.remove('hidden');
151    editor.setOption('lineWrapping', true);
152};
153
154const openInNewTab = () => {
155    window.open(location.href.replace(/[?&]readonly/, ''));
156};
157
158// Build a shareable URL
159const buildUrl = (rawData, mode) => {
160    const base = `${location.protocol}//${location.host}${location.pathname}`;
161    const query = shorten('Plain Text') === select.selected() ? '' : `?l=${encodeURIComponent(select.selected())}`;
162    const url = base + query + '#' + rawData;
163    if (mode === 'markdown') {
164        return `[NoPaste snippet](${url})`;
165    }
166    if (mode === 'iframe') {
167        const height = editor['doc'].height + 45;
168        return `<iframe width="100%" height="${height}" frameborder="0" src="${url}"></iframe>`;
169    }
170    return url;
171};
172
173// Transform a compressed base64 string into a plain text string
174const decompress = (base64, cb) => {
175    const progressBar = byId('progress');
176
177    const req = new XMLHttpRequest();
178    req.open('GET', 'data:application/octet;base64,' + base64);
179    req.responseType = 'arraybuffer';
180    req.onload = (e) => {
181        lzma.decompress(
182            new Uint8Array(e.target.response),
183            (result, err) => {
184                progressBar.style.width = '0';
185                cb(result, err);
186            },
187            (progress) => {
188                progressBar.style.width = 100 * progress + '%';
189            }
190        );
191    };
192    req.send();
193};
194
195// Transform a plain text string into a compressed base64 string
196const compress = (str, cb) => {
197    if (str.length === 0) {
198        cb('');
199        return;
200    }
201    const progressBar = byId('progress');
202
203    lzma.compress(
204        str,
205        1,
206        (compressed, err) => {
207            if (err) {
208                progressBar.style.width = '0';
209                cb(compressed, err);
210                return;
211            }
212            const reader = new FileReader();
213            reader.onload = () => {
214                progressBar.style.width = '0';
215                cb(reader.result.substr(reader.result.indexOf(',') + 1));
216            };
217            reader.readAsDataURL(new Blob([new Uint8Array(compressed)]));
218        },
219        (progress) => {
220            progressBar.style.width = 100 * progress + '%';
221        }
222    );
223};
224
225const slugify = (str) =>
226    str
227        .trim()
228        .toString()
229        .toLowerCase()
230        .replace(/\s+/g, '-')
231        .replace(/\+/g, '-p')
232        .replace(/#/g, '-sharp')
233        .replace(/[^\w\-]+/g, '');
234
235const shorten = (name) => {
236    let n = slugify(name).replace('script', '-s').replace('python', 'py');
237    const nov = (s) => s[0] + s.substr(1).replace(/[aeiouy-]/g, '');
238    if (n.replace(/-/g, '').length <= 4) {
239        return n.replace(/-/g, '');
240    }
241    if (n.split('-').length >= 2) {
242        return n
243            .split('-')
244            .map((x) => nov(x.substr(0, 2)))
245            .join('')
246            .substr(0, 4);
247    }
248    n = nov(n);
249    if (n.length <= 4) {
250        return n;
251    }
252    return n.substr(0, 2) + n.substr(n.length - 2, 2);
253};
254
255const byId = (id) => document.getElementById(id);
256
257// Legacy code, only for retro-compatibility
258const hash = function (str, seed = 0) {
259    let h1 = 0xdeadbeef ^ seed;
260    let h2 = 0x41c6ce57 ^ seed;
261    for (let i = 0, ch; i < str.length; i++) {
262        ch = str.charCodeAt(i);
263        h1 = Math.imul(h1 ^ ch, 2654435761);
264        h2 = Math.imul(h2 ^ ch, 1597334677);
265    }
266    h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
267    h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
268    const h = 4294967296 * (2097151 & h2) + (h1 >>> 0);
269    return h.toString(36).substr(0, 4).toUpperCase();
270};
271
272// Only for tests purposes
273const testAllModes = () => {
274    for (const [index, language] of Object.entries(CodeMirror.modeInfo)) {
275        CodeMirror.autoLoadMode(editor, language.mode);
276        setTimeout(() => {
277            console.info(language.name);
278            select.set(shorten(language.name));
279        }, 1000 * index);
280    }
281};
282
283init();