diff options
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r-- | app/assets/javascripts/lib/utils/axios_utils.js | 23 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 12 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/datetime_utility.js | 24 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/notify.js | 14 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/number_utils.js | 11 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/set.js | 9 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js | 16 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/text_markdown.js | 8 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/url_utility.js | 8 |
9 files changed, 104 insertions, 21 deletions
diff --git a/app/assets/javascripts/lib/utils/axios_utils.js b/app/assets/javascripts/lib/utils/axios_utils.js index 37721cd030c..a04fe609015 100644 --- a/app/assets/javascripts/lib/utils/axios_utils.js +++ b/app/assets/javascripts/lib/utils/axios_utils.js @@ -1,5 +1,6 @@ import axios from 'axios'; import csrf from './csrf'; +import suppressAjaxErrorsDuringNavigation from './suppress_ajax_errors_during_navigation'; axios.defaults.headers.common[csrf.headerKey] = csrf.token; // Used by Rails to check if it is a valid XHR request @@ -8,23 +9,37 @@ axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; // Maintain a global counter for active requests // see: spec/support/wait_for_requests.rb axios.interceptors.request.use(config => { - window.activeVueResources = window.activeVueResources || 0; - window.activeVueResources += 1; + window.pendingRequests = window.pendingRequests || 0; + window.pendingRequests += 1; return config; }); // Remove the global counter axios.interceptors.response.use( response => { - window.activeVueResources -= 1; + window.pendingRequests -= 1; return response; }, err => { - window.activeVueResources -= 1; + window.pendingRequests -= 1; return Promise.reject(err); }, ); +let isUserNavigating = false; +window.addEventListener('beforeunload', () => { + isUserNavigating = true; +}); + +// Ignore AJAX errors caused by requests +// being cancelled due to browser navigation +const { gon } = window; +const featureFlagEnabled = gon && gon.features && gon.features.suppressAjaxNavigationErrors; +axios.interceptors.response.use( + response => response, + err => suppressAjaxErrorsDuringNavigation(err, isUserNavigating, featureFlagEnabled), +); + export default axios; /** diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6e8f63a10a4..177ae4f9838 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -15,6 +15,8 @@ export const getPagePath = (index = 0) => { return page.split(':')[index]; }; +export const getDashPath = (path = window.location.pathname) => path.split('/-/')[1] || null; + export const isInGroupsPage = () => getPagePath() === 'groups'; export const isInProjectPage = () => getPagePath() === 'projects'; @@ -175,6 +177,15 @@ export const urlParamsToArray = (path = '') => export const getUrlParamsArray = () => urlParamsToArray(window.location.search); +/** + * Accepts encoding string which includes query params being + * sent to URL. + * + * @param {string} path Query param string + * + * @returns {object} Query params object containing key-value pairs + * with both key and values decoded into plain string. + */ export const urlParamsToObject = (path = '') => splitPath(path).reduce((dataParam, filterParam) => { if (filterParam === '') { @@ -183,6 +194,7 @@ export const urlParamsToObject = (path = '') => const data = dataParam; let [key, value] = filterParam.split('='); + key = /%\w+/g.test(key) ? decodeURIComponent(key) : key; const isArray = key.includes('[]'); key = key.replace('[]', ''); value = decodeURIComponent(value.replace(/\+/g, ' ')); diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index a4715789337..37b0215f6f9 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -537,13 +537,6 @@ export const stringifyTime = (timeObject, fullNameFormat = false) => { }; /** - * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns - * the first non-zero unit/value pair. - */ -export const abbreviateTime = timeStr => - timeStr.split(' ').filter(unitStr => unitStr.charAt(0) !== '0')[0]; - -/** * Calculates the milliseconds between now and a given date string. * The result cannot become negative. * @@ -554,3 +547,20 @@ export const calculateRemainingMilliseconds = endDate => { const remainingMilliseconds = new Date(endDate).getTime() - Date.now(); return Math.max(remainingMilliseconds, 0); }; + +/** + * Subtracts a given number of days from a given date and returns the new date. + * + * @param {Date} date the date that we will substract days from + * @param {number} daysInPast number of days that are subtracted from a given date + * @returns {String} Date string in ISO format + */ +export const getDateInPast = (date, daysInPast) => { + const dateClone = newDate(date); + return new Date( + dateClone.setTime(dateClone.getTime() - daysInPast * 24 * 60 * 60 * 1000), + ).toISOString(); +}; + +export const beginOfDayTime = 'T00:00:00Z'; +export const endOfDayTime = 'T23:59:59Z'; diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js index 3439db1e326..cd509a13193 100644 --- a/app/assets/javascripts/lib/utils/notify.js +++ b/app/assets/javascripts/lib/utils/notify.js @@ -1,12 +1,14 @@ -/* eslint-disable func-names, no-var, consistent-return, prefer-arrow-callback, no-return-assign */ +/* eslint-disable no-var, consistent-return, no-return-assign */ function notificationGranted(message, opts, onclick) { var notification; notification = new Notification(message, opts); - setTimeout(function() { - // Hide the notification after X amount of seconds - return notification.close(); - }, 8000); + setTimeout( + () => + // Hide the notification after X amount of seconds + notification.close(), + 8000, + ); return (notification.onclick = onclick || notification.close); } @@ -32,7 +34,7 @@ function notifyMe(message, body, icon, onclick) { // If it's okay let's create a notification return notificationGranted(message, opts, onclick); } else if (Notification.permission !== 'denied') { - return Notification.requestPermission(function(permission) { + return Notification.requestPermission(permission => { // If the user accepts, let's create a notification if (permission === 'granted') { return notificationGranted(message, opts, onclick); diff --git a/app/assets/javascripts/lib/utils/number_utils.js b/app/assets/javascripts/lib/utils/number_utils.js index 61c8b8803d7..0f2cc57b1f9 100644 --- a/app/assets/javascripts/lib/utils/number_utils.js +++ b/app/assets/javascripts/lib/utils/number_utils.js @@ -106,3 +106,14 @@ export const sum = (a = 0, b = 0) => a + b; * @param {Int} number */ export const isOdd = (number = 0) => number % 2; + +/** + * Computes the median for a given array. + * @param {Array} arr An array of numbers + * @returns {Number} The median of the given array + */ +export const median = arr => { + const middle = Math.floor(arr.length / 2); + const sorted = arr.sort((a, b) => a - b); + return arr.length % 2 !== 0 ? sorted[middle] : (sorted[middle - 1] + sorted[middle]) / 2; +}; diff --git a/app/assets/javascripts/lib/utils/set.js b/app/assets/javascripts/lib/utils/set.js new file mode 100644 index 00000000000..3845d648b61 --- /dev/null +++ b/app/assets/javascripts/lib/utils/set.js @@ -0,0 +1,9 @@ +/** + * Checks if the first argument is a subset of the second argument. + * @param {Set} subset The set to be considered as the subset. + * @param {Set} superset The set to be considered as the superset. + * @returns {boolean} + */ +// eslint-disable-next-line import/prefer-default-export +export const isSubset = (subset, superset) => + Array.from(subset).every(value => superset.has(value)); diff --git a/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js new file mode 100644 index 00000000000..4c61da9b862 --- /dev/null +++ b/app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js @@ -0,0 +1,16 @@ +/** + * An Axios error interceptor that suppresses AJAX errors caused + * by the request being cancelled when the user navigates to a new page + */ +export default (err, isUserNavigating, featureFlagEnabled) => { + if (featureFlagEnabled && isUserNavigating && err.code === 'ECONNABORTED') { + // If the user is navigating away from the current page, + // prevent .then() and .catch() handlers from being + // called by returning a Promise that never resolves + return new Promise(() => {}); + } + + // The error is not related to browser navigation, + // so propagate the error + return Promise.reject(err); +}; diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js index 7873eaf059f..2e0270ee42f 100644 --- a/app/assets/javascripts/lib/utils/text_markdown.js +++ b/app/assets/javascripts/lib/utils/text_markdown.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, consistent-return */ +/* eslint-disable func-names, no-var, no-param-reassign, one-var, operator-assignment, no-else-return, consistent-return */ import $ from 'jquery'; import { insertText } from '~/lib/utils/common_utils'; @@ -218,7 +218,7 @@ export function insertMarkdownText({ : blockTagText(text, textArea, blockTag, selected); } else { textToInsert = selectedSplit - .map(function(val) { + .map(val => { if (tag.indexOf(textPlaceholder) > -1) { return tag.replace(textPlaceholder, val); } @@ -237,7 +237,7 @@ export function insertMarkdownText({ } if (removedFirstNewLine) { - textToInsert = '\n' + textToInsert; + textToInsert = `\n${textToInsert}`; } if (removedLastNewLine) { @@ -301,7 +301,7 @@ export function addMarkdownListeners(form) { export function addEditorMarkdownListeners(editor) { $('.js-md') .off('click') - .on('click', function(e) { + .on('click', e => { const { mdTag, mdBlock, mdPrepend, mdSelect } = $(e.currentTarget).data(); insertMarkdownText({ diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js index 7ead9d46fbb..4be0d05a9b7 100644 --- a/app/assets/javascripts/lib/utils/url_utility.js +++ b/app/assets/javascripts/lib/utils/url_utility.js @@ -88,6 +88,14 @@ export function getLocationHash(url = window.location.href) { } /** + * Returns a boolean indicating whether the URL hash contains the given string value + */ +export function doesHashExistInUrl(hashName) { + const hash = getLocationHash(); + return hash && hash.includes(hashName); +} + +/** * Apply the fragment to the given url by returning a new url string that includes * the fragment. If the given url already contains a fragment, the original fragment * will be removed. |