Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib')
-rw-r--r--app/assets/javascripts/lib/dateformat.js60
-rw-r--r--app/assets/javascripts/lib/dompurify.js6
-rw-r--r--app/assets/javascripts/lib/gfm/constants.js10
-rw-r--r--app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js85
-rw-r--r--app/assets/javascripts/lib/gfm/index.js11
-rw-r--r--app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js1
-rw-r--r--app/assets/javascripts/lib/mermaid.js1
-rw-r--r--app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js26
-rw-r--r--app/assets/javascripts/lib/utils/datetime/date_format_utility.js2
-rw-r--r--app/assets/javascripts/lib/utils/datetime_range.js2
-rw-r--r--app/assets/javascripts/lib/utils/text_markdown.js10
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js1
12 files changed, 201 insertions, 14 deletions
diff --git a/app/assets/javascripts/lib/dateformat.js b/app/assets/javascripts/lib/dateformat.js
new file mode 100644
index 00000000000..1fd95dd03ab
--- /dev/null
+++ b/app/assets/javascripts/lib/dateformat.js
@@ -0,0 +1,60 @@
+import dateFormat, { i18n, masks } from 'dateformat';
+import { s__, __ } from '~/locale';
+
+i18n.dayNames = [
+ __('Sun'),
+ __('Mon'),
+ __('Tue'),
+ __('Wed'),
+ __('Thu'),
+ __('Fri'),
+ __('Sat'),
+ __('Sunday'),
+ __('Monday'),
+ __('Tuesday'),
+ __('Wednesday'),
+ __('Thursday'),
+ __('Friday'),
+ __('Saturday'),
+];
+
+i18n.monthNames = [
+ __('Jan'),
+ __('Feb'),
+ __('Mar'),
+ __('Apr'),
+ __('May'),
+ __('Jun'),
+ __('Jul'),
+ __('Aug'),
+ __('Sep'),
+ __('Oct'),
+ __('Nov'),
+ __('Dec'),
+ __('January'),
+ __('February'),
+ __('March'),
+ __('April'),
+ __('May'),
+ __('June'),
+ __('July'),
+ __('August'),
+ __('September'),
+ __('October'),
+ __('November'),
+ __('December'),
+];
+
+i18n.timeNames = [
+ s__('Time|a'),
+ s__('Time|p'),
+ s__('Time|am'),
+ s__('Time|pm'),
+ s__('Time|A'),
+ s__('Time|P'),
+ s__('Time|AM'),
+ s__('Time|PM'),
+];
+
+export { masks };
+export default dateFormat;
diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js
index 3e28ca2a0f7..6f24590f9e7 100644
--- a/app/assets/javascripts/lib/dompurify.js
+++ b/app/assets/javascripts/lib/dompurify.js
@@ -1,6 +1,8 @@
-import { sanitize as dompurifySanitize, addHook } from 'dompurify';
+import DOMPurify from 'dompurify';
import { getNormalizedURL, getBaseURL, relativePathToAbsolute } from '~/lib/utils/url_utility';
+const { sanitize: dompurifySanitize, addHook, isValidAttribute } = DOMPurify;
+
const defaultConfig = {
// Safely allow SVG <use> tags
ADD_TAGS: ['use', 'gl-emoji', 'copy-code'],
@@ -94,4 +96,4 @@ addHook('afterSanitizeAttributes', (node) => {
export const sanitize = (val, config) => dompurifySanitize(val, { ...defaultConfig, ...config });
-export { isValidAttribute } from 'dompurify';
+export { isValidAttribute };
diff --git a/app/assets/javascripts/lib/gfm/constants.js b/app/assets/javascripts/lib/gfm/constants.js
new file mode 100644
index 00000000000..eaabeb2a767
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/constants.js
@@ -0,0 +1,10 @@
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN = '[[';
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN = 'TOC';
+export const TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN = ']]';
+export const TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN = '[TOC]';
+
+export const MDAST_TEXT_NODE = 'text';
+export const MDAST_EMPHASIS_NODE = 'emphasis';
+export const MDAST_PARAGRAPH_NODE = 'paragraph';
+
+export const GLFM_TABLE_OF_CONTENTS_NODE = 'tableOfContents';
diff --git a/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js b/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js
new file mode 100644
index 00000000000..4d2484a657a
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/glfm_extensions/table_of_contents.js
@@ -0,0 +1,85 @@
+import { first, last } from 'lodash';
+import { u } from 'unist-builder';
+import { visitParents, SKIP, CONTINUE } from 'unist-util-visit-parents';
+import {
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN,
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN,
+ TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN,
+ TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN,
+ MDAST_TEXT_NODE,
+ MDAST_EMPHASIS_NODE,
+ MDAST_PARAGRAPH_NODE,
+ GLFM_TABLE_OF_CONTENTS_NODE,
+} from '../constants';
+
+const isTOCTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value === TABLE_OF_CONTENTS_DOUBLE_BRACKET_MIDDLE_TOKEN;
+
+const isTOCEmphasisNode = ({ type, children }) =>
+ type === MDAST_EMPHASIS_NODE && children.length === 1 && isTOCTextNode(first(children));
+
+const isTOCDoubleSquareBracketOpenTokenTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_DOUBLE_BRACKET_OPEN_TOKEN;
+
+const isTOCDoubleSquareBracketCloseTokenTextNode = ({ type, value }) =>
+ type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_DOUBLE_BRACKET_CLOSE_TOKEN;
+
+/*
+ * Detects table of contents declaration with syntax [[_TOC_]]
+ */
+const isTableOfContentsDoubleSquareBracketSyntax = ({ children }) => {
+ if (children.length !== 3) {
+ return false;
+ }
+
+ const [firstChild, middleChild, lastChild] = children;
+
+ return (
+ isTOCDoubleSquareBracketOpenTokenTextNode(firstChild) &&
+ isTOCEmphasisNode(middleChild) &&
+ isTOCDoubleSquareBracketCloseTokenTextNode(lastChild)
+ );
+};
+
+/*
+ * Detects table of contents declaration with syntax [TOC]
+ */
+const isTableOfContentsSingleSquareBracketSyntax = ({ children }) => {
+ if (children.length !== 1) {
+ return false;
+ }
+
+ const [firstChild] = children;
+ const { type, value } = firstChild;
+
+ return type === MDAST_TEXT_NODE && value.trim() === TABLE_OF_CONTENTS_SINGLE_BRACKET_TOKEN;
+};
+
+const isTableOfContentsNode = (node) =>
+ node.type === MDAST_PARAGRAPH_NODE &&
+ (isTableOfContentsDoubleSquareBracketSyntax(node) ||
+ isTableOfContentsSingleSquareBracketSyntax(node));
+
+export default () => {
+ return (tree) => {
+ visitParents(tree, (node, ancestors) => {
+ const parent = last(ancestors);
+
+ if (!parent) {
+ return CONTINUE;
+ }
+
+ if (isTableOfContentsNode(node)) {
+ const index = parent.children.indexOf(node);
+
+ parent.children[index] = u(GLFM_TABLE_OF_CONTENTS_NODE, {
+ position: node.position,
+ });
+ }
+
+ return SKIP;
+ });
+
+ return tree;
+ };
+};
diff --git a/app/assets/javascripts/lib/gfm/index.js b/app/assets/javascripts/lib/gfm/index.js
index eaf653e9924..fad73f93c1a 100644
--- a/app/assets/javascripts/lib/gfm/index.js
+++ b/app/assets/javascripts/lib/gfm/index.js
@@ -6,6 +6,8 @@ import remarkFrontmatter from 'remark-frontmatter';
import remarkGfm from 'remark-gfm';
import remarkRehype, { all } from 'remark-rehype';
import rehypeRaw from 'rehype-raw';
+import glfmTableOfContents from './glfm_extensions/table_of_contents';
+import * as glfmMdastToHastHandlers from './mdast_to_hast_handlers/glfm_mdast_to_hast_handlers';
const skipFrontmatterHandler = (language) => (h, node) =>
h(node.position, 'frontmatter', { language }, [{ type: 'text', value: node.value }]);
@@ -65,19 +67,22 @@ const skipRenderingHandlers = {
all(h, node),
);
},
+ tableOfContents: (h, node) => h(node.position, 'tableOfContents'),
toml: skipFrontmatterHandler('toml'),
yaml: skipFrontmatterHandler('yaml'),
json: skipFrontmatterHandler('json'),
};
-const createParser = ({ skipRendering = [] }) => {
+const createParser = ({ skipRendering }) => {
return unified()
.use(remarkParse)
.use(remarkGfm)
.use(remarkFrontmatter, ['yaml', 'toml', { type: 'json', marker: ';' }])
+ .use(glfmTableOfContents)
.use(remarkRehype, {
allowDangerousHtml: true,
handlers: {
+ ...glfmMdastToHastHandlers,
...pick(skipRenderingHandlers, skipRendering),
},
})
@@ -99,13 +104,13 @@ const compilerFactory = (renderer) =>
* tree in any desired representation
*
* @param {String} params.markdown Markdown to parse
- * @param {(tree: MDast -> any)} params.renderer A function that accepts mdast
+ * @param {Function} params.renderer A function that accepts mdast
* AST tree and returns an object of any type that represents the result of
* rendering the tree. See the references below to for more information
* about MDast.
*
* MDastTree documentation https://github.com/syntax-tree/mdast
- * @returns {Promise<any>} Returns a promise with the result of rendering
+ * @returns {Promise} Returns a promise with the result of rendering
* the MDast tree
*/
export const render = async ({ markdown, renderer, skipRendering = [] }) => {
diff --git a/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js b/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js
new file mode 100644
index 00000000000..91b09e69405
--- /dev/null
+++ b/app/assets/javascripts/lib/gfm/mdast_to_hast_handlers/glfm_mdast_to_hast_handlers.js
@@ -0,0 +1 @@
+export const tableOfContents = (h, node) => h(node.position, 'nav');
diff --git a/app/assets/javascripts/lib/mermaid.js b/app/assets/javascripts/lib/mermaid.js
index d621c9ddf9e..c72561ce69d 100644
--- a/app/assets/javascripts/lib/mermaid.js
+++ b/app/assets/javascripts/lib/mermaid.js
@@ -9,6 +9,7 @@ const setIframeRenderedSize = (h, w) => {
const drawDiagram = (source) => {
const element = document.getElementById('app');
const insertSvg = (svgCode) => {
+ // eslint-disable-next-line no-unsanitized/property
element.innerHTML = svgCode;
const height = parseInt(element.firstElementChild.getAttribute('height'), 10);
diff --git a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
index 4e7086e62c5..6c5d4ecc901 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_calculation_utility.js
@@ -142,9 +142,16 @@ export const dayInQuarter = (date, quarter) => {
export const millisecondsPerDay = 1000 * 60 * 60 * 24;
-export const getDayDifference = (a, b) => {
- const date1 = Date.UTC(a.getFullYear(), a.getMonth(), a.getDate());
- const date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
+/**
+ * Calculates the number of days between 2 specified dates, excluding the current date
+ *
+ * @param {Date} startDate the earlier date that we will substract from the end date
+ * @param {Date} endDate the last date in the range
+ * @return {Number} number of days in between
+ */
+export const getDayDifference = (startDate, endDate) => {
+ const date1 = Date.UTC(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
+ const date2 = Date.UTC(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay);
};
@@ -208,6 +215,19 @@ export const newDateAsLocaleTime = (date) => {
return new Date(`${date}${suffix}`);
};
+/**
+ * Takes a Date object (where timezone could be GMT or EST) and
+ * returns a Date object with the same date but in UTC.
+ *
+ * @param {Date} date A Date object
+ * @returns {Date|null} A Date object with the same date but in UTC
+ */
+export const getDateWithUTC = (date) => {
+ return date instanceof Date
+ ? new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
+ : null;
+};
+
export const beginOfDayTime = 'T00:00:00Z';
export const endOfDayTime = 'T23:59:59Z';
diff --git a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
index 830f4604382..d07abb72210 100644
--- a/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime/date_format_utility.js
@@ -1,5 +1,5 @@
-import dateFormat from 'dateformat';
import { isString, mapValues, reduce, isDate, unescape } from 'lodash';
+import dateFormat from '~/lib/dateformat';
import { roundToNearestHalf } from '~/lib/utils/common_utils';
import { sanitize } from '~/lib/dompurify';
import { s__, n__, __, sprintf } from '~/locale';
diff --git a/app/assets/javascripts/lib/utils/datetime_range.js b/app/assets/javascripts/lib/utils/datetime_range.js
index 840cc4600fe..548f5a438df 100644
--- a/app/assets/javascripts/lib/utils/datetime_range.js
+++ b/app/assets/javascripts/lib/utils/datetime_range.js
@@ -1,5 +1,5 @@
-import dateformat from 'dateformat';
import { pick, omit, isEqual, isEmpty } from 'lodash';
+import dateformat from '~/lib/dateformat';
import { DATETIME_RANGE_TYPES } from './constants';
import { secondsToMilliseconds } from './datetime_utility';
diff --git a/app/assets/javascripts/lib/utils/text_markdown.js b/app/assets/javascripts/lib/utils/text_markdown.js
index 9f4e12a3010..48be8af3ff6 100644
--- a/app/assets/javascripts/lib/utils/text_markdown.js
+++ b/app/assets/javascripts/lib/utils/text_markdown.js
@@ -263,10 +263,12 @@ export function insertMarkdownText({
if (tag === LINK_TAG_PATTERN) {
if (URL) {
try {
- new URL(selected); // eslint-disable-line no-new
- // valid url
- tag = '[text]({text})';
- select = 'text';
+ const url = new URL(selected);
+
+ if (url.origin !== 'null' || url.origin === null) {
+ tag = '[text]({text})';
+ select = 'text';
+ }
} catch (e) {
// ignore - no valid url
}
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 7b00995b2e5..59645d50e29 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -119,6 +119,7 @@ const getAverageCharWidth = memoize(function getAverageCharWidth(options = {}) {
div.style.left = -1000;
div.style.top = -1000;
+ // eslint-disable-next-line no-unsanitized/property
div.innerHTML = chars;
document.body.appendChild(div);