summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/lib/utils
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/lib/utils')
-rw-r--r--app/assets/javascripts/lib/utils/animate.js3
-rw-r--r--app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js.es6113
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js74
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js.es6163
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js109
-rw-r--r--app/assets/javascripts/lib/utils/jquery.timeago.js182
-rw-r--r--app/assets/javascripts/lib/utils/notify.js4
-rw-r--r--app/assets/javascripts/lib/utils/pretty_time.js.es665
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js60
-rw-r--r--app/assets/javascripts/lib/utils/type_utility.js3
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js7
11 files changed, 466 insertions, 317 deletions
diff --git a/app/assets/javascripts/lib/utils/animate.js b/app/assets/javascripts/lib/utils/animate.js
index a68edab2aad..ce090a2e4fd 100644
--- a/app/assets/javascripts/lib/utils/animate.js
+++ b/app/assets/javascripts/lib/utils/animate.js
@@ -1,4 +1,4 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-param-reassign, no-void, prefer-template, no-var, new-cap, prefer-arrow-callback, consistent-return, max-len */
(function() {
(function(w) {
if (w.gl == null) {
@@ -46,5 +46,4 @@
return dfd.promise();
};
})(window);
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js.es6 b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js.es6
new file mode 100644
index 00000000000..e810ee85bd3
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/bootstrap_linked_tabs.js.es6
@@ -0,0 +1,113 @@
+/**
+ * Linked Tabs
+ *
+ * Handles persisting and restores the current tab selection and content.
+ * Reusable component for static content.
+ *
+ * ### Example Markup
+ *
+ * <ul class="nav-links tab-links">
+ * <li class="active">
+ * <a data-action="tab1" data-target="#tab1" data-toggle="tab" href="/path/tab1">
+ * Tab 1
+ * </a>
+ * </li>
+ * <li class="groups-tab">
+ * <a data-action="tab2" data-target="#tab2" data-toggle="tab" href="/path/tab2">
+ * Tab 2
+ * </a>
+ * </li>
+ *
+ *
+ * <div class="tab-content">
+ * <div class="tab-pane" id="tab1">
+ * Tab 1 Content
+ * </div>
+ * <div class="tab-pane" id="tab2">
+ * Tab 2 Content
+ * </div>
+ * </div>
+ *
+ *
+ * ### How to use
+ *
+ * new window.gl.LinkedTabs({
+ * action: "#{controller.action_name}",
+ * defaultAction: 'tab1',
+ * parentEl: '.tab-links'
+ * });
+ */
+
+(() => {
+ window.gl = window.gl || {};
+
+ window.gl.LinkedTabs = class LinkedTabs {
+ /**
+ * Binds the events and activates de default tab.
+ *
+ * @param {Object} options
+ */
+ constructor(options) {
+ this.options = options || {};
+
+ this.defaultAction = this.options.defaultAction;
+ this.action = this.options.action || this.defaultAction;
+
+ if (this.action === 'show') {
+ this.action = this.defaultAction;
+ }
+
+ this.currentLocation = window.location;
+
+ const tabSelector = `${this.options.parentEl} a[data-toggle="tab"]`;
+
+ // since this is a custom event we need jQuery :(
+ $(document)
+ .off('shown.bs.tab', tabSelector)
+ .on('shown.bs.tab', tabSelector, e => this.tabShown(e));
+
+ this.activateTab(this.action);
+ }
+
+ /**
+ * Handles the `shown.bs.tab` event to set the currect url action.
+ *
+ * @param {type} evt
+ * @return {Function}
+ */
+ tabShown(evt) {
+ const source = evt.target.getAttribute('href');
+
+ return this.setCurrentAction(source);
+ }
+
+ /**
+ * Updates the URL with the path that matched the given action.
+ *
+ * @param {String} source
+ * @return {String}
+ */
+ setCurrentAction(source) {
+ const copySource = source;
+
+ copySource.replace(/\/+$/, '');
+
+ const newState = `${copySource}${this.currentLocation.search}${this.currentLocation.hash}`;
+
+ history.replaceState({
+ turbolinks: true,
+ url: newState,
+ }, document.title, newState);
+ return newState;
+ }
+
+ /**
+ * Given the current action activates the correct tab.
+ * http://getbootstrap.com/javascript/#tab-show
+ * Note: Will trigger `shown.bs.tab`
+ */
+ activateTab() {
+ return $(`${this.options.parentEl} a[data-action='${this.action}']`).tab('show');
+ }
+ };
+})();
diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js
deleted file mode 100644
index 21efe2d76dd..00000000000
--- a/app/assets/javascripts/lib/utils/common_utils.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* eslint-disable */
-(function() {
- (function(w) {
- var base;
- w.gl || (w.gl = {});
- (base = w.gl).utils || (base.utils = {});
- w.gl.utils.isInGroupsPage = function() {
- return gl.utils.getPagePath() === 'groups';
- };
- w.gl.utils.isInProjectPage = function() {
- return gl.utils.getPagePath() === 'projects';
- };
- w.gl.utils.getProjectSlug = function() {
- if (this.isInProjectPage()) {
- return $('body').data('project');
- } else {
- return null;
- }
- };
- w.gl.utils.getGroupSlug = function() {
- if (this.isInGroupsPage()) {
- return $('body').data('group');
- } else {
- return null;
- }
- };
- gl.utils.updateTooltipTitle = function($tooltipEl, newTitle) {
- return $tooltipEl.tooltip('destroy').attr('title', newTitle).tooltip('fixTitle');
- };
- gl.utils.preventDisabledButtons = function() {
- return $('.btn').click(function(e) {
- if ($(this).hasClass('disabled')) {
- e.preventDefault();
- e.stopImmediatePropagation();
- return false;
- }
- });
- };
- gl.utils.getPagePath = function() {
- return $('body').data('page').split(':')[0];
- };
- gl.utils.parseUrl = function (url) {
- var parser = document.createElement('a');
- parser.href = url;
- return parser;
- };
-
- gl.utils.cleanupBeforeFetch = function() {
- // Unbind scroll events
- $(document).off('scroll');
- // Close any open tooltips
- $('.has-tooltip, [data-toggle="tooltip"]').tooltip('destroy');
- };
-
- return jQuery.timefor = function(time, suffix, expiredLabel) {
- var suffixFromNow, timefor;
- if (!time) {
- return '';
- }
- suffix || (suffix = 'remaining');
- expiredLabel || (expiredLabel = 'Past due');
- jQuery.timeago.settings.allowFuture = true;
- suffixFromNow = jQuery.timeago.settings.strings.suffixFromNow;
- jQuery.timeago.settings.strings.suffixFromNow = suffix;
- timefor = $.timeago(time);
- if (timefor.indexOf('ago') > -1) {
- timefor = expiredLabel;
- }
- jQuery.timeago.settings.strings.suffixFromNow = suffixFromNow;
- return timefor;
- };
- })(window);
-
-}).call(this);
diff --git a/app/assets/javascripts/lib/utils/common_utils.js.es6 b/app/assets/javascripts/lib/utils/common_utils.js.es6
new file mode 100644
index 00000000000..6d57d31f380
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/common_utils.js.es6
@@ -0,0 +1,163 @@
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-unused-expressions, no-param-reassign, no-else-return, quotes, object-shorthand, comma-dangle, camelcase, one-var, vars-on-top, one-var-declaration-per-line, no-return-assign, consistent-return, max-len, prefer-template */
+(function() {
+ (function(w) {
+ var base;
+ w.gl || (w.gl = {});
+ (base = w.gl).utils || (base.utils = {});
+ w.gl.utils.isInGroupsPage = function() {
+ return gl.utils.getPagePath() === 'groups';
+ };
+ w.gl.utils.isInProjectPage = function() {
+ return gl.utils.getPagePath() === 'projects';
+ };
+ w.gl.utils.getProjectSlug = function() {
+ if (this.isInProjectPage()) {
+ return $('body').data('project');
+ } else {
+ return null;
+ }
+ };
+ w.gl.utils.getGroupSlug = function() {
+ if (this.isInGroupsPage()) {
+ return $('body').data('group');
+ } else {
+ return null;
+ }
+ };
+
+ w.gl.utils.ajaxGet = function(url) {
+ return $.ajax({
+ type: "GET",
+ url: url,
+ dataType: "script"
+ });
+ };
+
+ w.gl.utils.extractLast = function(term) {
+ return this.split(term).pop();
+ };
+
+ w.gl.utils.rstrip = function rstrip(val) {
+ if (val) {
+ return val.replace(/\s+$/, '');
+ } else {
+ return val;
+ }
+ };
+
+ w.gl.utils.disableButtonIfEmptyField = function(field_selector, button_selector, event_name) {
+ event_name = event_name || 'input';
+ var closest_submit, field, that;
+ that = this;
+ field = $(field_selector);
+ closest_submit = field.closest('form').find(button_selector);
+ if (this.rstrip(field.val()) === "") {
+ closest_submit.disable();
+ }
+ return field.on(event_name, function() {
+ if (that.rstrip($(this).val()) === "") {
+ return closest_submit.disable();
+ } else {
+ return closest_submit.enable();
+ }
+ });
+ };
+
+ // automatically adjust scroll position for hash urls taking the height of the navbar into account
+ // https://github.com/twitter/bootstrap/issues/1768
+ w.gl.utils.handleLocationHash = function() {
+ var hash = w.gl.utils.getLocationHash();
+ if (!hash) return;
+
+ var navbar = document.querySelector('.navbar-gitlab');
+ var subnav = document.querySelector('.layout-nav');
+ var fixedTabs = document.querySelector('.js-tabs-affix');
+
+ var adjustment = 0;
+ if (navbar) adjustment -= navbar.offsetHeight;
+ if (subnav) adjustment -= subnav.offsetHeight;
+
+ // scroll to user-generated markdown anchor if we cannot find a match
+ if (document.getElementById(hash) === null) {
+ var target = document.getElementById('user-content-' + hash);
+ if (target && target.scrollIntoView) {
+ target.scrollIntoView(true);
+ window.scrollBy(0, adjustment);
+ }
+ } else {
+ // only adjust for fixedTabs when not targeting user-generated content
+ if (fixedTabs) {
+ adjustment -= fixedTabs.offsetHeight;
+ }
+ window.scrollBy(0, adjustment);
+ }
+ };
+
+ // Check if element scrolled into viewport from above or below
+ // Courtesy http://stackoverflow.com/a/7557433/414749
+ w.gl.utils.isInViewport = function(el) {
+ var rect = el.getBoundingClientRect();
+
+ return (
+ rect.top >= 0 &&
+ rect.left >= 0 &&
+ rect.bottom <= window.innerHeight &&
+ rect.right <= window.innerWidth
+ );
+ };
+
+ gl.utils.getPagePath = function(index) {
+ index = index || 0;
+ return $('body').data('page').split(':')[index];
+ };
+
+ gl.utils.parseUrl = function (url) {
+ var parser = document.createElement('a');
+ parser.href = url;
+ return parser;
+ };
+
+ gl.utils.parseUrlPathname = function (url) {
+ var parsedUrl = gl.utils.parseUrl(url);
+ // parsedUrl.pathname will return an absolute path for Firefox and a relative path for IE11
+ // We have to make sure we always have an absolute path.
+ return parsedUrl.pathname.charAt(0) === '/' ? parsedUrl.pathname : '/' + parsedUrl.pathname;
+ };
+
+ gl.utils.getUrlParamsArray = function () {
+ // We can trust that each param has one & since values containing & will be encoded
+ // Remove the first character of search as it is always ?
+ return window.location.search.slice(1).split('&');
+ };
+
+ gl.utils.isMetaKey = function(e) {
+ return e.metaKey || e.ctrlKey || e.altKey || e.shiftKey;
+ };
+
+ gl.utils.scrollToElement = function($el) {
+ var top = $el.offset().top;
+ gl.navBarHeight = gl.navBarHeight || $('.navbar-gitlab').height();
+ gl.navLinksHeight = gl.navLinksHeight || $('.nav-links').height();
+ gl.mrTabsHeight = gl.mrTabsHeight || $('.merge-request-tabs').height();
+
+ return $('body, html').animate({
+ scrollTop: top - (gl.navBarHeight + gl.navLinksHeight + gl.mrTabsHeight)
+ }, 200);
+ };
+
+ /**
+ this will take in the `name` of the param you want to parse in the url
+ if the name does not exist this function will return `null`
+ otherwise it will return the value of the param key provided
+ */
+ w.gl.utils.getParameterByName = (name) => {
+ const url = window.location.href;
+ name = name.replace(/[[\]]/g, '\\$&');
+ const regex = new RegExp(`[?&]${name}(=([^&#]*)|&|#|$)`);
+ const results = regex.exec(url);
+ if (!results) return null;
+ if (!results[2]) return '';
+ return decodeURIComponent(results[2].replace(/\+/g, ' '));
+ };
+ })(window);
+}).call(this);
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 59e526ed623..3ed8bfd5651 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -1,4 +1,10 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, comma-dangle, no-unused-expressions, prefer-template, max-len */
+/* global timeago */
+/* global dateFormat */
+
+/*= require timeago */
+/*= require date.format */
+
(function() {
(function(w) {
var base;
@@ -22,51 +28,66 @@
if (setTimeago == null) {
setTimeago = true;
}
- $timeagoEls.each(function() {
- var $el;
- $el = $(this);
- return $el.attr('title', gl.utils.formatDate($el.attr('datetime')));
+
+ $timeagoEls.filter(':not([data-timeago-rendered])').each(function() {
+ var $el = $(this);
+ $el.attr('title', gl.utils.formatDate($el.attr('datetime')));
+
+ if (setTimeago) {
+ // Recreate with custom template
+ $el.tooltip({
+ template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
+ });
+ }
+
+ $el.attr('data-timeago-rendered', true);
+ gl.utils.renderTimeago($el);
});
- if (setTimeago) {
- $timeagoEls.timeago();
- $timeagoEls.tooltip('destroy');
- // Recreate with custom template
- return $timeagoEls.tooltip({
- template: '<div class="tooltip local-timeago" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'
- });
- }
};
- w.gl.utils.shortTimeAgo = function($el) {
- var shortLocale, tmpLocale;
- shortLocale = {
- prefixAgo: null,
- prefixFromNow: null,
- suffixAgo: 'ago',
- suffixFromNow: 'from now',
- seconds: '1 min',
- minute: '1 min',
- minutes: '%d mins',
- hour: '1 hr',
- hours: '%d hrs',
- day: '1 day',
- days: '%d days',
- month: '1 month',
- months: '%d months',
- year: '1 year',
- years: '%d years',
- wordSeparator: ' ',
- numbers: []
+ w.gl.utils.getTimeago = function() {
+ var locale = function(number, index) {
+ return [
+ ['less than a minute ago', 'a while'],
+ ['less than a minute ago', 'in %s seconds'],
+ ['about a minute ago', 'in 1 minute'],
+ ['%s minutes ago', 'in %s minutes'],
+ ['about an hour ago', 'in 1 hour'],
+ ['about %s hours ago', 'in %s hours'],
+ ['a day ago', 'in 1 day'],
+ ['%s days ago', 'in %s days'],
+ ['a week ago', 'in 1 week'],
+ ['%s weeks ago', 'in %s weeks'],
+ ['a month ago', 'in 1 month'],
+ ['%s months ago', 'in %s months'],
+ ['a year ago', 'in 1 year'],
+ ['%s years ago', 'in %s years']
+ ][index];
};
- tmpLocale = $.timeago.settings.strings;
- $el.each(function(el) {
- var $el1;
- $el1 = $(this);
- return $el1.attr('title', gl.utils.formatDate($el.attr('datetime')));
- });
- $.timeago.settings.strings = shortLocale;
- $el.timeago();
- $.timeago.settings.strings = tmpLocale;
+
+ timeago.register('gl_en', locale);
+ return timeago();
+ };
+
+ w.gl.utils.timeFor = function(time, suffix, expiredLabel) {
+ var timefor;
+ if (!time) {
+ return '';
+ }
+ suffix || (suffix = 'remaining');
+ expiredLabel || (expiredLabel = 'Past due');
+ timefor = gl.utils.getTimeago().format(time).replace('in', '');
+ if (timefor.indexOf('ago') > -1) {
+ timefor = expiredLabel;
+ } else {
+ timefor = timefor.trim() + ' ' + suffix;
+ }
+ return timefor;
+ };
+
+ w.gl.utils.renderTimeago = function($element) {
+ var timeagoInstance = gl.utils.getTimeago();
+ timeagoInstance.render($element, 'gl_en');
};
w.gl.utils.getDayDifference = function(a, b) {
@@ -75,8 +96,6 @@
var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate());
return Math.floor((date2 - date1) / millisecondsPerDay);
- }
-
+ };
})(window);
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js
deleted file mode 100644
index de76cdd2ea7..00000000000
--- a/app/assets/javascripts/lib/utils/jquery.timeago.js
+++ /dev/null
@@ -1,182 +0,0 @@
-/* eslint-disable */
-/**
- * Timeago is a jQuery plugin that makes it easy to support automatically
- * updating fuzzy timestamps (e.g. "4 minutes ago" or "about 1 day ago").
- *
- * @name timeago
- * @version 1.1.0
- * @requires jQuery v1.2.3+
- * @author Ryan McGeary
- * @license MIT License - http://www.opensource.org/licenses/mit-license.php
- *
- * For usage and examples, visit:
- * http://timeago.yarp.com/
- *
- * Copyright (c) 2008-2013, Ryan McGeary (ryan -[at]- mcgeary [*dot*] org)
- */
-
-(function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD. Register as an anonymous module.
- define(['jquery'], factory);
- } else {
- // Browser globals
- factory(jQuery);
- }
-}(function ($) {
- $.timeago = function(timestamp) {
- if (timestamp instanceof Date) {
- return inWords(timestamp);
- } else if (typeof timestamp === "string") {
- return inWords($.timeago.parse(timestamp));
- } else if (typeof timestamp === "number") {
- return inWords(new Date(timestamp));
- } else {
- return inWords($.timeago.datetime(timestamp));
- }
- };
- var $t = $.timeago;
-
- $.extend($.timeago, {
- settings: {
- refreshMillis: 60000,
- allowFuture: false,
- strings: {
- prefixAgo: null,
- prefixFromNow: null,
- suffixAgo: "ago",
- suffixFromNow: "from now",
- seconds: "less than a minute",
- minute: "about a minute",
- minutes: "%d minutes",
- hour: "about an hour",
- hours: "about %d hours",
- day: "a day",
- days: "%d days",
- month: "about a month",
- months: "%d months",
- year: "about a year",
- years: "%d years",
- wordSeparator: " ",
- numbers: []
- }
- },
- inWords: function(distanceMillis) {
- var $l = this.settings.strings;
- var prefix = $l.prefixAgo;
- var suffix = $l.suffixAgo;
- if (this.settings.allowFuture) {
- if (distanceMillis < 0) {
- prefix = $l.prefixFromNow;
- suffix = $l.suffixFromNow;
- }
- }
-
- var seconds = Math.abs(distanceMillis) / 1000;
- var minutes = seconds / 60;
- var hours = minutes / 60;
- var days = hours / 24;
- var years = days / 365;
-
- function substitute(stringOrFunction, number) {
- var string = $.isFunction(stringOrFunction) ? stringOrFunction(number, distanceMillis) : stringOrFunction;
- var value = ($l.numbers && $l.numbers[number]) || number;
- return string.replace(/%d/i, value);
- }
-
- var words = seconds < 45 && substitute($l.seconds, Math.round(seconds)) ||
- seconds < 90 && substitute($l.minute, 1) ||
- minutes < 45 && substitute($l.minutes, Math.round(minutes)) ||
- minutes < 90 && substitute($l.hour, 1) ||
- hours < 24 && substitute($l.hours, Math.round(hours)) ||
- hours < 42 && substitute($l.day, 1) ||
- days < 30 && substitute($l.days, Math.round(days)) ||
- days < 45 && substitute($l.month, 1) ||
- days < 365 && substitute($l.months, Math.round(days / 30)) ||
- years < 1.5 && substitute($l.year, 1) ||
- substitute($l.years, Math.round(years));
-
- var separator = $l.wordSeparator || "";
- if ($l.wordSeparator === undefined) { separator = " "; }
- return $.trim([prefix, words, suffix].join(separator));
- },
- parse: function(iso8601) {
- var s = $.trim(iso8601);
- s = s.replace(/\.\d+/,""); // remove milliseconds
- s = s.replace(/-/,"/").replace(/-/,"/");
- s = s.replace(/T/," ").replace(/Z/," UTC");
- s = s.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2"); // -04:00 -> -0400
- return new Date(s);
- },
- datetime: function(elem) {
- var iso8601 = $t.isTime(elem) ? $(elem).attr("datetime") : $(elem).attr("title");
- return $t.parse(iso8601);
- },
- isTime: function(elem) {
- // jQuery's `is()` doesn't play well with HTML5 in IE
- return $(elem).get(0).tagName.toLowerCase() === "time"; // $(elem).is("time");
- }
- });
-
- // functions that can be called via $(el).timeago('action')
- // init is default when no action is given
- // functions are called with context of a single element
- var functions = {
- init: function(){
- var refresh_el = $.proxy(refresh, this);
- refresh_el();
- var $s = $t.settings;
- if ($s.refreshMillis > 0) {
- setInterval(refresh_el, $s.refreshMillis);
- }
- },
- update: function(time){
- $(this).data('timeago', { datetime: $t.parse(time) });
- refresh.apply(this);
- }
- };
-
- $.fn.timeago = function(action, options) {
- var fn = action ? functions[action] : functions.init;
- if(!fn){
- throw new Error("Unknown function name '"+ action +"' for timeago");
- }
- // each over objects here and call the requested function
- this.each(function(){
- fn.call(this, options);
- });
- return this;
- };
-
- function refresh() {
- var data = prepareData(this);
- if (!isNaN(data.datetime)) {
- $(this).text(inWords(data.datetime));
- }
- return this;
- }
-
- function prepareData(element) {
- element = $(element);
- if (!element.data("timeago")) {
- element.data("timeago", { datetime: $t.datetime(element) });
- var text = $.trim(element.text());
- if (text.length > 0 && !($t.isTime(element) && element.attr("title"))) {
- element.attr("title", text);
- }
- }
- return element.data("timeago");
- }
-
- function inWords(date) {
- return $t.inWords(distance(date));
- }
-
- function distance(date) {
- return (new Date().getTime() - date.getTime());
- }
-
- // fix for IE6 suckage
- document.createElement("abbr");
- document.createElement("time");
-}));
diff --git a/app/assets/javascripts/lib/utils/notify.js b/app/assets/javascripts/lib/utils/notify.js
index dafc006d2e5..6d5979603b9 100644
--- a/app/assets/javascripts/lib/utils/notify.js
+++ b/app/assets/javascripts/lib/utils/notify.js
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, one-var, one-var-declaration-per-line, consistent-return, prefer-arrow-callback, no-return-assign, object-shorthand, comma-dangle, no-param-reassign, max-len */
+
(function() {
(function(w) {
var notificationGranted, notifyMe, notifyPermissions;
@@ -43,5 +44,4 @@
w.notify = notifyMe;
return w.notifyPermissions = notifyPermissions;
})(window);
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/pretty_time.js.es6 b/app/assets/javascripts/lib/utils/pretty_time.js.es6
new file mode 100644
index 00000000000..ae397212e55
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/pretty_time.js.es6
@@ -0,0 +1,65 @@
+(() => {
+ /*
+ * TODO: Make these methods more configurable (e.g. parseSeconds timePeriodContstraints,
+ * stringifyTime condensed or non-condensed, abbreviateTimelengths)
+ * */
+
+ const utils = window.gl.utils = gl.utils || {};
+ const prettyTime = utils.prettyTime = {
+ /*
+ * Accepts seconds and returns a timeObject { weeks: #, days: #, hours: #, minutes: # }
+ * Seconds can be negative or positive, zero or non-zero.
+ */
+ parseSeconds(seconds) {
+ const DAYS_PER_WEEK = 5;
+ const HOURS_PER_DAY = 8;
+ const MINUTES_PER_HOUR = 60;
+ const MINUTES_PER_WEEK = DAYS_PER_WEEK * HOURS_PER_DAY * MINUTES_PER_HOUR;
+ const MINUTES_PER_DAY = HOURS_PER_DAY * MINUTES_PER_HOUR;
+
+ const timePeriodConstraints = {
+ weeks: MINUTES_PER_WEEK,
+ days: MINUTES_PER_DAY,
+ hours: MINUTES_PER_HOUR,
+ minutes: 1,
+ };
+
+ let unorderedMinutes = prettyTime.secondsToMinutes(seconds);
+
+ return _.mapObject(timePeriodConstraints, (minutesPerPeriod) => {
+ const periodCount = Math.floor(unorderedMinutes / minutesPerPeriod);
+
+ unorderedMinutes -= (periodCount * minutesPerPeriod);
+
+ return periodCount;
+ });
+ },
+
+ /*
+ * Accepts a timeObject and returns a condensed string representation of it
+ * (e.g. '1w 2d 3h 1m' or '1h 30m'). Zero value units are not included.
+ */
+
+ stringifyTime(timeObject) {
+ const reducedTime = _.reduce(timeObject, (memo, unitValue, unitName) => {
+ const isNonZero = !!unitValue;
+ return isNonZero ? `${memo} ${unitValue}${unitName.charAt(0)}` : memo;
+ }, '').trim();
+ return reducedTime.length ? reducedTime : '0m';
+ },
+
+ /*
+ * Accepts a time string of any size (e.g. '1w 2d 3h 5m' or '1w 2d') and returns
+ * the first non-zero unit/value pair.
+ */
+
+ abbreviateTime(timeStr) {
+ return timeStr.split(' ')
+ .filter(unitStr => unitStr.charAt(0) !== '0')[0];
+ },
+
+ secondsToMinutes(seconds) {
+ return Math.abs(seconds / 60);
+ },
+ };
+})(window.gl || (window.gl = {}));
diff --git a/app/assets/javascripts/lib/utils/text_utility.js b/app/assets/javascripts/lib/utils/text_utility.js
index 98f9815ff05..6bb575059b7 100644
--- a/app/assets/javascripts/lib/utils/text_utility.js
+++ b/app/assets/javascripts/lib/utils/text_utility.js
@@ -1,4 +1,5 @@
-/* eslint-disable */
+/* 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 */
+
(function() {
(function(w) {
var base;
@@ -10,13 +11,28 @@
}
gl.text.addDelimiter = function(text) {
return text ? text.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : text;
- }
+ };
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);
};
@@ -44,9 +60,25 @@
}
};
gl.text.insertText = function(textArea, text, tag, blockTag, selected, wrap) {
- var insertText, inserted, selectedSplit, startChar;
+ var insertText, inserted, selectedSplit, startChar, removedLastNewLine, removedFirstNewLine;
+ removedLastNewLine = false;
+ removedFirstNewLine = 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');
startChar = !wrap && textArea.selectionStart > 0 ? '\n' : '';
+
if (selectedSplit.length > 1 && (!wrap || (blockTag != null))) {
if (blockTag != null) {
insertText = this.blockTagText(text, textArea, blockTag, selected);
@@ -62,6 +94,15 @@
} else {
insertText = "" + startChar + tag + selected + (wrap ? tag : ' ');
}
+
+ if (removedFirstNewLine) {
+ insertText = '\n' + insertText;
+ }
+
+ if (removedLastNewLine) {
+ insertText += '\n';
+ }
+
if (document.queryCommandSupported('insertText')) {
inserted = document.execCommand('insertText', false, insertText);
}
@@ -74,9 +115,9 @@
document.execCommand("ms-endUndoUnit");
} catch (error) {}
}
- return this.moveCursor(textArea, tag, wrap);
+ return this.moveCursor(textArea, tag, wrap, removedLastNewLine);
};
- gl.text.moveCursor = function(textArea, tag, wrapped) {
+ gl.text.moveCursor = function(textArea, tag, wrapped, removedLastNewLine) {
var pos;
if (!textArea.setSelectionRange) {
return;
@@ -87,6 +128,11 @@
} else {
pos = textArea.selectionStart;
}
+
+ if (removedLastNewLine) {
+ pos -= 1;
+ }
+
return textArea.setSelectionRange(pos, pos);
}
};
@@ -112,9 +158,11 @@
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);
+ };
return gl.text.truncate = function(string, maxLength) {
return string.substr(0, (maxLength - 3)) + '...';
};
})(window);
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/type_utility.js b/app/assets/javascripts/lib/utils/type_utility.js
index 4fd1e3fc1d3..6d813d61601 100644
--- a/app/assets/javascripts/lib/utils/type_utility.js
+++ b/app/assets/javascripts/lib/utils/type_utility.js
@@ -1,4 +1,4 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, no-return-assign, max-len */
(function() {
(function(w) {
var base;
@@ -12,5 +12,4 @@
return (obj != null) && (obj.constructor === Object);
};
})(window);
-
}).call(this);
diff --git a/app/assets/javascripts/lib/utils/url_utility.js b/app/assets/javascripts/lib/utils/url_utility.js
index 44a66a915e3..8e15bf0735c 100644
--- a/app/assets/javascripts/lib/utils/url_utility.js
+++ b/app/assets/javascripts/lib/utils/url_utility.js
@@ -1,4 +1,4 @@
-/* eslint-disable */
+/* eslint-disable func-names, space-before-function-paren, wrap-iife, no-var, no-param-reassign, no-cond-assign, one-var, one-var-declaration-per-line, no-void, guard-for-in, no-restricted-syntax, prefer-template, quotes, max-len */
(function() {
(function(w) {
var base;
@@ -22,7 +22,7 @@
if (sParameterName[0] === sParam) {
values.push(sParameterName[1].replace(/\+/g, ' '));
}
- i++;
+ i += 1;
}
return values;
};
@@ -57,7 +57,7 @@
return ((function() {
var j, len, results;
results = [];
- for (j = 0, len = urlVariables.length; j < len; j++) {
+ for (j = 0, len = urlVariables.length; j < len; j += 1) {
variables = urlVariables[j];
if (variables.indexOf(param) === -1) {
results.push(variables);
@@ -77,5 +77,4 @@
return hashIndex === -1 ? null : url.substring(hashIndex + 1);
};
})(window);
-
}).call(this);