PageSourceSearch

https://sqlformat.org/static/formatter.js

js sqlformat.org collected 2026-09-28 10:30:43 UTC 6,161 bytes, 143 lines download raw bytes

1$('#sql-input').on('keydown', function(event) {
2    if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
3      event.preventDefault();
4      formatSQL(event.shiftKey);
5    }
6  });
7
8document.getElementById('sql-input').focus();
9
10toastr.options = {"positionClass": "toast-bottom-right"};
11
12// Show merch banner occasionally after formatting
13let formatCount = 0;
14function showMerchBanner() {
15    formatCount++;
16    // Show banner every 5th format, but not immediately
17    if (formatCount % 5 === 0 && formatCount > 1) {
18        // Alternate between merch and donation
19        const showDonation = Math.random() < 0.5;
20        
21        const banner = showDonation ? `
22            <div id="merch-success-banner" class="notification is-light has-text-centered mt-3" style="background: linear-gradient(135deg, #f5f5f5 0%, #fff 100%); border-left: 4px solid #f80; animation: slideDown 0.3s ease;">
23                <button class="delete" onclick="$('#merch-success-banner').fadeOut()"></button>
24                <span class="material-icons-outlined" style="color: #f80; vertical-align: text-bottom; margin-right: 5px;">favorite</span>
25                <strong>SQL looks great!</strong> Love this tool? <a href="https://merch.sqlformat.org/pages/donate" target="_blank" style="color: #f80; text-decoration: underline;"><strong>Support with a donation</strong></a> 💝
26            </div>
27        ` : `
28            <div id="merch-success-banner" class="notification is-light has-text-centered mt-3" style="background: linear-gradient(135deg, #f5f5f5 0%, #fff 100%); border-left: 4px solid #f80; animation: slideDown 0.3s ease;">
29                <button class="delete" onclick="$('#merch-success-banner').fadeOut()"></button>
30                <span class="material-icons-outlined" style="color: #f80; vertical-align: text-bottom; margin-right: 5px;">local_cafe</span>
31                <strong>SQL looks great!</strong> Like this tool? Check out our <a href="https://merch.sqlformat.org" target="_blank" style="color: #f80; text-decoration: underline;"><strong>"I Speak SQL" merch</strong></a> ☕
32            </div>
33        `;
34        
35        $(banner).prependTo('#result-list').hide().fadeIn(500);
36        
37        // Auto-hide after 8 seconds
38        setTimeout(() => {
39            $('#merch-success-banner').fadeOut(1000);
40        }, 8000);
41    }
42}
43
44document.addEventListener('DOMContentLoaded', (event) => {
45$('#format-btn').on('click', (event) => {
46    event.preventDefault();
47    formatSQL();
48});
49
50$('#btn-clear-results').on('click', (event) => {
51    $('#result-list').empty();
52    $('#btn-clear-results').attr('disabled', '');
53});
54
55document.getElementById('sqlfile').addEventListener('change', function(event) {
56    const file = event.target.files[0];
57    if (file) {
58        const reader = new FileReader();
59        reader.onload = function(e) {
60            const content = e.target.result;
61            document.getElementById('sql-input').textContent = content;
62            formatSQL();
63        };
64        reader.readAsText(file);
65    }
66});
67});
68
69function formatSQL(copyToClipboard) {
70    const sqlInput = document.getElementById('sql-input').value;
71    const removeComments = $('#opt-comments').prop('checked');
72    const compact = $('#opt-compact').prop('checked');
73    let indent = $('#opt-indent').val();
74    indent = indent == '' ? null : indent;
75    let kwCase = $('#opt-keywords').val();
76    kwCase = kwCase != 'none' ? kwCase : null;
77    let idCase = $('#opt-identifiers').val();
78    idCase = idCase != 'none' ? idCase : null;
79    const outputFormat = $('#opt-output').val();
80    pyodideWorker.postMessage({ type: 'format',
81        sql: sqlInput, remove_comments: removeComments, compact: compact,
82        indent: indent, keyword_case: kwCase,
83        identifier_case: idCase, output_format: outputFormat,
84        copyToClipboard: copyToClipboard });
85    dataLayer.push({'event': 'sql_formatted'});
86}
87
88function addResult(sql) {
89    let div = $('<div>').addClass('box').addClass('flash-bg');
90    let actionDiv = $('<div>').addClass('has-text-right').addClass('is-size-7').appendTo(div);
91    $('<a onClick="copyToClipboardLink(this);">Copy to clipboard</a> | <a onClick="scrollToEditor();">Top</a>').appendTo(actionDiv);
92    let code = $('<pre>').addClass('language-sql').addClass('flash-bg').appendTo(div);
93    $('<textarea>').addClass('is-hidden').val(sql).appendTo(div);
94    code.html(sql);
95    div.prependTo('#result-list');
96    hljs.highlightBlock(code[0]);
97    div[0].scrollIntoView({behavior: "smooth"});
98    $('#btn-clear-results').removeAttr('disabled');
99}
100
101
102function copyToClipboardLink(elem) {
103    const content = $(elem).parent().parent().find('textarea').val();
104    copyToClipboard(content);
105}
106
107function copyToClipboard(content) {
108    if (navigator.clipboard) {
109        navigator.clipboard.writeText(content).then(function() {
110            toastr.success('Copied to clipboard');
111        }, function(err) {
112            toastr.error("Failed to copy to clipboard");
113        });
114    } else {
115        toastr.error("Sorry, clipbpoard access is not supported in your browser")
116    }
117}
118
119function scrollToEditor() {
120    $('body')[0].scrollIntoView({behavior: "smooth"});
121    $('#sql-input').focus()
122}
123
124const pyodideWorker = new Worker(WORKER_URL);
125pyodideWorker.onmessage = function(event) {
126    if (event.data.type === 'version') {
127        document.getElementById("format-btn").classList.remove("is-loading");
128        document.getElementById("info-loading").textContent = event.data.version + ', Formatter engine loaded.';
129        setTimeout(function() {
130            document.getElementById("info-loading").textContent = event.data.version;
131        }, 3000);
132    } else if (event.data.type === 'error') {
133        document.getElementById("info-loading").textContent = event.data.message;
134        document.getElementById("info-loading").classList.add("has-text-danger");
135    } else if (event.data.type === 'formatted') {
136        addResult(event.data.formattedSQL);
137        if (event.data.copyToClipboard) {
138            copyToClipboard(event.data.formattedSQL);
139        }
140        // Show merch banner occasionally
141        showMerchBanner();
142    }
143};

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.