summaryrefslogtreecommitdiff
path: root/sitestatic/archweb.js
diff options
context:
space:
mode:
Diffstat (limited to 'sitestatic/archweb.js')
-rw-r--r--sitestatic/archweb.js577
1 files changed, 577 insertions, 0 deletions
diff --git a/sitestatic/archweb.js b/sitestatic/archweb.js
new file mode 100644
index 00000000..dce9cd0c
--- /dev/null
+++ b/sitestatic/archweb.js
@@ -0,0 +1,577 @@
+/* archweb.js
+ * Homepage: https://projects.archlinux.org/archweb.git/
+ * Copyright: 2007-2013 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));
+ });
+}