Server : Apache System : Linux iad1-shared-b8-43 6.6.49-grsec-jammy+ #10 SMP Thu Sep 12 23:23:08 UTC 2024 x86_64 User : dh_edsupp ( 6597262) PHP Version : 8.2.26 Disable Function : NONE Directory : /lib/python3/dist-packages/trac/htdocs/js/ |
Upload File : |
// Create and modify custom queries (incl. support for batch modification) (function($){ // Create a <label> function createLabel(text, htmlFor) { var label = $($.htmlFormat("<label>$1</label>", text)); if (htmlFor) label.attr("for", htmlFor).addClass("control"); return label; } function createTextHtml(name, size) { return $.htmlFormat('<input type="text" name="$1" size="$2" />', name, size); } // Create an <input type="text"> function createText(name, size) { return $(createTextHtml(name, size)); } // Create an <input type="checkbox"> function createCheckbox(name, value, id) { return $($.htmlFormat('<input type="checkbox" id="$1" name="$2"' + ' value="$3">', id, name, value)); } // Create an <input type="radio"> function createRadio(name, value, id) { // Workaround for IE, otherwise the radio buttons are not selectable return $($.htmlFormat('<input type="radio" id="$1" name="$2"' + ' value="$3">', id, name, value)); } // Append a list of <option> to an element function appendOptions(e, options) { for (var i = 0; i < options.length; i++) { var opt = options[i], v = opt, t = opt; if (typeof opt == "object") v = opt.value, t = opt.name; $($.htmlFormat('<option value="$1">$2</option>', v, t)).appendTo(e); } } // Create a <select> function createSelect(name, options, optional, optgroups) { var e = $($.htmlFormat('<select name="$1">', name)); if (optional) $("<option>").appendTo(e); appendOptions(e, options); if (optgroups) { for (var i = 0; i < optgroups.length; i++) { var grp = optgroups[i]; var optgrp = $($.htmlFormat('<optgroup label="$1">', grp.label)); appendOptions(optgrp, grp.options); optgrp.appendTo(e); } } return e; } window.initializeFilters = function() { // Remove an existing row from the filters table function removeRow(button, propertyName) { var m = propertyName.match(/^(\d+)_(.*)$/); var clauseNum = m[1], field = m[2]; var tr = $(button).closest("tr"); // Keep the filter label when removing the first row var label = $("#label_" + propertyName); if (label.length && (label.closest("tr")[0] == tr[0])) { var next = tr.next("." + field); if (next.length) { var thisTh = tr.children().eq(1); var nextTh = next.children().eq(1); if (nextTh.attr("colSpan") == 1) { nextTh.replaceWith(thisTh); } else { nextTh.attr("colSpan", 1).before(thisTh); next.children().eq(2).replaceWith(tr.children().eq(1)); } } } // Remove the row, filter tbody or clause tbody var tbody = tr.closest("tbody"); if (tbody.children("tr").length > 1) { tr.remove(); } else { var table = tbody.closest("table.trac-clause"); var ctbody = table.closest("tbody"); if (table.children().length > 2 || !ctbody.siblings().length) { tbody.remove(); if (!ctbody.siblings().length && table.children().length == 1) { $("#add_clause").attr("disabled", true); } } else { var add_clause = $("#add_clause", ctbody); if (add_clause.length) $("tr.actions td.and", ctbody.prev()).attr("colSpan", 2) .after(add_clause.closest("td")); if (ctbody.prev().length == 0) ctbody.next().children("tr:first").attr("style", "display: none"); ctbody.remove(); return; } } // Re-enable non-multiline filter $("#add_filter_" + clauseNum + " option[value='" + field + "']") .enable(); } var $filters = $("#filters"); // Invert or flip checkboxes in the row $filters.on("click", "th label", function(event) { var name = this.id.slice(6); var $checkboxes = $filters.find("input[name=" + name + "]") .filter(":checkbox"); if (event.metaKey || event.altKey) { $checkboxes.each(function() { $(this).prop("checked", !this.checked) }); } else { var numChecked = $checkboxes.filter(":checked").length; var allChecked = numChecked == $checkboxes.length; $checkboxes.each(function() { $(this).prop("checked", !allChecked) }); } return false; }); // Select only clicked checkbox and clear others $filters.exclusiveOnClick("td :checkbox, td :checkbox + label"); $("fieldset#columns").exclusiveOnClick("label"); // Make the submit buttons for removing filters client-side triggers $filters.find("input[type='submit'][name^='rm_filter_']").each(function() { var idx = this.name.search(/_\d+$/); if (idx < 0) idx = this.name.length; var propertyName = this.name.substring(10, idx); $(this).replaceWith( $($.htmlFormat('<input type="button" value="$1">', this.value)) .click(function() { removeRow(this, propertyName); return false; })); }); // Make the drop-down menu for adding a filter a client-side trigger $filters.find("select[name^=add_filter_]").change(function() { if (this.selectedIndex < 1) return; if (this.options[this.selectedIndex].disabled) { // IE doesn't support disabled options alert(_("A filter already exists for that property")); this.selectedIndex = 0; return; } var propertyName = this.options[this.selectedIndex].value; var property = properties[propertyName]; var table = $(this).closest("table.trac-clause")[0]; var tbody = $("tr." + propertyName, table).closest("tbody").eq(0); var tr = $("<tr>").addClass(propertyName); var clauseNum = $(this).attr("name").split("_").pop(); propertyName = clauseNum + "_" + propertyName; // Add the remove button tr.append($('<td>') .append($('<div class="inlinebuttons">') .append($('<input type="button" value="–">') .click(function() { removeRow(this, propertyName); })))); // Add the row header var th = $('<th scope="row">'); if (!tbody.length) { th.append(createLabel(property.label) .attr("id", "label_" + propertyName)); } else { th.attr("colSpan", property.type == "time"? 1: 2) .append(createLabel(_("or"))) } tr.append(th); var td = $("<td>"); var focusElement = null; if (property.type == "radio" || property.type == "checkbox" || property.type == "time") { td.addClass("filter").attr("colSpan", 2); if (property.type == "radio") { for (var i = 0; i < property.options.length; i++) { var option = property.options[i]; var control = createCheckbox(propertyName, option, propertyName + "_" + option); if (i == 0) focusElement = control; td.append(control).append(" ") .append(createLabel(option ? option : "none", propertyName + "_" + option)).append(" "); } } else if (property.type == "checkbox") { focusElement = createRadio(propertyName, "1", propertyName + "_on"); td.append(focusElement) .append(" ").append(createLabel(_("yes"), propertyName + "_on")) .append(" ") .append(createRadio(propertyName, "0", propertyName + "_off")) .append(" ").append(createLabel(_("no"), propertyName + "_off")); } else if (property.type == "time") { var startElement = createTextHtml(propertyName, 14); var endElement = createTextHtml(propertyName + "_end", 14); td.append($.parseHTML( _("<label>between %(start)s</label> <label>and %(end)s</label>", {start: startElement, end: endElement}))); focusElement = td.find('input[name="' + propertyName + '"]'); if (property.format == "datetime") { td.find('input').datetimepicker(); } else if (property.format == "date" || property.format == "relative") { td.find('input').datepicker(); } } tr.append(td); } else { if (!tbody.length) { // Add the mode selector td.addClass("mode") .append(createSelect(propertyName + "_mode", modes[property.type])) .appendTo(tr); } // Add the selector or text input for the actual filter value td = $("<td>").addClass("filter"); if (property.type == "select") { focusElement = createSelect(propertyName, property.options, property.optional, property.optgroups); } else if ((property.type == "text") || (property.type == "id") || (property.type == "textarea")) { focusElement = createText(propertyName, 42); } td.append(focusElement).appendTo(tr); } if (!tbody.length) { tbody = $("<tbody>"); // Find the insertion point for the new row. We try to keep the filter // rows in the same order as the options in the 'Add filter' drop-down, // because that's the order they'll appear in when submitted var insertionPoint = $(this).closest("tbody"); outer: for (var i = this.selectedIndex + 1; i < this.options.length; i++) { for (var j = 0; j < table.tBodies.length; j++) { if (table.tBodies[j].rows[0].className == this.options[i].value) { insertionPoint = $(table.tBodies[j]); break outer; } } } insertionPoint.before(tbody); } tbody.append(tr); if(focusElement) focusElement.focus(); // Disable the add filter in the drop-down list if (property.type == "radio" || property.type == "checkbox" || property.type == "id") this.options[this.selectedIndex].disabled = true; this.selectedIndex = 0; // Enable the Or... button if it's been disabled $("#add_clause").attr("disabled", false); }).next("div.inlinebuttons").remove(); // Add a new empty clause at the end by cloning the current last clause function addClause(select) { var clauseNum = parseInt($(select).attr("name").split("_").pop()); var tbody = $(select).closest("tbody").parents("tbody").eq(0); var copy = tbody.clone(true); $(select).closest("td").next().attr("colSpan", 4).end().remove(); $("tr:first", copy).removeAttr("style"); $("tr tbody:not(:last)", copy).remove(); var newId = "add_filter_" + clauseNum; $("select[name^=add_filter_]", copy).attr("id", newId) .attr("name", newId) .children().enable().end() .prev().attr("for", newId); $("select[name^=add_clause_]", copy) .attr("name", "add_clause_" + (clauseNum + 1)); tbody.after(copy); } var add_clause = $("#add_clause"); add_clause.change(function() { // Add a new clause and fire a change event on the new clause's // add_filter select. var field = $(this).val(); addClause(this); $("#add_clause").closest("tr").find("select[name^=add_filter_]") .val(field).change(); }).next("div.inlinebuttons").remove(); if (!add_clause.closest("tbody").siblings().length) { // That is, if there are no filters added to this clause add_clause.attr("disabled", true); } }; window.initializeBatch = function() { // Create the appropriate input for the property. function createBatchInput(propertyName, property) { var td = $('<td class="batchmod_property">'); var inputName = getBatchInputName(propertyName); var focusElement = null; switch (property.type) { case 'select': focusElement = createSelect(inputName, property.options, property.optional, property.optgroups); focusElement.attr("id", inputName); td.append(focusElement); break; case 'radio': for (var i = 0; i < property.options.length; i++) { var option = property.options[i]; var control = createRadio(inputName, option, inputName + "_" + option); if (i == 0) focusElement = control; td.append(control).append(" ") .append(createLabel(option ? option : "none", inputName + "_" + option)) .append(" "); } break; case 'checkbox': focusElement = createRadio(inputName, "1", inputName + "_on"); td.append(focusElement) .append(" ").append(createLabel(_("yes"), inputName + "_on")) .append(" ") .append(createRadio(inputName, "0", inputName + "_off")) .append(" ").append(createLabel(_("no"), inputName + "_off")); break; case 'text': if ($.inArray(propertyName, batch_list_properties) >= 0) { focusElement = appendBatchListControls(td, propertyName, inputName); } else { focusElement = createText(inputName, 42).attr("id", inputName); td.append(focusElement); } break; case 'time': focusElement = createText(inputName, 42).addClass("time") .attr("id", inputName); if (property.format == "datetime") { focusElement.datetimepicker(); } else if (property.format == "date") { focusElement.datepicker(); } td.append(focusElement); break; } return [td, focusElement]; } function appendBatchListControls(td, propertyName, inputName) { var modeSelect = createSelect("batchmod_mode_" + propertyName, batch_list_modes).attr("id", inputName); var name1 = "batchmod_primary_" + propertyName; var name2 = "batchmod_secondary_" + propertyName; var text1 = createText(name1, 42).attr("id", name1); var text2 = createText(name2, 42).attr("id", name2); var label1 = createLabel(" " + batch_list_modes[0]['name'] + ":", name1); var label2 = createLabel(_(" remove:"), name2); td.append(modeSelect); td.append(label1); td.append(text1); // Only mode 2 ("Add / remove") has a second text field: $(modeSelect).change(function() { if (this.selectedIndex == 2) { td.append(label2); td.append(text2); label1.text(_(" add:")); } else { if (td.has(text2).length) { text2.remove(); label2.remove(); } label1.text(" " + batch_list_modes[this.selectedIndex]['name'] + ":"); // FIXME: french l10n } }); return text1; } function getBatchInputName(propertyName) { return 'batchmod_value_' + propertyName; } function getDisabledBatchOptions() { return $("#add_batchmod_field option:disabled"); } var $table = $("table.listing"); // Add a new column with checkboxes for each ticket. // Selecting a ticket marks it for inclusion in the batch. var $results_cells = $(".trac-query-results tr td.id", $table); if ($results_cells.length > 0) { $(".trac-query-heading tr th.id", $table).before($('<th class="sel">')); $results_cells.each(function() { var tId = $(this).text().substring(1); $(this).before( $('<td class="sel">').append( $('<input type="checkbox" name="selected_ticket" />').attr({ title: babel.format(_("Select ticket #%(id)s for modification"), {id: tId}), value: tId }) ) ); }); $(".trac-query-summary tr td.id", $table).before($('<td>')); $("tbody tr .trac-colspan", $table).each(function () { $(this).attr('colspan', $(this).attr('colspan') + 1); }); } // Add a Select All checkbox at the top of the column. $table.addSelectAllCheckboxes(); // Prevent submit of form if required items are not selected. $("#batchmod_submit").disableSubmit("input[name='selected_ticket']") .click(function() { var valid = true; // Remove existing validation messages. $(".batchmod_required").remove(); // Check that each radio property has something selected. getDisabledBatchOptions().each(function() { var propertyName = $(this).val(); if (properties[propertyName].type == "radio") { var isChecked = false; var inputName = getBatchInputName(propertyName); $("[name=" + inputName + "]").each(function() { isChecked = isChecked || $(this).is(':checked'); }); if (!isChecked) { // Select the last label in the row to add the error message. $("[name=" + inputName + "] ~ label:last") .after('<span class="batchmod_required">Required</span>'); valid = false; } } }); return valid; }); // Create an array of selected items on form submit. $("form#batchmod_form").submit(function() { var selectedTix = []; $("input[name=selected_ticket]:checked").each(function() { selectedTix.push(this.value); }); $("input[name=selected_tickets]").val(selectedTix); }); // Collapse the form by default $("#batchmod_fieldset").toggleClass("collapsed"); // Add the new batch modify field when the user selects one from the // dropdown. $("#add_batchmod_field").change(function() { if (this.selectedIndex < 1) { return; } // Trac has a properties object that has information about each property // that filters could be built with. We use it here to add batchmod // fields. var propertyName = this.options[this.selectedIndex].value; var property = properties[propertyName]; var tr = $("<tr>").attr('id', 'batchmod_' + propertyName); // Add the remove button tr.append($('<td>') .append($('<div class="inlinebuttons">') .append($('<input type="button" value="–">') .click(function() { $('#batchmod_' + propertyName).remove(); $($.htmlFormat("#add_batchmod_field option[value='$1']", propertyName)).enable(); }) ) ) ); // Add the header row. tr.append($('<th scope="row">') .append(createLabel(property.label + ":", // FIXME: french l10n getBatchInputName(propertyName))) ); // Add the input element. var batchInput = createBatchInput(propertyName, property); tr.append(batchInput[0]); // New rows are added in the same order as listed in the dropdown. // This is the same behavior as the filters. var insertionPoint = null; for (var i = this.selectedIndex + 1; i < this.options.length; i++) { if (insertionPoint === null && this.options[i].disabled) { insertionPoint = $("#batchmod_" + this.options[i].value); } } if (insertionPoint === null) { insertionPoint = $("#add_batchmod_field_row"); } insertionPoint.before(tr); // Disable each element from the option list when it is selected. this.options[this.selectedIndex].disabled = true; if (batchInput[1]) batchInput[1].focus(); this.selectedIndex = 0; }); // Make the form visible $("#batchmod_form").show(); } })(jQuery);