summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib/utils')
-rw-r--r--app/assets/javascripts/lib/utils/axios_utils.js23
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js12
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js24
-rw-r--r--app/assets/javascripts/lib/utils/notify.js14
-rw-r--r--app/assets/javascripts/lib/utils/number_utils.js11
-rw-r--r--app/assets/javascripts/lib/utils/set.js9
-rw-r--r--app/assets/javascripts/lib/utils/suppress_ajax_errors_during_navigation.js16
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js8
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js8
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.