diff --git a/assets/javascripts/reporting/filters.js b/assets/javascripts/reporting/filters.js index 64cf73e423..71ce527cbb 100644 --- a/assets/javascripts/reporting/filters.js +++ b/assets/javascripts/reporting/filters.js @@ -5,6 +5,12 @@ Reporting.Filters = { load_available_values_for_filter: function (filter_name, callback_func) { var select, radio_options, post_select_values; select = $$('.filter-value[data-filter-name="' + filter_name + '"]').first(); + // check if we might have a radio-box + radio_options = $$('.' + filter_name + '_radio_option input'); + if (radio_options && radio_options.size() !== 0) { + radio_options.first().checked = true; + callback_func(); + } if (select === null || select === undefined) { return; } @@ -30,7 +36,6 @@ Reporting.Filters = { }, onComplete: function (a, b) { $$("select[data-filter-name='" + filter_name + "']").each(function (e) { e.enable(); }); - callback_func(); if (select.tagName.toLowerCase() === "select") { if (post_select_values === undefined || post_select_values === null || post_select_values.size() === 0) { select.selectedIndex = 0; @@ -38,20 +43,13 @@ Reporting.Filters = { Reporting.Filters.select_values(select, post_select_values); } } + callback_func(); } }); Reporting.Filters.multi_select(select, false); } else { callback_func(); } - // select first option by default - if (select.tagName.toLowerCase() === "div") { - // check if we might have a radio-box - radio_options = $$('.' + filter_name + '_radio_option input'); - if (radio_options && radio_options.size() !== 0) { - radio_options.first().checked = true; - } - } }, show_filter: function (field, options) { @@ -67,6 +65,9 @@ Reporting.Filters = { if (options.show_filter === undefined) { options.show_filter = true; } + if (options.hide_only === undefined) { + options.hide_only = false; + } var field_el = $('tr_' + field); if (field_el !== null) { if (options.insert_after === undefined) { @@ -74,8 +75,10 @@ Reporting.Filters = { } if (options.insert_after !== undefined && options.show_filter) { // Move the filter down to appear after the last currently visible filter - field_el.remove(); - options.insert_after.insert({after: field_el}); + if (field_el.id !== options.insert_after.id) { + field_el.remove(); + options.insert_after.insert({after: field_el}); + } } // the following command might be included into the callback_function (which is called after the ajax request) later var display_functor; @@ -87,7 +90,9 @@ Reporting.Filters = { Reporting.Filters.set_filter_value_widths(100); } else { (options.slowly ? Effect.Fade : Element.hide)(field_el); - field_el.removeAttribute('data-selected'); + if (!options.hide_only) { // remember that this filter used to be selected + field_el.removeAttribute('data-selected'); + } $('rm_' + field).value = ""; // reset the value, so the serialized form will not return this filter Reporting.Filters.set_filter_value_widths(5000); } @@ -96,6 +101,40 @@ Reporting.Filters = { } }, + /** + * Activates the filter with the given name and loads dependent filters if necessary. + * + * @param filter_name Name of the filter to be activated. + */ + add_filter: function (filter_name, activate_dependent, on_complete) { + var field = filter_name + if (activate_dependent === undefined) { + activate_dependent = true; + } + if (on_complete === undefined) { + on_complete = function() { }; + } + Reporting.Filters.show_filter(field, { slowly: true, callback_func: function() { + if (activate_dependent) { + Reporting.Filters.activate_dependents($(field + "_arg_1_val")); + } + Reporting.Filters.select_option_enabled($("add_filter_select"), filter_name, false); + on_complete(); + } + }); + }, + + remove_filter: function (field, hide_only) { + Reporting.Filters.show_filter(field, { show_filter: false, hide_only: hide_only }); + var dependent = Reporting.Filters.get_dependents($(field + '_arg_1_val'), false).find(function(d) { + return Reporting.Filters.visible_filters().include(d); + }); + if (dependent !== undefined) { + Reporting.Filters.remove_filter(dependent); + } + Reporting.Filters.select_option_enabled($("add_filter_select"), field, true); + }, + /* Smoothly sets the width of currently displayed filters. Params: @@ -210,15 +249,6 @@ Reporting.Filters = { } }, - add_filter: function (select) { - var field; - field = select.value; - Reporting.Filters.show_filter(field, { slowly: true }); - select.selectedIndex = 0; - Reporting.Filters.select_option_enabled(select, field, false); - Reporting.Filters.activate_dependents($(field + "_arg_1_val")) - }, - select_option_enabled: function (box, value, state) { var option = box.select("[value='" + value + "']").first(); if (option !== undefined) { @@ -243,17 +273,6 @@ Reporting.Filters = { Reporting.Filters.multi_select(select, !select.multiple); }, - remove_filter: function (field) { - Reporting.Filters.show_filter(field, { show_filter: false }); - var dependent = Reporting.Filters.get_dependents($(field + '_arg_1_val'), false).find(function(d) { - return Reporting.Filters.visible_filters().include(d); - }); - if (dependent !== undefined) { - Reporting.Filters.remove_filter(dependent); - } - Reporting.Filters.select_option_enabled($("add_filter_select"), field, true); - }, - visible_filters: function () { return $("filter_table").select("tr").select(function (tr) { return tr.visible() === true; @@ -289,11 +308,14 @@ Reporting.Filters = { // Param: select [optional] - the select-box of the filter which should activate it's dependents activate_dependents: function (selectBox, callbackWhenFinished) { var all_dependents, next_dependents, dependent, active_filters, source; - if (selectBox === undefined || selectBox.type.toLowerCase() == 'change') { + if (selectBox !== undefined && selectBox.tagName.toLowerCase() !== "select") { + return; // only multi_value filters have dependents + } + if (selectBox === undefined || selectBox.type.toLowerCase() == 'change') { selectBox = this; } if (callbackWhenFinished === undefined) { - callbackWhenFinished = function() {}; + callbackWhenFinished = function(dependent) { }; } source = selectBox.getAttribute("data-filter-name"); all_dependents = Reporting.Filters.get_dependents(selectBox); @@ -326,14 +348,18 @@ Reporting.Filters = { var active_dependents = all_dependents.select(function (d) { return active_filters.include(d); }); - Reporting.Filters.narrow_values(Reporting.Filters.dependent_for(source), active_dependents, callbackWhenFinished); + Reporting.Filters.narrow_values( + Reporting.Filters.dependent_for(source), + active_dependents, + function() { callbackWhenFinished(dependent); } + ); }, 1); }, // return an array of all filters that depend on the given filter plus the given filter dependent_for: function(field) { var deps = $$('.filters-select[data-all-dependents]').findAll(function(selectBox) { - return selectBox.up('tr').visible() && Reporting.Filters.get_dependents(selectBox).include(field) + return (selectBox.up('tr').visible()) && Reporting.Filters.get_dependents(selectBox).include(field) }).map(function(selectBox) { return selectBox.getAttribute("data-filter-name"); }); @@ -400,7 +426,9 @@ Reporting.Filters = { // cannot use .innerhtml due to IE wierdness $(selectBox).insert(new Element('option', {value: value}).update(label.escapeHTML())); }); + Reporting.Filters.select_values(selectBox, selected); + sources.push(currentDependent); // Add as last element dependents.splice(0, 1); // Delete first element // if we got no values besides the <> value, do not show this selectBox @@ -421,10 +449,13 @@ Reporting.Filters = { if (continue_narrowing) { Reporting.Filters.narrow_values(sources, dependents); } + callbackWhenFinished(); } - callbackWhenFinished(); }, onException: function (response, error) { + if (console) { + console.log(error); + } Reporting.flash("Loading of filter values failed. Probably, the server is temporary offline for maintenance."); var selectBox = $(currentDependent + "_arg_1_val"); $(selectBox).insert(new Element('option', {value: '<>'}).update('Failed to load values.')); @@ -445,7 +476,8 @@ Reporting.onload(function () { if ($("add_filter_select")) { $("add_filter_select").observe("change", function () { if (!(Reporting.Filters.exists(this.value))) { - Reporting.Filters.add_filter(this); + Reporting.Filters.add_filter(this.value); + this.selectedIndex = 0; }; }); } @@ -474,15 +506,12 @@ Reporting.onload(function () { }).size(); s.multiple = (selected_size > 1); s.observe("change", function (evt) { - var filter_name = this.up('tr').getAttribute("data-filter-name"); - Reporting.Filters.value_changed(filter_name); + var filter_name = this.up('tr').getAttribute("data-filter-name"); + Reporting.Filters.value_changed(filter_name); }); }); $$('.filters-select[data-all-dependents]').each(function (dependency) { dependency.observe("change", Reporting.Filters.activate_dependents); }); - Reporting.Filters.visible_filters().each(function (filter) { - Reporting.Filters.load_available_values_for_filter(filter, function () {}); - }); }); diff --git a/assets/javascripts/reporting/restore_query.js b/assets/javascripts/reporting/restore_query.js index b3a742ac9e..e3a4b5d261 100644 --- a/assets/javascripts/reporting/restore_query.js +++ b/assets/javascripts/reporting/restore_query.js @@ -27,58 +27,44 @@ Reporting.RestoreQuery = { } }, - // This is called the first time the report loads. - // Params: - // elements: Array of visible filter-select-boxes that have dependents - // (and possibly are dependents themselfes) - initialize_load_dependent_filters: function(elements) { - var filters_to_load, dependent_filters; - dependent_filters = elements.findAll(function (select) { return select.getValue() == '<>' || select.select('option[selected]').size()==0 }); - filters_to_load = elements.reject( function (select) { return select.getValue() == '<>' }); - // Filters which are <> are probably dependents themselfes, so remove and forget them for now. - // This is OK as they get reloaded later - dependent_filters.each(function(select) { - Reporting.Filters.remove_filter(select.up('tr').readAttribute("data-filter-name")); - }); - // For each dependent filter we reload its dependent chain - filters_to_load.each(function(selectBox) { - var sources, selected_values; - Reporting.Filters.activate_dependents(selectBox, function() { - sources = Reporting.Filters.get_dependents(selectBox).collect(function(field) { - return $('tr_' + field).select('.filter_values select').first(); - }); - sources.each(function(source) { - if (source.hasAttribute('data-initially-selected')) { - selected_values = source.readAttribute('data-initially-selected').replace(/'/g, '"').evalJSON(true); - Reporting.Filters.select_values(source, selected_values); - Reporting.Filters.value_changed(source.up('tr').readAttribute("data-filter-name")); - } - }); - if (sources.reject( function (select) { return select.value == '<>' }).size() == 0) { - Reporting.Filters.activate_dependents(selectBox); - } - else { - Reporting.RestoreQuery.initialize_load_dependent_filters(sources); - } - }); + restore_dependent_filters: function(filter_name) { + $$("tr.filter[data-filter-name=" + filter_name + "] select.filter-value").each(function(selectBox) { + var activateNext = function(dependent) { + if (!dependent) return; + Reporting.RestoreQuery.restore_dependent_filters(dependent); + }; + if (selectBox.hasAttribute('data-initially-selected')) { + var selected_values = selectBox.readAttribute('data-initially-selected').replace(/'/g, '"').evalJSON(true); + Reporting.Filters.select_values(selectBox, selected_values); + Reporting.Filters.value_changed(filter_name); + } + if (selectBox.getValue() !== '<>') { + Reporting.Filters.activate_dependents(selectBox, activateNext); + } }); }, restore_filters: function () { - // FIXME: rm_xxx values for filters have to be set after re-displaying them + var deps = $$('.filters-select.filter-value').each(function(select) { + var tr = select.up('tr'); + if (tr.visible()) { + var filter = tr.readAttribute('data-filter-name'); + var dependent = select.readAttribute('data-dependent'); + if (filter && dependent) { + Reporting.Filters.remove_filter(filter, false); + } + } + }); + $$("tr[data-selected=true]").each(function (e) { - var rm_box, filter_name; - rm_box = e.select("input[id^=rm]").first(); - filter_name = e.getAttribute("data-filter-name"); - rm_box.value = filter_name; - Reporting.Filters.select_option_enabled($("add_filter_select"), filter_name, false); - // correctly display number of arguments of filters depending on their arity - Reporting.Filters.operator_changed(filter_name, $("operators[" + filter_name + "]")); + var select = e.down(".filter_values select"); + if (select && select.hasAttribute("data-dependent")) return; + var filter_name = e.getAttribute("data-filter-name"); + var on_complete = function() { + Reporting.RestoreQuery.restore_dependent_filters(filter_name); + }; + Reporting.Filters.add_filter(filter_name, false, on_complete); }); - // restore values of dependent filters - Reporting.RestoreQuery.initialize_load_dependent_filters($$('.filters-select[data-all-dependents]').findAll(function(select) { - return select.up('tr').visible() - })); }, restore_group_bys: function () { diff --git a/lib/report/controller.rb b/lib/report/controller.rb index 0da1a08eb0..d920f10fa6 100644 --- a/lib/report/controller.rb +++ b/lib/report/controller.rb @@ -340,15 +340,12 @@ module Report::Controller # renders option tags for each available value for a single filter def available_values if name = params[:filter_name] - begin - f_cls = report_engine::Filter.const_get(name.to_s.camelcase) - filter = f_cls.new.tap do |f| - f.values = JSON.parse(params[:values].gsub("'", '"')) if params[:values].present? and params[:values] - end - render_widget Widget::Filters::Option, filter, :to => canvas = "" - render :text => canvas, :layout => !request.xhr? - rescue NameError + f_cls = report_engine::Filter.const_get(name.to_s.camelcase) + filter = f_cls.new.tap do |f| + f.values = JSON.parse(params[:values].gsub("'", '"')) if params[:values].present? and params[:values] end + render_widget Widget::Filters::Option, filter, :to => canvas = "" + render :text => canvas, :layout => !request.xhr? end end diff --git a/lib/widget/filters/multi_choice.rb b/lib/widget/filters/multi_choice.rb index a7864120b2..c73f9b9e9c 100644 --- a/lib/widget/filters/multi_choice.rb +++ b/lib/widget/filters/multi_choice.rb @@ -12,7 +12,7 @@ class Widget::Filters::MultiChoice < Widget::Filters::Base :id => "#{filterName}_radio_option_#{i}", :value => value } - opts[:checked] = "checked" if filter.values == value + opts[:checked] = "checked" if filter.values == [value].flatten radio_button = tag :input, opts content_tag :label, radio_button + translate(label), :for => "#{filterName}_radio_option_#{i}", diff --git a/lib/widget/filters/multi_values.rb b/lib/widget/filters/multi_values.rb index bbf24c8d2e..6ec8d9865b 100644 --- a/lib/widget/filters/multi_values.rb +++ b/lib/widget/filters/multi_values.rb @@ -23,9 +23,11 @@ class Widget::Filters::MultiValues < Widget::Filters::Base # store selected value(s) in data-initially-selected if this filter is a dependent # of another filter, as we have to restore values manually in the client js if (filter_class.is_dependent? || @options[:lazy]) && !Array(filter.values).empty? - select_options.merge! :"data-initially-selected" => filter.values.to_json.gsub!('"', "'") + select_options.merge! :"data-initially-selected" => + filter.values.to_json.gsub!('"', "'") || "[" + filter.values.map { |v| "'#{v}'" }.join(',') + "]" end - box_content = "" + select_options.merge! :"data-dependent" => true if filter_class.is_dependent? + box_content = "".html_safe box = content_tag :select, select_options do render_widget Widget::Filters::Option, filter, :to => box_content unless @options[:lazy] end