pontoon/base/static/js/table.js (207 lines of code) (raw):

/* Must be available immediately */ // Add case insensitive :contains-like selector to jQuery (search & filter) $.expr[':'].containsi = function (a, i, m) { return ( (a.textContent || a.innerText || '') .toUpperCase() .indexOf(m[3].toUpperCase()) >= 0 ); }; /* Latest activity tooltip */ const delay = 500, date_formatter = new Intl.DateTimeFormat('en-US', { day: 'numeric', month: 'long', year: 'numeric', }), time_formatter = new Intl.DateTimeFormat('en-US', { hour: '2-digit', minute: '2-digit', second: '2-digit', }); let timer = null; $('body') .on('mouseenter', '.latest-activity .latest time', function () { const $element = $(this); timer = setTimeout(function () { const translation = Pontoon.doNotRender($element.data('translation')), avatar = $element.data('user-avatar'), action = $element.data('action'), name = $element.data('user-name'), link = $element.data('user-link'), date = date_formatter.format(new Date($element.attr('datetime'))), time = time_formatter.format(new Date($element.attr('datetime'))); $element.after( '<aside class="tooltip">' + '<span class="quote fas fa-quote-right fa-2x"></span>' + '<p class="translation">' + translation + '</p>' + '<footer class="clearfix">' + '<div class="wrapper">' + '<div class="translation-details">' + '<p class="translation-action">' + action + ' <a href="' + link + '">' + name + '</a></p>' + '<p class="translation-time">on ' + date + ' at ' + time + '</p>' + '</div>' + (avatar ? '<img class="rounded" height="44" width="44" src="' + avatar + '">' : '') + '</div>' + '</footer>' + '</aside>', ); }, delay); }) .on('mouseleave', 'td.latest-activity', function () { $('.latest-activity .latest .tooltip').remove(); clearTimeout(timer); }); /* Public functions used across different files */ // eslint-disable-next-line no-var var Pontoon = (function (my) { return $.extend(true, my, { table: { /* * Filter table * * TODO: remove old search code from main.js */ filter: (function () { $('body').on('input.filter', 'input.table-filter', function (e) { if (e.which === 9) { return; } // Filter input field const field = $(this), // Selector of the element containing a list of items to filter list = $(this).data('list') || '.table-sort tbody', // Selector of the list item element, relative to list item = $(this).data('item') || 'tr', // Selector of the list item element's child to match filter query against filter = $(this).data('filter') || 'td:first-child'; $(list) .find(item + '.limited') .hide() .end() .find( item + '.limited ' + filter + ':containsi("' + $(field).val() + '")', ) .parents(item) .show(); }); })(), /* * Sort table */ sort: (function () { $('body').on('click', 'table.table-sort th', function () { function getProgress(el) { const legend = $(el).find('.progress .legend'), all = legend.find('.all .value').data('value') || 0, translated = legend.find('.translated .value').data('value') / all || 0, pretranslated = legend.find('.pretranslated .value').data('value') / all || 0, warnings = legend.find('.warnings .value').data('value') / all || 0; if ($(el).find('.progress .not-ready').length) { return 'not-ready'; } return translated + pretranslated + warnings; } function getUnreviewed(el) { return parseInt( $(el) .find('.progress .legend .unreviewed .value') .data('value') || 0, ); } function getTime(el) { const date = $(el) .find('td:eq(' + index + ')') .find('time') .attr('datetime') || 0; return new Date(date).getTime(); } function getPriority(el) { return $(el).find('.priority .fa-star.active').length; } function getEnabled(el) { return $(el).find('.check.enabled').length; } function getNumber(el) { return parseInt($(el).find('span').text().replace(/,/g, '')); } function getSort(el) { return parseInt($(el).find('[data-sort]').data('sort'), 10) || 0; } function getString(el) { return $(el) .find('td:eq(' + index + ')') .text(); } const node = $(this), index = node.index(), table = node.parents('.table-sort'), list = table.find('tbody'), items = list.find('tr'), dir = node.hasClass('asc') ? -1 : 1, cls = node.hasClass('asc') ? 'desc' : 'asc'; // Default value for rows which don't have a timestamp let defaultTime; if (node.is('.deadline')) { defaultTime = new Date(0).getTime(); } $(table).find('th').removeClass('asc desc'); node.addClass(cls); items.sort(function (a, b) { // Sort by completion if (node.is('.progress')) { const chartA = getProgress(a), chartB = getProgress(b); if (chartA === 'not-ready') { if (chartB === 'not-ready') { return 0; } else { return -1 * dir; } } if (chartB === 'not-ready') { return 1 * dir; } return (chartA - chartB) * dir; // Sort by unreviewed state } else if (node.is('.unreviewed-status')) { return (getUnreviewed(b) - getUnreviewed(a)) * dir; // Sort by deadline } else if (node.is('.deadline')) { const timeA = getTime(a), timeB = getTime(b); if (timeA === defaultTime && timeB === defaultTime) { return getString(a).localeCompare(getString(b)) * dir; } else if (timeA === defaultTime) { return 1 * dir; } else if (timeB === defaultTime) { return -1 * dir; } return (timeA - timeB) * dir; // Sort by relative time } else if ( node.is('.latest-activity') || node.is('.relative-time') ) { return (getTime(b) - getTime(a)) * dir; // Sort by priority } else if (node.is('.priority')) { return (getPriority(b) - getPriority(a)) * dir; // Sort by enabled state } else if (node.is('.check')) { return (getEnabled(a) - getEnabled(b)) * dir; // Sort by number of speakers } else if (node.is('.population')) { return (getNumber(a) - getNumber(b)) * dir; // Sort by the data-sort attribute } else if (node.attr('data-sort') !== undefined) { return (getSort(a) - getSort(b)) * dir; // Sort by alphabetical order } else { return getString(a).localeCompare(getString(b)) * dir; } }); list.append(items); }); })(), }, }); })(Pontoon || {});