diff --git a/lib/assets/javascripts/application.js b/lib/assets/javascripts/application.js index fe85da0..59b9a26 100644 --- a/lib/assets/javascripts/application.js +++ b/lib/assets/javascripts/application.js @@ -21,6 +21,7 @@ //= require i18n/translations //= require_tree ./locale //= require gettext/all +//= require jquery-accessible-autocomplet-list-aria.js $( document ).ready(function() { diff --git a/lib/assets/javascripts/jquery-accessible-autocomplet-list-aria.js b/lib/assets/javascripts/jquery-accessible-autocomplet-list-aria.js new file mode 100755 index 0000000..60d3b26 --- /dev/null +++ b/lib/assets/javascripts/jquery-accessible-autocomplet-list-aria.js @@ -0,0 +1,356 @@ +$(document).ready(function(){ + + /* + * jQuery accessible and keyboard-enhanced autocomplete list + * Website: http://a11y.nicolas-hoffmann.net/autocomplet-list/ + * License MIT: https://github.com/nico3333fr/jquery-accessible-autocomplete-list-aria/blob/master/LICENSE + */ + // loading combobox ------------------------------------------------------------------------------------------------------------ + // init + var $js_combobox = $('.js-combobox'), + $body = $('body'), + default_text_help = 'Use tabulation (or down) key to access and browse suggestions after input. Confirm your choice with enter key, or esc key to close suggestions box.', + default_class_for_invisible_text = 'invisible', + suggestion_single = 'There is ', + suggestion_plural = 'There are ', + suggestion_word = 'suggestion', + button_clear_title = 'clear this field', + button_clear_text = 'X', + case_sensitive = 'yes', + min_length = 0, + limit_number_suggestions = 666, + search_option = 'beginning', // or 'containing' + see_more_text = 'See more results…', + tablo_suggestions = []; + + if ( $js_combobox.length ) { // if there are at least one :) + + // init + $js_combobox.each( function(index_combo) { + var $this = $(this), + $this_id = $this.attr('id'), + $label_this = $( 'label[for="' + $this_id + '"]' ), + index_lisible = index_combo+1, + options = $this.data() + $combobox_prefix_class = typeof options.comboboxPrefixClass !== 'undefined' ? options.comboboxPrefixClass + '-' : '', + $combobox_help_text = typeof options.comboboxHelpText !== 'undefined' ? options.comboboxHelpText : default_text_help, + $list_suggestions = $( '#' + $this.attr('list') ), + $combobox_button_title = typeof options.comboboxButtonTitle !== 'undefined' ? options.comboboxButtonTitle : button_clear_title, + $combobox_button_text = typeof options.comboboxButtonText !== 'undefined' ? options.comboboxButtonText : button_clear_text, + $combobox_case_sensitive = typeof options.comboboxCaseSensitive !== 'undefined' ? options.comboboxCaseSensitive : case_sensitive, + tablo_temp_suggestions = [] ; + + // input + $this.attr({ + 'data-number' : index_lisible, + 'autocorrect' : 'off', + 'autocapitalize' : 'off', + 'spellcheck' : 'off', + 'autocomplete' : 'off', + 'aria-describedby' : $combobox_prefix_class + 'help-text' + index_lisible, + 'aria-autocomplete' : 'list', + 'data-lastval' : '', + 'aria-owns' : $combobox_prefix_class + 'suggest_' + index_lisible + }); + // stock into tables + $list_suggestions.find('option').each( function(index_option, index_element) { + tablo_temp_suggestions.push(index_element.value); + }); + if ($combobox_case_sensitive === 'no'){ + // order case tablo_temp_suggestions + tablo_suggestions[index_lisible] = tablo_temp_suggestions.sort(function(a,b) { + a = a.toLowerCase(); + b = b.toLowerCase(); + if ( a == b) { + return 0; + } + if ( a > b) { + return 1; + } + return -1; + }); + } + else { tablo_suggestions[index_lisible] = tablo_temp_suggestions.sort(); } + + // wrap into a container + $this.wrap('
'); + + var $combobox_container = $this.parent(); + + // custom datalist/listbox linked to input + $combobox_container.append( '").text(text_number_suggestions); + $suggestions_text.attr('aria-live','polite'); + $suggestions_text.empty(); + $suggestions_text.append(suggestions_to_add); + } + } + + } + + } + } + + }) + .on('click', function(event) { + var $target = $(event.target), + $suggestions_text = $('.js-suggestion-text:not(:empty)'), // if a suggestion text is not empty => suggestion opened somewhere + $container = $suggestions_text.parents('.js-container'), + $input_text = $container.find('.js-combobox'), + $suggestions = $container.find('.js-suggest div'); + + // if click outside => close opened suggestions + if ( !$target.is('.js-suggestion') && !$target.is('.js-combobox') && $suggestions_text.length) { + $input_text.val( $input_text.attr('data-lastval') ); + $suggestions.empty(); + $suggestions_text.empty(); + } + }) + // tab + down management for autocomplete (when list of suggestion) + .on( 'keydown', '.js-combobox', function( event ) { + var $this = $(this), + $container = $this.parent(), + $input_text = $container.find('.js-combobox'), + $suggestions = $container.find('.js-suggest div'), + $suggestion_list = $suggestions.find('.js-suggestion'), + $suggestions_text = $container.find('.js-suggestion-text'), + $autorise_tab_options = typeof $this.attr('data-combobox-notab-options') !== 'undefined' ? false : true, + $first_suggestion = $suggestion_list.first(); + + if ( ( !event.shiftKey && event.keyCode == 9 && $autorise_tab_options ) || event.keyCode == 40 ) { // tab (if authorised) or bottom + // See if there are suggestions, and yes => focus on first one + if ($suggestion_list.length) { + $input_text.val($first_suggestion.html()); + $suggestion_list.first().focus(); + event.preventDefault(); + } + } + if ( event.keyCode == 27 || ($autorise_tab_options === false && event.keyCode == 9 ) ) { // esc or (tab/shift tab + notab option) = close + $input_text.val( $input_text.attr('data-lastval') ); + $suggestions.empty(); + $suggestions_text.empty(); + if ( event.keyCode == 27) { // Esc prevented only, tab can go :) + event.preventDefault(); + setTimeout(function(){ $input_text.focus(); }, 300); // timeout to avoid problem in suggestions display + } + } + + }) + // tab + down management in list of suggestions + .on( 'keydown', '.js-suggestion', function( event ) { + var $this = $(this), + $container = $this.parents('.js-container'), + $input_text = $container.find('.js-combobox'), + $autorise_tab_options = typeof $input_text.attr('data-combobox-notab-options') !== 'undefined' ? false : true, + $suggestions = $container.find('.js-suggest div'), + $suggestions_text = $container.find('.js-suggestion-text'), + $next_suggestion = $this.next(), + $previous_suggestion = $this.prev(); + + if ( event.keyCode == 27 || ($autorise_tab_options === false && event.keyCode == 9 ) ) { // esc or (tab/shift tab + notab option) = close + if ( event.keyCode == 27) { // Esc prevented only, tab can go :) + $input_text.val( $input_text.attr('data-lastval') ); + $suggestions.empty(); + $suggestions_text.empty(); + setTimeout(function(){ $input_text.focus(); }, 300); // timeout to avoid problem in suggestions display + event.preventDefault(); + } + if ( $autorise_tab_options === false && event.keyCode == 9 ) { + $suggestions.empty(); + $suggestions_text.empty(); + $input_text.focus(); + } + } + if ( event.keyCode == 13 || event.keyCode == 32 ) { // Enter or space + if ( $this.hasClass('js-seemore') ) { + $input_text.val($input_text.attr('data-lastval')); + $suggestions.empty(); + $suggestions_text.empty(); + setTimeout(function(){ $input_text.focus(); }, 300); // timeout to avoid problem in suggestions display + // go define the function you need when we click the see_more option + setTimeout(function(){ do_see_more_option(); }, 301); // timeout to avoid problem in suggestions display + event.preventDefault(); + } + else { + $input_text.val( $this.html() ); + $input_text.attr('data-lastval', $this.html() ); + $suggestions.empty(); + $suggestions_text.empty(); + setTimeout(function(){ $input_text.focus(); }, 300); // timeout to avoid problem in suggestions display + event.preventDefault(); + } + + } + if ( ( !event.shiftKey && event.keyCode == 9 && $autorise_tab_options ) || event.keyCode == 40 ) { // tab (if authorised) or bottom + if ($next_suggestion.length) { + $input_text.val($next_suggestion.html()); + $next_suggestion.focus(); + } + else { + $input_text.val( $input_text.attr('data-lastval') ); + if ( !event.shiftKey && event.keyCode == 9 ) { // tab closes the list + var e = jQuery.Event("keydown"); + e.which = 27; // # Some key code value + e.keyCode = 27; + $this.trigger(e); + } + else { setTimeout(function(){ $input_text.focus(); }, 300); } // timeout to avoid problem in suggestions display + + } + event.preventDefault(); + } + + if ( ( event.shiftKey && event.keyCode == 9 && $autorise_tab_options ) || event.keyCode == 38 ) { // top or Maj+tab (if authorised) + if ($previous_suggestion.length) { + $input_text.val($previous_suggestion.html()); + $previous_suggestion.focus(); + } + else { + $input_text.val( $input_text.attr('data-lastval') ).focus(); + } + event.preventDefault(); + } + }) + // clear button + .on( 'click', '.js-clear-button', function( event ) { + var $this = $(this), + $container = $this.parent(), + $input_text = $container.find('.js-combobox'), + $suggestions = $container.find('.js-suggest div'), + $suggestions_text = $container.find('.js-suggestion-text'); + + $suggestions.empty(); + $suggestions_text.empty(); + $input_text.val(''); + $input_text.attr( 'data-lastval', ''); + + }) + .on( 'click', '.js-suggestion', function( event ) { + var $this = $(this), + value = $this.html(), + $container = $this.parents('.js-container'), + $input_text = $container.find('.js-combobox'), + $suggestions = $container.find('.js-suggest div'), + $suggestions_text = $container.find('.js-suggestion-text'); + + if ( $this.hasClass('js-seemore') ) { + $suggestions.empty(); + $suggestions_text.empty(); + $input_text.focus(); + // go define the function you need when we click the see_more option + do_see_more_option( ); + } + else { + $input_text.val(value).focus(); + $suggestions.empty(); + $suggestions_text.empty(); + } + + + }); + + + } + +});