Initial Kapa.ai chat integration.
Continue refactoring JavaScript into a component pattern and ESM. Replaces some jQuery with native DOM API. chore(ai): reference documentation and instructions for training AI chore(ai): implement Kapa AI chat widget - Move script tag to HTML template to make it obvious. - Cleanup javascript to make it more component-like - Set Kapa attributes, support setting userid chore(js): add JS dependencies, previously referenced in script tags, to package.json for JS builds. fix(api): indents chore(js): package Mermaid diagram library chore(js): refactor JS for AIChat and Theme as examples of using the component pattern for HTML/CSS/JS chore(js): Use the new local-storage API in refactored module code and in code not yet ported. Cleanup syntax in local-storage and make functions available from window.LocalStorageAPI. fix(js): theme.js name-change chore(js): fix ai-chat.js file name fix(js): refactor: - componentNames are snakecase in HTML - replace DOM selection method and jQuery eventhandler assignment - remove old theme.js references chore(ai): configure chat window overlay, size, and position: - removes overlay and scroll lock - positions chat to the right and bottom - expands sample question width to 12 cols chore(ai): edit disclaimer fix(ai): size and position chore(js): make ai-chat specific to configuration and and setting userid (for testing and future use). fix(js): copy referrerHost variable to v3-wayfinding instead of relying on influxdb-url to assign it. chore(ai): add a footer div at page bottom to contain modal triggers for custom-time and ask-ai. Still needs some CSS help. Moves tooltip text from CSS to HTML data attribute. chore(ai): dynamically load AI script tag after DOMContentLoaded to avoid race conditions. Call initialization from the modal trigger module and pass the show trigger function to the onload handler. fix(ai): fix modal triggers to viewport fix(modal-triggers): stack the triggers into a single column. restyle footer widgets updated time selector modal to use correct storage term minor style update WIP(ai-chat): get product data chore(js): Factor out pageContext module from influxdb-url.js chore(js): Refactor helpers.js out of inflluxdb-url.js WIP: refactor influxdburl - minimal changes for module conversions feat(ai): Custom AI chat example questions product and version. Ask AI example questions: - Adds support for customizing example Ask AI questions per product or version. - Configure questions in site `data/products.yml`; otherwise, it uses default questions from `ask-ai.js` Context, page, and product data: - Adds sample URLs for remaining versions in influxdb_urls - `page-context.js` consolidates and exports constants for page context (protocol, host, path, referrer) and path-to-data mappings for product and influxdb_url site data Module refactor: - Refactors some JavaScript into ES6 modules, and refactors some of those further into a Component pattern--just vanilla JS and no shadow DOM stuff. The Component pattern that uses data attributes to "bind" JavaScript modules with CSS and HTML is a popular approach in modern web development. This pattern enhances modularity, reusability, and maintainability by associating behavior (JavaScript), structure (HTML), and style (CSS) through the use of data attributes. - `assets/main.js` is the entrypoint - Passes pageParams from the Hugo page to modules that import `@params`. - Moves most external dependencies out of `script` tags and into package.json to be managed with `yarn`. - Adds `eslint`. - For modules that aren't yet components, wraps execution statements inside an `initialize()` function and calls the function from `main.js` on `DOMContentLoaded`. - For components, if the page contains the `data-component="<component-name>"`, the matching element is passed to the component function on `DOMContentLoaded`. - I tried to avoid changing logic where it wasn't necessary. Update DOC_GPT_PROFILE.md customize ai chat modal styles fix(influxdb-url): Rename to cloud_dedicated in influxdb_urls.yml, remove newly added placeholder URL and use the extant default, refactor - Rename to in influxdb_urls.yml - Fix influxdb-url.js and data provision in local-storage.js to use the new name, mapping it to to retain the existing local storage key chore(api-lib): Use local-storage import instead of window global chore(js): cleanup fix(js): Ensure feature-callout initializes on page load fix(theme): Load preferred theme before making the page visible. Execute a predefined function by specifying the function name in data-theme-callback fix(search-toggle): Restores toggling the search field when sidebar is collapsed. Moves the event handler to a new search-button component fix(ai): Fix custom attribute assignment. Rename property to ai_example_questions Include the word `Bearer` or `Token`, a space, and your **token** value (all case-sensitive). Fix TOC links. Fixes #5781 fix(api-docs): Update API reference directories and generation script for influxdb3 URL paths, update links and names in reference content fix(api-ref): Update getswagger.sh destination paths to use the new directory structure when fetching spec files. Update the redocly plugin module path. hotfix: fix hlevel bug in children shortcode Remove underline from custom time widget add color to custom time widget stylingpull/5830/head
parent
a15f8c8154
commit
c173edce68
|
@ -0,0 +1,52 @@
|
|||
Doc is a public custom GPT for OpenAI ChatGPT used to help write and style InfluxData and InfluxDB documentation.
|
||||
|
||||
## Introduction
|
||||
|
||||
Doc writes technical software documentation for InfluxData. The public web site is https://docs.influxdata.com and the source repository is https://github.com/influxdata/docs-v2.
|
||||
Documentation provides step-by-step guides and reference documentation for InfluxDB and associated clients (CLIs, client libraries (SDKs), and Telegraf (https://docs.influxdata.com/telegraf/v1/)), and the legacy v1 components Kapacitor and Chronograf.
|
||||
|
||||
## Instruction
|
||||
|
||||
When a user asks a question and doesn't include a product from the list below, ask them which product in the list they are using, along with the version and query language:
|
||||
|
||||
InfluxDB OSS 1.x (v1)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/v1/
|
||||
- Query languages: v1.8+ supports InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v1 client libraries
|
||||
InfluxDB Enterprise (v1)
|
||||
- Documentation: https://docs.influxdata.com/enterprise_influxdb/v1/
|
||||
- Query languages: v1.8+ supports InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v1 client libraries
|
||||
InfluxDB OSS 2.x (v2)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/v2/
|
||||
- Query languages: InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v2 client libraries
|
||||
InfluxDB Cloud (v2, multi-tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/cloud/
|
||||
- Query languages: InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v2 client libraries
|
||||
InfluxDB Clustered (v3, 3.0, self-managed distributed)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/clustered/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influxctl CLI, v3 client libraries
|
||||
InfluxDB Cloud Dedicated (3.0, v3, InfluxData-managed single tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/cloud-dedicated/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influxctl CLI, v3 client libraries
|
||||
InfluxDB Cloud Serverless (v3, 3.0, InfluxData-managed multi-tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/clustered/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influx CLI, v3 client libraries
|
||||
|
||||
If I ask about a REST API or SDK (client library) and don't specify a product, ask which product.
|
||||
For API client libraries, refer to the documentation and to the source repositories in https://github.com/InfluxCommunity for the version-specific client library.
|
||||
|
||||
When writing documentation, always use Google Developer Documentation style guidelines and Markdown format.
|
||||
If writing REST API reference documentation follow YouTube Data API style and Google Developer Documentation style guidelines.
|
||||
|
||||
The project uses the Hugo static site generator to build the documentation.
|
||||
The site uses JavaScript and jQuery.
|
||||
For information about linting, tests (using pytests for codeblocks), shortcode <shortcode_name>, refer to https://github.com/influxdata/docs-v2/blob/master/README.md and https://github.com/influxdata/docs-v2/blob/master/CONTRIBUTING.md.
|
||||
If something in CONTRIBUTING.md needs clarification, then give me the suggested revision for CONTRIBUTING.md in Markdown.
|
||||
|
||||
The community forum is https://community.influxdata.com/ and should not be used as a primary source of information, but might contain useful suggestions or solutions to specific problems from users.
|
|
@ -0,0 +1,30 @@
|
|||
When a user asks a question and doesn't include a product from the list below, ask them which product in the list they are using, along with the version and query language:
|
||||
|
||||
InfluxDB OSS 1.x (v1)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/v1/
|
||||
- Query languages: v1.8+ supports InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v1 client libraries
|
||||
InfluxDB Enterprise (v1)
|
||||
- Documentation: https://docs.influxdata.com/enterprise_influxdb/v1/
|
||||
- Query languages: v1.8+ supports InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v1 client libraries
|
||||
InfluxDB OSS 2.x (v2)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/v2/
|
||||
- Query languages: InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v2 client libraries
|
||||
InfluxDB Cloud (v2, multi-tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/cloud/
|
||||
- Query languages: InfluxQL and Flux
|
||||
- Clients: Telegraf, influx CLI, v2 client libraries
|
||||
InfluxDB Clustered (v3, 3.0, self-managed distributed)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/clustered/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influxctl CLI, v3 client libraries
|
||||
InfluxDB Cloud Dedicated (3.0, v3, InfluxData-managed single tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/cloud-dedicated/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influxctl CLI, v3 client libraries
|
||||
InfluxDB Cloud Serverless (v3, 3.0, InfluxData-managed multi-tenant)
|
||||
- Documentation: https://docs.influxdata.com/influxdb/clustered/
|
||||
- Query languages: SQL and InfluxQL
|
||||
- Clients: Telegraf, influx CLI, v3 client libraries
|
|
@ -1,6 +1,8 @@
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////// Preferred Client Library programming language ///////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
import { activateTabs, updateBtnURLs } from './tabbed-content.js';
|
||||
import { getPreference, setPreference } from './local-storage.js';
|
||||
|
||||
function getVisitedApiLib() {
|
||||
const path = window.location.pathname.match(
|
||||
|
@ -23,15 +25,19 @@ function getApiLibPreference () {
|
|||
return getPreference('api_lib') || '';
|
||||
}
|
||||
|
||||
// When visit a client library page, set the api_lib preference
|
||||
function initialize() {
|
||||
// When visiting a client library page, set the api_lib preference
|
||||
if (isApiLib()) {
|
||||
var selectedApiLib = getVisitedApiLib();
|
||||
setPreference('api_lib', selectedApiLib);
|
||||
}
|
||||
|
||||
// Activate code-tabs based on the cookie then override with query param.
|
||||
var tab = getApiLibPreference();
|
||||
const tab = getApiLibPreference();
|
||||
['.tabs, .code-tabs'].forEach(
|
||||
selector => activateTabs(selector, tab),
|
||||
(selector) => activateTabs(selector, tab),
|
||||
updateBtnURLs(tab)
|
||||
);
|
||||
}
|
||||
|
||||
export { initialize };
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
import AskAI from './ask-ai.js';
|
||||
|
||||
function showTrigger(element) {
|
||||
// Remove the inline display: none style
|
||||
element.removeAttribute('style');
|
||||
}
|
||||
|
||||
export default function AskAITrigger({ component }) {
|
||||
const kapaContainer = document.querySelector('#kapa-widget-container');
|
||||
if (!component && !kapaContainer) {
|
||||
return;
|
||||
}
|
||||
if (!kapaContainer) {
|
||||
// Initialize the chat widget
|
||||
AskAI({ onChatLoad: () => showTrigger(component) });
|
||||
} else {
|
||||
showTrigger(component);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
import { productData } from './page-context.js';
|
||||
|
||||
function setUser(userid, email) {
|
||||
const NAMESPACE = 'kapaSettings';
|
||||
|
||||
// Set the user ID and email in the global settings namespace.
|
||||
// The chat widget will use this on subsequent chats to personalize the user's experience.
|
||||
window[NAMESPACE] = {
|
||||
user: {
|
||||
uniqueClientId: userid,
|
||||
email: email,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the chat widget
|
||||
function initializeChat({onChatLoad, chatAttributes}) {
|
||||
/* See https://docs.kapa.ai/integrations/website-widget/configuration for
|
||||
* available configuration options.
|
||||
* All values are strings.
|
||||
*/
|
||||
const requiredAttributes = {
|
||||
websiteId: 'a02bca75-1dd3-411e-95c0-79ee1139be4d',
|
||||
projectName: 'InfluxDB',
|
||||
projectColor: '#020a47',
|
||||
projectLogo: '/img/influx-logo-cubo-white.png',
|
||||
}
|
||||
|
||||
const optionalAttributes = {
|
||||
modalDisclaimer: 'This AI can access [documentation for InfluxDB, clients, and related tools](https://docs.influxdata.com). Information you submit is used in accordance with our [Privacy Policy](https://www.influxdata.com/legal/privacy-policy/).',
|
||||
modalExampleQuestions: 'Use Python to write data to InfluxDB 3,How do I query using SQL?,How do I use MQTT with Telegraf?',
|
||||
buttonHide: 'true',
|
||||
modalOpenOnCommandK: 'true',
|
||||
modalExampleQuestionsColSpan: '8',
|
||||
modalFullScreenOnMobile: 'true',
|
||||
modalHeaderPadding: '.5rem',
|
||||
modalInnerPositionRight: '0',
|
||||
modalInnerPositionLeft: '',
|
||||
modalLockScroll: 'false',
|
||||
modalOverrideOpenClassAskAi: 'ask-ai-open',
|
||||
modalSize: '500px',
|
||||
modalWithOverlay: 'false',
|
||||
modalInnerMaxWidth: '500px',
|
||||
modalXOffset: '1rem',
|
||||
modalYOffset: '10vh',
|
||||
userAnalyticsFingerprintEnabled: 'true',
|
||||
fontFamily: 'Proxima Nova, sans-serif',
|
||||
modalHeaderBgColor: 'linear-gradient(90deg, #d30971 0%, #9b2aff 100%)',
|
||||
modalHeaderBorderBottom: 'none',
|
||||
modalTitleColor: '#fff',
|
||||
modalTitleFontSize: '1.25rem',
|
||||
}
|
||||
|
||||
const scriptUrl = 'https://widget.kapa.ai/kapa-widget.bundle.js';
|
||||
const script = document.createElement('script');
|
||||
script.async = true;
|
||||
script.src = scriptUrl;
|
||||
script.onload = function() {
|
||||
onChatLoad();
|
||||
window.influxdatadocs.AskAI = AskAI;
|
||||
};
|
||||
script.onerror = function() {
|
||||
console.error('Error loading AI chat widget script');
|
||||
};
|
||||
|
||||
const dataset = {...requiredAttributes, ...optionalAttributes, ...chatAttributes};
|
||||
Object.keys(dataset).forEach(key => {
|
||||
// Assign dataset attributes from the object
|
||||
script.dataset[key] = dataset[key];
|
||||
});
|
||||
|
||||
// Check for an existing script element to remove
|
||||
const oldScript= document.querySelector(`script[src="${scriptUrl}"]`);
|
||||
if (oldScript) {
|
||||
oldScript.remove();
|
||||
}
|
||||
document.head.appendChild(script);
|
||||
}
|
||||
|
||||
function getProductExampleQuestions() {
|
||||
const questions = productData?.product?.ai_sample_questions || null;
|
||||
return questions?.join(',') || '';
|
||||
}
|
||||
|
||||
/**
|
||||
* chatParams: specify custom (for example, page-specific) attribute values for the chat, pass the dataset key-values (collected in ...chatParams). See https://docs.kapa.ai/integrations/website-widget/configuration for available configuration options.
|
||||
* onChatLoad: function to call when the chat widget has loaded
|
||||
* userid: optional, a unique user ID for the user (not currently used for public docs)
|
||||
*/
|
||||
export default function AskAI({ userid, email, onChatLoad, ...chatParams }) {
|
||||
const chatAttributes = {
|
||||
modalExampleQuestions: getProductExampleQuestions(),
|
||||
...chatParams,
|
||||
}
|
||||
initializeChat({onChatLoad, chatAttributes});
|
||||
|
||||
if (userid) {
|
||||
setUser(userid, email);
|
||||
}
|
||||
}
|
|
@ -1,4 +1,7 @@
|
|||
var codeBlockSelector = ".article--content pre";
|
||||
import $ from 'jquery';
|
||||
|
||||
function initialize() {
|
||||
var codeBlockSelector = '.article--content pre';
|
||||
var codeBlocks = $(codeBlockSelector);
|
||||
|
||||
var appendHTML = `
|
||||
|
@ -9,7 +12,7 @@ var appendHTML = `
|
|||
<li class='fullscreen-toggle'><span class='cf-icon ExpandB'></span> Fill window</li>
|
||||
</ul>
|
||||
</div>
|
||||
`
|
||||
`;
|
||||
|
||||
// Wrap all codeblocks with a new 'codeblock' div
|
||||
$(codeBlocks).each(function () {
|
||||
|
@ -23,13 +26,13 @@ $('.codeblock').append(appendHTML);
|
|||
|
||||
// Click outside of the code-controls to close them
|
||||
$(document).click(function () {
|
||||
$('.code-controls').removeClass('open')
|
||||
$('.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) {
|
||||
|
@ -40,41 +43,44 @@ $('.code-controls').click(function(e) {
|
|||
|
||||
// Update button text during lifecycles
|
||||
function updateText(element, currentText, newText) {
|
||||
let inner = (element)[0].innerHTML;
|
||||
inner = inner.replace(currentText, newText)
|
||||
let inner = element[0].innerHTML;
|
||||
inner = inner.replace(currentText, newText);
|
||||
|
||||
element[0].innerHTML = inner
|
||||
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!'})
|
||||
let stateData =
|
||||
state === 'success'
|
||||
? { state: 'success', message: 'Copied!' }
|
||||
: { state: 'failed', message: 'Copy failed!' };
|
||||
|
||||
updateText(element, 'Copy', stateData.message)
|
||||
element.addClass(stateData.state)
|
||||
updateText(element, 'Copy', stateData.message);
|
||||
element.addClass(stateData.state);
|
||||
|
||||
setTimeout(function () {
|
||||
updateText(element, stateData.message, 'Copy');
|
||||
element.removeClass(stateData.state)
|
||||
}, 2500)
|
||||
element.removeClass(stateData.state);
|
||||
}, 2500);
|
||||
}
|
||||
|
||||
// Trigger copy failure state lifecycle
|
||||
|
||||
$('.copy-code').click(function () {
|
||||
let text = $(this).closest('.code-controls').prev('pre')[0].innerText
|
||||
let text = $(this).closest('.code-controls').prev('pre')[0].innerText;
|
||||
|
||||
const copyContent = async () => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
copyLifeCycle($(this), 'success')
|
||||
} catch (err) {
|
||||
copyLifeCycle($(this), 'failed')
|
||||
}
|
||||
copyLifeCycle($(this), 'success');
|
||||
} catch {
|
||||
copyLifeCycle($(this), 'failed');
|
||||
}
|
||||
};
|
||||
|
||||
copyContent()
|
||||
})
|
||||
copyContent();
|
||||
});
|
||||
|
||||
/////////////////////////////// FULL WINDOW CODE ///////////////////////////////
|
||||
|
||||
|
@ -90,7 +96,7 @@ $('.fullscreen-toggle').click(function() {
|
|||
$('body').css('overflow', 'hidden');
|
||||
$('body > div:not(.fullscreen-code)').css('user-select', 'none');
|
||||
$('.fullscreen-code').fadeIn();
|
||||
})
|
||||
});
|
||||
|
||||
/*
|
||||
On click, close the fullscreen code block.
|
||||
|
@ -102,5 +108,10 @@ $('.fullscreen-close').click(function() {
|
|||
$('body').css('overflow', 'auto');
|
||||
$('body > div:not(.fullscreen-code)').css('user-select', '');
|
||||
$('.fullscreen-code').fadeOut();
|
||||
$('.fullscreen-code pre').replaceWith('<div id="fullscreen-code-placeholder"></div>')
|
||||
$('.fullscreen-code pre').replaceWith(
|
||||
'<div id="fullscreen-code-placeholder"></div>'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export { initialize };
|
||||
|
|
|
@ -1,102 +1,124 @@
|
|||
import $ from 'jquery';
|
||||
|
||||
///////////////////////////// Make headers linkable /////////////////////////////
|
||||
|
||||
var headingWhiteList = $("\
|
||||
function makeHeadersLinkable() {
|
||||
var headingWhiteList = $(
|
||||
'\
|
||||
.article--content h2, \
|
||||
.article--content h3, \
|
||||
.article--content h4, \
|
||||
.article--content h5, \
|
||||
.article--content h6 \
|
||||
");
|
||||
'
|
||||
);
|
||||
|
||||
var headingBlackList = ("\
|
||||
var headingBlackList =
|
||||
'\
|
||||
.influxdbu-banner h4 \
|
||||
");
|
||||
';
|
||||
|
||||
headingElements = headingWhiteList.not(headingBlackList);
|
||||
const headingElements = headingWhiteList.not(headingBlackList);
|
||||
|
||||
headingElements.each(function () {
|
||||
function getLink(element) {
|
||||
return ((element.attr('href') === undefined ) ? $(element).attr("id") : element.attr('href'))
|
||||
return element.attr('href') === undefined
|
||||
? $(element).attr('id')
|
||||
: element.attr('href');
|
||||
}
|
||||
var link = "<a href=\"#" + getLink($(this)) + "\"></a>"
|
||||
var link = '<a href="#' + getLink($(this)) + '"></a>';
|
||||
$(this).wrapInner(link);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
///////////////////////////////// Smooth Scroll /////////////////////////////////
|
||||
|
||||
function smoothScroll() {
|
||||
var elementWhiteList = [
|
||||
".tabs p a",
|
||||
".code-tabs p a",
|
||||
".children-links a",
|
||||
".list-links a",
|
||||
"a.url-trigger",
|
||||
"a.fullscreen-close"
|
||||
]
|
||||
|
||||
function scrollToAnchor(target) {
|
||||
var $target = $(target);
|
||||
if($target && $target.length > 0) {
|
||||
$('html, body').stop().animate({
|
||||
'scrollTop': ($target.offset().top)
|
||||
}, 400, 'swing', function () {
|
||||
window.location.hash = target;
|
||||
});
|
||||
|
||||
// Unique accordion functionality
|
||||
// If the target is an accordion element, open the accordion after scrolling
|
||||
if ($target.hasClass('expand')) {
|
||||
if ($(target + ' .expand-label .expand-toggle').hasClass('open')) {}
|
||||
else {
|
||||
$(target + '> .expand-label').trigger('click');
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
'.tabs p a',
|
||||
'.code-tabs p a',
|
||||
'.children-links a',
|
||||
'.list-links a',
|
||||
'a.url-trigger',
|
||||
'a.fullscreen-close',
|
||||
];
|
||||
|
||||
$('.article a[href^="#"]:not(' + elementWhiteList + ')').click(function (e) {
|
||||
e.preventDefault();
|
||||
scrollToAnchor(this.hash);
|
||||
});
|
||||
}
|
||||
|
||||
function scrollToAnchor(target) {
|
||||
var $target = $(target);
|
||||
if ($target && $target.length > 0) {
|
||||
$('html, body')
|
||||
.stop()
|
||||
.animate(
|
||||
{
|
||||
scrollTop: $target.offset().top,
|
||||
},
|
||||
400,
|
||||
'swing',
|
||||
function () {
|
||||
window.location.hash = target;
|
||||
}
|
||||
);
|
||||
|
||||
// Unique accordion functionality
|
||||
// If the target is an accordion element, open the accordion after scrolling
|
||||
if ($target.hasClass('expand')) {
|
||||
if ($(target + ' .expand-label .expand-toggle').hasClass('open')) {
|
||||
// Do nothing?
|
||||
} else {
|
||||
$(target + '> .expand-label').trigger('click');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////// Left Nav Interactions /////////////////////////////
|
||||
|
||||
$(".children-toggle").click(function(e) {
|
||||
e.preventDefault()
|
||||
function leftNavInteractions() {
|
||||
$('.children-toggle').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).toggleClass('open');
|
||||
$(this).siblings('.children').toggleClass('open');
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
//////////////////////////// Mobile Contents Toggle ////////////////////////////
|
||||
|
||||
function mobileContentsToggle() {
|
||||
$('#contents-toggle-btn').click(function (e) {
|
||||
e.preventDefault();
|
||||
$(this).toggleClass('open');
|
||||
$('#nav-tree').toggleClass('open');
|
||||
})
|
||||
|
||||
});
|
||||
}
|
||||
/////////////////////////////// Truncate Content ///////////////////////////////
|
||||
|
||||
$(".truncate-toggle").click(function(e) {
|
||||
e.preventDefault()
|
||||
var truncateParent = $(this).closest('.truncate')
|
||||
var truncateParentID = $(this).closest('.truncate')[0].id
|
||||
function truncateContent() {
|
||||
$('.truncate-toggle').click(function (e) {
|
||||
e.preventDefault();
|
||||
var truncateParent = $(this).closest('.truncate');
|
||||
var truncateParentID = $(this).closest('.truncate')[0].id;
|
||||
|
||||
if (truncateParent.hasClass('closed')) {
|
||||
$(this)[0].href = `#${truncateParentID}`
|
||||
$(this)[0].href = `#${truncateParentID}`;
|
||||
} else {
|
||||
$(this)[0].href = "#"
|
||||
$(this)[0].href = '#';
|
||||
}
|
||||
|
||||
truncateParent.toggleClass('closed')
|
||||
truncateParent.find('.truncate-content').toggleClass('closed')
|
||||
})
|
||||
|
||||
truncateParent.toggleClass('closed');
|
||||
truncateParent.find('.truncate-content').toggleClass('closed');
|
||||
});
|
||||
}
|
||||
////////////////////////////// Expand Accordions ///////////////////////////////
|
||||
|
||||
function expandAccordions() {
|
||||
$('.expand-label').click(function () {
|
||||
$(this).children('.expand-toggle').toggleClass('open')
|
||||
$(this).next('.expand-content').slideToggle(200)
|
||||
})
|
||||
$(this).children('.expand-toggle').toggleClass('open');
|
||||
$(this).next('.expand-content').slideToggle(200);
|
||||
});
|
||||
|
||||
// Expand accordions on load based on URL anchor
|
||||
function openAccordionByHash() {
|
||||
|
@ -108,47 +130,71 @@ function openAccordionByHash() {
|
|||
} else if ($(anchor).hasClass('expand')) {
|
||||
return $(anchor).children('.expand-label');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (expandElement() != null) {
|
||||
if (expandElement().children('.expand-toggle').hasClass('open')) {}
|
||||
else {
|
||||
if (expandElement().children('.expand-toggle').hasClass('open')) {
|
||||
// Do nothing?
|
||||
} else {
|
||||
expandElement().children('.expand-toggle').trigger('click');
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open accordions by hash on page load.
|
||||
openAccordionByHash()
|
||||
|
||||
openAccordionByHash();
|
||||
}
|
||||
////////////////////////// Inject tooltips on load //////////////////////////////
|
||||
|
||||
function injectTooltips() {
|
||||
$('.tooltip').each(function () {
|
||||
$toolTipText = $('<div/>').addClass('tooltip-text').text($(this).attr('data-tooltip-text'));
|
||||
$toolTipElement = $('<div/>').addClass('tooltip-container').append($toolTipText);
|
||||
const $toolTipText = $('<div/>')
|
||||
.addClass('tooltip-text')
|
||||
.text($(this).attr('data-tooltip-text'));
|
||||
const $toolTipElement = $('<div/>')
|
||||
.addClass('tooltip-container')
|
||||
.append($toolTipText);
|
||||
$(this).prepend($toolTipElement);
|
||||
});
|
||||
|
||||
}
|
||||
//////////////////// Style time cells in tables to not wrap ////////////////////
|
||||
|
||||
function styleTimeCells() {
|
||||
$('.article--content table').each(function () {
|
||||
var table = $(this);
|
||||
|
||||
table.find('td').each(function () {
|
||||
let cellContent = $(this)[0].innerText
|
||||
let cellContent = $(this)[0].innerText;
|
||||
|
||||
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.*Z/.test(cellContent)) {
|
||||
$(this).addClass('nowrap')
|
||||
$(this).addClass('nowrap');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/////////////////////// Open external links in a new tab ///////////////////////
|
||||
|
||||
function openExternalLinks() {
|
||||
$('.article--content a').each(function () {
|
||||
var currentHost = location.host;
|
||||
|
||||
if (!($(this)[0].href).includes(currentHost)) {
|
||||
if (!$(this)[0].href.includes(currentHost)) {
|
||||
$(this).attr('target', '_blank');
|
||||
};
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/////////////////////// Initialize all functions //////////////////////////////
|
||||
function initialize() {
|
||||
makeHeadersLinkable();
|
||||
smoothScroll();
|
||||
leftNavInteractions();
|
||||
mobileContentsToggle();
|
||||
truncateContent();
|
||||
expandAccordions();
|
||||
injectTooltips();
|
||||
styleTimeCells();
|
||||
openExternalLinks();
|
||||
}
|
||||
|
||||
export { initialize, scrollToAnchor };
|
||||
|
|
|
@ -1,3 +1,8 @@
|
|||
import $ from 'jquery';
|
||||
import { Datepicker } from 'vanillajs-datepicker';
|
||||
import { toggleModal } from './modals.js';
|
||||
import * as localStorage from './local-storage.js';
|
||||
|
||||
// Placeholder start date used in InfluxDB custom timestamps
|
||||
const defaultStartDate = '2022-01-01';
|
||||
|
||||
|
@ -16,24 +21,24 @@ function yesterday () {
|
|||
|
||||
// Split a date string into year, month, and day
|
||||
function datePart(date) {
|
||||
datePartRegex = /(\d{4})-(\d{2})-(\d{2})/;
|
||||
year = date.replace(datePartRegex, '$1');
|
||||
month = date.replace(datePartRegex, '$2');
|
||||
day = date.replace(datePartRegex, '$3');
|
||||
const datePartRegex = /(\d{4})-(\d{2})-(\d{2})/;
|
||||
const year = date.replace(datePartRegex, '$1');
|
||||
const month = date.replace(datePartRegex, '$2');
|
||||
const day = date.replace(datePartRegex, '$3');
|
||||
|
||||
return { year: year, month: month, day: day };
|
||||
}
|
||||
|
||||
///////////////////////// PREFERENCE COOKIE MANAGEMENT /////////////////////////
|
||||
|
||||
prefID = 'sample_get_started_date';
|
||||
const prefID = 'sample_get_started_date';
|
||||
|
||||
function setStartDate(setDate) {
|
||||
setPreference(prefID, setDate);
|
||||
localStorage.setPreference(prefID, setDate);
|
||||
}
|
||||
|
||||
function getStartDate() {
|
||||
return getPreference(prefID);
|
||||
return localStorage.getPreference(prefID);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -43,13 +48,13 @@ var startDate = getStartDate() || yesterday();
|
|||
|
||||
// Convert a time value to a Unix timestamp (seconds)
|
||||
function timeToUnixSeconds(time) {
|
||||
unixSeconds = new Date(time).getTime() / 1000;
|
||||
const unixSeconds = new Date(time).getTime() / 1000;
|
||||
|
||||
return unixSeconds;
|
||||
}
|
||||
|
||||
// Default time values in getting started sample data
|
||||
let times = [
|
||||
const defaultTimes = [
|
||||
{
|
||||
rfc3339: `${defaultStartDate}T08:00:00Z`,
|
||||
unix: timeToUnixSeconds(`${defaultStartDate}T08:00:00Z`),
|
||||
|
@ -104,9 +109,9 @@ let times = [
|
|||
}, // 1641067200
|
||||
];
|
||||
|
||||
function updateTimestamps (newStartDate) {
|
||||
function updateTimestamps (newStartDate, seedTimes=defaultTimes) {
|
||||
// Update the times array with replacement times
|
||||
times = times.map(x => {
|
||||
const times = seedTimes.map(x => {
|
||||
var newStartTimestamp = x.rfc3339.replace(/^.*T/, newStartDate + 'T');
|
||||
|
||||
return {
|
||||
|
@ -128,13 +133,13 @@ function updateTimestamps (newStartDate) {
|
|||
var wrapper = $(this)[0];
|
||||
|
||||
times.forEach(function (x) {
|
||||
oldDatePart = datePart(x.rfc3339.replace(/T.*$/, ''));
|
||||
newDatePart = datePart(x.rfc3339_new.replace(/T.*$/, ''));
|
||||
rfc3339Regex = new RegExp(
|
||||
const oldDatePart = datePart(x.rfc3339.replace(/T.*$/, ''));
|
||||
const newDatePart = datePart(x.rfc3339_new.replace(/T.*$/, ''));
|
||||
const rfc3339Regex = new RegExp(
|
||||
`${oldDatePart.year}(.*?)${oldDatePart.month}(.*?)${oldDatePart.day}`,
|
||||
'g'
|
||||
);
|
||||
rfc3339Repl = `${newDatePart.year}$1${newDatePart.month}$2${newDatePart.day}`;
|
||||
const rfc3339Repl = `${newDatePart.year}$1${newDatePart.month}$2${newDatePart.day}`;
|
||||
|
||||
wrapper.innerHTML = wrapper.innerHTML
|
||||
.replaceAll(x.unix, x.unix_new)
|
||||
|
@ -146,13 +151,13 @@ function updateTimestamps (newStartDate) {
|
|||
var wrapper = $(this)[0];
|
||||
|
||||
times.forEach(function (x) {
|
||||
oldDatePart = datePart(x.rfc3339.replace(/T.*$/, ''));
|
||||
newDatePart = datePart(x.rfc3339_new.replace(/T.*$/, ''));
|
||||
rfc3339Regex = new RegExp(
|
||||
const oldDatePart = datePart(x.rfc3339.replace(/T.*$/, ''));
|
||||
const newDatePart = datePart(x.rfc3339_new.replace(/T.*$/, ''));
|
||||
const rfc3339Regex = new RegExp(
|
||||
`${oldDatePart.year}-${oldDatePart.month}-${oldDatePart.day}`,
|
||||
'g'
|
||||
);
|
||||
rfc3339Repl = `${newDatePart.year}-${newDatePart.month}-${newDatePart.day}`;
|
||||
const rfc3339Repl = `${newDatePart.year}-${newDatePart.month}-${newDatePart.day}`;
|
||||
|
||||
wrapper.innerHTML = wrapper.innerHTML
|
||||
.replaceAll(x.unix, x.unix_new)
|
||||
|
@ -161,7 +166,7 @@ function updateTimestamps (newStartDate) {
|
|||
});
|
||||
|
||||
// Create a new seed times array with new start time for next change
|
||||
times = times.map(x => {
|
||||
return times.map((x) => {
|
||||
var newStartTimestamp = x.rfc3339.replace(/^.*T/, newStartDate + 'T');
|
||||
|
||||
return {
|
||||
|
@ -173,6 +178,12 @@ function updateTimestamps (newStartDate) {
|
|||
|
||||
/////////////////////// MODAL INTERACTIONS / DATE PICKER ///////////////////////
|
||||
|
||||
function CustomTimeTrigger({component}) {
|
||||
const $component = $(component);
|
||||
$component
|
||||
.find('a[data-action="open"]:first')
|
||||
.on('click', () => toggleModal('#influxdb-gs-date-select'));
|
||||
|
||||
// Date picker form element
|
||||
var datePickerEl = $('#custom-date-selector');
|
||||
|
||||
|
@ -189,22 +200,28 @@ const datepicker = new Datepicker(elem, {
|
|||
|
||||
// Initial update to yesterdays date ON PAGE LOAD
|
||||
// Conditionally set the start date cookie it startDate is equal to the default value
|
||||
updateTimestamps(startDate);
|
||||
let updatedTimes = updateTimestamps(startDate, defaultTimes);
|
||||
|
||||
if (startDate === yesterday()) {
|
||||
setStartDate(startDate);
|
||||
}
|
||||
|
||||
// Sumbit new date
|
||||
// Submit new date
|
||||
$('#submit-custom-date').click(function () {
|
||||
let newDate = datepicker.getDate();
|
||||
|
||||
if (newDate != undefined) {
|
||||
newDate = formatDate(newDate);
|
||||
|
||||
updateTimestamps(newDate);
|
||||
// Update the last updated timestamps with the new date
|
||||
// and reassign the updated times.
|
||||
updatedTimes = updateTimestamps(newDate, updatedTimes);
|
||||
setStartDate(newDate);
|
||||
toggleModal('#influxdb-gs-date-select');
|
||||
} else {
|
||||
toggleModal('#influxdb-gs-date-select');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export { CustomTimeTrigger };
|
||||
|
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
Copied and pasted this script for CSS swaps w/ cookies from
|
||||
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
|
||||
*/
|
||||
|
||||
// *** TO BE CUSTOMISED ***
|
||||
var style_preference_name = 'theme';
|
||||
var style_cookie_duration = 30;
|
||||
var style_domain = 'docs.influxdata.com';
|
||||
|
||||
// *** END OF CUSTOMISABLE SECTION ***
|
||||
// You do not need to customise anything below this line
|
||||
|
||||
function switchStyle (css_title) {
|
||||
// You may use this script on your site free of charge provided
|
||||
// you do not remove this notice or the URL below. Script from
|
||||
// http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
|
||||
var i, link_tag;
|
||||
for (
|
||||
i = 0, link_tag = document.getElementsByTagName('link');
|
||||
i < link_tag.length;
|
||||
i++
|
||||
) {
|
||||
if (
|
||||
link_tag[i].rel.indexOf('stylesheet') != -1 &&
|
||||
link_tag[i].title.includes('theme')
|
||||
) {
|
||||
link_tag[i].disabled = true;
|
||||
if (link_tag[i].title == css_title) {
|
||||
link_tag[i].disabled = false;
|
||||
}
|
||||
}
|
||||
setPreference(style_preference_name, css_title.replace(/-theme/, ''));
|
||||
}
|
||||
}
|
||||
|
||||
function setStyleFromCookie () {
|
||||
var css_title = `${getPreference(style_preference_name)}-theme`;
|
||||
if (css_title !== undefined) {
|
||||
switchStyle(css_title);
|
||||
}
|
||||
}
|
|
@ -13,22 +13,24 @@ function getCalloutID (el) {
|
|||
|
||||
// Hide a callout and update the cookie with the viewed callout
|
||||
function hideCallout (calloutID) {
|
||||
if (!notificationIsRead(calloutID)) {
|
||||
setNotificationAsRead(calloutID, 'callout');
|
||||
if (!window.LocalStorageAPI.notificationIsRead(calloutID)) {
|
||||
window.LocalStorageAPI.setNotificationAsRead(calloutID, 'callout');
|
||||
$(`#${calloutID}`).fadeOut(200);
|
||||
}
|
||||
}
|
||||
|
||||
// Show the url feature callouts on page load
|
||||
$(document).ready(function () {
|
||||
$('.feature-callout').each(function () {
|
||||
calloutID = calloutID($(this));
|
||||
const calloutID = getCalloutID($(this));
|
||||
|
||||
if (!notificationIsRead(calloutID, 'callout')) {
|
||||
if (!window.LocalStorageAPI.notificationIsRead(calloutID, 'callout')) {
|
||||
$(`#${calloutID}.feature-callout`)
|
||||
.fadeIn(300)
|
||||
.removeClass('start-position');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Hide the InfluxDB URL selector callout
|
||||
// $('button.url-trigger, #influxdb-url-selector .close').click(function () {
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
|
||||
/** Delay execution of a function `fn` for a number of milliseconds `ms`
|
||||
* e.g., delay a validation handler to avoid annoying the user.
|
||||
*/
|
||||
function delay(fn, ms) {
|
||||
let timer = 0;
|
||||
return function (...args) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(fn.bind(this, ...args), ms || 0);
|
||||
};
|
||||
}
|
||||
|
||||
export { delay };
|
|
@ -1,48 +1,34 @@
|
|||
var placeholderUrls = {
|
||||
oss: 'http://localhost:8086',
|
||||
cloud: 'https://cloud2.influxdata.com',
|
||||
core: 'http://localhost:8181',
|
||||
enterprise: 'http://localhost:8181',
|
||||
serverless: 'https://cloud2.influxdata.com',
|
||||
dedicated: 'cluster-id.a.influxdb.io',
|
||||
clustered: 'cluster-host.com',
|
||||
};
|
||||
|
||||
/*
|
||||
NOTE: The defaultUrls variable is defined in assets/js/local-storage.js
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////// INFLUXDB URL PREFERENCE /////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
import * as pageParams from '@params';
|
||||
import {
|
||||
DEFAULT_STORAGE_URLS,
|
||||
getPreference,
|
||||
setPreference,
|
||||
setInfluxDBUrls,
|
||||
removeInfluxDBUrl,
|
||||
getInfluxDBUrl,
|
||||
getInfluxDBUrls,
|
||||
} from './local-storage.js';
|
||||
import $ from 'jquery';
|
||||
import { context as PRODUCT_CONTEXT, referrerHost } from './page-context.js';
|
||||
import { delay } from './helpers.js';
|
||||
import { toggleModal } from './modals.js';
|
||||
|
||||
var elementSelector = '.article--content pre:not(.preserve)';
|
||||
export const CLOUD_URLS = Object.values(pageParams.influxdb_urls.cloud.providers).flatMap((provider) => provider.regions?.map((region) => region.url));
|
||||
|
||||
// Return the page context (cloud, serverless, oss/enterprise, dedicated, clustered, other)
|
||||
function context() {
|
||||
if (/\/influxdb\/cloud\//.test(window.location.pathname)) {
|
||||
return 'cloud';
|
||||
} else if (/\/influxdb3\/core/.test(window.location.pathname)) {
|
||||
return 'core';
|
||||
} else if (/\/influxdb3\/enterprise/.test(window.location.pathname)) {
|
||||
return 'enterprise';
|
||||
} else if (/\/influxdb3\/cloud-serverless/.test(window.location.pathname)) {
|
||||
return 'serverless';
|
||||
} else if (/\/influxdb3\/cloud-dedicated/.test(window.location.pathname)) {
|
||||
return 'dedicated';
|
||||
} else if (/\/influxdb3\/clustered/.test(window.location.pathname)) {
|
||||
return 'clustered';
|
||||
} else if (
|
||||
/\/(enterprise_|influxdb).*\/v[1-2]\//.test(window.location.pathname)
|
||||
) {
|
||||
return 'oss/enterprise';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
export function InfluxDBUrl() {
|
||||
const UNIQUE_URL_PRODUCTS = ['dedicated', 'clustered'];
|
||||
const IS_UNIQUE_URL_PRODUCT = UNIQUE_URL_PRODUCTS.includes(PRODUCT_CONTEXT);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////// Session-management functions /////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Add actual cloud URLs as needed
|
||||
const elementSelector = '.article--content pre:not(.preserve)';
|
||||
|
||||
// Retrieve the user's InfluxDB preference (cloud or oss) from the influxdb_pref
|
||||
// local storage key. Default is cloud.
|
||||
///////////////////// Stored preference management ///////////////////////
|
||||
// Retrieve the user's InfluxDB preference (cloud or oss) from the influxdb_pref local storage key. Default is cloud.
|
||||
function getURLPreference() {
|
||||
return getPreference('influxdb_url');
|
||||
}
|
||||
|
@ -72,7 +58,7 @@ function setURLPreference(preference) {
|
|||
|
||||
// Store URLs in the urls local storage object
|
||||
function storeUrl(context, newUrl, prevUrl) {
|
||||
urlsObj = {};
|
||||
let urlsObj = {};
|
||||
urlsObj['prev_' + context] = prevUrl;
|
||||
urlsObj[context] = newUrl;
|
||||
|
||||
|
@ -95,7 +81,7 @@ function removeCustomUrl() {
|
|||
// Store a product URL in the urls local storage object
|
||||
// Used to populate the custom URL field
|
||||
function storeProductUrl(product, productUrl) {
|
||||
urlsObj = {};
|
||||
let urlsObj = {};
|
||||
urlsObj[product] = productUrl;
|
||||
|
||||
setInfluxDBUrls(urlsObj);
|
||||
|
@ -130,52 +116,27 @@ function addPreserve() {
|
|||
|
||||
// Retrieve the currently selected URLs from the urls local storage object.
|
||||
function getUrls() {
|
||||
var storedUrls = getInfluxDBUrls();
|
||||
var currentCloudUrl = storedUrls.cloud;
|
||||
var currentOSSUrl = storedUrls.oss;
|
||||
var currentCoreUrl = storedUrls.core;
|
||||
var currentEnterpriseUrl = storedUrls.enterprise;
|
||||
var currentServerlessUrl = storedUrls.serverless;
|
||||
var currentDedicatedUrl = storedUrls.dedicated;
|
||||
var currentClusteredUrl = storedUrls.clustered;
|
||||
var urls = {
|
||||
oss: currentOSSUrl,
|
||||
cloud: currentCloudUrl,
|
||||
core: currentCoreUrl,
|
||||
enterprise: currentEnterpriseUrl,
|
||||
serverless: currentServerlessUrl,
|
||||
dedicated: currentDedicatedUrl,
|
||||
clustered: currentClusteredUrl,
|
||||
};
|
||||
return urls;
|
||||
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = getInfluxDBUrls();
|
||||
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
|
||||
}
|
||||
|
||||
// Retrieve the previously selected URLs from the from the urls local storage object.
|
||||
// This is used to update URLs whenever you switch between browser tabs.
|
||||
function getPrevUrls() {
|
||||
var storedUrls = getInfluxDBUrls();
|
||||
var prevCloudUrl = storedUrls.prev_cloud;
|
||||
var prevOSSUrl = storedUrls.prev_oss;
|
||||
var prevCoreUrl = storedUrls.prev_core;
|
||||
var prevEnterpriseUrl = storedUrls.prev_enterprise;
|
||||
var prevServerlessUrl = storedUrls.prev_serverless;
|
||||
var prevDedicatedUrl = storedUrls.prev_dedicated;
|
||||
var prevClusteredUrl = storedUrls.prev_clustered;
|
||||
var prevUrls = {
|
||||
oss: prevOSSUrl,
|
||||
cloud: prevCloudUrl,
|
||||
core: prevCoreUrl,
|
||||
enterprise: prevEnterpriseUrl,
|
||||
serverless: prevServerlessUrl,
|
||||
dedicated: prevDedicatedUrl,
|
||||
clustered: prevClusteredUrl,
|
||||
};
|
||||
return prevUrls;
|
||||
const {
|
||||
prev_cloud: cloud,
|
||||
prev_oss: oss,
|
||||
prev_core: core,
|
||||
prev_enterprise: enterprise,
|
||||
prev_serverless: serverless,
|
||||
prev_dedicated: dedicated,
|
||||
prev_clustered: clustered,
|
||||
} = getInfluxDBUrls();
|
||||
return { oss, cloud, core, enterprise, serverless, dedicated, clustered };
|
||||
}
|
||||
|
||||
// Iterate through code blocks and update InfluxDB urls
|
||||
function updateUrls(prevUrls, newUrls) {
|
||||
var preference = getURLPreference();
|
||||
var prevUrlsParsed = {
|
||||
oss: {},
|
||||
cloud: {},
|
||||
|
@ -241,29 +202,43 @@ function updateUrls(prevUrls, newUrls) {
|
|||
{ replace: prevUrlsParsed.clustered, with: newUrlsParsed.clustered },
|
||||
];
|
||||
|
||||
if (context() === 'cloud') {
|
||||
var replacements = cloudReplacements;
|
||||
} else if (context() === 'core') {
|
||||
var replacements = coreReplacements;
|
||||
} else if (context() === 'enterprise') {
|
||||
var replacements = enterpriseReplacements;
|
||||
} else if (context() === 'serverless') {
|
||||
var replacements = serverlessReplacements;
|
||||
} else if (context() === 'dedicated') {
|
||||
var replacements = dedicatedReplacements;
|
||||
} else if (context() === 'clustered') {
|
||||
var replacements = clusteredReplacements;
|
||||
} else if (context() === 'oss/enterprise') {
|
||||
var replacements = ossReplacements;
|
||||
} else if (preference === 'cloud') {
|
||||
var replacements = cloudReplacements;
|
||||
var replacements;
|
||||
switch (PRODUCT_CONTEXT) {
|
||||
case 'cloud':
|
||||
replacements = cloudReplacements;
|
||||
break;
|
||||
case 'core':
|
||||
replacements = coreReplacements;
|
||||
break;
|
||||
case 'enterprise':
|
||||
replacements = enterpriseReplacements;
|
||||
break;
|
||||
case 'serverless':
|
||||
replacements = serverlessReplacements;
|
||||
break;
|
||||
case 'dedicated':
|
||||
replacements = dedicatedReplacements;
|
||||
break;
|
||||
case 'clustered':
|
||||
replacements = clusteredReplacements;
|
||||
break;
|
||||
case 'oss/enterprise':
|
||||
replacements = ossReplacements;
|
||||
break;
|
||||
default:
|
||||
if (getURLPreference() === 'cloud') {
|
||||
replacements = cloudReplacements;
|
||||
} else {
|
||||
var replacements = ossReplacements;
|
||||
replacements = ossReplacements;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
replacements.forEach(function (o) {
|
||||
if (o.replace.origin != o.with.origin) {
|
||||
var fuzzyOrigin = new RegExp(o.replace.origin + '(:(^443)|[0-9]+)?', 'g');
|
||||
var fuzzyOrigin = new RegExp(
|
||||
o.replace.origin + '(:(^443)|[0-9]+)?',
|
||||
'g'
|
||||
);
|
||||
$(elementSelector).each(function () {
|
||||
$(this).html(
|
||||
$(this)
|
||||
|
@ -310,25 +285,25 @@ function updateUrls(prevUrls, newUrls) {
|
|||
}
|
||||
|
||||
// Append the URL selector button to each codeblock containing a placeholder URL
|
||||
function appendUrlSelector() {
|
||||
var appendToUrls = [
|
||||
placeholderUrls.oss,
|
||||
placeholderUrls.cloud,
|
||||
placeholderUrls.core,
|
||||
placeholderUrls.enterprise,
|
||||
placeholderUrls.serverless,
|
||||
placeholderUrls.dedicated,
|
||||
placeholderUrls.clustered,
|
||||
];
|
||||
function appendUrlSelector(urls={
|
||||
cloud: '',
|
||||
oss: '',
|
||||
core: '',
|
||||
enterprise: '',
|
||||
serverless: '',
|
||||
dedicated: '',
|
||||
clustered: '',
|
||||
}) {
|
||||
const appendToUrls = Object.values(urls);
|
||||
|
||||
getBtnText = (context) => {
|
||||
contextText = {
|
||||
const getBtnText = (context) => {
|
||||
const contextText = {
|
||||
'oss/enterprise': 'Change InfluxDB URL',
|
||||
cloud: 'InfluxDB Cloud Region',
|
||||
core: 'Change InfluxDB URL',
|
||||
enterprise: 'Change InfluxDB URL',
|
||||
serverless: 'InfluxDB Cloud Region',
|
||||
dedicated: 'Set Dedicated cluster URL',
|
||||
dedicated: 'Set Cloud Dedicated cluster URL',
|
||||
clustered: 'Set InfluxDB cluster URL',
|
||||
other: 'InfluxDB Cloud or OSS?',
|
||||
};
|
||||
|
@ -342,7 +317,7 @@ function appendUrlSelector() {
|
|||
if (code.includes(url)) {
|
||||
$(this).after(
|
||||
"<div class='select-url'><a class='url-trigger' href='#'>" +
|
||||
getBtnText(context()) +
|
||||
getBtnText(PRODUCT_CONTEXT) +
|
||||
'</a></div>'
|
||||
);
|
||||
$('.select-url').fadeIn(400);
|
||||
|
@ -351,18 +326,20 @@ function appendUrlSelector() {
|
|||
});
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// Function executions //////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
////////////////// Initialize InfluxDB URL interactions ////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Add the preserve tag to code blocks that shouldn't be updated
|
||||
addPreserve();
|
||||
const { cloud, oss, core, enterprise, serverless, dedicated, clustered } = DEFAULT_STORAGE_URLS;
|
||||
|
||||
// Append URL selector buttons to code blocks
|
||||
appendUrlSelector();
|
||||
appendUrlSelector({ cloud, oss, core, enterprise, serverless, dedicated, clustered });
|
||||
|
||||
// Update URLs on load
|
||||
updateUrls(placeholderUrls, getUrls());
|
||||
|
||||
updateUrls({ cloud, oss, core, enterprise, serverless, dedicated, clustered }, getUrls());
|
||||
|
||||
// Set active radio button on page load
|
||||
setRadioButtons(getUrls());
|
||||
|
@ -381,11 +358,10 @@ $('.url-trigger').click(function (e) {
|
|||
|
||||
// Set the selected URL radio buttons to :checked
|
||||
function setRadioButtons() {
|
||||
currentUrls = getUrls();
|
||||
$('input[name="influxdb-cloud-url"][value="' + currentUrls.cloud + '"]').prop(
|
||||
'checked',
|
||||
true
|
||||
);
|
||||
const currentUrls = getUrls();
|
||||
$(
|
||||
'input[name="influxdb-cloud-url"][value="' + currentUrls.cloud + '"]'
|
||||
).prop('checked', true);
|
||||
$(
|
||||
'input[name="influxdb-serverless-url"][value="' +
|
||||
currentUrls.serverless +
|
||||
|
@ -399,10 +375,11 @@ function setRadioButtons() {
|
|||
'checked',
|
||||
true
|
||||
);
|
||||
$('input[name="influxdb-enterprise-url"][value="' + currentUrls.enterprise + '"]').prop(
|
||||
'checked',
|
||||
true
|
||||
);
|
||||
$(
|
||||
'input[name="influxdb-enterprise-url"][value="' +
|
||||
currentUrls.enterprise +
|
||||
'"]'
|
||||
).prop('checked', true);
|
||||
}
|
||||
|
||||
// Add checked to fake-radio if cluster is selected on page load
|
||||
|
@ -481,10 +458,17 @@ $('input[name="influxdb-clustered-url"]').change(function () {
|
|||
updateUrls(getPrevUrls(), getUrls());
|
||||
});
|
||||
|
||||
// Populate the product-specific URL fields on page load
|
||||
UNIQUE_URL_PRODUCTS.forEach(function (productEl) {
|
||||
let productUrlCookie = getInfluxDBUrl(productEl);
|
||||
$(`input#${productEl}-url-field`).val(productUrlCookie);
|
||||
$(`#${productEl}-url-field`).val(productUrlCookie);
|
||||
});
|
||||
|
||||
// Toggle preference tabs
|
||||
function togglePrefBtns(el) {
|
||||
preference = el.length ? el.attr('id').replace('pref-', '') : 'cloud';
|
||||
prefUrls = $('#' + preference + '-urls');
|
||||
const preference = el.length ? el.attr('id').replace('pref-', '') : 'cloud';
|
||||
const prefUrls = $('#' + preference + '-urls');
|
||||
|
||||
el.addClass('active');
|
||||
el.siblings().removeClass('active');
|
||||
|
@ -500,8 +484,8 @@ $('#pref-tabs .pref-tab').click(function () {
|
|||
|
||||
// Select preference tab from local storage
|
||||
function showPreference() {
|
||||
var preference = getPreference('influxdb_url');
|
||||
prefTab = $('#pref-' + preference);
|
||||
const preference = getPreference('influxdb_url');
|
||||
const prefTab = $('#pref-' + preference);
|
||||
togglePrefBtns(prefTab);
|
||||
}
|
||||
|
||||
|
@ -515,38 +499,42 @@ showPreference();
|
|||
// Validate custom URLs
|
||||
function validateUrl(url) {
|
||||
/** validDomain = (Named host | IPv6 host | IPvFuture host)(:Port)? **/
|
||||
var validDomain = new RegExp(
|
||||
const validDomain = new RegExp(
|
||||
`([a-z0-9\-._~%]+` +
|
||||
`|\[[a-f0-9:.]+\]` +
|
||||
`|\[v[a-f0-9][a-z0-9\-._~%!$&'()*+,;=:]+\])` +
|
||||
`(:[0-9]+)?`
|
||||
);
|
||||
|
||||
if (!['dedicated', 'clustered'].includes(context())) {
|
||||
if (!IS_UNIQUE_URL_PRODUCT) {
|
||||
// Validation for non-dedicated, non-clustered custom InfluxDB URLs
|
||||
try {
|
||||
new URL(url);
|
||||
return { valid: true, error: '' };
|
||||
} catch (e) {
|
||||
var validProtocol = /^http(s?)/;
|
||||
var protocol = url.match(/http(s?):\/\//)
|
||||
const validProtocol = /^http(s?)/;
|
||||
const protocol = url.match(/http(s?):\/\//)
|
||||
? url.match(/http(s?):\/\//)[0]
|
||||
: '';
|
||||
var domain = url.replace(protocol, '');
|
||||
const domain = url.replace(protocol, '');
|
||||
|
||||
// First use the regex to check for an HTTP protocol and valid domain
|
||||
// --JS URL validation can't differentiate host:port string from a protocol.
|
||||
if (validProtocol.test(protocol) == false) {
|
||||
return { valid: false, error: 'Invalid protocol, use http[s]' };
|
||||
} else if (validDomain.test(domain) == false) {
|
||||
return { valid: false, error: 'Invalid domain' };
|
||||
} else if (e) {
|
||||
} else {
|
||||
try {
|
||||
new URL(url);
|
||||
return { valid: true, error: '' };
|
||||
} catch (e) {
|
||||
if (e instanceof TypeError) {
|
||||
return { valid: false, error: 'Invalid URL' };
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Validation for product-specific URLs
|
||||
var includesProtocol = /^.*:\/\//;
|
||||
var protocol = url.match(/^.*:\/\//) ? url.match(/^.*:\/\//)[0] : '';
|
||||
var domain = url.replace(protocol, '');
|
||||
const includesProtocol = /^.*:\/\//;
|
||||
const protocol = url.match(/^.*:\/\//) ? url.match(/^.*:\/\//)[0] : '';
|
||||
const domain = url.replace(protocol, '');
|
||||
|
||||
if (url.length === 0) {
|
||||
return { valid: true, error: '' };
|
||||
|
@ -580,7 +568,7 @@ function applyCustomUrl() {
|
|||
if (urlValidation.valid) {
|
||||
hideValidationMessage();
|
||||
storeCustomUrl(custUrl);
|
||||
storeUrl(context(), custUrl, getUrls()[context()]);
|
||||
storeUrl(PRODUCT_CONTEXT, custUrl, getUrls()[PRODUCT_CONTEXT]);
|
||||
updateUrls(getPrevUrls(), getUrls());
|
||||
} else {
|
||||
showValidationMessage(urlValidation);
|
||||
|
@ -589,7 +577,7 @@ function applyCustomUrl() {
|
|||
removeCustomUrl();
|
||||
hideValidationMessage();
|
||||
$(
|
||||
'input[name="influxdb-${context()}-url"][value="' + defaultUrls[context()] + '"]'
|
||||
`input[name="influxdb-${PRODUCT_CONTEXT}-url"][value="${DEFAULT_URLS[PRODUCT_CONTEXT]}"]`
|
||||
).trigger('click');
|
||||
}
|
||||
}
|
||||
|
@ -603,7 +591,7 @@ function applyProductUrl(product) {
|
|||
if (urlValidation.valid) {
|
||||
hideValidationMessage();
|
||||
storeProductUrl(product, productUrl);
|
||||
getUrls(product, productUrl, getUrls()[product]);
|
||||
storeUrl(product, productUrl, getUrls()[product]);
|
||||
updateUrls(getPrevUrls(), getUrls());
|
||||
} else {
|
||||
showValidationMessage(urlValidation);
|
||||
|
@ -615,7 +603,7 @@ function applyProductUrl(product) {
|
|||
}
|
||||
|
||||
// Trigger radio button on custom URL field focus
|
||||
$('input#custom-url-field').focus(function (e) {
|
||||
$('input#custom-url-field').focus(function () {
|
||||
$('input#custom[type="radio"]').trigger('click');
|
||||
});
|
||||
|
||||
|
@ -623,20 +611,19 @@ $('input#custom-url-field').focus(function (e) {
|
|||
$('#custom-url').submit(function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const productContext = context();
|
||||
let url = $('#custom-url-field').val() || '';
|
||||
|
||||
if (['dedicated', 'clustered'].includes(productContext)) {
|
||||
url = $(`#${productContext}-url-field`).val() || '';
|
||||
if (['dedicated', 'clustered'].includes(PRODUCT_CONTEXT)) {
|
||||
url = $(`#${PRODUCT_CONTEXT}-url-field`).val() || '';
|
||||
}
|
||||
|
||||
const urlValidation = validateUrl(url);
|
||||
|
||||
if (url === '' || urlValidation.valid) {
|
||||
if (!['dedicated', 'clustered'].includes(productContext)) {
|
||||
if (!['dedicated', 'clustered'].includes(PRODUCT_CONTEXT)) {
|
||||
applyCustomUrl();
|
||||
} else {
|
||||
applyProductUrl(productContext);
|
||||
applyProductUrl(PRODUCT_CONTEXT);
|
||||
}
|
||||
$('#modal-close').trigger('click');
|
||||
} else {
|
||||
|
@ -653,22 +640,11 @@ var urlValueElements = [
|
|||
|
||||
// Store the custom InfluxDB URL or product-specific URL when exiting the field
|
||||
$(urlValueElements).blur(function () {
|
||||
!['dedicated', 'clustered'].includes(context())
|
||||
!['dedicated', 'clustered'].includes(PRODUCT_CONTEXT)
|
||||
? applyCustomUrl()
|
||||
: applyProductUrl(context());
|
||||
: applyProductUrl(PRODUCT_CONTEXT);
|
||||
});
|
||||
|
||||
/** Delay execution of a function `fn` for a number of milliseconds `ms`
|
||||
* e.g., delay a validation handler to avoid annoying the user.
|
||||
*/
|
||||
function delay(fn, ms) {
|
||||
let timer = 0;
|
||||
return function (...args) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(fn.bind(this, ...args), ms || 0);
|
||||
};
|
||||
}
|
||||
|
||||
function handleUrlValidation() {
|
||||
let url = $(urlValueElements).val();
|
||||
let urlValidation = validateUrl(url);
|
||||
|
@ -692,7 +668,7 @@ if (customUrlOnLoad != '') {
|
|||
var productsWithUniqueURLs = ['dedicated', 'clustered'];
|
||||
|
||||
productsWithUniqueURLs.forEach(function (productEl) {
|
||||
productUrlCookie = getInfluxDBUrl(productEl);
|
||||
const productUrlCookie = getInfluxDBUrl(productEl);
|
||||
$(`input#${productEl}-url-field`).val(productUrlCookie);
|
||||
$(`#${productEl}-url-field`).val(productUrlCookie);
|
||||
});
|
||||
|
@ -701,16 +677,13 @@ productsWithUniqueURLs.forEach(function (productEl) {
|
|||
/////////////////////////// Dynamically update URLs ////////////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Extract the protocol and hostname of referrer
|
||||
referrerMatch = document.referrer.match(/^(?:[^\/]*\/){2}[^\/]+/g);
|
||||
referrerHost = referrerMatch ? referrerMatch[0] : '';
|
||||
|
||||
// Check if the referrerHost is one of the cloud URLs
|
||||
// cloudUrls is built dynamically in layouts/partials/footer/javascript.html
|
||||
if (cloudUrls.includes(referrerHost)) {
|
||||
if (CLOUD_URLS.includes(referrerHost)) {
|
||||
storeUrl('cloud', referrerHost, getUrls().cloud);
|
||||
updateUrls(getPrevUrls(), getUrls());
|
||||
setRadioButtons();
|
||||
setURLPreference('cloud');
|
||||
showPreference();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
- messages: Messages (data/notifications.yaml) that have been seen (array)
|
||||
- callouts: Feature callouts that have been seen (array)
|
||||
*/
|
||||
import * as pageParams from '@params';
|
||||
|
||||
// Prefix for all InfluxData docs local storage
|
||||
const storagePrefix = 'influxdata_docs_';
|
||||
|
@ -17,14 +18,14 @@ const storagePrefix = 'influxdata_docs_';
|
|||
/*
|
||||
Initialize data in local storage with a default value.
|
||||
*/
|
||||
initializeLocalStorage = (storageKey, defaultValue) => {
|
||||
fullStorageKey = storagePrefix + storageKey;
|
||||
function initializeStorageItem(storageKey, defaultValue) {
|
||||
const fullStorageKey = storagePrefix + storageKey;
|
||||
|
||||
// Check if the data exists before initializing the data
|
||||
if (localStorage.getItem(fullStorageKey) === null) {
|
||||
localStorage.setItem(fullStorageKey, defaultValue);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -35,7 +36,7 @@ initializeLocalStorage = (storageKey, defaultValue) => {
|
|||
const prefStorageKey = storagePrefix + 'preferences';
|
||||
|
||||
// Default preferences
|
||||
var defaultPrefObj = {
|
||||
const defaultPrefObj = {
|
||||
api_lib: null,
|
||||
influxdb_url: 'cloud',
|
||||
sidebar_state: 'open',
|
||||
|
@ -48,119 +49,113 @@ var defaultPrefObj = {
|
|||
Retrieve a preference from the preference key.
|
||||
If the key doesn't exist, initialize it with default values.
|
||||
*/
|
||||
getPreference = prefName => {
|
||||
function getPreference(prefName) {
|
||||
// Initialize preference data if it doesn't already exist
|
||||
if (localStorage.getItem(prefStorageKey) === null) {
|
||||
initializeLocalStorage('preferences', JSON.stringify(defaultPrefObj));
|
||||
initializeStorageItem('preferences', JSON.stringify(defaultPrefObj));
|
||||
}
|
||||
|
||||
// Retrieve and parse preferences as JSON
|
||||
prefString = localStorage.getItem(prefStorageKey);
|
||||
prefObj = JSON.parse(prefString);
|
||||
const prefString = localStorage.getItem(prefStorageKey);
|
||||
const prefObj = JSON.parse(prefString);
|
||||
|
||||
// Return the value of the specified preference
|
||||
return prefObj[prefName];
|
||||
};
|
||||
}
|
||||
|
||||
// Set a preference in the preferences key
|
||||
setPreference = (prefID, prefValue) => {
|
||||
var prefString = localStorage.getItem(prefStorageKey);
|
||||
let prefObj = JSON.parse(prefString);
|
||||
function setPreference(prefID, prefValue) {
|
||||
const prefString = localStorage.getItem(prefStorageKey);
|
||||
const prefObj = JSON.parse(prefString);
|
||||
|
||||
prefObj[prefID] = prefValue;
|
||||
|
||||
localStorage.setItem(prefStorageKey, JSON.stringify(prefObj));
|
||||
};
|
||||
}
|
||||
|
||||
// Return an object containing all preferences
|
||||
getPreferences = () => JSON.parse(localStorage.getItem(prefStorageKey));
|
||||
function getPreferences() {
|
||||
return JSON.parse(localStorage.getItem(prefStorageKey));
|
||||
}
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////// INFLUXDATA DOCS URLS /////////////////////////////
|
||||
//////////// MANAGE INFLUXDATA DOCS URLS IN LOCAL STORAGE //////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
*/
|
||||
|
||||
const urlStorageKey = storagePrefix + 'urls';
|
||||
|
||||
// Default URLs per product
|
||||
var defaultUrls = {
|
||||
oss: 'http://localhost:8086',
|
||||
cloud: 'https://us-west-2-1.aws.cloud2.influxdata.com',
|
||||
core: 'http://localhost:8181',
|
||||
enterprise: 'http://localhost:8181',
|
||||
serverless: 'https://us-east-1-1.aws.cloud2.influxdata.com',
|
||||
dedicated: 'cluster-id.a.influxdb.io',
|
||||
clustered: 'cluster-host.com',
|
||||
};
|
||||
const defaultUrls = {};
|
||||
Object.entries(pageParams.influxdb_urls).forEach(([product, {providers}]) => {
|
||||
defaultUrls[product] = providers.filter(provider => provider.name === 'Default')[0]?.regions[0]?.url;
|
||||
});
|
||||
|
||||
// Defines the default urls value
|
||||
var defaultUrlsObj = {
|
||||
export const DEFAULT_STORAGE_URLS = {
|
||||
oss: defaultUrls.oss,
|
||||
cloud: defaultUrls.cloud,
|
||||
serverless: defaultUrls.serverless,
|
||||
core: defaultUrls.core,
|
||||
enterprise: defaultUrls.enterprise,
|
||||
dedicated: defaultUrls.dedicated,
|
||||
dedicated: defaultUrls.cloud_dedicated,
|
||||
clustered: defaultUrls.clustered,
|
||||
prev_oss: defaultUrls.oss,
|
||||
prev_cloud: defaultUrls.cloud,
|
||||
prev_core: defaultUrls.core,
|
||||
prev_enterprise: defaultUrls.enterprise,
|
||||
prev_serverless: defaultUrls.serverless,
|
||||
prev_dedicated: defaultUrls.dedicated,
|
||||
prev_dedicated: defaultUrls.cloud_dedicated,
|
||||
prev_clustered: defaultUrls.clustered,
|
||||
custom: '',
|
||||
};
|
||||
|
||||
const urlStorageKey = storagePrefix + 'urls';
|
||||
|
||||
// Return an object that contains all InfluxDB urls stored in the urls key
|
||||
getInfluxDBUrls = () => {
|
||||
function getInfluxDBUrls() {
|
||||
// Initialize urls data if it doesn't already exist
|
||||
if (localStorage.getItem(urlStorageKey) === null) {
|
||||
initializeLocalStorage('urls', JSON.stringify(defaultUrlsObj));
|
||||
initializeStorageItem('urls', JSON.stringify(DEFAULT_STORAGE_URLS));
|
||||
}
|
||||
|
||||
return JSON.parse(localStorage.getItem(urlStorageKey));
|
||||
};
|
||||
}
|
||||
|
||||
// Get the current or previous URL for a specific product or a custom url
|
||||
getInfluxDBUrl = product => {
|
||||
function getInfluxDBUrl(product) {
|
||||
// Initialize urls data if it doesn't already exist
|
||||
if (localStorage.getItem(urlStorageKey) === null) {
|
||||
initializeLocalStorage('urls', JSON.stringify(defaultUrlsObj));
|
||||
initializeStorageItem('urls', JSON.stringify(DEFAULT_STORAGE_URLS));
|
||||
}
|
||||
|
||||
// Retrieve and parse the URLs as JSON
|
||||
urlsString = localStorage.getItem(urlStorageKey);
|
||||
urlsObj = JSON.parse(urlsString);
|
||||
const urlsString = localStorage.getItem(urlStorageKey);
|
||||
const urlsObj = JSON.parse(urlsString);
|
||||
|
||||
// Return the URL of the specified product
|
||||
return urlsObj[product];
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Set multiple product URLs in the urls key.
|
||||
Input should be an object where the key is the product and the value is the
|
||||
URL to set for that product.
|
||||
*/
|
||||
setInfluxDBUrls = updatedUrlsObj => {
|
||||
var urlsString = localStorage.getItem(urlStorageKey);
|
||||
let urlsObj = JSON.parse(urlsString);
|
||||
function setInfluxDBUrls(updatedUrlsObj) {
|
||||
const urlsString = localStorage.getItem(urlStorageKey);
|
||||
const urlsObj = JSON.parse(urlsString);
|
||||
|
||||
newUrlsObj = { ...urlsObj, ...updatedUrlsObj };
|
||||
const newUrlsObj = { ...urlsObj, ...updatedUrlsObj };
|
||||
|
||||
localStorage.setItem(urlStorageKey, JSON.stringify(newUrlsObj));
|
||||
};
|
||||
}
|
||||
|
||||
// Set an InfluxDB URL to an empty string in the urls key
|
||||
removeInfluxDBUrl = product => {
|
||||
var urlsString = localStorage.getItem(urlStorageKey);
|
||||
let urlsObj = JSON.parse(urlsString);
|
||||
function removeInfluxDBUrl(product) {
|
||||
const urlsString = localStorage.getItem(urlStorageKey);
|
||||
const urlsObj = JSON.parse(urlsString);
|
||||
|
||||
urlsObj[product] = '';
|
||||
|
||||
localStorage.setItem(urlStorageKey, JSON.stringify(urlsObj));
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -171,24 +166,24 @@ removeInfluxDBUrl = product => {
|
|||
const notificationStorageKey = storagePrefix + 'notifications';
|
||||
|
||||
// Default notifications
|
||||
var defaultNotificationsObj = {
|
||||
const defaultNotificationsObj = {
|
||||
messages: [],
|
||||
callouts: [],
|
||||
};
|
||||
|
||||
getNotifications = () => {
|
||||
function getNotifications() {
|
||||
// Initialize notifications data if it doesn't already exist
|
||||
if (localStorage.getItem(notificationStorageKey) === null) {
|
||||
initializeLocalStorage('notifications', JSON.stringify(defaultNotificationsObj));
|
||||
initializeStorageItem('notifications', JSON.stringify(defaultNotificationsObj));
|
||||
}
|
||||
|
||||
// Retrieve and parse the notifications data as JSON
|
||||
notificationString = localStorage.getItem(notificationStorageKey);
|
||||
notificationObj = JSON.parse(notificationString);
|
||||
const notificationString = localStorage.getItem(notificationStorageKey);
|
||||
const notificationObj = JSON.parse(notificationString);
|
||||
|
||||
// Return the notifications object
|
||||
return notificationObj;
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Checks if a notification is read. Provide the notification ID and one of the
|
||||
|
@ -200,12 +195,12 @@ getNotifications = () => {
|
|||
If the notification ID exists in the array assigned to the specified type, the
|
||||
notification has been read.
|
||||
*/
|
||||
notificationIsRead = (notificationID, notificationType) => {
|
||||
let notificationsObj = getNotifications();
|
||||
readNotifications = notificationsObj[`${notificationType}s`];
|
||||
function notificationIsRead(notificationID, notificationType) {
|
||||
const notificationsObj = getNotifications();
|
||||
const readNotifications = notificationsObj[`${notificationType}s`];
|
||||
|
||||
return readNotifications.includes(notificationID);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Sets a notification as read. Provide the notification ID and one of the
|
||||
|
@ -216,12 +211,28 @@ notificationIsRead = (notificationID, notificationType) => {
|
|||
|
||||
The notification ID is added to the array assigned to the specified type.
|
||||
*/
|
||||
setNotificationAsRead = (notificationID, notificationType) => {
|
||||
let notificationsObj = getNotifications();
|
||||
let readNotifications = notificationsObj[`${notificationType}s`];
|
||||
function setNotificationAsRead(notificationID, notificationType) {
|
||||
const notificationsObj = getNotifications();
|
||||
const readNotifications = notificationsObj[`${notificationType}s`];
|
||||
|
||||
readNotifications.push(notificationID);
|
||||
notificationsObj[notificationType + 's'] = readNotifications;
|
||||
|
||||
localStorage.setItem(notificationStorageKey, JSON.stringify(notificationsObj));
|
||||
}
|
||||
|
||||
// Export functions as a module and make the file backwards compatible for non-module environments until all remaining dependent scripts are ported to modules
|
||||
export {
|
||||
defaultUrls,
|
||||
initializeStorageItem,
|
||||
getPreference,
|
||||
setPreference,
|
||||
getPreferences,
|
||||
getInfluxDBUrls,
|
||||
getInfluxDBUrl,
|
||||
setInfluxDBUrls,
|
||||
removeInfluxDBUrl,
|
||||
getNotifications,
|
||||
notificationIsRead,
|
||||
setNotificationAsRead,
|
||||
};
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
// assets/js/main.js
|
||||
|
||||
// If you need to pass parameters from the calling Hugo page, you can import them here like so:
|
||||
// import * as pageParams from '@params';
|
||||
|
||||
/** Import modules that are not components.
|
||||
* TODO: Refactor these into single-purpose component modules.
|
||||
*/
|
||||
// import * as codeblocksPreferences from './api-libs.js';
|
||||
// import * as datetime from './datetime.js';
|
||||
// import * as featureCallouts from './feature-callouts.js';
|
||||
import * as apiLibs from './api-libs.js';
|
||||
import * as codeControls from './code-controls.js';
|
||||
import * as contentInteractions from './content-interactions.js';
|
||||
import { delay } from './helpers.js';
|
||||
import { InfluxDBUrl } from './influxdb-url.js';
|
||||
import * as localStorage from './local-storage.js';
|
||||
import * as modals from './modals.js';
|
||||
import * as notifications from './notifications.js';
|
||||
import * as pageContext from './page-context.js';
|
||||
import * as pageFeedback from './page-feedback.js';
|
||||
import * as tabbedContent from './tabbed-content.js';
|
||||
import * as v3Wayfinding from './v3-wayfinding.js';
|
||||
// import * as homeInteractions from './home-interactions.js';
|
||||
// import { getUrls, getReferrerHost, InfluxDBUrl } from './influxdb-url.js';
|
||||
// import * as keybindings from './keybindings.js';
|
||||
// import * as listFilters from './list-filters.js';
|
||||
// import { Modal } from './modal.js';
|
||||
// import { showNotifications } from './notifications.js';
|
||||
// import ReleaseTOC from './release-toc.js';
|
||||
// import * as scroll from './scroll.js';
|
||||
// import { TabbedContent } from './tabbed-content.js';
|
||||
|
||||
/** Import component modules
|
||||
* The component pattern organizes JavaScript, CSS, and HTML for a specific UI element or interaction:
|
||||
* - HTML: my-component.html
|
||||
* - CSS: my-component.css
|
||||
* - JavaScript: my-component.js
|
||||
* The JavaScript is ideally a single-purpose module that exports a single default function to initialize the component and handle any component interactions.
|
||||
*/
|
||||
import AskAITrigger from './ask-ai-trigger.js';
|
||||
import { CustomTimeTrigger } from './custom-timestamps.js';
|
||||
import { SearchButton } from './search-button.js';
|
||||
import { SidebarToggle } from './sidebar-toggle.js';
|
||||
import Theme from './theme.js';
|
||||
import ThemeSwitch from './theme-switch.js';
|
||||
// import CodeControls from './code-controls.js';
|
||||
// import ContentInteractions from './content-interactions.js';
|
||||
// import CustomTimestamps from './custom-timestamps.js';
|
||||
// import Diagram from './Diagram.js';
|
||||
// import FluxGroupKeysExample from './FluxGroupKeysExample.js';
|
||||
// import FluxInfluxDBVersionsModal from './flux-influxdb-versions.js';
|
||||
// import PageFeedback from './page-feedback.js';
|
||||
// import SearchInput from './SearchInput.js';
|
||||
// import Sidebar from './Sidebar.js';
|
||||
// import V3Wayfinding from './v3-wayfinding.js';
|
||||
// import VersionSelector from './VersionSelector.js';
|
||||
|
||||
// Expose libraries and components within a namespaced object (for backwards compatibility or testing)
|
||||
// Expose libraries and components within a namespaced object (for backwards compatibility or testing)
|
||||
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
if (typeof window.influxdatadocs === 'undefined') {
|
||||
window.influxdatadocs = {};
|
||||
}
|
||||
|
||||
// Expose modules to the global object for debugging, testing, and backwards compatibility for non-ES6 modules.
|
||||
window.influxdatadocs.delay = delay;
|
||||
window.influxdatadocs.localStorage = window.LocalStorageAPI = localStorage;
|
||||
window.influxdatadocs.pageContext = pageContext;
|
||||
window.influxdatadocs.toggleModal = modals.toggleModal;
|
||||
|
||||
// On content loaded, initialize (not-component-ready) UI interaction modules
|
||||
// To differentiate these from component-ready modules, these modules typically export an initialize function that wraps UI interactions and event listeners.
|
||||
modals.initialize();
|
||||
apiLibs.initialize();
|
||||
codeControls.initialize();
|
||||
contentInteractions.initialize();
|
||||
InfluxDBUrl();
|
||||
notifications.initialize();
|
||||
pageFeedback.initialize();
|
||||
tabbedContent.initialize();
|
||||
v3Wayfinding.initialize();
|
||||
|
||||
/** Initialize components
|
||||
Component Structure: Each component is structured as a jQuery anonymous function that listens for the document ready state.
|
||||
Initialization in main.js: Each component is called in main.js inside a jQuery document ready function to ensure they are initialized when the document is ready.
|
||||
Note: These components should *not* be called directly in the HTML.
|
||||
*/
|
||||
const components = document.querySelectorAll('[data-component]');
|
||||
components.forEach((component) => {
|
||||
const componentName = component.getAttribute('data-component');
|
||||
switch (componentName) {
|
||||
case 'ask-ai-trigger':
|
||||
AskAITrigger({ component });
|
||||
window.influxdatadocs[componentName] = AskAITrigger;
|
||||
break;
|
||||
case 'custom-time-trigger':
|
||||
CustomTimeTrigger({ component });
|
||||
window.influxdatadocs[componentName] = CustomTimeTrigger;
|
||||
break;
|
||||
case 'search-button':
|
||||
SearchButton({ component });
|
||||
window.influxdatadocs[componentName] = SearchButton;
|
||||
break;
|
||||
case 'sidebar-toggle':
|
||||
SidebarToggle({ component });
|
||||
window.influxdatadocs[componentName] = SidebarToggle;
|
||||
break;
|
||||
case 'theme':
|
||||
Theme({ component });
|
||||
window.influxdatadocs[componentName] = Theme;
|
||||
break;
|
||||
// CodeControls();
|
||||
// ContentInteractions();
|
||||
// CustomTimestamps();
|
||||
// Diagram();
|
||||
// FluxGroupKeysExample();
|
||||
// FluxInfluxDBVersionsModal();
|
||||
// InfluxDBUrl();
|
||||
// Modal();
|
||||
// PageFeedback();
|
||||
// ReleaseTOC();
|
||||
// SearchInput();
|
||||
// showNotifications();
|
||||
// Sidebar();
|
||||
// TabbedContent();
|
||||
// ThemeSwitch({});
|
||||
// V3Wayfinding();
|
||||
// VersionSelector();
|
||||
case 'theme-switch':
|
||||
ThemeSwitch({ component });
|
||||
window.influxdatadocs[componentName] = ThemeSwitch;
|
||||
break;
|
||||
default:
|
||||
console.warn(`Unknown component: ${componentName}`);
|
||||
}
|
||||
});
|
||||
});
|
|
@ -2,21 +2,19 @@
|
|||
/////////////////////// General modal window interactions //////////////////////
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Toggle the URL selector modal window
|
||||
function toggleModal(modalID="") {
|
||||
if ($(".modal").hasClass("open")) {
|
||||
$(".modal").fadeOut(200).removeClass("open");
|
||||
$(".modal-content").delay(400).hide(0);
|
||||
} else {
|
||||
$(".modal").fadeIn(200).addClass("open");
|
||||
$(`${modalID}.modal-content`).show();
|
||||
}
|
||||
}
|
||||
import $ from 'jquery';
|
||||
|
||||
function handleModalClick() {
|
||||
// Open modal window on click
|
||||
$('.modal-trigger').click(function (e) {
|
||||
e.preventDefault();
|
||||
toggleModal();
|
||||
});
|
||||
|
||||
// Close modal window on click
|
||||
$("#modal-close, .modal-overlay").click(function(e) {
|
||||
e.preventDefault()
|
||||
toggleModal()
|
||||
$('#modal-close, .modal-overlay').click(function (e) {
|
||||
e.preventDefault();
|
||||
toggleModal();
|
||||
|
||||
// Remove modal query param ('view') if it exists
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
|
@ -25,5 +23,22 @@ $("#modal-close, .modal-overlay").click(function(e) {
|
|||
if (queryParams.get('view') !== null) {
|
||||
queryParams.delete('view');
|
||||
window.history.replaceState({}, '', `${location.pathname}${anchor}`);
|
||||
};
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function toggleModal(modalID = '') {
|
||||
if ($('.modal').hasClass('open')) {
|
||||
$('.modal').fadeOut(200).removeClass('open');
|
||||
$('.modal-content').delay(400).hide(0);
|
||||
} else {
|
||||
$('.modal').fadeIn(200).addClass('open');
|
||||
$(`${modalID}.modal-content`).show();
|
||||
}
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
handleModalClick();
|
||||
}
|
||||
|
||||
export { initialize, toggleModal };
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
IDs in the messages array are considered read and no longer appear to the user.
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
// Get notification ID
|
||||
function notificationID(el) {
|
||||
return $(el).attr('id');
|
||||
|
@ -39,7 +41,10 @@ function showNotifications() {
|
|||
var exclude = $(this).data('exclude').split(',');
|
||||
var pageInScope = inScope(window.location.pathname, scope);
|
||||
var pageExcluded = excludePage(window.location.pathname, exclude);
|
||||
var notificationRead = notificationIsRead(notificationID(this), 'message');
|
||||
var notificationRead = window.LocalStorageAPI.notificationIsRead(
|
||||
notificationID(this),
|
||||
'message'
|
||||
);
|
||||
|
||||
if (pageInScope && !pageExcluded && !notificationRead) {
|
||||
$(this).show().animate({ right: 0, opacity: 1 }, 200, 'swing');
|
||||
|
@ -53,10 +58,14 @@ function hideNotification(el) {
|
|||
.closest('.notification')
|
||||
.animate({ height: 0, opacity: 0 }, 200, 'swing', function () {
|
||||
$(this).hide();
|
||||
setNotificationAsRead(notificationID(this), 'message');
|
||||
window.LocalStorageAPI.setNotificationAsRead(
|
||||
notificationID(this),
|
||||
'message'
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
// Show notifications on page load
|
||||
showNotifications();
|
||||
|
||||
|
@ -82,3 +91,10 @@ $(window).scroll(function () {
|
|||
: 10;
|
||||
$('#docs-notifications').css('top', notificationPosition);
|
||||
});
|
||||
}
|
||||
|
||||
export {
|
||||
initialize,
|
||||
showNotifications,
|
||||
hideNotification,
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/** This module retrieves browser context information and site data for the
|
||||
* current page, version, and product.
|
||||
*/
|
||||
import { products, influxdb_urls } from '@params';
|
||||
|
||||
function getCurrentProductData() {
|
||||
const path = window.location.pathname;
|
||||
const mappings = [
|
||||
{ pattern: /\/influxdb\/cloud\//, product: products.cloud, urls: influxdb_urls.influxdb_cloud },
|
||||
{ pattern: /\/influxdb3\/core/, product: products.influxdb3_core, urls: influxdb_urls.core },
|
||||
{ pattern: /\/influxdb3\/enterprise/, product: products.influxdb3_enterprise, urls: influxdb_urls.enterprise },
|
||||
{ pattern: /\/influxdb3\/cloud-serverless/, product: products.influxdb3_cloud_serverless, urls: influxdb_urls.cloud },
|
||||
{ pattern: /\/influxdb3\/cloud-dedicated/, product: products.influxdb3_cloud_dedicated, urls: influxdb_urls.dedicated },
|
||||
{ pattern: /\/influxdb3\/clustered/, product: products.influxdb3_clustered, urls: influxdb_urls.clustered },
|
||||
{ pattern: /\/enterprise_v1\//, product: products.enterprise_influxdb, urls: influxdb_urls.oss },
|
||||
{ pattern: /\/influxdb.*v1\//, product: products.influxdb, urls: influxdb_urls.oss },
|
||||
{ pattern: /\/influxdb.*v2\//, product: products.influxdb, urls: influxdb_urls.oss },
|
||||
{ pattern: /\/kapacitor\//, product: products.kapacitor, urls: influxdb_urls.oss },
|
||||
{ pattern: /\/telegraf\//, product: products.telegraf, urls: influxdb_urls.oss },
|
||||
{ pattern: /\/chronograf\//, product: products.chronograf, urls: influxdb_urls.oss },
|
||||
];
|
||||
|
||||
for (const { pattern, product, urls } of mappings) {
|
||||
if (pattern.test(path)) {
|
||||
return { product, urls };
|
||||
}
|
||||
}
|
||||
|
||||
return 'other';
|
||||
}
|
||||
|
||||
// Return the page context (cloud, serverless, oss/enterprise, dedicated, clustered, other)
|
||||
function getContext() {
|
||||
if (/\/influxdb\/cloud\//.test(window.location.pathname)) {
|
||||
return 'cloud';
|
||||
} else if (/\/influxdb3\/core/.test(window.location.pathname)) {
|
||||
return 'core';
|
||||
} else if (/\/influxdb3\/enterprise/.test(window.location.pathname)) {
|
||||
return 'enterprise';
|
||||
} else if (/\/influxdb3\/cloud-serverless/.test(window.location.pathname)) {
|
||||
return 'serverless';
|
||||
} else if (/\/influxdb3\/cloud-dedicated/.test(window.location.pathname)) {
|
||||
return 'dedicated';
|
||||
} else if (/\/influxdb3\/clustered/.test(window.location.pathname)) {
|
||||
return 'clustered';
|
||||
} else if (
|
||||
/\/(enterprise_|influxdb).*\/v[1-2]\//.test(window.location.pathname)
|
||||
) {
|
||||
return 'oss/enterprise';
|
||||
} else {
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
||||
// Store the host value for the current page
|
||||
const currentPageHost = window.location.href.match(/^(?:[^/]*\/){2}[^/]+/g)[0];
|
||||
|
||||
function getReferrerHost() {
|
||||
// Extract the protocol and hostname of referrer
|
||||
const referrerMatch = document.referrer.match(/^(?:[^/]*\/){2}[^/]+/g);
|
||||
return referrerMatch ? referrerMatch[0] : '';
|
||||
}
|
||||
|
||||
const context = getContext(),
|
||||
host = currentPageHost,
|
||||
hostname = location.hostname,
|
||||
path = location.pathname,
|
||||
pathArr = location.pathname.split('/').slice(1, -1),
|
||||
product = pathArr[0],
|
||||
productData = getCurrentProductData(),
|
||||
protocol = location.protocol,
|
||||
referrer = document.referrer === '' ? 'direct' : document.referrer,
|
||||
referrerHost = getReferrerHost(),
|
||||
// TODO: Verify this still does what we want since the addition of InfluxDB 3 naming and the Core and Enterprise versions.
|
||||
version = (/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud') ? pathArr[1].replace(/^v/, '') : "n/a")
|
||||
|
||||
export {
|
||||
context,
|
||||
host,
|
||||
hostname,
|
||||
path,
|
||||
product,
|
||||
productData,
|
||||
protocol,
|
||||
referrer,
|
||||
referrerHost,
|
||||
version,
|
||||
};
|
|
@ -3,52 +3,58 @@
|
|||
* buttons and modal.
|
||||
*/
|
||||
|
||||
// Collect data from the page path
|
||||
const pathArr = location.pathname.split('/').slice(1, -1)
|
||||
const pageData = {
|
||||
host: location.hostname,
|
||||
path: location.pathname,
|
||||
product: pathArr[0],
|
||||
version: (/^v\d/.test(pathArr[1]) || pathArr[1]?.includes('cloud') ? pathArr[1].replace(/^v/, '') : "n/a"),
|
||||
}
|
||||
import $ from 'jquery';
|
||||
import { hostname, path, product, protocol, version } from './page-context.js';
|
||||
import { toggleModal } from './modals.js';
|
||||
|
||||
// Hijack form submission and send feedback data to be stored.
|
||||
// Called by onSubmit in each feedback form.
|
||||
function submitFeedbackForm(formID) {
|
||||
|
||||
// Collect form data, structure as an object, and remove fname honeypot
|
||||
const formData = new FormData(document.forms[formID]);
|
||||
const formDataObj = Object.fromEntries(formData.entries());
|
||||
const {fname, ...feedbackData} = formDataObj;
|
||||
const { ...feedbackData } = formDataObj;
|
||||
|
||||
// Build lp fields from form data
|
||||
let fields = "";
|
||||
let fields = '';
|
||||
for (let key in feedbackData) {
|
||||
// Strip out newlines and escape double quotes if the field key is "feedback"
|
||||
if (key == "feedback-text") {
|
||||
fields += key + '="' + feedbackData[key].replace(/(\r\n|\n+|\r+)/gm, " ").replace(/(\")/gm, '\\"') + '",';
|
||||
if (key == 'feedback-text') {
|
||||
fields +=
|
||||
key +
|
||||
'="' +
|
||||
feedbackData[key]
|
||||
.replace(/(\r\n|\n+|\r+)/gm, ' ')
|
||||
.replace(/(\")/gm, '\\"') +
|
||||
'",';
|
||||
} else {
|
||||
fields += key + "=" + feedbackData[key] + ",";
|
||||
fields += key + '=' + feedbackData[key] + ',';
|
||||
}
|
||||
}
|
||||
fields = fields.substring(0, fields.length - 1);
|
||||
|
||||
// Build lp using page data and the fields string
|
||||
const lp = `feedback,host=${pageData.host},path=${pageData.path},product=${pageData.product},version=${pageData.version} ${fields}`
|
||||
const lp = `feedback,host=${hostname},path=${path},product=${product},version=${version} ${fields}`;
|
||||
|
||||
// Use a honeypot form field to detect a bot
|
||||
// If the value of the honeypot field is greater than 0, the submitter is a bot
|
||||
function isBot() {
|
||||
const honeypot = formData.get('fname');
|
||||
return (honeypot.length > 0)
|
||||
return honeypot.length > 0;
|
||||
}
|
||||
|
||||
// If the submitter is not a bot, send the feedback data
|
||||
if (!isBot()) {
|
||||
xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', 'https://j32dswat7l.execute-api.us-east-1.amazonaws.com/prod');
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(
|
||||
'POST',
|
||||
'https://j32dswat7l.execute-api.us-east-1.amazonaws.com/prod'
|
||||
);
|
||||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader('Access-Control-Allow-Origin', `${location.protocol}//${location.host}`);
|
||||
xhr.setRequestHeader(
|
||||
'Access-Control-Allow-Origin',
|
||||
`${protocol}//${location.host}`
|
||||
);
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
xhr.send(lp);
|
||||
|
@ -70,28 +76,43 @@ function submitLifeCycle() {
|
|||
// Called by onclick in the page-feedback modal submit button.
|
||||
function submitLifeCycleAndClose() {
|
||||
submitFeedbackForm('pagefeedbacktext');
|
||||
$('.modal #page-feedback .loader-wrapper').css('display', 'flex').hide().fadeIn(200);
|
||||
$('.modal #page-feedback #thank-you').css('display', 'flex').hide().delay(800).fadeIn(200);
|
||||
$('.modal #page-feedback textarea').css('box-shadow', 'none')
|
||||
$('.modal #page-feedback .loader-wrapper')
|
||||
.css('display', 'flex')
|
||||
.hide()
|
||||
.fadeIn(200);
|
||||
$('.modal #page-feedback #thank-you')
|
||||
.css('display', 'flex')
|
||||
.hide()
|
||||
.delay(800)
|
||||
.fadeIn(200);
|
||||
$('.modal #page-feedback textarea').css('box-shadow', 'none');
|
||||
$('.modal #page-feedback .loader-wrapper').delay(1000).hide(0);
|
||||
setTimeout(function() {toggleModal()}, 1800);
|
||||
setTimeout(function () {
|
||||
toggleModal();
|
||||
}, 1800);
|
||||
return false;
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
//////////////////////////////// Event triggers ////////////////////////////////
|
||||
|
||||
// Submit page feedback (yes/no) on radio select and trigger life cycle
|
||||
$('#pagefeedback input[type=radio]').change(function () {
|
||||
$('form#pagefeedback').submit();
|
||||
submitLifeCycle()
|
||||
})
|
||||
submitLifeCycle();
|
||||
});
|
||||
|
||||
// Toggle the feedback modal when user selects that the page is not helpful
|
||||
$('#pagefeedback #not-helpful input[type=radio]').click(function () {
|
||||
setTimeout(function() {toggleModal('#page-feedback')}, 400);
|
||||
})
|
||||
setTimeout(function () {
|
||||
toggleModal('#page-feedback');
|
||||
}, 400);
|
||||
});
|
||||
|
||||
// Toggle the feedback modal when user selects that the page is not helpful
|
||||
$('.modal #no-thanks').click(function () {
|
||||
toggleModal();
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
export { initialize };
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { toggleSidebar } from './sidebar-toggle.js';
|
||||
|
||||
export function SearchButton({ component }) {
|
||||
component.querySelector('[data-action="toggle"]')
|
||||
.addEventListener('click', () => {
|
||||
toggleSidebar('sidebar-open');
|
||||
document.getElementById('algolia-search-input').focus();
|
||||
return false;
|
||||
});
|
||||
}
|
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
Copied and pasted this script for CSS swaps w/ cookies from
|
||||
Portions of this code come from CSS swaps w/ cookies from
|
||||
http://www.thesitewizard.com/javascripts/change-style-sheets.shtml
|
||||
*/
|
||||
|
||||
import * as localStorage from './local-storage.js';
|
||||
|
||||
// *** TO BE CUSTOMISED ***
|
||||
var sidebar_state_preference_name = 'sidebar_state';
|
||||
var sidebar_state_duration = 30;
|
||||
|
@ -30,7 +32,7 @@ function toggleSidebar (toggle_state) {
|
|||
link_tag[i].disabled = false;
|
||||
}
|
||||
}
|
||||
setPreference(
|
||||
localStorage.setPreference(
|
||||
sidebar_state_preference_name,
|
||||
toggle_state.replace(/sidebar-/, '')
|
||||
);
|
||||
|
@ -38,8 +40,22 @@ function toggleSidebar (toggle_state) {
|
|||
}
|
||||
|
||||
function setSidebarState() {
|
||||
var toggle_state = `sidebar-${getPreference(sidebar_state_preference_name)}`;
|
||||
var toggle_state = `sidebar-${localStorage.getPreference(sidebar_state_preference_name)}`;
|
||||
if (toggle_state !== undefined) {
|
||||
toggleSidebar(toggle_state);
|
||||
}
|
||||
}
|
||||
|
||||
function SidebarToggle({ component }) {
|
||||
const current_state = component.getAttribute('data-state');
|
||||
component
|
||||
.querySelector('[data-action="toggle"]')
|
||||
.addEventListener('click', () => {
|
||||
toggleSidebar(`sidebar-${current_state}`);
|
||||
return false;
|
||||
});
|
||||
|
||||
setSidebarState();
|
||||
}
|
||||
|
||||
export { setSidebarState, toggleSidebar, SidebarToggle };
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* smoothscroll when clicked. The whitelist is defined in content-interactions.js.
|
||||
**/
|
||||
|
||||
import $ from 'jquery';
|
||||
import { scrollToAnchor } from './content-interactions.js';
|
||||
|
||||
function tabbedContent(container, tab, content) {
|
||||
// Add the active class to the first tab in each tab group,
|
||||
// in case it wasn't already set in the markup.
|
||||
|
@ -30,9 +33,6 @@ function tabbedContent (container, tab, content) {
|
|||
});
|
||||
}
|
||||
|
||||
tabbedContent('.code-tabs-wrapper', '.code-tabs p a', '.code-tab-content');
|
||||
tabbedContent('.tabs-wrapper', '.tabs p a', '.tab-content');
|
||||
|
||||
function getTabQueryParam() {
|
||||
const queryParams = new URLSearchParams(window.location.search);
|
||||
return $('<textarea />').html(queryParams.get('t')).text();
|
||||
|
@ -74,6 +74,10 @@ function activateTabs (selector, tab) {
|
|||
}
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
tabbedContent('.code-tabs-wrapper', '.code-tabs p a', '.code-tab-content');
|
||||
tabbedContent('.tabs-wrapper', '.tabs p a', '.tab-content');
|
||||
|
||||
$(`.tabs p a, .code-tabs p a`).click(function () {
|
||||
var queryParams = new URLSearchParams(window.location.search);
|
||||
var anchor = window.location.hash;
|
||||
|
@ -95,8 +99,11 @@ $(`.tabs p a, .code-tabs p a`).click(function () {
|
|||
|
||||
//////////////////// Activate Tab with Cookie or Query Param ///////////////////
|
||||
|
||||
tab = getTabQueryParam();
|
||||
const tab = getTabQueryParam();
|
||||
['.tabs', '.code-tabs'].forEach(
|
||||
selector => activateTabs(selector, tab),
|
||||
(selector) => activateTabs(selector, tab),
|
||||
updateBtnURLs(tab)
|
||||
);
|
||||
}
|
||||
|
||||
export { activateTabs, initialize, tabbedContent, updateBtnURLs };
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import Theme from './theme.js';
|
||||
|
||||
export default function ThemeSwitch({ component }) {
|
||||
if ( component == undefined) {
|
||||
component = document;
|
||||
}
|
||||
component.querySelectorAll(`.theme-switch-light`).forEach((button) => {
|
||||
button.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
Theme({ style: 'light-theme' });
|
||||
});
|
||||
});
|
||||
|
||||
component.querySelectorAll(`.theme-switch-dark`).forEach((button) => {
|
||||
button.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
Theme({ style: 'dark-theme' });
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
import { getPreference, setPreference } from './local-storage.js';
|
||||
|
||||
const PROPS = {
|
||||
style_preference_name: 'theme',
|
||||
style_cookie_duration: 30, // number of days
|
||||
style_domain: 'docs.influxdata.com',
|
||||
};
|
||||
|
||||
function getPreferredTheme () {
|
||||
return `${getPreference(PROPS.style_preference_name)}-theme`;
|
||||
}
|
||||
|
||||
function switchStyle({ styles_element, css_title }) {
|
||||
// Disable all other theme stylesheets
|
||||
styles_element.querySelectorAll('link[rel*="stylesheet"][title*="theme"]')
|
||||
.forEach(function (link) {
|
||||
link.disabled = true;
|
||||
});
|
||||
|
||||
// Enable the stylesheet with the specified title
|
||||
const link = styles_element.querySelector(`link[rel*="stylesheet"][title="${css_title}"]`);
|
||||
link && (link.disabled = false);
|
||||
|
||||
setPreference(PROPS.style_preference_name, css_title.replace(/-theme/, ''));
|
||||
}
|
||||
|
||||
function setVisibility(component) {
|
||||
component.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
export default function Theme({ component, style }) {
|
||||
if (style == undefined) {
|
||||
style = getPreferredTheme();
|
||||
}
|
||||
style && switchStyle({ styles_element: document, css_title: style });
|
||||
|
||||
// Check for the data attribute and set visibility if needed
|
||||
if (component.dataset?.themeCallback === 'setVisibility') {
|
||||
setVisibility(component);
|
||||
}
|
||||
|
||||
}
|
|
@ -1,40 +1,30 @@
|
|||
// Store the host value for the current page
|
||||
const currentPageHost = window.location.href.match(/^(?:[^\/]*\/){2}[^\/]+/g)[0];
|
||||
|
||||
// Define v3-wayfinding elements
|
||||
var wayfindingModal = document.getElementById('v3-wayfinding-modal');
|
||||
var wayfindingClose = document.getElementById('v3-wayfinding-close');
|
||||
var wayfindingStay = document.getElementById('v3-wayfinding-stay');
|
||||
var wayfindingSwitch = document.getElementById('v3-wayfinding-switch');
|
||||
var wayfindingOptOut = document.getElementById('v3-wayfinding-opt-out');
|
||||
var wayfindingOptOutInput = document.getElementById(
|
||||
'v3-wayfinding-opt-out-input'
|
||||
);
|
||||
var wayfindingFindOutToggle = document.getElementById('find-out-toggle');
|
||||
var wayfindingFindOutInstructions = document.getElementById(
|
||||
'find-out-instructions'
|
||||
);
|
||||
import { CLOUD_URLS } from './influxdb-url.js';
|
||||
import * as localStorage from './local-storage.js';
|
||||
import { context, host, hostname, path, protocol, referrer, referrerHost } from './page-context.js';
|
||||
|
||||
/**
|
||||
* Builds a referrer whitelist array that includes the current page host and all
|
||||
* values from the cloudUrls array defined in layouts/partials/footer/javascript.html
|
||||
*/
|
||||
var referrerWhitelist = cloudUrls.concat(currentPageHost);
|
||||
const cloudUrls = CLOUD_URLS || [];
|
||||
var referrerWhitelist = cloudUrls.concat(host);
|
||||
|
||||
// v3-wayfinding preference cookie name
|
||||
var wayfindingPrefCookie = 'v3_wayfinding_show';
|
||||
|
||||
// Toggle the v3-wayfinding modal
|
||||
function toggleWayfinding() {
|
||||
// Define v3-wayfinding elements
|
||||
var wayfindingModal = document.getElementById('v3-wayfinding-modal');
|
||||
wayfindingModal.classList.toggle('open');
|
||||
}
|
||||
|
||||
// Toggle wayfinding modal preference cookie
|
||||
function toggleWayfindingPreference() {
|
||||
if (getPreference(wayfindingPrefCookie) === true) {
|
||||
setPreference(wayfindingPrefCookie, false);
|
||||
if (localStorage.getPreference(wayfindingPrefCookie) === true) {
|
||||
localStorage.setPreference(wayfindingPrefCookie, false);
|
||||
} else {
|
||||
setPreference(wayfindingPrefCookie, true);
|
||||
localStorage.setPreference(wayfindingPrefCookie, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,13 +46,11 @@ function slideUp (elem) {
|
|||
*/
|
||||
function shouldOpenWayfinding() {
|
||||
// Extract the protocol and hostname of referrer
|
||||
const referrerMatch = document.referrer.match(/^(?:[^\/]*\/){2}[^\/]+/g);
|
||||
const referrerHost = referrerMatch ? referrerMatch[0] : '';
|
||||
var isExternalReferrer = !referrerWhitelist.includes(referrerHost);
|
||||
var wayfindingOptedOut = getPreference(wayfindingPrefCookie);
|
||||
const isExternalReferrer = !referrerWhitelist.includes(referrerHost);
|
||||
const preferToShow = localStorage.getPreference(wayfindingPrefCookie);
|
||||
|
||||
// Only return true if all conditions are true
|
||||
return isExternalReferrer && wayfindingOptedOut;
|
||||
return isExternalReferrer && preferToShow;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,25 +58,24 @@ function shouldOpenWayfinding () {
|
|||
* wayfinding checkbox input.
|
||||
*/
|
||||
function setWayfindingInputState() {
|
||||
var currentPreference = getPreference(wayfindingPrefCookie);
|
||||
const preferToShow = localStorage.getPreference(wayfindingPrefCookie);
|
||||
const wayfindingOptOutInput = document.getElementById(
|
||||
'v3-wayfinding-opt-out-input'
|
||||
);
|
||||
|
||||
if (currentPreference === false) {
|
||||
if (preferToShow === false) {
|
||||
wayfindingOptOutInput.checked = true;
|
||||
}
|
||||
}
|
||||
|
||||
function submitWayfindingData(engine, action) {
|
||||
const pageData = {
|
||||
host: location.hostname,
|
||||
path: location.pathname,
|
||||
referrer: document.referrer === '' ? 'direct' : document.referrer,
|
||||
};
|
||||
|
||||
|
||||
// Build lp using page data and engine data
|
||||
const lp = `ioxwayfinding,host=${pageData.host},path=${pageData.path},referrer=${pageData.referrer},engine=${engine} action="${action}"`;
|
||||
const lp = `ioxwayfinding,host=${hostname},path=${path},referrer=${referrer},engine=${engine} action="${action}"`;
|
||||
|
||||
// Send the wayfinding data
|
||||
xhr = new XMLHttpRequest();
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open(
|
||||
'POST',
|
||||
'https://j32dswat7l.execute-api.us-east-1.amazonaws.com/prod/wayfinding'
|
||||
|
@ -96,7 +83,7 @@ function submitWayfindingData (engine, action) {
|
|||
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
|
||||
xhr.setRequestHeader(
|
||||
'Access-Control-Allow-Origin',
|
||||
`${location.protocol}//${location.host}`
|
||||
`${protocol}//${host}`
|
||||
);
|
||||
xhr.setRequestHeader('Content-Type', 'text/plain; charset=utf-8');
|
||||
xhr.setRequestHeader('Accept', 'application/json');
|
||||
|
@ -105,7 +92,14 @@ function submitWayfindingData (engine, action) {
|
|||
return false;
|
||||
}
|
||||
|
||||
function initialize() {
|
||||
const wayfindingVersions = ['serverless', 'cloud'];
|
||||
if (!wayfindingVersions.includes(context)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// When the user clicks on the stay button, close modal, submit data, and stay on the page.
|
||||
var wayfindingStay = document.getElementById('v3-wayfinding-stay');
|
||||
wayfindingStay.onclick = function (event) {
|
||||
var engine = wayfindingStay.dataset.engine;
|
||||
var action = 'stay';
|
||||
|
@ -116,7 +110,8 @@ wayfindingStay.onclick = function (event) {
|
|||
};
|
||||
|
||||
// When the user clicks on the switch button, submit data and follow link.
|
||||
wayfindingSwitch.onclick = function (event) {
|
||||
var wayfindingSwitch = document.getElementById('v3-wayfinding-switch');
|
||||
wayfindingSwitch.onclick = function () {
|
||||
var engine = wayfindingSwitch.dataset.engine;
|
||||
var action = 'switch';
|
||||
|
||||
|
@ -124,17 +119,23 @@ wayfindingSwitch.onclick = function (event) {
|
|||
};
|
||||
|
||||
// When the user clicks on the "X" wayfinding close element, close the modal
|
||||
wayfindingClose.onclick = function (event) {
|
||||
var wayfindingClose = document.getElementById('v3-wayfinding-close');
|
||||
wayfindingClose.onclick = function () {
|
||||
toggleWayfinding();
|
||||
};
|
||||
|
||||
wayfindingOptOut.onclick = function (event) {
|
||||
var wayfindingOptOut = document.getElementById('v3-wayfinding-opt-out');
|
||||
wayfindingOptOut.onclick = function () {
|
||||
toggleWayfindingPreference();
|
||||
};
|
||||
|
||||
// Toggle instructions for finding out which storage engine you're using
|
||||
var wayfindingFindOutToggle = document.getElementById('find-out-toggle');
|
||||
wayfindingFindOutToggle.onclick = function (event) {
|
||||
event.preventDefault();
|
||||
var wayfindingFindOutInstructions = document.getElementById(
|
||||
'find-out-instructions'
|
||||
);
|
||||
if (wayfindingFindOutInstructions.classList.contains('open')) {
|
||||
slideUp(wayfindingFindOutInstructions);
|
||||
wayfindingFindOutInstructions.classList.remove('open');
|
||||
|
@ -144,14 +145,16 @@ wayfindingFindOutToggle.onclick = function (event) {
|
|||
}
|
||||
};
|
||||
|
||||
// Set the state of the show wayfinding input checkbox
|
||||
setWayfindingInputState();
|
||||
|
||||
/**
|
||||
* Check to see if the referrer is in the referrer whitelist, otherwise trigger
|
||||
* the v3-wayfinding modal.
|
||||
* This reuses the referrerHost variable defined in assets/js/influxdb-url.js
|
||||
*/
|
||||
if (shouldOpenWayfinding()) {
|
||||
toggleWayfinding();
|
||||
}
|
||||
}
|
||||
|
||||
// Set the state of the show wayfinding input checkbox
|
||||
setWayfindingInputState();
|
||||
export { initialize };
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"*": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
.custom-time-trigger {
|
||||
display: block;
|
||||
position: fixed;
|
||||
bottom: 1.5rem;
|
||||
right: 1.5rem;
|
||||
border-radius: $radius;
|
||||
box-shadow: 2px 2px 6px $sidebar-search-shadow;
|
||||
z-index: 1;
|
||||
color: $g20-white;
|
||||
background: $g5-pepper;
|
||||
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: $radius;
|
||||
@include gradient($article-btn-gradient, 320deg);
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
&:hover:before {opacity: 1}
|
||||
|
||||
a {
|
||||
display: block;
|
||||
padding: 1rem;
|
||||
line-height: 1rem;
|
||||
|
||||
&:before {
|
||||
content: "Select custom date for sample data";
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
font-size: .9rem;
|
||||
font-style: italic;
|
||||
white-space: nowrap;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
transition: width .2s, opacity .2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
|
||||
&:before{
|
||||
width: 240px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
///////////////////////////////// MEDIA QUERIES ////////////////////////////////
|
||||
|
||||
@include media(small) {
|
||||
.custom-time-trigger {
|
||||
bottom: .75rem;
|
||||
right: .75rem;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
.footer-widgets {
|
||||
position: fixed;
|
||||
bottom: 1rem;
|
||||
right: 1rem;
|
||||
width: auto; /* Ensure the parent takes up the full width */
|
||||
height: auto; /* Adjust height to fit content */
|
||||
display: flex;
|
||||
flex-direction: column; /* Arrange items in a single column */
|
||||
justify-content: flex-end; /* Align items to the start of the column */
|
||||
align-items: flex-end; /* Align items to the left */
|
||||
z-index: 100; /* Ensure the triggers are above other content except modals*/
|
||||
|
||||
.widget {
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
border-radius: $radius * 3;
|
||||
position: relative;
|
||||
box-shadow: 2px 2px 6px $sidebar-search-shadow;
|
||||
color: $g20-white;
|
||||
|
||||
&:not(:last-child) {margin-bottom: 5px;}
|
||||
|
||||
/* The before pseudo element contains the "tool tip" */
|
||||
&:before {
|
||||
content: attr(data-tooltip);
|
||||
padding: .25rem .5rem;
|
||||
display: flex;
|
||||
position: absolute;
|
||||
top: 11px;
|
||||
right: 60px;
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
font-size: .9rem;
|
||||
font-weight: bold;
|
||||
border-radius: $radius * 3;
|
||||
@include gradient($grad-burningDusk, 270deg);
|
||||
pointer-events: none;
|
||||
z-index: -1;
|
||||
opacity: 0;
|
||||
transform: translateX(15px);
|
||||
transition: opacity .2s, transform .2s;
|
||||
}
|
||||
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 56px;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
opacity: 0;
|
||||
transform: translateX(15px);
|
||||
transition: opacity .2s, transform .2s;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
&:before, &:after {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
&.magenta {
|
||||
@include gradient($grad-burningDusk, 90deg);
|
||||
&:before {@include gradient($grad-burningDusk, 270deg);}
|
||||
&:after {
|
||||
border-style: solid;
|
||||
border-width: 10px 0 10px 5px;
|
||||
border-color: transparent transparent transparent $br-new-magenta;}
|
||||
}
|
||||
&.blue {
|
||||
color: rgba($br-dark-blue, .7);
|
||||
@include gradient($grad-tealDream, 270deg);
|
||||
&:before {@include gradient($grad-tealDream, 90deg);}
|
||||
&:after {
|
||||
border-style: solid;
|
||||
border-width: 10px 0 10px 5px;
|
||||
border-color: transparent transparent transparent $br-teal;}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////// CUSTOM TIME TRIGGER /////////////////////////////
|
||||
|
||||
.custom-time-trigger {
|
||||
a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 1.2rem;
|
||||
color: rgba($br-dark-blue, .8);
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////// ASK AI WIDGET ////////////////////////////////
|
||||
|
||||
.ask-ai-trigger {
|
||||
|
||||
.ask-ai-open {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
font-size: .8rem;
|
||||
font-weight: bold;
|
||||
|
||||
.icon-influx-logo {
|
||||
margin-bottom: .15rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////// ANIMATIONS //////////////////////////////////
|
||||
|
||||
@keyframes fadeInAnimation {
|
||||
0% {opacity: 0;}
|
||||
100% {opacity: 1;}
|
||||
}
|
||||
|
||||
///////////////////////////////// MEDIA QUERIES ////////////////////////////////
|
||||
|
||||
@include media(small) {
|
||||
.footer-widgets {
|
||||
bottom: .75rem;
|
||||
right: .75rem;
|
||||
}
|
||||
}
|
|
@ -187,7 +187,7 @@
|
|||
margin-left: .5rem;
|
||||
}
|
||||
|
||||
.theme-switcher, #search-btn, .url-trigger {
|
||||
.theme-switch, #search-btn, .url-trigger {
|
||||
display: inline-block;
|
||||
padding: 0;
|
||||
line-height: 0;
|
||||
|
@ -206,8 +206,8 @@
|
|||
color: rgba($topnav-link, 1);
|
||||
cursor: pointer;
|
||||
}
|
||||
&#theme-switch-dark { display: $theme-switch-dark; font-size: 1.15rem; }
|
||||
&#theme-switch-light { display: $theme-switch-light; font-size: 1.3rem; }
|
||||
&.theme-switch-dark { display: $theme-switch-dark; font-size: 1.15rem; }
|
||||
&.theme-switch-light { display: $theme-switch-light; font-size: 1.3rem; }
|
||||
}
|
||||
|
||||
.url-trigger {
|
||||
|
|
|
@ -24,12 +24,12 @@
|
|||
"layouts/algolia-search-overrides",
|
||||
"layouts/landing",
|
||||
"layouts/error-page",
|
||||
"layouts/footer-widgets",
|
||||
"layouts/modals",
|
||||
"layouts/loading-spinner",
|
||||
"layouts/feature-callouts",
|
||||
"layouts/v1-overrides",
|
||||
"layouts/notifications",
|
||||
"layouts/custom-time-trigger",
|
||||
"layouts/code-controls",
|
||||
"layouts/v3-wayfinding";
|
||||
|
||||
|
|
|
@ -268,7 +268,16 @@ With `accept_partial=true`:
|
|||
< date: Wed, 15 Jan 2025 19:35:36 GMT
|
||||
<
|
||||
* Connection #0 to host localhost left intact
|
||||
{"error":"partial write of line protocol occurred","data":[{"original_line":"dquote> home,room=Sunroom temp=hi","line_number":2,"error_message":"No fields were provided"}]}%
|
||||
{
|
||||
"error": "partial write of line protocol occurred",
|
||||
"data": [
|
||||
{
|
||||
"original_line": "dquote> home,room=Sunroom temp=hi",
|
||||
"line_number": 2,
|
||||
"error_message": "No fields were provided"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Line `1` is written and queryable.
|
||||
|
@ -278,16 +287,28 @@ The response is an HTTP error (`400`) status, and the response body contains `pa
|
|||
|
||||
With `accept_partial=false`:
|
||||
|
||||
```
|
||||
> curl -v -XPOST "localhost:8181/api/v3/write_lp?db=sensors&precision=auto&accept_partial=false" \
|
||||
```bash
|
||||
curl -v "http://{{< influxdb/host >}}/api/v3/write_lp?db=sensors&precision=auto&accept_partial=false" \
|
||||
--data-raw "home,room=Sunroom temp=96
|
||||
dquote> home,room=Sunroom temp=hi"
|
||||
home,room=Sunroom temp=hi"
|
||||
```
|
||||
|
||||
The response is the following:
|
||||
|
||||
```
|
||||
< HTTP/1.1 400 Bad Request
|
||||
< transfer-encoding: chunked
|
||||
< date: Wed, 15 Jan 2025 19:28:27 GMT
|
||||
<
|
||||
* Connection #0 to host localhost left intact
|
||||
{"error":"parsing failed for write_lp endpoint","data":{"original_line":"dquote> home,room=Sunroom temp=hi","line_number":2,"error_message":"No fields were provided"}}%
|
||||
{
|
||||
"error": "parsing failed for write_lp endpoint",
|
||||
"data": {
|
||||
"original_line": "home,room=Sunroom temp=hi",
|
||||
"line_number": 2,
|
||||
"error_message": "No fields were provided"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Neither line is written to the database.
|
||||
|
@ -401,7 +422,7 @@ Use the `format` parameter to specify the response format: `pretty`, `jsonl`, `p
|
|||
The following example sends an HTTP `GET` request with a URL-encoded SQL query:
|
||||
|
||||
```bash
|
||||
curl -v "http://127.0.0.1:8181/api/v3/query_sql?db=servers&q=select+*+from+cpu+limit+5"
|
||||
curl -v "http://{{< influxdb/host >}}/api/v3/query_sql?db=servers&q=select+*+from+cpu+limit+5"
|
||||
```
|
||||
|
||||
##### Example: Query passing JSON parameters
|
||||
|
@ -409,7 +430,7 @@ curl -v "http://127.0.0.1:8181/api/v3/query_sql?db=servers&q=select+*+from+cpu+l
|
|||
The following example sends an HTTP `POST` request with parameters in a JSON payload:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8181/api/v3/query_sql \
|
||||
curl http://{{< influxdb/host >}}/api/v3/query_sql \
|
||||
--data '{"db": "server", "q": "select * from cpu limit 5"}'
|
||||
```
|
||||
|
||||
|
@ -430,7 +451,7 @@ From here, you can connect to your database with the client library using just t
|
|||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
client = InfluxDBClient3(
|
||||
host='http://127.0.0.1:8181',
|
||||
host='http://{{< influxdb/host >}}',
|
||||
database='servers'
|
||||
)
|
||||
```
|
||||
|
@ -442,7 +463,7 @@ use PyArrow to explore the schema and process results:
|
|||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
client = InfluxDBClient3(
|
||||
host='http://127.0.0.1:8181',
|
||||
host='http://{{< influxdb/host >}}',
|
||||
|
||||
database='servers'
|
||||
)
|
||||
|
|
|
@ -263,7 +263,16 @@ With `accept_partial=true`:
|
|||
< date: Wed, 15 Jan 2025 19:35:36 GMT
|
||||
<
|
||||
* Connection #0 to host localhost left intact
|
||||
{"error":"partial write of line protocol occurred","data":[{"original_line":"dquote> home,room=Sunroom temp=hi","line_number":2,"error_message":"No fields were provided"}]}%
|
||||
{
|
||||
"error": "partial write of line protocol occurred",
|
||||
"data": [
|
||||
{
|
||||
"original_line": "dquote> home,room=Sunroom temp=hi",
|
||||
"line_number": 2,
|
||||
"error_message": "No fields were provided"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Line `1` is written and queryable.
|
||||
|
@ -273,16 +282,28 @@ The response is an HTTP error (`400`) status, and the response body contains `pa
|
|||
|
||||
With `accept_partial=false`:
|
||||
|
||||
```
|
||||
> curl -v -XPOST "localhost:8181/api/v3/write_lp?db=sensors&precision=auto&accept_partial=false" \
|
||||
```bash
|
||||
curl -v "http://{{< influxdb/host >}}/api/v3/write_lp?db=sensors&precision=auto&accept_partial=false" \
|
||||
--data-raw "home,room=Sunroom temp=96
|
||||
dquote> home,room=Sunroom temp=hi"
|
||||
home,room=Sunroom temp=hi"
|
||||
```
|
||||
|
||||
The response is the following:
|
||||
|
||||
```
|
||||
< HTTP/1.1 400 Bad Request
|
||||
< transfer-encoding: chunked
|
||||
< date: Wed, 15 Jan 2025 19:28:27 GMT
|
||||
<
|
||||
* Connection #0 to host localhost left intact
|
||||
{"error":"parsing failed for write_lp endpoint","data":{"original_line":"dquote> home,room=Sunroom temp=hi","line_number":2,"error_message":"No fields were provided"}}%
|
||||
{
|
||||
"error": "parsing failed for write_lp endpoint",
|
||||
"data": {
|
||||
"original_line": "home,room=Sunroom temp=hi",
|
||||
"line_number": 2,
|
||||
"error_message": "No fields were provided"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Neither line is written to the database.
|
||||
|
@ -394,7 +415,7 @@ Use the `format` parameter to specify the response format: `pretty`, `jsonl`, `p
|
|||
The following example sends an HTTP `GET` request with a URL-encoded SQL query:
|
||||
|
||||
```bash
|
||||
curl -v "http://127.0.0.1:8181/api/v3/query_sql?db=servers&q=select+*+from+cpu+limit+5"
|
||||
curl -v "http://{{< influxdb/host >}}/api/v3/query_sql?db=servers&q=select+*+from+cpu+limit+5"
|
||||
```
|
||||
|
||||
##### Example: Query passing JSON parameters
|
||||
|
@ -402,7 +423,7 @@ curl -v "http://127.0.0.1:8181/api/v3/query_sql?db=servers&q=select+*+from+cpu+l
|
|||
The following example sends an HTTP `POST` request with parameters in a JSON payload:
|
||||
|
||||
```bash
|
||||
curl http://127.0.0.1:8181/api/v3/query_sql \
|
||||
curl http://{{< influxdb/host >}}/api/v3/query_sql \
|
||||
--data '{"db": "server", "q": "select * from cpu limit 5"}'
|
||||
```
|
||||
|
||||
|
@ -423,7 +444,7 @@ From here, you can connect to your database with the client library using just t
|
|||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
client = InfluxDBClient3(
|
||||
host='http://127.0.0.1:8181',
|
||||
host='http://{{< influxdb/host >}}',
|
||||
database='servers'
|
||||
)
|
||||
```
|
||||
|
@ -435,7 +456,7 @@ use PyArrow to explore the schema and process results:
|
|||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
client = InfluxDBClient3(
|
||||
host='http://127.0.0.1:8181',
|
||||
host='http://{{< influxdb/host >}}',
|
||||
|
||||
database='servers'
|
||||
)
|
||||
|
@ -659,8 +680,7 @@ def process_writes(influxdb3_local, table_batches, args=None):
|
|||
|
||||
##### Test a plugin on the server
|
||||
|
||||
Use InfluxDB 3 to safely test a plugin before you load it, without touching written data.
|
||||
During a plugin test:
|
||||
Test your InfluxDB 3 plugin safely without affecting written data. During a plugin test:
|
||||
|
||||
- A query executed by the plugin queries against the server you send the request to.
|
||||
- Writes aren't sent to the server but are returned to you.
|
||||
|
@ -795,7 +815,7 @@ The example commands pass the following options:
|
|||
# compactor-id: 'c01'
|
||||
|
||||
|
||||
influxdb3 serve --node-id=host01 --read-from-node-ids=host02 --compactor-id=c01 --run-compactions --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8181 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host01 --read-from-node-ids=host02 --compactor-id=c01 --run-compactions --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://{{< influxdb/host >}} --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -805,7 +825,7 @@ influxdb3 serve --node-id=host01 --read-from-node-ids=host02 --compactor-id=c01
|
|||
# node-id: 'host02'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host02 --read-from-node-ids=host01 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8282
|
||||
influxdb3 serve --node-id=host02 --read-from-node-ids=host01 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://localhost:8282
|
||||
--aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
|
@ -821,12 +841,12 @@ influxdb3 serve --read-from-node-ids=host01,host02 [...OPTIONS]
|
|||
>
|
||||
> ```bash
|
||||
> # In terminal 1
|
||||
> influxdb3 serve --node-id=host01 --http-bind=http://127.0.0.1:8181 [...OPTIONS]
|
||||
> influxdb3 serve --node-id=host01 --http-bind=http://{{< influxdb/host >}} [...OPTIONS]
|
||||
> ```
|
||||
>
|
||||
> ```bash
|
||||
> # In terminal 2
|
||||
> influxdb3 serve --node-id=host01 --http-bind=http://127.0.0.1:8181 [...OPTIONS]
|
||||
> influxdb3 serve --node-id=host01 --http-bind=http://{{< influxdb/host >}} [...OPTIONS]
|
||||
|
||||
### High availability with a dedicated Compactor
|
||||
|
||||
|
@ -847,7 +867,7 @@ The following examples show how to set up HA with a dedicated Compactor node:
|
|||
# node-id: 'host01'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host01 --compactor-id=c01 --read-from-node-ids=host02 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8181 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host01 --compactor-id=c01 --read-from-node-ids=host02 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://{{< influxdb/host >}} --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
```bash
|
||||
|
@ -857,7 +877,7 @@ The following examples show how to set up HA with a dedicated Compactor node:
|
|||
# node-id: 'host02'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host02 --compactor-id=c01 --read-from-node-ids=host01 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8282 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host02 --compactor-id=c01 --read-from-node-ids=host01 --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://localhost:8282 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
2. Start the dedicated compactor node, which uses the following options:
|
||||
|
@ -895,10 +915,12 @@ For a very robust and effective setup for managing time-series data, you can run
|
|||
# node-id: 'host01'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host01 --mode=read_write --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8181 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host01 --mode=read_write --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://{{< influxdb/host >}} --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
|
||||
```
|
||||
|
||||
<!-- The following examples use different ports for different nodes. Don't use the influxdb/host shortcode below. -->
|
||||
|
||||
```
|
||||
## NODE 2 — Writer Node #2
|
||||
|
||||
|
@ -906,7 +928,7 @@ For a very robust and effective setup for managing time-series data, you can run
|
|||
# node-id: 'host02'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
Usage: $ influxdb3 serve --node-id=host02 --mode=read_write --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8282 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
Usage: $ influxdb3 serve --node-id=host02 --mode=read_write --object-store=s3 --bucket=influxdb-3-enterprise-storage --http-bind=http://localhost:8282 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
2. Start the dedicated Compactor node (`--mode=compactor`) and ensure it runs compactions on the specified `compaction-hosts`.
|
||||
|
@ -934,7 +956,7 @@ For a very robust and effective setup for managing time-series data, you can run
|
|||
# node-id: 'host04'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host04 --mode=read --object-store=s3 --read-from-node-ids=host01,host02 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8383 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host04 --mode=read --object-store=s3 --read-from-node-ids=host01,host02 --bucket=influxdb-3-enterprise-storage --http-bind=http://localhost:8383 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
```
|
||||
|
@ -944,7 +966,7 @@ For a very robust and effective setup for managing time-series data, you can run
|
|||
# node-id: 'host05'
|
||||
# bucket: 'influxdb-3-enterprise-storage'
|
||||
|
||||
influxdb3 serve --node-id=host05 --mode=read --object-store=s3 --read-from-node-ids=host01,host02 --bucket=influxdb-3-enterprise-storage --http-bind=0.0.0.0:8484 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
influxdb3 serve --node-id=host05 --mode=read --object-store=s3 --read-from-node-ids=host01,host02 --bucket=influxdb-3-enterprise-storage --http-bind=http://localhost:8484 --aws-access-key-id=<AWS_ACCESS_KEY_ID> --aws-secret-access-key=<AWS_SECRET_ACCESS_KEY>
|
||||
```
|
||||
|
||||
Congratulations, you have a robust setup to workload isolation using {{% product-name %}}.
|
||||
|
@ -968,7 +990,7 @@ You can use the default port `8181` for any write or query, without changing any
|
|||
```
|
||||
# Example variables on a query
|
||||
# HTTP-bound Port: 8585
|
||||
Usage: $ influxdb3 query --host=http://127.0.0.1:8585 -d <DATABASE> "<QUERY>"
|
||||
Usage: $ influxdb3 query --host=http://localhost:8585 -d <DATABASE> "<QUERY>"
|
||||
```
|
||||
|
||||
### File index settings
|
||||
|
@ -982,11 +1004,11 @@ This feature is only available in Enterprise and is not available in Core.
|
|||
# Example variables on a query
|
||||
# HTTP-bound Port: 8585
|
||||
|
||||
influxdb3 create file_index --host=http://127.0.0.1:8585 -d <DATABASE> -t <TABLE> <COLUMNS>
|
||||
influxdb3 create file_index --host=http://localhost:8585 -d <DATABASE> -t <TABLE> <COLUMNS>
|
||||
```
|
||||
|
||||
#### Delete a file index
|
||||
|
||||
```bash
|
||||
influxdb3 delete file_index --host=http://127.0.0.1:8585 -d <DATABASE> -t <TABLE>
|
||||
influxdb3 delete file_index --host=http://localhost:8585 -d <DATABASE> -t <TABLE>
|
||||
```
|
||||
|
|
|
@ -66,3 +66,20 @@ cloud:
|
|||
- name: East US (Virginia)
|
||||
location: Virginia, USA
|
||||
url: https://eastus-1.azure.cloud2.influxdata.com
|
||||
cloud_dedicated:
|
||||
providers:
|
||||
- name: Default
|
||||
regions:
|
||||
- name: cluster-id.a.influxdb.io
|
||||
url: https://cluster-id.a.influxdb.io
|
||||
- name: Custom
|
||||
url: http://example.com:8080
|
||||
product: InfluxDB Cloud Dedicated
|
||||
clustered:
|
||||
providers:
|
||||
- name: Default
|
||||
regions:
|
||||
- name: cluster-host.com
|
||||
url: https://cluster-host.com
|
||||
- name: Custom
|
||||
url: http://example.com:8080
|
|
@ -7,6 +7,10 @@ influxdb3_core:
|
|||
list_order: 2
|
||||
latest: core
|
||||
placeholder_host: localhost:8181
|
||||
ai_sample_questions:
|
||||
- How do I install and run InfluxDB 3 Core?
|
||||
- How do I write a plugin for the Python Processing engine?
|
||||
- How do I write data using the HTTP API for InfluxDB 3 Core?
|
||||
|
||||
influxdb3_enterprise:
|
||||
name: InfluxDB 3 Enterprise
|
||||
|
@ -17,6 +21,10 @@ influxdb3_enterprise:
|
|||
list_order: 2
|
||||
latest: enterprise
|
||||
placeholder_host: localhost:8181
|
||||
ai_sample_questions:
|
||||
- How do I install and run InfluxDB 3 Enterprise?
|
||||
- Help me write a plugin for the Python Processing engine?
|
||||
- How do I start a read replica node with InfluxDB 3 Enterprise?
|
||||
|
||||
influxdb3_cloud_serverless:
|
||||
name: InfluxDB Cloud Serverless
|
||||
|
@ -27,6 +35,9 @@ influxdb3_cloud_serverless:
|
|||
list_order: 2
|
||||
latest: cloud-serverless
|
||||
placeholder_host: cloud2.influxdata.com
|
||||
ai_sample_questions:
|
||||
- How do I migrate from InfluxDB Cloud 2 to InfluxDB Cloud Serverless?
|
||||
- What tools can I use to write data to InfluxDB Cloud Serverless?
|
||||
|
||||
influxdb3_cloud_dedicated:
|
||||
name: InfluxDB Cloud Dedicated
|
||||
|
@ -39,6 +50,10 @@ influxdb3_cloud_dedicated:
|
|||
link: "https://www.influxdata.com/contact-sales-form/"
|
||||
latest_cli: 2.9.9
|
||||
placeholder_host: cluster-id.a.influxdb.io
|
||||
ai_sample_questions:
|
||||
- How do I migrate from InfluxDB v1 to InfluxDB Cloud Dedicated?
|
||||
- What tools can I use to write data to Cloud Dedicated?
|
||||
- How do I use SQL and parameterized queries with Cloud Dedicated?
|
||||
|
||||
influxdb3_clustered:
|
||||
name: InfluxDB Clustered
|
||||
|
@ -50,6 +65,10 @@ influxdb3_clustered:
|
|||
latest: clustered
|
||||
link: "https://www.influxdata.com/contact-sales-influxdb-clustered"
|
||||
placeholder_host: cluster-host.com
|
||||
ai_sample_questions:
|
||||
- How do I use a Helm chart to configure Clustered?
|
||||
- What tools can I use to write data to Clustered?
|
||||
- How do I use SQL and parameterized queries with InfluxDB Clustered?
|
||||
|
||||
influxdb:
|
||||
name: InfluxDB
|
||||
|
@ -68,6 +87,10 @@ influxdb:
|
|||
v1: 1.11.8
|
||||
latest_cli:
|
||||
v2: 2.7.5
|
||||
ai_sample_questions:
|
||||
- How do I write and query data with InfluxDB v2 OSS?
|
||||
- How can I migrate from InfluxDB v2 OSS to InfluxDB 3 Core?
|
||||
- How do I manage auth tokens in InfluxDB v2 OSS?
|
||||
|
||||
influxdb_cloud:
|
||||
name: InfluxDB Cloud (TSM)
|
||||
|
@ -78,6 +101,10 @@ influxdb_cloud:
|
|||
list_order: 1
|
||||
latest: cloud
|
||||
placeholder_host: cloud2.influxdata.com
|
||||
ai_sample_questions:
|
||||
- How do I write and query data with InfluxDB Cloud 2?
|
||||
- How is Cloud 2 different from Cloud Serverless?
|
||||
- How do I manage auth tokens in InfluxDB Cloud 2?
|
||||
|
||||
telegraf:
|
||||
name: Telegraf
|
||||
|
@ -88,6 +115,10 @@ telegraf:
|
|||
latest: v1.33
|
||||
latest_patches:
|
||||
v1: 1.33.0
|
||||
ai_sample_questions:
|
||||
- How do I install and configure Telegraf?
|
||||
- How do I write a custom Telegraf plugin?
|
||||
- How do I use Telegraf for MQTT?
|
||||
|
||||
chronograf:
|
||||
name: Chronograf
|
||||
|
@ -118,6 +149,10 @@ enterprise_influxdb:
|
|||
latest: v1.11
|
||||
latest_patches:
|
||||
v1: 1.11.8
|
||||
ai_sample_questions:
|
||||
- How can I configure my InfluxDB v1 Enterprise server?
|
||||
- How do I replicate data between InfluxDB v1 Enterprise and OSS?
|
||||
- How do I query data stored in InfluxDB v1?
|
||||
|
||||
flux:
|
||||
name: Flux
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import globals from "globals";
|
||||
import pluginJs from "@eslint/js";
|
||||
|
||||
|
||||
/** @type {import('eslint').Linter.Config[]} */
|
||||
export default [
|
||||
{languageOptions: { globals: globals.browser }},
|
||||
pluginJs.configs.recommended,
|
||||
];
|
|
@ -9,13 +9,13 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/png" sizes="32x32">
|
||||
|
||||
{{ partial "header/javascript.html" }}
|
||||
{{ partial "header/stylesheets.html" }}
|
||||
{{ partial "header/google-fonts.html" }}
|
||||
{{ partial "header/javascript.html" }}
|
||||
|
||||
<meta name="Copyright" content="InfluxData Inc." />
|
||||
</head>
|
||||
<body>
|
||||
<body data-component="theme" data-theme-callback="setVisibility">
|
||||
|
||||
{{ partial "header/google-analytics-body.html" }}
|
||||
|
||||
|
@ -67,4 +67,3 @@
|
|||
</body>
|
||||
{{ partial "footer/javascript.html" . }}
|
||||
</html>
|
||||
|
||||
|
|
|
@ -13,8 +13,8 @@
|
|||
<!-- Docs Notifications -->
|
||||
{{ partial "footer/notifications.html" . }}
|
||||
|
||||
<!-- Custom time modal trigger-->
|
||||
{{ partial "footer/custom-time-trigger" . }}
|
||||
<!-- Page widgets -->
|
||||
{{ partial "footer/widgets.html" . }}
|
||||
|
||||
<!-- IOx wayfinding modal -->
|
||||
{{ if in (slice "cloud" "cloud-serverless") $version }}
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
{{ if or (.Page.HasShortcode "influxdb/custom-timestamps") (.Page.HasShortcode "influxdb/custom-timestamps-span") }}
|
||||
<div class="custom-time-trigger">
|
||||
<a onclick='toggleModal("#influxdb-gs-date-select")'><span class="cf-icon Clock_New"></span></a>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -1,29 +1,18 @@
|
|||
{{ $jquery := resources.Get "js/jquery-3.5.0.min.js" }}
|
||||
{{ $versionSelector := resources.Get "js/version-selector.js" }}
|
||||
{{ $contentInteractions := resources.Get "js/content-interactions.js" }}
|
||||
{{ $searchInteractions := resources.Get "js/search-interactions.js" }}
|
||||
{{ $listFilters := resources.Get "js/list-filters.js" }}
|
||||
{{ $modals := resources.Get "js/modals.js" }}
|
||||
{{ $influxdbURLs := resources.Get "js/influxdb-url.js" }}
|
||||
{{ $featureCallouts := resources.Get "js/feature-callouts.js" }}
|
||||
{{ $tabbedContent := resources.Get "js/tabbed-content.js" }}
|
||||
{{ $apiLibs := resources.Get "js/api-libs.js" }}
|
||||
{{ $notifications := resources.Get "js/notifications.js" }}
|
||||
{{ $keybindings := resources.Get "js/keybindings.js" }}
|
||||
{{ $fluxGroupKeys := resources.Get "js/flux-group-keys.js" }}
|
||||
{{ $dateTime := resources.Get "js/datetime.js" }}
|
||||
{{ $influxdbGSTimestamps := resources.Get "js/custom-timestamps.js" }}
|
||||
{{ $codeControls := resources.Get "js/code-controls.js" }}
|
||||
{{ $pageFeedback := resources.Get "js/page-feedback.js" }}
|
||||
{{ $homepageInteractions := resources.Get "js/home-interactions.js" }}
|
||||
{{ $fluxInfluxDBVersions := resources.Get "/js/flux-influxdb-versions.js" }}
|
||||
{{ $v3Wayfinding := resources.Get "/js/v3-wayfinding.js"}}
|
||||
{{ $codePlaceholders := resources.Get "/js/code-placeholders.js" }}
|
||||
{{ $releaseTOC := resources.Get "/js/release-toc.js" }}
|
||||
{{ $footerjs := slice $versionSelector $contentInteractions $searchInteractions $listFilters $modals $influxdbURLs $featureCallouts $tabbedContent $apiLibs $notifications $keybindings $codeControls $pageFeedback $homepageInteractions $fluxInfluxDBVersions | resources.Concat "js/footer.bundle.js" | resources.Fingerprint }}
|
||||
{{ $footerjs := slice $versionSelector $searchInteractions $listFilters $featureCallouts $keybindings $homepageInteractions $fluxInfluxDBVersions | resources.Concat "js/footer.bundle.js" | resources.Fingerprint }}
|
||||
{{ $fluxGroupKeyjs := $fluxGroupKeys | resources.Fingerprint }}
|
||||
{{ $dateTimejs := $dateTime | resources.Fingerprint }}
|
||||
{{ $influxdbGSTimestampsjs := $influxdbGSTimestamps | resources.Fingerprint }}
|
||||
{{ $v3Wayfindingjs := $v3Wayfinding | resources.Fingerprint }}
|
||||
{{ $codePlaceholdersjs := $codePlaceholders | resources.Fingerprint }}
|
||||
{{ $releaseTOCjs := $releaseTOC | resources.Fingerprint }}
|
||||
|
||||
|
@ -62,17 +51,6 @@
|
|||
<script type="text/javascript" src="{{ $dateTimejs.RelPermalink }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<!-- Load getting started timestamps js if when the influxdb/custom-gs-timestamps shortcode is present -->
|
||||
{{ if or (.Page.HasShortcode "influxdb/custom-timestamps") (.Page.HasShortcode "influxdb/custom-timestamps-span") }}
|
||||
<script src="https://cdn.jsdelivr.net/npm/vanillajs-datepicker@1.2.0/dist/js/datepicker.min.js"></script>
|
||||
<script type="text/javascript" src="{{ $influxdbGSTimestampsjs.RelPermalink }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<!-- Load IOx wayfinding JS if page is in cloud or cloud-serverless -->
|
||||
{{ if in (slice "cloud" "cloud-serverless") (index (findRE "[^/]+.*?" .Page.RelPermalink) 1) }}
|
||||
<script type="text/javascript" src="{{ $v3Wayfinding.RelPermalink }}"></script>
|
||||
{{ end }}
|
||||
|
||||
<!-- Load code placeholders js when code-placeholders shortcode is present -->
|
||||
{{ if .Page.HasShortcode "code-placeholders" }}
|
||||
<script type="text/javascript" src="{{ $codePlaceholdersjs.RelPermalink }}"></script>
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
{{ partial "footer/modals/influxdb-url.html" . }}
|
||||
{{ partial "footer/modals/page-feedback.html" . }}
|
||||
{{ if or (.Page.HasShortcode "influxdb/custom-timestamps") (.Page.HasShortcode "influxdb/custom-timestamps-span") }}
|
||||
{{ partial "footer/modals/influxdb-gs-date-select.html" }}
|
||||
{{ partial "footer/modals/influxdb-gs-date-select.html" . }}
|
||||
{{ end }}
|
||||
{{ if $inStdlib }}
|
||||
{{ partial "footer/modals/flux-influxdb-versions.html" . }}
|
||||
|
|
|
@ -1,6 +1,11 @@
|
|||
{{ $productPathData := findRE "[^/]+.*?" $.Page.RelPermalink }}
|
||||
{{ $version := index $productPathData 1 }}
|
||||
{{ $dbWhiteList := (slice "core" "enterprise" "cloud-dedicated" "clustered") }}
|
||||
{{ $useDB := in $dbWhiteList $version }}
|
||||
{{ $storageTerm := cond $useDB "database" "bucket" }}
|
||||
<div class="modal-content" id="influxdb-gs-date-select">
|
||||
<h3>Select a new date</h3>
|
||||
<p><em>Select a date in your bucket's retention period.</em></p>
|
||||
<p><em>Select a date in your {{ $storageTerm }}'s retention period.</em></p>
|
||||
<div id="custom-date-selector"></div>
|
||||
<a class="btn" id="submit-custom-date" onclick="">Update</a>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="footer-widgets">
|
||||
<!-- Custom time modal trigger -->
|
||||
{{ partial "footer/widgets/custom-time-trigger" . }}
|
||||
<!-- Ask AI modal trigger -->
|
||||
{{ partial "footer/widgets/ask-ai-trigger" . }}
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="ask-ai-trigger widget magenta" data-component="ask-ai-trigger" data-tooltip="Ask InfluxData AI for help" style="display: none;">
|
||||
<a class="ask-ai-open" >
|
||||
<div class="icon-influx-logo"></div>
|
||||
Ask AI
|
||||
</a>
|
||||
</div>
|
|
@ -0,0 +1,7 @@
|
|||
{{ if or (.Page.HasShortcode "influxdb/custom-timestamps") (.Page.HasShortcode "influxdb/custom-timestamps-span") }}
|
||||
<div data-component="custom-time-trigger" class="custom-time-trigger widget blue" data-tooltip="Select custom date for sample data">
|
||||
<a href="#" class="custom-time-trigger__button" data-action="open">
|
||||
<span class="cf-icon Clock_New"></span>
|
||||
</a>
|
||||
</div>
|
||||
{{ end }}
|
|
@ -15,15 +15,15 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
|
||||
<link rel="shortcut icon" href="/img/favicon.png" type="image/png" sizes="32x32">
|
||||
|
||||
{{ partial "header/javascript.html" . }}
|
||||
{{ partial "header/canonical.html" . }}
|
||||
{{ partial "header/stylesheets.html" }}
|
||||
{{ partial "header/google-fonts.html" }}
|
||||
{{ partial "header/javascript.html" }}
|
||||
{{ partial "header/search-attributes.html" . }}
|
||||
{{ partial "header/coveo-meta-data.html" . }}
|
||||
{{ if not hugo.IsServer }}{{ partial "header/marketing.html" }}{{ end }}
|
||||
|
||||
<meta name="Copyright" content="InfluxData Inc." />
|
||||
</head>
|
||||
<body class='{{if ne $product nil}}{{ $product }}{{ else }}home{{ end }}{{ if in $version "v1" }} v1{{ end }}'>
|
||||
<body class='{{if ne $product nil}}{{ $product }}{{ else }}home{{ end }}{{ if in $version "v1" }} v1{{ end }}' data-component="theme" data-theme-callback="setVisibility">
|
||||
{{ if not hugo.IsServer }}{{ partial "header/google-analytics-body.html" }}{{ end }}
|
||||
|
|
|
@ -1,9 +1,42 @@
|
|||
<!-- START COMPONENT AND JS BUNDLING REFACTOR
|
||||
Eventually, all site-specific JavaScript and external JS
|
||||
dependencies will be bundled in main.js
|
||||
-->
|
||||
<!-- Legacy: keep jquery here until component refactor is for scripts in footer.bundle.js that still require it. -->
|
||||
{{ $jquery := resources.Get "js/jquery-3.5.0.min.js" }}
|
||||
{{ $localStorage := resources.Get "js/local-storage.js" }}
|
||||
{{ $themes := resources.Get "js/docs-themes.js" }}
|
||||
{{ $sidebar := resources.Get "js/sidebar-toggle.js" }}
|
||||
{{ $headerjs := slice $jquery $localStorage $themes $sidebar | resources.Concat "js/header.bundle.js" | resources.Fingerprint }}
|
||||
{{ $headerjs := slice $jquery | resources.Concat "js/header.bundle.js" | resources.Fingerprint }}
|
||||
|
||||
<script type="text/javascript" src="{{ $headerjs.RelPermalink }}"></script>
|
||||
<script type="text/javascript">setStyleFromCookie();</script>
|
||||
<script type="text/javascript">setSidebarState();</script>
|
||||
|
||||
<!-- $productPathData here is buggy - it might not return the current page path due to the context in which .RelPermalink is called -->
|
||||
{{ $productPathData := findRE "[^/]+.*?" .RelPermalink }}
|
||||
{{ $product := index $productPathData 0 }}
|
||||
{{ $currentVersion := index $productPathData 1 }}
|
||||
<!-- Get site data -->
|
||||
<!-- Load cloudUrls array -->
|
||||
{{ $cloudUrls := slice }}
|
||||
{{- range.Site.Data.influxdb_urls.cloud.providers }}
|
||||
{{- range.regions }}
|
||||
{{ $cloudUrls = $cloudUrls | append "{{ safeHTML .url }}" }}
|
||||
{{ end -}}
|
||||
{{ end -}}
|
||||
{{ $products := .Site.Data.products }}
|
||||
{{ $influxdb_urls := .Site.Data.influxdb_urls }}
|
||||
<!-- Build main.js -->
|
||||
{{ with resources.Get "js/main.js" }}
|
||||
{{ $opts := dict
|
||||
"minify" hugo.IsProduction
|
||||
"sourceMap" (cond hugo.IsProduction "" "external")
|
||||
"targetPath" "js/main.js"
|
||||
"params" (dict "product" $product "currentVersion" $currentVersion "isServer" hugo.IsServer "products" $products "influxdb_urls" $influxdb_urls "cloudUrls" $cloudUrls)
|
||||
}}
|
||||
{{ with . | js.Build $opts }}
|
||||
{{ if hugo.IsProduction }}
|
||||
{{ with . | fingerprint }}
|
||||
<script src="{{ .RelPermalink }}" integrity="{{ .Data.Integrity }}" crossorigin="anonymous"></script>
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
<script src="{{ .RelPermalink }}"></script>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
<!-- Prevent flash of unstyled content -->
|
||||
<style>
|
||||
body {
|
||||
visibility: hidden;
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- Docsearch Styles -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"/>
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
{{- $initialState := .state | default "Open" -}}
|
||||
{{- $modifiedState := cond (eq $initialState "Close") "closed" "open" -}}
|
||||
<div class="sidebar-toggle" onclick="toggleSidebar('sidebar-{{ $modifiedState }}');return false;"><a href="#"><span
|
||||
<div class="sidebar-toggle" data-component="sidebar-toggle" data-state="{{ $modifiedState }}" ><a data-action="toggle" href="#"><span
|
||||
class="cf-icon Sidebar{{ $initialState }}"></span></a></div>
|
|
@ -14,16 +14,16 @@
|
|||
{{ partial "topnav/product-selector.html" . }}
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<div class="search-btn">
|
||||
<button id="search-btn"
|
||||
onclick="toggleSidebar('sidebar-open');document.getElementById('algolia-search-input').focus();return false;"><span
|
||||
class="cf-icon Search_New"></span></button>
|
||||
<div class="search-btn" data-component="search-button">
|
||||
<button id="search-btn" data-action="toggle"><span class="cf-icon Search_New"></span></button>
|
||||
</div>
|
||||
<button class="url-trigger" href="#"><span class="cf-icon CogSolid_New"></span></button>
|
||||
<button class="theme-switcher" id="theme-switch-light" onclick="switchStyle('light-theme');return false;"><span
|
||||
class="cf-icon Lightmode_New"></span></button>
|
||||
<button class="theme-switcher" id="theme-switch-dark" onclick="switchStyle('dark-theme');return false;"><span
|
||||
class="cf-icon Darkmode_New"></span></button>
|
||||
<span data-component="theme-switch">
|
||||
<button class="theme-switch theme-switch-light">
|
||||
<span class="cf-icon Lightmode_New"></span></button>
|
||||
<button class="theme-switch theme-switch-dark">
|
||||
<span class="cf-icon Darkmode_New"></span></button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
12
package.json
12
package.json
|
@ -5,18 +5,26 @@
|
|||
"description": "InfluxDB documentation",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.18.0",
|
||||
"@evilmartians/lefthook": "^1.7.1",
|
||||
"@vvago/vale": "^3.4.2",
|
||||
"autoprefixer": ">=10.2.5",
|
||||
"eslint": "^9.18.0",
|
||||
"globals": "^15.14.0",
|
||||
"hugo-extended": ">=0.101.0",
|
||||
"postcss": ">=8.4.31",
|
||||
"postcss-cli": ">=9.1.0",
|
||||
"prettier": "^3.2.5",
|
||||
"prettier-plugin-sql": "^0.18.0"
|
||||
"prettier-plugin-sql": "^0.18.0",
|
||||
"winston": "^3.16.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"axios": "^1.7.4",
|
||||
"js-yaml": "^4.1.0"
|
||||
"jquery": "^3.7.1",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-yaml": "^4.1.0",
|
||||
"mermaid": "^11.4.1",
|
||||
"vanillajs-datepicker": "^1.3.4"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "LEFTHOOK_EXCLUDE=test lefthook run pre-commit && lefthook run pre-push",
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 3.4 KiB |
Loading…
Reference in New Issue