123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /* archweb.js
- * Homepage: https://projects.archlinux.org/archweb.git/
- * Copyright: 2007-2014 The Archweb Team
- * License: GPLv2
- *
- * This file is part of Archweb.
- *
- * Archweb is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Archweb is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with Archweb. If not, see <http://www.gnu.org/licenses/>.
- */
- /*'use strict';*/
- /* tablesorter custom parsers for various pages:
- * devel/index.html, mirrors/status.html, todolists/view.html */
- if (typeof $ !== 'undefined' && typeof $.tablesorter !== 'undefined') {
- $.tablesorter.addParser({
- id: 'pkgcount',
- is: function(s) { return false; },
- format: function(s) {
- var m = s.match(/\d+/);
- return m ? parseInt(m[0], 10) : 0;
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- id: 'todostatus',
- is: function(s) { return false; },
- format: function(s) {
- if (s.match(/incomplete/i)) {
- return 1;
- } else if (s.match(/in-progress/i)) {
- return 0.5;
- }
- return 0;
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- /* sorts numeric, but put '', 'unknown', and '∞' last. */
- id: 'mostlydigit',
- special: ['', 'unknown', '∞'],
- is: function(s, table) {
- var c = table.config;
- return ($.inArray(s, this.special) > -1) || $.tablesorter.isDigit(s, c);
- },
- format: function(s, t) {
- if ($.inArray(s, this.special) > -1) {
- return Number.MAX_VALUE;
- }
- return $.tablesorter.formatFloat(s, t);
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- /* sorts duration; put '', 'unknown', and '∞' last. */
- id: 'duration',
- re: /^([0-9]+):([0-5][0-9])$/,
- special: ['', 'unknown', '∞'],
- is: function(s) {
- return ($.inArray(s, this.special) > -1) || this.re.test(s);
- },
- format: function(s) {
- if ($.inArray(s, this.special) > -1) {
- return Number.MAX_VALUE;
- }
- var matches = this.re.exec(s);
- if (!matches) {
- return Number.MAX_VALUE;
- }
- return matches[1] * 60 + matches[2];
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- id: 'epochdate',
- is: function(s) { return false; },
- format: function(s, t, c) {
- /* TODO: this assumes our magic class is the only one */
- var epoch = $(c).attr('class');
- if (epoch.indexOf('epoch-') !== 0) {
- return 0;
- }
- return epoch.slice(6);
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- id: 'longDateTime',
- re: /^(\d{4})-(\d{2})-(\d{2}) ([012]\d):([0-5]\d)(:([0-5]\d))?( (\w+))?$/,
- is: function(s) {
- return this.re.test(s);
- },
- format: function(s, t) {
- var matches = this.re.exec(s);
- if (!matches) {
- return 0;
- }
- /* skip group 6, group 7 is optional seconds */
- if (matches[7] === undefined) {
- matches[7] = 0;
- }
- /* The awesomeness of the JS date constructor. Month needs to be
- * between 0-11, because things have to be difficult. */
- var date = new Date(matches[1], matches[2] - 1, matches[3],
- matches[4], matches[5], matches[7]);
- return $.tablesorter.formatFloat(date.getTime(), t);
- },
- type: 'numeric'
- });
- $.tablesorter.addParser({
- id: 'filesize',
- re: /^(\d+(?:\.\d+)?)[ \u00a0](bytes?|[KMGTPEZY]i?B)$/,
- is: function(s) {
- return this.re.test(s);
- },
- format: function(s) {
- var matches = this.re.exec(s);
- if (!matches) {
- return 0;
- }
- var size = parseFloat(matches[1]),
- suffix = matches[2];
- switch(suffix) {
- /* intentional fall-through at each level */
- case 'YB':
- case 'YiB':
- size *= 1024;
- case 'ZB':
- case 'ZiB':
- size *= 1024;
- case 'EB':
- case 'EiB':
- size *= 1024;
- case 'PB':
- case 'PiB':
- size *= 1024;
- case 'TB':
- case 'TiB':
- size *= 1024;
- case 'GB':
- case 'GiB':
- size *= 1024;
- case 'MB':
- case 'MiB':
- size *= 1024;
- case 'KB':
- case 'KiB':
- size *= 1024;
- }
- return size;
- },
- type: 'numeric'
- });
- $.tablesorter.removeParser = function(id) {
- $.tablesorter.parsers = $.grep($.tablesorter.parsers,
- function(ele, i) {
- return ele.id !== id;
- });
- };
- // We don't use currency, and the parser is over-zealous at deciding it
- // matches. Kill it from the parser selection.
- $.tablesorter.removeParser('currency');
- }
- (function($) {
- $.fn.enableCheckboxRangeSelection = function() {
- var lastCheckbox = null,
- spec = this;
- spec.unbind("click.checkboxrange");
- spec.bind("click.checkboxrange", function(e) {
- if (lastCheckbox !== null && e.shiftKey) {
- spec.slice(
- Math.min(spec.index(lastCheckbox), spec.index(e.target)),
- Math.max(spec.index(lastCheckbox), spec.index(e.target)) + 1
- ).attr({checked: e.target.checked ? "checked" : ""});
- }
- lastCheckbox = e.target;
- });
- };
- })(jQuery);
- /* news/add.html */
- function enablePreview() {
- $('#news-preview-button').click(function(event) {
- event.preventDefault();
- $.post('/news/preview/', {
- data: $('#id_content').val(),
- csrfmiddlewaretoken: $('#newsform input[name=csrfmiddlewaretoken]').val()
- },
- function(data) {
- $('#news-preview-data').html(data);
- $('#news-preview').show();
- }
- );
- $('#news-preview-title').html($('#id_title').val());
- });
- }
- /* packages/details.html */
- function ajaxifyFiles() {
- $('#filelink').click(function(event) {
- event.preventDefault();
- $.getJSON(this.href + 'json/', function(data) {
- // Map each file item into an <li/> with the correct class
- var list_items = $.map(data.files, function(value, i) {
- var cls = value.match(/\/$/) ? 'd' : 'f';
- return ['<li class="', cls, '">', value, '</li>'];
- });
- $('#pkgfilelist').empty();
- if (data.pkg_last_update > data.files_last_update) {
- $('#pkgfilelist').append('<p class="message">Note: This file list was generated from a previous version of the package; it may be out of date.</p>');
- }
- if (list_items.length > 0) {
- $('#pkgfilelist').append('<ul>' + list_items.join('') + '</ul>');
- } else if (data.files_last_update === null) {
- $('#pkgfilelist').append('<p class="message">No file list available.</p>');
- } else {
- $('#pkgfilelist').append('<p class="message">Package has no files.</p>');
- }
- });
- });
- }
- function collapseDependsList(list) {
- list = $(list);
- // Hide everything past a given limit. Don't do anything if we don't have
- // enough items, or the link already exists.
- var limit = 20,
- linkid = list.attr('id') + 'link',
- items = list.find('li').slice(limit);
- if (items.length <= 1 || $('#' + linkid).length > 0) {
- return;
- }
- items.hide();
- list.after('<p><a id="' + linkid + '" href="#">Show More…</a></p>');
- // add link and wire it up to show the hidden items
- $('#' + linkid).click(function(event) {
- event.preventDefault();
- list.find('li').show();
- // remove the full <p/> node from the DOM
- $(this).parent().remove();
- });
- }
- function collapseRelatedTo(elements) {
- var limit = 5;
- $(elements).each(function(idx, ele) {
- ele = $(ele);
- // Hide everything past a given limit. Don't do anything if we don't
- // have enough items, or the link already exists.
- var items = ele.find('span.related').slice(limit);
- if (items.length <= 1 || ele.find('a.morelink').length > 0) {
- return;
- }
- items.hide();
- ele.append('<a class="morelink" href="#">More…</a>');
- // add link and wire it up to show the hidden items
- ele.find('a.morelink').click(function(event) {
- event.preventDefault();
- ele.find('span.related').show();
- $(this).remove();
- });
- });
- }
- /* packages/differences.html */
- function filter_packages() {
- /* start with all rows, and then remove ones we shouldn't show */
- var rows = $('#tbody_differences').children(),
- all_rows = rows;
- if (!$('#id_multilib').is(':checked')) {
- rows = rows.not('.multilib').not('.multilib-testing');
- }
- var arch = $('#id_archonly').val();
- if (arch !== 'all') {
- rows = rows.filter('.' + arch);
- }
- if (!$('#id_minor').is(':checked')) {
- /* this check is done last because it is the most expensive */
- var pat = /(.*)-(.+)/;
- rows = rows.filter(function(index) {
- var cells = $(this).children('td');
- /* all this just to get the split version out of the table cell */
- var ver_a = cells.eq(2).text().match(pat);
- if (!ver_a) {
- return true;
- }
- var ver_b = cells.eq(3).text().match(pat);
- if (!ver_b) {
- return true;
- }
- /* first check pkgver */
- if (ver_a[1] !== ver_b[1]) {
- return true;
- }
- /* pkgver matched, so see if rounded pkgrel matches */
- if (Math.floor(parseFloat(ver_a[2])) ===
- Math.floor(parseFloat(ver_b[2]))) {
- return false;
- }
- /* pkgrel didn't match, so keep the row */
- return true;
- });
- }
- /* hide all rows, then show the set we care about */
- all_rows.hide();
- rows.show();
- /* make sure we update the odd/even styling from sorting */
- $('.results').trigger('applyWidgets', [false]);
- }
- function filter_packages_reset() {
- $('#id_archonly').val('both');
- $('#id_multilib').removeAttr('checked');
- $('#id_minor').removeAttr('checked');
- filter_packages();
- }
- /* todolists/view.html */
- function todolist_flag() {
- // TODO: fix usage of this
- var link = this;
- $.getJSON(link.href, function(data) {
- $(link).text(data.status).removeClass(
- 'complete inprogress incomplete').addClass(
- data.css_class.toLowerCase());
- /* let tablesorter know the cell value has changed */
- $('.results').trigger('updateCell', [$(link).closest('td')[0], false, null]);
- });
- return false;
- }
- function filter_pkgs_list(filter_ele, tbody_ele) {
- /* start with all rows, and then remove ones we shouldn't show */
- var rows = $(tbody_ele).children(),
- all_rows = rows;
- /* apply the filters, cheaper ones first */
- if ($('#id_mine_only').is(':checked')) {
- rows = rows.filter('.mine');
- }
- /* apply arch and repo filters */
- $(filter_ele + ' .arch_filter').add(
- filter_ele + ' .repo_filter').each(function() {
- if (!$(this).is(':checked')) {
- rows = rows.not('.' + $(this).val());
- }
- });
- /* more expensive filter because of 'has' call */
- if ($('#id_incomplete').is(':checked')) {
- rows = rows.has('.incomplete');
- }
- /* hide all rows, then show the set we care about */
- all_rows.hide();
- rows.show();
- $('#filter-count').text(rows.length);
- /* make sure we update the odd/even styling from sorting */
- $('.results').trigger('applyWidgets', [false]);
- }
- function filter_pkgs_reset(callback) {
- $('#id_incomplete').removeAttr('checked');
- $('#id_mine_only').removeAttr('checked');
- $('.arch_filter').attr('checked', 'checked');
- $('.repo_filter').attr('checked', 'checked');
- callback();
- }
- function filter_todolist_save(list_id) {
- var state = $('#todolist_filter').serializeArray();
- localStorage['filter_todolist_' + list_id] = JSON.stringify(state);
- }
- function filter_todolist_load(list_id) {
- var state = localStorage['filter_todolist_' + list_id];
- if (!state)
- return;
- state = JSON.parse(state);
- $('#todolist_filter input[type="checkbox"]').removeAttr('checked');
- $.each(state, function (i, v) {
- // this assumes our only filters are checkboxes
- $('#todolist_filter input[name="' + v['name'] + '"]').attr('checked', 'checked');
- });
- }
- function filter_report_save(report_id) {
- var state = $('#report_filter').serializeArray();
- localStorage['filter_report_' + report_id] = JSON.stringify(state);
- }
- function filter_report_load(report_id) {
- var state = localStorage['filter_report_' + report_id];
- if (!state)
- return;
- state = JSON.parse(state);
- $('#report_filter input[type="checkbox"]').removeAttr('checked');
- $.each(state, function (i, v) {
- // this assumes our only filters are checkboxes
- $('#report_filter input[name="' + v['name'] + '"]').attr('checked', 'checked');
- });
- }
- /* signoffs.html */
- function signoff_package() {
- // TODO: fix usage of this
- var link = this;
- $.getJSON(link.href, function(data) {
- link = $(link);
- var signoff = null,
- cell = link.closest('td');
- if (data.created) {
- signoff = $('<li>').addClass('signed-username').text(data.user);
- var list = cell.children('ul.signoff-list');
- if (list.size() === 0) {
- list = $('<ul class="signoff-list">').prependTo(cell);
- }
- list.append(signoff);
- } else if(data.user) {
- signoff = link.closest('td').find('li').filter(function(index) {
- return $(this).text() == data.user;
- });
- }
- if (signoff && data.revoked) {
- signoff.text(signoff.text() + ' (revoked)');
- }
- /* update the approved column to reflect reality */
- var approved = link.closest('tr').children('.approval');
- approved.attr('class', 'approval');
- if (data.known_bad) {
- approved.text('Bad').addClass('signoff-bad');
- } else if (!data.enabled) {
- approved.text('Disabled').addClass('signoff-disabled');
- } else if (data.approved) {
- approved.text('Yes').addClass('signoff-yes');
- } else {
- approved.text('No').addClass('signoff-no');
- }
- link.removeAttr('title');
- /* Form our new link. The current will be something like
- * '/packages/repo/arch/package/...' */
- var base_href = link.attr('href').split('/').slice(0, 5).join('/');
- if (data.revoked) {
- link.text('Signoff');
- link.attr('href', base_href + '/signoff/');
- /* should we be hiding the link? */
- if (data.known_bad || !data.enabled) {
- link.remove();
- }
- } else {
- link.text('Revoke Signoff');
- link.attr('href', base_href + '/signoff/revoke/');
- }
- /* let tablesorter know the cell value has changed */
- $('.results').trigger('updateCell', [approved[0], false, null]);
- });
- return false;
- }
- function filter_signoffs() {
- /* start with all rows, and then remove ones we shouldn't show */
- var rows = $('#tbody_signoffs').children(),
- all_rows = rows;
- /* apply arch and repo filters */
- $('#signoffs_filter .arch_filter').add(
- '#signoffs_filter .repo_filter').each(function() {
- if (!$(this).is(':checked')) {
- rows = rows.not('.' + $(this).val());
- }
- });
- /* and then the slightly more expensive pending check */
- if ($('#id_pending').is(':checked')) {
- rows = rows.has('td.signoff-no');
- }
- /* hide all rows, then show the set we care about */
- all_rows.hide();
- rows.show();
- $('#filter-count').text(rows.length);
- /* make sure we update the odd/even styling from sorting */
- $('.results').trigger('applyWidgets', [false]);
- filter_signoffs_save();
- }
- function filter_signoffs_reset() {
- $('#signoffs_filter .arch_filter').attr('checked', 'checked');
- $('#signoffs_filter .repo_filter').attr('checked', 'checked');
- $('#id_pending').removeAttr('checked');
- filter_signoffs();
- }
- function filter_signoffs_save() {
- var state = $('#signoffs_filter').serializeArray();
- localStorage['filter_signoffs'] = JSON.stringify(state);
- }
- function filter_signoffs_load() {
- var state = localStorage['filter_signoffs'];
- if (!state)
- return;
- state = JSON.parse(state);
- $('#signoffs_filter input[type="checkbox"]').removeAttr('checked');
- $.each(state, function (i, v) {
- // this assumes our only filters are checkboxes
- $('#signoffs_filter input[name="' + v['name'] + '"]').attr('checked', 'checked');
- });
- }
- function collapseNotes(elements) {
- // Remove any trailing <br/> tags from the note contents
- $(elements).children('br').filter(':last-child').filter(function(i, e) { return !e.nextSibling; }).remove();
- var maxElements = 8;
- $(elements).each(function(idx, ele) {
- ele = $(ele);
- // Hide everything past a given limit. Don't do anything if we don't
- // have enough items, or the link already exists.
- var contents = ele.contents();
- if (contents.length <= maxElements || ele.find('a.morelink').length > 0) {
- return;
- }
- contents.slice(maxElements).wrapAll('<div class="hide"/>');
- ele.append('<br class="morelink-spacer"/><a class="morelink" href="#">Show More…</a>');
- // add link and wire it up to show the hidden items
- ele.find('a.morelink').click(function(event) {
- event.preventDefault();
- $(this).remove();
- ele.find('br.morelink-spacer').remove();
- // move the div contents back and delete the empty div
- var hidden = ele.find('div.hide');
- hidden.contents().appendTo(ele);
- hidden.remove();
- });
- });
- }
- /* visualizations */
- function format_filesize(size, decimals) {
- /*var labels = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB'];*/
- var labels = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],
- label = 0;
- while (size > 2048.0 && label < labels.length - 1) {
- label++;
- size /= 1024.0;
- }
- if (decimals === undefined) {
- decimals = 2;
- }
- return size.toFixed(decimals) + ' ' + labels[label];
- }
- /* HTML5 input type and attribute enhancements */
- function modify_attributes(to_change) {
- /* jQuery doesn't let us change the 'type' attribute directly due to IE
- woes, so instead we can clone and replace, setting the type. */
- $.each(to_change, function(id, attrs) {
- var obj = $(id);
- obj.replaceWith(obj.clone().attr(attrs));
- });
- }
|