all repos — NoPaste @ 8991febab3704ec1c9878a9371aeffb96acb557f

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