remove jquery.timeago.js in favor of a native js implementation

This commit is contained in:
Mario
2024-12-12 14:18:55 +00:00
parent e9222d0d9a
commit 2da55d3bd8
15 changed files with 76 additions and 323 deletions

View File

@@ -1271,7 +1271,8 @@ class App {
'$zid' => get_my_address(),
'$channel_id' => self::$profile['uid'] ?? 0,
'$auto_save_draft' => ((isset(self::$profile['uid']) && feature_enabled(self::$profile['uid'], 'auto_save_draft')) ? "true" : "false"),
'$module' => App::$module
'$module' => App::$module,
'$lang' => App::$language
]
) . ((isset(self::$page['htmlhead'])) ? self::$page['htmlhead'] : '');

View File

@@ -49,29 +49,6 @@ function js_strings() {
'months' => tt('%d months', '%d months', '%d'),
'years' => tt('%d years', '%d years', '%d'),
// get plural function code
'plural_func' => tf(),
'$t01' => ((t('timeago.prefixAgo') == 'timeago.prefixAgo') ? '' : ((t('timeago.prefixAgo') == 'NONE') ? '' : t('timeago.prefixAgo'))),
'$t02' => ((t('timeago.prefixFromNow') == 'timeago.prefixFromNow') ? '' : ((t('timeago.prefixFromNow') == 'NONE') ? '' : t('timeago.prefixFromNow'))),
'$t03' => ((t('timeago.suffixAgo') == 'timeago.suffixAgo') ? 'ago' : ((t('timeago.suffixAgo') == 'NONE') ? '' : t('timeago.suffixAgo'))),
'$t04' => ((t('timeago.suffixFromNow') == 'timeago.suffixFromNow') ? 'from now' : ((t('timeago.suffixFromNow') == 'NONE') ? '' : t('timeago.suffixFromNow'))),
// translatable main strings for jquery.timeago
'$t05' => t('less than a minute'),
'$t06' => t('about a minute'),
'$t07' => ta('%d minutes'),
'$t08' => t('about an hour'),
'$t09' => ta('about %d hours'),
'$t10' => t('a day'),
'$t11' => ta('%d days'),
'$t12' => t('about a month'),
'$t13' => ta('%d months'),
'$t14' => t('about a year'),
'$t15' => ta('%d years'),
'$t16' => t(' '), // wordSeparator
'$t17' => ((t('timeago.numbers') != 'timeago.numbers') ? t('timeago.numbers') : '[]'),
'$January' => t('January'),
'$February' => t('February'),
'$March' => t('March'),

View File

@@ -1,232 +0,0 @@
/**
* 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.6.3
* @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-2017, 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 if (typeof module === 'object' && typeof module.exports === 'object') {
factory(require('jquery'));
} 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,
allowPast: true,
allowFuture: false,
localeTitle: false,
cutoff: 0,
autoDispose: true,
strings: {
prefixAgo: null,
prefixFromNow: null,
suffixAgo: "ago",
suffixFromNow: "from now",
inPast: 'any moment 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) {
if (!this.settings.allowPast && ! this.settings.allowFuture) {
throw 'timeago allowPast and allowFuture settings can not both be set to false.';
}
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;
}
}
if (!this.settings.allowPast && distanceMillis >= 0) {
return this.settings.strings.inPast;
}
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
s = s.replace(/([\+\-]\d\d)$/," $100"); // +09 -> +0900
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() {
functions.dispose.call(this);
var refresh_el = $.proxy(refresh, this);
refresh_el();
var $s = $t.settings;
if ($s.refreshMillis > 0) {
this._timeagoInterval = setInterval(refresh_el, $s.refreshMillis);
}
},
update: function(timestamp) {
var date = (timestamp instanceof Date) ? timestamp : $t.parse(timestamp);
$(this).data('timeago', { datetime: date });
if ($t.settings.localeTitle) {
$(this).attr("title", date.toLocaleString());
}
refresh.apply(this);
},
updateFromDOM: function() {
$(this).data('timeago', { datetime: $t.parse( $t.isTime(this) ? $(this).attr("datetime") : $(this).attr("title") ) });
refresh.apply(this);
},
dispose: function () {
if (this._timeagoInterval) {
window.clearInterval(this._timeagoInterval);
this._timeagoInterval = null;
}
}
};
$.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 $s = $t.settings;
//check if it's still visible
if ($s.autoDispose && !$.contains(document.documentElement,this)) {
//stop if it has been removed
$(this).timeago("dispose");
return this;
}
var data = prepareData(this);
if (!isNaN(data.datetime)) {
if ( $s.cutoff === 0 || Math.abs(distance(data.datetime)) < $s.cutoff) {
$(this).text(inWords(data.datetime));
} else {
if ($(this).attr('title').length > 0) {
$(this).text($(this).attr('title'));
}
}
}
return this;
}
function prepareData(element) {
element = $(element);
if (!element.data("timeago")) {
element.data("timeago", { datetime: $t.datetime(element) });
var text = $.trim(element.text());
if ($t.settings.localeTitle) {
element.attr("title", element.data('timeago').datetime.toLocaleString());
} else 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");
}));

View File

@@ -25,6 +25,7 @@ var savedTitle = '';
var followUpPageLoad = false;
var window_needs_alert = true;
var expanded_items = [];
var updateTimeout = [];
var page_cache = {};
@@ -58,28 +59,6 @@ $.ajaxSetup({cache: false});
var tf = new Function('n', 's', 'var k = s.split("/")['+aStr['plural_func']+']; return (k ? k : s);');
jQuery.timeago.settings.strings = {
prefixAgo : aStr['t01'],
prefixFromNow : aStr['t02'],
suffixAgo : aStr['t03'],
suffixFromNow : aStr['t04'],
seconds : aStr['t05'],
minute : aStr['t06'],
minutes : function(value){return tf(value, aStr['t07']);},
hour : aStr['t08'],
hours : function(value){return tf(value, aStr['t09']);},
day : aStr['t10'],
days : function(value){return tf(value, aStr['t11']);},
month : aStr['t12'],
months : function(value){return tf(value, aStr['t13']);},
year : aStr['t14'],
years : function(value){return tf(value, aStr['t15']);},
wordSeparator : aStr['t16'],
numbers : aStr['t17'],
};
jQuery.timeago.settings.allowFuture = true;
$(document).ready(function() {
$(document).on('click focus', '.comment-edit-form', handle_comment_form);
@@ -514,14 +493,12 @@ function viewsrc(id) {
function showHideComments(id) {
if($('#collapsed-comments-' + id).is(':visible')) {
$('#collapsed-comments-' + id + ' .autotime').timeago('dispose');
$('#collapsed-comments-' + id).hide();
$('#hide-comments-label-' + id).html(aStr.showmore);
$('#hide-comments-total-' + id).show();
$('#hide-comments-icon-' + id).toggleClass('bi-chevron-down bi-chevron-up');
} else {
$('#collapsed-comments-' + id + ' .autotime').timeago();
$('#collapsed-comments-' + id).show();
$('#hide-comments-label-' + id).html(aStr.showfewer);
$('#hide-comments-total-' + id).hide();
@@ -718,8 +695,6 @@ function updateConvItems(mode,data) {
}
// trigger the autotime function on all newly created content
$("> .wall-item-outside-wrapper .autotime, > .thread-wrapper .autotime",this).timeago();
$("> .shared_header .autotime",this).timeago();
if((mode === 'append' || mode === 'replace') && (loadingPage)) {
loadingPage = false;
@@ -818,6 +793,59 @@ function updateConvItems(mode,data) {
followUpPageLoad = true;
updateRelativeTime('.autotime');
}
function updateRelativeTime(selector) {
// Get all elements with the given selector
const timeElements = document.querySelectorAll(selector);
if (timeElements.length === 0) return;
// Default time style and map for supported options
const styleMap = ['narrow', 'short', 'long'];
const style = styleMap.find(s => selector.includes(s)) || 'long';
// Create an instance of RelativeTimeFormat
const rtf = new Intl.RelativeTimeFormat(lang, {
localeMatcher: 'best fit', // 'best fit' or 'lookup'
numeric: 'always', // 'always' or 'auto'
style: style // 'long', 'short', or 'narrow'
});
const now = Date.now(); // Get the current time only once
// Helper function to calculate the time difference in appropriate units
function getRelativeTime(diffInSeconds) {
const isFuture = diffInSeconds > 0;
const absDiffInSeconds = Math.abs(diffInSeconds);
if (absDiffInSeconds < 60) return { value: absDiffInSeconds, unit: 'second' };
if (absDiffInSeconds < 3600) return { value: Math.floor(absDiffInSeconds / 60), unit: 'minute' };
if (absDiffInSeconds < 86400) return { value: Math.floor(absDiffInSeconds / 3600), unit: 'hour' };
if (absDiffInSeconds < 2592000) return { value: Math.floor(absDiffInSeconds / 86400), unit: 'day' };
if (absDiffInSeconds < 31536000) return { value: Math.floor(absDiffInSeconds / 2592000), unit: 'month' };
return { value: Math.floor(absDiffInSeconds / 31536000), unit: 'year' };
}
// Process each element
timeElements.forEach(element => {
const timestamp = new Date(element.title).getTime();
if (isNaN(timestamp)) return; // Skip invalid timestamps
const diffInSeconds = Math.floor((timestamp - now) / 1000); // Time difference in seconds
const { value, unit } = getRelativeTime(diffInSeconds);
// Format the relative time and set it as the element's text
const formattedTime = rtf.format(diffInSeconds > 0 ? value : -value, unit);
element.textContent = formattedTime;
});
// Avoid duplicate timeout registrations for the same selector
if (!updateTimeout.includes(selector)) {
updateTimeout.push(selector);
setTimeout(() => updateRelativeTime(selector), 60000); // Re-run the update every 60 seconds
}
}
function scrollToItem() {
@@ -834,7 +862,6 @@ function scrollToItem() {
if($(this).data('b64mids').indexOf(submid) > -1 && !$(this).hasClass('toplevel_item')) {
if($('.collapsed-comments').length) {
var scrolltoid = $('.collapsed-comments').attr('id').substring(19);
$('#collapsed-comments-' + scrolltoid + ' .autotime').timeago();
$('#collapsed-comments-' + scrolltoid).show();
$('#hide-comments-label-' + scrolltoid).html(aStr.showfewer);
$('#hide-comments-total-' + scrolltoid).hide();
@@ -1155,7 +1182,7 @@ function pageUpdate() {
scroll_next = false;
updatePageItems(update_mode,data);
$("#page-spinner").hide();
$(".autotime").timeago();
updateRelativeTime('.autotime');
in_progress = false;
});
}
@@ -1216,7 +1243,7 @@ function dolike(ident, verb) {
$('#thread-wrapper-' + data.orig_id).replaceWith(data.html);
}
$('#wall-item-ago-' + data.id + ' .autotime').timeago();
updateRelativeTime('.autotime');
collapseHeight();
liking = 0;
}
@@ -1457,7 +1484,7 @@ function post_comment(id) {
$("#comment-edit-preview-" + id).hide();
$("#comment-edit-text-" + id).val('').blur().attr('placeholder', aStr.comment);
$('#wall-item-comment-wrapper-' + id).before(data.html);
$('#wall-item-ago-' + data.id + ' .autotime').timeago();
updateRelativeTime('.autotime');
$('body').css('cursor', 'unset');
collapseHeight();
commentBusy = false;
@@ -1487,7 +1514,7 @@ function preview_comment(id) {
function(data) {
if(data.preview) {
$("#comment-edit-preview-" + id).html(data.preview);
$("#comment-edit-preview-" + id + " .autotime").timeago();
updateRelativeTime('.autotime');
$("#comment-edit-preview-" + id + " a").click(function() { return false; });
}
},
@@ -1517,7 +1544,7 @@ function preview_post() {
function(data) {
if(data.preview) {
$("#jot-preview-content").html(data.preview);
$("#jot-preview-content .autotime").timeago();
updateRelativeTime('.autotime');
$("#jot-preview-content" + " a").click(function() { return false; });
}
},

View File

@@ -1,5 +1,5 @@
$(document).ready( function() {
$(".autotime").timeago();
updateRelativeTime('.autotime');
/* autocomplete @nicknames */
$(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl?f=&n=1");

View File

@@ -1,5 +1,5 @@
$(document).ready( function() {
$(".autotime").timeago();
updateRelativeTime('.autotime');
/* autocomplete @nicknames */
$(".comment-edit-form textarea").editor_autocomplete(baseurl+"/acl?f=&n=1");

View File

@@ -2,6 +2,6 @@ $(document).ready(function() {
$("#contacts-search").name_autocomplete(baseurl + '/acl', 'a', true, function(data) {
$("#contacts-search-xchan").val(data.xid);
});
$(".autotime").timeago();
updateRelativeTime('.autotime');
});

View File

@@ -1,6 +1,5 @@
$(document).ready(function() {
$('.autotime').timeago();
updateRelativeTime('.autotime');
if (bParam_mid) {
src = 'hq';

View File

@@ -19,7 +19,6 @@ head_add_js('/library/sprintf.js/dist/sprintf.min.js');
head_add_js('/library/textcomplete/textcomplete.min.js');
head_add_js('autocomplete.js');
head_add_js('/library/jquery.timeago.js');
head_add_js('/library/readmore.js/readmore.js');
head_add_js('/library/sjcl/sjcl.js');

View File

@@ -206,7 +206,7 @@ function update_chats(chats) {
chat_issue_notification(item.name + ':\n' + item.text, 'Hubzilla Chat');
}
$('#chatLineHolder').append(newNode);
$(".autotime").timeago();
updateRelativeTime('.autotime');
var elem = document.getElementById('chatTopBar');
elem.scrollTop = elem.scrollHeight;

View File

@@ -8,6 +8,7 @@
{{$linkrel}}
{{$plugins}}
<script>
var lang = '{{$lang}}';
var updateInterval = {{$update_interval}};
var sse_enabled = {{$sse_enabled}};
var localUser = {{if $local_channel}}{{$local_channel}}{{else}}false{{/if}};

View File

@@ -39,26 +39,6 @@
'pin_item' : "{{$pin_item}}",
'unpin_item' : "{{$unpin_item}}",
'plural_func' : "{{$plural_func}}",
't01' : "{{$t01}}",
't02' : "{{$t02}}",
't03' : "{{$t03}}",
't04' : "{{$t04}}",
't05' : "{{$t05}}",
't06' : "{{$t06}}",
't07' : "{{$t07}}",
't08' : "{{$t08}}",
't09' : "{{$t09}}",
't10' : "{{$t10}}",
't11' : "{{$t11}}",
't12' : "{{$t12}}",
't13' : "{{$t13}}",
't14' : "{{$t14}}",
't15' : "{{$t15}}",
't16' : "{{$t16}}",
't17' : "{{$t17}}",
'monthNames' : [ "{{$January}}","{{$February}}","{{$March}}","{{$April}}","{{$May}}","{{$June}}","{{$July}}","{{$August}}","{{$September}}","{{$October}}","{{$November}}","{{$December}}" ],
'monthNamesShort' : [ "{{$Jan}}","{{$Feb}}","{{$Mar}}","{{$Apr}}","{{$MayShort}}","{{$Jun}}","{{$Jul}}","{{$Aug}}","{{$Sep}}","{{$Oct}}","{{$Nov}}","{{$Dec}}" ],
'dayNames' : ["{{$Sunday}}","{{$Monday}}","{{$Tuesday}}","{{$Wednesday}}","{{$Thursday}}","{{$Friday}}","{{$Saturday}}"],

View File

@@ -40,7 +40,7 @@
{7}
<strong title="{4}">{4}</strong>
</div>
<small class="messages-timeago opacity-75" title="{1}"></small>
<small class="autotime-narrow opacity-75" title="{1}"></small>
</div>
<div class="text-truncate">
<small class="opacity-75" title="{5}">{5}</small>
@@ -82,7 +82,7 @@
{{$e.icon}}
<strong title="{{$e.author_name}}">{{$e.author_name}}</strong>
</div>
<small class="messages-timeago opacity-75" title="{{$e.created}}"></small>
<small class="autotime-narrow opacity-75" title="{{$e.created}}"></small>
</div>
<div class="text-truncate">
<small class="opacity-75" title="{{$e.author_addr}}">{{$e.author_addr}}</small>
@@ -116,7 +116,8 @@
let file;
$(document).ready(function () {
$('.messages-timeago').timeago();
updateRelativeTime('.autotime-narrow');
if (bParam_mid) {
$('.message[data-b64mid=\'' + bParam_mid + '\']').addClass('active');
}
@@ -251,7 +252,7 @@
$('.message[data-b64mid=\'' + bParam_mid + '\']').addClass('active');
}
$('#messages-loading').hide();
$('.messages-timeago').timeago();
updateRelativeTime('.autotime-narrow');
});

View File

@@ -401,8 +401,6 @@
}).appendTo('#nav-' + notifyType + '-menu');
}
$("#nav-" + notifyType + "-menu .notifications-autotime").timeago();
if($('#tt-' + notifyType + '-only').hasClass('active'))
$('#nav-' + notifyType + '-menu [data-thread_top=false]').addClass('tt-filter-active');
@@ -421,6 +419,8 @@
});
}
}
updateRelativeTime('.autotime-narrow');
}
function sse_updateNotifications(type, mid) {
@@ -552,7 +552,7 @@
<div class="text-truncate pe-1">
<strong title="{2} - {3}">{2}</strong>
</div>
<small class="notifications-autotime opacity-75" title="{5}"></small>
<small class="autotime-narrow opacity-75" title="{5}"></small>
</div>
<div class="text-truncate">{4}</div>
</div>

View File

@@ -214,5 +214,5 @@
</script>
{{/if}}
<script>
$(".pinned-item .autotime").timeago();
updateRelativeTime('.autotime');
</script>