mirror of
https://github.com/moodle/moodle.git
synced 2025-04-20 16:04:25 +02:00
MDL-51970 autocomplete: Small refactor because too many options
Each function was taking 8-10 args - combine them all in state and options objects and pass those around instead.
This commit is contained in:
parent
97d2ea7f87
commit
273b255673
2
lib/amd/build/form-autocomplete.min.js
vendored
2
lib/amd/build/form-autocomplete.min.js
vendored
File diff suppressed because one or more lines are too long
@ -46,11 +46,11 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @method activateSelection
|
||||
* @private
|
||||
* @param {Number} index The index in the current (visible) list of selection.
|
||||
* @param {String} selectionId The id of the selection element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
*/
|
||||
var activateSelection = function(index, selectionId) {
|
||||
var activateSelection = function(index, state) {
|
||||
// Find the elements in the DOM.
|
||||
var selectionElement = $(document.getElementById(selectionId));
|
||||
var selectionElement = $(document.getElementById(state.selectionId));
|
||||
|
||||
// Count the visible items.
|
||||
var length = selectionElement.children('[aria-selected=true]').length;
|
||||
@ -62,7 +62,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Find the specified element.
|
||||
var element = $(selectionElement.children('[aria-selected=true]').get(index));
|
||||
// Create an id we can assign to this element.
|
||||
var itemId = selectionId + '-' + index;
|
||||
var itemId = state.selectionId + '-' + index;
|
||||
|
||||
// Deselect all the selections.
|
||||
selectionElement.children().attr('data-active-selection', false).attr('id', '');
|
||||
@ -77,17 +77,16 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method deselectItem
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} selectionId The id of the selection element for this instance of the autocomplete.
|
||||
* @param {Object} options Original options for this autocomplete element.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
* @param {Element} The item to be deselected.
|
||||
* @param {Element} originalSelect The original select list.
|
||||
* @param {Boolean} multiple Is this a multi select.
|
||||
*/
|
||||
var deselectItem = function(inputId, selectionId, item, originalSelect, multiple) {
|
||||
var deselectItem = function(options, state, item, originalSelect) {
|
||||
var selectedItemValue = $(item).attr('data-value');
|
||||
|
||||
// We can only deselect items if this is a multi-select field.
|
||||
if (multiple) {
|
||||
if (options.multiple) {
|
||||
// Look for a match, and toggle the selected property if there is a match.
|
||||
originalSelect.children('option').each(function(index, ele) {
|
||||
if ($(ele).attr('value') == selectedItemValue) {
|
||||
@ -100,7 +99,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
});
|
||||
}
|
||||
// Rerender the selection list.
|
||||
updateSelectionList(selectionId, inputId, originalSelect, multiple);
|
||||
updateSelectionList(options, state, originalSelect);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -109,13 +108,12 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @method activateItem
|
||||
* @private
|
||||
* @param {Number} index The index in the current (visible) list of suggestions.
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this instance of autocomplete.
|
||||
*/
|
||||
var activateItem = function(index, inputId, suggestionsId) {
|
||||
var activateItem = function(index, state) {
|
||||
// Find the elements in the DOM.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
|
||||
// Count the visible items.
|
||||
var length = suggestionsElement.children('[aria-hidden=false]').length;
|
||||
@ -129,7 +127,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Find the index of this item in the full list of suggestions (including hidden).
|
||||
var globalIndex = $(suggestionsElement.children('[role=option]')).index(element);
|
||||
// Create an id we can assign to this element.
|
||||
var itemId = suggestionsId + '-' + globalIndex;
|
||||
var itemId = state.suggestionsId + '-' + globalIndex;
|
||||
|
||||
// Deselect all the suggestions.
|
||||
suggestionsElement.children().attr('aria-selected', false).attr('id', '');
|
||||
@ -153,18 +151,17 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method activateNextItem
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} state State variable for this auto complete element.
|
||||
*/
|
||||
var activateNextItem = function(inputId, suggestionsId) {
|
||||
var activateNextItem = function(state) {
|
||||
// Find the list of suggestions.
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Find the active one.
|
||||
var element = suggestionsElement.children('[aria-selected=true]');
|
||||
// Find it's index.
|
||||
var current = suggestionsElement.children('[aria-hidden=false]').index(element);
|
||||
// Activate the next one.
|
||||
activateItem(current+1, inputId, suggestionsId);
|
||||
activateItem(current+1, state);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -172,42 +169,42 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method activatePreviousSelection
|
||||
* @private
|
||||
* @param {String} selectionId The id of the selection element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this instance of autocomplete.
|
||||
*/
|
||||
var activatePreviousSelection = function(selectionId) {
|
||||
var activatePreviousSelection = function(state) {
|
||||
// Find the list of selections.
|
||||
var selectionsElement = $(document.getElementById(selectionId));
|
||||
var selectionsElement = $(document.getElementById(state.selectionId));
|
||||
// Find the active one.
|
||||
var element = selectionsElement.children('[data-active-selection=true]');
|
||||
if (!element) {
|
||||
activateSelection(0, selectionId);
|
||||
activateSelection(0, state);
|
||||
return;
|
||||
}
|
||||
// Find it's index.
|
||||
var current = selectionsElement.children('[aria-selected=true]').index(element);
|
||||
// Activate the next one.
|
||||
activateSelection(current-1, selectionId);
|
||||
activateSelection(current-1, state);
|
||||
};
|
||||
/**
|
||||
* Find the index of the current active selection, and activate the next one.
|
||||
*
|
||||
* @method activateNextSelection
|
||||
* @private
|
||||
* @param {String} selectionId The id of the selection element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this instance of autocomplete.
|
||||
*/
|
||||
var activateNextSelection = function(selectionId) {
|
||||
var activateNextSelection = function(state) {
|
||||
// Find the list of selections.
|
||||
var selectionsElement = $(document.getElementById(selectionId));
|
||||
var selectionsElement = $(document.getElementById(state.selectionId));
|
||||
// Find the active one.
|
||||
var element = selectionsElement.children('[data-active-selection=true]');
|
||||
if (!element) {
|
||||
activateSelection(0, selectionId);
|
||||
activateSelection(0, state);
|
||||
return;
|
||||
}
|
||||
// Find it's index.
|
||||
var current = selectionsElement.children('[aria-selected=true]').index(element);
|
||||
// Activate the next one.
|
||||
activateSelection(current+1, selectionId);
|
||||
activateSelection(current+1, state);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -215,18 +212,17 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method activatePreviousItem
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
*/
|
||||
var activatePreviousItem = function(inputId, suggestionsId) {
|
||||
var activatePreviousItem = function(state) {
|
||||
// Find the list of suggestions.
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Find the active one.
|
||||
var element = suggestionsElement.children('[aria-selected=true]');
|
||||
// Find it's index.
|
||||
var current = suggestionsElement.children('[aria-hidden=false]').index(element);
|
||||
// Activate the next one.
|
||||
activateItem(current-1, inputId, suggestionsId);
|
||||
activateItem(current-1, state);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -234,16 +230,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method closeSuggestions
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
*/
|
||||
var closeSuggestions = function(inputId, suggestionsId, selectionId) {
|
||||
var closeSuggestions = function(state) {
|
||||
// Find the elements in the DOM.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
|
||||
// Announce the list of suggestions was closed, and read the current list of selections.
|
||||
inputElement.attr('aria-expanded', false).attr('aria-activedescendant', selectionId);
|
||||
inputElement.attr('aria-expanded', false).attr('aria-activedescendant', state.selectionId);
|
||||
// Hide the suggestions list (from screen readers too).
|
||||
suggestionsElement.hide().attr('aria-hidden', true);
|
||||
};
|
||||
@ -253,46 +248,44 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method updateSuggestions
|
||||
* @private
|
||||
* @param {String} query The current query typed in the input field.
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} options The original options for this autocomplete.
|
||||
* @param {Object} state The state variables for this autocomplete.
|
||||
* @param {String} query The current text for the search string.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
var updateSuggestions = function(query, inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive) {
|
||||
var updateSuggestions = function(options, state, query, originalSelect) {
|
||||
// Find the elements in the DOM.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
|
||||
// Used to track if we found any visible suggestions.
|
||||
var matchingElements = false;
|
||||
// Options is used by the context when rendering the suggestions from a template.
|
||||
var options = [];
|
||||
var suggestions = [];
|
||||
originalSelect.children('option').each(function(index, option) {
|
||||
if ($(option).prop('selected') !== true) {
|
||||
options[options.length] = { label: option.innerHTML, value: $(option).attr('value') };
|
||||
suggestions[suggestions.length] = { label: option.innerHTML, value: $(option).attr('value') };
|
||||
}
|
||||
});
|
||||
|
||||
// Re-render the list of suggestions.
|
||||
var searchquery = caseSensitive ? query : query.toLocaleLowerCase();
|
||||
var searchquery = state.caseSensitive ? query : query.toLocaleLowerCase();
|
||||
var context = $.extend({ options: suggestions}, options, state);
|
||||
templates.render(
|
||||
'core/form_autocomplete_suggestions',
|
||||
{ inputId: inputId, suggestionsId: suggestionsId, options: options, multiple: multiple}
|
||||
context
|
||||
).done(function(newHTML) {
|
||||
// We have the new template, insert it in the page.
|
||||
suggestionsElement.replaceWith(newHTML);
|
||||
// Get the element again.
|
||||
suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Show it if it is hidden.
|
||||
suggestionsElement.show().attr('aria-hidden', false);
|
||||
// For each option in the list, hide it if it doesn't match the query.
|
||||
suggestionsElement.children().each(function(index, node) {
|
||||
node = $(node);
|
||||
if ((caseSensitive && node.text().indexOf(searchquery) > -1) ||
|
||||
(!caseSensitive && node.text().toLocaleLowerCase().indexOf(searchquery) > -1)) {
|
||||
if ((options.caseSensitive && node.text().indexOf(searchquery) > -1) ||
|
||||
(!options.caseSensitive && node.text().toLocaleLowerCase().indexOf(searchquery) > -1)) {
|
||||
node.show().attr('aria-hidden', false);
|
||||
matchingElements = true;
|
||||
} else {
|
||||
@ -305,8 +298,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// We only activate the first item in the list if tags is false,
|
||||
// because otherwise "Enter" would select the first item, instead of
|
||||
// creating a new tag.
|
||||
if (!tags) {
|
||||
activateItem(0, inputId, suggestionsId);
|
||||
if (!options.tags) {
|
||||
activateItem(0, state);
|
||||
}
|
||||
} else {
|
||||
// Nothing matches. Tell them that.
|
||||
@ -323,14 +316,13 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method createItem
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Object} options The original options for the autocomplete.
|
||||
* @param {Object} state State variables for the autocomplete.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
*/
|
||||
var createItem = function(inputId, suggestionsId, selectionId, multiple, originalSelect) {
|
||||
var createItem = function(options, state, originalSelect) {
|
||||
// Find the element in the DOM.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
// Get the current text in the input field.
|
||||
var query = inputElement.val();
|
||||
var tags = query.split(',');
|
||||
@ -340,7 +332,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// If we can only select one at a time, deselect any current value.
|
||||
tag = tag.trim();
|
||||
if (tag !== '') {
|
||||
if (!multiple) {
|
||||
if (!options.multiple) {
|
||||
originalSelect.children('option').prop('selected', false);
|
||||
}
|
||||
// Look for an existing option in the select list that matches this new tag.
|
||||
@ -363,12 +355,12 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
});
|
||||
|
||||
updateSelectionList(selectionId, inputId, originalSelect, multiple);
|
||||
updateSelectionList(options, state, originalSelect);
|
||||
|
||||
// Clear the input field.
|
||||
inputElement.val('');
|
||||
// Close the suggestions list.
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
closeSuggestions(state);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -376,15 +368,14 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method updateSelectionList
|
||||
* @private
|
||||
* @param {String} selectionId The id of the selections element for this instance of the autocomplete.
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {Object} options Original options for this autocomplete element.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Does this element support multiple selections.
|
||||
*/
|
||||
var updateSelectionList = function(selectionId, inputId, originalSelect, multiple) {
|
||||
var updateSelectionList = function(options, state, originalSelect) {
|
||||
// Build up a valid context to re-render the template.
|
||||
var items = [];
|
||||
var newSelection = $(document.getElementById(selectionId));
|
||||
var newSelection = $(document.getElementById(state.selectionId));
|
||||
var activeId = newSelection.attr('aria-activedescendant');
|
||||
var activeValue = false;
|
||||
|
||||
@ -396,11 +387,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
items.push( { label: $(ele).html(), value: $(ele).attr('value') } );
|
||||
}
|
||||
});
|
||||
var context = {
|
||||
selectionId: selectionId,
|
||||
items: items,
|
||||
multiple: multiple
|
||||
};
|
||||
var context = $.extend({ items: items }, options, state);
|
||||
|
||||
// Render the template.
|
||||
templates.render('core/form_autocomplete_selection', context).done(function(newHTML) {
|
||||
// Add it to the page.
|
||||
@ -410,7 +398,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// Reselect any previously selected item.
|
||||
newSelection.children('[aria-selected=true]').each(function(index, ele) {
|
||||
if ($(ele).attr('data-value') === activeValue) {
|
||||
activateSelection(index, selectionId);
|
||||
activateSelection(index, state);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -425,16 +413,14 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method selectCurrentItem
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {String} selectionId The id of the selection element for this instance of the autocomplete.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Object} options The original options for the autocomplete.
|
||||
* @param {Object} state State variables for the autocomplete.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
*/
|
||||
var selectCurrentItem = function(inputId, suggestionsId, selectionId, multiple, originalSelect) {
|
||||
var selectCurrentItem = function(options, state, originalSelect) {
|
||||
// Find the elements in the page.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Here loop through suggestions and set val to join of all selected items.
|
||||
|
||||
var selectedItemValue = suggestionsElement.children('[aria-selected=true]').attr('data-value');
|
||||
@ -442,7 +428,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// select one or more items correctly.
|
||||
// Take care to use 'prop' and not 'attr' for selected properties.
|
||||
// If only one can be selected at a time, start by deselecting everything.
|
||||
if (!multiple) {
|
||||
if (!options.multiple) {
|
||||
originalSelect.children('option').prop('selected', false);
|
||||
}
|
||||
// Look for a match, and toggle the selected property if there is a match.
|
||||
@ -452,11 +438,11 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
});
|
||||
// Rerender the selection list.
|
||||
updateSelectionList(selectionId, inputId, originalSelect, multiple);
|
||||
updateSelectionList(options, state, originalSelect);
|
||||
// Clear the input element.
|
||||
inputElement.val('');
|
||||
// Close the list of suggestions.
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
closeSuggestions(state);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -465,22 +451,18 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
* @method updateAjax
|
||||
* @private
|
||||
* @param {Event} e The event that triggered this update.
|
||||
* @param {String} selector The selector pointing to the original select.
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {Object} options The original options for the autocomplete.
|
||||
* @param {Object} state The state variables for the autocomplete.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {Object} ajaxHandler This is a module that does the ajax fetch and translates the results.
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
*/
|
||||
var updateAjax = function(e, selector, inputId, suggestionsId, originalSelect, multiple, tags, ajaxHandler, caseSensitive) {
|
||||
var updateAjax = function(e, options, state, originalSelect, ajaxHandler) {
|
||||
// Get the query to pass to the ajax function.
|
||||
var query = $(e.currentTarget).val();
|
||||
// Call the transport function to do the ajax (name taken from Select2).
|
||||
ajaxHandler.transport(selector, query, function(results) {
|
||||
ajaxHandler.transport(options.selector, query, function(results) {
|
||||
// We got a result - pass it through the translator before using it.
|
||||
var processedResults = ajaxHandler.processResults(selector, results);
|
||||
var processedResults = ajaxHandler.processResults(options.selector, results);
|
||||
var existingValues = [];
|
||||
|
||||
// Now destroy all options that are not currently selected.
|
||||
@ -502,7 +484,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
});
|
||||
// Update the list of suggestions now from the new values in the select list.
|
||||
updateSuggestions('', inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
updateSuggestions(options, state, '', originalSelect);
|
||||
}, notification.exception);
|
||||
};
|
||||
|
||||
@ -511,93 +493,60 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*
|
||||
* @method addNavigation
|
||||
* @private
|
||||
* @param {String} inputId The id of the input element for this instance of the autocomplete.
|
||||
* @param {String} suggestionsId The id of the suggestions element for this instance of the autocomplete.
|
||||
* @param {String} downArrowId The id of arrow to open the suggestions list.
|
||||
* @param {String} selectionId The id of element that shows the current selections.
|
||||
* @param {Object} options The options used to create this autocomplete element.
|
||||
* @param {Object} state State variables for this autocomplete element.
|
||||
* @param {JQuery} originalSelect The JQuery object matching the hidden select list.
|
||||
* @param {Boolean} multiple Are multiple items allowed to be selected?
|
||||
* @param {Boolean} tags Are we allowed to create new items on the fly?
|
||||
* @param {String} selector The selector for this select list.
|
||||
* @param {String} ajax Name of an AMD module to handle ajax requests. If specified, the AMD
|
||||
* module must expose 2 functions "transport" and "processResults".
|
||||
* @param {Boolean} caseSensitive - If search has to be made case sensitive.
|
||||
* @param {Boolean} showSuggestions - Auto complete suggestions can be disabled completely.
|
||||
*/
|
||||
var addNavigation = function(inputId,
|
||||
suggestionsId,
|
||||
downArrowId,
|
||||
selectionId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
selector,
|
||||
ajax,
|
||||
caseSensitive,
|
||||
showSuggestions) {
|
||||
var addNavigation = function(options, state, originalSelect) {
|
||||
// Start with the input element.
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
// Add keyboard nav with keydown.
|
||||
inputElement.on('keydown', function(e) {
|
||||
switch (e.keyCode) {
|
||||
case KEYS.DOWN:
|
||||
// If the suggestion list is open, move to the next item.
|
||||
if (!showSuggestions) {
|
||||
if (!options.showSuggestions) {
|
||||
// Do not consume this event.
|
||||
return true;
|
||||
} else if (inputElement.attr('aria-expanded') === "true") {
|
||||
activateNextItem(inputId, suggestionsId);
|
||||
activateNextItem(state);
|
||||
} else {
|
||||
// Handle ajax population of suggestions.
|
||||
if (!inputElement.val() && ajax) {
|
||||
require([ajax], function(ajaxHandler) {
|
||||
updateAjax(e,
|
||||
selector,
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
ajaxHandler,
|
||||
caseSensitive);
|
||||
if (!inputElement.val() && options.ajax) {
|
||||
require([options.ajax], function(ajaxHandler) {
|
||||
updateAjax(e, options, state, originalSelect, ajaxHandler);
|
||||
});
|
||||
} else {
|
||||
// Else - open the suggestions list.
|
||||
updateSuggestions(inputElement.val(),
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
caseSensitive);
|
||||
updateSuggestions(options, state, inputElement.val(), originalSelect);
|
||||
}
|
||||
}
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
return false;
|
||||
case KEYS.COMMA:
|
||||
if (tags) {
|
||||
if (options.tags) {
|
||||
// If we are allowing tags, comma should create a tag (or enter).
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
createItem(options, state, originalSelect);
|
||||
}
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
return false;
|
||||
case KEYS.UP:
|
||||
// Choose the previous active item.
|
||||
activatePreviousItem(inputId, suggestionsId);
|
||||
activatePreviousItem(state);
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
return false;
|
||||
case KEYS.ENTER:
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
if ((inputElement.attr('aria-expanded') === "true") &&
|
||||
(suggestionsElement.children('[aria-selected=true]').length > 0)) {
|
||||
// If the suggestion list has an active item, select it.
|
||||
selectCurrentItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
} else if (tags) {
|
||||
selectCurrentItem(options, state, originalSelect);
|
||||
} else if (options.tags) {
|
||||
// If tags are enabled, create a tag.
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
createItem(options, state, originalSelect);
|
||||
}
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
@ -605,7 +554,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
case KEYS.ESCAPE:
|
||||
if (inputElement.attr('aria-expanded') === "true") {
|
||||
// If the suggestion list is open, close it.
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
closeSuggestions(state);
|
||||
}
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
@ -615,8 +564,8 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
});
|
||||
// Handler used to force set the value from behat.
|
||||
inputElement.on('behat:set-value', function() {
|
||||
if (tags) {
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
if (options.tags) {
|
||||
createItem(options, state, originalSelect);
|
||||
}
|
||||
});
|
||||
inputElement.on('blur focus', function(e) {
|
||||
@ -628,15 +577,15 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
}
|
||||
closeSuggestionsTimer = window.setTimeout(function() {
|
||||
if (e.type == 'blur') {
|
||||
if (tags) {
|
||||
createItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
if (options.tags) {
|
||||
createItem(options, state, originalSelect);
|
||||
}
|
||||
closeSuggestions(inputId, suggestionsId, selectionId);
|
||||
closeSuggestions(state);
|
||||
}
|
||||
}, 500);
|
||||
});
|
||||
if (showSuggestions) {
|
||||
var arrowElement = $(document.getElementById(downArrowId));
|
||||
if (options.showSuggestions) {
|
||||
var arrowElement = $(document.getElementById(state.downArrowId));
|
||||
arrowElement.on('click', function() {
|
||||
// Prevent the close timer, or we will open, then close the suggestions.
|
||||
inputElement.focus();
|
||||
@ -644,52 +593,52 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
window.clearTimeout(closeSuggestionsTimer);
|
||||
}
|
||||
// Show the suggestions list.
|
||||
updateSuggestions(inputElement.val(), inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
updateSuggestions(options, state, inputElement.val(), originalSelect);
|
||||
});
|
||||
}
|
||||
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
suggestionsElement.parent().on('click', '[role=option]', function(e) {
|
||||
// Handle clicks on suggestions.
|
||||
var element = $(e.currentTarget).closest('[role=option]');
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Find the index of the clicked on suggestion.
|
||||
var current = suggestionsElement.children('[aria-hidden=false]').index(element);
|
||||
// Activate it.
|
||||
activateItem(current, inputId, suggestionsId);
|
||||
activateItem(current, state);
|
||||
// And select it.
|
||||
selectCurrentItem(inputId, suggestionsId, selectionId, multiple, originalSelect);
|
||||
selectCurrentItem(options, state, originalSelect);
|
||||
});
|
||||
var selectionElement = $(document.getElementById(selectionId));
|
||||
var selectionElement = $(document.getElementById(state.selectionId));
|
||||
// Handle clicks on the selected items (will unselect an item).
|
||||
selectionElement.on('click', '[role=listitem]', function(e) {
|
||||
// Get the item that was clicked.
|
||||
var item = $(e.currentTarget);
|
||||
// Remove it from the selection.
|
||||
deselectItem(inputId, selectionId, item, originalSelect, multiple);
|
||||
deselectItem(options, state, item, originalSelect);
|
||||
});
|
||||
// Keyboard navigation for the selection list.
|
||||
selectionElement.on('keydown', function(e) {
|
||||
switch (e.keyCode) {
|
||||
case KEYS.DOWN:
|
||||
// Choose the next selection item.
|
||||
activateNextSelection(selectionId);
|
||||
activateNextSelection(state);
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
return false;
|
||||
case KEYS.UP:
|
||||
// Choose the previous selection item.
|
||||
activatePreviousSelection(selectionId);
|
||||
activatePreviousSelection(state);
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
return false;
|
||||
case KEYS.SPACE:
|
||||
case KEYS.ENTER:
|
||||
// Get the item that is currently selected.
|
||||
var selectedItem = $(document.getElementById(selectionId)).children('[data-active-selection=true]');
|
||||
var selectedItem = $(document.getElementById(state.selectionId)).children('[data-active-selection=true]');
|
||||
if (selectedItem) {
|
||||
// Unselect this item.
|
||||
deselectItem(inputId, selectionId, selectedItem, originalSelect, multiple);
|
||||
deselectItem(options, state, selectedItem, originalSelect);
|
||||
// We handled this event, so prevent it.
|
||||
e.preventDefault();
|
||||
}
|
||||
@ -698,7 +647,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
return true;
|
||||
});
|
||||
// Whenever the input field changes, update the suggestion list.
|
||||
if (showSuggestions) {
|
||||
if (options.showSuggestions) {
|
||||
inputElement.on('input', function(e) {
|
||||
var query = $(e.currentTarget).val();
|
||||
var last = $(e.currentTarget).data('last-value');
|
||||
@ -709,7 +658,7 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
// We need to only do this for real value changed events or the suggestions will be
|
||||
// unclickable on IE11 (because they will be rebuilt before the click event fires).
|
||||
if (last != query) {
|
||||
updateSuggestions(query, inputId, suggestionsId, originalSelect, multiple, tags, caseSensitive);
|
||||
updateSuggestions(options, state, query, originalSelect);
|
||||
$(e.currentTarget).data('last-value', query);
|
||||
}
|
||||
});
|
||||
@ -732,17 +681,25 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
*/
|
||||
enhance: function(selector, tags, ajax, placeholder, caseSensitive, showSuggestions) {
|
||||
// Set some default values.
|
||||
if (typeof tags === "undefined") {
|
||||
tags = false;
|
||||
var options = {
|
||||
selector: selector,
|
||||
tags: false,
|
||||
ajax: false,
|
||||
placeholder: placeholder,
|
||||
caseSensitive: false,
|
||||
showSuggestions: true
|
||||
};
|
||||
if (typeof tags !== "undefined") {
|
||||
options.tags = tags;
|
||||
}
|
||||
if (typeof ajax === "undefined") {
|
||||
ajax = false;
|
||||
if (typeof ajax !== "undefined") {
|
||||
options.ajax = ajax;
|
||||
}
|
||||
if (typeof caseSensitive === "undefined") {
|
||||
caseSensitive = false;
|
||||
if (typeof caseSensitive !== "undefined") {
|
||||
options.caseSensitive = caseSensitive;
|
||||
}
|
||||
if (typeof showSuggestions === "undefined") {
|
||||
showSuggestions = true;
|
||||
if (typeof showSuggestions !== "undefined") {
|
||||
options.showSuggestions = showSuggestions;
|
||||
}
|
||||
|
||||
// Look for the select element.
|
||||
@ -756,39 +713,30 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
originalSelect.hide().attr('aria-hidden', true);
|
||||
|
||||
// Find or generate some ids.
|
||||
var selectId = originalSelect.attr('id');
|
||||
var multiple = originalSelect.attr('multiple');
|
||||
var inputId = 'form_autocomplete_input-' + $.now();
|
||||
var suggestionsId = 'form_autocomplete_suggestions-' + $.now();
|
||||
var selectionId = 'form_autocomplete_selection-' + $.now();
|
||||
var downArrowId = 'form_autocomplete_downarrow-' + $.now();
|
||||
var state = {
|
||||
selectId: originalSelect.attr('id'),
|
||||
inputId: 'form_autocomplete_input-' + $.now(),
|
||||
suggestionsId: 'form_autocomplete_suggestions-' + $.now(),
|
||||
selectionId: 'form_autocomplete_selection-' + $.now(),
|
||||
downArrowId: 'form_autocomplete_downarrow-' + $.now()
|
||||
};
|
||||
options.multiple = originalSelect.attr('multiple');
|
||||
|
||||
var originalLabel = $('[for=' + selectId + ']');
|
||||
var originalLabel = $('[for=' + state.selectId + ']');
|
||||
// Create the new markup and insert it after the select.
|
||||
var options = [];
|
||||
var suggestions = [];
|
||||
originalSelect.children('option').each(function(index, option) {
|
||||
options[index] = { label: option.innerHTML, value: $(option).attr('value') };
|
||||
suggestions[index] = { label: option.innerHTML, value: $(option).attr('value') };
|
||||
});
|
||||
|
||||
// Render all the parts of our UI.
|
||||
var renderInput = templates.render(
|
||||
'core/form_autocomplete_input',
|
||||
{ downArrowId: downArrowId,
|
||||
inputId: inputId,
|
||||
suggestionsId: suggestionsId,
|
||||
selectionId: selectionId,
|
||||
placeholder: placeholder,
|
||||
multiple: multiple,
|
||||
showSuggestions: showSuggestions }
|
||||
);
|
||||
var renderDatalist = templates.render(
|
||||
'core/form_autocomplete_suggestions',
|
||||
{ inputId: inputId, suggestionsId: suggestionsId, options: options, multiple: multiple}
|
||||
);
|
||||
var renderSelection = templates.render(
|
||||
'core/form_autocomplete_selection',
|
||||
{ selectionId: selectionId, items: [], multiple: multiple}
|
||||
);
|
||||
var context = $.extend({}, options, state);
|
||||
context.options = suggestions;
|
||||
context.items = [];
|
||||
|
||||
var renderInput = templates.render('core/form_autocomplete_input', context);
|
||||
var renderDatalist = templates.render('core/form_autocomplete_suggestions', context);
|
||||
var renderSelection = templates.render('core/form_autocomplete_selection', context);
|
||||
|
||||
$.when(renderInput, renderDatalist, renderSelection).done(function(input, suggestions, selection) {
|
||||
// Add our new UI elements to the page.
|
||||
@ -796,47 +744,29 @@ define(['jquery', 'core/log', 'core/str', 'core/templates', 'core/notification']
|
||||
originalSelect.after(input);
|
||||
originalSelect.after(selection);
|
||||
// Update the form label to point to the text input.
|
||||
originalLabel.attr('for', inputId);
|
||||
originalLabel.attr('for', state.inputId);
|
||||
// Add the event handlers.
|
||||
addNavigation(inputId,
|
||||
suggestionsId,
|
||||
downArrowId,
|
||||
selectionId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
selector,
|
||||
ajax,
|
||||
caseSensitive,
|
||||
showSuggestions);
|
||||
addNavigation(options, state, originalSelect);
|
||||
|
||||
var inputElement = $(document.getElementById(inputId));
|
||||
var suggestionsElement = $(document.getElementById(suggestionsId));
|
||||
var inputElement = $(document.getElementById(state.inputId));
|
||||
var suggestionsElement = $(document.getElementById(state.suggestionsId));
|
||||
// Hide the suggestions by default.
|
||||
suggestionsElement.hide().attr('aria-hidden', true);
|
||||
|
||||
// If this field uses ajax, set it up.
|
||||
if (ajax) {
|
||||
require([ajax], function(ajaxHandler) {
|
||||
if (options.ajax) {
|
||||
require([options.ajax], function(ajaxHandler) {
|
||||
var handler = function(e) {
|
||||
updateAjax(e,
|
||||
selector,
|
||||
inputId,
|
||||
suggestionsId,
|
||||
originalSelect,
|
||||
multiple,
|
||||
tags,
|
||||
ajaxHandler,
|
||||
caseSensitive);
|
||||
updateAjax(e, options, state, originalSelect, ajaxHandler);
|
||||
};
|
||||
// Trigger an ajax update after the text field value changes.
|
||||
inputElement.on("input keypress", handler);
|
||||
var arrowElement = $(document.getElementById(downArrowId));
|
||||
var arrowElement = $(document.getElementById(state.downArrowId));
|
||||
arrowElement.on("click", handler);
|
||||
});
|
||||
}
|
||||
// Show the current values in the selection list.
|
||||
updateSelectionList(selectionId, inputId, originalSelect, multiple);
|
||||
updateSelectionList(options, state, originalSelect);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user