1/** 2 * @file better_exposed_filters.js 3 * 4 * Provides some client-side functionality for the Better Exposed Filters module 5 */ 6(function ($) { 7 Drupal.behaviors.betterExposedFilters = { 8 attach: function(context) { 9 // Add highlight class to checked checkboxes for better theming 10 $('.bef-tree input[type=checkbox], .bef-checkboxes input[type=checkbox]') 11 // Highlight newly selected checkboxes 12 .change(function() { 13 _bef_highlight(this, context); 14 }) 15 .filter(':checked').closest('.form-item', context).addClass('highlight') 16 ; 17 } 18 }; 19 20 Drupal.behaviors.betterExposedFiltersSelectAllNone = { 21 attach: function(context) { 22 23 /* 24 * Add Select all/none links to specified checkboxes 25 */ 26 var selected = $('.form-checkboxes.bef-select-all-none:not(.bef-processed)'); 27 if (selected.length) { 28 var selAll = Drupal.t('Select All'); 29 var selNone = Drupal.t('Select None'); 30 31 // Set up a prototype link and event handlers 32 var link = $('<a class="bef-toggle" href="#">'+ selAll +'</a>') 33 link.click(function(event) { 34 // Don't actually follow the link... 35 event.preventDefault(); 36 event.stopPropagation(); 37 38 if (selAll == $(this).text()) { 39 // Select all the checkboxes 40 $(this) 41 .html(selNone) 42 .siblings('.bef-checkboxes, .bef-tree') 43 .find('.form-item input:checkbox').each(function() { 44 $(this).attr('checked', true); 45 _bef_highlight(this, context); 46 }) 47 .end() 48 49 // attr() doesn't trigger a change event, so we do it ourselves. But just on 50 // one checkbox otherwise we have many spinning cursors 51 .find('input[type=checkbox]:first').change() 52 ; 53 } 54 else { 55 // Unselect all the checkboxes 56 $(this) 57 .html(selAll) 58 .siblings('.bef-checkboxes, .bef-tree') 59 .find('.form-item input:checkbox').each(function() { 60 $(this).attr('checked', false); 61 _bef_highlight(this, context); 62 }) 63 .end() 64 65 // attr() doesn't trigger a change event, so we do it ourselves. But just on 66 // one checkbox otherwise we have many spinning cursors 67 .find('input[type=checkbox]:first').change() 68 ; 69 } 70 }); 71 72 // Add link to the page for each set of checkboxes. 73 selected 74 .addClass('bef-processed') 75 .each(function(index) { 76 // Clone the link prototype and insert into the DOM 77 var newLink = link.clone(true); 78 79 newLink.insertBefore($('.bef-checkboxes, .bef-tree', this)); 80 81 // If all checkboxes are already checked by default then switch to Select None 82 if ($('input:checkbox:checked', this).length == $('input:checkbox', this).length) { 83 newLink.click(); 84 } 85 }) 86 ; 87 } 88 89 // Check for and initialize datepickers 90 var befSettings = Drupal.settings.better_exposed_filters; 91 if (befSettings && befSettings.datepicker && befSettings.datepicker_options && $.fn.datepicker) { 92 var opt = [];
93 $.each(befSettings.datepicker_options, function(key, val) { 94 if (key && val) { 95 opt[key] = JSON.parse(val); 96 } 97 }); 98 $('.bef-datepicker').datepicker(opt); 99 } 100 101 } // attach: function() { 102 }; // Drupal.behaviors.better_exposed_filters = { 103 104 Drupal.behaviors.betterExposedFiltersAllNoneNested = { 105 attach:function (context, settings) { 106 $('.form-checkboxes.bef-select-all-none-nested li').has('ul').once('bef-all-none-nested', function () { 107 $(this) 108 // To respect term depth, check/uncheck child term checkboxes. 109 .find('input.form-checkboxes:first') 110 .click(function() { 111 var checkedParent = $(this).attr('checked'); 112 if (!checkedParent) { 113 // Uncheck all children if parent is unchecked. 114 $(this).parents('li:first').find('ul input.form-checkboxes').removeAttr('checked'); 115 } 116 else { 117 // Check all children if parent is checked. 118 $(this).parents('li:first').find('ul input.form-checkboxes').attr('checked', $(this).attr('checked')); 119 } 120 }) 121 .end() 122 // When a child term is checked or unchecked, set the parent term's 123 // status. 124 .find('ul input.form-checkboxes') 125 .click(function() { 126 var checked = $(this).attr('checked'); 127 128 // Determine the number of unchecked sibling checkboxes. 129 var ct = $(this).parents('ul:first').find('input.form-checkboxes:not(:checked)').size(); 130 131 // If the child term is unchecked, uncheck the parent. 132 if (!checked) { 133 // Uncheck parent if any of the childres is unchecked. 134 $(this).parents('li:first').parents('li:first').find('input.form-checkboxes:first').removeAttr('checked'); 135 } 136 137 // If all sibling terms are checked, check the parent. 138 if (!ct) { 139 // Check the parent if all the children are checked. 140 $(this).parents('li:first').parents('li:first').find('input.form-checkboxes:first').attr('checked', checked); 141 } 142 }); 143 }); 144 } 145 }; 146 147 Drupal.behaviors.better_exposed_filters_slider = { 148 attach: function(context, settings) { 149 var befSettings = settings.better_exposed_filters; 150 if (befSettings && befSettings.slider && befSettings.slider_options) {
151 $.each(befSettings.slider_options, function(i, sliderOptions) { 152 var containing_parent = "#" + sliderOptions.viewId + " #edit-" + sliderOptions.id + "-wrapper .views-widget"; 153 var $filter = $(containing_parent); 154 155 // If the filter is placed in a secondary fieldset, we may not have 156 // the usual wrapper element. 157 if (!$filter.length) { 158 containing_parent = "#" + sliderOptions.viewId + " .bef-slider-wrapper"; 159 $filter = $(containing_parent); 160 } 161 162 // Only make one slider per filter. 163 $filter.once('slider-filter', function() { 164 var $input = $(this).find('input[type=text]'); 165 166 // This is a "between" or "not between" filter with two values. 167 if ($input.length == 2) { 168 var $min = $input.parent().find('input#edit-' + sliderOptions.id + '-min'), 169 $max = $input.parent().find('input#edit-' + sliderOptions.id + '-max'), 170 default_min, 171 default_max; 172 173 if (!$min.length || !$max.length) { 174 return; 175 } 176 177 // Get the default values. 178 // We use slider min & max if there are no defaults. 179 default_min = parseFloat(($min.val() == '') ? sliderOptions.min : $min.val(), 10); 180 default_max = parseFloat(($max.val() == '') ? sliderOptions.max : $max.val(), 10); 181 // Set the element value in case we are using the slider min & max. 182 $min.val(default_min); 183 $max.val(default_max); 184 185 $min.parents(containing_parent).after( 186 $('<div class="bef-slider"></div>').slider({ 187 range: true, 188 min: parseFloat(sliderOptions.min, 10), 189 max: parseFloat(sliderOptions.max, 10), 190 step: parseFloat(sliderOptions.step, 10), 191 animate: sliderOptions.animate ? sliderOptions.animate : false, 192 orientation: sliderOptions.orientation, 193 values: [default_min, default_max], 194 // Update the textfields as the sliders are moved 195 slide: function (event, ui) { 196 $min.val(ui.values[0]); 197 $max.val(ui.values[1]); 198 }, 199 // This fires when the value is set programmatically or the 200 // stop event fires. 201 // This takes care of the case that a user enters a value 202 // into the text field that is not a valid step of the slider. 203 // In that case the slider will go to the nearest step and 204 // this change event will update the text area. 205 change: function (event, ui) { 206 $min.val(ui.values[0]); 207 $max.val(ui.values[1]); 208 }, 209 // Attach stop listeners. 210 stop: function(event, ui) { 211 // Click the auto submit button. 212 $(this).parents('form').find('.ctools-auto-submit-click').click(); 213 } 214 }) 215 ); 216 217 // Update the slider when the fields are updated. 218 $min.blur(function() { 219 befUpdateSlider($(this), 0, sliderOptions); 220 }); 221 $max.blur(function() { 222 befUpdateSlider($(this), 1, sliderOptions); 223 }); 224 } 225 // This is single value filter. 226 else if ($input.length == 1) { 227 if ($input.attr('id') != 'edit-' + sliderOptions.id) { 228 return; 229 } 230 231 // Get the default value. We use slider min if there is no default. 232 var default_value = parseFloat(($input.val() == '') ? sliderOptions.min : $input.val(), 10); 233 // Set the element value in case we are using the slider min. 234 $input.val(default_value); 235 236 $input.parents(containing_parent).after( 237 $('<div class="bef-slider"></div>').slider({ 238 min: parseFloat(sliderOptions.min, 10), 239 max: parseFloat(sliderOptions.max, 10), 240 step: parseFloat(sliderOptions.step, 10), 241 animate: sliderOptions.animate ? sliderOptions.animate : false, 242 orientation: sliderOptions.orientation, 243 value: default_value, 244 // Update the textfields as the sliders are moved. 245 slide: function (event, ui) { 246 $input.val(ui.value); 247 }, 248 // This fires when the value is set programmatically or the 249 // stop event fires. 250 // This takes care of the case that a user enters a value 251 // into the text field that is not a valid step of the slider. 252 // In that case the slider will go to the nearest step and 253 // this change event will update the text area. 254 change: function (event, ui) { 255 $input.val(ui.value); 256 }, 257 // Attach stop listeners. 258 stop: function(event, ui) { 259 // Click the auto submit button. 260 $(this).parents('form').find('.ctools-auto-submit-click').click(); 261 } 262 }) 263 ); 264 265 // Update the slider when the field is updated. 266 $input.blur(function() { 267 befUpdateSlider($(this), null, sliderOptions); 268 }); 269 } 270 else { 271 return; 272 } 273 }) 274 }); 275 } 276 } 277 }; 278 279 // This is only needed to provide ajax functionality 280 Drupal.behaviors.better_exposed_filters_select_as_links = { 281 attach: function(context, settings) { 282 283 $('.bef-select-as-links', context).once(function() { 284 var $element = $(this); 285 286 // Check if ajax submission is enabled. If it's not enabled then we 287 // don't need to attach our custom submission handling, because the 288 // links are already properly built. 289 290 // First check if any ajax views are contained in the current page. 291 if (typeof settings.views == 'undefined' || typeof settings.views.ajaxViews == 'undefined') { 292 return; 293 } 294 295 // Now check that the view for which the current filter block is used, 296 // is part of the configured ajax views. 297 var $uses_ajax = false;
298 $.each(settings.views.ajaxViews, function(i, item) { 299 var $view_name = item.view_name.replace(/_/g, '-'); 300 var $view_display_id = item.view_display_id.replace(/_/g, '-'); 301 var $id = 'views-exposed-form-' + $view_name + '-' + $view_display_id; 302 var $form_id = $element.parents('form').attr('id'); 303 if ($form_id == $id) { 304 $uses_ajax = true; 305 return; 306 } 307 }); 308 309 // If no ajax is used for form submission, we quit here. 310 if (!$uses_ajax) { 311 return; 312 } 313 314 // Attach selection toggle and form submit on click to each link. 315 $(this).find('a').click(function(event) { 316 var $wrapper = $(this).parents('.bef-select-as-links'); 317 var $options = $wrapper.find('select option'); 318 // We have to prevent the page load triggered by the links. 319 event.preventDefault(); 320 event.stopPropagation(); 321 // Un select old select value. 322 $wrapper.find('select option').removeAttr('selected'); 323 324 // Set the corresponding option inside the select element as selected. 325 var link_text = $(this).text(); 326 $selected = $options.filter(function() { 327 return $(this).text() == link_text; 328 }); 329 $selected.attr('selected', 'selected'); 330 $wrapper.find('.bef-new-value').val($selected.val()); 331 $wrapper.find('a').removeClass('active'); 332 $(this).addClass('active'); 333 // Submit the form. 334 $wrapper.parents('form').find('.views-submit-button *[type=submit]').click(); 335 }); 336 }); 337 } 338 }; 339 340 Drupal.behaviors.betterExposedFiltersRequiredFilter = { 341 attach: function(context, settings) { 342 // Required checkboxes should re-check all inputs if a user un-checks 343 // them all. 344 $('.bef-select-as-checkboxes', context).once('bef-required-filter').ajaxComplete(function (e, xhr, s) { 345 var $element = $(this); 346 347 if (typeof settings.views == 'undefined' || typeof settings.views.ajaxViews == 'undefined') { 348 return; 349 } 350 351 // Now check that the view for which the current filter block is used, 352 // is part of the configured ajax views. 353 var $view_name; 354 var $view_display_id; 355 var $uses_ajax = false; 356 $.each(settings.views.ajaxViews, function(i, item) { 357 $view_name = item.view_name; 358 $view_display_id = item.view_display_id; 359 var $id = 'views-exposed-form-' + $view_name.replace(/_/g, '-') + '-' + $view_display_id.replace(/_/g, '-'); 360 var $form_id = $element.parents('form').attr('id'); 361 if ($form_id == $id) { 362 $uses_ajax = true; 363 return false; 364 } 365 }); 366 367 //Check if we have any filters at all because of Views Selective Filter 368 if($('input', this).length > 0) { 369 var $filter_name = $('input', this).attr('name').slice(0, -2); 370 if (Drupal.settings.better_exposed_filters.views[$view_name].displays[$view_display_id].filters[$filter_name].required && $('input:checked', this).length == 0) { 371 $('input', this).prop('checked', true); 372 } 373 } 374 }); 375 } 376 } 377 378 /* 379 * Helper functions 380 */ 381 382 /** 383 * Adds/Removes the highlight class from the form-item div as appropriate 384 */ 385 function _bef_highlight(elem, context) { 386 $elem = $(elem, context); 387 $elem.attr('checked') 388 ? $elem.closest('.form-item', context).addClass('highlight') 389 : $elem.closest('.form-item', context).removeClass('highlight'); 390 } 391 392 /** 393 * Update a slider when a related input element is changed. 394 * 395 * We don't need to check whether the new value is valid based on slider min, 396 * max, and step because the slider will do that automatically and then we 397 * update the textfield on the slider's change event. 398 * 399 * We still have to make sure that the min & max values of a range slider 400 * don't pass each other though, however once this jQuery UI bug is fixed we 401 * won't have to. - http://bugs.jqueryui.com/ticket/3762 402 * 403 * @param $el 404 * A jQuery object of the updated element. 405 * @param valIndex 406 * The index of the value for a range slider or null for a non-range slider. 407 * @param sliderOptions 408 * The options for the current slider. 409 */ 410 function befUpdateSlider($el, valIndex, sliderOptions) { 411 var val = parseFloat($el.val(), 10), 412 currentMin = $el.parents('div.views-widget').next('.bef-slider').slider('values', 0), 413 currentMax = $el.parents('div.views-widget').next('.bef-slider').slider('values', 1); 414 // If we have a range slider. 415 if (valIndex != null) { 416 // Make sure the min is not more than the current max value. 417 if (valIndex == 0 && val > currentMax) { 418 val = currentMax; 419 } 420 // Make sure the max is not more than the current max value. 421 if (valIndex == 1 && val < currentMin) { 422 val = currentMin; 423 }
424 // If the number is invalid, go back to the last value. 425 if (isNaN(val)) { 426 val = $el.parents('div.views-widget').next('.bef-slider').slider('values', valIndex); 427 } 428 } 429 else { 430 // If the number is invalid, go back to the last value. 431 if (isNaN(val)) { 432 val = $el.parents('div.views-widget').next('.bef-slider').slider('value'); 433 } 434 } 435 // Make sure we are a number again. 436 val = parseFloat(val, 10); 437 // Set the slider to the new value. 438 // The slider's change event will then update the textfield again so that 439 // they both have the same value. 440 if (valIndex != null) { 441 $el.parents('div.views-widget').next('.bef-slider').slider('values', valIndex, val); 442 } 443 else { 444 $el.parents('div.views-widget').next('.bef-slider').slider('value', val); 445 } 446 } 447 448}) (jQuery);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.