', { class: 'form-control-feedback' }).text(error);\n element.parent().append(el);\n }\n\n function validateField(el) {\n const value = el.val();\n const name = el.attr('name');\n\n const validationResult = validations[name](value);\n const isValid = _.isNil(validationResult);\n\n setTimeout(() => {\n // Remove the current errors, if they exist. If validation fails, then\n // the error will be replaced with the new error message.\n const feedback = el.siblings('.form-control-feedback').first();\n\n // If we're clicking on a link, no longer validate cause we're going away\n if (form.data('clicking')) return;\n\n if (feedback) feedback.remove();\n if (isValid) {\n clearFieldError(el);\n } else {\n if (!el.data('blurred')) return;\n setFieldError(el, validationResult);\n }\n validateForm();\n }, 100);\n }\n\n function validatePasswordRules(e) {\n const value = e.target.value;\n\n const passedRules = [];\n ruleElements.forEach(({ element, regex }) => {\n const passed = regex.test(value);\n element.find('.check').toggleClass('hidden', !passed);\n element.find('.fail').toggleClass('hidden', passed);\n passedRules.push(passed);\n });\n\n if (!passwordInput.data('blurred')) return;\n const allPassed = !passedRules.some(v => v === false);\n $('#user_password').toggleClass('form-control-error', !allPassed);\n }\n\n function validateForm() {\n const validities = [companyNameInput, emailInput, passwordInput, eulaInput].map(\n field => {\n const name = field.prop('name');\n if (validations[name]) {\n if (field.prop('type') === 'checkbox') {\n return _.isNil(validations[name](field.prop('checked')));\n }\n return _.isNil(validations[name](field.val()));\n }\n\n return true;\n },\n );\n\n const isValid = !validities.some(v => v === false);\n submitButton.prop('disabled', !isValid);\n }\n const basicFields = [companyNameInput, emailInput, passwordInput];\n\n basicFields.forEach(field => field.on('change keyup', () => validateField(field)));\n\n // We don't want to validate the field until they leave it, so we don't show\n // them an error as they're typing their email for the first time\n basicFields.forEach(field =>\n field.on('blur', () => {\n field.data('blurred', true);\n validateField(field);\n }),\n );\n\n // special-case the eula field - the error div is outside the .field div,\n // so we have to remove/add it specially.\n eulaInput.on('change blur', () => {\n const value = eulaInput.prop('checked');\n const name = eulaInput.attr('name');\n\n const validationResult = validations[name](value);\n const isValid = _.isNil(validationResult);\n\n const feedback = $('.form-control-feedback.form-control-inline-error');\n if (feedback) feedback.remove();\n\n if (!isValid) {\n const el = $('
', {\n class: 'form-control-feedback form-control-inline-error',\n }).text(validationResult);\n eulaInput.parent().after(el);\n }\n\n validateForm();\n });\n\n // The password rules should be hidden until typed into, but then should stay.\n // Otherwise it looks janky adding/removing elements on field focus\n passwordInput.on('change keyup blur', e => {\n setTimeout(() => {\n // If we're clicking on a link, no longer validate cause we're going away\n if (form.data('clicking')) return;\n }, 100);\n\n validatePasswordRules(e);\n });\n\n $('a').click(() => {\n form.data('clicking', true);\n });\n\n $('#new_user').submit(() => {\n if (window.dataLayer) {\n // eslint-disable-next-line quote-props\n window.dataLayer.push({ event: 'new-signup', signup_type: 'regular' });\n }\n\n return true;\n });\n});\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport styled, { css } from 'react-emotion';\nimport { theme } from 'utils/styles';\n\nimport { blockStyle } from './common';\nimport SVGText from './SVGText';\n\nexport const TooltipHeader = ({ children, width, x, y }) => (\n
\n {children}\n \n);\n\nTooltipHeader.propTypes = {\n children: PropTypes.node.isRequired,\n width: PropTypes.number.isRequired,\n x: PropTypes.number.isRequired,\n y: PropTypes.number.isRequired,\n};\n\nexport const TooltipLabel = ({ children, width, x, y }) => (\n
\n {children}\n \n);\n\nTooltipLabel.propTypes = {\n children: PropTypes.node.isRequired,\n width: PropTypes.number.isRequired,\n x: PropTypes.number.isRequired,\n y: PropTypes.number.isRequired,\n};\n\nconst ARROW_TYPES = {\n bottom: {\n translateY: height => height,\n arrowPoints: '23,21 14,0 32,0',\n gagX: 10,\n gagY: -5,\n transparentGagX: 0,\n transparentGagY: -50,\n },\n top: {\n translateY: () => 0,\n arrowPoints: '25,-26 14,0 36,0',\n gagX: 14,\n gagY: 0,\n transparentGagX: 0,\n transparentGagY: -65,\n },\n};\n\nconst Tooltip = ({ arrow, children, height, status, transform, width }) => (\n
\n \n\n \n\n {children}\n\n \n \n \n \n \n \n);\n\nTooltip.propTypes = {\n arrow: PropTypes.oneOf(['top', 'bottom']).isRequired,\n children: PropTypes.node.isRequired,\n height: PropTypes.number.isRequired,\n status: PropTypes.oneOf(['disabled', 'healthy', 'unhealthy', undefined]),\n transform: PropTypes.string.isRequired,\n width: PropTypes.number.isRequired,\n};\nTooltip.defaultProps = {\n status: undefined,\n};\n\nexport default Tooltip;\n\nconst headerClass = css`\n fill: black;\n font-weight: 600;\n`;\n\nconst labelClass = css`\n fill: ${theme.colors.darkGray};\n font-size: 14px;\n`;\n\nconst Container = styled.g`\n ${blockStyle}\n`;\n\nconst healthyLabel = css`\n fill: ${theme.colors.healthyColor};\n`;\n\nconst unhealthyLabel = css`\n fill: ${theme.colors.unhealthyColor};\n`;\n\nconst disabledLabel = css`\n fill: ${theme.colors.darkGray};\n`;\n\nconst LABEL_CLASSES = {\n disabled: disabledLabel,\n healthy: healthyLabel,\n unhealthy: unhealthyLabel,\n};\n\nconst Arrow = styled.polygon`\n ${blockStyle};\n`;\n\n// To remove blockStyle effect from arrow back side\nconst Gag = styled.rect`\n fill: white;\n filter: none;\n`;\n\n// To make place near arrow clickable\nconst TransparentGag = styled.rect`\n fill: red;\n fill-opacity: 0;\n filter: none;\n`;\n","import Alerts from 'utils/alerts';\n\nexport const COLLECTOR_TIMEOUT_MS = 120000;\n\nexport const Status = {\n CANCELED: -3,\n EXCEPTION: -2,\n FAILED: -1,\n CREATED: 0,\n SENT: 1,\n COMPLETE: 2,\n};\n\nconst watchTask = function (collectorId, taskId, callbacks) {\n const interval = setInterval(() => {\n $.get(`/collectors/${collectorId}/tasks/${taskId}`).then(task => {\n if (task.status === Status.CREATED) {\n if (callbacks.created) {\n callbacks.created(task);\n }\n } else if (task.status === Status.SENT) {\n if (callbacks.sent) {\n callbacks.sent(task);\n }\n } else if (task.status === Status.COMPLETE) {\n clearTimeout(timeoutID);\n clearInterval(interval);\n if (callbacks.complete) {\n callbacks.complete(task);\n }\n } else if (task.status === Status.FAILED) {\n clearTimeout(timeoutID);\n clearInterval(interval);\n if (callbacks.failed) {\n callbacks.failed(task);\n }\n } else if (task.status === Status.EXCEPTION) {\n clearTimeout(timeoutID);\n clearInterval(interval);\n if (callbacks.exception) {\n callbacks.exception(task);\n }\n } else if (task.status === Status.CANCELED) {\n clearTimeout(timeoutID);\n clearInterval(interval);\n if (callbacks.canceled) {\n callbacks.canceled(task);\n }\n } else {\n clearTimeout(timeoutID);\n Alerts.error(\n 'An unexpected error occurred. If the problem persists, let us know through the Chat Button in the bottom corner.',\n );\n }\n });\n }, 300);\n\n // Automatically cancel task after 120s - matches TestConnectionTaskRunner in collector, plus gives extra time\n const timeoutID = setTimeout(() => {\n clearInterval(interval);\n if (callbacks.timeout) {\n callbacks.timeout(interval);\n }\n }, COLLECTOR_TIMEOUT_MS);\n};\n\nexport default watchTask;\n","import React, { useState } from 'react';\nimport PropTypes from 'prop-types';\nimport { css } from 'react-emotion';\nimport { getPositionForPlatform } from 'utils/overview';\nimport { Platform } from 'utils/platforms';\n\nimport Tooltip, { TooltipHeader } from './SVGPane/Tooltip';\nimport SVGPane from './SVGPane';\nimport { LargeHexagon } from './SVGPane/shapes';\n\nconst platforms = [\n {\n id: Platform.NEW_RELIC,\n label: 'New Relic',\n linkName: 'new_relic',\n name: 'new_relic',\n tooltipWidth: 166,\n x: 4,\n y: 4,\n },\n {\n id: Platform.STACKDRIVER_V4,\n label: 'Google Cloud Monitoring',\n linkName: 'google',\n name: 'google_cloud_monitoring',\n tooltipWidth: 282,\n x: 104,\n y: 64,\n },\n {\n id: Platform.DATADOG,\n label: 'Datadog',\n linkName: 'datadog',\n name: 'datadog',\n tooltipWidth: 156,\n x: 4,\n y: 124,\n },\n {\n id: Platform.OMS,\n label: 'Microsoft Azure',\n linkName: 'azure',\n name: 'microsoft_azure_monitor',\n tooltipWidth: 210,\n x: 204,\n y: 4,\n },\n {\n id: Platform.WAVEFRONT,\n label: 'Wavefront',\n linkName: 'wavefront',\n name: 'wavefront',\n tooltipWidth: 172,\n x: 204,\n y: 124,\n },\n];\n\nconst SCALE = 1.3;\n\nexport default function PlatformHexes({ onClick }) {\n const [hoveredPlatform, setHoveredPlatform] = useState(null);\n const [selectedPlatform, setSelectedPlatform] = useState(null);\n\n function onHover(platform) {\n setHoveredPlatform(platform);\n }\n\n function onClickHexagon(platform) {\n setSelectedPlatform(platform);\n onClick(platform);\n }\n\n function renderPlatformHexagon(platform) {\n const position = getPositionForPlatform(platform.name);\n const isHexagonActive =\n _.get(selectedPlatform, 'name', '') === platform.name ||\n _.get(hoveredPlatform, 'name', '') === platform.name;\n\n if (onClick) {\n return (\n
onHover(null)}\n onClick={() => onClickHexagon(platform)}\n onFocus={() => onHover(platform)}\n onMouseLeave={() => onHover(null)}\n onMouseOver={() => onHover(platform)}\n transform={`translate(${platform.x}, ${platform.y})`}\n >\n \n \n \n );\n }\n\n return (\n
{\n onHover(null);\n }}\n onFocus={() => {\n onHover(platform);\n }}\n onMouseLeave={() => {\n onHover(null);\n }}\n onMouseOver={() => {\n onHover(platform);\n }}\n transform={`translate(${platform.x}, ${platform.y})`}\n >\n \n \n \n );\n }\n\n function renderTooltip() {\n if (_.isNil(hoveredPlatform)) return null;\n\n return (\n
\n \n \n {`Stream to ${hoveredPlatform.label}`}\n \n \n \n );\n }\n\n return (\n
\n \n {platforms.map(platform => renderPlatformHexagon(platform))}\n \n {renderTooltip()}\n \n );\n}\nPlatformHexes.propTypes = {\n onClick: PropTypes.func,\n};\nPlatformHexes.defaultProps = {\n onClick: null,\n};\n\nconst buttonStyle = css`\n cursor: pointer;\n\n &:active {\n outline: none;\n }\n`;\n\nconst linkStyle = css`\n &:active {\n outline: none;\n }\n`;\n","import React from 'react';\nimport PropTypes from 'prop-types';\nimport styled, { css } from 'react-emotion';\nimport { theme } from 'utils/styles';\n\nimport { blockStyle } from './common';\n\nconst PLATFORM_D = `\nM 3 53.7\nq -3 -5.2, 0 -10.4\nl 22 -38.1\nq 3 -5.2, 9 -5.2\nl 44 0\nq 6 0, 9 5.2\nl 22 38.1\nq 3 5.2, 0 10.4\nl -22, 38.1\nq -3, 5.2, -9, 5.2\nl -44 0\nq -6 0, -9 -5.2\nZ\n`;\n\nconst ENDPOINT_D = `\nM 25 3\nq 5 -3, 10 0\nl 21 12\nq 4 3, 4 8\nl 0 25\nq 0 4, -4 7\nl -21 12\nq -5 3, -10 0\nl -21 -12\nq -4 -3, -4 -7\nl 0 -25\nq 0 -5, 4 -8\nZ\n`;\n\nconst AGENT_D = `\nM 3 36.7\nq -3 -5.2, 0 -10.4\nl 9 -15.6\nq 3 -5.2, 9 -5.2\nl 101 0\nq 6 0, 9 5.2\nl 9 15.6\nq 3 5.2, 0 10.4\nl -9, 15.6\nq -3, 5.2, -9, 5.2\nl -101 0\nq -6 0, -9 -5.2\nZ\n`;\n\nexport const SmallHexagon = props =>
;\nexport const LargeHexagon = props =>
;\nexport const WideHexagon = props =>
;\n\n/**\n * Just a path element to the hexagon. Should be inserted into `svg` DOM element\n */\nfunction Hexagon({ active, className, disabled, fill, invalid, shadow, type }) {\n let d;\n\n switch (type) {\n case 'endpoint':\n d = ENDPOINT_D;\n break;\n case 'platform':\n d = PLATFORM_D;\n break;\n case 'agent':\n d = AGENT_D;\n break;\n default:\n throw new Error(`No such hexagon type specified: ${type}`);\n }\n\n return (\n
\n );\n}\nHexagon.propTypes = {\n active: PropTypes.bool,\n className: PropTypes.string,\n disabled: PropTypes.bool,\n fill: PropTypes.oneOf([\n 'blue',\n 'gray',\n 'lightGray',\n 'mediumGray',\n 'pink',\n 'primaryBlue',\n 'red',\n 'white',\n ]),\n invalid: PropTypes.bool,\n shadow: PropTypes.bool,\n type: PropTypes.oneOf(['endpoint', 'platform', 'agent']).isRequired,\n};\nHexagon.defaultProps = {\n active: false,\n className: '',\n disabled: false,\n fill: 'white',\n invalid: false,\n shadow: true,\n};\n\nconst active = props =>\n props.active &&\n css`\n stroke: ${theme.colors.lightBlue};\n stroke-width: 3px;\n `;\n\nconst disabled = props =>\n props.disabled &&\n css`\n cursor: disabled;\n stroke: ${theme.colors.darkGray};\n stroke-width: 2px;\n filter: none;\n `;\n\nconst invalid = props =>\n props.invalid &&\n css`\n stroke: ${theme.colors.unhealthyColor};\n stroke-width: 2px;\n filter: none;\n `;\n\nconst fill = props =>\n !_.isNil(props.fill) &&\n css`\n fill: ${theme.colors[`${props.fill}HexagonFill`]} !important;\n `;\n\nconst shadow = props => props.shadow && blockStyle;\n\nconst Path = styled.path`\n ${invalid}\n ${disabled}\n ${active}\n ${fill}\n ${shadow}\n`;\n"],"sourceRoot":""}