import $ from 'jquery'; import { context } from './page-context.js'; function initialize() { var codeBlockSelector = '.article--content pre'; var $codeBlocks = $(codeBlockSelector); var appendHTML = `
`; // Wrap all codeblocks with a new 'codeblock' div $codeBlocks.each(function () { $(this).wrap("
"); }); // Append code controls to all codeblock divs $('.codeblock').append(appendHTML); //////////////////////////// CODE CONTROLS TOGGLING //////////////////////////// // Click outside of the code-controls to close them $(document).click(function () { $('.code-controls').removeClass('open'); }); // Click the code controls toggle to open code controls $('.code-controls-toggle').click(function () { $(this).parent('.code-controls').toggleClass('open'); }); // Stop event propagation for clicks inside of the code-controls div $('.code-controls').click(function (e) { e.stopPropagation(); }); /////////////////////////////// COPY TO CLIPBOARD ////////////////////////////// // Update button text during lifecycles function updateText(element, currentText, newText) { let inner = element[0].innerHTML; inner = inner.replace(currentText, newText); element[0].innerHTML = inner; } // Trigger copy success state lifecycle function copyLifeCycle(element, state) { let stateData = state === 'success' ? { state: 'success', message: 'Copied!' } : { state: 'failed', message: 'Copy failed!' }; updateText(element, 'Copy', stateData.message); element.addClass(stateData.state); setTimeout(function () { updateText(element, stateData.message, 'Copy'); element.removeClass(stateData.state); }, 2500); } // Trigger copy failure state lifecycle $('.copy-code').click(function () { let codeElement = $(this) .closest('.code-controls') .prevAll('pre:has(code)')[0]; let text = codeElement.innerText; // Extract additional code block information const codeBlockInfo = extractCodeBlockInfo(codeElement); // Add Google Analytics event tracking const currentUrl = new URL(window.location.href); // Determine which tracking parameter to add based on product context switch (context) { case 'cloud': currentUrl.searchParams.set('dl', 'cloud'); break; case 'core': /** Track using the same value used by www.influxdata.com pages */ currentUrl.searchParams.set('dl', 'oss3'); break; case 'enterprise': /** Track using the same value used by www.influxdata.com pages */ currentUrl.searchParams.set('dl', 'enterprise'); break; case 'serverless': currentUrl.searchParams.set('dl', 'serverless'); break; case 'dedicated': currentUrl.searchParams.set('dl', 'dedicated'); break; case 'clustered': currentUrl.searchParams.set('dl', 'clustered'); break; case 'oss/enterprise': currentUrl.searchParams.set('dl', 'oss'); break; case 'other': default: // No tracking parameter for other/unknown products break; } // Add code block specific tracking parameters if (codeBlockInfo.language) { currentUrl.searchParams.set('code_lang', codeBlockInfo.language); } if (codeBlockInfo.lineCount) { currentUrl.searchParams.set('code_lines', codeBlockInfo.lineCount); } if (codeBlockInfo.hasPlaceholders) { currentUrl.searchParams.set('has_placeholders', 'true'); } if (codeBlockInfo.blockType) { currentUrl.searchParams.set('code_type', codeBlockInfo.blockType); } if (codeBlockInfo.sectionTitle) { currentUrl.searchParams.set( 'section', encodeURIComponent(codeBlockInfo.sectionTitle) ); } if (codeBlockInfo.firstLine) { currentUrl.searchParams.set( 'first_line', encodeURIComponent(codeBlockInfo.firstLine.substring(0, 100)) ); } // Update browser history without triggering page reload if (window.history && window.history.replaceState) { window.history.replaceState(null, '', currentUrl.toString()); } // Send custom Google Analytics event if gtag is available if (typeof window.gtag !== 'undefined') { window.gtag('event', 'code_copy', { language: codeBlockInfo.language, line_count: codeBlockInfo.lineCount, has_placeholders: codeBlockInfo.hasPlaceholders, dl: codeBlockInfo.dl || null, section_title: codeBlockInfo.sectionTitle, first_line: codeBlockInfo.firstLine ? codeBlockInfo.firstLine.substring(0, 100) : null, product: context, }); } const copyContent = async () => { try { await navigator.clipboard.writeText(text); copyLifeCycle($(this), 'success'); } catch { copyLifeCycle($(this), 'failed'); } }; copyContent(); }); /** * Extract contextual information about a code block * @param {HTMLElement} codeElement - The code block element * @returns {Object} Information about the code block */ function extractCodeBlockInfo(codeElement) { const codeTag = codeElement.querySelector('code'); const info = { language: null, lineCount: 0, hasPlaceholders: false, blockType: 'code', dl: null, // Download script type sectionTitle: null, firstLine: null, }; // Extract language from class attribute if (codeTag && codeTag.className) { const langMatch = codeTag.className.match( /language-(\w+)|hljs-(\w+)|(\w+)/ ); if (langMatch) { info.language = langMatch[1] || langMatch[2] || langMatch[3]; } } // Count lines const text = codeElement.innerText || ''; const lines = text.split('\n'); info.lineCount = lines.length; // Get first non-empty line info.firstLine = lines.find((line) => line.trim() !== '') || null; // Check for placeholders (common patterns) info.hasPlaceholders = /\b[A-Z_]{2,}\b|\{\{[^}]+\}\}|\$\{[^}]+\}|<[^>]+>/.test(text); // Determine if this is a download script if (text.includes('https://www.influxdata.com/d/install_influxdb3.sh')) { if (text.includes('install_influxdb3.sh enterprise')) { info.dl = 'enterprise'; } else { info.dl = 'oss3'; } } else if (text.includes('docker pull influxdb:3-enterprise')) { info.dl = 'enterprise'; } else if (text.includes('docker pull influxdb3-core')) { info.dl = 'oss3'; } // Find nearest section heading let element = codeElement; while (element && element !== document.body) { element = element.previousElementSibling || element.parentElement; if (element && element.tagName && /^H[1-6]$/.test(element.tagName)) { info.sectionTitle = element.textContent.trim(); break; } } return info; } /////////////////////////////// FULL WINDOW CODE /////////////////////////////// /* On click, open the fullscreen code modal and append a clone of the selected codeblock. Disable scrolling on the body. Disable user selection on everything but the fullscreen codeblock. */ $('.fullscreen-toggle').click(function () { var code = $(this) .closest('.code-controls') .prevAll('pre:has(code)') .clone(); $('#fullscreen-code-placeholder').replaceWith(code[0]); $('body').css('overflow', 'hidden'); $('body > div:not(.fullscreen-code)').css('user-select', 'none'); $('.fullscreen-code').fadeIn(); }); /* On click, close the fullscreen code block. Reenable scrolling on the body. Reenable user selection on everything. Close the modal and replace the code block with the placeholder element. */ $('.fullscreen-close').click(function () { $('body').css('overflow', 'auto'); $('body > div:not(.fullscreen-code)').css('user-select', ''); $('.fullscreen-code').fadeOut(); $('.fullscreen-code pre').replaceWith( '
' ); }); } export { initialize };