summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/utils/common_utils.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib/utils/common_utils.js')
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js168
1 files changed, 42 insertions, 126 deletions
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
index f88a0433535..128ef5b335e 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ b/app/assets/javascripts/lib/utils/common_utils.js
@@ -4,9 +4,8 @@
import { GlBreakpointInstance as breakpointInstance } from '@gitlab/ui/dist/utils';
import $ from 'jquery';
-import { isFunction } from 'lodash';
+import { isFunction, defer } from 'lodash';
import Cookies from 'js-cookie';
-import axios from './axios_utils';
import { getLocationHash } from './url_utility';
import { convertToCamelCase, convertToSnakeCase } from './text_utility';
import { isObject } from './type_utility';
@@ -54,7 +53,7 @@ export const getCspNonceValue = () => {
return metaTag && metaTag.content;
};
-export const rstrip = val => {
+export const rstrip = (val) => {
if (val) {
return val.replace(/\s+$/, '');
}
@@ -68,7 +67,7 @@ export const disableButtonIfEmptyField = (fieldSelector, buttonSelector, eventNa
closestSubmit.disable();
}
// eslint-disable-next-line func-names
- return field.on(eventName, function() {
+ return field.on(eventName, function () {
if (rstrip($(this).val()) === '') {
return closestSubmit.disable();
}
@@ -149,13 +148,13 @@ export const isInViewport = (el, offset = {}) => {
);
};
-export const parseUrl = url => {
+export const parseUrl = (url) => {
const parser = document.createElement('a');
parser.href = url;
return parser;
};
-export const parseUrlPathname = url => {
+export const parseUrlPathname = (url) => {
const parsedUrl = parseUrl(url);
// parsedUrl.pathname will return an absolute path for Firefox and a relative path for IE11
// We have to make sure we always have an absolute path.
@@ -166,8 +165,8 @@ const splitPath = (path = '') => path.replace(/^\?/, '').split('&');
export const urlParamsToArray = (path = '') =>
splitPath(path)
- .filter(param => param.length > 0)
- .map(param => {
+ .filter((param) => param.length > 0)
+ .map((param) => {
const split = param.split('=');
return [decodeURI(split[0]), split[1]].join('=');
});
@@ -209,13 +208,13 @@ export const urlParamsToObject = (path = '') =>
return data;
}, {});
-export const isMetaKey = e => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
+export const isMetaKey = (e) => e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
// Identify following special clicks
// 1) Cmd + Click on Mac (e.metaKey)
// 2) Ctrl + Click on PC (e.ctrlKey)
// 3) Middle-click or Mouse Wheel Click (e.which is 2)
-export const isMetaClick = e => e.metaKey || e.ctrlKey || e.which === 2;
+export const isMetaClick = (e) => e.metaKey || e.ctrlKey || e.which === 2;
export const contentTop = () => {
const isDesktop = breakpointInstance.isDesktop();
@@ -261,23 +260,26 @@ export const contentTop = () => {
};
export const scrollToElement = (element, options = {}) => {
- let $el = element;
- if (!(element instanceof $)) {
- $el = $(element);
+ let el = element;
+ if (element instanceof $) {
+ // eslint-disable-next-line prefer-destructuring
+ el = element[0];
+ } else if (typeof el === 'string') {
+ el = document.querySelector(element);
}
- const { top } = $el.offset();
- const { offset = 0 } = options;
- // eslint-disable-next-line no-jquery/no-animate
- return $('body, html').animate(
- {
- scrollTop: top - contentTop() + offset,
- },
- 200,
- );
+ if (el && el.getBoundingClientRect) {
+ // In the previous implementation, jQuery naturally deferred this scrolling.
+ // Unfortunately, we're quite coupled to this implementation detail now.
+ defer(() => {
+ const { duration = 200, offset = 0 } = options;
+ const y = el.getBoundingClientRect().top + window.pageYOffset + offset - contentTop();
+ window.scrollTo({ top: y, behavior: duration ? 'smooth' : 'auto' });
+ });
+ }
};
-export const scrollToElementWithContext = element => {
+export const scrollToElementWithContext = (element) => {
const offsetMultiplier = -0.1;
return scrollToElement(element, { offset: window.innerHeight * offsetMultiplier });
};
@@ -287,7 +289,7 @@ export const scrollToElementWithContext = element => {
* each browser screen repaint.
* @param {Function} fn
*/
-export const debounceByAnimationFrame = fn => {
+export const debounceByAnimationFrame = (fn) => {
let requestId;
return function debounced(...args) {
@@ -334,7 +336,7 @@ const handleSelectedRange = (range, restrictToNode) => {
return range.cloneContents();
};
-export const getSelectedFragment = restrictToNode => {
+export const getSelectedFragment = (restrictToNode) => {
const selection = window.getSelection();
if (selection.rangeCount === 0) return null;
// Most usages of the selection only want text from a part of the page (e.g. discussion)
@@ -390,10 +392,10 @@ export const insertText = (target, text) => {
this will take in the headers from an API response and normalize them
this way we don't run into production issues when nginx gives us lowercased header keys
*/
-export const normalizeHeaders = headers => {
+export const normalizeHeaders = (headers) => {
const upperCaseHeaders = {};
- Object.keys(headers || {}).forEach(e => {
+ Object.keys(headers || {}).forEach((e) => {
upperCaseHeaders[e.toUpperCase()] = headers[e];
});
@@ -406,7 +408,7 @@ export const normalizeHeaders = headers => {
* @param {Object} paginationInformation
* @returns {Object}
*/
-export const parseIntPagination = paginationInformation => ({
+export const parseIntPagination = (paginationInformation) => ({
perPage: parseInt(paginationInformation['X-PER-PAGE'], 10),
page: parseInt(paginationInformation['X-PAGE'], 10),
total: parseInt(paginationInformation['X-TOTAL'], 10),
@@ -445,10 +447,10 @@ export const parseQueryStringIntoObject = (query = '') => {
*/
export const objectToQueryString = (params = {}) =>
Object.keys(params)
- .map(param => `${param}=${params[param]}`)
+ .map((param) => `${param}=${params[param]}`)
.join('&');
-export const buildUrlWithCurrentLocation = param => {
+export const buildUrlWithCurrentLocation = (param) => {
if (param) return `${window.location.pathname}${param}`;
return window.location.pathname;
@@ -460,7 +462,7 @@ export const buildUrlWithCurrentLocation = param => {
*
* @param {String} param
*/
-export const historyPushState = newUrl => {
+export const historyPushState = (newUrl) => {
window.history.pushState({}, document.title, newUrl);
};
@@ -470,7 +472,7 @@ export const historyPushState = newUrl => {
*
* @param {String} param
*/
-export const historyReplaceState = newUrl => {
+export const historyReplaceState = (newUrl) => {
window.history.replaceState({}, document.title, newUrl);
};
@@ -482,7 +484,7 @@ export const historyReplaceState = newUrl => {
* @param {String} value
* @returns {Boolean}
*/
-export const parseBoolean = value => (value && value.toString()) === 'true';
+export const parseBoolean = (value) => (value && value.toString()) === 'true';
export const BACKOFF_TIMEOUT = 'BACKOFF_TIMEOUT';
@@ -529,7 +531,7 @@ export const backOff = (fn, timeout = 60000) => {
let timeElapsed = 0;
return new Promise((resolve, reject) => {
- const stop = arg => (arg instanceof Error ? reject(arg) : resolve(arg));
+ const stop = (arg) => (arg instanceof Error ? reject(arg) : resolve(arg));
const next = () => {
if (timeElapsed < timeout) {
@@ -545,92 +547,6 @@ export const backOff = (fn, timeout = 60000) => {
});
};
-export const createOverlayIcon = (iconPath, overlayPath) => {
- const faviconImage = document.createElement('img');
-
- return new Promise(resolve => {
- faviconImage.onload = () => {
- const size = 32;
-
- const canvas = document.createElement('canvas');
- canvas.width = size;
- canvas.height = size;
-
- const context = canvas.getContext('2d');
- context.clearRect(0, 0, size, size);
- context.drawImage(
- faviconImage,
- 0,
- 0,
- faviconImage.width,
- faviconImage.height,
- 0,
- 0,
- size,
- size,
- );
-
- const overlayImage = document.createElement('img');
- overlayImage.onload = () => {
- context.drawImage(
- overlayImage,
- 0,
- 0,
- overlayImage.width,
- overlayImage.height,
- 0,
- 0,
- size,
- size,
- );
-
- const faviconWithOverlayUrl = canvas.toDataURL();
-
- resolve(faviconWithOverlayUrl);
- };
- overlayImage.src = overlayPath;
- };
- faviconImage.src = iconPath;
- });
-};
-
-export const setFaviconOverlay = overlayPath => {
- const faviconEl = document.getElementById('favicon');
-
- if (!faviconEl) {
- return null;
- }
-
- const iconPath = faviconEl.getAttribute('data-original-href');
-
- return createOverlayIcon(iconPath, overlayPath).then(faviconWithOverlayUrl =>
- faviconEl.setAttribute('href', faviconWithOverlayUrl),
- );
-};
-
-export const resetFavicon = () => {
- const faviconEl = document.getElementById('favicon');
-
- if (faviconEl) {
- const originalFavicon = faviconEl.getAttribute('data-original-href');
- faviconEl.setAttribute('href', originalFavicon);
- }
-};
-
-export const setCiStatusFavicon = pageUrl =>
- axios
- .get(pageUrl)
- .then(({ data }) => {
- if (data && data.favicon) {
- return setFaviconOverlay(data.favicon);
- }
- return resetFavicon();
- })
- .catch(error => {
- resetFavicon();
- throw error;
- });
-
export const spriteIcon = (icon, className = '') => {
const classAttribute = className.length > 0 ? `class="${className}"` : '';
@@ -728,7 +644,7 @@ export const convertObjectPropsToCamelCase = (obj = {}, options = {}) =>
export const convertObjectPropsToSnakeCase = (obj = {}, options = {}) =>
convertObjectProps(convertToSnakeCase, obj, options);
-export const imagePath = imgUrl =>
+export const imagePath = (imgUrl) =>
`${gon.asset_host || ''}${gon.relative_url_root || ''}/assets/${imgUrl}`;
export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
@@ -737,7 +653,7 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => {
$(selector).on('focusin', function selectOnFocusCallback() {
$(this)
.select()
- .one('mouseup', e => {
+ .one('mouseup', (e) => {
e.preventDefault();
});
});
@@ -833,7 +749,7 @@ export const searchBy = (query = '', searchSpace = {}) => {
const normalizedQuery = query.toLowerCase();
const matches = targetKeys
- .filter(item => {
+ .filter((item) => {
const searchItem = `${searchSpace[item]}`.toLowerCase();
return (
@@ -867,9 +783,9 @@ export const isScopedLabel = ({ title = '' }) => title.indexOf('::') !== -1;
// Methods to set and get Cookie
export const setCookie = (name, value) => Cookies.set(name, value, { expires: 365 });
-export const getCookie = name => Cookies.get(name);
+export const getCookie = (name) => Cookies.get(name);
-export const removeCookie = name => Cookies.remove(name);
+export const removeCookie = (name) => Cookies.remove(name);
/**
* Returns the status of a feature flag.
@@ -884,4 +800,4 @@ export const removeCookie = name => Cookies.remove(name);
* @param {String} flag Feature flag
* @returns {Boolean} on/off
*/
-export const isFeatureFlagEnabled = flag => window.gon.features?.[flag];
+export const isFeatureFlagEnabled = (flag) => window.gon.features?.[flag];