1134 lines
44 KiB
JavaScript
1134 lines
44 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This Source Code Form is "Incompatible With Secondary Licenses", as
|
|
* defined by the Mozilla Public License, v. 2.0.
|
|
*/
|
|
|
|
/* This library assumes that the needed YUI libraries have been loaded
|
|
already. */
|
|
|
|
var bz_no_validate_enter_bug = false;
|
|
function validateEnterBug(theform) {
|
|
// This is for the "bookmarkable templates" button.
|
|
if (bz_no_validate_enter_bug) {
|
|
// Set it back to false for people who hit the "back" button
|
|
bz_no_validate_enter_bug = false;
|
|
return true;
|
|
}
|
|
|
|
var component = theform.component;
|
|
var short_desc = theform.short_desc;
|
|
var version = theform.version;
|
|
var bug_status = theform.bug_status;
|
|
var description = theform.comment;
|
|
var attach_data = theform.data;
|
|
var attach_desc = theform.description;
|
|
|
|
var current_errors = YAHOO.util.Dom.getElementsByClassName(
|
|
'validation_error_text', null, theform);
|
|
for (var i = 0; i < current_errors.length; i++) {
|
|
current_errors[i].parentNode.removeChild(current_errors[i]);
|
|
}
|
|
var current_error_fields = YAHOO.util.Dom.getElementsByClassName(
|
|
'validation_error_field', null, theform);
|
|
for (var i = 0; i < current_error_fields.length; i++) {
|
|
var field = current_error_fields[i];
|
|
YAHOO.util.Dom.removeClass(field, 'validation_error_field');
|
|
}
|
|
|
|
var focus_me;
|
|
|
|
// These are checked in the reverse order that they appear on the page,
|
|
// so that the one closest to the top of the form will be focused.
|
|
if (attach_data && attach_data.value && YAHOO.lang.trim(attach_desc.value) == '') {
|
|
_errorFor(attach_desc, 'attach_desc');
|
|
focus_me = attach_desc;
|
|
}
|
|
// bug_status can be undefined if the bug_status field is not editable by
|
|
// the currently logged in user.
|
|
if (bug_status) {
|
|
var check_description = status_comment_required[bug_status.value];
|
|
if (check_description && YAHOO.lang.trim(description.value) == '') {
|
|
_errorFor(description, 'description');
|
|
focus_me = description;
|
|
}
|
|
}
|
|
if (YAHOO.lang.trim(short_desc.value) == '') {
|
|
_errorFor(short_desc);
|
|
focus_me = short_desc;
|
|
}
|
|
if (version.selectedIndex < 0) {
|
|
_errorFor(version);
|
|
focus_me = version;
|
|
}
|
|
if (component.selectedIndex < 0) {
|
|
_errorFor(component);
|
|
focus_me = component;
|
|
}
|
|
|
|
if (focus_me) {
|
|
focus_me.focus();
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function _errorFor(field, name) {
|
|
if (!name) name = field.id;
|
|
var string_name = name + '_required';
|
|
var error_text = BUGZILLA.string[string_name];
|
|
var new_node = document.createElement('div');
|
|
YAHOO.util.Dom.addClass(new_node, 'validation_error_text');
|
|
new_node.innerHTML = error_text;
|
|
YAHOO.util.Dom.insertAfter(new_node, field);
|
|
YAHOO.util.Dom.addClass(field, 'validation_error_field');
|
|
}
|
|
|
|
/* This function is never to be called directly, but only indirectly
|
|
* using template/en/default/global/calendar.js.tmpl, so that localization
|
|
* works. For the same reason, if you modify this function's parameter list,
|
|
* you need to modify the documentation in said template as well. */
|
|
function createCalendar(name, start_weekday, months_long, weekdays_short) {
|
|
var cal = new YAHOO.widget.Calendar('calendar_' + name,
|
|
'con_calendar_' + name,
|
|
{ START_WEEKDAY: start_weekday,
|
|
MONTHS_LONG: months_long,
|
|
WEEKDAYS_SHORT: weekdays_short
|
|
});
|
|
YAHOO.bugzilla['calendar_' + name] = cal;
|
|
var field = document.getElementById(name);
|
|
cal.selectEvent.subscribe(setFieldFromCalendar, field, false);
|
|
updateCalendarFromField(field);
|
|
cal.render();
|
|
}
|
|
|
|
/* The onclick handlers for the button that shows the calendar. */
|
|
function showCalendar(field_name) {
|
|
var calendar = YAHOO.bugzilla["calendar_" + field_name];
|
|
var field = document.getElementById(field_name);
|
|
var button = document.getElementById('button_calendar_' + field_name);
|
|
|
|
bz_overlayBelow(calendar.oDomContainer, field);
|
|
calendar.show();
|
|
button.onclick = function() { hideCalendar(field_name); };
|
|
|
|
// Because of the way removeListener works, this has to be a function
|
|
// attached directly to this calendar.
|
|
calendar.bz_myBodyCloser = function(event) {
|
|
var container = this.oDomContainer;
|
|
var target = YAHOO.util.Event.getTarget(event);
|
|
if (target != container && target != button
|
|
&& !YAHOO.util.Dom.isAncestor(container, target))
|
|
{
|
|
hideCalendar(field_name);
|
|
}
|
|
};
|
|
|
|
// If somebody clicks outside the calendar, hide it.
|
|
YAHOO.util.Event.addListener(document.body, 'click',
|
|
calendar.bz_myBodyCloser, calendar, true);
|
|
|
|
// Make Esc close the calendar.
|
|
calendar.bz_escCal = function (event) {
|
|
var key = YAHOO.util.Event.getCharCode(event);
|
|
if (key == 27) {
|
|
hideCalendar(field_name);
|
|
}
|
|
};
|
|
YAHOO.util.Event.addListener(document.body, 'keydown', calendar.bz_escCal);
|
|
}
|
|
|
|
function hideCalendar(field_name) {
|
|
var cal = YAHOO.bugzilla["calendar_" + field_name];
|
|
cal.hide();
|
|
var button = document.getElementById('button_calendar_' + field_name);
|
|
button.onclick = function() { showCalendar(field_name); };
|
|
YAHOO.util.Event.removeListener(document.body, 'click',
|
|
cal.bz_myBodyCloser);
|
|
YAHOO.util.Event.removeListener(document.body, 'keydown', cal.bz_escCal);
|
|
}
|
|
|
|
/* This is the selectEvent for our Calendar objects on our custom
|
|
* DateTime fields.
|
|
*/
|
|
function setFieldFromCalendar(type, args, date_field) {
|
|
var dates = args[0];
|
|
var setDate = dates[0];
|
|
|
|
// We can't just write the date straight into the field, because there
|
|
// might already be a time there.
|
|
var timeRe = /\b(\d{1,2}):(\d\d)(?::(\d\d))?/;
|
|
var currentTime = timeRe.exec(date_field.value);
|
|
var d = new Date(setDate[0], setDate[1] - 1, setDate[2]);
|
|
if (currentTime) {
|
|
d.setHours(currentTime[1], currentTime[2]);
|
|
if (currentTime[3]) {
|
|
d.setSeconds(currentTime[3]);
|
|
}
|
|
}
|
|
|
|
var year = d.getFullYear();
|
|
// JavaScript's "Date" represents January as 0 and December as 11.
|
|
var month = d.getMonth() + 1;
|
|
if (month < 10) month = '0' + String(month);
|
|
var day = d.getDate();
|
|
if (day < 10) day = '0' + String(day);
|
|
var dateStr = year + '-' + month + '-' + day;
|
|
|
|
if (currentTime) {
|
|
var minutes = d.getMinutes();
|
|
if (minutes < 10) minutes = '0' + String(minutes);
|
|
var seconds = d.getSeconds();
|
|
if (seconds > 0 && seconds < 10) {
|
|
seconds = '0' + String(seconds);
|
|
}
|
|
|
|
dateStr = dateStr + ' ' + d.getHours() + ':' + minutes;
|
|
if (seconds) dateStr = dateStr + ':' + seconds;
|
|
}
|
|
|
|
date_field.value = dateStr;
|
|
hideCalendar(date_field.id);
|
|
}
|
|
|
|
/* Sets the calendar based on the current field value.
|
|
*/
|
|
function updateCalendarFromField(date_field) {
|
|
var dateRe = /(\d\d\d\d)-(\d\d?)-(\d\d?)/;
|
|
var pieces = dateRe.exec(date_field.value);
|
|
if (pieces) {
|
|
var cal = YAHOO.bugzilla["calendar_" + date_field.id];
|
|
cal.select(new Date(pieces[1], pieces[2] - 1, pieces[3]));
|
|
var selectedArray = cal.getSelectedDates();
|
|
var selected = selectedArray[0];
|
|
cal.cfg.setProperty("pagedate", (selected.getMonth() + 1) + '/'
|
|
+ selected.getFullYear());
|
|
cal.render();
|
|
}
|
|
}
|
|
|
|
function setupEditLink(id) {
|
|
var link_container = 'container_showhide_' + id;
|
|
var input_container = 'container_' + id;
|
|
var link = 'showhide_' + id;
|
|
hideEditableField(link_container, input_container, link);
|
|
}
|
|
|
|
/* Hide input/select fields and show the text with (edit) next to it */
|
|
function hideEditableField( container, input, action, field_id, original_value, new_value, hide_input ) {
|
|
YAHOO.util.Dom.removeClass(container, 'bz_default_hidden');
|
|
YAHOO.util.Dom.addClass(input, 'bz_default_hidden');
|
|
YAHOO.util.Event.addListener(action, 'click', showEditableField,
|
|
new Array(container, input, field_id, new_value));
|
|
if(field_id != ""){
|
|
// if WEBKIT_CHANGES
|
|
YAHOO.util.Event.addListener(field_id + '_nonedit_display', 'click', showEditableField,
|
|
new Array(container, input, undefined, undefined, field_id));
|
|
// endif WEBKIT_CHANGES
|
|
YAHOO.util.Event.addListener(window, 'load', checkForChangedFieldValues,
|
|
new Array(container, input, field_id, original_value, hide_input ));
|
|
}
|
|
}
|
|
|
|
/* showEditableField (e, ContainerInputArray)
|
|
* Function hides the (edit) link and the text and displays the input/select field
|
|
*
|
|
* var e: the event
|
|
* var ContainerInputArray: An array containing the (edit) and text area and the input being displayed
|
|
* var ContainerInputArray[0]: the container that will be hidden usually shows the (edit) or (take) text
|
|
* var ContainerInputArray[1]: the input area and label that will be displayed
|
|
* var ContainerInputArray[2]: the input/select field id for which the new value must be set
|
|
* var ContainerInputArray[3]: the new value to set the input/select field to when (take) is clicked
|
|
* var ContainerInputArray[4]: [optional] the id of the input element to focus
|
|
*/
|
|
function showEditableField (e, ContainerInputArray) {
|
|
var inputs = new Array();
|
|
var inputArea = YAHOO.util.Dom.get(ContainerInputArray[1]);
|
|
if ( ! inputArea ){
|
|
YAHOO.util.Event.preventDefault(e);
|
|
return;
|
|
}
|
|
YAHOO.util.Dom.addClass(ContainerInputArray[0], 'bz_default_hidden');
|
|
YAHOO.util.Dom.removeClass(inputArea, 'bz_default_hidden');
|
|
if ( inputArea.tagName.toLowerCase() == "input" ) {
|
|
inputs.push(inputArea);
|
|
} else if (ContainerInputArray[2]) {
|
|
inputs.push(document.getElementById(ContainerInputArray[2]));
|
|
} else {
|
|
inputs = inputArea.getElementsByTagName('input');
|
|
}
|
|
if ( inputs.length > 0 ) {
|
|
// Change the first field's value to ContainerInputArray[2]
|
|
// if present before focusing.
|
|
var type = inputs[0].tagName.toLowerCase();
|
|
if (ContainerInputArray[3]) {
|
|
if ( type == "input" ) {
|
|
inputs[0].value = ContainerInputArray[3];
|
|
} else {
|
|
for (var i = 0; inputs[0].length; i++) {
|
|
if ( inputs[0].options[i].value == ContainerInputArray[3] ) {
|
|
inputs[0].options[i].selected = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// if WEBKIT_CHANGES
|
|
var elementToFocus = YAHOO.util.Dom.get(ContainerInputArray[4]);
|
|
if (elementToFocus) {
|
|
// focus on the requested field
|
|
elementToFocus.focus();
|
|
var elementToFocusType = elementToFocus.tagName.toLowerCase();
|
|
if ( elementToFocusType == "input" || elementToFocusType == "textarea" ) {
|
|
elementToFocus.select();
|
|
}
|
|
} else {
|
|
// focus on the first field, this makes it easier to edit
|
|
inputs[0].focus();
|
|
if ( type == "input" || type == "textarea" ) {
|
|
inputs[0].select();
|
|
}
|
|
}
|
|
// endif WEBKIT_CHANGES
|
|
}
|
|
YAHOO.util.Event.preventDefault(e);
|
|
}
|
|
|
|
|
|
/* checkForChangedFieldValues(e, array )
|
|
* Function checks if after the autocomplete by the browser if the values match the originals.
|
|
* If they don't match then hide the text and show the input so users don't get confused.
|
|
*
|
|
* var e: the event
|
|
* var ContainerInputArray: An array containing the (edit) and text area and the input being displayed
|
|
* var ContainerInputArray[0]: the conainer that will be hidden usually shows the (edit) text
|
|
* var ContainerInputArray[1]: the input area and label that will be displayed
|
|
* var ContainerInputArray[2]: the field that is on the page, might get changed by browser autocomplete
|
|
* var ContainerInputArray[3]: the original value from the page loading.
|
|
*
|
|
*/
|
|
function checkForChangedFieldValues(e, ContainerInputArray ) {
|
|
var el = document.getElementById(ContainerInputArray[2]);
|
|
var unhide = false;
|
|
if ( el ) {
|
|
if ( !ContainerInputArray[4]
|
|
&& (el.value != ContainerInputArray[3]
|
|
|| (el.value == "" && el.id != "qa_contact")) )
|
|
{
|
|
unhide = true;
|
|
}
|
|
else {
|
|
var set_default = document.getElementById("set_default_" +
|
|
ContainerInputArray[2]);
|
|
if ( set_default ) {
|
|
if(set_default.checked){
|
|
unhide = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(unhide){
|
|
YAHOO.util.Dom.addClass(ContainerInputArray[0], 'bz_default_hidden');
|
|
YAHOO.util.Dom.removeClass(ContainerInputArray[1], 'bz_default_hidden');
|
|
}
|
|
|
|
}
|
|
|
|
function showPeopleOnChange( field_id_list ) {
|
|
for(var i = 0; i < field_id_list.length; i++) {
|
|
YAHOO.util.Event.addListener( field_id_list[i],'change', showEditableField,
|
|
new Array('bz_qa_contact_edit_container',
|
|
'bz_qa_contact_input'));
|
|
YAHOO.util.Event.addListener( field_id_list[i],'change',showEditableField,
|
|
new Array('bz_assignee_edit_container',
|
|
'bz_assignee_input'));
|
|
}
|
|
}
|
|
|
|
function assignToDefaultOnChange(field_id_list, default_assignee, default_qa_contact) {
|
|
showPeopleOnChange(field_id_list);
|
|
for(var i = 0, l = field_id_list.length; i < l; i++) {
|
|
YAHOO.util.Event.addListener(field_id_list[i], 'change', function(evt, defaults) {
|
|
if (document.getElementById('assigned_to').value == defaults[0]) {
|
|
setDefaultCheckbox(evt, 'set_default_assignee');
|
|
}
|
|
if (document.getElementById('qa_contact')
|
|
&& document.getElementById('qa_contact').value == defaults[1])
|
|
{
|
|
setDefaultCheckbox(evt, 'set_default_qa_contact');
|
|
}
|
|
}, [default_assignee, default_qa_contact]);
|
|
}
|
|
}
|
|
|
|
function initDefaultCheckbox(field_id){
|
|
YAHOO.util.Event.addListener( 'set_default_' + field_id,'change', boldOnChange,
|
|
'set_default_' + field_id);
|
|
YAHOO.util.Event.addListener( window,'load', checkForChangedFieldValues,
|
|
new Array( 'bz_' + field_id + '_edit_container',
|
|
'bz_' + field_id + '_input',
|
|
'set_default_' + field_id ,'1'));
|
|
|
|
YAHOO.util.Event.addListener( window, 'load', boldOnChange,
|
|
'set_default_' + field_id );
|
|
}
|
|
|
|
function showHideStatusItems(e, dupArrayInfo) {
|
|
var el = document.getElementById('bug_status');
|
|
// finish doing stuff based on the selection.
|
|
if ( el ) {
|
|
showDuplicateItem(el);
|
|
|
|
// Make sure that fields whose visibility or values are controlled
|
|
// by "resolution" behave properly when resolution is hidden.
|
|
var resolution = document.getElementById('resolution');
|
|
if (resolution && resolution.options[0].value != '') {
|
|
resolution.bz_lastSelected = resolution.selectedIndex;
|
|
var emptyOption = new Option('', '');
|
|
resolution.insertBefore(emptyOption, resolution.options[0]);
|
|
emptyOption.selected = true;
|
|
}
|
|
YAHOO.util.Dom.addClass('resolution_settings', 'bz_default_hidden');
|
|
if (document.getElementById('resolution_settings_warning')) {
|
|
YAHOO.util.Dom.addClass('resolution_settings_warning',
|
|
'bz_default_hidden');
|
|
}
|
|
YAHOO.util.Dom.addClass('duplicate_display', 'bz_default_hidden');
|
|
|
|
|
|
if ( (el.value == dupArrayInfo[1] && dupArrayInfo[0] == "is_duplicate")
|
|
|| bz_isValueInArray(close_status_array, el.value) )
|
|
{
|
|
YAHOO.util.Dom.removeClass('resolution_settings',
|
|
'bz_default_hidden');
|
|
YAHOO.util.Dom.removeClass('resolution_settings_warning',
|
|
'bz_default_hidden');
|
|
|
|
// Remove the blank option we inserted.
|
|
if (resolution && resolution.options[0].value == '') {
|
|
resolution.removeChild(resolution.options[0]);
|
|
resolution.selectedIndex = resolution.bz_lastSelected;
|
|
}
|
|
}
|
|
|
|
if (resolution) {
|
|
bz_fireEvent(resolution, 'change');
|
|
}
|
|
}
|
|
}
|
|
|
|
function showDuplicateItem(e) {
|
|
var resolution = document.getElementById('resolution');
|
|
var bug_status = document.getElementById('bug_status');
|
|
var dup_id = document.getElementById('dup_id');
|
|
if (resolution) {
|
|
if (resolution.value == 'DUPLICATE' && bz_isValueInArray( close_status_array, bug_status.value) ) {
|
|
// hide resolution show duplicate
|
|
YAHOO.util.Dom.removeClass('duplicate_settings',
|
|
'bz_default_hidden');
|
|
YAHOO.util.Dom.addClass('dup_id_discoverable', 'bz_default_hidden');
|
|
// check to make sure the field is visible or IE throws errors
|
|
if( ! YAHOO.util.Dom.hasClass( dup_id, 'bz_default_hidden' ) ){
|
|
dup_id.focus();
|
|
dup_id.select();
|
|
}
|
|
}
|
|
else {
|
|
YAHOO.util.Dom.addClass('duplicate_settings', 'bz_default_hidden');
|
|
YAHOO.util.Dom.removeClass('dup_id_discoverable',
|
|
'bz_default_hidden');
|
|
dup_id.blur();
|
|
}
|
|
}
|
|
YAHOO.util.Event.preventDefault(e); //prevents the hyperlink from going to the url in the href.
|
|
}
|
|
|
|
function setResolutionToDuplicate(e, duplicate_or_move_bug_status) {
|
|
var status = document.getElementById('bug_status');
|
|
var resolution = document.getElementById('resolution');
|
|
YAHOO.util.Dom.addClass('dup_id_discoverable', 'bz_default_hidden');
|
|
status.value = duplicate_or_move_bug_status;
|
|
bz_fireEvent(status, 'change');
|
|
resolution.value = "DUPLICATE";
|
|
bz_fireEvent(resolution, 'change');
|
|
YAHOO.util.Event.preventDefault(e);
|
|
}
|
|
|
|
function setDefaultCheckbox(e, field_id) {
|
|
var el = document.getElementById(field_id);
|
|
var elLabel = document.getElementById(field_id + "_label");
|
|
if( el && elLabel ) {
|
|
el.checked = "true";
|
|
YAHOO.util.Dom.setStyle(elLabel, 'font-weight', 'bold');
|
|
}
|
|
}
|
|
|
|
function boldOnChange(e, field_id){
|
|
var el = document.getElementById(field_id);
|
|
var elLabel = document.getElementById(field_id + "_label");
|
|
if( el && elLabel ) {
|
|
if( el.checked ){
|
|
YAHOO.util.Dom.setStyle(elLabel, 'font-weight', 'bold');
|
|
}
|
|
else{
|
|
YAHOO.util.Dom.setStyle(elLabel, 'font-weight', 'normal');
|
|
}
|
|
}
|
|
}
|
|
|
|
function updateCommentTagControl(checkbox, field) {
|
|
if (checkbox.checked) {
|
|
YAHOO.util.Dom.addClass(field, 'bz_private');
|
|
} else {
|
|
YAHOO.util.Dom.removeClass(field, 'bz_private');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reset the value of the classification field and fire an event change
|
|
* on it. Called when the product changes, in case the classification
|
|
* field (which is hidden) controls the visibility of any other fields.
|
|
*/
|
|
function setClassification() {
|
|
var classification = document.getElementById('classification');
|
|
var product = document.getElementById('product');
|
|
var selected_product = product.value;
|
|
var select_classification = all_classifications[selected_product];
|
|
classification.value = select_classification;
|
|
bz_fireEvent(classification, 'change');
|
|
}
|
|
|
|
/**
|
|
* Says that a field should only be displayed when another field has
|
|
* a certain value. May only be called after the controller has already
|
|
* been added to the DOM.
|
|
*/
|
|
function showFieldWhen(controlled_id, controller_id, values) {
|
|
var controller = document.getElementById(controller_id);
|
|
// Note that we don't get an object for "controlled" here, because it
|
|
// might not yet exist in the DOM. We just pass along its id.
|
|
YAHOO.util.Event.addListener(controller, 'change',
|
|
handleVisControllerValueChange, [controlled_id, controller, values]);
|
|
}
|
|
|
|
/**
|
|
* Called by showFieldWhen when a field's visibility controller
|
|
* changes values.
|
|
*/
|
|
function handleVisControllerValueChange(e, args) {
|
|
var controlled_id = args[0];
|
|
var controller = args[1];
|
|
var values = args[2];
|
|
|
|
var field = document.getElementById(controlled_id);
|
|
var label_container =
|
|
document.getElementById('field_label_' + controlled_id);
|
|
var field_container =
|
|
document.getElementById('field_container_' + controlled_id);
|
|
var selected = false;
|
|
for (var i = 0; i < values.length; i++) {
|
|
if (bz_valueSelected(controller, values[i])) {
|
|
selected = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selected) {
|
|
YAHOO.util.Dom.removeClass(label_container, 'bz_hidden_field');
|
|
YAHOO.util.Dom.removeClass(field_container, 'bz_hidden_field');
|
|
/* If a custom field such as a textarea field contains some text, then
|
|
* its content is visible by default as a readonly field (assuming that
|
|
* the field is displayed). But if such a custom field contains no text,
|
|
* then it's not displayed at all and an (edit) link is displayed instead.
|
|
* This is problematic if the custom field is mandatory, because at least
|
|
* Firefox complains that you must enter a value, but is unable to point
|
|
* to the custom field because this one is hidden, and so the user has
|
|
* to guess what the web browser is talking about, which is confusing.
|
|
* So in that case, we display the custom field automatically instead of
|
|
* the (edit) link, so that the user can enter some text in it.
|
|
*/
|
|
var field_readonly = document.getElementById(controlled_id + '_readonly');
|
|
|
|
if (!field_readonly) {
|
|
var field_input = document.getElementById(controlled_id + '_input');
|
|
var edit_container =
|
|
document.getElementById(controlled_id + '_edit_container');
|
|
|
|
if (field_input) {
|
|
YAHOO.util.Dom.removeClass(field_input, 'bz_default_hidden');
|
|
}
|
|
if (edit_container) {
|
|
YAHOO.util.Dom.addClass(edit_container, 'bz_hidden_field');
|
|
}
|
|
}
|
|
// Restore the 'required' attribute for mandatory fields.
|
|
if (field.getAttribute('data-required') == "true") {
|
|
field.setAttribute('required', 'true');
|
|
field.setAttribute('aria-required', 'true');
|
|
}
|
|
}
|
|
else {
|
|
YAHOO.util.Dom.addClass(label_container, 'bz_hidden_field');
|
|
YAHOO.util.Dom.addClass(field_container, 'bz_hidden_field');
|
|
// A hidden field must never be required, because the user cannot set it.
|
|
if (field.getAttribute('data-required') == "true") {
|
|
field.removeAttribute('required');
|
|
field.removeAttribute('aria-required');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This is a data structure representing the tree of controlled values.
|
|
* Let's call the "controller value" the "source" and the "controlled
|
|
* value" the "target". A target can have only one source, but a source
|
|
* can have an infinite number of targets.
|
|
*
|
|
* The data structure is a series of hash tables that go something
|
|
* like this:
|
|
*
|
|
* source_field -> target_field -> source_value_id -> target_value_ids
|
|
*
|
|
* We always know source_field when our event handler is called, since
|
|
* that's the field the event is being triggered on. We can then enumerate
|
|
* through every target field, check the status of each source field value,
|
|
* and act appropriately on each target value.
|
|
*/
|
|
var bz_value_controllers = {};
|
|
// This keeps track of whether or not we've added an onchange handler
|
|
// for the source field yet.
|
|
var bz_value_controller_has_handler = {};
|
|
function showValueWhen(target_field_id, target_value_ids,
|
|
source_field_id, source_value_id, empty_shows_all)
|
|
{
|
|
if (!bz_value_controllers[source_field_id]) {
|
|
bz_value_controllers[source_field_id] = {};
|
|
}
|
|
if (!bz_value_controllers[source_field_id][target_field_id]) {
|
|
bz_value_controllers[source_field_id][target_field_id] = {};
|
|
}
|
|
var source_values = bz_value_controllers[source_field_id][target_field_id];
|
|
source_values[source_value_id] = target_value_ids;
|
|
|
|
if (!bz_value_controller_has_handler[source_field_id]) {
|
|
var source_field = document.getElementById(source_field_id);
|
|
YAHOO.util.Event.addListener(source_field, 'change',
|
|
handleValControllerChange, [source_field, empty_shows_all]);
|
|
bz_value_controller_has_handler[source_field_id] = true;
|
|
}
|
|
}
|
|
|
|
function handleValControllerChange(e, args) {
|
|
var source = args[0];
|
|
var empty_shows_all = args[1];
|
|
|
|
for (var target_field_id in bz_value_controllers[source.id]) {
|
|
var target = document.getElementById(target_field_id);
|
|
if (!target) continue;
|
|
_update_displayed_values(source, target, empty_shows_all);
|
|
}
|
|
}
|
|
|
|
/* See the docs for bz_option_duplicate count lower down for an explanation
|
|
* of this data structure.
|
|
*/
|
|
var bz_option_hide_count = {};
|
|
|
|
function _update_displayed_values(source, target, empty_shows_all) {
|
|
var show_all = (empty_shows_all && source.selectedIndex == -1);
|
|
|
|
bz_option_hide_count[target.id] = {};
|
|
|
|
var source_values = bz_value_controllers[source.id][target.id];
|
|
for (source_value_id in source_values) {
|
|
var source_option = getPossiblyHiddenOption(source, source_value_id);
|
|
var target_values = source_values[source_value_id];
|
|
for (var i = 0; i < target_values.length; i++) {
|
|
var target_value_id = target_values[i];
|
|
_handle_source_target(source_option, target, target_value_id,
|
|
show_all);
|
|
}
|
|
}
|
|
|
|
// We may have updated which elements are selected or not selected
|
|
// in the target field, and it may have handlers associated with
|
|
// that, so we need to fire the change event on the target.
|
|
bz_fireEvent(target, 'change');
|
|
}
|
|
|
|
function _handle_source_target(source_option, target, target_value_id,
|
|
show_all)
|
|
{
|
|
var target_option = getPossiblyHiddenOption(target, target_value_id);
|
|
|
|
// We always call either _show_option or _hide_option on every single
|
|
// target value. Although this is not theoretically the most efficient
|
|
// thing we can do, it handles all possible edge cases, and there are
|
|
// a lot of those, particularly when this code is being used on the
|
|
// search form.
|
|
if (source_option.selected || (show_all && !source_option.disabled)) {
|
|
_show_option(target_option, target);
|
|
}
|
|
else {
|
|
_hide_option(target_option, target);
|
|
}
|
|
}
|
|
|
|
/* When an option has duplicates (see the docs for bz_option_duplicates
|
|
* lower down in this file), we only want to hide it if *all* the duplicates
|
|
* would be hidden. So we keep a counter of how many duplicates each option
|
|
* has. Then, when we run through a "change" call for a source field,
|
|
* we count how many times each value gets hidden, and only actually
|
|
* hide it if the counter hits a number higher than the duplicate count.
|
|
*/
|
|
var bz_option_duplicate_count = {};
|
|
|
|
function _show_option(option, field) {
|
|
if (!option.disabled) return;
|
|
option = showOptionInIE(option, field);
|
|
YAHOO.util.Dom.removeClass(option, 'bz_hidden_option');
|
|
option.disabled = false;
|
|
}
|
|
|
|
function _hide_option(option, field) {
|
|
if (option.disabled) return;
|
|
|
|
var value_id = option.bz_value_id;
|
|
|
|
if (field.id in bz_option_duplicate_count
|
|
&& value_id in bz_option_duplicate_count[field.id])
|
|
{
|
|
if (!bz_option_hide_count[field.id][value_id]) {
|
|
bz_option_hide_count[field.id][value_id] = 0;
|
|
}
|
|
bz_option_hide_count[field.id][value_id]++;
|
|
var current = bz_option_hide_count[field.id][value_id];
|
|
var dups = bz_option_duplicate_count[field.id][value_id];
|
|
// We check <= because the value in bz_option_duplicate_count is
|
|
// 1 less than the total number of duplicates (since the shown
|
|
// option is also a "duplicate" but not counted in
|
|
// bz_option_duplicate_count).
|
|
if (current <= dups) return;
|
|
}
|
|
|
|
YAHOO.util.Dom.addClass(option, 'bz_hidden_option');
|
|
option.selected = false;
|
|
option.disabled = true;
|
|
hideOptionInIE(option, field);
|
|
}
|
|
|
|
// A convenience function to generate the "id" tag of an <option>
|
|
// based on the numeric id that Bugzilla uses for that value.
|
|
function _value_id(field_name, id) {
|
|
return 'v' + id + '_' + field_name;
|
|
}
|
|
|
|
/*********************************/
|
|
/* Code for Hiding Options in IE */
|
|
/*********************************/
|
|
|
|
/* IE 7 and below (and some other browsers) don't respond to "display: none"
|
|
* on <option> tags. However, you *can* insert a Comment Node as a
|
|
* child of a <select> tag. So we just insert a Comment where the <option>
|
|
* used to be. */
|
|
var ie_hidden_options = {};
|
|
function hideOptionInIE(anOption, aSelect) {
|
|
if (browserCanHideOptions(aSelect)) return;
|
|
|
|
var commentNode = document.createComment(anOption.value);
|
|
commentNode.id = anOption.id;
|
|
// This keeps the interface of Comments and Options the same for
|
|
// our other functions.
|
|
commentNode.disabled = true;
|
|
// replaceChild is very slow on IE in a <select> that has a lot of
|
|
// options, so we use replaceNode when we can.
|
|
if (anOption.replaceNode) {
|
|
anOption.replaceNode(commentNode);
|
|
}
|
|
else {
|
|
aSelect.replaceChild(commentNode, anOption);
|
|
}
|
|
|
|
// Store the comment node for quick access for getPossiblyHiddenOption
|
|
if (!ie_hidden_options[aSelect.id]) {
|
|
ie_hidden_options[aSelect.id] = {};
|
|
}
|
|
ie_hidden_options[aSelect.id][anOption.id] = commentNode;
|
|
}
|
|
|
|
function showOptionInIE(aNode, aSelect) {
|
|
if (browserCanHideOptions(aSelect)) return aNode;
|
|
|
|
// We do this crazy thing with innerHTML and createElement because
|
|
// this is the ONLY WAY that this works properly in IE.
|
|
var optionNode = document.createElement('option');
|
|
optionNode.innerHTML = aNode.data;
|
|
optionNode.value = aNode.data;
|
|
optionNode.id = aNode.id;
|
|
// replaceChild is very slow on IE in a <select> that has a lot of
|
|
// options, so we use replaceNode when we can.
|
|
if (aNode.replaceNode) {
|
|
aNode.replaceNode(optionNode);
|
|
}
|
|
else {
|
|
aSelect.replaceChild(optionNode, aNode);
|
|
}
|
|
delete ie_hidden_options[aSelect.id][optionNode.id];
|
|
return optionNode;
|
|
}
|
|
|
|
function initHidingOptionsForIE(select_name) {
|
|
var aSelect = document.getElementById(select_name);
|
|
if (browserCanHideOptions(aSelect)) return;
|
|
if (!aSelect) return;
|
|
|
|
for (var i = 0; ;i++) {
|
|
var item = aSelect.options[i];
|
|
if (!item) break;
|
|
if (item.disabled) {
|
|
hideOptionInIE(item, aSelect);
|
|
i--; // Hiding an option means that the options array has changed.
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Certain fields, like the Component field, have duplicate values in
|
|
* them (the same name, but different ids). We don't display these
|
|
* duplicate values in the UI, but the option hiding/showing code still
|
|
* uses the ids of these unshown duplicates. So, whenever we get the
|
|
* id of an unshown duplicate in getPossiblyHiddenOption, we have to
|
|
* return the actually-used <option> instead.
|
|
*
|
|
* The structure of the data looks like:
|
|
*
|
|
* field_name -> unshown_value_id -> shown_value_id_it_is_a_duplicate_of
|
|
*/
|
|
var bz_option_duplicates = {};
|
|
|
|
function getPossiblyHiddenOption(aSelect, optionId) {
|
|
|
|
if (bz_option_duplicates[aSelect.id]
|
|
&& bz_option_duplicates[aSelect.id][optionId])
|
|
{
|
|
optionId = bz_option_duplicates[aSelect.id][optionId];
|
|
}
|
|
|
|
// Works always for <option> tags, and works for commentNodes
|
|
// in IE (but not in Webkit).
|
|
var id = _value_id(aSelect.id, optionId);
|
|
var val = document.getElementById(id);
|
|
|
|
// This is for WebKit and other browsers that can't "display: none"
|
|
// an <option> and also can't getElementById for a commentNode.
|
|
if (!val && ie_hidden_options[aSelect.id]) {
|
|
val = ie_hidden_options[aSelect.id][id];
|
|
}
|
|
|
|
// We add this property for our own convenience, it's used in
|
|
// other places.
|
|
val.bz_value_id = optionId;
|
|
|
|
return val;
|
|
}
|
|
|
|
var browser_can_hide_options;
|
|
function browserCanHideOptions(aSelect) {
|
|
/* As far as I can tell, browsers that don't hide <option> tags
|
|
* also never have a X position for <option> tags, even if
|
|
* they're visible. This is the only reliable way I found to
|
|
* differentiate browsers. So we create a visible option, see
|
|
* if it has a position, and then remove it. */
|
|
if (typeof(browser_can_hide_options) == "undefined") {
|
|
var new_opt = bz_createOptionInSelect(aSelect, '', '');
|
|
var opt_pos = YAHOO.util.Dom.getX(new_opt);
|
|
aSelect.removeChild(new_opt);
|
|
if (opt_pos) {
|
|
browser_can_hide_options = true;
|
|
}
|
|
else {
|
|
browser_can_hide_options = false;
|
|
}
|
|
}
|
|
return browser_can_hide_options;
|
|
}
|
|
|
|
/* (end) option hiding code */
|
|
|
|
/**
|
|
* The Autoselect
|
|
*/
|
|
YAHOO.bugzilla.userAutocomplete = {
|
|
counter : 0,
|
|
dataSource : null,
|
|
generateRequest : function ( enteredText ){
|
|
YAHOO.bugzilla.userAutocomplete.counter =
|
|
YAHOO.bugzilla.userAutocomplete.counter + 1;
|
|
YAHOO.util.Connect.setDefaultPostHeader('application/json', true);
|
|
var json_object = {
|
|
method : "User.get",
|
|
id : YAHOO.bugzilla.userAutocomplete.counter,
|
|
params : [ {
|
|
Bugzilla_api_token: BUGZILLA.api_token,
|
|
match : [ decodeURIComponent(enteredText) ],
|
|
include_fields : [ "name", "real_name" ]
|
|
} ]
|
|
};
|
|
var stringified = YAHOO.lang.JSON.stringify(json_object);
|
|
var debug = { msg: "json-rpc obj debug info", "json obj": json_object,
|
|
"param" : stringified}
|
|
YAHOO.bugzilla.userAutocomplete.debug_helper( debug );
|
|
return stringified;
|
|
},
|
|
resultListFormat : function(oResultData, enteredText, sResultMatch) {
|
|
return ( YAHOO.lang.escapeHTML(oResultData.real_name) + " ("
|
|
+ YAHOO.lang.escapeHTML(oResultData.name) + ")");
|
|
},
|
|
debug_helper : function ( ){
|
|
/* used to help debug any errors that might happen */
|
|
if( typeof(console) !== 'undefined' && console != null && arguments.length > 0 ){
|
|
console.log("debug helper info:", arguments);
|
|
}
|
|
return true;
|
|
},
|
|
init_ds : function(){
|
|
this.dataSource = new YAHOO.util.XHRDataSource("jsonrpc.cgi");
|
|
this.dataSource.connTimeout = 30000;
|
|
this.dataSource.connMethodPost = true;
|
|
this.dataSource.connXhrMode = "cancelStaleRequests";
|
|
this.dataSource.maxCacheEntries = 5;
|
|
this.dataSource.responseSchema = {
|
|
resultsList : "result.users",
|
|
metaFields : { error: "error", jsonRpcId: "id"},
|
|
fields : [
|
|
{ key : "name" },
|
|
{ key : "real_name"}
|
|
]
|
|
};
|
|
},
|
|
init : function( field, container, multiple ) {
|
|
if( this.dataSource == null ){
|
|
this.init_ds();
|
|
}
|
|
var userAutoComp = new YAHOO.widget.AutoComplete( field, container,
|
|
this.dataSource );
|
|
// other stuff we might want to do with the autocomplete goes here
|
|
userAutoComp.maxResultsDisplayed = BUGZILLA.param.maxusermatches;
|
|
userAutoComp.generateRequest = this.generateRequest;
|
|
userAutoComp.formatResult = this.resultListFormat;
|
|
userAutoComp.doBeforeLoadData = this.debug_helper;
|
|
userAutoComp.minQueryLength = 3;
|
|
userAutoComp.autoHighlight = false;
|
|
// this is a throttle to determine the delay of the query from typing
|
|
// set this higher to cause fewer calls to the server
|
|
userAutoComp.queryDelay = 0.05;
|
|
userAutoComp.useIFrame = true;
|
|
userAutoComp.resultTypeList = false;
|
|
if( multiple == true ){
|
|
userAutoComp.delimChar = [","];
|
|
}
|
|
|
|
}
|
|
};
|
|
|
|
YAHOO.bugzilla.fieldAutocomplete = {
|
|
dataSource : [],
|
|
init_ds : function( field ) {
|
|
this.dataSource[field] =
|
|
new YAHOO.util.LocalDataSource( YAHOO.bugzilla.field_array[field] );
|
|
},
|
|
init : function( field, container ) {
|
|
if( this.dataSource[field] == null ) {
|
|
this.init_ds( field );
|
|
}
|
|
var fieldAutoComp =
|
|
new YAHOO.widget.AutoComplete(field, container, this.dataSource[field]);
|
|
fieldAutoComp.maxResultsDisplayed = YAHOO.bugzilla.field_array[field].length;
|
|
fieldAutoComp.formatResult = fieldAutoComp.formatEscapedResult;
|
|
fieldAutoComp.minQueryLength = 0;
|
|
fieldAutoComp.useIFrame = true;
|
|
fieldAutoComp.delimChar = [","," "];
|
|
fieldAutoComp.resultTypeList = false;
|
|
fieldAutoComp.queryDelay = 0;
|
|
/* Causes all the possibilities in the field to appear when a user
|
|
* focuses on the textbox
|
|
*/
|
|
fieldAutoComp.textboxFocusEvent.subscribe( function(){
|
|
var sInputValue = YAHOO.util.Dom.get(field).value;
|
|
if( sInputValue.length === 0
|
|
&& YAHOO.bugzilla.field_array[field].length > 0 ){
|
|
this.sendQuery(sInputValue);
|
|
this.collapseContainer();
|
|
this.expandContainer();
|
|
}
|
|
});
|
|
fieldAutoComp.dataRequestEvent.subscribe( function(type, args) {
|
|
args[0].autoHighlight = args[1] != '';
|
|
});
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Set the disable email checkbox to true if the user has disabled text
|
|
*/
|
|
function userDisabledTextOnChange(disabledtext) {
|
|
var disable_mail = document.getElementById('disable_mail');
|
|
if (disabledtext.value === "" && !disable_mail_manually_set) {
|
|
disable_mail.checked = false;
|
|
}
|
|
if (disabledtext.value !== "" && !disable_mail_manually_set) {
|
|
disable_mail.checked = true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Force the browser to honour the selected option when a page is refreshed,
|
|
* but only if the user hasn't explicitly selected a different option.
|
|
*/
|
|
function initDirtyFieldTracking() {
|
|
// old IE versions don't provide the information we need to make this fix work
|
|
// however they aren't affected by this issue, so it's ok to ignore them
|
|
if (YAHOO.env.ua.ie > 0 && YAHOO.env.ua.ie <= 8) return;
|
|
var selects = document.getElementById('changeform').getElementsByTagName('select');
|
|
for (var i = 0, l = selects.length; i < l; i++) {
|
|
var el = selects[i];
|
|
var el_dirty = document.getElementById(el.name + '_dirty');
|
|
if (!el_dirty) continue;
|
|
if (!el_dirty.value) {
|
|
var preSelected = bz_preselectedOptions(el);
|
|
if (!el.multiple) {
|
|
preSelected.selected = true;
|
|
} else {
|
|
el.selectedIndex = -1;
|
|
for (var j = 0, m = preSelected.length; j < m; j++) {
|
|
preSelected[j].selected = true;
|
|
}
|
|
}
|
|
}
|
|
YAHOO.util.Event.on(el, "change", function(e) {
|
|
var el = e.target || e.srcElement;
|
|
var preSelected = bz_preselectedOptions(el);
|
|
var currentSelected = bz_selectedOptions(el);
|
|
var isDirty = false;
|
|
if (!el.multiple) {
|
|
isDirty = preSelected.index != currentSelected.index;
|
|
} else {
|
|
if (preSelected.length != currentSelected.length) {
|
|
isDirty = true;
|
|
} else {
|
|
for (var i = 0, l = preSelected.length; i < l; i++) {
|
|
if (currentSelected[i].index != preSelected[i].index) {
|
|
isDirty = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
document.getElementById(el.name + '_dirty').value = isDirty ? '1' : '';
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Comment preview
|
|
*/
|
|
|
|
var last_comment_text = '';
|
|
|
|
function show_comment_preview(bug_id) {
|
|
var Dom = YAHOO.util.Dom;
|
|
var comment = document.getElementById('comment');
|
|
var preview = document.getElementById('comment_preview');
|
|
|
|
if (!comment || !preview) return;
|
|
if (Dom.hasClass('comment_preview_tab', 'active_comment_tab')) return;
|
|
|
|
preview.style.width = (comment.clientWidth - 4) + 'px';
|
|
preview.style.height = comment.offsetHeight + 'px';
|
|
|
|
var comment_tab = document.getElementById('comment_tab');
|
|
Dom.addClass(comment, 'bz_default_hidden');
|
|
Dom.removeClass(comment_tab, 'active_comment_tab');
|
|
comment_tab.setAttribute('aria-selected', 'false');
|
|
|
|
var preview_tab = document.getElementById('comment_preview_tab');
|
|
Dom.removeClass(preview, 'bz_default_hidden');
|
|
Dom.addClass(preview_tab, 'active_comment_tab');
|
|
preview_tab.setAttribute('aria-selected', 'true');
|
|
|
|
Dom.addClass('comment_preview_error', 'bz_default_hidden');
|
|
|
|
if (last_comment_text == comment.value)
|
|
return;
|
|
|
|
Dom.addClass('comment_preview_text', 'bz_default_hidden');
|
|
Dom.removeClass('comment_preview_loading', 'bz_default_hidden');
|
|
|
|
YAHOO.util.Connect.setDefaultPostHeader('application/json', true);
|
|
YAHOO.util.Connect.asyncRequest('POST', 'jsonrpc.cgi',
|
|
{
|
|
success: function(res) {
|
|
data = YAHOO.lang.JSON.parse(res.responseText);
|
|
if (data.error) {
|
|
Dom.addClass('comment_preview_loading', 'bz_default_hidden');
|
|
Dom.removeClass('comment_preview_error', 'bz_default_hidden');
|
|
Dom.get('comment_preview_error').innerHTML =
|
|
YAHOO.lang.escapeHTML(data.error.message);
|
|
} else {
|
|
document.getElementById('comment_preview_text').innerHTML = data.result.html;
|
|
Dom.addClass('comment_preview_loading', 'bz_default_hidden');
|
|
Dom.removeClass('comment_preview_text', 'bz_default_hidden');
|
|
last_comment_text = comment.value;
|
|
}
|
|
},
|
|
failure: function(res) {
|
|
Dom.addClass('comment_preview_loading', 'bz_default_hidden');
|
|
Dom.removeClass('comment_preview_error', 'bz_default_hidden');
|
|
Dom.get('comment_preview_error').innerHTML =
|
|
YAHOO.lang.escapeHTML(res.responseText);
|
|
}
|
|
},
|
|
YAHOO.lang.JSON.stringify({
|
|
version: "1.1",
|
|
method: 'Bug.render_comment',
|
|
params: {
|
|
Bugzilla_api_token: BUGZILLA.api_token,
|
|
id: bug_id,
|
|
text: comment.value
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
function show_comment_edit() {
|
|
var comment = document.getElementById('comment');
|
|
var preview = document.getElementById('comment_preview');
|
|
if (!comment || !preview) return;
|
|
if (YAHOO.util.Dom.hasClass(comment, 'active_comment_tab')) return;
|
|
|
|
var preview_tab = document.getElementById('comment_preview_tab');
|
|
YAHOO.util.Dom.addClass(preview, 'bz_default_hidden');
|
|
YAHOO.util.Dom.removeClass(preview_tab, 'active_comment_tab');
|
|
preview_tab.setAttribute('aria-selected', 'false');
|
|
|
|
var comment_tab = document.getElementById('comment_tab');
|
|
YAHOO.util.Dom.removeClass(comment, 'bz_default_hidden');
|
|
YAHOO.util.Dom.addClass(comment_tab, 'active_comment_tab');
|
|
comment_tab.setAttribute('aria-selected', 'true');
|
|
}
|
|
|
|
function adjustRemainingTime() {
|
|
// subtracts time spent from remaining time
|
|
// prevent negative values if work_time > bz_remaining_time
|
|
var new_time = Math.max(bz_remaining_time - document.changeform.work_time.value, 0.0);
|
|
// get upto 2 decimal places
|
|
document.changeform.remaining_time.value = Math.round(new_time * 100)/100;
|
|
}
|
|
|
|
function updateRemainingTime() {
|
|
// if the remaining time is changed manually, update bz_remaining_time
|
|
bz_remaining_time = document.changeform.remaining_time.value;
|
|
}
|