Commit a40bb176 authored by Fabio Busatto's avatar Fabio Busatto

Resolve conflicts

parents ad9c0bae e99ddb6f
......@@ -14,7 +14,7 @@ linters:
# Whether or not to prefer `border: 0` over `border: none`.
BorderZero:
enabled: false
enabled: true
# Reports when you define a rule set using a selector with chained classes
# (a.k.a. adjoining classes).
......
/* eslint-disable class-methods-use-this, object-shorthand, no-unused-vars, no-use-before-define, no-new, max-len, no-restricted-syntax, guard-for-in, no-continue */
import _ from 'underscore';
import { insertText, getSelectedFragment, nodeMatchesSelector } from './lib/utils/common_utils';
import { placeholderImage } from './lazy_loader';
import { insertText, getSelectedFragment, nodeMatchesSelector } from '../lib/utils/common_utils';
import { placeholderImage } from '../lazy_loader';
const gfmRules = {
// The filters referenced in lib/banzai/pipeline/gfm_pipeline.rb convert
......@@ -284,7 +285,7 @@ const gfmRules = {
},
};
class CopyAsGFM {
export class CopyAsGFM {
constructor() {
$(document).on('copy', '.md, .wiki', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformGFMSelection); });
$(document).on('copy', 'pre.code.highlight, .diff-content .line_content', (e) => { CopyAsGFM.copyAsGFM(e, CopyAsGFM.transformCodeSelection); });
......@@ -469,7 +470,12 @@ class CopyAsGFM {
}
}
window.gl = window.gl || {};
window.gl.CopyAsGFM = CopyAsGFM;
// Export CopyAsGFM as a global for rspec to access
// see /spec/features/copy_as_gfm_spec.rb
if (process.env.NODE_ENV !== 'production') {
window.CopyAsGFM = CopyAsGFM;
}
new CopyAsGFM();
export default function initCopyAsGFM() {
return new CopyAsGFM();
}
import './autosize';
import './bind_in_out';
import initCopyAsGFM from './copy_as_gfm';
import './details_behavior';
import installGlEmojiElement from './gl_emoji';
import './quick_submit';
......@@ -7,3 +8,4 @@ import './requires_input';
import './toggler_behavior';
installGlEmojiElement();
initCopyAsGFM();
......@@ -46,7 +46,6 @@ import './commits';
import './compare';
import './compare_autocomplete';
import './confirm_danger_modal';
import './copy_as_gfm';
import './copy_to_clipboard';
import Flash, { removeFlashClickListener } from './flash';
import './gl_dropdown';
......
......@@ -138,7 +138,7 @@
renderAxesPaths() {
this.timeSeries = createTimeSeries(
this.graphData.queries[0],
this.graphData.queries,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset,
......@@ -153,8 +153,9 @@
const axisYScale = d3.scale.linear()
.range([this.graphHeight - this.graphHeightOffset, 0]);
axisXScale.domain(d3.extent(this.timeSeries[0].values, d => d.time));
axisYScale.domain([0, d3.max(this.timeSeries[0].values.map(d => d.value))]);
const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisXScale.domain(d3.extent(allValues, d => d.time));
axisYScale.domain([0, d3.max(allValues.map(d => d.value))]);
const xAxis = d3.svg.axis()
.scale(axisXScale)
......@@ -246,6 +247,7 @@
:key="index"
:generated-line-path="path.linePath"
:generated-area-path="path.areaPath"
:line-style="path.lineStyle"
:line-color="path.lineColor"
:area-color="path.areaColor"
/>
......
......@@ -79,7 +79,8 @@
},
formatMetricUsage(series) {
const value = series.values[this.currentDataIndex].value;
const value = series.values[this.currentDataIndex] &&
series.values[this.currentDataIndex].value;
if (isNaN(value)) {
return '-';
}
......@@ -92,6 +93,12 @@
}
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
},
strokeDashArray(type) {
if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3';
return null;
},
},
mounted() {
this.$nextTick(() => {
......@@ -162,13 +169,15 @@
v-for="(series, index) in timeSeries"
:key="index"
:transform="translateLegendGroup(index)">
<rect
:fill="series.areaColor"
:width="measurements.legends.width"
:height="measurements.legends.height"
x="20"
:y="graphHeight - measurements.legendOffset">
</rect>
<line
:stroke="series.lineColor"
:stroke-width="measurements.legends.height"
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:x1="measurements.legends.offsetX"
:x2="measurements.legends.offsetX + measurements.legends.width"
:y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY">
</line>
<text
v-if="timeSeries.length > 1"
class="legend-metric-title"
......
......@@ -9,6 +9,10 @@
type: String,
required: true,
},
lineStyle: {
type: String,
required: false,
},
lineColor: {
type: String,
required: true,
......@@ -18,6 +22,13 @@
required: true,
},
},
computed: {
strokeDashArray() {
if (this.lineStyle === 'dashed') return '3, 1';
if (this.lineStyle === 'dotted') return '1, 1';
return null;
},
},
};
</script>
<template>
......@@ -34,6 +45,7 @@
:stroke="lineColor"
fill="none"
stroke-width="1"
:stroke-dasharray="strokeDashArray"
transform="translate(-5, 20)">
</path>
</g>
......
......@@ -7,15 +7,16 @@ export default {
left: 40,
},
legends: {
width: 10,
width: 15,
height: 3,
offsetX: 20,
offsetY: 32,
},
backgroundLegend: {
width: 30,
height: 50,
},
axisLabelLineOffset: -20,
legendOffset: 33,
},
large: { // This covers both md and lg screen sizes
margin: {
......@@ -27,13 +28,14 @@ export default {
legends: {
width: 15,
height: 3,
offsetX: 20,
offsetY: 34,
},
backgroundLegend: {
width: 30,
height: 150,
},
axisLabelLineOffset: 20,
legendOffset: 36,
},
xTicks: 8,
yTicks: 3,
......
......@@ -11,7 +11,9 @@ const defaultColorPalette = {
const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
export default function createTimeSeries(queryData, graphWidth, graphHeight, graphHeightOffset) {
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
let usedColors = [];
function pickColor(name) {
......@@ -31,17 +33,7 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
return defaultColorPalette[pick];
}
const maxValues = queryData.result.map((timeSeries, index) => {
const maxValue = d3.max(timeSeries.values.map(d => d.value));
return {
maxValue,
index,
};
});
const maxValueFromSeries = _.max(maxValues, val => val.maxValue);
return queryData.result.map((timeSeries, timeSeriesNumber) => {
return query.result.map((timeSeries, timeSeriesNumber) => {
let metricTag = '';
let lineColor = '';
let areaColor = '';
......@@ -52,9 +44,9 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
const timeSeriesScaleY = d3.scale.linear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time));
timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.time.minute, 60);
timeSeriesScaleY.domain([0, maxValueFromSeries.maxValue]);
timeSeriesScaleY.domain(yDom);
const defined = d => !isNaN(d.value) && d.value != null;
......@@ -72,10 +64,10 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
.y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = queryData.series != null &&
_.findWhere(queryData.series[0].when,
{ value: timeSeriesMetricLabel });
if (seriesCustomizationData != null) {
const seriesCustomizationData = query.series != null &&
_.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
......@@ -83,14 +75,35 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
[lineColor, areaColor] = pickColor();
}
if (query.track) {
metricTag += ` - ${query.track}`;
}
return {
linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
values: timeSeries.values,
lineStyle,
lineColor,
areaColor,
metricTag,
};
});
}
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
const allValues = queries.reduce((allQueryResults, query) => allQueryResults.concat(
query.result.reduce((allResults, result) => allResults.concat(result.values), []),
), []);
const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))];
return queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat(
queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
);
}, []);
}
......@@ -162,13 +162,19 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
items = [
{
header: "" + name
}, {
}
];
const issueItems = [
{
text: 'Issues assigned to me',
url: issuesPath + "/?assignee_username=" + userName
}, {
text: "Issues I've created",
url: issuesPath + "/?author_username=" + userName
}, 'separator', {
}
];
const mergeRequestItems = [
{
text: 'Merge requests assigned to me',
url: mrPath + "/?assignee_username=" + userName
}, {
......@@ -176,6 +182,11 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
url: mrPath + "/?author_username=" + userName
}
];
if (options.issuesDisabled) {
items = items.concat(mergeRequestItems);
} else {
items = items.concat(...issueItems, 'separator', ...mergeRequestItems);
}
if (!name) {
items.splice(0, 1);
}
......@@ -408,6 +419,7 @@ import { isInGroupsPage, isInProjectPage, getGroupSlug, getProjectSlug } from '.
gl.projectOptions[projectPath] = {
name: $projectOptionsDataEl.data('name'),
issuesPath: $projectOptionsDataEl.data('issues-path'),
issuesDisabled: $projectOptionsDataEl.data('issues-disabled'),
mrPath: $projectOptionsDataEl.data('mr-path')
};
}
......
......@@ -4,6 +4,7 @@
import _ from 'underscore';
import 'mousetrap';
import ShortcutsNavigation from './shortcuts_navigation';
import { CopyAsGFM } from './behaviors/copy_as_gfm';
export default class ShortcutsIssuable extends ShortcutsNavigation {
constructor(isMergeRequest) {
......@@ -33,8 +34,8 @@ export default class ShortcutsIssuable extends ShortcutsNavigation {
return false;
}
const el = window.gl.CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
const selected = window.gl.CopyAsGFM.nodeToGFM(el);
const el = CopyAsGFM.transformGFMSelection(documentFragment.cloneNode(true));
const selected = CopyAsGFM.nodeToGFM(el);
if (selected.trim() === '') {
return false;
......
......@@ -47,8 +47,10 @@
},
},
methods: {
toggleMarkdownPreview() {
this.previewMarkdown = !this.previewMarkdown;
showPreviewTab() {
if (this.previewMarkdown) return;
this.previewMarkdown = true;
/*
Can't use `$refs` as the component is technically in the parent component
......@@ -56,20 +58,22 @@
*/
const text = this.$slots.textarea[0].elm.value;
if (!this.previewMarkdown) {
this.markdownPreview = '';
} else if (text) {
if (text) {
this.markdownPreviewLoading = true;
this.$http.post(this.markdownPreviewPath, { text })
.then(resp => resp.json())
.then((data) => {
this.renderMarkdown(data);
})
.then(data => this.renderMarkdown(data))
.catch(() => new Flash('Error loading markdown preview'));
} else {
this.renderMarkdown();
}
},
showWriteTab() {
this.markdownPreview = '';
this.previewMarkdown = false;
},
renderMarkdown(data = {}) {
this.markdownPreviewLoading = false;
this.markdownPreview = data.body || 'Nothing to preview.';
......@@ -106,7 +110,8 @@
ref="gl-form">
<markdown-header
:preview-markdown="previewMarkdown"
@toggle-markdown="toggleMarkdownPreview" />
@preview-markdown="showPreviewTab"
@write-markdown="showWriteTab" />
<div
class="md-write-holder"
v-show="!previewMarkdown">
......
......@@ -18,23 +18,31 @@
icon,
},
methods: {
toggleMarkdownPreview(e, form) {
if (form && !form.find('.js-vue-markdown-field').length) {
return;
} else if (e.target.blur) {
e.target.blur();
}
isMarkdownForm(form) {
return form && !form.find('.js-vue-markdown-field').length;
},
previewMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (this.isMarkdownForm(form)) return;
this.$emit('preview-markdown');
},
writeMarkdownTab(event, form) {
if (event.target.blur) event.target.blur();
if (this.isMarkdownForm(form)) return;
this.$emit('toggle-markdown');
this.$emit('write-markdown');
},
},
mounted() {
$(document).on('markdown-preview:show.vue', this.toggleMarkdownPreview);
$(document).on('markdown-preview:hide.vue', this.toggleMarkdownPreview);
$(document).on('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).on('markdown-preview:hide.vue', this.writeMarkdownTab);
},
beforeDestroy() {
$(document).on('markdown-preview:show.vue', this.toggleMarkdownPreview);
$(document).off('markdown-preview:hide.vue', this.toggleMarkdownPreview);
$(document).off('markdown-preview:show.vue', this.previewMarkdownTab);
$(document).off('markdown-preview:hide.vue', this.writeMarkdownTab);
},
};
</script>
......@@ -44,17 +52,19 @@
<ul class="nav-links clearfix">
<li :class="{ active: !previewMarkdown }">
<a
class="js-write-link"
href="#md-write-holder"
tabindex="-1"
@click.prevent="toggleMarkdownPreview($event)">
@click.prevent="writeMarkdownTab($event)">
Write
</a>
</li>
<li :class="{ active: previewMarkdown }">
<a
class="js-preview-link"
href="#md-preview-holder"
tabindex="-1"
@click.prevent="toggleMarkdownPreview($event)">
@click.prevent="previewMarkdownTab($event)">
Preview
</a>
</li>
......
......@@ -42,8 +42,7 @@
&.avatar-inline {
float: none;
display: inline-block;
margin-left: 4px;
margin-bottom: 2px;
margin-left: 2px;
flex-shrink: 0;
-webkit-flex-shrink: 0;
......@@ -59,7 +58,7 @@
&.avatar-tile {
border-radius: 0;
border: none;
border: 0;
}
&:not([href]):hover {
......@@ -96,7 +95,7 @@
.avatar {
border-radius: 0;
border: none;
border: 0;
height: auto;
width: 100%;
margin: 0;
......
......@@ -39,7 +39,7 @@
}
&.top-block {
border-top: none;
border-top: 0;
.container-fluid {
background-color: inherit;
......@@ -63,7 +63,7 @@
&.footer-block {
margin-top: 0;
border-bottom: none;
border-bottom: 0;
margin-bottom: -$gl-padding;
}
......@@ -100,7 +100,7 @@
&.build-content {
background-color: $white-light;
border-top: none;
border-top: 0;
}
}
......@@ -287,12 +287,12 @@
cursor: pointer;
color: $blue-300;
z-index: 1;
border: none;
border: 0;
background-color: transparent;
&:hover,
&:focus {
border: none;
border: 0;
color: $blue-400;
}
}
......
......@@ -304,7 +304,7 @@
}
.btn-clipboard {
border: none;
border: 0;
padding: 0 5px;
}
......
......@@ -28,7 +28,7 @@
pre {
&.clean {
background: none;
border: none;
border: 0;
margin: 0;
padding: 0;
}
......@@ -142,7 +142,7 @@ li.note {
img { max-width: 100%; }
.note-title {
li {
border-bottom: none !important;