From 1749fbf3e6169c7aa304c967ab4ff7ea4217e61e Mon Sep 17 00:00:00 2001 From: Jean-Philippe Lang Date: Tue, 7 Aug 2012 16:41:46 +0000 Subject: [PATCH] Build issue filters using javascript. git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@10162 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/queries_helper.rb | 9 +- app/models/query.rb | 18 ++++ app/views/queries/_filters.html.erb | 58 +++-------- app/views/queries/_form.html.erb | 2 +- public/javascripts/application.js | 156 +++++++++++++++++++++------- public/stylesheets/application.css | 5 +- 6 files changed, 164 insertions(+), 84 deletions(-) diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index c35bfc01e..076a8959e 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -18,9 +18,12 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. module QueriesHelper - - def operators_for_select(filter_type) - Query.operators_by_filter_type[filter_type].collect {|o| [l(Query.operators[o]), o]} + def filters_options_for_select(query) + options = [[]] + options += query.available_filters.sort {|a,b| a[1][:order] <=> b[1][:order]}.map do |field, field_options| + [field_options[:name], field] + end + options_for_select(options) end def column_header(column) diff --git a/app/models/query.rb b/app/models/query.rb index 8c5f0b090..628a1dfeb 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -214,6 +214,11 @@ class Query < ActiveRecord::Base @trackers ||= project.nil? ? Tracker.find(:all, :order => 'position') : project.rolled_up_trackers end + # Returns a hash of localized labels for all filter operators + def self.operators_labels + operators.inject({}) {|h, operator| h[operator.first] = l(operator.last); h} + end + def available_filters return @available_filters if @available_filters @@ -309,9 +314,22 @@ class Query < ActiveRecord::Base @available_filters.delete field } + @available_filters.each do |field, options| + options[:name] ||= l("field_#{field}".gsub(/_id$/, '')) + end + @available_filters end + # Returns a representation of the available filters for JSON serialization + def available_filters_as_json + json = {} + available_filters.each do |field, options| + json[field] = options.slice(:type, :name, :values).stringify_keys + end + json + end + def add_filter(field, operator, values) # values must be an array return unless values.nil? || values.is_a?(Array) diff --git a/app/views/queries/_filters.html.erb b/app/views/queries/_filters.html.erb index d9f91ce2e..80b06c10a 100644 --- a/app/views/queries/_filters.html.erb +++ b/app/views/queries/_filters.html.erb @@ -1,53 +1,27 @@ +<%= javascript_tag do %> +var operatorLabels = <%= raw Query.operators_labels.to_json %>; +var operatorByType = <%= raw Query.operators_by_filter_type.to_json %>; +var availableFilters = <%= raw query.available_filters_as_json.to_json %>; +var labelDayPlural = "<%= raw escape_javascript(l(:label_day_plural)) %>"; +$(document).ready(function(){ + initFilters(); + <% query.filters.each do |field, options| %> + addFilter("<%= field %>", <%= raw query.operator_for(field).to_json %>, <%= raw query.values_for(field).to_json %>); + <% end %> +}); +<% end %> +
- -<% query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.each do |filter| %> - <% field = filter[0] - options = filter[1] %> - id="tr_<%= field %>" class="filter"> - - - - -<% end %> +
- <%= check_box_tag 'f[]', field, query.has_filter?(field), :onclick => "toggle_filter('#{field}');", :id => "cb_#{field}" %> - - - <%= label_tag "operators_#{field}", l(:description_filter), :class => "hidden-for-sighted" %> - <%= select_tag "op[#{field}]", options_for_select(operators_for_select(options[:type]), - query.operator_for(field)), :id => "operators_#{field}", - :onchange => "toggle_operator('#{field}');" %> - - - -
<%= label_tag('add_filter_select', l(:label_filter_add)) %> -<%= select_tag 'add_filter_select', options_for_select([["",""]] + query.available_filters.sort{|a,b| a[1][:order]<=>b[1][:order]}.collect{|field| [ field[1][:name] || l(("field_"+field[0].to_s.gsub(/_id$/, "")).to_sym), field[0]] unless query.has_filter?(field[0])}.compact), - :onchange => "add_filter();", - :name => nil %> +<%= select_tag 'add_filter_select', filters_options_for_select(query), :name => nil %>
<%= hidden_field_tag 'f[]', '' %> -<%= javascript_tag '$(document).ready(function(){observeIssueFilters();});' %> +<% include_calendar_headers_tags %> diff --git a/app/views/queries/_form.html.erb b/app/views/queries/_form.html.erb index 76ce35ebe..0e75d9418 100644 --- a/app/views/queries/_form.html.erb +++ b/app/views/queries/_form.html.erb @@ -23,7 +23,7 @@ <%= select 'query', 'group_by', @query.groupable_columns.collect {|c| [c.caption, c.name.to_s]}, :include_blank => true %>

-
<%= l(:label_filter_plural) %> +
<%= l(:label_filter_plural) %> <%= render :partial => 'queries/filters', :locals => {:query => query}%>
diff --git a/public/javascripts/application.js b/public/javascripts/application.js index 42fb945ad..20bfb0895 100644 --- a/public/javascripts/application.js +++ b/public/javascripts/application.js @@ -78,35 +78,131 @@ function hideFieldset(el) { fieldset.children('div').hide(); } -function add_filter() { - var select = $('#add_filter_select'); - var field = select.val(); - $('#tr_'+field).show(); - var check_box = $('#cb_' + field); - check_box.attr('checked', true); - toggle_filter(field); - select.val(''); - - select.children('option').each(function(index) { +function initFilters(){ + $('#add_filter_select').change(function(){ + addFilter($(this).val(), '', []); + }); + $('#filters-table td.field input[type=checkbox]').each(function(){ + toggleFilter($(this).val()); + }); + $('#filters-table td.field input[type=checkbox]').live('click',function(){ + toggleFilter($(this).val()); + }); + $('#filters-table .toggle-multiselect').live('click',function(){ + toggleMultiSelect($(this).siblings('select')); + }); + $('#filters-table input[type=text]').live('keypress', function(e){ + if (e.keyCode == 13) submit_query_form("query_form"); + }); +} + +function addFilter(field, operator, values) { + var fieldId = field.replace('.', '_'); + var tr = $('#tr_'+fieldId); + if (tr.length > 0) { + tr.show(); + } else { + buildFilterRow(field, operator, values); + } + $('#cb_'+fieldId).attr('checked', true); + toggleFilter(field); + $('#add_filter_select').val('').children('option').each(function(){ if ($(this).attr('value') == field) { $(this).attr('disabled', true); } }); } -function toggle_filter(field) { - check_box = $('#cb_' + field); - if (check_box.is(':checked')) { - $("#operators_" + field).show().removeAttr('disabled'); - toggle_operator(field); +function buildFilterRow(field, operator, values) { + var fieldId = field.replace('.', '_'); + var filterTable = $("#filters-table"); + var filterOptions = availableFilters[field]; + var operators = operatorByType[filterOptions['type']]; + var filterValues = filterOptions['values']; + var i, select; + + var tr = $('').attr('id', 'tr_'+fieldId).html( + '' + + '' + + '  ' + ); + select = tr.find('td.values select'); + if (values.length > 1) {select.attr('multiple', true)}; + for (i=0;i'); + if ($.isArray(filterValue)) { + option.val(filterValue[1]).html(filterValue[0]); + } else { + option.val(filterValue).html(filterValue); + } + if (values.indexOf(filterValues[i][1]) > -1) {option.attr('selected', true)}; + select.append(option); + } + break; + case "date": + case "date_past": + tr.find('td.values').append( + '' + + ' ' + + ' '+labelDayPlural+'' + ); + $('#values_'+fieldId+'_1').val(values[0]).datepicker(datepickerOptions); + $('#values_'+fieldId+'_2').val(values[1]).datepicker(datepickerOptions); + $('#values_'+fieldId).val(values[0]); + break; + case "string": + case "text": + tr.find('td.values').append( + '' + ); + $('#values_'+fieldId).val(values[0]); + break; + case "integer": + case "float": + tr.find('td.values').append( + '' + + ' ' + ); + $('#values_'+fieldId+'_1').val(values[0]); + $('#values_'+fieldId+'_2').val(values[1]); + break; + } +} + +function toggleFilter(field) { + var fieldId = field.replace('.', '_'); + if ($('#cb_' + fieldId).is(':checked')) { + $("#operators_" + fieldId).show().removeAttr('disabled'); + toggleOperator(field); } else { - $("#operators_" + field).hide().attr('disabled', true); + $("#operators_" + fieldId).hide().attr('disabled', true); enableValues(field, []); } } function enableValues(field, indexes) { - $(".values_" + field).each(function(index) { + var fieldId = field.replace('.', '_'); + $('#tr_'+fieldId+' td.values .value').each(function(index) { if (indexes.indexOf(index) >= 0) { $(this).removeAttr('disabled'); $(this).parents('span').first().show(); @@ -122,16 +218,11 @@ function enableValues(field, indexes) { $(this).show(); } }); - - if (indexes.length > 0) { - $("#div_values_" + field).show(); - } else { - $("#div_values_" + field).hide(); - } } -function toggle_operator(field) { - operator = $("#operators_" + field); +function toggleOperator(field) { + var fieldId = field.replace('.', '_'); + var operator = $("#operators_" + fieldId); switch (operator.val()) { case "!*": case "*": @@ -158,12 +249,11 @@ function toggle_operator(field) { } } -function toggle_multi_select(id) { - var select = $('#'+id); - if (select.attr('multiple')) { - select.removeAttr('multiple'); +function toggleMultiSelect(el) { + if (el.attr('multiple')) { + el.removeAttr('multiple'); } else { - select.attr('multiple', true); + el.attr('multiple', true); } } @@ -172,12 +262,6 @@ function submit_query_form(id) { $('#'+id).submit(); } -function observeIssueFilters() { - $('#query_form input[type=text]').keypress(function(e){ - if (e.keyCode == 13) submit_query_form("query_form"); - }); -} - var fileFieldCount = 1; function addFileField() { var fields = $('#attachments_fields'); diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css index 3b4831d47..3400bd4c0 100644 --- a/public/stylesheets/application.css +++ b/public/stylesheets/application.css @@ -338,13 +338,14 @@ fieldset.collapsible.collapsed legend { background-image: url(../images/arrow_co fieldset#date-range p { margin: 2px 0 2px 0; } fieldset#filters table { border-collapse: collapse; } fieldset#filters table td { padding: 0; vertical-align: middle; } -fieldset#filters tr.filter { height: 2em; } +fieldset#filters tr.filter { height: 2.1em; } fieldset#filters td.field { width:200px; } fieldset#filters td.operator { width:170px; } fieldset#filters td.values { white-space:nowrap; } fieldset#filters td.values select {min-width:130px;} -fieldset#filters td.values img { vertical-align: middle; margin-left:1px; } +fieldset#filters td.values input {height:1em;} fieldset#filters td.add-filter { text-align: right; vertical-align: top; } +.toggle-multiselect {background: url(../images/bullet_toggle_plus.png) no-repeat 0% 40%; padding-left:8px; margin-left:0; cursor:pointer;} .buttons { font-size: 0.9em; margin-bottom: 1.4em; margin-top: 1em; } div#issue-changesets {float:right; width:45%; margin-left: 1em; margin-bottom: 1em; background: #fff; padding-left: 1em; font-size: 90%;} -- 2.39.5