{"id":23,"date":"2025-09-06T18:56:31","date_gmt":"2025-09-06T18:56:31","guid":{"rendered":"https:\/\/skillladderpro.com\/tools\/?page_id=23"},"modified":"2025-09-26T22:38:20","modified_gmt":"2025-09-26T22:38:20","slug":"home","status":"publish","type":"page","link":"https:\/\/skillladderpro.com\/tools\/","title":{"rendered":"Home"},"content":{"rendered":"        <div id=\"json_formatter_69fb5c84b7444\" class=\"json-formatter-container\">\r\n            <style>\r\n                .json-formatter-container {\r\n                    max-width: 100%;\r\n                    margin: 20px 0;\r\n                    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;\r\n                    background: #fff;\r\n                    border: 1px solid #e1e1e1;\r\n                    border-radius: 8px;\r\n                    padding: 20px;\r\n                    box-shadow: 0 2px 4px rgba(0,0,0,0.1);\r\n                }\r\n                \r\n                .json-formatter-container h3 {\r\n                    color: #333;\r\n                    margin: 0 0 20px 0;\r\n                    font-size: 1.5em;\r\n                    font-weight: 600;\r\n                }\r\n                \r\n                .json-input-section {\r\n                    margin-bottom: 20px;\r\n                }\r\n                \r\n                .json-textarea-container {\r\n                    position: relative;\r\n                    border: 1px solid #ddd;\r\n                    border-radius: 6px;\r\n                    overflow: hidden;\r\n                    background: #fff;\r\n                }\r\n                \r\n                .json-textarea-wrapper {\r\n                    display: flex;\r\n                    position: relative;\r\n                    min-height: 300px;\r\n                }\r\n                \r\n                .line-numbers {\r\n                    background: #f8f9fa;\r\n                    padding: 12px 8px;\r\n                    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n                    font-size: 13px;\r\n                    line-height: 1.5;\r\n                    color: #6c757d;\r\n                    text-align: right;\r\n                    user-select: none;\r\n                    min-width: 50px;\r\n                    white-space: pre;\r\n                    overflow: hidden;\r\n                    border-right: 1px solid #e9ecef;\r\n                }\r\n                \r\n               .json-textarea {\r\n    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n    font-size: 14px;\r\n    line-height: 1.5em; \/* lock it *\/\r\n    white-space: pre;\r\n    wrap: off; \/* no auto-wrap *\/\r\n}\r\n                \r\n                .json-textarea:focus {\r\n                    outline: none;\r\n                }\r\n                \r\n                .json-textarea-container:focus-within {\r\n                    border-color: #007cba;\r\n                    box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);\r\n                }\r\n                \r\n             .json-textarea.error-highlight {\r\n    background-image: linear-gradient(\r\n        transparent calc((var(--error-line) - 1) * 1.5em),\r\n        rgba(220, 53, 69, 0.2) calc((var(--error-line) - 1) * 1.5em),\r\n        rgba(220, 53, 69, 0.2) calc(var(--error-line) * 1.5em),\r\n        transparent calc(var(--error-line) * 1.5em)\r\n    );\r\n}\r\n                .json-controls {\r\n                    margin: 20px 0;\r\n                    display: flex;\r\n                    gap: 12px;\r\n                    flex-wrap: wrap;\r\n                }\r\n                \r\n                .json-btn {\r\n                    padding: 10px 18px;\r\n                    border: none;\r\n                    border-radius: 5px;\r\n                    cursor: pointer;\r\n                    font-size: 14px;\r\n                    font-weight: 500;\r\n                    transition: all 0.2s ease;\r\n                    text-decoration: none;\r\n                    display: inline-flex;\r\n                    align-items: center;\r\n                    min-height: 38px;\r\n                }\r\n                \r\n                .json-btn:disabled {\r\n                    opacity: 0.6;\r\n                    cursor: not-allowed;\r\n                }\r\n                \r\n                .json-btn-primary {\r\n                    background: #007cba;\r\n                    color: white;\r\n                }\r\n                \r\n                .json-btn-primary:hover:not(:disabled) {\r\n                    background: #005a87;\r\n                }\r\n                \r\n                .json-btn-secondary {\r\n                    background: #6c757d;\r\n                    color: white;\r\n                }\r\n                \r\n                .json-btn-secondary:hover:not(:disabled) {\r\n                    background: #5a6268;\r\n                }\r\n                \r\n                .json-btn-success {\r\n                    background: #28a745;\r\n                    color: white;\r\n                }\r\n                \r\n                .json-btn-success:hover:not(:disabled) {\r\n                    background: #218838;\r\n                }\r\n                \r\n                .json-btn-danger {\r\n                    background: #dc3545;\r\n                    color: white;\r\n                }\r\n                \r\n                .json-btn-danger:hover:not(:disabled) {\r\n                    background: #c82333;\r\n                }\r\n                \r\n                .json-output {\r\n                    margin-top: 20px;\r\n                }\r\n                \r\n                .json-output-textarea {\r\n                    width: 100%;\r\n                    min-height: 200px;\r\n                    padding: 12px;\r\n                    border: 1px solid #ddd;\r\n                    border-radius: 6px;\r\n                    font-family: 'Consolas', 'Monaco', 'Courier New', monospace;\r\n                    font-size: 13px;\r\n                    line-height: 1.5;\r\n                    resize: vertical;\r\n                    background: #f8f9fa;\r\n                    color: #333;\r\n                    outline: none;\r\n                    box-sizing: border-box;\r\n                }\r\n                \r\n                .json-output-textarea:focus {\r\n                    border-color: #007cba;\r\n                    box-shadow: 0 0 0 2px rgba(0, 124, 186, 0.2);\r\n                }\r\n                \r\n                .json-message {\r\n                    margin: 15px 0;\r\n                    padding: 12px 16px;\r\n                    border-radius: 6px;\r\n                    font-size: 14px;\r\n                    border: 1px solid;\r\n                }\r\n                \r\n                .json-message.success {\r\n                    background: #d4edda;\r\n                    border-color: #c3e6cb;\r\n                    color: #155724;\r\n                }\r\n                \r\n                .json-message.error {\r\n                    background: #f8d7da;\r\n                    border-color: #f5c6cb;\r\n                    color: #721c24;\r\n                }\r\n                \r\n                .error-details {\r\n                    font-weight: 600;\r\n                    margin-top: 8px;\r\n                    font-size: 13px;\r\n                }\r\n                \r\n                .json-label {\r\n                    display: block;\r\n                    margin-bottom: 8px;\r\n                    font-weight: 500;\r\n                    color: #333;\r\n                }\r\n            <\/style>\r\n            \r\n            <h3>JSON Formatter & Validator<\/h3>\r\n            \r\n            <div class=\"json-input-section\">\r\n                <label class=\"json-label\" for=\"json-input-json_formatter_69fb5c84b7444\">Input JSON:<\/label>\r\n                <div class=\"json-textarea-container\">\r\n                    <div class=\"json-textarea-wrapper\">\r\n                        <div class=\"line-numbers\" id=\"line-numbers-json_formatter_69fb5c84b7444\">1<\/div>\r\n                        <textarea \r\n                            class=\"json-textarea\" \r\n                            id=\"json-input-json_formatter_69fb5c84b7444\" \r\n                            placeholder=\"Paste your JSON here...\"\r\n                            spellcheck=\"false\"\r\n                        ><\/textarea>\r\n                    <\/div>\r\n                <\/div>\r\n            <\/div>\r\n            \r\n            <div class=\"json-controls\">\r\n                <button class=\"json-btn json-btn-primary\" onclick=\"window.jsonFormatterFunctions.formatJSON('json_formatter_69fb5c84b7444')\">\r\n                    Format JSON\r\n                <\/button>\r\n                <button class=\"json-btn json-btn-success\" onclick=\"window.jsonFormatterFunctions.validateJSON('json_formatter_69fb5c84b7444')\">\r\n                    Validate JSON\r\n                <\/button>\r\n                <button class=\"json-btn json-btn-secondary\" onclick=\"window.jsonFormatterFunctions.downloadJSON('json_formatter_69fb5c84b7444')\">\r\n                    Download JSON\r\n                <\/button>\r\n                <button class=\"json-btn json-btn-danger\" onclick=\"window.jsonFormatterFunctions.clearJSON('json_formatter_69fb5c84b7444')\">\r\n                    Clear All\r\n                <\/button>\r\n            <\/div>\r\n            \r\n            <div id=\"json-message-json_formatter_69fb5c84b7444\"><\/div>\r\n            \r\n            <div class=\"json-output\">\r\n                <label class=\"json-label\" for=\"json-output-json_formatter_69fb5c84b7444\">Output:<\/label>\r\n                <textarea \r\n                    class=\"json-output-textarea\" \r\n                    id=\"json-output-json_formatter_69fb5c84b7444\" \r\n                    placeholder=\"Formatted\/validated JSON will appear here...\"\r\n                    readonly\r\n                ><\/textarea>\r\n            <\/div>\r\n        <\/div>\r\n\r\n        <script>\r\n            (function() {\r\n                \/\/ Initialize global functions object if it doesn't exist\r\n                if (!window.jsonFormatterFunctions) {\r\n                    window.jsonFormatterFunctions = {};\r\n                }\r\n                \r\n                \/\/ Update line numbers\r\n                function updateLineNumbers(instanceId) {\r\n                    const textarea = document.getElementById('json-input-' + instanceId);\r\n                    const lineNumbers = document.getElementById('line-numbers-' + instanceId);\r\n                    \r\n                    if (!textarea || !lineNumbers) return;\r\n                    \r\n                    const value = textarea.value || '';\r\n                    const lines = value.split('\\n');\r\n                    const lineCount = lines.length;\r\n                    \r\n                    let numbers = '';\r\n                    for (let i = 1; i <= lineCount; i++) {\r\n                        numbers += i + (i < lineCount ? '\\n' : '');\r\n                    }\r\n                    lineNumbers.textContent = numbers;\r\n                    \r\n                    \/\/ Sync scroll\r\n                    lineNumbers.scrollTop = textarea.scrollTop;\r\n                }\r\n                \r\n                \/\/ Sync scroll between textarea and line numbers\r\n                function syncScroll(instanceId) {\r\n                    const textarea = document.getElementById('json-input-' + instanceId);\r\n                    const lineNumbers = document.getElementById('line-numbers-' + instanceId);\r\n                    \r\n                    if (!textarea || !lineNumbers) return;\r\n                    \r\n                    textarea.addEventListener('scroll', function() {\r\n                        lineNumbers.scrollTop = textarea.scrollTop;\r\n                    });\r\n                    \r\n                    textarea.addEventListener('input', function() {\r\n                        updateLineNumbers(instanceId);\r\n                        clearErrorHighlight(instanceId);\r\n                    });\r\n                    \r\n                    \/\/ Handle resize\r\n                    textarea.addEventListener('input', function() {\r\n                        setTimeout(() => updateLineNumbers(instanceId), 0);\r\n                    });\r\n                }\r\n                \r\n                \/\/ Show message\r\n                function showMessage(instanceId, message, type) {\r\n                    const messageDiv = document.getElementById('json-message-' + instanceId);\r\n                    if (messageDiv) {\r\n                        messageDiv.innerHTML = '<div class=\"json-message ' + type + '\">' + message + '<\/div>';\r\n                        \r\n                        \/\/ Auto-clear success messages after 3 seconds\r\n                        if (type === 'success') {\r\n                            setTimeout(() => {\r\n                                messageDiv.innerHTML = '';\r\n                            }, 3000);\r\n                        }\r\n                    }\r\n                }\r\n                \r\n                \/\/ Clear error highlight\r\n                function clearErrorHighlight(instanceId) {\r\n                    const textarea = document.getElementById('json-input-' + instanceId);\r\n                    if (textarea) {\r\n                        textarea.classList.remove('error-highlight');\r\n                        textarea.style.removeProperty('--error-line');\r\n                    }\r\n                }\r\n                \r\n                \/\/ Highlight error line\r\n                function highlightErrorLine(instanceId, lineNumber) {\r\n                    const textarea = document.getElementById('json-input-' + instanceId);\r\n                    if (textarea && lineNumber > 0) {\r\n                        textarea.classList.add('error-highlight');\r\n                        textarea.style.setProperty('--error-line', lineNumber);\r\n                    }\r\n                }\r\n                \r\n                \/\/ Parse JSON error and extract line\/column\r\n                function parseJSONError(error, jsonText) {\r\n                    const errorStr = error.toString();\r\n                    \r\n                    \/\/ Try different error message patterns\r\n                    let lineMatch = errorStr.match(\/line (\\d+)\/i) || \r\n                                   errorStr.match(\/at line (\\d+)\/i) ||\r\n                                   errorStr.match(\/position (\\d+)\/i);\r\n                    \r\n                    let columnMatch = errorStr.match(\/column (\\d+)\/i) || \r\n                                     errorStr.match(\/at column (\\d+)\/i);\r\n                    \r\n                    if (lineMatch) {\r\n                        const lineOrPos = parseInt(lineMatch[1]);\r\n                        \r\n                        \/\/ If this looks like a position, convert to line number\r\n                        if (errorStr.includes('position') && jsonText) {\r\n                            const line = getLineFromPosition(jsonText, lineOrPos);\r\n                            return {\r\n                                line: line,\r\n                                column: columnMatch ? parseInt(columnMatch[1]) : null,\r\n                                message: errorStr\r\n                            };\r\n                        } else {\r\n                            return {\r\n                                line: lineOrPos,\r\n                                column: columnMatch ? parseInt(columnMatch[1]) : null,\r\n                                message: errorStr\r\n                            };\r\n                        }\r\n                    }\r\n                    \r\n                    \/\/ Try to find line info in different formats\r\n                    const unexpectedMatch = errorStr.match(\/Unexpected token .* in JSON at position (\\d+)\/i);\r\n                    if (unexpectedMatch && jsonText) {\r\n                        const position = parseInt(unexpectedMatch[1]);\r\n                        const line = getLineFromPosition(jsonText, position);\r\n                        return {\r\n                            line: line,\r\n                            message: errorStr\r\n                        };\r\n                    }\r\n                    \r\n                    return { message: errorStr };\r\n                }\r\n                \r\n                \/\/ Calculate line number from position\r\n                function getLineFromPosition(text, position) {\r\n                    if (!text || position < 0) return 1;\r\n                    const beforePosition = text.substring(0, position);\r\n                    return beforePosition.split('\\n').length;\r\n                }\r\n                \r\n                \/\/ Download file function\r\n                function downloadFile(content, filename) {\r\n                    try {\r\n                        const blob = new Blob([content], { type: 'application\/json;charset=utf-8' });\r\n                        const url = URL.createObjectURL(blob);\r\n                        const a = document.createElement('a');\r\n                        a.href = url;\r\n                        a.download = filename;\r\n                        a.style.display = 'none';\r\n                        document.body.appendChild(a);\r\n                        a.click();\r\n                        document.body.removeChild(a);\r\n                        URL.revokeObjectURL(url);\r\n                        return true;\r\n                    } catch (e) {\r\n                        console.error('Download failed:', e);\r\n                        return false;\r\n                    }\r\n                }\r\n                \r\n                \/\/ Global functions\r\n                window.jsonFormatterFunctions.formatJSON = function(instanceId) {\r\n                    const input = document.getElementById('json-input-' + instanceId);\r\n                    const output = document.getElementById('json-output-' + instanceId);\r\n                    \r\n                    if (!input || !output) return;\r\n                    \r\n                    const inputValue = input.value.trim();\r\n                    if (!inputValue) {\r\n                        showMessage(instanceId, 'Please enter some JSON to format.', 'error');\r\n                        return;\r\n                    }\r\n                    \r\n                    try {\r\n                        const parsed = JSON.parse(inputValue);\r\n                        const formatted = JSON.stringify(parsed, null, 2);\r\n                        output.value = formatted;\r\n                        showMessage(instanceId, 'JSON formatted successfully!', 'success');\r\n                        clearErrorHighlight(instanceId);\r\n                    } catch (error) {\r\n                        const errorInfo = parseJSONError(error, inputValue);\r\n                        let errorMessage = 'Invalid JSON: ' + errorInfo.message;\r\n                        \r\n                        if (errorInfo.line) {\r\n                            errorMessage += '<div class=\"error-details\">Error at line ' + errorInfo.line;\r\n                            if (errorInfo.column) {\r\n                                errorMessage += ', column ' + errorInfo.column;\r\n                            }\r\n                            errorMessage += '<\/div>';\r\n                            highlightErrorLine(instanceId, errorInfo.line);\r\n                        }\r\n                        \r\n                        showMessage(instanceId, errorMessage, 'error');\r\n                        output.value = '';\r\n                    }\r\n                };\r\n                \r\n                window.jsonFormatterFunctions.validateJSON = function(instanceId) {\r\n                    const input = document.getElementById('json-input-' + instanceId);\r\n                    const output = document.getElementById('json-output-' + instanceId);\r\n                    \r\n                    if (!input || !output) return;\r\n                    \r\n                    const inputValue = input.value.trim();\r\n                    if (!inputValue) {\r\n                        showMessage(instanceId, 'Please enter some JSON to validate.', 'error');\r\n                        return;\r\n                    }\r\n                    \r\n                    try {\r\n                        JSON.parse(inputValue);\r\n                        showMessage(instanceId, '\u2705 Valid JSON!', 'success');\r\n                        output.value = 'JSON is valid!';\r\n                        clearErrorHighlight(instanceId);\r\n                    } catch (error) {\r\n                        const errorInfo = parseJSONError(error, inputValue);\r\n                        let errorMessage = 'Invalid JSON: ' + errorInfo.message;\r\n                        \r\n                        if (errorInfo.line) {\r\n                            errorMessage += '<div class=\"error-details\">Error at line ' + errorInfo.line;\r\n                            if (errorInfo.column) {\r\n                                errorMessage += ', column ' + errorInfo.column;\r\n                            }\r\n                            errorMessage += '<\/div>';\r\n                            highlightErrorLine(instanceId, errorInfo.line);\r\n                        }\r\n                        \r\n                        showMessage(instanceId, errorMessage, 'error');\r\n                        output.value = 'JSON is invalid. See error message above.';\r\n                    }\r\n                };\r\n                \r\n                window.jsonFormatterFunctions.downloadJSON = function(instanceId) {\r\n                    const input = document.getElementById('json-input-' + instanceId);\r\n                    const output = document.getElementById('json-output-' + instanceId);\r\n                    \r\n                    if (!input || !output) return;\r\n                    \r\n                    let contentToDownload = output.value;\r\n                    \r\n                    \/\/ If output is empty or just status message, try to format input\r\n                    if (!contentToDownload || \r\n                        contentToDownload === 'JSON is valid!' || \r\n                        contentToDownload === 'JSON is invalid. See error message above.') {\r\n                        \r\n                        const inputValue = input.value.trim();\r\n                        if (!inputValue) {\r\n                            showMessage(instanceId, 'No JSON to download. Please enter some JSON first.', 'error');\r\n                            return;\r\n                        }\r\n                        \r\n                        try {\r\n                            const parsed = JSON.parse(inputValue);\r\n                            contentToDownload = JSON.stringify(parsed, null, 2);\r\n                        } catch (error) {\r\n                            showMessage(instanceId, 'Cannot download invalid JSON. Please fix errors first.', 'error');\r\n                            return;\r\n                        }\r\n                    }\r\n                    \r\n                    const timestamp = new Date().toISOString().replace(\/[:.]\/g, '-').split('T')[0];\r\n                    const filename = 'formatted-json-' + timestamp + '.json';\r\n                    \r\n                    if (downloadFile(contentToDownload, filename)) {\r\n                        showMessage(instanceId, 'JSON downloaded successfully as ' + filename, 'success');\r\n                    } else {\r\n                        showMessage(instanceId, 'Download failed. Please try again.', 'error');\r\n                    }\r\n                };\r\n                \r\n                window.jsonFormatterFunctions.clearJSON = function(instanceId) {\r\n                    const input = document.getElementById('json-input-' + instanceId);\r\n                    const output = document.getElementById('json-output-' + instanceId);\r\n                    const messageDiv = document.getElementById('json-message-' + instanceId);\r\n                    \r\n                    if (input) input.value = '';\r\n                    if (output) output.value = '';\r\n                    if (messageDiv) messageDiv.innerHTML = '';\r\n                    \r\n                    clearErrorHighlight(instanceId);\r\n                    updateLineNumbers(instanceId);\r\n                };\r\n                \r\n                \/\/ Initialize for this instance\r\n                const instanceId = 'json_formatter_69fb5c84b7444';\r\n                \r\n                \/\/ Initialize when DOM is ready\r\n                function initializeInstance() {\r\n                    updateLineNumbers(instanceId);\r\n                    syncScroll(instanceId);\r\n                }\r\n                \r\n                if (document.readyState === 'loading') {\r\n                    document.addEventListener('DOMContentLoaded', initializeInstance);\r\n                } else {\r\n                    \/\/ DOM is already ready\r\n                    setTimeout(initializeInstance, 0);\r\n                }\r\n            })();\r\n        <\/script>\r\n        \r\n        \n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-23","page","type-page","status-publish","hentry"],"blocksy_meta":[],"_links":{"self":[{"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/pages\/23","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/comments?post=23"}],"version-history":[{"count":7,"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/pages\/23\/revisions"}],"predecessor-version":[{"id":75,"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/pages\/23\/revisions\/75"}],"wp:attachment":[{"href":"https:\/\/skillladderpro.com\/tools\/wp-json\/wp\/v2\/media?parent=23"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}