diff options
author | Fatih Acet <acetfatih@gmail.com> | 2016-11-08 20:06:54 +0000 |
---|---|---|
committer | Fatih Acet <acetfatih@gmail.com> | 2016-11-08 20:06:54 +0000 |
commit | 9eb9d05b454a59fbe09e122d64935d1842206bc7 (patch) | |
tree | 58c9ba2aa04b4d373ac889cd0ed9223ccdf45540 /app/assets | |
parent | c6d010986724faf10b46453d66eaca38a948fabf (diff) | |
parent | fa1ac47ef167e55d79a69d69596012fe5ac99d20 (diff) | |
download | gitlab-ce-9eb9d05b454a59fbe09e122d64935d1842206bc7.tar.gz |
Merge branch 'upgrade-timeago' into 'master'
Replace jQuery.timeago with timeago.js
## What does this MR do?
Replaces jQuery.timeago with [timeago.js](https://github.com/hustcc/timeago.js)
## Are there points in the code the reviewer needs to double check?
* Check to make sure its working everywhere :smile:
* Check to make sure the timeago wording matches what we have now (I think I've got this down but an extra pair of :eyes: would help too)
## Why was this MR needed?
* The jQuery.timeago version we have is outdated
* timeago.js is smaller (7.19 KB => 4.52 KB)
* timeago.js has no jQuery dependency
* removes all inline javascript :crossed_swords: for timeago
## Screenshots (if relevant)
None
## Does this MR meet the acceptance criteria?
- [x] [CHANGELOG](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CHANGELOG) entry added
- Tests
- [x] All builds are passing
- [x] Conform by the [merge request performance guides](http://docs.gitlab.com/ce/development/merge_request_performance_guidelines.html)
- [x] Conform by the [style guides](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/CONTRIBUTING.md#style-guides)
- [x] Branch has no merge conflicts with `master` (if you do - rebase it please)
- [x] [Squashed related commits together](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
## What are the relevant issue numbers?
Closes #21793
See merge request !6274
Diffstat (limited to 'app/assets')
-rw-r--r-- | app/assets/javascripts/activities.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/application.js | 4 | ||||
-rw-r--r-- | app/assets/javascripts/build.js | 2 | ||||
-rw-r--r-- | app/assets/javascripts/compare.js | 3 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/common_utils.js | 19 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/datetime_utility.js | 95 | ||||
-rw-r--r-- | app/assets/javascripts/lib/utils/timeago.js | 237 | ||||
-rw-r--r-- | app/assets/javascripts/merge_request_widget.js.es6 | 2 | ||||
-rw-r--r-- | app/assets/javascripts/milestone_select.js | 2 |
9 files changed, 298 insertions, 70 deletions
diff --git a/app/assets/javascripts/activities.js b/app/assets/javascripts/activities.js index 59ac9b9cef5..919107b8cb9 100644 --- a/app/assets/javascripts/activities.js +++ b/app/assets/javascripts/activities.js @@ -13,12 +13,12 @@ } Activities.prototype.updateTooltips = function() { - return gl.utils.localTimeAgo($('.js-timeago', '.content_list')); + gl.utils.localTimeAgo($('.js-timeago', '.content_list')); }; Activities.prototype.reloadActivities = function() { $(".content_list").html(''); - return Pager.init(20, true); + Pager.init(20, true, false, this.updateTooltips); }; Activities.prototype.toggleFilter = function(sender) { diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 7d942de0184..e6b55c9b6ae 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -13,7 +13,6 @@ /*= require jquery-ui/sortable */ /*= require jquery_ujs */ /*= require jquery.endless-scroll */ -/*= require jquery.timeago */ /*= require jquery.highlight */ /*= require jquery.waitforimages */ /*= require jquery.atwho */ @@ -238,8 +237,5 @@ // bind sidebar events new gl.Sidebar(); - - // Custom time ago - gl.utils.shortTimeAgo($('.js-short-timeago')); }); }).call(this); diff --git a/app/assets/javascripts/build.js b/app/assets/javascripts/build.js index 12e653f4122..d63125ce44b 100644 --- a/app/assets/javascripts/build.js +++ b/app/assets/javascripts/build.js @@ -169,7 +169,7 @@ $date = $('.js-artifacts-remove'); if ($date.length) { date = $date.text(); - return $date.text($.timefor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' ')); + return $date.text(gl.utils.timefor(new Date(date.replace(/([0-9]+)-([0-9]+)-([0-9]+)/g, '$1/$2/$3')), ' ')); } }; diff --git a/app/assets/javascripts/compare.js b/app/assets/javascripts/compare.js index b3f769d4129..61cc91c524b 100644 --- a/app/assets/javascripts/compare.js +++ b/app/assets/javascripts/compare.js @@ -80,7 +80,8 @@ success: function(html) { loading.hide(); $target.html(html); - return $('.js-timeago', $target).timeago(); + var className = '.' + $target[0].className.replace(' ', '.'); + gl.utils.localTimeAgo($('.js-timeago', className)); } }); }; diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 8447421195d..6cb3d95f984 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -119,31 +119,12 @@ 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/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js index 59e526ed623..3965109dd65 100644 --- a/app/assets/javascripts/lib/utils/datetime_utility.js +++ b/app/assets/javascripts/lib/utils/datetime_utility.js @@ -22,51 +22,64 @@ if (setTimeago == null) { setTimeago = true; } + $timeagoEls.each(function() { - var $el; - $el = $(this); - return $el.attr('title', gl.utils.formatDate($el.attr('datetime'))); + 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>' + }); + } + 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,7 +88,7 @@ var date2 = Date.UTC(b.getFullYear(), b.getMonth(), b.getDate()); return Math.floor((date2 - date1) / millisecondsPerDay); - } + }; })(window); diff --git a/app/assets/javascripts/lib/utils/timeago.js b/app/assets/javascripts/lib/utils/timeago.js new file mode 100644 index 00000000000..42606dd2d46 --- /dev/null +++ b/app/assets/javascripts/lib/utils/timeago.js @@ -0,0 +1,237 @@ +/** + * Copyright (c) 2016 hustcc + * License: MIT + * Version: v2.0.2 + * https://github.com/hustcc/timeago.js + * This is a forked from (https://gitlab.com/ClemMakesApps/timeago.js) +**/ +/* eslint-disable */ +/* jshint expr: true */ +!function (root, factory) { + if (typeof module === 'object' && module.exports) + module.exports = factory(root); + else + root.timeago = factory(root); +}(typeof window !== 'undefined' ? window : this, +function () { + var cnt = 0, // the timer counter, for timer key + indexMapEn = 'second_minute_hour_day_week_month_year'.split('_'), + + // build-in locales: en & zh_CN + locales = { + 'en': function(number, index) { + if (index === 0) return ['just now', 'right now']; + var unit = indexMapEn[parseInt(index / 2)]; + if (number > 1) unit += 's'; + return [number + ' ' + unit + ' ago', 'in ' + number + ' ' + unit]; + }, + }, + // second, minute, hour, day, week, month, year(365 days) + SEC_ARRAY = [60, 60, 24, 7, 365/7/12, 12], + SEC_ARRAY_LEN = 6, + ATTR_DATETIME = 'datetime'; + + // format Date / string / timestamp to Date instance. + function toDate(input) { + if (input instanceof Date) return input; + if (!isNaN(input)) return new Date(toInt(input)); + if (/^\d+$/.test(input)) return new Date(toInt(input, 10)); + input = (input || '').trim().replace(/\.\d+/, '') // remove milliseconds + .replace(/-/, '/').replace(/-/, '/') + .replace(/T/, ' ').replace(/Z/, ' UTC') + .replace(/([\+\-]\d\d)\:?(\d\d)/, ' $1$2'); // -04:00 -> -0400 + return new Date(input); + } + // change f into int, remove Decimal. just for code compression + function toInt(f) { + return parseInt(f); + } + // format the diff second to *** time ago, with setting locale + function formatDiff(diff, locale, defaultLocale) { + // if locale is not exist, use defaultLocale. + // if defaultLocale is not exist, use build-in `en`. + // be sure of no error when locale is not exist. + locale = locales[locale] ? locale : (locales[defaultLocale] ? defaultLocale : 'en'); + // if (! locales[locale]) locale = defaultLocale; + var i = 0; + agoin = diff < 0 ? 1 : 0; // timein or timeago + diff = Math.abs(diff); + + for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) { + diff /= SEC_ARRAY[i]; + } + diff = toInt(diff); + i *= 2; + + if (diff > (i === 0 ? 9 : 1)) i += 1; + return locales[locale](diff, i)[agoin].replace('%s', diff); + } + // calculate the diff second between date to be formated an now date. + function diffSec(date, nowDate) { + nowDate = nowDate ? toDate(nowDate) : new Date(); + return (nowDate - toDate(date)) / 1000; + } + /** + * nextInterval: calculate the next interval time. + * - diff: the diff sec between now and date to be formated. + * + * What's the meaning? + * diff = 61 then return 59 + * diff = 3601 (an hour + 1 second), then return 3599 + * make the interval with high performace. + **/ + function nextInterval(diff) { + var rst = 1, i = 0, d = Math.abs(diff); + for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) { + diff /= SEC_ARRAY[i]; + rst *= SEC_ARRAY[i]; + } + // return leftSec(d, rst); + d = d % rst; + d = d ? rst - d : rst; + return Math.ceil(d); + } + // get the datetime attribute, jQuery and DOM + function getDateAttr(node) { + if (node.getAttribute) return node.getAttribute(ATTR_DATETIME); + if(node.attr) return node.attr(ATTR_DATETIME); + } + /** + * timeago: the function to get `timeago` instance. + * - nowDate: the relative date, default is new Date(). + * - defaultLocale: the default locale, default is en. if your set it, then the `locale` parameter of format is not needed of you. + * + * How to use it? + * var timeagoLib = require('timeago.js'); + * var timeago = timeagoLib(); // all use default. + * var timeago = timeagoLib('2016-09-10'); // the relative date is 2016-09-10, so the 2016-09-11 will be 1 day ago. + * var timeago = timeagoLib(null, 'zh_CN'); // set default locale is `zh_CN`. + * var timeago = timeagoLib('2016-09-10', 'zh_CN'); // the relative date is 2016-09-10, and locale is zh_CN, so the 2016-09-11 will be 1天前. + **/ + function Timeago(nowDate, defaultLocale) { + var timers = {}; // real-time render timers + // if do not set the defaultLocale, set it with `en` + if (! defaultLocale) defaultLocale = 'en'; // use default build-in locale + // what the timer will do + function doRender(node, date, locale, cnt) { + var diff = diffSec(date, nowDate); + node.innerHTML = formatDiff(diff, locale, defaultLocale); + // waiting %s seconds, do the next render + timers['k' + cnt] = setTimeout(function() { + doRender(node, date, locale, cnt); + }, nextInterval(diff) * 1000); + } + /** + * nextInterval: calculate the next interval time. + * - diff: the diff sec between now and date to be formated. + * + * What's the meaning? + * diff = 61 then return 59 + * diff = 3601 (an hour + 1 second), then return 3599 + * make the interval with high performace. + **/ + // this.nextInterval = function(diff) { // for dev test + // var rst = 1, i = 0, d = Math.abs(diff); + // for (; diff >= SEC_ARRAY[i] && i < SEC_ARRAY_LEN; i++) { + // diff /= SEC_ARRAY[i]; + // rst *= SEC_ARRAY[i]; + // } + // // return leftSec(d, rst); + // d = d % rst; + // d = d ? rst - d : rst; + // return Math.ceil(d); + // }; // for dev test + /** + * format: format the date to *** time ago, with setting or default locale + * - date: the date / string / timestamp to be formated + * - locale: the formated string's locale name, e.g. en / zh_CN + * + * How to use it? + * var timeago = require('timeago.js')(); + * timeago.format(new Date(), 'pl'); // Date instance + * timeago.format('2016-09-10', 'fr'); // formated date string + * timeago.format(1473473400269); // timestamp with ms + **/ + this.format = function(date, locale) { + return formatDiff(diffSec(date, nowDate), locale, defaultLocale); + }; + /** + * render: render the DOM real-time. + * - nodes: which nodes will be rendered. + * - locale: the locale name used to format date. + * + * How to use it? + * var timeago = new require('timeago.js')(); + * // 1. javascript selector + * timeago.render(document.querySelectorAll('.need_to_be_rendered')); + * // 2. use jQuery selector + * timeago.render($('.need_to_be_rendered'), 'pl'); + * + * Notice: please be sure the dom has attribute `datetime`. + **/ + this.render = function(nodes, locale) { + if (nodes.length === undefined) nodes = [nodes]; + for (var i = 0; i < nodes.length; i++) { + doRender(nodes[i], getDateAttr(nodes[i]), locale, ++ cnt); // render item + } + }; + /** + * cancel: cancel all the timers which are doing real-time render. + * + * How to use it? + * var timeago = new require('timeago.js')(); + * timeago.render(document.querySelectorAll('.need_to_be_rendered')); + * timeago.cancel(); // will stop all the timer, stop render in real time. + **/ + this.cancel = function() { + for (var key in timers) { + clearTimeout(timers[key]); + } + timers = {}; + }; + /** + * setLocale: set the default locale name. + * + * How to use it? + * var timeago = require('timeago.js'); + * timeago = new timeago(); + * timeago.setLocale('fr'); + **/ + this.setLocale = function(locale) { + defaultLocale = locale; + }; + return this; + } + /** + * timeago: the function to get `timeago` instance. + * - nowDate: the relative date, default is new Date(). + * - defaultLocale: the default locale, default is en. if your set it, then the `locale` parameter of format is not needed of you. + * + * How to use it? + * var timeagoLib = require('timeago.js'); + * var timeago = timeagoLib(); // all use default. + * var timeago = timeagoLib('2016-09-10'); // the relative date is 2016-09-10, so the 2016-09-11 will be 1 day ago. + * var timeago = timeagoLib(null, 'zh_CN'); // set default locale is `zh_CN`. + * var timeago = timeagoLib('2016-09-10', 'zh_CN'); // the relative date is 2016-09-10, and locale is zh_CN, so the 2016-09-11 will be 1天前. + **/ + function timeagoFactory(nowDate, defaultLocale) { + return new Timeago(nowDate, defaultLocale); + } + /** + * register: register a new language locale + * - locale: locale name, e.g. en / zh_CN, notice the standard. + * - localeFunc: the locale process function + * + * How to use it? + * var timeagoLib = require('timeago.js'); + * + * timeagoLib.register('the locale name', the_locale_func); + * // or + * timeagoLib.register('pl', require('timeago.js/locales/pl')); + **/ + timeagoFactory.register = function(locale, localeFunc) { + locales[locale] = localeFunc; + }; + + return timeagoFactory; +});
\ No newline at end of file diff --git a/app/assets/javascripts/merge_request_widget.js.es6 b/app/assets/javascripts/merge_request_widget.js.es6 index 3a2fe454b68..56c87af3226 100644 --- a/app/assets/javascripts/merge_request_widget.js.es6 +++ b/app/assets/javascripts/merge_request_widget.js.es6 @@ -218,7 +218,7 @@ } if (environment.deployed_at && environment.deployed_at_formatted) { - environment.deployed_at = $.timeago(environment.deployed_at) + '.'; + environment.deployed_at = gl.utils.getTimeago(environment.deployed_at) + '.'; } else { $('.js-environment-timeago', $template).remove(); environment.name += '.'; diff --git a/app/assets/javascripts/milestone_select.js b/app/assets/javascripts/milestone_select.js index c909b53dc21..d1cd38ad110 100644 --- a/app/assets/javascripts/milestone_select.js +++ b/app/assets/javascripts/milestone_select.js @@ -162,7 +162,7 @@ if (data.milestone != null) { data.milestone.namespace = _this.currentProject.namespace; data.milestone.path = _this.currentProject.path; - data.milestone.remaining = $.timefor(data.milestone.due_date); + data.milestone.remaining = gl.utils.timeFor(data.milestone.due_date); $value.html(milestoneLinkTemplate(data.milestone)); return $sidebarCollapsedValue.find('span').html(collapsedSidebarLabelTemplate(data.milestone)); } else { |