Commit 6a23b833 authored by mkanat%bugzilla.org's avatar mkanat%bugzilla.org

Bug 463598: Improve the performance of the JavaScript that adjusts field values…

Bug 463598: Improve the performance of the JavaScript that adjusts field values based on the value of another field Patch by Max Kanat-Alexander <mkanat@bugzilla.org> r=wicked, a=mkanat
parent 4c3c583a
...@@ -199,7 +199,7 @@ sub _check_if_controller { ...@@ -199,7 +199,7 @@ sub _check_if_controller {
my $self = shift; my $self = shift;
my $vis_fields = $self->controls_visibility_of_fields; my $vis_fields = $self->controls_visibility_of_fields;
my $values = $self->controlled_values; my $values = $self->controlled_values;
if (@$vis_fields || @$values) { if (@$vis_fields || scalar(keys %$values)) {
ThrowUserError('fieldvalue_is_controller', ThrowUserError('fieldvalue_is_controller',
{ value => $self, fields => [map($_->name, @$vis_fields)], { value => $self, fields => [map($_->name, @$vis_fields)],
vals => $values }); vals => $values });
...@@ -287,13 +287,13 @@ sub controlled_values { ...@@ -287,13 +287,13 @@ sub controlled_values {
my $self = shift; my $self = shift;
return $self->{controlled_values} if defined $self->{controlled_values}; return $self->{controlled_values} if defined $self->{controlled_values};
my $fields = $self->field->controls_values_of; my $fields = $self->field->controls_values_of;
my @controlled_values; my %controlled_values;
foreach my $field (@$fields) { foreach my $field (@$fields) {
my $controlled = Bugzilla::Field::Choice->type($field) $controlled_values{$field->name} =
->match({ visibility_value_id => $self->id }); Bugzilla::Field::Choice->type($field)
push(@controlled_values, @$controlled); ->match({ visibility_value_id => $self->id });
} }
$self->{controlled_values} = \@controlled_values; $self->{controlled_values} = \%controlled_values;
return $self->{controlled_values}; return $self->{controlled_values};
} }
...@@ -431,4 +431,14 @@ The key that determines the sort order of this item. ...@@ -431,4 +431,14 @@ The key that determines the sort order of this item.
The L<Bugzilla::Field> object that this field value belongs to. The L<Bugzilla::Field> object that this field value belongs to.
=item C<controlled_values>
Tells you which values in B<other> fields appear (become visible) when this
value is set in its field.
Returns a hashref of arrayrefs. The hash keys are the names of fields,
and the values are arrays of C<Bugzilla::Field::Choice> objects,
representing values that this value controls the visibility of, for
that field.
=back =back
...@@ -389,40 +389,52 @@ function handleVisControllerValueChange(e, args) { ...@@ -389,40 +389,52 @@ function handleVisControllerValueChange(e, args) {
} }
} }
function showValueWhen(controlled_field_id, controlled_value, function showValueWhen(controlled_field_id, controlled_value_ids,
controller_field_id, controller_value) controller_field_id, controller_value_id)
{ {
var controller_field = document.getElementById(controller_field_id); var controller_field = document.getElementById(controller_field_id);
// Note that we don't get an object for the controlled field here, // Note that we don't get an object for the controlled field here,
// because it might not yet exist in the DOM. We just pass along its id. // because it might not yet exist in the DOM. We just pass along its id.
YAHOO.util.Event.addListener(controller_field, 'change', YAHOO.util.Event.addListener(controller_field, 'change',
handleValControllerChange, [controlled_field_id, controlled_value, handleValControllerChange, [controlled_field_id, controlled_value_ids,
controller_field, controller_value]); controller_field, controller_value_id]);
} }
function handleValControllerChange(e, args) { function handleValControllerChange(e, args) {
var controlled_field = document.getElementById(args[0]); var controlled_field = document.getElementById(args[0]);
var controlled_value = args[1]; var controlled_value_ids = args[1];
var controller_field = args[2]; var controller_field = args[2];
var controller_value = args[3]; var controller_value_id = args[3];
var item = getPossiblyHiddenOption(controlled_field, controlled_value); var controller_item = document.getElementById(
if (bz_valueSelected(controller_field, controller_value)) { _value_id(controller_field.id, controller_value_id));
showOptionInIE(item, controlled_field);
YAHOO.util.Dom.removeClass(item, 'bz_hidden_option'); for (var i = 0; i < controlled_value_ids.length; i++) {
item.disabled = false; var item = getPossiblyHiddenOption(controlled_field,
} controlled_value_ids[i]);
else if (!item.disabled) { if (item.disabled && controller_item && controller_item.selected) {
YAHOO.util.Dom.addClass(item, 'bz_hidden_option'); item = showOptionInIE(item, controlled_field);
if (item.selected) { YAHOO.util.Dom.removeClass(item, 'bz_hidden_option');
item.selected = false; item.disabled = false;
bz_fireEvent(controlled_field, 'change'); }
else if (!item.disabled) {
YAHOO.util.Dom.addClass(item, 'bz_hidden_option');
if (item.selected) {
item.selected = false;
bz_fireEvent(controlled_field, 'change');
}
item.disabled = true;
hideOptionInIE(item, controlled_field);
} }
item.disabled = true;
hideOptionInIE(item, controlled_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 */ /* Code for Hiding Options in IE */
/*********************************/ /*********************************/
...@@ -431,24 +443,50 @@ function handleValControllerChange(e, args) { ...@@ -431,24 +443,50 @@ function handleValControllerChange(e, args) {
* on <option> tags. However, you *can* insert a Comment Node as a * 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> * child of a <select> tag. So we just insert a Comment where the <option>
* used to be. */ * used to be. */
var ie_hidden_options = new Array();
function hideOptionInIE(anOption, aSelect) { function hideOptionInIE(anOption, aSelect) {
if (browserCanHideOptions(aSelect)) return; if (browserCanHideOptions(aSelect)) return;
var commentNode = document.createComment(anOption.value); var commentNode = document.createComment(anOption.value);
aSelect.replaceChild(commentNode, anOption); 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] = new Array();
}
ie_hidden_options[aSelect.id][anOption.id] = commentNode;
} }
function showOptionInIE(aNode, aSelect) { function showOptionInIE(aNode, aSelect) {
if (browserCanHideOptions(aSelect)) return; if (browserCanHideOptions(aSelect)) return aNode;
// If aNode is an Option
if (typeof(aNode.value) != 'undefined') return;
// We do this crazy thing with innerHTML and createElement because // We do this crazy thing with innerHTML and createElement because
// this is the ONLY WAY that this works properly in IE. // this is the ONLY WAY that this works properly in IE.
var optionNode = document.createElement('option'); var optionNode = document.createElement('option');
optionNode.innerHTML = aNode.data; optionNode.innerHTML = aNode.data;
optionNode.value = aNode.data; optionNode.value = aNode.data;
var old_node = aSelect.replaceChild(optionNode, aNode); 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) { function initHidingOptionsForIE(select_name) {
...@@ -465,26 +503,19 @@ function initHidingOptionsForIE(select_name) { ...@@ -465,26 +503,19 @@ function initHidingOptionsForIE(select_name) {
} }
} }
function getPossiblyHiddenOption(aSelect, aValue) { function getPossiblyHiddenOption(aSelect, optionId) {
var val_index = bz_optionIndex(aSelect, aValue); // Works always for <option> tags, and works for commentNodes
// in IE (but not in Webkit).
/* We have to go fishing for one of our comment nodes if we var id = _value_id(aSelect.id, optionId);
* don't find the <option>. */ var val = document.getElementById(id);
if (val_index < 0 && !browserCanHideOptions(aSelect)) {
var children = aSelect.childNodes; // This is for WebKit and other browsers that can't "display: none"
for (var i = 0; i < children.length; i++) { // an <option> and also can't getElementById for a commentNode.
var item = children[i]; if (!val && ie_hidden_options[aSelect.id]) {
if (item.data == aValue) { val = ie_hidden_options[aSelect.id][id];
// Set this for handleValControllerChange, so that both options
// and commentNodes have this.
children[i].disabled = true;
return children[i];
}
}
} }
/* Otherwise we just return the Option we found. */ return val;
return aSelect.options[val_index];
} }
var browser_can_hide_options; var browser_can_hide_options;
......
...@@ -319,7 +319,7 @@ div#docslinks { ...@@ -319,7 +319,7 @@ div#docslinks {
/** End Comments **/ /** End Comments **/
.bz_default_hidden, .bz_tui_hidden { .bz_default_hidden, .bz_tui_hidden, .bz_hidden_field, .bz_hidden_option {
/* We have !important because we want elements with these classes to always /* We have !important because we want elements with these classes to always
* be hidden, even if there is some CSS that overrides it (we use these * be hidden, even if there is some CSS that overrides it (we use these
* classes inside JavaScript to hide things). */ * classes inside JavaScript to hide things). */
...@@ -456,13 +456,6 @@ div.user_match { ...@@ -456,13 +456,6 @@ div.user_match {
vertical-align: top; vertical-align: top;
} }
.bz_hidden_field, .bz_hidden_option {
display: none;
}
.bz_hidden_option {
visibility: hidden;
}
.calendar_button { .calendar_button {
background: transparent url("global/calendar.png") no-repeat; background: transparent url("global/calendar.png") no-repeat;
width: 20px; width: 20px;
......
...@@ -126,13 +126,15 @@ ...@@ -126,13 +126,15 @@
[% IF value.controlled_values.size %] [% IF value.controlled_values.size %]
<li>This value controls the visibility of the following values in <li>This value controls the visibility of the following values in
other fields:<br> other fields:<br>
[% FOREACH controlled = value.controlled_values %] [% FOREACH field_name = value.controlled_values.keys %]
<a href="editvalues.cgi?action=edit&field= [% FOREACH controlled = value.controlled_values.${field_name} %]
[%- controlled.field.name FILTER url_quote %]&value= <a href="editvalues.cgi?action=edit&field=
[%- controlled.name FILTER url_quote %]"> [%- controlled.field.name FILTER url_quote %]&value=
[% controlled.field.description FILTER html %] [%- controlled.name FILTER url_quote %]">
([% controlled.field.name FILTER html %]): [% controlled.field.description FILTER html %]
[%+ controlled.name FILTER html %]</a><br> ([% controlled.field.name FILTER html %]):
[%+ controlled.name FILTER html %]</a><br>
[% END %]
[% END %] [% END %]
</li> </li>
[% END %] [% END %]
......
...@@ -27,10 +27,14 @@ ...@@ -27,10 +27,14 @@
'[% controlled_field.visibility_value.name FILTER js %]'); '[% controlled_field.visibility_value.name FILTER js %]');
[% END %] [% END %]
[% FOREACH legal_value = field.legal_values %] [% FOREACH legal_value = field.legal_values %]
[% FOREACH controlled_value = legal_value.controlled_values %] [% FOREACH controlled_field = legal_value.controlled_values.keys %]
showValueWhen('[% controlled_value.field.name FILTER js %]', [% SET cont_ids = [] %]
'[% controlled_value.name FILTER js %]', [% FOREACH val = legal_value.controlled_values.$controlled_field %]
[% cont_ids.push(val.id) %]
[% END %]
showValueWhen('[% controlled_field FILTER js %]',
[[% cont_ids.join(',') FILTER js %]],
'[% field.name FILTER js %]', '[% field.name FILTER js %]',
'[% legal_value.name FILTER js %]'); [% legal_value.id FILTER js %]);
[% END %] [% END %]
[% END %] [% END %]
...@@ -134,6 +134,8 @@ ...@@ -134,6 +134,8 @@
[% SET control_value = legal_value.visibility_value %] [% SET control_value = legal_value.visibility_value %]
[% SET control_field = field.value_field %] [% SET control_field = field.value_field %]
<option value="[% legal_value.name FILTER html %]" <option value="[% legal_value.name FILTER html %]"
id="v[% legal_value.id FILTER html %]_
[%- field.name FILTER html %]"
[%# We always show selected values, even if they should be [%# We always show selected values, even if they should be
# hidden %] # hidden %]
[% IF value.contains(legal_value.name).size %] [% IF value.contains(legal_value.name).size %]
...@@ -144,8 +146,7 @@ ...@@ -144,8 +146,7 @@
%] %]
class="bz_hidden_option" disabled="disabled" class="bz_hidden_option" disabled="disabled"
[% END %]> [% END %]>
[%- legal_value.name FILTER html -%] [%- legal_value.name FILTER html %]</option>
</option>
[% END %] [% END %]
</select> </select>
[%# When you pass an empty multi-select in the web interface, [%# When you pass an empty multi-select in the web interface,
......
...@@ -490,9 +490,11 @@ ...@@ -490,9 +490,11 @@
[% IF vals.size %] [% IF vals.size %]
it controls the visibility of the following field values: it controls the visibility of the following field values:
<ul> <ul>
[% FOREACH val = vals %] [% FOREACH field_name = vals.keys %]
<li>[% val.field.name FILTER html %]: [% FOREACH val = vals.${field_name} %]
'[% val.name FILTER html %]'</li> <li>[% val.field.name FILTER html %]:
'[% val.name FILTER html %]'</li>
[% END %]
[% END %] [% END %]
</ul> </ul>
[% END %] [% END %]
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment