Commit d8d0f668 authored by Filipa Lacerda's avatar Filipa Lacerda

[ci skip] Merge branch 'master' into 42923-close-issue

* master: (25 commits)
  Addressed mr observations
  Clean new Flash() and stop disabling no-new (eslint) when possible
  Disable query limiting warnings for now on GitLab.com
  Revert "Merge branch 'rd-40552-gitlab-should-check-if-keys-are-valid-before-saving' into 'master'"
  Fix warning messages for promoting labels and milestones
  Fixed missing js selector for the realtime pipelines commit comp
  Honour workhorse provided file name
  Create an empty wiki when there is no wiki in the gitlab export bundle
  Revert and remove header_title line from labels issue
  Fixed bug with param config
  changed params passed to from a string to an object
  Move IssuableTimeTracker vue component
  Fix breadcrumb on labels page for groups
  Convert groups_select ajax to use axios
  Default CI variables to unprotected
  make sure there is a dependency on Gitlab::CurrentSettings is
  Make GITLAB_FEATURES in build_spec compatible with EE
  Update jquery.waitforimages & use npm version
  Fixed typo, updated test, and removed commented code
  Replaced use of $.get with axios.get and updated tests
  ...
parents c33245e9 201f53e9
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
import Vue from 'vue'; import Vue from 'vue';
import Flash from '../../flash'; import Flash from '../../flash';
import { __ } from '../../locale';
import Sidebar from '../../right_sidebar'; import Sidebar from '../../right_sidebar';
import eventHub from '../../sidebar/event_hub'; import eventHub from '../../sidebar/event_hub';
import assigneeTitle from '../../sidebar/components/assignees/assignee_title'; import assigneeTitle from '../../sidebar/components/assignees/assignee_title';
...@@ -95,7 +96,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({ ...@@ -95,7 +96,7 @@ gl.issueBoards.BoardSidebar = Vue.extend({
}) })
.catch(() => { .catch(() => {
this.loadingAssignees = false; this.loadingAssignees = false;
return new Flash('An error occurred while saving assignees'); Flash(__('An error occurred while saving assignees'));
}); });
}, },
}, },
......
/* eslint-disable no-new */
import Vue from 'vue'; import Vue from 'vue';
import Flash from '../../../flash'; import Flash from '../../../flash';
import { __ } from '../../../locale';
import './lists_dropdown'; import './lists_dropdown';
import { pluralize } from '../../../lib/utils/text_utility'; import { pluralize } from '../../../lib/utils/text_utility';
...@@ -36,7 +35,7 @@ gl.issueBoards.ModalFooter = Vue.extend({ ...@@ -36,7 +35,7 @@ gl.issueBoards.ModalFooter = Vue.extend({
gl.boardService.bulkUpdate(issueIds, { gl.boardService.bulkUpdate(issueIds, {
add_label_ids: [list.label.id], add_label_ids: [list.label.id],
}).catch(() => { }).catch(() => {
new Flash('Failed to update issues, please try again.', 'alert'); Flash(__('Failed to update issues, please try again.'));
selectedIssues.forEach((issue) => { selectedIssues.forEach((issue) => {
list.removeIssue(issue); list.removeIssue(issue);
......
/* eslint-disable no-new */
import Vue from 'vue'; import Vue from 'vue';
import Flash from '../../../flash'; import Flash from '../../../flash';
import { __ } from '../../../locale';
const Store = gl.issueBoards.BoardsStore; const Store = gl.issueBoards.BoardsStore;
...@@ -45,7 +44,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({ ...@@ -45,7 +44,7 @@ gl.issueBoards.RemoveIssueBtn = Vue.extend({
}, },
}; };
Vue.http.patch(this.updateUrl, data).catch(() => { Vue.http.patch(this.updateUrl, data).catch(() => {
new Flash('Failed to remove issue from board, please try again.', 'alert'); Flash(__('Failed to remove issue from board, please try again.'));
lists.forEach((list) => { lists.forEach((list) => {
list.addIssue(issue); list.addIssue(issue);
......
...@@ -39,7 +39,7 @@ export default class VariableList { ...@@ -39,7 +39,7 @@ export default class VariableList {
}, },
protected: { protected: {
selector: '.js-ci-variable-input-protected', selector: '.js-ci-variable-input-protected',
default: 'true', default: 'false',
}, },
environment_scope: { environment_scope: {
// We can't use a `.js-` class here because // We can't use a `.js-` class here because
......
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-use-before-define, prefer-arrow-callback, no-else-return, consistent-return, prefer-template, quotes, one-var, one-var-declaration-per-line, no-unused-vars, no-return-assign, comma-dangle, quote-props, no-unused-expressions, no-sequences, object-shorthand, max-len */
import 'vendor/jquery.waitforimages';
// Width where images must fits in, for 2-up this gets divided by 2 // Width where images must fits in, for 2-up this gets divided by 2
const availWidth = 900; const availWidth = 900;
......
...@@ -6,5 +6,5 @@ import 'vendor/jquery.endless-scroll'; ...@@ -6,5 +6,5 @@ import 'vendor/jquery.endless-scroll';
import 'vendor/jquery.caret'; import 'vendor/jquery.caret';
import 'vendor/jquery.atwho'; import 'vendor/jquery.atwho';
import 'vendor/jquery.scrollTo'; import 'vendor/jquery.scrollTo';
import 'vendor/jquery.waitforimages'; import 'jquery.waitforimages';
import 'select2/select2'; import 'select2/select2';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
import { getLocationHash } from './lib/utils/url_utility'; import { getLocationHash } from './lib/utils/url_utility';
import FilesCommentButton from './files_comment_button'; import FilesCommentButton from './files_comment_button';
import SingleFileDiff from './single_file_diff'; import SingleFileDiff from './single_file_diff';
...@@ -69,7 +72,9 @@ export default class Diff { ...@@ -69,7 +72,9 @@ export default class Diff {
const view = file.data('view'); const view = file.data('view');
const params = { since, to, bottom, offset, unfold, view }; const params = { since, to, bottom, offset, unfold, view };
$.get(link, params, response => $target.parent().replaceWith(response)); axios.get(link, { params })
.then(({ data }) => $target.parent().replaceWith(data))
.catch(() => flash(__('An error occurred while loading diff')));
} }
openAnchoredDiff(cb) { openAnchoredDiff(cb) {
......
import { parseQueryStringIntoObject } from '~/lib/utils/common_utils';
import axios from '~/lib/utils/axios_utils';
import flash from '~/flash';
import { __ } from '~/locale';
export default class GpgBadges { export default class GpgBadges {
static fetch() { static fetch() {
const badges = $('.js-loading-gpg-badge'); const badges = $('.js-loading-gpg-badge');
...@@ -5,13 +10,13 @@ export default class GpgBadges { ...@@ -5,13 +10,13 @@ export default class GpgBadges {
badges.html('<i class="fa fa-spinner fa-spin"></i>'); badges.html('<i class="fa fa-spinner fa-spin"></i>');
$.get({ const params = parseQueryStringIntoObject(form.serialize());
url: form.data('signatures-path'), return axios.get(form.data('signatures-path'), { params })
data: form.serialize(), .then(({ data }) => {
}).done((response) => { data.signatures.forEach((signature) => {
response.signatures.forEach((signature) => {
badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html); badges.filter(`[data-commit-sha="${signature.commit_sha}"]`).replaceWith(signature.html);
}); });
}); })
.catch(() => flash(__('An error occurred while loading commits')));
} }
} }
import axios from './lib/utils/axios_utils';
import Api from './api'; import Api from './api';
import { normalizeCRLFHeaders } from './lib/utils/common_utils'; import { normalizeHeaders } from './lib/utils/common_utils';
export default function groupsSelect() { export default function groupsSelect() {
// Needs to be accessible in rspec // Needs to be accessible in rspec
...@@ -17,24 +18,23 @@ export default function groupsSelect() { ...@@ -17,24 +18,23 @@ export default function groupsSelect() {
dataType: 'json', dataType: 'json',
quietMillis: 250, quietMillis: 250,
transport(params) { transport(params) {
return $.ajax(params) axios[params.type.toLowerCase()](params.url, {
.then((data, status, xhr) => { params: params.data,
const results = data || []; })
.then((res) => {
const headers = normalizeCRLFHeaders(xhr.getAllResponseHeaders()); const results = res.data || [];
const headers = normalizeHeaders(res.headers);
const currentPage = parseInt(headers['X-PAGE'], 10) || 0; const currentPage = parseInt(headers['X-PAGE'], 10) || 0;
const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0; const totalPages = parseInt(headers['X-TOTAL-PAGES'], 10) || 0;
const more = currentPage < totalPages; const more = currentPage < totalPages;
return { params.success({
results, results,
pagination: { pagination: {
more, more,
}, },
}; });
}) }).catch(params.error);
.then(params.success)
.fail(params.error);
}, },
data(search, page) { data(search, page) {
return { return {
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, one-var, no-underscore-dangle, one-var-declaration-per-line, object-shorthand, no-unused-vars, no-new, comma-dangle, consistent-return, quotes, dot-notation, quote-props, prefer-arrow-callback, max-len */
import 'vendor/jquery.waitforimages';
import axios from './lib/utils/axios_utils'; import axios from './lib/utils/axios_utils';
import { addDelimiter } from './lib/utils/text_utility'; import { addDelimiter } from './lib/utils/text_utility';
import flash from './flash'; import flash from './flash';
......
/* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */ /* eslint-disable func-names, space-before-function-paren, no-var, prefer-rest-params, wrap-iife, quotes, no-underscore-dangle, one-var, one-var-declaration-per-line, consistent-return, dot-notation, quote-props, comma-dangle, object-shorthand, max-len, prefer-arrow-callback */
import 'vendor/jquery.waitforimages';
import { __ } from '~/locale'; import { __ } from '~/locale';
import TaskList from './task_list'; import TaskList from './task_list';
import MergeRequestTabs from './merge_request_tabs'; import MergeRequestTabs from './merge_request_tabs';
......
...@@ -14,7 +14,7 @@ export default () => { ...@@ -14,7 +14,7 @@ export default () => {
$('#tree-slider').waitForImages(() => $('#tree-slider').waitForImages(() =>
ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath)); ajaxGet(document.querySelector('.js-tree-content').dataset.logsPath));
const commitPipelineStatusEl = document.getElementById('commit-pipeline-status'); const commitPipelineStatusEl = document.querySelector('.js-commit-pipeline-status');
const statusLink = document.querySelector('.commit-actions .ci-status-link'); const statusLink = document.querySelector('.commit-actions .ci-status-link');
if (statusLink != null) { if (statusLink != null) {
statusLink.remove(); statusLink.remove();
......
/* global katex */ import { __ } from './locale';
import flash from './flash';
// Renders math using KaTeX in any element with the // Renders math using KaTeX in any element with the
// `js-render-math` class // `js-render-math` class
...@@ -8,15 +9,8 @@ ...@@ -8,15 +9,8 @@
// <code class="js-render-math"></div> // <code class="js-render-math"></div>
// //
import { __ } from './locale';
import axios from './lib/utils/axios_utils';
import flash from './flash';
// Only load once
let katexLoaded = false;
// Loop over all math elements and render math // Loop over all math elements and render math
function renderWithKaTeX(elements) { function renderWithKaTeX(elements, katex) {
elements.each(function katexElementsLoop() { elements.each(function katexElementsLoop() {
const mathNode = $('<span></span>'); const mathNode = $('<span></span>');
const $this = $(this); const $this = $(this);
...@@ -34,30 +28,10 @@ function renderWithKaTeX(elements) { ...@@ -34,30 +28,10 @@ function renderWithKaTeX(elements) {
export default function renderMath($els) { export default function renderMath($els) {
if (!$els.length) return; if (!$els.length) return;
Promise.all([
if (katexLoaded) { import(/* webpackChunkName: 'katex' */ 'katex'),
renderWithKaTeX($els); import(/* webpackChunkName: 'katex' */ 'katex/dist/katex.css'),
} else { ]).then(([katex]) => {
axios.get(gon.katex_css_url) renderWithKaTeX($els, katex);
.then(() => { }).catch(() => flash(__('An error occurred while rendering KaTeX')));
const css = $('<link>', {
rel: 'stylesheet',
type: 'text/css',
href: gon.katex_css_url,
});
css.appendTo('head');
})
.then(() => axios.get(gon.katex_js_url, {
responseType: 'text',
}))
.then(({ data }) => {
// Add katex js to our document
$.globalEval(data);
})
.then(() => {
katexLoaded = true;
renderWithKaTeX($els); // Run KaTeX
})
.catch(() => flash(__('An error occurred while rendering KaTeX')));
}
} }
...@@ -2,7 +2,7 @@ import _ from 'underscore'; ...@@ -2,7 +2,7 @@ import _ from 'underscore';
import '~/smart_interval'; import '~/smart_interval';
import timeTracker from './time_tracker'; import IssuableTimeTracker from './time_tracker.vue';
import Store from '../../stores/sidebar_store'; import Store from '../../stores/sidebar_store';
import Mediator from '../../sidebar_mediator'; import Mediator from '../../sidebar_mediator';
...@@ -16,7 +16,7 @@ export default { ...@@ -16,7 +16,7 @@ export default {
}; };
}, },
components: { components: {
'issuable-time-tracker': timeTracker, IssuableTimeTracker,
}, },
methods: { methods: {
listenForQuickActions() { listenForQuickActions() {
......
<script>
import timeTrackingHelpState from './help_state'; import timeTrackingHelpState from './help_state';
import timeTrackingCollapsedState from './collapsed_state'; import timeTrackingCollapsedState from './collapsed_state';
import timeTrackingSpentOnlyPane from './spent_only_pane'; import timeTrackingSpentOnlyPane from './spent_only_pane';
...@@ -8,7 +9,15 @@ import timeTrackingComparisonPane from './comparison_pane'; ...@@ -8,7 +9,15 @@ import timeTrackingComparisonPane from './comparison_pane';
import eventHub from '../../event_hub'; import eventHub from '../../event_hub';
export default { export default {
name: 'issuable-time-tracker', name: 'IssuableTimeTracker',
components: {
'time-tracking-collapsed-state': timeTrackingCollapsedState,
'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane,
'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane,
'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane,
'time-tracking-comparison-pane': timeTrackingComparisonPane,
'time-tracking-help-state': timeTrackingHelpState,
},
props: { props: {
time_estimate: { time_estimate: {
type: Number, type: Number,
...@@ -38,14 +47,6 @@ export default { ...@@ -38,14 +47,6 @@ export default {
showHelp: false, showHelp: false,
}; };
}, },
components: {
'time-tracking-collapsed-state': timeTrackingCollapsedState,
'time-tracking-estimate-only-pane': timeTrackingEstimateOnlyPane,
'time-tracking-spent-only-pane': timeTrackingSpentOnlyPane,
'time-tracking-no-tracking-pane': timeTrackingNoTrackingPane,
'time-tracking-comparison-pane': timeTrackingComparisonPane,
'time-tracking-help-state': timeTrackingHelpState,
},
computed: { computed: {
timeSpent() { timeSpent() {
return this.time_spent; return this.time_spent;
...@@ -81,6 +82,9 @@ export default { ...@@ -81,6 +82,9 @@ export default {
return !!this.showHelp; return !!this.showHelp;
}, },
}, },
created() {
eventHub.$on('timeTracker:updateData', this.update);
},
methods: { methods: {
toggleHelpState(show) { toggleHelpState(show) {
this.showHelp = show; this.showHelp = show;
...@@ -92,72 +96,73 @@ export default { ...@@ -92,72 +96,73 @@ export default {
this.human_time_spent = data.human_time_spent; this.human_time_spent = data.human_time_spent;
}, },
}, },
created() { };
eventHub.$on('timeTracker:updateData', this.update); </script>
},
template: ` <template>
<div <div
class="time_tracker time-tracking-component-wrap" class="time_tracker time-tracking-component-wrap"
v-cloak v-cloak
> >
<time-tracking-collapsed-state <time-tracking-collapsed-state
:show-comparison-state="showComparisonState" :show-comparison-state="showComparisonState"
:show-no-time-tracking-state="showNoTimeTrackingState" :show-no-time-tracking-state="showNoTimeTrackingState"
:show-help-state="showHelpState" :show-help-state="showHelpState"
:show-spent-only-state="showSpentOnlyState" :show-spent-only-state="showSpentOnlyState"
:show-estimate-only-state="showEstimateOnlyState" :show-estimate-only-state="showEstimateOnlyState"
:time-spent-human-readable="timeSpentHumanReadable"
:time-estimate-human-readable="timeEstimateHumanReadable"
/>
<div class="title hide-collapsed">
{{ __('Time tracking') }}
<div
class="help-button pull-right"
v-if="!showHelpState"
@click="toggleHelpState(true)"
>
<i
class="fa fa-question-circle"
aria-hidden="true"
>
</i>
</div>
<div
class="close-help-button pull-right"
v-if="showHelpState"
@click="toggleHelpState(false)"
>
<i
class="fa fa-close"
aria-hidden="true"
>
</i>
</div>
</div>
<div class="time-tracking-content hide-collapsed">
<time-tracking-estimate-only-pane
v-if="showEstimateOnlyState"
:time-estimate-human-readable="timeEstimateHumanReadable"
/>
<time-tracking-spent-only-pane
v-if="showSpentOnlyState"
:time-spent-human-readable="timeSpentHumanReadable"
/>
<time-tracking-no-tracking-pane
v-if="showNoTimeTrackingState"
/>
<time-tracking-comparison-pane
v-if="showComparisonState"
:time-estimate="timeEstimate"
:time-spent="timeSpent"
:time-spent-human-readable="timeSpentHumanReadable" :time-spent-human-readable="timeSpentHumanReadable"
:time-estimate-human-readable="timeEstimateHumanReadable" :time-estimate-human-readable="timeEstimateHumanReadable"
/> />
<div class="title hide-collapsed"> <transition name="help-state-toggle">
{{ __('Time tracking') }} <time-tracking-help-state
<div
class="help-button pull-right"
v-if="!showHelpState"
@click="toggleHelpState(true)"
>
<i
class="fa fa-question-circle"
aria-hidden="true"
/>
</div>
<div
class="close-help-button pull-right"
v-if="showHelpState" v-if="showHelpState"
@click="toggleHelpState(false)" :root-path="rootPath"
>
<i
class="fa fa-close"
aria-hidden="true"
/>
</div>
</div>
<div class="time-tracking-content hide-collapsed">
<time-tracking-estimate-only-pane
v-if="showEstimateOnlyState"
:time-estimate-human-readable="timeEstimateHumanReadable"
/> />
<time-tracking-spent-only-pane </transition>
v-if="showSpentOnlyState"
:time-spent-human-readable="timeSpentHumanReadable"
/>
<time-tracking-no-tracking-pane
v-if="showNoTimeTrackingState"
/>
<time-tracking-comparison-pane
v-if="showComparisonState"
:time-estimate="timeEstimate"
:time-spent="timeSpent"
:time-spent-human-readable="timeSpentHumanReadable"
:time-estimate-human-readable="timeEstimateHumanReadable"
/>
<transition name="help-state-toggle">
<time-tracking-help-state
v-if="showHelpState"
:rootPath="rootPath"
/>
</transition>
</div>
</div> </div>
`, </div>
}; </template>
...@@ -33,8 +33,9 @@ class Key < ActiveRecord::Base ...@@ -33,8 +33,9 @@ class Key < ActiveRecord::Base
after_destroy :refresh_user_cache after_destroy :refresh_user_cache
def key=(value) def key=(value)
write_attribute(:key, value.present? ? Gitlab::SSHPublicKey.sanitize(value) : nil) value&.delete!("\n\r")
value.strip! unless value.blank?
write_attribute(:key, value)
@public_key = nil @public_key = nil
end end
...@@ -96,7 +97,7 @@ class Key < ActiveRecord::Base ...@@ -96,7 +97,7 @@ class Key < ActiveRecord::Base
def generate_fingerprint def generate_fingerprint
self.fingerprint = nil self.fingerprint = nil
return unless public_key.valid? return unless self.key.present?
self.fingerprint = public_key.fingerprint self.fingerprint = public_key.fingerprint
end end
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
- id = variable&.id - id = variable&.id
- key = variable&.key - key = variable&.key
- value = variable&.value - value = variable&.value
- is_protected = variable && !only_key_value ? variable.protected : true - is_protected = variable && !only_key_value ? variable.protected : false
- id_input_name = "#{form_field}[variables_attributes][][id]" - id_input_name = "#{form_field}[variables_attributes][][id]"
- destroy_input_name = "#{form_field}[variables_attributes][][_destroy]" - destroy_input_name = "#{form_field}[variables_attributes][][_destroy]"
......
- breadcrumb_title "Labels" - breadcrumb_title "Labels"
- page_title 'New Label' - page_title 'New Label'
- header_title group_title(@group, 'Labels', group_labels_path(@group))
%h3.page-title %h3.page-title
New Label New Label
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
- if commit.status(ref) - if commit.status(ref)
= render_commit_status(commit, ref: ref) = render_commit_status(commit, ref: ref)
#commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } } .js-commit-pipeline-status{ data: { endpoint: pipelines_project_commit_path(project, commit.id) } }
= link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link" = link_to commit.short_id, link, class: "commit-sha btn btn-transparent btn-link"
= clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard")) = clipboard_button(text: commit.id, title: _("Copy commit SHA to clipboard"))
= link_to_browse_code(project, commit) = link_to_browse_code(project, commit)
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
Edit Edit
- if @project.group - if @project.group
= link_to promote_project_milestone_path(@milestone.project, @milestone), title: "Promote to Group Milestone", class: 'btn btn-grouped', data: { confirm: "You are about to promote #{@milestone.title} to a group level. This will make this milestone available to all projects inside #{@project.group.name}. The existing project milestone will be merged into the group level. This action cannot be reversed.", toggle: "tooltip" }, method: :post do = link_to promote_project_milestone_path(@milestone.project, @milestone), title: "Promote to Group Milestone", class: 'btn btn-grouped', data: { confirm: "Promoting #{@milestone.title} will make it available for all projects inside #{@project.group.name}. Existing project milestones with the same name will be merged. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
Promote Promote
- if @milestone.active? - if @milestone.active?
......
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
.pull-right.hidden-xs.hidden-sm .pull-right.hidden-xs.hidden-sm
- if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group) - if label.is_a?(ProjectLabel) && label.project.group && can?(current_user, :admin_label, label.project.group)
= link_to promote_project_label_path(label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "You are about to promote #{label.title} to a group level. This will make this milestone available to all projects inside #{label.project.group.name}. The existing project label will be merged into the group level. This action cannot be reversed.", toggle: "tooltip"}, method: :post do = link_to promote_project_label_path(label.project, label), title: "Promote to Group Label", class: 'btn btn-transparent btn-action', data: {confirm: "Promoting #{label.title} will make it available for all projects inside #{label.project.group.name}. Existing project labels with the same name will be merged. This action cannot be reversed.", toggle: "tooltip"}, method: :post do
%span.sr-only Promote to Group %span.sr-only Promote to Group
= sprite_icon('level-up') = sprite_icon('level-up')
- if can?(current_user, :admin_label, label) - if can?(current_user, :admin_label, label)
......
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
\ \
- if @project.group - if @project.group
= link_to promote_project_milestone_path(milestone.project, milestone), title: "Promote to Group Milestone", class: 'btn btn-xs btn-grouped', data: { confirm: "You are about to promote #{milestone.title} to a group level. This will make this milestone available to all projects inside #{@project.group.name}. The existing project milestone will be merged into the group level. This action cannot be reversed.", toggle: "tooltip" }, method: :post do = link_to promote_project_milestone_path(milestone.project, milestone), title: "Promote to Group Milestone", class: 'btn btn-xs btn-grouped', data: { confirm: "Promoting #{milestone.title} will make it available for all projects inside #{@project.group.name}. Existing project milestones with the same name will be merged. This action cannot be reversed.", toggle: "tooltip" }, method: :post do
Promote Promote
= link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped" = link_to 'Close Milestone', project_milestone_path(@project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close btn-grouped"
......
---
title: Sanitize extra blank spaces used when uploading a SSH key
merge_request: 40552
author:
type: fixed
---
title: Create empty wiki when import from GitLab and wiki is not there
merge_request:
author:
type: fixed
---
title: Fix breadcrumb on labels page for groups
merge_request: 17045
author: Onuwa Nnachi Isaac
type: fixed
---
title: Updated the katex library
merge_request: 15864
author:
type: other
---
title: Move IssuableTimeTracker vue component
merge_request: 16948
author: George Tsiolis
type: performance
...@@ -11,6 +11,7 @@ module Gitlab ...@@ -11,6 +11,7 @@ module Gitlab
require_dependency Rails.root.join('lib/gitlab/redis/queues') require_dependency Rails.root.join('lib/gitlab/redis/queues')
require_dependency Rails.root.join('lib/gitlab/redis/shared_state') require_dependency Rails.root.join('lib/gitlab/redis/shared_state')
require_dependency Rails.root.join('lib/gitlab/request_context') require_dependency Rails.root.join('lib/gitlab/request_context')
require_dependency Rails.root.join('lib/gitlab/current_settings')
# Settings in config/environments/* take precedence over those specified here. # Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers # Application configuration should go into files in config/initializers
...@@ -107,8 +108,6 @@ module Gitlab ...@@ -107,8 +108,6 @@ module Gitlab
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
config.assets.precompile << "katex.css"
config.assets.precompile << "katex.js"
config.assets.precompile << "xterm/xterm.css" config.assets.precompile << "xterm/xterm.css"
config.assets.precompile << "performance_bar.css" config.assets.precompile << "performance_bar.css"
config.assets.precompile << "lib/ace.js" config.assets.precompile << "lib/ace.js"
......
...@@ -153,6 +153,27 @@ var config = { ...@@ -153,6 +153,27 @@ var config = {
name: '[name].[hash].[ext]', name: '[name].[hash].[ext]',
} }
}, },
{
test: /katex.css$/,
include: /node_modules\/katex\/dist/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
name: '[name].[hash].[ext]'
}
},
],
},
{
test: /\.(eot|ttf|woff|woff2)$/,
include: /node_modules\/katex\/dist\/fonts/,
loader: 'file-loader',
options: {
name: '[name].[hash].[ext]',
}
},
{ {
test: /monaco-editor\/\w+\/vs\/loader\.js$/, test: /monaco-editor\/\w+\/vs\/loader\.js$/,
use: [ use: [
......
...@@ -13,8 +13,6 @@ module Gitlab ...@@ -13,8 +13,6 @@ module Gitlab
gon.relative_url_root = Gitlab.config.gitlab.relative_url_root gon.relative_url_root = Gitlab.config.gitlab.relative_url_root
gon.shortcuts_path = help_page_path('shortcuts') gon.shortcuts_path = help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.katex_css_url = ActionController::Base.helpers.asset_path('katex.css')
gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js')
gon.sentry_dsn = Gitlab::CurrentSettings.clientside_sentry_dsn if Gitlab::CurrentSettings.clientside_sentry_enabled gon.sentry_dsn = Gitlab::CurrentSettings.clientside_sentry_dsn if Gitlab::CurrentSettings.clientside_sentry_enabled
gon.gitlab_url = Gitlab.config.gitlab.url gon.gitlab_url = Gitlab.config.gitlab.url
gon.revision = Gitlab::REVISION gon.revision = Gitlab::REVISION
......
...@@ -50,9 +50,10 @@ module Gitlab ...@@ -50,9 +50,10 @@ module Gitlab
end end
def wiki_restorer def wiki_restorer
Gitlab::ImportExport::RepoRestorer.new(path_to_bundle: wiki_repo_path, Gitlab::ImportExport::WikiRestorer.new(path_to_bundle: wiki_repo_path,
shared: @shared, shared: @shared,
project: ProjectWiki.new(project_tree.restored_project)) project: ProjectWiki.new(project_tree.restored_project),
wiki_enabled: @project.wiki_enabled?)
end end
def uploads_restorer def uploads_restorer
......
module Gitlab
module ImportExport
class WikiRestorer < RepoRestorer
def initialize(project:, shared:, path_to_bundle:, wiki_enabled:)
super(project: project, shared: shared, path_to_bundle: path_to_bundle)
@wiki_enabled = wiki_enabled
end
def restore
@project.wiki if create_empty_wiki?
super
end
private
def create_empty_wiki?
!File.exist?(@path_to_bundle) && @wiki_enabled
end
end
end
end
...@@ -42,7 +42,7 @@ module Gitlab ...@@ -42,7 +42,7 @@ module Gitlab
key, value = parsed_field.first key, value = parsed_field.first
if value.nil? if value.nil?
value = open_file(tmp_path) value = open_file(tmp_path, @request.params["#{key}.name"])
@open_files << value @open_files << value
else else
value = decorate_params_value(value, @request.params[key], tmp_path) value = decorate_params_value(value, @request.params[key], tmp_path)
...@@ -70,7 +70,7 @@ module Gitlab ...@@ -70,7 +70,7 @@ module Gitlab
case path_value case path_value
when nil when nil
value_hash[path_key] = open_file(tmp_path) value_hash[path_key] = open_file(tmp_path, value_hash.dig(path_key, '.name'))
@open_files << value_hash[path_key] @open_files << value_hash[path_key]
value_hash value_hash
when Hash when Hash
...@@ -81,8 +81,8 @@ module Gitlab ...@@ -81,8 +81,8 @@ module Gitlab
end end
end end
def open_file(path) def open_file(path, name)
::UploadedFile.new(path, File.basename(path), 'application/octet-stream') ::UploadedFile.new(path, name || File.basename(path), 'application/octet-stream')
end end
end end
......
...@@ -6,7 +6,7 @@ module Gitlab ...@@ -6,7 +6,7 @@ module Gitlab
# This ensures we don't produce any errors that users can't do anything # This ensures we don't produce any errors that users can't do anything
# about themselves. # about themselves.
def self.enable? def self.enable?
Gitlab.com? || Rails.env.development? || Rails.env.test? Rails.env.development? || Rails.env.test?
end end
# Allows the current request to execute any number of SQL queries. # Allows the current request to execute any number of SQL queries.
......
...@@ -21,22 +21,6 @@ module Gitlab ...@@ -21,22 +21,6 @@ module Gitlab
technology(name)&.supported_sizes technology(name)&.supported_sizes
end end
def self.sanitize(key_content)
ssh_type, *parts = key_content.strip.split
return key_content if parts.empty?
parts.each_with_object("#{ssh_type} ").with_index do |(part, content), index|
content << part
if Gitlab::SSHPublicKey.new(content).valid?
break [content, parts[index + 1]].compact.join(' ') # Add the comment part if present
elsif parts.size == index + 1 # return original content if we've reached the last element
break key_content
end
end
end
attr_reader :key_text, :key attr_reader :key_text, :key
# Unqualified MD5 fingerprint for compatibility # Unqualified MD5 fingerprint for compatibility
...@@ -53,23 +37,23 @@ module Gitlab ...@@ -53,23 +37,23 @@ module Gitlab
end end
def valid? def valid?
key.present? && bits && technology.supported_sizes.include?(bits) key.present?
end end
def type def type
technology.name if key.present? technology.name if valid?
end end
def bits def bits
return if key.blank? return unless valid?
case type case type
when :rsa when :rsa
key.n&.num_bits key.n.num_bits
when :dsa when :dsa
key.p&.num_bits key.p.num_bits
when :ecdsa when :ecdsa
key.group.order&.num_bits key.group.order.num_bits
when :ed25519 when :ed25519
256 256
else else
......
...@@ -5,10 +5,6 @@ FactoryBot.define do ...@@ -5,10 +5,6 @@ FactoryBot.define do
title title
key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com' } key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate + ' dummy@gitlab.com' }
factory :key_without_comment do
key { Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate }
end
factory :deploy_key, class: 'DeployKey' factory :deploy_key, class: 'DeployKey'
factory :personal_key do factory :personal_key do
......
...@@ -126,7 +126,7 @@ describe('VariableList', () => { ...@@ -126,7 +126,7 @@ describe('VariableList', () => {
// Check for the correct default in the new row // Check for the correct default in the new row
const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected'); const $protectedInput = $wrapper.find('.js-row:last-child').find('.js-ci-variable-input-protected');
expect($protectedInput.val()).toBe('true'); expect($protectedInput.val()).toBe('false');
}) })
.then(done) .then(done)
.catch(done.fail); .catch(done.fail);
......
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import GpgBadges from '~/gpg_badges'; import GpgBadges from '~/gpg_badges';
describe('GpgBadges', () => { describe('GpgBadges', () => {
let mock;
const dummyCommitSha = 'n0m0rec0ffee'; const dummyCommitSha = 'n0m0rec0ffee';
const dummyBadgeHtml = 'dummy html'; const dummyBadgeHtml = 'dummy html';
const dummyResponse = { const dummyResponse = {
...@@ -11,38 +14,43 @@ describe('GpgBadges', () => { ...@@ -11,38 +14,43 @@ describe('GpgBadges', () => {
}; };
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios);
setFixtures(` setFixtures(`
<form
class="commits-search-form" data-signatures-path="/hello" action="/hello"
method="get">
<input name="utf8" type="hidden" value="✓">
<input type="search" name="search" id="commits-search"class="form-control search-text-input input-short">
</form>
<div class="parent-container"> <div class="parent-container">
<div class="js-loading-gpg-badge" data-commit-sha="${dummyCommitSha}"></div> <div class="js-loading-gpg-badge" data-commit-sha="${dummyCommitSha}"></div>
</div> </div>
`); `);
}); });
it('displays a loading spinner', () => { afterEach(() => {
spyOn($, 'get').and.returnValue({ mock.restore();
done() { });
// intentionally left blank
},
});
GpgBadges.fetch(); it('displays a loading spinner', (done) => {
mock.onGet('/hello').reply(200);
expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null); GpgBadges.fetch().then(() => {
const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin'); expect(document.querySelector('.js-loading-gpg-badge:empty')).toBe(null);
expect(spinners.length).toBe(1); const spinners = document.querySelectorAll('.js-loading-gpg-badge i.fa.fa-spinner.fa-spin');
expect(spinners.length).toBe(1);
done();
}).catch(done.fail);
}); });
it('replaces the loading spinner', () => { it('replaces the loading spinner', (done) => {
spyOn($, 'get').and.returnValue({ mock.onGet('/hello').reply(200, dummyResponse);
done(callback) {
callback(dummyResponse);
},
});
GpgBadges.fetch();
expect(document.querySelector('.js-loading-gpg-badge')).toBe(null); GpgBadges.fetch().then(() => {
const parentContainer = document.querySelector('.parent-container'); expect(document.querySelector('.js-loading-gpg-badge')).toBe(null);
expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml); const parentContainer = document.querySelector('.parent-container');
expect(parentContainer.innerHTML.trim()).toEqual(dummyBadgeHtml);
done();
}).catch(done.fail);
}); });
}); });
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
import Vue from 'vue'; import Vue from 'vue';
import timeTracker from '~/sidebar/components/time_tracking/time_tracker'; import timeTracker from '~/sidebar/components/time_tracking/time_tracker.vue';
function initTimeTrackingComponent(opts) { function initTimeTrackingComponent(opts) {
setFixtures(` setFixtures(`
......
import Vue from 'vue'; import Vue from 'vue';
import MarkdownComponent from '~/notebook/cells/markdown.vue'; import MarkdownComponent from '~/notebook/cells/markdown.vue';
import katex from 'vendor/katex'; import katex from 'katex';
const Component = Vue.extend(MarkdownComponent); const Component = Vue.extend(MarkdownComponent);
......
require 'spec_helper'
describe Gitlab::ImportExport::WikiRestorer do
describe 'restore a wiki Git repo' do
let!(:project_with_wiki) { create(:project, :wiki_repo) }
let!(:project_without_wiki) { create(:project) }
let!(:project) { create(:project) }
let(:export_path) { "#{Dir.tmpdir}/project_tree_saver_spec" }
let(:shared) { Gitlab::ImportExport::Shared.new(relative_path: project.full_path) }
let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_with_wiki, shared: shared) }
let(:bundle_path) { File.join(shared.export_path, Gitlab::ImportExport.project_bundle_filename) }
let(:restorer) do
described_class.new(path_to_bundle: bundle_path,
shared: shared,
project: project.wiki,
wiki_enabled: true)
end
before do
allow(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
bundler.save
end
after do
FileUtils.rm_rf(export_path)
Gitlab::Shell.new.remove_repository(project_with_wiki.wiki.repository_storage_path, project_with_wiki.wiki.disk_path)
Gitlab::Shell.new.remove_repository(project.wiki.repository_storage_path, project.wiki.disk_path)
end
it 'restores the wiki repo successfully' do
expect(restorer.restore).to be true
end
describe "no wiki in the bundle" do
let(:bundler) { Gitlab::ImportExport::WikiRepoSaver.new(project: project_without_wiki, shared: shared) }
it 'creates an empty wiki' do
expect(restorer.restore).to be true
expect(project.wiki_repository_exists?).to be true
end
end
end
end
...@@ -5,15 +5,17 @@ require 'tempfile' ...@@ -5,15 +5,17 @@ require 'tempfile'
describe Gitlab::Middleware::Multipart do describe Gitlab::Middleware::Multipart do
let(:app) { double(:app) } let(:app) { double(:app) }
let(:middleware) { described_class.new(app) } let(:middleware) { described_class.new(app) }
let(:original_filename) { 'filename' }
it 'opens top-level files' do it 'opens top-level files' do
Tempfile.open('top-level') do |tempfile| Tempfile.open('top-level') do |tempfile|
env = post_env({ 'file' => tempfile.path }, { 'file.name' => 'filename' }, Gitlab::Workhorse.secret, 'gitlab-workhorse') env = post_env({ 'file' => tempfile.path }, { 'file.name' => original_filename }, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env| expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['file'] file = Rack::Request.new(env).params['file']
expect(file).to be_a(::UploadedFile) expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path) expect(file.path).to eq(tempfile.path)
expect(file.original_filename).to eq(original_filename)
end end
middleware.call(env) middleware.call(env)
...@@ -34,13 +36,14 @@ describe Gitlab::Middleware::Multipart do ...@@ -34,13 +36,14 @@ describe Gitlab::Middleware::Multipart do
it 'opens files one level deep' do it 'opens files one level deep' do
Tempfile.open('one-level') do |tempfile| Tempfile.open('one-level') do |tempfile|
in_params = { 'user' => { 'avatar' => { '.name' => 'filename' } } } in_params = { 'user' => { 'avatar' => { '.name' => original_filename } } }
env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') env = post_env({ 'user[avatar]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env| expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['user']['avatar'] file = Rack::Request.new(env).params['user']['avatar']
expect(file).to be_a(::UploadedFile) expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path) expect(file.path).to eq(tempfile.path)
expect(file.original_filename).to eq(original_filename)
end end
middleware.call(env) middleware.call(env)
...@@ -49,13 +52,14 @@ describe Gitlab::Middleware::Multipart do ...@@ -49,13 +52,14 @@ describe Gitlab::Middleware::Multipart do
it 'opens files two levels deep' do it 'opens files two levels deep' do
Tempfile.open('two-levels') do |tempfile| Tempfile.open('two-levels') do |tempfile|
in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => 'filename' } } } } in_params = { 'project' => { 'milestone' => { 'themesong' => { '.name' => original_filename } } } }
env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse') env = post_env({ 'project[milestone][themesong]' => tempfile.path }, in_params, Gitlab::Workhorse.secret, 'gitlab-workhorse')
expect(app).to receive(:call) do |env| expect(app).to receive(:call) do |env|
file = Rack::Request.new(env).params['project']['milestone']['themesong'] file = Rack::Request.new(env).params['project']['milestone']['themesong']
expect(file).to be_a(::UploadedFile) expect(file).to be_a(::UploadedFile)
expect(file.path).to eq(tempfile.path) expect(file.path).to eq(tempfile.path)
expect(file.original_filename).to eq(original_filename)
end end
middleware.call(env) middleware.call(env)
......
...@@ -12,14 +12,16 @@ describe Gitlab::QueryLimiting do ...@@ -12,14 +12,16 @@ describe Gitlab::QueryLimiting do
expect(described_class.enable?).to eq(true) expect(described_class.enable?).to eq(true)
end end
it 'returns true on GitLab.com' do it 'returns false on GitLab.com' do
expect(Rails.env).to receive(:development?).and_return(false)
expect(Rails.env).to receive(:test?).and_return(false)
allow(Gitlab).to receive(:com?).and_return(true) allow(Gitlab).to receive(:com?).and_return(true)
expect(described_class.enable?).to eq(true) expect(described_class.enable?).to eq(false)
end end
it 'returns true in a non GitLab.com' do it 'returns false in a non GitLab.com' do
expect(Gitlab).to receive(:com?).and_return(false) allow(Gitlab).to receive(:com?).and_return(false)
expect(Rails.env).to receive(:development?).and_return(false) expect(Rails.env).to receive(:development?).and_return(false)
expect(Rails.env).to receive(:test?).and_return(false) expect(Rails.env).to receive(:test?).and_return(false)
......
...@@ -37,41 +37,6 @@ describe Gitlab::SSHPublicKey, lib: true do ...@@ -37,41 +37,6 @@ describe Gitlab::SSHPublicKey, lib: true do
end end
end end
describe '.sanitize(key_content)' do
let(:content) { build(:key).key }
context 'when key has blank space characters' do
it 'removes the extra blank space characters' do
unsanitized = content.insert(100, "\n")
.insert(40, "\r\n")
.insert(30, ' ')
sanitized = described_class.sanitize(unsanitized)
_, body = sanitized.split
expect(sanitized).not_to eq(unsanitized)
expect(body).not_to match(/\s/)
end
end
context "when key doesn't have blank space characters" do
it "doesn't modify the content" do
sanitized = described_class.sanitize(content)
expect(sanitized).to eq(content)
end
end
context "when key is invalid" do
it 'returns the original content' do
unsanitized = "ssh-foo any content=="
sanitized = described_class.sanitize(unsanitized)
expect(sanitized).to eq(unsanitized)
end
end
end
describe '#valid?' do describe '#valid?' do
subject { public_key } subject { public_key }
......
...@@ -1413,7 +1413,7 @@ describe Ci::Build do ...@@ -1413,7 +1413,7 @@ describe Ci::Build do
[ [
{ key: 'CI', value: 'true', public: true }, { key: 'CI', value: 'true', public: true },
{ key: 'GITLAB_CI', value: 'true', public: true }, { key: 'GITLAB_CI', value: 'true', public: true },
{ key: 'GITLAB_FEATURES', value: '', public: true }, { key: 'GITLAB_FEATURES', value: project.namespace.features.join(','), public: true },
{ key: 'CI_SERVER_NAME', value: 'GitLab', public: true }, { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
{ key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true }, { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
{ key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true }, { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
......
...@@ -72,52 +72,15 @@ describe Key, :mailer do ...@@ -72,52 +72,15 @@ describe Key, :mailer do
expect(build(:key)).to be_valid expect(build(:key)).to be_valid
end end
it 'rejects the unfingerprintable key (not a key)' do it 'accepts a key with newline charecters after stripping them' do
expect(build(:key, key: 'ssh-rsa an-invalid-key==')).not_to be_valid key = build(:key)
end key.key = key.key.insert(100, "\n")
key.key = key.key.insert(40, "\r\n")
where(:factory, :chars, :expected_sections) do expect(key).to be_valid
[
[:key, ["\n", "\r\n"], 3],
[:key, [' ', ' '], 3],
[:key_without_comment, [' ', ' '], 2]
]
end
with_them do
let!(:key) { create(factory) }
let!(:original_fingerprint) { key.fingerprint }
it 'accepts a key with blank space characters after stripping them' do
modified_key = key.key.insert(100, chars.first).insert(40, chars.last)
_, content = modified_key.split
key.update!(key: modified_key)
expect(key).to be_valid
expect(key.key.split.size).to eq(expected_sections)
expect(content).not_to match(/\s/)
expect(original_fingerprint).to eq(key.fingerprint)
end
end
end
context 'validate size' do
where(:key_content, :result) do
[
[Spec::Support::Helpers::KeyGeneratorHelper.new(512).generate, false],
[Spec::Support::Helpers::KeyGeneratorHelper.new(8192).generate, false],
[Spec::Support::Helpers::KeyGeneratorHelper.new(1024).generate, true]
]
end end
with_them do it 'rejects the unfingerprintable key (not a key)' do
it 'validates the size of the key' do expect(build(:key, key: 'ssh-rsa an-invalid-key==')).not_to be_valid
key = build(:key, key: key_content)
expect(key.valid?).to eq(result)
end
end end
end end
......
...@@ -41,13 +41,13 @@ shared_examples 'variable list' do ...@@ -41,13 +41,13 @@ shared_examples 'variable list' do
end end
end end
it 'adds new unprotected variable' do it 'adds new protected variable' do
page.within('.js-ci-variable-list-section .js-row:last-child') do page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('key') find('.js-ci-variable-input-key').set('key')
find('.js-ci-variable-input-value').set('key value') find('.js-ci-variable-input-value').set('key value')
find('.ci-variable-protected-item .js-project-feature-toggle').click find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false') expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end end
click_button('Save variables') click_button('Save variables')
...@@ -59,7 +59,7 @@ shared_examples 'variable list' do ...@@ -59,7 +59,7 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do page.within('.js-ci-variable-list-section .js-row:nth-child(1)') do
expect(find('.js-ci-variable-input-key').value).to eq('key') expect(find('.js-ci-variable-input-key').value).to eq('key')
expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value') expect(find('.js-ci-variable-input-value', visible: false).value).to eq('key value')
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false') expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end end
end end
...@@ -143,7 +143,6 @@ shared_examples 'variable list' do ...@@ -143,7 +143,6 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section .js-row:last-child') do page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('unprotected_key') find('.js-ci-variable-input-key').set('unprotected_key')
find('.js-ci-variable-input-value').set('unprotected_value') find('.js-ci-variable-input-value').set('unprotected_value')
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false') expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('false')
end end
...@@ -178,6 +177,7 @@ shared_examples 'variable list' do ...@@ -178,6 +177,7 @@ shared_examples 'variable list' do
page.within('.js-ci-variable-list-section .js-row:last-child') do page.within('.js-ci-variable-list-section .js-row:last-child') do
find('.js-ci-variable-input-key').set('protected_key') find('.js-ci-variable-input-key').set('protected_key')
find('.js-ci-variable-input-value').set('protected_value') find('.js-ci-variable-input-value').set('protected_value')
find('.ci-variable-protected-item .js-project-feature-toggle').click
expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true') expect(find('.js-ci-variable-input-protected', visible: false).value).to eq('true')
end end
......
/*
* waitForImages 1.4
* -----------------
* Provides a callback when all images have loaded in your given selector.
* http://www.alexanderdickson.com/
*
*
* Copyright (c) 2011 Alex Dickson
* Licensed under the MIT licenses.
* See website for more info.
*
*/
;(function($) {
// Namespace all events.
var eventNamespace = 'waitForImages';
// CSS properties which contain references to images.
$.waitForImages = {
hasImageProperties: [
'backgroundImage',
'listStyleImage',
'borderImage',
'borderCornerImage'
]
};
// Custom selector to find `img` elements that have a valid `src` attribute and have not already loaded.
$.expr[':'].uncached = function(obj) {
// Ensure we are dealing with an `img` element with a valid `src` attribute.
if ( ! $(obj).is('img[src!=""]')) {
return false;
}
// Firefox's `complete` property will always be`true` even if the image has not been downloaded.
// Doing it this way works in Firefox.
var img = document.createElement('img');
img.src = obj.src;
return ! img.complete;
};
$.fn.waitForImages = function(finishedCallback, eachCallback, waitForAll) {
// Handle options object.
if ($.isPlainObject(arguments[0])) {
eachCallback = finishedCallback.each;
waitForAll = finishedCallback.waitForAll;
finishedCallback = finishedCallback.finished;
}
// Handle missing callbacks.
finishedCallback = finishedCallback || $.noop;
eachCallback = eachCallback || $.noop;
// Convert waitForAll to Boolean
waitForAll = !! waitForAll;
// Ensure callbacks are functions.
if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
throw new TypeError('An invalid callback was supplied.');
};
return this.each(function() {
// Build a list of all imgs, dependent on what images will be considered.
var obj = $(this),
allImgs = [];
if (waitForAll) {
// CSS properties which may contain an image.
var hasImgProperties = $.waitForImages.hasImageProperties || [],
matchUrl = /url\((['"]?)(.*?)\1\)/g;
// Get all elements, as any one of them could have a background image.
obj.find('*').each(function() {
var element = $(this);
// If an `img` element, add it. But keep iterating in case it has a background image too.
if (element.is('img:uncached')) {
allImgs.push({
src: element.attr('src'),
element: element[0]
});
}
$.each(hasImgProperties, function(i, property) {
var propertyValue = element.css(property);
// If it doesn't contain this property, skip.
if ( ! propertyValue) {
return true;
}
// Get all url() of this element.
var match;
while (match = matchUrl.exec(propertyValue)) {
allImgs.push({
src: match[2],
element: element[0]
});
};
});
});
} else {
// For images only, the task is simpler.
obj
.find('img:uncached')
.each(function() {
allImgs.push({
src: this.src,
element: this
});
});
};
var allImgsLength = allImgs.length,
allImgsLoaded = 0;
// If no images found, don't bother.
if (allImgsLength == 0) {
finishedCallback.call(obj[0]);
};
$.each(allImgs, function(i, img) {
var image = new Image;
// Handle the image loading and error with the same callback.
$(image).bind('load.' + eventNamespace + ' error.' + eventNamespace, function(event) {
allImgsLoaded++;
// If an error occurred with loading the image, set the third argument accordingly.
eachCallback.call(img.element, allImgsLoaded, allImgsLength, event.type == 'load');
if (allImgsLoaded == allImgsLength) {
finishedCallback.call(obj[0]);
return false;
};
});
image.src = img.src;
});
});
};
})(jQuery);
This source diff could not be displayed because it is too large. You can view the blob instead.
/*
The MIT License (MIT)
Copyright (c) 2015 Khan Academy
This software also uses portions of the underscore.js project, which is
MIT licensed with the following copyright:
Copyright (c) 2009-2015 Jeremy Ashkenas, DocumentCloud and Investigative
Reporters & Editors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
Here is how to build a version of KaTeX that works with gitlab.
The problem is that the standard procedure for changing font location doesn't work for the empty string.
1. Clone KaTeX. Anything later than 4fb9445a9 (is merged into master) will do.
2. make (requires node)
3. sed -e 's,fonts/,,' -e 's/url\(([^)]*)\)/url(font-path\1)/g' build/katex.css > build/katex.scss
4. Copy build/katex.js to gitlab/vendor/assets/javascripts/katex.js,
build/katex.scss to gitlab/vendor/assets/stylesheets/katex.scss and
fonts/* to gitlab/vendor/assets/fonts/.
*/
@font-face {
font-family: 'KaTeX_AMS';
src: url(font-path('KaTeX_AMS-Regular.eot'));
src: url(font-path('KaTeX_AMS-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_AMS-Regular.woff2')) format('woff2'), url(font-path('KaTeX_AMS-Regular.woff')) format('woff'), url(font-path('KaTeX_AMS-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Caligraphic';
src: url(font-path('KaTeX_Caligraphic-Bold.eot'));
src: url(font-path('KaTeX_Caligraphic-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Bold.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Bold.ttf')) format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Caligraphic';
src: url(font-path('KaTeX_Caligraphic-Regular.eot'));
src: url(font-path('KaTeX_Caligraphic-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Caligraphic-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Caligraphic-Regular.woff')) format('woff'), url(font-path('KaTeX_Caligraphic-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Fraktur';
src: url(font-path('KaTeX_Fraktur-Bold.eot'));
src: url(font-path('KaTeX_Fraktur-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Bold.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Bold.ttf')) format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Fraktur';
src: url(font-path('KaTeX_Fraktur-Regular.eot'));
src: url(font-path('KaTeX_Fraktur-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Fraktur-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Fraktur-Regular.woff')) format('woff'), url(font-path('KaTeX_Fraktur-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Main';
src: url(font-path('KaTeX_Main-Bold.eot'));
src: url(font-path('KaTeX_Main-Bold.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Bold.woff2')) format('woff2'), url(font-path('KaTeX_Main-Bold.woff')) format('woff'), url(font-path('KaTeX_Main-Bold.ttf')) format('truetype');
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Main';
src: url(font-path('KaTeX_Main-Italic.eot'));
src: url(font-path('KaTeX_Main-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Main-Italic.woff')) format('woff'), url(font-path('KaTeX_Main-Italic.ttf')) format('truetype');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'KaTeX_Main';
src: url(font-path('KaTeX_Main-Regular.eot'));
src: url(font-path('KaTeX_Main-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Main-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Main-Regular.woff')) format('woff'), url(font-path('KaTeX_Main-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Math';
src: url(font-path('KaTeX_Math-Italic.eot'));
src: url(font-path('KaTeX_Math-Italic.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Math-Italic.woff2')) format('woff2'), url(font-path('KaTeX_Math-Italic.woff')) format('woff'), url(font-path('KaTeX_Math-Italic.ttf')) format('truetype');
font-weight: 400;
font-style: italic;
}
@font-face {
font-family: 'KaTeX_SansSerif';
src: url(font-path('KaTeX_SansSerif-Regular.eot'));
src: url(font-path('KaTeX_SansSerif-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_SansSerif-Regular.woff2')) format('woff2'), url(font-path('KaTeX_SansSerif-Regular.woff')) format('woff'), url(font-path('KaTeX_SansSerif-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Script';
src: url(font-path('KaTeX_Script-Regular.eot'));
src: url(font-path('KaTeX_Script-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Script-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Script-Regular.woff')) format('woff'), url(font-path('KaTeX_Script-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size1';
src: url(font-path('KaTeX_Size1-Regular.eot'));
src: url(font-path('KaTeX_Size1-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size1-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size1-Regular.woff')) format('woff'), url(font-path('KaTeX_Size1-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size2';
src: url(font-path('KaTeX_Size2-Regular.eot'));
src: url(font-path('KaTeX_Size2-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size2-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size2-Regular.woff')) format('woff'), url(font-path('KaTeX_Size2-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size3';
src: url(font-path('KaTeX_Size3-Regular.eot'));
src: url(font-path('KaTeX_Size3-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size3-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size3-Regular.woff')) format('woff'), url(font-path('KaTeX_Size3-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size4';
src: url(font-path('KaTeX_Size4-Regular.eot'));
src: url(font-path('KaTeX_Size4-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Size4-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Size4-Regular.woff')) format('woff'), url(font-path('KaTeX_Size4-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Typewriter';
src: url(font-path('KaTeX_Typewriter-Regular.eot'));
src: url(font-path('KaTeX_Typewriter-Regular.eot#iefix')) format('embedded-opentype'), url(font-path('KaTeX_Typewriter-Regular.woff2')) format('woff2'), url(font-path('KaTeX_Typewriter-Regular.woff')) format('woff'), url(font-path('KaTeX_Typewriter-Regular.ttf')) format('truetype');
font-weight: 400;
font-style: normal;
}
.katex-display {
display: block;
margin: 1em 0;
text-align: center;
}
.katex-display > .katex {
display: inline-block;
text-align: initial;
}
.katex {
font: normal 1.21em KaTeX_Main, Times New Roman, serif;
line-height: 1.2;
white-space: nowrap;
text-indent: 0;
}
.katex .katex-html {
display: inline-block;
}
.katex .katex-mathml {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
}
.katex .base {
display: inline-block;
}
.katex .strut {
display: inline-block;
}
.katex .mathit {
font-family: KaTeX_Math;
font-style: italic;
}
.katex .mathbf {
font-family: KaTeX_Main;
font-weight: 600;
}
.katex .amsrm {
font-family: KaTeX_AMS;
}
.katex .mathbb {
font-family: KaTeX_AMS;
}
.katex .mathcal {
font-family: KaTeX_Caligraphic;
}
.katex .mathfrak {
font-family: KaTeX_Fraktur;
}
.katex .mathtt {
font-family: KaTeX_Typewriter;
}
.katex .mathscr {
font-family: KaTeX_Script;
}
.katex .mathsf {
font-family: KaTeX_SansSerif;
}
.katex .mainit {
font-family: KaTeX_Main;
font-style: italic;
}
.katex .textstyle > .mord + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mord + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .mord + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mord + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mop + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mbin + .mord {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .mop {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .mopen {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .minner {
margin-left: 0.22222em;
}
.katex .textstyle > .mrel + .mord {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .mop {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .mopen {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .minner {
margin-left: 0.27778em;
}
.katex .textstyle > .mclose + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mclose + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .mclose + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mclose + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mrel {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mopen {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mclose {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mpunct {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .minner + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .minner + .mopen {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mpunct {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .minner {
margin-left: 0.16667em;
}
.katex .mord + .mop {
margin-left: 0.16667em;
}
.katex .mop + .mord {
margin-left: 0.16667em;
}
.katex .mop + .mop {
margin-left: 0.16667em;
}
.katex .mclose + .mop {
margin-left: 0.16667em;
}
.katex .minner + .mop {
margin-left: 0.16667em;
}
.katex .reset-textstyle.textstyle {
font-size: 1em;
}
.katex .reset-textstyle.scriptstyle {
font-size: 0.7em;
}
.katex .reset-textstyle.scriptscriptstyle {
font-size: 0.5em;
}
.katex .reset-scriptstyle.textstyle {
font-size: 1.42857em;
}
.katex .reset-scriptstyle.scriptstyle {
font-size: 1em;
}
.katex .reset-scriptstyle.scriptscriptstyle {
font-size: 0.71429em;
}
.katex .reset-scriptscriptstyle.textstyle {
font-size: 2em;
}
.katex .reset-scriptscriptstyle.scriptstyle {
font-size: 1.4em;
}
.katex .reset-scriptscriptstyle.scriptscriptstyle {
font-size: 1em;
}
.katex .style-wrap {
position: relative;
}
.katex .vlist {
display: inline-block;
}
.katex .vlist > span {
display: block;
height: 0;
position: relative;
}
.katex .vlist > span > span {
display: inline-block;
}
.katex .vlist .baseline-fix {
display: inline-table;
table-layout: fixed;
}
.katex .msupsub {
text-align: left;
}
.katex .mfrac > span > span {
text-align: center;
}
.katex .mfrac .frac-line {
width: 100%;
}
.katex .mfrac .frac-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .mfrac .frac-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .mspace {
display: inline-block;
}
.katex .mspace.negativethinspace {
margin-left: -0.16667em;
}
.katex .mspace.thinspace {
width: 0.16667em;
}
.katex .mspace.mediumspace {
width: 0.22222em;
}
.katex .mspace.thickspace {
width: 0.27778em;
}
.katex .mspace.enspace {
width: 0.5em;
}
.katex .mspace.quad {
width: 1em;
}
.katex .mspace.qquad {
width: 2em;
}
.katex .llap,
.katex .rlap {
width: 0;
position: relative;
}
.katex .llap > .inner,
.katex .rlap > .inner {
position: absolute;
}
.katex .llap > .fix,
.katex .rlap > .fix {
display: inline-block;
}
.katex .llap > .inner {
right: 0;
}
.katex .rlap > .inner {
left: 0;
}
.katex .katex-logo .a {
font-size: 0.75em;
margin-left: -0.32em;
position: relative;
top: -0.2em;
}
.katex .katex-logo .t {
margin-left: -0.23em;
}
.katex .katex-logo .e {
margin-left: -0.1667em;
position: relative;
top: 0.2155em;
}
.katex .katex-logo .x {
margin-left: -0.125em;
}
.katex .rule {
display: inline-block;
border: solid 0;
position: relative;
}
.katex .overline .overline-line,
.katex .underline .underline-line {
width: 100%;
}
.katex .overline .overline-line:before,
.katex .underline .underline-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .overline .overline-line:after,
.katex .underline .underline-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .sqrt > .sqrt-sign {
position: relative;
}
.katex .sqrt .sqrt-line {
width: 100%;
}
.katex .sqrt .sqrt-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .sqrt .sqrt-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .sqrt > .root {
margin-left: 0.27777778em;
margin-right: -0.55555556em;
}
.katex .sizing,
.katex .fontsize-ensurer {
display: inline-block;
}
.katex .sizing.reset-size1.size1,
.katex .fontsize-ensurer.reset-size1.size1 {
font-size: 1em;
}
.katex .sizing.reset-size1.size2,
.katex .fontsize-ensurer.reset-size1.size2 {
font-size: 1.4em;
}
.katex .sizing.reset-size1.size3,
.katex .fontsize-ensurer.reset-size1.size3 {
font-size: 1.6em;
}
.katex .sizing.reset-size1.size4,
.katex .fontsize-ensurer.reset-size1.size4 {
font-size: 1.8em;
}
.katex .sizing.reset-size1.size5,
.katex .fontsize-ensurer.reset-size1.size5 {
font-size: 2em;
}
.katex .sizing.reset-size1.size6,
.katex .fontsize-ensurer.reset-size1.size6 {
font-size: 2.4em;
}
.katex .sizing.reset-size1.size7,
.katex .fontsize-ensurer.reset-size1.size7 {
font-size: 2.88em;
}
.katex .sizing.reset-size1.size8,
.katex .fontsize-ensurer.reset-size1.size8 {
font-size: 3.46em;
}
.katex .sizing.reset-size1.size9,
.katex .fontsize-ensurer.reset-size1.size9 {
font-size: 4.14em;
}
.katex .sizing.reset-size1.size10,
.katex .fontsize-ensurer.reset-size1.size10 {
font-size: 4.98em;
}
.katex .sizing.reset-size2.size1,
.katex .fontsize-ensurer.reset-size2.size1 {
font-size: 0.71428571em;
}
.katex .sizing.reset-size2.size2,
.katex .fontsize-ensurer.reset-size2.size2 {
font-size: 1em;
}
.katex .sizing.reset-size2.size3,
.katex .fontsize-ensurer.reset-size2.size3 {
font-size: 1.14285714em;
}
.katex .sizing.reset-size2.size4,
.katex .fontsize-ensurer.reset-size2.size4 {
font-size: 1.28571429em;
}
.katex .sizing.reset-size2.size5,
.katex .fontsize-ensurer.reset-size2.size5 {
font-size: 1.42857143em;
}
.katex .sizing.reset-size2.size6,
.katex .fontsize-ensurer.reset-size2.size6 {
font-size: 1.71428571em;
}
.katex .sizing.reset-size2.size7,
.katex .fontsize-ensurer.reset-size2.size7 {
font-size: 2.05714286em;
}
.katex .sizing.reset-size2.size8,
.katex .fontsize-ensurer.reset-size2.size8 {
font-size: 2.47142857em;
}
.katex .sizing.reset-size2.size9,
.katex .fontsize-ensurer.reset-size2.size9 {
font-size: 2.95714286em;
}
.katex .sizing.reset-size2.size10,
.katex .fontsize-ensurer.reset-size2.size10 {
font-size: 3.55714286em;
}
.katex .sizing.reset-size3.size1,
.katex .fontsize-ensurer.reset-size3.size1 {
font-size: 0.625em;
}
.katex .sizing.reset-size3.size2,
.katex .fontsize-ensurer.reset-size3.size2 {
font-size: 0.875em;
}
.katex .sizing.reset-size3.size3,
.katex .fontsize-ensurer.reset-size3.size3 {
font-size: 1em;
}
.katex .sizing.reset-size3.size4,
.katex .fontsize-ensurer.reset-size3.size4 {
font-size: 1.125em;
}
.katex .sizing.reset-size3.size5,
.katex .fontsize-ensurer.reset-size3.size5 {
font-size: 1.25em;
}
.katex .sizing.reset-size3.size6,
.katex .fontsize-ensurer.reset-size3.size6 {
font-size: 1.5em;
}
.katex .sizing.reset-size3.size7,
.katex .fontsize-ensurer.reset-size3.size7 {
font-size: 1.8em;
}
.katex .sizing.reset-size3.size8,
.katex .fontsize-ensurer.reset-size3.size8 {
font-size: 2.1625em;
}
.katex .sizing.reset-size3.size9,
.katex .fontsize-ensurer.reset-size3.size9 {
font-size: 2.5875em;
}
.katex .sizing.reset-size3.size10,
.katex .fontsize-ensurer.reset-size3.size10 {
font-size: 3.1125em;
}
.katex .sizing.reset-size4.size1,
.katex .fontsize-ensurer.reset-size4.size1 {
font-size: 0.55555556em;
}
.katex .sizing.reset-size4.size2,
.katex .fontsize-ensurer.reset-size4.size2 {
font-size: 0.77777778em;
}
.katex .sizing.reset-size4.size3,
.katex .fontsize-ensurer.reset-size4.size3 {
font-size: 0.88888889em;
}
.katex .sizing.reset-size4.size4,
.katex .fontsize-ensurer.reset-size4.size4 {
font-size: 1em;
}
.katex .sizing.reset-size4.size5,
.katex .fontsize-ensurer.reset-size4.size5 {
font-size: 1.11111111em;
}
.katex .sizing.reset-size4.size6,
.katex .fontsize-ensurer.reset-size4.size6 {
font-size: 1.33333333em;
}
.katex .sizing.reset-size4.size7,
.katex .fontsize-ensurer.reset-size4.size7 {
font-size: 1.6em;
}
.katex .sizing.reset-size4.size8,
.katex .fontsize-ensurer.reset-size4.size8 {
font-size: 1.92222222em;
}
.katex .sizing.reset-size4.size9,
.katex .fontsize-ensurer.reset-size4.size9 {
font-size: 2.3em;
}
.katex .sizing.reset-size4.size10,
.katex .fontsize-ensurer.reset-size4.size10 {
font-size: 2.76666667em;
}
.katex .sizing.reset-size5.size1,
.katex .fontsize-ensurer.reset-size5.size1 {
font-size: 0.5em;
}
.katex .sizing.reset-size5.size2,
.katex .fontsize-ensurer.reset-size5.size2 {
font-size: 0.7em;
}
.katex .sizing.reset-size5.size3,
.katex .fontsize-ensurer.reset-size5.size3 {
font-size: 0.8em;
}
.katex .sizing.reset-size5.size4,
.katex .fontsize-ensurer.reset-size5.size4 {
font-size: 0.9em;
}
.katex .sizing.reset-size5.size5,
.katex .fontsize-ensurer.reset-size5.size5 {
font-size: 1em;
}
.katex .sizing.reset-size5.size6,
.katex .fontsize-ensurer.reset-size5.size6 {
font-size: 1.2em;
}
.katex .sizing.reset-size5.size7,
.katex .fontsize-ensurer.reset-size5.size7 {
font-size: 1.44em;
}
.katex .sizing.reset-size5.size8,
.katex .fontsize-ensurer.reset-size5.size8 {
font-size: 1.73em;
}
.katex .sizing.reset-size5.size9,
.katex .fontsize-ensurer.reset-size5.size9 {
font-size: 2.07em;
}
.katex .sizing.reset-size5.size10,
.katex .fontsize-ensurer.reset-size5.size10 {
font-size: 2.49em;
}
.katex .sizing.reset-size6.size1,
.katex .fontsize-ensurer.reset-size6.size1 {
font-size: 0.41666667em;
}
.katex .sizing.reset-size6.size2,
.katex .fontsize-ensurer.reset-size6.size2 {
font-size: 0.58333333em;
}
.katex .sizing.reset-size6.size3,
.katex .fontsize-ensurer.reset-size6.size3 {
font-size: 0.66666667em;
}
.katex .sizing.reset-size6.size4,
.katex .fontsize-ensurer.reset-size6.size4 {
font-size: 0.75em;
}
.katex .sizing.reset-size6.size5,
.katex .fontsize-ensurer.reset-size6.size5 {
font-size: 0.83333333em;
}
.katex .sizing.reset-size6.size6,
.katex .fontsize-ensurer.reset-size6.size6 {
font-size: 1em;
}
.katex .sizing.reset-size6.size7,
.katex .fontsize-ensurer.reset-size6.size7 {
font-size: 1.2em;
}
.katex .sizing.reset-size6.size8,
.katex .fontsize-ensurer.reset-size6.size8 {
font-size: 1.44166667em;
}
.katex .sizing.reset-size6.size9,
.katex .fontsize-ensurer.reset-size6.size9 {
font-size: 1.725em;
}
.katex .sizing.reset-size6.size10,
.katex .fontsize-ensurer.reset-size6.size10 {
font-size: 2.075em;
}
.katex .sizing.reset-size7.size1,
.katex .fontsize-ensurer.reset-size7.size1 {
font-size: 0.34722222em;
}
.katex .sizing.reset-size7.size2,
.katex .fontsize-ensurer.reset-size7.size2 {
font-size: 0.48611111em;
}
.katex .sizing.reset-size7.size3,
.katex .fontsize-ensurer.reset-size7.size3 {
font-size: 0.55555556em;
}
.katex .sizing.reset-size7.size4,
.katex .fontsize-ensurer.reset-size7.size4 {
font-size: 0.625em;
}
.katex .sizing.reset-size7.size5,
.katex .fontsize-ensurer.reset-size7.size5 {
font-size: 0.69444444em;
}
.katex .sizing.reset-size7.size6,
.katex .fontsize-ensurer.reset-size7.size6 {
font-size: 0.83333333em;
}
.katex .sizing.reset-size7.size7,
.katex .fontsize-ensurer.reset-size7.size7 {
font-size: 1em;
}
.katex .sizing.reset-size7.size8,
.katex .fontsize-ensurer.reset-size7.size8 {
font-size: 1.20138889em;
}
.katex .sizing.reset-size7.size9,
.katex .fontsize-ensurer.reset-size7.size9 {
font-size: 1.4375em;
}
.katex .sizing.reset-size7.size10,
.katex .fontsize-ensurer.reset-size7.size10 {
font-size: 1.72916667em;
}
.katex .sizing.reset-size8.size1,
.katex .fontsize-ensurer.reset-size8.size1 {
font-size: 0.28901734em;
}
.katex .sizing.reset-size8.size2,
.katex .fontsize-ensurer.reset-size8.size2 {
font-size: 0.40462428em;
}
.katex .sizing.reset-size8.size3,
.katex .fontsize-ensurer.reset-size8.size3 {
font-size: 0.46242775em;
}
.katex .sizing.reset-size8.size4,
.katex .fontsize-ensurer.reset-size8.size4 {
font-size: 0.52023121em;
}
.katex .sizing.reset-size8.size5,
.katex .fontsize-ensurer.reset-size8.size5 {
font-size: 0.57803468em;
}
.katex .sizing.reset-size8.size6,
.katex .fontsize-ensurer.reset-size8.size6 {
font-size: 0.69364162em;
}
.katex .sizing.reset-size8.size7,
.katex .fontsize-ensurer.reset-size8.size7 {
font-size: 0.83236994em;
}
.katex .sizing.reset-size8.size8,
.katex .fontsize-ensurer.reset-size8.size8 {
font-size: 1em;
}
.katex .sizing.reset-size8.size9,
.katex .fontsize-ensurer.reset-size8.size9 {
font-size: 1.19653179em;
}
.katex .sizing.reset-size8.size10,
.katex .fontsize-ensurer.reset-size8.size10 {
font-size: 1.43930636em;
}
.katex .sizing.reset-size9.size1,
.katex .fontsize-ensurer.reset-size9.size1 {
font-size: 0.24154589em;
}
.katex .sizing.reset-size9.size2,
.katex .fontsize-ensurer.reset-size9.size2 {
font-size: 0.33816425em;
}
.katex .sizing.reset-size9.size3,
.katex .fontsize-ensurer.reset-size9.size3 {
font-size: 0.38647343em;
}
.katex .sizing.reset-size9.size4,
.katex .fontsize-ensurer.reset-size9.size4 {
font-size: 0.43478261em;
}
.katex .sizing.reset-size9.size5,
.katex .fontsize-ensurer.reset-size9.size5 {
font-size: 0.48309179em;
}
.katex .sizing.reset-size9.size6,
.katex .fontsize-ensurer.reset-size9.size6 {
font-size: 0.57971014em;
}
.katex .sizing.reset-size9.size7,
.katex .fontsize-ensurer.reset-size9.size7 {
font-size: 0.69565217em;
}
.katex .sizing.reset-size9.size8,
.katex .fontsize-ensurer.reset-size9.size8 {
font-size: 0.83574879em;
}
.katex .sizing.reset-size9.size9,
.katex .fontsize-ensurer.reset-size9.size9 {
font-size: 1em;
}
.katex .sizing.reset-size9.size10,
.katex .fontsize-ensurer.reset-size9.size10 {
font-size: 1.20289855em;
}
.katex .sizing.reset-size10.size1,
.katex .fontsize-ensurer.reset-size10.size1 {
font-size: 0.20080321em;
}
.katex .sizing.reset-size10.size2,
.katex .fontsize-ensurer.reset-size10.size2 {
font-size: 0.2811245em;
}
.katex .sizing.reset-size10.size3,
.katex .fontsize-ensurer.reset-size10.size3 {
font-size: 0.32128514em;
}
.katex .sizing.reset-size10.size4,
.katex .fontsize-ensurer.reset-size10.size4 {
font-size: 0.36144578em;
}
.katex .sizing.reset-size10.size5,
.katex .fontsize-ensurer.reset-size10.size5 {
font-size: 0.40160643em;
}
.katex .sizing.reset-size10.size6,
.katex .fontsize-ensurer.reset-size10.size6 {
font-size: 0.48192771em;
}
.katex .sizing.reset-size10.size7,
.katex .fontsize-ensurer.reset-size10.size7 {
font-size: 0.57831325em;
}
.katex .sizing.reset-size10.size8,
.katex .fontsize-ensurer.reset-size10.size8 {
font-size: 0.69477912em;
}
.katex .sizing.reset-size10.size9,
.katex .fontsize-ensurer.reset-size10.size9 {
font-size: 0.8313253em;
}
.katex .sizing.reset-size10.size10,
.katex .fontsize-ensurer.reset-size10.size10 {
font-size: 1em;
}
.katex .delimsizing.size1 {
font-family: KaTeX_Size1;
}
.katex .delimsizing.size2 {
font-family: KaTeX_Size2;
}
.katex .delimsizing.size3 {
font-family: KaTeX_Size3;
}
.katex .delimsizing.size4 {
font-family: KaTeX_Size4;
}
.katex .delimsizing.mult .delim-size1 > span {
font-family: KaTeX_Size1;
}
.katex .delimsizing.mult .delim-size4 > span {
font-family: KaTeX_Size4;
}
.katex .nulldelimiter {
display: inline-block;
width: 0.12em;
}
.katex .op-symbol {
position: relative;
}
.katex .op-symbol.small-op {
font-family: KaTeX_Size1;
}
.katex .op-symbol.large-op {
font-family: KaTeX_Size2;
}
.katex .op-limits > .vlist > span {
text-align: center;
}
.katex .accent > .vlist > span {
text-align: center;
}
.katex .accent .accent-body > span {
width: 0;
}
.katex .accent .accent-body.accent-vec > span {
position: relative;
left: 0.326em;
}
.katex .mtable .vertical-separator {
display: inline-block;
margin: 0 -0.025em;
border-right: 0.05em solid black;
}
.katex .mtable .arraycolsep {
display: inline-block;
}
.katex .mtable .col-align-c > .vlist {
text-align: center;
}
.katex .mtable .col-align-l > .vlist {
text-align: left;
}
.katex .mtable .col-align-r > .vlist {
text-align: right;
}
...@@ -4203,6 +4203,10 @@ jquery-ujs@1.2.2: ...@@ -4203,6 +4203,10 @@ jquery-ujs@1.2.2:
dependencies: dependencies:
jquery ">=1.8.0" jquery ">=1.8.0"
jquery.waitforimages@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/jquery.waitforimages/-/jquery.waitforimages-2.2.0.tgz#63f23131055a1b060dc913e6d874bcc9b9e6b16b"
"jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^2.2.4: "jquery@>= 1.9.1", jquery@>=1.8.0, jquery@^2.2.4:
version "2.2.4" version "2.2.4"
resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02" resolved "https://registry.yarnpkg.com/jquery/-/jquery-2.2.4.tgz#2c89d6889b5eac522a7eea32c14521559c6cbf02"
...@@ -4397,6 +4401,12 @@ karma@^2.0.0: ...@@ -4397,6 +4401,12 @@ karma@^2.0.0:
tmp "0.0.33" tmp "0.0.33"
useragent "^2.1.12" useragent "^2.1.12"
katex@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/katex/-/katex-0.8.3.tgz#909d99864baf964c3ccae39c4a99a8e0fb9a1bd0"
dependencies:
match-at "^0.1.0"
kind-of@^3.0.2: kind-of@^3.0.2:
version "3.1.0" version "3.1.0"
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47"
...@@ -4787,6 +4797,10 @@ marked@^0.3.12: ...@@ -4787,6 +4797,10 @@ marked@^0.3.12:
version "0.3.12" version "0.3.12"
resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519" resolved "https://registry.yarnpkg.com/marked/-/marked-0.3.12.tgz#7cf25ff2252632f3fe2406bde258e94eee927519"
match-at@^0.1.0:
version "0.1.1"
resolved "https://registry.yarnpkg.com/match-at/-/match-at-0.1.1.tgz#25d040d291777704d5e6556bbb79230ec2de0540"
math-expression-evaluator@^1.2.14: math-expression-evaluator@^1.2.14:
version "1.2.16" version "1.2.16"
resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.16.tgz#b357fa1ca9faefb8e48d10c14ef2bcb2d9f0a7c9"
...@@ -7079,6 +7093,13 @@ strip-json-comments@~2.0.1: ...@@ -7079,6 +7093,13 @@ strip-json-comments@~2.0.1:
version "2.0.1" version "2.0.1"
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
style-loader@^0.19.1:
version "0.19.1"
resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-0.19.1.tgz#591ffc80bcefe268b77c5d9ebc0505d772619f85"
dependencies:
loader-utils "^1.0.2"
schema-utils "^0.3.0"
subarg@^1.0.0: subarg@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment