// Module Imports global.jQuery = require('jquery'); const $ = jQuery; const Popper = require('popper.js'); const bootstrap = require('bootstrap'); const selectpicker = require('bootstrap-select'); const animsition = require('animsition'); const ClipboardJS = require('clipboard'); const frontEndConfig = require('./frontend.json'); const cfgGeneral = frontEndConfig.config.general; const cfgBranding = frontEndConfig.config.branding; const cfgNetworks = frontEndConfig.networks; const inputMessages = frontEndConfig.config.messages; const pageContainer = $('#hg-page-container'); const formContainer = $('#hg-form'); const titleColumn = $('#hg-title-col'); const rowTwo = $('#hg-row-2'); const vrfContainer = $('#hg-container-vrf'); const queryLocation = $('#location'); const queryType = $('#query_type'); const queryTarget = $('#query_target'); const queryVrf = $('#query_vrf'); const queryTargetAppend = $('#hg-target-append'); const submitIcon = $('#hg-submit-icon'); const resultsContainer = $('#hg-results'); const resultsAccordion = $('#hg-accordion'); const resultsColumn = resultsAccordion.parent(); const backButton = $('#hg-back-btn'); const footerHelpBtn = $('#hg-footer-help-btn'); const footerTermsBtn = $('#hg-footer-terms-btn'); const footerCreditBtn = $('#hg-footer-credit-btn'); const footerPopoverTemplate = ''; const feedbackInvalid = msg => `
${msg}
`; const supportedBtn = qt => ``; const vrfSelect = title => ` `; const vrfOption = txt => ``; class InputInvalid extends Error { constructor(validationMsg, invalidField, fieldContainer) { super(validationMsg, invalidField, fieldContainer); this.name = this.constructor.name; this.message = validationMsg; this.field = invalidField; this.container = fieldContainer; } } function swapSpacing(goTo) { if (goTo === 'form') { pageContainer.removeClass('px-0 px-md-3'); resultsColumn.removeClass('px-0'); titleColumn.removeClass('text-center'); } else if (goTo === 'results') { pageContainer.addClass('px-0 px-md-3'); resultsColumn.addClass('px-0'); titleColumn.addClass('text-left'); } } function resetResults() { queryLocation.selectpicker('deselectAll'); queryLocation.selectpicker('val', ''); queryType.selectpicker('val', ''); queryTarget.val(''); resultsContainer.animsition('out', formContainer, '#'); resultsContainer.hide(); $('.hg-info-btn').remove(); swapSpacing('form'); formContainer.show(); formContainer.animsition('in'); backButton.addClass('d-none'); resultsAccordion.empty(); } function reloadPage() { queryLocation.selectpicker('deselectAll'); queryLocation.selectpicker('val', []); queryType.selectpicker('val', ''); queryTarget.val(''); resultsAccordion.empty(); } function findIntersection(firstSet, ...sets) { const count = sets.length; const result = new Set(firstSet); firstSet.forEach((item) => { let i = count; let allHave = true; while (i--) { allHave = sets[i].has(item); if (!allHave) { break; } } if (!allHave) { result.delete(item); } }); return result; } /* Removed liveSearch until bootstrap-select mergest the fix for the mobile keyboard opening issue. Basically, any time an option is selected on a mobile device, the keyboard pops open which is super annoying. */ queryLocation.selectpicker({ iconBase: '', liveSearch: false, selectedTextFormat: 'count > 2', style: '', styleBase: 'form-control', tickIcon: 'remixicon-check-line', }).on('hidden.bs.select', (e) => { $(e.currentTarget).nextAll('.dropdown-menu.show').find('input').blur(); }); queryType.selectpicker({ iconBase: '', liveSearch: false, style: '', styleBase: 'form-control', }).on('hidden.bs.select', (e) => { $(e.currentTarget).nextAll('.form-control.dropdown-toggle').blur(); }); footerTermsBtn.popover({ html: true, trigger: 'manual', template: footerPopoverTemplate, placement: 'top', content: $('#hg-footer-terms-html').html(), }).on('click', (e) => { $(e.currentTarget).popover('toggle'); }).on('focusout', (e) => { $(e.currentTarget).popover('hide'); }); footerHelpBtn.popover({ html: true, trigger: 'manual', placement: 'top', template: footerPopoverTemplate, content: $('#hg-footer-help-html').html(), }).on('click', (e) => { $(e.currentTarget).popover('toggle'); }).on('focusout', (e) => { $(e.currentTarget).popover('hide'); }); footerCreditBtn.popover({ html: true, trigger: 'manual', placement: 'top', title: $('#hg-footer-credit-title').html(), content: $('#hg-footer-credit-content').html(), template: footerPopoverTemplate, }).on('click', (e) => { $(e.currentTarget).popover('toggle'); }).on('focusout', (e) => { $(e.currentTarget).popover('hide'); }); $(document).ready(() => { reloadPage(); resultsContainer.hide(); $('#hg-ratelimit-query').modal('hide'); if (location.pathname == '/') { $('.animsition').animsition({ inClass: 'fade-in', outClass: 'fade-out', inDuration: 400, outDuration: 400, transition: (url) => { window.location.href = url; }, }); formContainer.animsition('in'); } }); queryType.on('changed.bs.select', () => { const queryTypeId = queryType.val(); const queryTypeBtn = $('.hg-info-btn'); if ((queryTypeId === 'bgp_community') || (queryTypeId === 'bgp_aspath')) { queryTypeBtn.remove(); queryTargetAppend.prepend(supportedBtn(queryTypeId)); } else { queryTypeBtn.remove(); } }); queryLocation.on('changed.bs.select', (e, clickedIndex, isSelected, previousValue) => { const net = $(e.currentTarget); vrfContainer.empty().removeClass('col'); const queryLocationIds = net.val(); if (Array.isArray(queryLocationIds) && (queryLocationIds.length)) { const queryLocationNet = net[0][clickedIndex].dataset.netname; const selectedVrfs = () => { const allVrfs = []; $.each(queryLocationIds, (i, loc) => { const locVrfs = cfgNetworks[queryLocationNet][loc].vrfs; allVrfs.push(new Set(locVrfs)); }); return allVrfs; }; const intersectingVrfs = Array.from(findIntersection(...selectedVrfs())); // Add the VRF select element if (vrfContainer.find('#query_vrf').length === 0) { vrfContainer.addClass('col').html(vrfSelect(cfgBranding.text.vrf)); } // Build the select options for each VRF in array const vrfHtmlList = []; $.each(intersectingVrfs, (i, vrf) => { vrfHtmlList.push(vrfOption(vrf)); }); // Add the options to the VRF select element, enable it, initialize Bootstrap Select vrfContainer.find('#query_vrf').html(vrfHtmlList.join('')).removeAttr('disabled').selectpicker({ iconBase: '', liveSearch: false, style: '', styleBase: 'form-control', }); if (intersectingVrfs.length === 0) { vrfContainer.find('#query_vrf').selectpicker('destroy'); vrfContainer.find('#query_vrf').prop('title', inputMessages.no_matching_vrfs).prop('disabled', true); vrfContainer.find('#query_vrf').selectpicker({ iconBase: '', liveSearch: false, style: '', styleBase: 'form-control', }); } } }); queryTargetAppend.on('click', '.hg-info-btn', () => { const queryTypeId = $('.hg-info-btn').data('hg-type'); $(`#hg-info-${queryTypeId}`).modal('show'); }); $('#hg-row-2').find('#query_vrf').on('hidden.bs.select', (e) => { $(e.currentTarget).nextAll('.form-control.dropdown-toggle').blur(); }); const queryApp = (queryType, queryTypeName, locationList, queryTarget, queryVrf) => { const resultsTitle = `${queryTypeName} Query for ${queryTarget}`; $('#hg-results-title').html(resultsTitle); submitIcon.empty().removeClass('hg-loading').html(''); $.each(locationList, (n, loc) => { const locationName = $(`#${loc}`).data('display-name'); const contentHtml = `

`; if ($(`#${loc}-output`).length) { $(`#${loc}-output`).replaceWith(contentHtml); } else { $('#hg-accordion').append(contentHtml); } const iconLoading = ``; $(`#${loc}-heading-text`).text(locationName); $(`#${loc}-status-container`) .addClass('hg-loading') .find('.hg-status-btn') .empty() .html(iconLoading); const generateError = (errorClass, locError, text) => { const iconError = ''; $(`#${locError}-heading`).removeClass('bg-overlay').addClass(`bg-${errorClass}`); $(`#${locError}-heading`).find('.hg-menu-btn').removeClass('btn-loading').addClass(`btn-${errorClass}`); $(`#${locError}-status-container`) .removeClass('hg-loading') .find('.hg-status-btn') .empty() .html(iconError) .addClass('hg-done'); $(`#${locError}-text`).html(text); }; const timeoutError = (locError, text) => { const iconTimeout = ''; $(`#${locError}-heading`).removeClass('bg-overlay').addClass('bg-warning'); $(`#${locError}-heading`).find('.hg-menu-btn').removeClass('btn-loading').addClass('btn-warning'); $(`#${locError}-status-container`).removeClass('hg-loading').find('.hg-status-btn').empty() .html(iconTimeout) .addClass('hg-done'); $(`#${locError}-text`).empty().html(text); }; $.ajax({ url: '/query', method: 'POST', data: JSON.stringify({ query_location: loc, query_type: queryType, query_target: queryTarget, query_vrf: queryVrf, response_format: 'html', }), contentType: 'application/json; charset=utf-8', context: document.body, async: true, timeout: cfgGeneral.request_timeout * 1000, }) .done((data, textStatus, jqXHR) => { const displayHtml = `
${data.output}
`; const iconSuccess = ''; $(`#${loc}-heading`).removeClass('bg-overlay').addClass('bg-primary'); $(`#${loc}-heading`).find('.hg-menu-btn').removeClass('btn-loading').addClass('btn-primary'); $(`#${loc}-status-container`) .removeClass('hg-loading') .find('.hg-status-btn') .empty() .html(iconSuccess) .addClass('hg-done'); $(`#${loc}-text`).empty().html(displayHtml); }) .fail((jqXHR, textStatus, errorThrown) => { const statusCode = jqXHR.status; if (textStatus === 'timeout') { timeoutError(loc, inputMessages.request_timeout); } else if (jqXHR.status === 429) { resetResults(); $('#hg-ratelimit-query').modal('show'); } else if (statusCode === 500 && textStatus !== 'timeout') { timeoutError(loc, inputMessages.request_timeout); } else if ((jqXHR.responseJSON.alert === 'danger') || (jqXHR.responseJSON.alert === 'warning')) { generateError(jqXHR.responseJSON.alert, loc, jqXHR.responseJSON.output); } }) .always(() => { $(`#${loc}-status-btn`).removeAttr('disabled'); $(`#${loc}-copy-btn`).removeAttr('disabled'); }); $(`#${locationList[0]}-content`).collapse('show'); }); }; $(document).on('InvalidInputEvent', (e, domField) => { const errorField = $(domField); if (errorField.hasClass('is-invalid')) { errorField.on('keyup', () => { errorField.removeClass('is-invalid'); errorField.nextAll('.invalid-feedback').remove(); }); } }); // Submit Form Action $('#lgForm').on('submit', (e) => { e.preventDefault(); submitIcon.empty().html('').addClass('hg-loading'); const queryType = $('#query_type').val() || ''; const queryLocation = $('#location').val() || ''; const queryTarget = $('#query_target').val() || ''; const queryVrf = $('#query_vrf').val() || []; const queryTargetContainer = $('#query_target'); const queryTypeContainer = $('#query_type').next('.dropdown-toggle'); const queryLocationContainer = $('#location').next('.dropdown-toggle'); try { // message, thing to circle in red, place to put error text if (!queryTarget) { throw new InputInvalid( inputMessages.no_input, queryTargetContainer, queryTargetContainer.parent(), ); } if (!queryType) { throw new InputInvalid( inputMessages.no_query_type, queryTypeContainer, queryTypeContainer.parent(), ); } if (queryLocation === undefined || queryLocation.length === 0) { throw new InputInvalid( inputMessages.no_location, queryLocationContainer, queryLocationContainer.parent(), ); } } catch (err) { err.field.addClass('is-invalid'); err.container.append(feedbackInvalid(err.message)); submitIcon.empty().removeClass('hg-loading').html(''); $(document).trigger('InvalidInputEvent', err.field); return false; } const queryTypeTitle = $(`#${queryType}`).data('display-name'); queryApp(queryType, queryTypeTitle, queryLocation, queryTarget, queryVrf); $('#hg-form').animsition('out', $('#hg-results'), '#'); $('#hg-form').hide(); swapSpacing('results'); $('#hg-results').show(); $('#hg-results').animsition('in'); $('#hg-submit-spinner').remove(); $('#hg-back-btn').removeClass('d-none'); $('#hg-back-btn').animsition('in'); }); titleColumn.on('click', (e) => { window.location = $(e.currentTarget).data('href'); return false; }); backButton.click(() => { resetResults(); }); const clipboard = new ClipboardJS('.hg-copy-btn'); clipboard.on('success', (e) => { const copyIcon = $(e.trigger).find('.hg-copy-icon'); copyIcon.removeClass('remixicon-checkbox-multiple-blank-line').addClass('remixicon-checkbox-multiple-line'); e.clearSelection(); setTimeout(() => { copyIcon.removeClass('remixicon-checkbox-multiple-line').addClass('remixicon-checkbox-multiple-blank-line'); }, 800); }); clipboard.on('error', (e) => { console.log(e); }); $('#hg-accordion').on('mouseenter', '.hg-done', (e) => { $(e.currentTarget) .find('.hg-status-icon') .addClass('remixicon-repeat-line'); }); $('#hg-accordion').on('mouseleave', '.hg-done', (e) => { $(e.currentTarget) .find('.hg-status-icon') .removeClass('remixicon-repeat-line'); }); $('#hg-accordion').on('click', '.hg-done', (e) => { const thisLocation = $(e.currentTarget).data('location'); const queryType = $('#query_type').val(); const queryTypeTitle = $(`#${queryType}`).data('display-name'); const queryTarget = $('#query_target').val(); queryApp(queryType, queryTypeTitle, [thisLocation], queryTarget); }); $('#hg-ratelimit-query').on('shown.bs.modal', () => { $('#hg-ratelimit-query').trigger('focus'); }); $('#hg-ratelimit-query').find('btn').on('click', () => { $('#hg-ratelimit-query').modal('hide'); });