diff options
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r-- | app/assets/javascripts/lib/ace.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/lib/ace/ace_config_paths.js.erb | 34 | ||||
-rw-r--r-- | app/assets/javascripts/lib/graphql.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/ace_utils.js | 6 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/apollo_startup_js_link.js | 106 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 21 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/constants.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/css_utils.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/datetime_utility.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/dom_utils.js | 22 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/http_status.js | 1 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/number_utils.js | 14 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/text_utility.js | 12 |
13 files changed, 164 insertions, 66 deletions
diff --git a/app/assets/javascripts/lib/ace.js b/app/assets/javascripts/lib/ace.js deleted file mode 100644 index e90b3d2eec7..00000000000 --- a/app/assets/javascripts/lib/ace.js +++ /dev/null @@ -1,4 +0,0 @@ -/*= require ace/ace */ -/*= require ace/ext-modelist */ -/*= require ace/ext-searchbox */ -/*= require ./ace/ace_config_paths */ diff --git a/app/assets/javascripts/lib/ace/ace_config_paths.js.erb b/app/assets/javascripts/lib/ace/ace_config_paths.js.erb deleted file mode 100644 index 976769ba84a..00000000000 --- a/app/assets/javascripts/lib/ace/ace_config_paths.js.erb +++ /dev/null @@ -1,34 +0,0 @@ -<% -ace_gem_path = Bundler.rubygems.find_name('ace-rails-ap').first.full_gem_path -ace_workers = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/worker-*.js'].sort.map do |file| - File.basename(file, '.js').sub(/^worker-/, '') -end -ace_modes = Dir[ace_gem_path + '/vendor/assets/javascripts/ace/mode-*.js'].sort.map do |file| - File.basename(file, '.js').sub(/^mode-/, '') -end -%> -// Lazy-load configuration when ace.edit is called -(function() { - var basePath; - var ace = window.ace; - var edit = ace.edit; - ace.edit = function() { - window.gon = window.gon || {}; - basePath = (window.gon.relative_url_root || '').replace(/\/$/, '') + '/assets/ace'; - ace.config.set('basePath', basePath); - - // configure paths for all worker modules -<% ace_workers.each do |worker| %> - ace.config.setModuleUrl('ace/mode/<%= worker %>_worker', basePath + '/<%= File.basename(asset_path("ace/worker-#{worker}.js")) %>'); -<% end %> - - // configure paths for all mode modules -<% ace_modes.each do |mode| %> - ace.config.setModuleUrl('ace/mode/<%= mode %>', basePath + '/<%= File.basename(asset_path("ace/mode-#{mode}.js")) %>'); -<% end %> - - // restore original method - ace.edit = edit; - return ace.edit.apply(ace, arguments); - }; -})(); diff --git a/app/assets/javascripts/lib/graphql.js b/app/assets/javascripts/lib/graphql.js index 0e07f7d8e44..e0d9a903e0a 100644 --- a/app/assets/javascripts/lib/graphql.js +++ b/app/assets/javascripts/lib/graphql.js @@ -5,6 +5,7 @@ import { ApolloLink } from 'apollo-link'; import { BatchHttpLink } from 'apollo-link-batch-http'; import csrf from '~/lib/utils/csrf'; import PerformanceBarService from '~/performance_bar/services/performance_bar_service'; +import { StartupJSLink } from '~/lib/utils/apollo_startup_js_link'; export const fetchPolicies = { CACHE_FIRST: 'cache-first', @@ -62,7 +63,7 @@ export default (resolvers = {}, config = {}) => { return new ApolloClient({ typeDefs: config.typeDefs, - link: ApolloLink.from([performanceBarLink, uploadsLink]), + link: ApolloLink.from([performanceBarLink, new StartupJSLink(), uploadsLink]), cache: new InMemoryCache({ ...config.cacheConfig, freezeResults: config.assumeImmutableResults, diff --git a/app/assets/javascripts/lib/utils/ace_utils.js b/app/assets/javascripts/lib/utils/ace_utils.js deleted file mode 100644 index ee71ae0e61a..00000000000 --- a/app/assets/javascripts/lib/utils/ace_utils.js +++ /dev/null @@ -1,6 +0,0 @@ -/* global ace */ - -export default function getModeByFileExtension(path) { - const modelist = ace.require('ace/ext/modelist'); - return modelist.getModeForPath(path).mode; -} diff --git a/app/assets/javascripts/lib/utils/apollo_startup_js_link.js b/app/assets/javascripts/lib/utils/apollo_startup_js_link.js new file mode 100644 index 00000000000..5c120dd532f --- /dev/null +++ b/app/assets/javascripts/lib/utils/apollo_startup_js_link.js @@ -0,0 +1,106 @@ +import { ApolloLink, Observable } from 'apollo-link'; +import { parse } from 'graphql'; +import { isEqual, pickBy } from 'lodash'; + +/** + * Remove undefined values from object + * @param obj + * @returns {Dictionary<unknown>} + */ +const pickDefinedValues = obj => pickBy(obj, x => x !== undefined); + +/** + * Compares two set of variables, order independent + * + * Ignores undefined values (in the top level) and supports arrays etc. + */ +const variablesMatch = (var1 = {}, var2 = {}) => { + return isEqual(pickDefinedValues(var1), pickDefinedValues(var2)); +}; + +export class StartupJSLink extends ApolloLink { + constructor() { + super(); + this.startupCalls = new Map(); + this.parseStartupCalls(window.gl?.startup_graphql_calls || []); + } + + // Extract operationNames from the queries and ensure that we can + // match operationName => element from result array + parseStartupCalls(calls) { + calls.forEach(call => { + const { query, variables, fetchCall } = call; + const operationName = parse(query)?.definitions?.find(x => x.kind === 'OperationDefinition') + ?.name?.value; + + if (operationName) { + this.startupCalls.set(operationName, { + variables, + fetchCall, + }); + } + }); + } + + static noopRequest = (operation, forward) => forward(operation); + + disable() { + this.request = StartupJSLink.noopRequest; + this.startupCalls = null; + } + + request(operation, forward) { + // Disable StartupJSLink in case all calls are done or none are set up + if (this.startupCalls && this.startupCalls.size === 0) { + this.disable(); + return forward(operation); + } + + const { operationName } = operation; + + // Skip startup call if the operationName doesn't match + if (!this.startupCalls.has(operationName)) { + return forward(operation); + } + + const { variables: startupVariables, fetchCall } = this.startupCalls.get(operationName); + this.startupCalls.delete(operationName); + + // Skip startup call if the variables values do not match + if (!variablesMatch(startupVariables, operation.variables)) { + return forward(operation); + } + + return new Observable(observer => { + fetchCall + .then(response => { + // Handle HTTP errors + if (!response.ok) { + throw new Error('fetchCall failed'); + } + operation.setContext({ response }); + return response.json(); + }) + .then(result => { + if (result && (result.errors || !result.data)) { + throw new Error('Received GraphQL error'); + } + + // we have data and can send it to back up the link chain + observer.next(result); + observer.complete(); + }) + .catch(() => { + forward(operation).subscribe({ + next: result => { + observer.next(result); + }, + error: error => { + observer.error(error); + }, + complete: observer.complete.bind(observer), + }); + }); + }); + } +} diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index fe1ac00fd1d..42a5de68cfa 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -61,9 +61,6 @@ export const rstrip = val => { return val; }; -export const updateTooltipTitle = ($tooltipEl, newTitle) => - $tooltipEl.attr('title', newTitle).tooltip('_fixTitle'); - export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventName = 'input') => { const field = $(fieldSelector); const closestSubmit = field.closest('form').find(buttonSelector); @@ -744,6 +741,24 @@ export const roundOffFloat = (number, precision = 0) => { }; /** + * Method to round down values with decimal places + * with provided precision. + * + * Eg; roundDownFloat(3.141592, 3) = 3.141 + * + * Refer to spec/javascripts/lib/utils/common_utils_spec.js for + * more supported examples. + * + * @param {Float} number + * @param {Number} precision + */ +export const roundDownFloat = (number, precision = 0) => { + // eslint-disable-next-line no-restricted-properties + const multiplier = Math.pow(10, precision); + return Math.floor(number * multiplier) / multiplier; +}; + +/** * Represents navigation type constants of the Performance Navigation API. * Detailed explanation see https://developer.mozilla.org/en-US/docs/Web/API/PerformanceNavigation. */ diff --git a/app/assets/javascripts/lib/utils/constants.js b/app/assets/javascripts/lib/utils/constants.js index 1a4ecc12f01..993d51370ec 100644 --- a/app/assets/javascripts/lib/utils/constants.js +++ b/app/assets/javascripts/lib/utils/constants.js @@ -1,5 +1,4 @@ export const BYTES_IN_KIB = 1024; -export const BYTES_IN_KB = 1000; export const HIDDEN_CLASS = 'hidden'; export const TRUNCATE_WIDTH_DEFAULT_WIDTH = 80; export const TRUNCATE_WIDTH_DEFAULT_FONT_SIZE = 12; diff --git a/app/assets/javascripts/lib/utils/css_utils.js b/app/assets/javascripts/lib/utils/css_utils.js index 90213221443..02f092e73e1 100644 --- a/app/assets/javascripts/lib/utils/css_utils.js +++ b/app/assets/javascripts/lib/utils/css_utils.js @@ -1,5 +1,7 @@ export function loadCSSFile(path) { return new Promise(resolve => { + if (!path) resolve(); + if (document.querySelector(`link[href="${path}"]`)) { resolve(); } else { diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 753245147d2..46b0f0cbc70 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -206,10 +206,6 @@ export const localTimeAgo = ($timeagoEls, setTimeago = true) => { $timeagoEls.each((i, el) => { // Recreate with custom template el.setAttribute('title', formatDate(el.dateTime)); - $(el).tooltip({ - template: - '<div class="tooltip local-timeago" role="tooltip"><div class="arrow"></div><div class="tooltip-inner"></div></div>', - }); }); } diff --git a/app/assets/javascripts/lib/utils/dom_utils.js b/app/assets/javascripts/lib/utils/dom_utils.js index d9b0e8c4476..7bba7ba2f45 100644 --- a/app/assets/javascripts/lib/utils/dom_utils.js +++ b/app/assets/javascripts/lib/utils/dom_utils.js @@ -47,3 +47,25 @@ export const parseBooleanDataAttributes = ({ dataset }, names) => return acc; }, {}); + +/** + * Returns whether or not the provided element is currently visible. + * This function operates identically to jQuery's `:visible` pseudo-selector. + * Documentation for this selector: https://api.jquery.com/visible-selector/ + * Implementation of this selector: https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/css/hiddenVisibleSelectors.js#L8 + * @param {HTMLElement} element The element to test + * @returns {Boolean} `true` if the element is currently visible, otherwise false + */ +export const isElementVisible = element => + Boolean(element.offsetWidth || element.offsetHeight || element.getClientRects().length); + +/** + * The opposite of `isElementVisible`. + * Returns whether or not the provided element is currently hidden. + * This function operates identically to jQuery's `:hidden` pseudo-selector. + * Documentation for this selector: https://api.jquery.com/hidden-selector/ + * Implementation of this selector: https://github.com/jquery/jquery/blob/d0ce00cdfa680f1f0c38460bc51ea14079ae8b07/src/css/hiddenVisibleSelectors.js#L6 + * @param {HTMLElement} element The element to test + * @returns {Boolean} `true` if the element is currently hidden, otherwise false + */ +export const isElementHidden = element => !isElementVisible(element); diff --git a/app/assets/javascripts/lib/utils/http_status.js b/app/assets/javascripts/lib/utils/http_status.js index 7132986a7e6..06529f06a66 100644 --- a/app/assets/javascripts/lib/utils/http_status.js +++ b/app/assets/javascripts/lib/utils/http_status.js @@ -22,6 +22,7 @@ const httpStatusCodes = { CONFLICT: 409, GONE: 410, UNPROCESSABLE_ENTITY: 422, + TOO_MANY_REQUESTS: 429, INTERNAL_SERVER_ERROR: 500, SERVICE_UNAVAILABLE: 503, }; diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index 2424d6cbf3b..bc87232f40b 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -1,4 +1,4 @@ -import { BYTES_IN_KIB, BYTES_IN_KB } from './constants'; +import { BYTES_IN_KIB } from './constants'; import { sprintf, __ } from '~/locale'; /** @@ -35,18 +35,6 @@ export function formatRelevantDigits(number) { } /** - * Utility function that calculates KB of the given bytes. - * Note: This method calculates KiloBytes as opposed to - * Kibibytes. For Kibibytes, bytesToKiB should be used. - * - * @param {Number} number bytes - * @return {Number} KiB - */ -export function bytesToKB(number) { - return number / BYTES_IN_KB; -} - -/** * Utility function that calculates KiB of the given bytes. * * @param {Number} number bytes diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 8ac6a44cba9..a81ca3f211f 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -399,3 +399,15 @@ export const truncateNamespace = (string = '') => { * @returns {Boolean} */ export const hasContent = obj => isString(obj) && obj.trim() !== ''; + +/** + * A utility function that validates if a + * string is valid SHA1 hash format. + * + * @param {String} hash to validate + * + * @return {Boolean} true if valid + */ +export const isValidSha1Hash = str => { + return /^[0-9a-f]{5,40}$/.test(str); +}; |