From e1e6e946d8cf889c487a3d10cbbae09d04ec3b6e Mon Sep 17 00:00:00 2001 From: Alfredo Sumaran Date: Mon, 10 Apr 2017 20:02:55 -0500 Subject: [PATCH] Remove IIFEs from files in cycle_analytics_bundle.js --- .../components/stage_code_component.js | 81 ++-- .../components/stage_issue_component.js | 85 +++-- .../components/stage_plan_component.js | 85 +++-- .../components/stage_production_component.js | 85 +++-- .../components/stage_review_component.js | 105 +++--- .../components/stage_staging_component.js | 85 +++-- .../components/stage_test_component.js | 85 +++-- .../components/total_time_component.js | 41 +- .../cycle_analytics_service.js | 72 ++-- .../cycle_analytics/cycle_analytics_store.js | 171 +++++---- .../javascripts/lib/utils/text_utility.js | 354 +++++++++--------- .../lib/utils/text_utility_spec.js | 172 +++++---- 12 files changed, 703 insertions(+), 718 deletions(-) diff --git a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js index 3f419a96ff9..80bd2df6f42 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_code_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_code_component.js @@ -2,46 +2,45 @@ import Vue from 'vue'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageCodeComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageCodeComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js index 7ffa38edd9e..20a43798fbe 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_issue_component.js @@ -2,48 +2,47 @@ import Vue from 'vue'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageIssueComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageIssueComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js index d736c8b0c28..f33cac3da82 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_plan_component.js @@ -2,50 +2,49 @@ import Vue from 'vue'; import iconCommit from '../svg/icon_commit.svg'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StagePlanComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, +global.cycleAnalytics.StagePlanComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, - data() { - return { iconCommit }; - }, + data() { + return { iconCommit }; + }, - template: ` -
-
- {{ stage.description }} - -
- + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js index 698a79ca68c..657f5385374 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_production_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_production_component.js @@ -2,48 +2,47 @@ import Vue from 'vue'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageProductionComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageProductionComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js index e63c41f2a57..8a801300647 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_review_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_review_component.js @@ -2,58 +2,57 @@ import Vue from 'vue'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageReviewComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageReviewComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js index d51f7134e25..4a286379588 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_staging_component.js @@ -2,48 +2,47 @@ import Vue from 'vue'; import iconBranch from '../svg/icon_branch.svg'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageStagingComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - data() { - return { iconBranch }; - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageStagingComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + data() { + return { iconBranch }; + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js index 17ae3a9ddc1..e306026429e 100644 --- a/app/assets/javascripts/cycle_analytics/components/stage_test_component.js +++ b/app/assets/javascripts/cycle_analytics/components/stage_test_component.js @@ -3,48 +3,47 @@ import Vue from 'vue'; import iconBuildStatus from '../svg/icon_build_status.svg'; import iconBranch from '../svg/icon_branch.svg'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.StageTestComponent = Vue.extend({ - props: { - items: Array, - stage: Object, - }, - data() { - return { iconBuildStatus, iconBranch }; - }, - template: ` -
-
- {{ stage.description }} - -
- +global.cycleAnalytics.StageTestComponent = Vue.extend({ + props: { + items: Array, + stage: Object, + }, + data() { + return { iconBuildStatus, iconBranch }; + }, + template: ` +
+
+ {{ stage.description }} +
- `, - }); -})(window.gl || (window.gl = {})); + +
+ `, +}); diff --git a/app/assets/javascripts/cycle_analytics/components/total_time_component.js b/app/assets/javascripts/cycle_analytics/components/total_time_component.js index b4442ea5566..77edcb76273 100644 --- a/app/assets/javascripts/cycle_analytics/components/total_time_component.js +++ b/app/assets/javascripts/cycle_analytics/components/total_time_component.js @@ -2,25 +2,24 @@ import Vue from 'vue'; -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - global.cycleAnalytics.TotalTimeComponent = Vue.extend({ - props: { - time: Object, - }, - template: ` - - - - - `, - }); -})(window.gl || (window.gl = {})); +global.cycleAnalytics.TotalTimeComponent = Vue.extend({ + props: { + time: Object, + }, + template: ` + + + + + `, +}); diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js index 9f74b14c4b9..681d6eef565 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_service.js @@ -1,41 +1,41 @@ /* eslint-disable no-param-reassign */ -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; - class CycleAnalyticsService { - constructor(options) { - this.requestPath = options.requestPath; - } +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - fetchCycleAnalyticsData(options) { - options = options || { startDate: 30 }; - - return $.ajax({ - url: this.requestPath, - method: 'GET', - dataType: 'json', - contentType: 'application/json', - data: { - cycle_analytics: { - start_date: options.startDate, - }, - }, - }); - } - - fetchStageData(options) { - const { - stage, - startDate, - } = options; - - return $.get(`${this.requestPath}/events/${stage.title.toLowerCase()}.json`, { - cycle_analytics: { - start_date: startDate, - }, - }); - } +class CycleAnalyticsService { + constructor(options) { + this.requestPath = options.requestPath; } - global.cycleAnalytics.CycleAnalyticsService = CycleAnalyticsService; -})(window.gl || (window.gl = {})); + fetchCycleAnalyticsData(options) { + options = options || { startDate: 30 }; + + return $.ajax({ + url: this.requestPath, + method: 'GET', + dataType: 'json', + contentType: 'application/json', + data: { + cycle_analytics: { + 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; diff --git a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js index 7ae9de7297c..6536a8fd7fa 100644 --- a/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js +++ b/app/assets/javascripts/cycle_analytics/cycle_analytics_store.js @@ -3,102 +3,101 @@ require('../lib/utils/text_utility'); const DEFAULT_EVENT_OBJECTS = require('./default_event_objects'); -((global) => { - global.cycleAnalytics = global.cycleAnalytics || {}; +const global = window.gl || (window.gl = {}); +global.cycleAnalytics = global.cycleAnalytics || {}; - 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.', - 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.', - 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.', - 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.', - }; +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.', + 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.', + 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.', + 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.', +}; - global.cycleAnalytics.CycleAnalyticsStore = { - state: { - summary: '', - stats: '', - analytics: '', - events: [], - stages: [], - }, - setCycleAnalyticsData(data) { - this.state = Object.assign(this.state, this.decorateData(data)); - }, - decorateData(data) { - const newData = {}; +global.cycleAnalytics.CycleAnalyticsStore = { + state: { + summary: '', + stats: '', + analytics: '', + events: [], + stages: [], + }, + setCycleAnalyticsData(data) { + this.state = Object.assign(this.state, this.decorateData(data)); + }, + decorateData(data) { + const newData = {}; - newData.stages = data.stats || []; - newData.summary = data.summary || []; + newData.stages = data.stats || []; + newData.summary = data.summary || []; - newData.summary.forEach((item) => { - item.value = item.value || '-'; - }); + newData.summary.forEach((item) => { + item.value = item.value || '-'; + }); - newData.stages.forEach((item) => { - const stageSlug = gl.text.dasherize(item.title.toLowerCase()); - item.active = false; - item.isUserAllowed = data.permissions[stageSlug]; - item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug]; - item.component = `stage-${stageSlug}-component`; - item.slug = stageSlug; - }); - newData.analytics = data; - return newData; - }, - setLoadingState(state) { - this.state.isLoading = state; - }, - setErrorState(state) { - this.state.hasError = state; - }, - deactivateAllStages() { - this.state.stages.forEach((stage) => { - stage.active = false; - }); - }, - setActiveStage(stage) { - this.deactivateAllStages(); - stage.active = true; - }, - setStageEvents(events, stage) { - this.state.events = this.decorateEvents(events, stage); - }, - decorateEvents(events, stage) { - const newEvents = []; + newData.stages.forEach((item) => { + const stageSlug = gl.text.dasherize(item.title.toLowerCase()); + item.active = false; + item.isUserAllowed = data.permissions[stageSlug]; + item.emptyStageText = EMPTY_STAGE_TEXTS[stageSlug]; + item.component = `stage-${stageSlug}-component`; + item.slug = stageSlug; + }); + newData.analytics = data; + return newData; + }, + setLoadingState(state) { + this.state.isLoading = state; + }, + setErrorState(state) { + this.state.hasError = state; + }, + deactivateAllStages() { + this.state.stages.forEach((stage) => { + stage.active = false; + }); + }, + setActiveStage(stage) { + this.deactivateAllStages(); + stage.active = true; + }, + setStageEvents(events, stage) { + this.state.events = this.decorateEvents(events, stage); + }, + decorateEvents(events, stage) { + const newEvents = []; - events.forEach((item) => { - if (!item) return; + events.forEach((item) => { + 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) { - eventItem.author.webUrl = eventItem.author.web_url; - eventItem.author.avatarUrl = eventItem.author.avatar_url; - } + if (eventItem.author) { + eventItem.author.webUrl = eventItem.author.web_url; + eventItem.author.avatarUrl = eventItem.author.avatar_url; + } - if (eventItem.created_at) eventItem.createdAt = eventItem.created_at; - if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha; - if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url; + if (eventItem.created_at) eventItem.createdAt = eventItem.created_at; + if (eventItem.short_sha) eventItem.shortSha = eventItem.short_sha; + if (eventItem.commit_url) eventItem.commitUrl = eventItem.commit_url; - delete eventItem.author.web_url; - delete eventItem.author.avatar_url; - delete eventItem.total_time; - delete eventItem.created_at; - delete eventItem.short_sha; - delete eventItem.commit_url; + delete eventItem.author.web_url; + delete eventItem.author.avatar_url; + delete eventItem.total_time; + delete eventItem.created_at; + delete eventItem.short_sha; + delete eventItem.commit_url; - newEvents.push(eventItem); - }); + newEvents.push(eventItem); + }); - return newEvents; - }, - currentActiveStage() { - return this.state.stages.find(stage => stage.active); - }, - }; -})(window.gl || (window.gl = {})); + return newEvents; + }, + currentActiveStage() { + return this.state.stages.find(stage => stage.active); + }, +}; diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js index 2e5f8a09fc1..fecd531328d 100644 --- a/app/assets/javascripts/lib/utils/text_utility.js +++ b/app/assets/javascripts/lib/utils/text_utility.js @@ -1,192 +1,188 @@ -/* 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'); -(function() { - (function(w) { - var base; - if (w.gl == null) { - w.gl = {}; +var base; +var w = window; +if (w.gl == null) { + w.gl = {}; +} +if ((base = w.gl).text == null) { + 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); } - if ((base = w.gl).text == null) { - base.text = {}; + 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 + if (selected.indexOf('\n') === 0) { + removedFirstNewLine = true; + selected = selected.replace(/\n+/, ''); + } + + // Remove the last newline + if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) { + removedLastNewLine = true; + selected = selected.replace(/\n$/, ''); + } + + selectedSplit = selected.split('\n'); + + if (!wrap) { + lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n'); + + // Check whether the current line is empty or consists only of spaces(=handle as empty) + if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) { + currentLineEmpty = true; } - 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); - } - 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 - if (selected.indexOf('\n') === 0) { - removedFirstNewLine = true; - selected = selected.replace(/\n+/, ''); - } + startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : ''; - // Remove the last newline - if (textArea.selectionEnd - textArea.selectionStart > selected.replace(/\n$/, '').length) { - removedLastNewLine = true; - selected = selected.replace(/\n$/, ''); - } - - selectedSplit = selected.split('\n'); - - if (!wrap) { - lastNewLine = textArea.value.substr(0, textArea.selectionStart).lastIndexOf('\n'); - - // Check whether the current line is empty or consists only of spaces(=handle as empty) - if (/^\s*$/.test(textArea.value.substring(lastNewLine, textArea.selectionStart))) { - currentLineEmpty = true; - } - } - - startChar = !wrap && !currentLineEmpty && textArea.selectionStart > 0 ? '\n' : ''; - - if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) { - if (blockTag != null) { - insertText = this.blockTagText(text, textArea, blockTag, selected); + if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) { + if (blockTag != null) { + insertText = this.blockTagText(text, textArea, blockTag, selected); + } else { + insertText = selectedSplit.map(function(val) { + if (val.indexOf(tag) === 0) { + return "" + (val.replace(tag, '')); } else { - insertText = selectedSplit.map(function(val) { - if (val.indexOf(tag) === 0) { - return "" + (val.replace(tag, '')); - } else { - return "" + tag + val; - } - }).join('\n'); + return "" + tag + val; } - } else { - insertText = "" + startChar + tag + selected + (wrap ? tag : ' '); - } + }).join('\n'); + } + } else { + insertText = "" + startChar + tag + selected + (wrap ? tag : ' '); + } - if (removedFirstNewLine) { - insertText = '\n' + insertText; - } + if (removedFirstNewLine) { + insertText = '\n' + insertText; + } - if (removedLastNewLine) { - insertText += '\n'; - } + if (removedLastNewLine) { + insertText += '\n'; + } - if (document.queryCommandSupported('insertText')) { - inserted = document.execCommand('insertText', false, insertText); - } - if (!inserted) { - try { - document.execCommand("ms-beginUndoUnit"); - } catch (error) {} - textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText); - try { - document.execCommand("ms-endUndoUnit"); - } catch (error) {} - } - return this.moveCursor(textArea, tag, wrap, removedLastNewLine); - }; - gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) { - var pos; - if (!textArea.setSelectionRange) { - return; - } - if (textArea.selectionStart === textArea.selectionEnd) { - if (wrapped) { - pos = textArea.selectionStart - tag.length; - } else { - pos = textArea.selectionStart; - } + if (document.queryCommandSupported('insertText')) { + inserted = document.execCommand('insertText', false, insertText); + } + if (!inserted) { + try { + document.execCommand("ms-beginUndoUnit"); + } catch (error) {} + textArea.value = this.replaceRange(text, textArea.selectionStart, textArea.selectionEnd, insertText); + try { + document.execCommand("ms-endUndoUnit"); + } catch (error) {} + } + return this.moveCursor(textArea, tag, wrap, removedLastNewLine); +}; +gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) { + var pos; + if (!textArea.setSelectionRange) { + return; + } + if (textArea.selectionStart === textArea.selectionEnd) { + if (wrapped) { + pos = textArea.selectionStart - tag.length; + } else { + pos = textArea.selectionStart; + } - if (removedLastNewLine) { - pos -= 1; - } + if (removedLastNewLine) { + pos -= 1; + } - return textArea.setSelectionRange(pos, pos); - } - }; - gl.text.updateText = function(textArea, tag, blockTag, wrap) { - var $textArea, selected, text; - $textArea = $(textArea); - textArea = $textArea.get(0); - text = $textArea.val(); - selected = this.selectedText(text, textArea); - $textArea.focus(); - return this.insertText(textArea, text, tag, blockTag, selected, wrap); - }; - gl.text.init = function(form) { - var self; - self = this; - return $('.js-md', form).off('click').on('click', function() { - var $this; - $this = $(this); - 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) { - return $('.js-md', form).off(); - }; - gl.text.humanize = function(string) { - return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); - }; - gl.text.pluralize = function(str, count) { - return str + (count > 1 || count === 0 ? 's' : ''); - }; - gl.text.truncate = function(string, maxLength) { - return string.substr(0, (maxLength - 3)) + '...'; - }; - gl.text.dasherize = function(str) { - return str.replace(/[_\s]+/g, '-'); - }; - gl.text.slugify = function(str) { - return str.trim().toLowerCase().latinise(); - }; - })(window); -}).call(window); + return textArea.setSelectionRange(pos, pos); + } +}; +gl.text.updateText = function(textArea, tag, blockTag, wrap) { + var $textArea, selected, text; + $textArea = $(textArea); + textArea = $textArea.get(0); + text = $textArea.val(); + selected = this.selectedText(text, textArea); + $textArea.focus(); + return this.insertText(textArea, text, tag, blockTag, selected, wrap); +}; +gl.text.init = function(form) { + var self; + self = this; + return $('.js-md', form).off('click').on('click', function() { + var $this; + $this = $(this); + 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) { + return $('.js-md', form).off(); +}; +gl.text.humanize = function(string) { + return string.charAt(0).toUpperCase() + string.replace(/_/g, ' ').slice(1); +}; +gl.text.pluralize = function(str, count) { + return str + (count > 1 || count === 0 ? 's' : ''); +}; +gl.text.truncate = function(string, maxLength) { + return string.substr(0, (maxLength - 3)) + '...'; +}; +gl.text.dasherize = function(str) { + return str.replace(/[_\s]+/g, '-'); +}; +gl.text.slugify = function(str) { + return str.trim().toLowerCase().latinise(); +}; diff --git a/spec/javascripts/lib/utils/text_utility_spec.js b/spec/javascripts/lib/utils/text_utility_spec.js index 4200e943121..daef9b93fa5 100644 --- a/spec/javascripts/lib/utils/text_utility_spec.js +++ b/spec/javascripts/lib/utils/text_utility_spec.js @@ -1,110 +1,108 @@ require('~/lib/utils/text_utility'); -(() => { - describe('text_utility', () => { - describe('gl.text.getTextWidth', () => { - it('returns zero width when no text is passed', () => { - expect(gl.text.getTextWidth('')).toBe(0); - }); - - it('returns zero width when no text is passed and font is passed', () => { - expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0); - }); - - it('returns width when text is passed', () => { - expect(gl.text.getTextWidth('foo') > 0).toBe(true); - }); - - it('returns bigger width when font is larger', () => { - const largeFont = gl.text.getTextWidth('foo', '100px sans-serif'); - const regular = gl.text.getTextWidth('foo', '10px sans-serif'); - expect(largeFont > regular).toBe(true); - }); +describe('text_utility', () => { + describe('gl.text.getTextWidth', () => { + it('returns zero width when no text is passed', () => { + expect(gl.text.getTextWidth('')).toBe(0); }); - describe('gl.text.pluralize', () => { - it('returns pluralized', () => { - expect(gl.text.pluralize('test', 2)).toBe('tests'); - }); - - it('returns pluralized when count is 0', () => { - expect(gl.text.pluralize('test', 0)).toBe('tests'); - }); - - it('does not return pluralized', () => { - expect(gl.text.pluralize('test', 1)).toBe('test'); - }); + it('returns zero width when no text is passed and font is passed', () => { + expect(gl.text.getTextWidth('', '100px sans-serif')).toBe(0); }); - describe('gl.text.highCountTrim', () => { - it('returns 99+ for count >= 100', () => { - expect(gl.text.highCountTrim(105)).toBe('99+'); - expect(gl.text.highCountTrim(100)).toBe('99+'); - }); - - it('returns exact number for count < 100', () => { - expect(gl.text.highCountTrim(45)).toBe(45); - }); + it('returns width when text is passed', () => { + expect(gl.text.getTextWidth('foo') > 0).toBe(true); }); - describe('gl.text.insertText', () => { - let textArea; + it('returns bigger width when font is larger', () => { + const largeFont = gl.text.getTextWidth('foo', '100px sans-serif'); + const regular = gl.text.getTextWidth('foo', '10px sans-serif'); + expect(largeFont > regular).toBe(true); + }); + }); - beforeAll(() => { - textArea = document.createElement('textarea'); - document.querySelector('body').appendChild(textArea); + describe('gl.text.pluralize', () => { + it('returns pluralized', () => { + expect(gl.text.pluralize('test', 2)).toBe('tests'); + }); + + it('returns pluralized when count is 0', () => { + expect(gl.text.pluralize('test', 0)).toBe('tests'); + }); + + it('does not return pluralized', () => { + expect(gl.text.pluralize('test', 1)).toBe('test'); + }); + }); + + describe('gl.text.highCountTrim', () => { + it('returns 99+ for count >= 100', () => { + expect(gl.text.highCountTrim(105)).toBe('99+'); + expect(gl.text.highCountTrim(100)).toBe('99+'); + }); + + it('returns exact number for count < 100', () => { + expect(gl.text.highCountTrim(45)).toBe(45); + }); + }); + + describe('gl.text.insertText', () => { + let textArea; + + beforeAll(() => { + textArea = document.createElement('textarea'); + document.querySelector('body').appendChild(textArea); + }); + + afterAll(() => { + textArea.parentNode.removeChild(textArea); + }); + + describe('without selection', () => { + it('inserts the tag on an empty line', () => { + const initialValue = ''; + + textArea.value = initialValue; + textArea.selectionStart = 0; + textArea.selectionEnd = 0; + + gl.text.insertText(textArea, textArea.value, '*', null, '', false); + + expect(textArea.value).toEqual(`${initialValue}* `); }); - afterAll(() => { - textArea.parentNode.removeChild(textArea); + it('inserts the tag on a new line if the current one is not empty', () => { + const initialValue = 'some text'; + + textArea.value = initialValue; + textArea.setSelectionRange(initialValue.length, initialValue.length); + + gl.text.insertText(textArea, textArea.value, '*', null, '', false); + + expect(textArea.value).toEqual(`${initialValue}\n* `); }); - describe('without selection', () => { - it('inserts the tag on an empty line', () => { - const initialValue = ''; + it('inserts the tag on the same line if the current line only contains spaces', () => { + const initialValue = ' '; - textArea.value = initialValue; - textArea.selectionStart = 0; - textArea.selectionEnd = 0; + textArea.value = initialValue; + 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 a new line if the current one is not empty', () => { - const initialValue = 'some text'; + it('inserts the tag on the same line if the current line only contains tabs', () => { + const initialValue = '\t\t\t'; - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); + textArea.value = initialValue; + 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* `); - }); - - it('inserts the tag on the same line if the current line only contains spaces', () => { - const initialValue = ' '; - - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); - - gl.text.insertText(textArea, textArea.value, '*', null, '', false); - - expect(textArea.value).toEqual(`${initialValue}* `); - }); - - it('inserts the tag on the same line if the current line only contains tabs', () => { - const initialValue = '\t\t\t'; - - textArea.value = initialValue; - textArea.setSelectionRange(initialValue.length, initialValue.length); - - gl.text.insertText(textArea, textArea.value, '*', null, '', false); - - expect(textArea.value).toEqual(`${initialValue}* `); - }); + expect(textArea.value).toEqual(`${initialValue}* `); }); }); }); -})(); +});