Commit e1e6e946 authored by Alfredo Sumaran's avatar Alfredo Sumaran

Remove IIFEs from files in cycle_analytics_bundle.js

parent 100f4370
...@@ -2,46 +2,45 @@ ...@@ -2,46 +2,45 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageCodeComponent = Vue.extend({ global.cycleAnalytics.StageCodeComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
</a>
</h5>
<a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
&middot;
<span>
Opened
<a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
</span>
<span>
by
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
</span>
</div>
<div class="item-time">
<total-time :time="mergeRequest.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="mergeRequest in items" class="stage-event-item">
})(window.gl || (window.gl = {})); <div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl">
<h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url">
{{ mergeRequest.title }}
</a>
</h5>
<a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
&middot;
<span>
Opened
<a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
</span>
<span>
by
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
</span>
</div>
<div class="item-time">
<total-time :time="mergeRequest.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -2,48 +2,47 @@ ...@@ -2,48 +2,47 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageIssueComponent = Vue.extend({ global.cycleAnalytics.StageIssueComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
</a>
</h5>
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
Opened
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
by
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="issue.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="issue in items" class="stage-event-item">
})(window.gl || (window.gl = {})); <div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
</a>
</h5>
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
Opened
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
by
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="issue.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -2,50 +2,49 @@ ...@@ -2,50 +2,49 @@
import Vue from 'vue'; import Vue from 'vue';
import iconCommit from '../svg/icon_commit.svg'; import iconCommit from '../svg/icon_commit.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StagePlanComponent = Vue.extend({ global.cycleAnalytics.StagePlanComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() { data() {
return { iconCommit }; return { iconCommit };
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="commit in items" class="stage-event-item">
<div class="item-details item-conmmit-component">
<img class="avatar" :src="commit.author.avatarUrl">
<h5 class="item-title commit-title">
<a :href="commit.commitUrl">
{{ commit.title }}
</a>
</h5>
<span>
First
<span class="commit-icon">${iconCommit}</span>
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
pushed by
<a :href="commit.author.webUrl" class="commit-author-link">
{{ commit.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="commit.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="commit in items" class="stage-event-item">
})(window.gl || (window.gl = {})); <div class="item-details item-conmmit-component">
<img class="avatar" :src="commit.author.avatarUrl">
<h5 class="item-title commit-title">
<a :href="commit.commitUrl">
{{ commit.title }}
</a>
</h5>
<span>
First
<span class="commit-icon">${iconCommit}</span>
<a :href="commit.commitUrl" class="commit-hash-link monospace">{{ commit.shortSha }}</a>
pushed by
<a :href="commit.author.webUrl" class="commit-author-link">
{{ commit.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="commit.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -2,48 +2,47 @@ ...@@ -2,48 +2,47 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageProductionComponent = Vue.extend({ global.cycleAnalytics.StageProductionComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="issue in items" class="stage-event-item">
<div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
</a>
</h5>
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
Opened
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
by
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="issue.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="issue in items" class="stage-event-item">
})(window.gl || (window.gl = {})); <div class="item-details">
<img class="avatar" :src="issue.author.avatarUrl">
<h5 class="item-title issue-title">
<a class="issue-title" :href="issue.url">
{{ issue.title }}
</a>
</h5>
<a :href="issue.url" class="issue-link">#{{ issue.iid }}</a>
&middot;
<span>
Opened
<a :href="issue.url" class="issue-date">{{ issue.createdAt }}</a>
</span>
<span>
by
<a :href="issue.author.webUrl" class="issue-author-link">
{{ issue.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="issue.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -2,58 +2,57 @@ ...@@ -2,58 +2,57 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageReviewComponent = Vue.extend({ global.cycleAnalytics.StageReviewComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div> </div>
<ul class="stage-event-list"> <ul class="stage-event-list">
<li v-for="mergeRequest in items" class="stage-event-item"> <li v-for="mergeRequest in items" class="stage-event-item">
<div class="item-details"> <div class="item-details">
<img class="avatar" :src="mergeRequest.author.avatarUrl"> <img class="avatar" :src="mergeRequest.author.avatarUrl">
<h5 class="item-title merge-merquest-title"> <h5 class="item-title merge-merquest-title">
<a :href="mergeRequest.url"> <a :href="mergeRequest.url">
{{ mergeRequest.title }} {{ mergeRequest.title }}
</a> </a>
</h5> </h5>
<a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a> <a :href="mergeRequest.url" class="issue-link">!{{ mergeRequest.iid }}</a>
&middot; &middot;
<span> <span>
Opened Opened
<a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a> <a :href="mergeRequest.url" class="issue-date">{{ mergeRequest.createdAt }}</a>
</span>
<span>
by
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a>
</span>
<template v-if="mergeRequest.state === 'closed'">
<span class="merge-request-state">
<i class="fa fa-ban"></i>
{{ mergeRequest.state.toUpperCase() }}
</span> </span>
<span> </template>
by <template v-else>
<a :href="mergeRequest.author.webUrl" class="issue-author-link">{{ mergeRequest.author.name }}</a> <span class="merge-request-branch" v-if="mergeRequest.branch">
<i class= "fa fa-code-fork"></i>
<a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a>
</span> </span>
<template v-if="mergeRequest.state === 'closed'"> </template>
<span class="merge-request-state"> </div>
<i class="fa fa-ban"></i> <div class="item-time">
{{ mergeRequest.state.toUpperCase() }} <total-time :time="mergeRequest.totalTime"></total-time>
</span> </div>
</template> </li>
<template v-else> </ul>
<span class="merge-request-branch" v-if="mergeRequest.branch"> </div>
<i class= "fa fa-code-fork"></i> `,
<a :href="mergeRequest.branch.url">{{ mergeRequest.branch.name }}</a> });
</span>
</template>
</div>
<div class="item-time">
<total-time :time="mergeRequest.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
})(window.gl || (window.gl = {}));
...@@ -2,48 +2,47 @@ ...@@ -2,48 +2,47 @@
import Vue from 'vue'; import Vue from 'vue';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageStagingComponent = Vue.extend({ global.cycleAnalytics.StageStagingComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() { data() {
return { iconBranch }; return { iconBranch };
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details">
<img class="avatar" :src="build.author.avatarUrl">
<h5 class="item-title">
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="build-date">{{ build.date }}</a>
by
<a :href="build.author.webUrl" class="issue-author-link">
{{ build.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="build.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="build in items" class="stage-event-item item-build-component">
})(window.gl || (window.gl = {})); <div class="item-details">
<img class="avatar" :src="build.author.avatarUrl">
<h5 class="item-title">
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="build-date">{{ build.date }}</a>
by
<a :href="build.author.webUrl" class="issue-author-link">
{{ build.author.name }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="build.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -3,48 +3,47 @@ import Vue from 'vue'; ...@@ -3,48 +3,47 @@ import Vue from 'vue';
import iconBuildStatus from '../svg/icon_build_status.svg'; import iconBuildStatus from '../svg/icon_build_status.svg';
import iconBranch from '../svg/icon_branch.svg'; import iconBranch from '../svg/icon_branch.svg';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.StageTestComponent = Vue.extend({ global.cycleAnalytics.StageTestComponent = Vue.extend({
props: { props: {
items: Array, items: Array,
stage: Object, stage: Object,
}, },
data() { data() {
return { iconBuildStatus, iconBranch }; return { iconBuildStatus, iconBranch };
}, },
template: ` template: `
<div> <div>
<div class="events-description"> <div class="events-description">
{{ stage.description }} {{ stage.description }}
<limit-warning :count="items.length" /> <limit-warning :count="items.length" />
</div>
<ul class="stage-event-list">
<li v-for="build in items" class="stage-event-item item-build-component">
<div class="item-details">
<h5 class="item-title">
<span class="icon-build-status">${iconBuildStatus}</span>
<a :href="build.url" class="item-build-name">{{ build.name }}</a>
&middot;
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="issue-date">
{{ build.date }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="build.totalTime"></total-time>
</div>
</li>
</ul>
</div> </div>
`, <ul class="stage-event-list">
}); <li v-for="build in items" class="stage-event-item item-build-component">
})(window.gl || (window.gl = {})); <div class="item-details">
<h5 class="item-title">
<span class="icon-build-status">${iconBuildStatus}</span>
<a :href="build.url" class="item-build-name">{{ build.name }}</a>
&middot;
<a :href="build.url" class="pipeline-id">#{{ build.id }}</a>
<i class="fa fa-code-fork"></i>
<a :href="build.branch.url" class="branch-name monospace">{{ build.branch.name }}</a>
<span class="icon-branch">${iconBranch}</span>
<a :href="build.commitUrl" class="short-sha monospace">{{ build.shortSha }}</a>
</h5>
<span>
<a :href="build.url" class="issue-date">
{{ build.date }}
</a>
</span>
</div>
<div class="item-time">
<total-time :time="build.totalTime"></total-time>
</div>
</li>
</ul>
</div>
`,
});
...@@ -2,25 +2,24 @@ ...@@ -2,25 +2,24 @@
import Vue from 'vue'; import Vue from 'vue';
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
global.cycleAnalytics.TotalTimeComponent = Vue.extend({ global.cycleAnalytics.TotalTimeComponent = Vue.extend({
props: { props: {
time: Object, time: Object,
}, },
template: ` template: `
<span class="total-time"> <span class="total-time">
<template v-if="Object.keys(time).length"> <template v-if="Object.keys(time).length">
<template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template> <template v-if="time.days">{{ time.days }} <span>{{ time.days === 1 ? 'day' : 'days' }}</span></template>
<template v-if="time.hours">{{ time.hours }} <span>hr</span></template> <template v-if="time.hours">{{ time.hours }} <span>hr</span></template>
<template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template> <template v-if="time.mins && !time.days">{{ time.mins }} <span>mins</span></template>
<template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template> <template v-if="time.seconds && Object.keys(time).length === 1 || time.seconds === 0">{{ time.seconds }} <span>s</span></template>
</template> </template>
<template v-else> <template v-else>
-- --
</template> </template>
</span> </span>
`, `,
}); });
})(window.gl || (window.gl = {}));
/* eslint-disable no-param-reassign */ /* eslint-disable no-param-reassign */
((global) => {
global.cycleAnalytics = global.cycleAnalytics || {};
class CycleAnalyticsService { const global = window.gl || (window.gl = {});
constructor(options) { global.cycleAnalytics = global.cycleAnalytics || {};
this.requestPath = options.requestPath;
}
fetchCycleAnalyticsData(options) { class CycleAnalyticsService {
options = options || { startDate: 30 }; constructor(options) {
this.requestPath = options.requestPath;
return $.ajax({ }
url: this.requestPath,
method: 'GET',
dataType: 'json',
contentType: 'application/json',
data: {
cycle_analytics: {
start_date: options.startDate,
},
},
});
}
fetchStageData(options) { fetchCycleAnalyticsData(options) {
const { options = options || { startDate: 30 };
stage,
startDate,
} = options;
return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, { return $.ajax({
url: this.requestPath,
method: 'GET',
dataType: 'json',
contentType: 'application/json',
data: {
cycle_analytics: { cycle_analytics: {
start_date: startDate, start_date: options.startDate,
}, },
}); },
} });
}
fetchStageData(options) {
const {
stage,
startDate,
} = options;
return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, {
cycle_analytics: {
start_date: startDate,
},
});
} }
}
global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService; global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService;
})(window.gl || (window.gl = {}));
...@@ -3,102 +3,101 @@ ...@@ -3,102 +3,101 @@
require('../lib/utils/text_utility'); require('../lib/utils/text_utility');
const DEFAULT_EVENT_OBJECTS = require('./default_event_objects'); const DEFAULT_EVENT_OBJECTS = require('./default_event_objects');
((global) => { const global = window.gl || (window.gl = {});
global.cycleAnalytics = global.cycleAnalytics || {}; global.cycleAnalytics = global.cycleAnalytics || {};
const EMPTY_STAGE_TEXTS = { const EMPTY_STAGE_TEXTS = {
issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.', issue: 'The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.',
plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.', plan: 'The planning stage shows the time from the previous step to pushing your first commit. This time will be added automatically once you push your first commit.',
code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.', code: 'The coding stage shows the time from the first commit to creating the merge request. The data will automatically be added here once you create your first merge request.',
test: 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.', test: 'The testing stage shows the time GitLab CI takes to run every pipeline for the related merge request. The data will automatically be added after your first pipeline finishes running.',
review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.', review: 'The review stage shows the time from creating the merge request to merging it. The data will automatically be added after you merge your first merge request.',
staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.', staging: 'The staging stage shows the time between merging the MR and deploying code to the production environment. The data will be automatically added once you deploy to production for the first time.',
production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.', production: 'The production stage shows the total time it takes between creating an issue and deploying the code to production. The data will be automatically added once you have completed the full idea to production cycle.',
}; };
global.cycleAnalytics.CycleAnalyticsStore = { global.cycleAnalytics.CycleAnalyticsStore = {
state: { state: {
summary: '', summary: '',
stats: '', stats: '',
analytics: '', analytics: '',
events: [], events: [],
stages: [], stages: [],
}, },
setCycleAnalyticsData(data) { setCycleAnalyticsData(data) {
this.state = Object.assign(this.state, this.decorateData(data)); this.state = Object.assign(this.state, this.decorateData(data));
}, },
decorateData(data) { decorateData(data) {
const newData = {}; const newData = {};
newData.stages = data.stats || []; newData.stages = data.stats || [];
newData.summary = data.summary || []; newData.summary = data.summary || [];
newData.summary.forEach((item) => { newData.summary.forEach((item) => {
item.value = item.value || '-'; item.value = item.value || '-';
}); });
newData.stages.forEach((item) => { newData.stages.forEach((item) => {
const stageSlug = gl.text.dasherize(item.title.toLowerCase()); const stageSlug = gl.text.dasherize(item.title.toLowerCase());
item.active = false; item.active = false;
item.isUserAllowed = data.permissions[stageSlug]; item.isUserAllowed = data.permissions[stageSlug];
item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug]; item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug];
item.component = `stage-${stageSlug}-component`; item.component = `stage-${stageSlug}-component`;
item.slug = stageSlug; item.slug = stageSlug;
}); });
newData.analytics = data; newData.analytics = data;
return newData; return newData;
}, },
setLoadingState(state) { setLoadingState(state) {
this.state.isLoading = state; this.state.isLoading = state;
}, },
setErrorState(state) { setErrorState(state) {
this.state.hasError = state; this.state.hasError = state;
}, },
deactivateAllStages() { deactivateAllStages() {
this.state.stages.forEach((stage) => { this.state.stages.forEach((stage) => {
stage.active = false; stage.active = false;
}); });
}, },
setActiveStage(stage) { setActiveStage(stage) {
this.deactivateAllStages(); this.deactivateAllStages();
stage.active = true; stage.active = true;
}, },
setStageEvents(events, stage) { setStageEvents(events, stage) {
this.state.events = this.decorateEvents(events, stage); this.state.events = this.decorateEvents(events, stage);
}, },
decorateEvents(events, stage) { decorateEvents(events, stage) {
const newEvents = []; const newEvents = [];
events.forEach((item) => { events.forEach((item) => {
if (!item) return; if (!item) return;
const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item); const eventItem = Object.assign({}, DEFAULT_EVENT_OBJECTS[stage.slug], item);
eventItem.totalTime = eventItem.total_time; eventItem.totalTime = eventItem.total_time;
if (eventItem.author) { if (eventItem.author) {
eventItem.author.webUrl = eventItem.author.web_url; eventItem.author.webUrl = eventItem.author.web_url;
eventItem.author.avatarUrl = eventItem.author.avatar_url; eventItem.author.avatarUrl = eventItem.author.avatar_url;
} }
if (eventItem.created_at) eventItem.createdAt = eventItem.created_at; if (eventItem.created_at) eventItem.createdAt = eventItem.created_at;
if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha; if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha;
if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url; if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url;
delete eventItem.author.web_url; delete eventItem.author.web_url;
delete eventItem.author.avatar_url; delete eventItem.author.avatar_url;
delete eventItem.total_time; delete eventItem.total_time;
delete eventItem.created_at; delete eventItem.created_at;
delete eventItem.short_sha; delete eventItem.short_sha;
delete eventItem.commit_url; delete eventItem.commit_url;
newEvents.push(eventItem); newEvents.push(eventItem);
}); });
return newEvents; return newEvents;
}, },
currentActiveStage() { currentActiveStage() {
return this.state.stages.find(stage => stage.active); return this.state.stages.find(stage => stage.active);
}, },
}; };
})(window.gl || (window.gl = {}));
/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len */ /* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, quotes, one-var, one-var-declaration-per-line, operator-assignment, no-else-return, prefer-template, prefer-arrow-callback, no-empty, max-len, consistent-return, no-unused-vars, no-return-assign, max-len, vars-on-top */
require('vendor/latinise'); require('vendor/latinise');
(function() { var base;
(function(w) { var w = window;
var base; if (w.gl == null) {
if (w.gl == null) { w.gl = {};
w.gl = {}; }
} if ((base = w.gl).text == null) {
if ((base = w.gl).text == null) { base.text = {};
base.text = {}; }
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
};
gl.text.highCountTrim = function(count) {
return count > 99 ? '99+' : count;
};
gl.text.randomString = function() {
return Math.random().toString(36).substring(7);
};
gl.text.replaceRange = function(s, start, end, substitute) {
return s.substring(0, start) + substitute + s.substring(end);
};
gl.text.getTextWidth = function(text, font) {
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
// re-use canvas object for better performance
var canvas = gl.text.getTextWidth.canvas || (gl.text.getTextWidth.canvas = document.createElement('canvas'));
var context = canvas.getContext('2d');
context.font = font;
return context.measureText(text).width;
};
gl.text.selectedText = function(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
};
gl.text.lineBefore = function(text, textarea) {
var split;
split = text.substring(0, textarea.selectionStart).trim().split('\n');
return split[split.length - 1];
};
gl.text.lineAfter = function(text, textarea) {
return text.substring(textarea.selectionEnd).trim().split('\n')[0];
};
gl.text.blockTagText = function(text, textArea, blockTag, selected) {
var lineAfter, lineBefore;
lineBefore = this.lineBefore(text, textArea);
lineAfter = this.lineAfter(text, textArea);
if (lineBefore === blockTag && lineAfter === blockTag) {
// To remove the block tag we have to select the line before & after
if (blockTag != null) {
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
} }
gl.text.addDelimiter = function(text) { return selected;
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text; } else {
}; return blockTag + "\n" + selected + "\n" + blockTag;
gl.text.highCountTrim = function(count) { }
return count > 99 ? '99+' : count; };
}; gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
gl.text.randomString = function() { var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
return Math.random().toString(36).substring(7); removedLastNewLine = false;
}; removedFirstNewLine = false;
gl.text.replaceRange = function(s, start, end, substitute) { currentLineEmpty = false;
return s.substring(0, start) + substitute + s.substring(end);
};
gl.text.getTextWidth = function(text, font) {
/**
* Uses canvas.measureText to compute and return the width of the given text of given font in pixels.
*
* @param {String} text The text to be rendered.
* @param {String} font The css font descriptor that text is to be rendered with (e.g. "bold 14px verdana").
*
* @see http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript/21015393#21015393
*/
// re-use canvas object for better performance
var canvas = gl.text.getTextWidth.canvas || (gl.text.getTextWidth.canvas = document.createElement('canvas'));
var context = canvas.getContext('2d');
context.font = font;
return context.measureText(text).width;
};
gl.text.selectedText = function(text, textarea) {
return text.substring(textarea.selectionStart, textarea.selectionEnd);
};
gl.text.lineBefore = function(text, textarea) {
var split;
split = text.substring(0, textarea.selectionStart).trim().split('\n');
return split[split.length - 1];
};
gl.text.lineAfter = function(text, textarea) {
return text.substring(textarea.selectionEnd).trim().split('\n')[0];
};
gl.text.blockTagText = function(text, textArea, blockTag, selected) {
var lineAfter, lineBefore;
lineBefore = this.lineBefore(text, textArea);
lineAfter = this.lineAfter(text, textArea);
if (lineBefore === blockTag && lineAfter === blockTag) {
// To remove the block tag we have to select the line before & after
if (blockTag != null) {
textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1);
textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1);
}
return selected;
} else {
return blockTag + "\n" + selected + "\n" + blockTag;
}
};
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine, currentLineEmpty, lastNewLine;
removedLastNewLine = false;
removedFirstNewLine = false;
currentLineEmpty = false;
// Remove the first newline // Remove the first newline
if (selected.indexOf('\n') === 0) { if (selected.indexOf('\n') === 0) {
removedFirstNewLine = true; removedFirstNewLine = true;
selected = selected.replace(/\n+/, ''); selected = selected.replace(/\n+/, '');
} }
// Remove the last newline // Remove the last newline
if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) { if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) {
removedLastNewLine = true; removedLastNewLine = true;
selected = selected.replace(/\n$/, ''); selected = selected.replace(/\n$/, '');
} }
selectedSplit = selected.split('\n'); selectedSplit = selected.split('\n');
if (!wrap) { if (!wrap) {
lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n'); lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n');
// Check whether the current line is empty or consists only of spaces(=handle as empty) // Check whether the current line is empty or consists only of spaces(=handle as empty)
if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) { if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) {
currentLineEmpty = true; currentLineEmpty = true;
} }
} }
startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : ''; startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : '';
if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) { if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
if (blockTag != null) { if (blockTag != null) {
insertText = this.blockTagText(text, textArea, blockTag, selected); insertText = this.blockTagText(text, textArea, blockTag, selected);
} else {
insertText = selectedSplit.map(function(val) {
if (val.indexOf(tag) === 0) {
return "" + (val.replace(tag, ''));
} else { } else {
insertText = selectedSplit.map(function(val) { return "" + tag + val;
if (val.indexOf(tag) === 0) {
return "" + (val.replace(tag, ''));
} else {
return "" + tag + val;
}
}).join('\n');
} }
} else { }).join('\n');
insertText = "" + startChar + tag + selected + (wrap ? tag : ' '); }
} } else {
insertText = "" + startChar + tag + selected + (wrap ? tag : ' ');
}
if (removedFirstNewLine) { if (removedFirstNewLine) {
insertText = '\n' + insertText; insertText = '\n' + insertText;
} }
if (removedLastNewLine) { if (removedLastNewLine) {
insertText += '\n'; insertText += '\n';
} }
if (document.queryCommandSupported('insertText')) { if (document.queryCommandSupported('insertText')) {
inserted = document.execCommand('insertText', false, insertText); inserted = document.execCommand('insertText', false, insertText);
} }
if (!inserted) { if (!inserted) {
try { try {
document.execCommand("ms-beginUndoUnit"); document.execCommand("ms-beginUndoUnit");
} catch (error) {} } catch (error) {}
textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText); textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText);
try { try {
document.execCommand("ms-endUndoUnit"); document.execCommand("ms-endUndoUnit");
} catch (error) {} } catch (error) {}
} }
return this.moveCursor(textArea, tag, wrap, removedLastNewLine); return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
}; };
gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) { gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
var pos; var pos;
if (!textArea.setSelectionRange) { if (!textArea.setSelectionRange) {
return; return;
} }
if (textArea.selectionStart === textArea.selectionEnd) { if (textArea.selectionStart === textArea.selectionEnd) {
if (wrapped) { if (wrapped) {
pos = textArea.selectionStart - tag.length; pos = textArea.selectionStart - tag.length;
} else { } else {
pos = textArea.selectionStart; pos = textArea.selectionStart;
} }
if (removedLastNewLine) { if (removedLastNewLine) {
pos -= 1; pos -= 1;
} }
return textArea.setSelectionRange(pos, pos); return textArea.setSelectionRange(pos, pos);
} }
}; };
gl.text.updateText = function(textArea, tag, blockTag, wrap) { gl.text.updateText = function(textArea, tag, blockTag, wrap) {
var $textArea, selected, text; var $textArea, selected, text;
$textArea = $(textArea); $textArea = $(textArea);
textArea = $textArea.get(0); textArea = $textArea.get(0);
text = $textArea.val(); text = $textArea.val();
selected = this.selectedText(text, textArea); selected = this.selectedText(text, textArea);
$textArea.focus(); $textArea.focus();
return this.insertText(textArea, text, tag, blockTag, selected, wrap); return this.insertText(textArea, text, tag, blockTag, selected, wrap);
}; };
gl.text.init = function(form) { gl.text.init = function(form) {
var self; var self;
self = this; self = this;
return $('.js-md', form).off('click').on('click', function() { return $('.js-md', form).off('click').on('click', function() {
var $this; var $this;
$this = $(this); $this = $(this);
return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend')); return self.updateText($this.closest('.md-area').find('textarea'), $this.data('md-tag'), $this.data('md-block'), !$this.data('md-prepend'));
}); });
}; };
gl.text.removeListeners = function(form) { gl.text.removeListeners = function(form) {
return $('.js-md', form).off(); return $('.js-md', form).off();
}; };
gl.text.humanize = function(string) { gl.text.humanize = function(string) {
return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1);
}; };
gl.text.pluralize = function(str, count) { gl.text.pluralize = function(str, count) {
return str + (count > 1 || count === 0 ? 's' : ''); return str + (count > 1 || count === 0 ? 's' : '');
}; };
gl.text.truncate = function(string, maxLength) { gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...'; return string.substr(0, (maxLength - 3)) + '...';
}; };
gl.text.dasherize = function(str) { gl.text.dasherize = function(str) {
return str.replace(/[_\s]+/g, '-'); return str.replace(/[_\s]+/g, '-');
}; };
gl.text.slugify = function(str) { gl.text.slugify = function(str) {
return str.trim().toLowerCase().latinise(); return str.trim().toLowerCase().latinise();
}; };
})(window);
}).call(window);
require('~/lib/utils/text_utility'); require('~/lib/utils/text_utility');
(() => { describe('text_utility', () => {
describe('text_utility', () => { describe('gl.text.getTextWidth', () => {
describe('gl.text.getTextWidth', () => { it('returns zero width when no text is passed', () => {
it('returns zero width when no text is passed', () => { expect(gl.text.getTextWidth('')).toBe(0);
expect(gl.text.getTextWidth('')).toBe(0); });
});
it('returns zero width when no text is passed and font is passed', () => { it('returns zero width when no text is passed and font is passed', () => {
expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0); expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0);
}); });
it('returns width when text is passed', () => { it('returns width when text is passed', () => {
expect(gl.text.getTextWidth('foo') > 0).toBe(true); expect(gl.text.getTextWidth('foo') > 0).toBe(true);
}); });
it('returns bigger width when font is larger', () => { it('returns bigger width when font is larger', () => {
const largeFont = gl.text.getTextWidth('foo', '100px sans-serif'); const largeFont = gl.text.getTextWidth('foo', '100px sans-serif');
const regular = gl.text.getTextWidth('foo', '10px sans-serif'); const regular = gl.text.getTextWidth('foo', '10px sans-serif');
expect(largeFont > regular).toBe(true); expect(largeFont > regular).toBe(true);
});
}); });
});
describe('gl.text.pluralize', () => { describe('gl.text.pluralize', () => {
it('returns pluralized', () => { it('returns pluralized', () => {
expect(gl.text.pluralize('test', 2)).toBe('tests'); expect(gl.text.pluralize('test', 2)).toBe('tests');
}); });
it('returns pluralized when count is 0', () => { it('returns pluralized when count is 0', () => {
expect(gl.text.pluralize('test', 0)).toBe('tests'); expect(gl.text.pluralize('test', 0)).toBe('tests');
}); });
it('does not return pluralized', () => { it('does not return pluralized', () => {
expect(gl.text.pluralize('test', 1)).toBe('test'); expect(gl.text.pluralize('test', 1)).toBe('test');
});
}); });
});
describe('gl.text.highCountTrim', () => { describe('gl.text.highCountTrim', () => {
it('returns 99+ for count >= 100', () => { it('returns 99+ for count >= 100', () => {
expect(gl.text.highCountTrim(105)).toBe('99+'); expect(gl.text.highCountTrim(105)).toBe('99+');
expect(gl.text.highCountTrim(100)).toBe('99+'); expect(gl.text.highCountTrim(100)).toBe('99+');
}); });
it('returns exact number for count < 100', () => { it('returns exact number for count < 100', () => {
expect(gl.text.highCountTrim(45)).toBe(45); expect(gl.text.highCountTrim(45)).toBe(45);
});
}); });
});
describe('gl.text.insertText', () => { describe('gl.text.insertText', () => {
let textArea; let textArea;
beforeAll(() => { beforeAll(() => {
textArea = document.createElement('textarea'); textArea = document.createElement('textarea');
document.querySelector('body').appendChild(textArea); document.querySelector('body').appendChild(textArea);
}); });
afterAll(() => { afterAll(() => {
textArea.parentNode.removeChild(textArea); textArea.parentNode.removeChild(textArea);
}); });
describe('without selection', () => { describe('without selection', () => {
it('inserts the tag on an empty line', () => { it('inserts the tag on an empty line', () => {
const initialValue = ''; const initialValue = '';
textArea.value = initialValue; textArea.value = initialValue;
textArea.selectionStart = 0; textArea.selectionStart = 0;
textArea.selectionEnd = 0; textArea.selectionEnd = 0;
gl.text.insertText(textArea, textArea.value, '*', null, '', false); gl.text.insertText(textArea, textArea.value, '*', null, '', false);
expect(textArea.value).toEqual(`${initialValue}* `); expect(textArea.value).toEqual(`${initialValue}* `);
}); });
it('inserts the tag on a new line if the current one is not empty', () => { it('inserts the tag on a new line if the current one is not empty', () => {
const initialValue = 'some text'; const initialValue = 'some text';
textArea.value = initialValue; textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length); textArea.setSelectionRange(initialValue.length, initialValue.length);
gl.text.insertText(textArea, textArea.value, '*', null, '', false); gl.text.insertText(textArea, textArea.value, '*', null, '', false);
expect(textArea.value).toEqual(`${initialValue}\n* `); expect(textArea.value).toEqual(`${initialValue}\n* `);
}); });
it('inserts the tag on the same line if the current line only contains spaces', () => { it('inserts the tag on the same line if the current line only contains spaces', () => {
const initialValue = ' '; const initialValue = ' ';
textArea.value = initialValue; textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length); textArea.setSelectionRange(initialValue.length, initialValue.length);
gl.text.insertText(textArea, textArea.value, '*', null, '', false); gl.text.insertText(textArea, textArea.value, '*', null, '', false);
expect(textArea.value).toEqual(`${initialValue}* `); expect(textArea.value).toEqual(`${initialValue}* `);
}); });
it('inserts the tag on the same line if the current line only contains tabs', () => { it('inserts the tag on the same line if the current line only contains tabs', () => {
const initialValue = '\t\t\t'; const initialValue = '\t\t\t';
textArea.value = initialValue; textArea.value = initialValue;
textArea.setSelectionRange(initialValue.length, initialValue.length); textArea.setSelectionRange(initialValue.length, initialValue.length);
gl.text.insertText(textArea, textArea.value, '*', null, '', false); gl.text.insertText(textArea, textArea.value, '*', null, '', false);
expect(textArea.value).toEqual(`${initialValue}* `); expect(textArea.value).toEqual(`${initialValue}* `);
});
}); });
}); });
}); });
})(); });
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