uber_select_rails 0.6.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/uber_select_rails/version.rb +1 -1
- data/vendor/assets/javascript/uber_select/README.md +28 -7
- data/vendor/assets/javascript/uber_select/javascript/jquery.uber-select.js +12 -6
- data/vendor/assets/javascript/uber_select/javascript/uber_search/list.js +160 -0
- data/vendor/assets/javascript/uber_select/javascript/uber_search/output_container.js +29 -0
- data/vendor/assets/javascript/uber_select/javascript/uber_search/pane.js +93 -0
- data/vendor/assets/javascript/uber_select/javascript/uber_search/search.js +77 -0
- data/vendor/assets/javascript/uber_select/javascript/uber_search/search_field.js +96 -0
- data/vendor/assets/javascript/uber_select/javascript/{search.js → uber_search/search_model.js} +11 -69
- data/vendor/assets/javascript/uber_select/javascript/uber_search.js +378 -309
- data/vendor/assets/javascript/uber_select/test.css +1 -0
- data/vendor/assets/javascript/uber_select/test.html +26 -8
- data/vendor/assets/javascript/uber_select.js +1 -1
- metadata +9 -13
- data/vendor/assets/javascript/uber_select/javascript/list.js +0 -122
- data/vendor/assets/javascript/uber_select/javascript/output_container.js +0 -25
- data/vendor/assets/javascript/uber_select/javascript/pane.js +0 -89
- data/vendor/assets/javascript/uber_select/javascript/search_field.js +0 -84
- data/vendor/assets/javascript/uber_select/javascript/string_extensions.js +0 -8
@@ -1,387 +1,456 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
value: null, // Initialize with this selectedValue
|
12
|
-
disabled: false, // Initialize with this disabled value
|
13
|
-
search: true, // Show the search input
|
14
|
-
clearSearchButton:'✕', // Text content of clear search button
|
15
|
-
selectCaret: '⌄', // Text content of select caret
|
16
|
-
hideBlankOption: false, // Should blank options be hidden automatically?
|
17
|
-
treatBlankOptionAsPlaceholder: false, // Should blank options use the placeholder as text?
|
18
|
-
highlightByDefault: true, // Should the first result be auto-highlighted?
|
19
|
-
minQueryLength: 0, // Number of characters to type before results are displayed
|
20
|
-
minQueryMessage: true, // Message to show when the query doesn't exceed the minimum length. True for default, false for none, or custom message.
|
21
|
-
placeholder: null, // Placeholder to show in the selected text area
|
22
|
-
searchPlaceholder: 'Type to search', // Placeholder to show in the search input
|
23
|
-
noResultsText: 'No Matches Found', // The message shown when there are no results
|
24
|
-
resultPostprocessor: function(result, datum){}, // A function that is run after a result is built and can be used to decorate it
|
25
|
-
buildResult: null, // A function that is used to build result elements
|
26
|
-
outputContainer: null, // An object that receives the output once a result is selected. Must respond to setValue(value), and view()
|
27
|
-
onRender: function(resultsContainer, result) {}, // A function to run when the results container is rendered. If the result returns false, the default select handler is not run and the event is cancelled
|
28
|
-
onSelect: function(datum, result, clickEvent) {}, // A function to run when a result is selected. If the result returns false, the default select handler is not run and the event is cancelled
|
29
|
-
onNoHighlightSubmit: function(value) {}, // A function to run when a user presses enter without selecting a result.
|
30
|
-
noDataText: 'No options' // Text to show in there is nothing in the set of data to pick from
|
31
|
-
}, options)
|
32
|
-
|
33
|
-
var context = this
|
34
|
-
var view = $('<span class="uber_select" role="listbox"></span>')
|
35
|
-
var selectedValue = options.value // Internally selected value
|
36
|
-
var outputContainer = options.outputContainer || new OutputContainer({selectCaret: options.selectCaret})
|
37
|
-
var resultsContainer = $('<div class="results_container"></div>')
|
38
|
-
var messages = $('<div class="messages"></div>')
|
39
|
-
var pane = new Pane()
|
40
|
-
|
41
|
-
if (options.ariaLabel) { view.attr("aria-label", options.ariaLabel) }
|
42
|
-
|
43
|
-
var searchField = new SearchField({
|
44
|
-
placeholder: options.searchPlaceholder,
|
45
|
-
clearButton: options.clearSearchButton,
|
46
|
-
searchInputAttributes: options.searchInputAttributes
|
47
|
-
})
|
48
|
-
|
49
|
-
var search = new Search(searchField.input, resultsContainer, {
|
50
|
-
model: {
|
51
|
-
dataForMatching: dataForMatching,
|
52
|
-
minQueryLength: options.minQueryLength,
|
53
|
-
queryPreprocessor: options.queryPreprocessor || Search.prototype.queryPreprocessor,
|
54
|
-
datumPreprocessor: options.datumPreprocessor || datumPreprocessor,
|
55
|
-
patternForMatching: options.patternForMatching || Search.prototype.patternForMatching
|
56
|
-
},
|
57
|
-
view: {
|
58
|
-
renderResults: renderResults,
|
59
|
-
buildResult: options.buildResult || buildResult,
|
60
|
-
keypressInput: options.search ? searchField.input : null
|
1
|
+
//= require_self
|
2
|
+
//= require_tree ./uber_search
|
3
|
+
|
4
|
+
(function($) {
|
5
|
+
window.UberSearch = function(data, options){
|
6
|
+
var eventsTriggered = {
|
7
|
+
shown: 'shown',
|
8
|
+
renderedResults: 'renderedResults',
|
9
|
+
clear: 'clear',
|
10
|
+
select: 'select'
|
61
11
|
}
|
62
|
-
})
|
63
12
|
|
13
|
+
options = $.extend({
|
14
|
+
uberSelectId: generateUUID(), // A unique identifier for select
|
15
|
+
ariaLabel: null, // Label of the select for screen readers
|
16
|
+
value: null, // Initialize with this selectedValue
|
17
|
+
disabled: false, // Initialize with this disabled value
|
18
|
+
search: true, // Show the search input
|
19
|
+
clearSearchButton:'✕', // Text content of clear search button
|
20
|
+
selectCaret: '⌄', // Text content of select caret
|
21
|
+
hideBlankOption: false, // Should blank options be hidden automatically?
|
22
|
+
treatBlankOptionAsPlaceholder: false, // Should blank options use the placeholder as text?
|
23
|
+
highlightByDefault: true, // Should the first result be auto-highlighted?
|
24
|
+
minQueryLength: 0, // Number of characters to type before results are displayed
|
25
|
+
minQueryMessage: true, // Message to show when the query doesn't exceed the minimum length. True for default, false for none, or custom message.
|
26
|
+
placeholder: null, // Placeholder to show in the selected text area
|
27
|
+
searchPlaceholder: 'Type to search', // Placeholder to show in the search input
|
28
|
+
noResultsText: 'No Matches Found', // The message shown when there are no results
|
29
|
+
resultPostprocessor: function(result, datum){}, // A function that is run after a result is built and can be used to decorate it
|
30
|
+
buildResult: null, // A function that is used to build result elements
|
31
|
+
outputContainer: null, // An object that receives the output once a result is selected. Must respond to setValue(value), and view()
|
32
|
+
onRender: function(resultsContainer, result) {}, // A function to run when the results container is rendered. If the result returns false, the default render handler is not run and the event is cancelled
|
33
|
+
onSelect: function(datum, result, clickEvent) {}, // A function to run when a result is selected. If the result returns false, the default select handler is not run and the event is cancelled
|
34
|
+
onNoHighlightSubmit: function(value) {}, // A function to run when a user presses enter without selecting a result.
|
35
|
+
noDataText: 'No options', // Text to show in there is nothing in the set of data to pick from
|
36
|
+
matchGroupNames: false, // Show results for searches that match the result's group name
|
37
|
+
alwaysOpen: false // Should the options list always appear open?
|
38
|
+
}, options)
|
39
|
+
|
40
|
+
var context = this
|
41
|
+
var view = $('<span>', { class: "uber_select", id: options.uberSelectId })
|
42
|
+
var selectedValue = options.value // Internally selected value
|
43
|
+
var outputContainer = options.outputContainer || new UberSearch.OutputContainer({selectCaret: options.selectCaret, ariaLabel: options.ariaLabel})
|
44
|
+
var resultsContainer = $('<div class="results_container"></div>')
|
45
|
+
var messages = $('<div class="messages"></div>')
|
46
|
+
var pane = new UberSearch.Pane()
|
47
|
+
|
48
|
+
var searchField = new UberSearch.SearchField({
|
49
|
+
placeholder: options.searchPlaceholder,
|
50
|
+
clearButton: options.clearSearchButton,
|
51
|
+
searchInputAttributes: options.searchInputAttributes
|
52
|
+
})
|
64
53
|
|
65
|
-
|
54
|
+
var search = new UberSearch.Search(searchField.input, resultsContainer, {
|
55
|
+
model: {
|
56
|
+
dataForMatching: dataForMatching,
|
57
|
+
minQueryLength: options.minQueryLength,
|
58
|
+
queryPreprocessor: options.queryPreprocessor || UberSearch.Search.prototype.queryPreprocessor,
|
59
|
+
datumPreprocessor: options.datumPreprocessor || datumPreprocessor,
|
60
|
+
patternForMatching: options.patternForMatching || UberSearch.Search.prototype.patternForMatching
|
61
|
+
},
|
62
|
+
view: {
|
63
|
+
renderResults: renderResults,
|
64
|
+
buildResult: options.buildResult || buildResult,
|
65
|
+
}
|
66
|
+
})
|
66
67
|
|
67
|
-
// Show the pane when the select element is clicked
|
68
|
-
$(outputContainer.view).on('click', function(event){
|
69
|
-
if (outputContainer.view.hasClass('disabled')) { return }
|
70
68
|
|
71
|
-
|
72
|
-
})
|
69
|
+
// BEHAVIOUR
|
73
70
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
71
|
+
// Hide the pane when clicked out or another pane is opened
|
72
|
+
$(document).on('click shown', function(event){
|
73
|
+
if (!options.alwaysOpen && pane.isOpen() && isEventOutside(event)){
|
74
|
+
pane.hide()
|
75
|
+
}
|
76
|
+
})
|
80
77
|
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
// Hide the pane when tabbing away from view
|
79
|
+
$(view).on('keydown', function(event){
|
80
|
+
if (!options.alwaysOpen && pane.isOpen() && event.which === 9) {
|
81
|
+
pane.hide()
|
82
|
+
}
|
83
|
+
})
|
84
84
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
85
|
+
$(view).on('setHighlight', function(event, result, index) {
|
86
|
+
if (index < 0 && options.search) {
|
87
|
+
setOutputContainerAria("aria-activedescendant", "")
|
88
|
+
$(searchField.input).focus()
|
89
|
+
} else if (index < 0) {
|
90
|
+
setOutputContainerAria("aria-activedescendant", "")
|
91
|
+
$(outputContainer.view).focus()
|
92
|
+
} else {
|
93
|
+
setOutputContainerAria("aria-activedescendant", result.id)
|
94
|
+
}
|
95
|
+
})
|
94
96
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
markSelected()
|
99
|
-
view.addClass('open')
|
97
|
+
$(view).on('inputDownArrow', function(event) {
|
98
|
+
search.stepHighlight(1)
|
99
|
+
})
|
100
100
|
|
101
|
-
|
102
|
-
|
103
|
-
}
|
104
|
-
pane.view.find("ul.results li:first").focus()
|
105
|
-
}
|
101
|
+
$(view).on('inputUpArrow', function(event) {
|
102
|
+
outputContainer.view.focus()
|
103
|
+
})
|
106
104
|
|
107
|
-
|
108
|
-
|
105
|
+
// Show the pane if the user was tabbed onto the trigger and pressed enter, space, or down arrow
|
106
|
+
$(outputContainer.view).on('keydown', function(event){
|
107
|
+
if (outputContainer.view.hasClass('disabled')) { return }
|
108
|
+
|
109
|
+
if (event.which === 40) { // open and focus pane when down key is pressed
|
110
|
+
if (pane.isClosed()) {
|
111
|
+
pane.show()
|
112
|
+
} else if (options.search) {
|
113
|
+
$(searchField.input).focus()
|
114
|
+
} else {
|
115
|
+
search.stepHighlight(1)
|
116
|
+
}
|
117
|
+
return false
|
118
|
+
}
|
109
119
|
|
120
|
+
if (event.which === 32 || event.which === 13){ // toggle pane when space or enter is pressed
|
121
|
+
pane.toggle()
|
122
|
+
return false
|
123
|
+
}
|
124
|
+
})
|
110
125
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
view.focus()
|
115
|
-
})
|
126
|
+
// Show the pane when the select element is clicked
|
127
|
+
$(outputContainer.view).on('click', function(event){
|
128
|
+
if (outputContainer.view.hasClass('disabled')) { return }
|
116
129
|
|
117
|
-
|
118
|
-
|
119
|
-
updateMessages()
|
120
|
-
})
|
130
|
+
pane.show()
|
131
|
+
})
|
121
132
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
133
|
+
// When the pane is opened
|
134
|
+
$(pane).on('shown', function(){
|
135
|
+
setOutputContainerAria('aria-expanded', true)
|
136
|
+
search.clear()
|
137
|
+
markSelected(true)
|
138
|
+
view.addClass('open')
|
128
139
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
})
|
140
|
+
if (options.search) {
|
141
|
+
$(searchField.input).focus()
|
142
|
+
}
|
133
143
|
|
134
|
-
|
135
|
-
|
136
|
-
triggerEvent(eventsTriggered.clear)
|
137
|
-
})
|
144
|
+
triggerEvent(eventsTriggered.shown)
|
145
|
+
})
|
138
146
|
|
139
|
-
|
140
|
-
|
141
|
-
|
147
|
+
// When the pane is hidden
|
148
|
+
$(pane).on('hidden', function(){
|
149
|
+
setOutputContainerAria('aria-expanded', false)
|
150
|
+
view.removeClass('open')
|
151
|
+
view.focus()
|
152
|
+
})
|
142
153
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
}
|
154
|
+
// When the query is changed
|
155
|
+
$(search).on('queryChanged', function(){
|
156
|
+
updateMessages()
|
157
|
+
})
|
147
158
|
|
148
|
-
|
159
|
+
// When the search results are rendered
|
160
|
+
$(search).on('renderedResults', function(event){
|
161
|
+
if (options.onRender(resultsContainer, getSelection()) === false) {
|
162
|
+
event.stopPropagation()
|
163
|
+
return
|
164
|
+
}
|
149
165
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
166
|
+
markSelected()
|
167
|
+
updateMessages()
|
168
|
+
triggerEvent(eventsTriggered.renderedResults)
|
169
|
+
})
|
154
170
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
171
|
+
// When the search field is cleared
|
172
|
+
$(searchField).on('clear', function(){
|
173
|
+
triggerEvent(eventsTriggered.clear)
|
174
|
+
})
|
159
175
|
|
176
|
+
// When a search result is chosen
|
177
|
+
resultsContainer.on('click', '.result:not(.disabled)', function(event){
|
178
|
+
var datum = $(this).data()
|
160
179
|
|
161
|
-
|
180
|
+
if (options.onSelect(datum, this, event) === false) {
|
181
|
+
event.stopPropagation()
|
182
|
+
return
|
183
|
+
}
|
162
184
|
|
163
|
-
|
164
|
-
setData(data)
|
185
|
+
event.stopPropagation();
|
165
186
|
|
166
|
-
|
167
|
-
|
168
|
-
|
187
|
+
setValue(valueFromResult(this))
|
188
|
+
if (!options.alwaysOpen) {
|
189
|
+
pane.hide()
|
190
|
+
}
|
191
|
+
triggerEvent(eventsTriggered.select, [datum, this, event])
|
192
|
+
})
|
169
193
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
194
|
+
// When query is submitted
|
195
|
+
$(searchField.input).on('noHighlightSubmit', function(event) {
|
196
|
+
options.onNoHighlightSubmit($(this).val())
|
197
|
+
})
|
174
198
|
|
175
|
-
$(view).append(pane.view)
|
176
199
|
|
177
|
-
|
178
|
-
updateSelectedText()
|
179
|
-
markSelected()
|
200
|
+
// INITIALIZATION
|
180
201
|
|
202
|
+
setDisabled(options.disabled)
|
203
|
+
setData(data)
|
181
204
|
|
182
|
-
|
205
|
+
if (options.search) { pane.addContent('search', searchField.view) }
|
206
|
+
pane.addContent('messages', messages)
|
207
|
+
pane.addContent('results', resultsContainer)
|
183
208
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
209
|
+
// If the output container isn't in the DOM yet, add it
|
210
|
+
if (!$(outputContainer.view).closest('body').length){
|
211
|
+
$(outputContainer.view).appendTo(view)
|
212
|
+
}
|
213
|
+
|
214
|
+
$(view).append(pane.view)
|
190
215
|
|
191
|
-
|
192
|
-
function setValue(value){
|
193
|
-
if (selectedValue == value) { return }
|
194
|
-
selectedValue = value
|
216
|
+
updateMessages()
|
195
217
|
updateSelectedText()
|
196
218
|
markSelected()
|
197
|
-
}
|
198
219
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
}
|
220
|
+
if (options.alwaysOpen) {
|
221
|
+
pane.show()
|
222
|
+
}
|
203
223
|
|
204
|
-
// Enables or disables UberSearch
|
205
|
-
function setDisabled(boolean){
|
206
|
-
outputContainer.setDisabled(boolean)
|
207
|
-
}
|
208
224
|
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
225
|
+
// HELPER FUNCTIONS
|
226
|
+
function generateUUID() {
|
227
|
+
// https://www.w3resource.com/javascript-exercises/javascript-math-exercise-23.php
|
228
|
+
var dt = new Date().getTime();
|
229
|
+
var uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
230
|
+
var r = (dt + Math.random()*16)%16 | 0;
|
231
|
+
dt = Math.floor(dt/16);
|
232
|
+
return (c=='x' ? r :(r&0x3|0x8)).toString(16);
|
233
|
+
});
|
234
|
+
return uuid;
|
215
235
|
}
|
216
|
-
}
|
217
236
|
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
237
|
+
function setData(newData){
|
238
|
+
data = setDataDefaults(newData)
|
239
|
+
search.setData(data)
|
240
|
+
updateSelectedText()
|
241
|
+
markSelected()
|
242
|
+
}
|
224
243
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
} else {
|
232
|
-
return $.map(data, function(datum){ if (datum.visibility != 'query' || datum.value == selectedValue) return datum })
|
244
|
+
// Selects the result corresponding to the given value
|
245
|
+
function setValue(value){
|
246
|
+
if (selectedValue == value) { return }
|
247
|
+
selectedValue = value
|
248
|
+
updateSelectedText()
|
249
|
+
markSelected()
|
233
250
|
}
|
234
|
-
}
|
235
251
|
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
252
|
+
// Returns the selected value
|
253
|
+
function getValue(){
|
254
|
+
return selectedValue
|
255
|
+
}
|
256
|
+
|
257
|
+
// Enables or disables UberSearch
|
258
|
+
function setDisabled(boolean){
|
259
|
+
outputContainer.setDisabled(boolean)
|
260
|
+
}
|
240
261
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
262
|
+
// Updates the enhanced select with the text of the selected result
|
263
|
+
function setSelectedText(text){
|
264
|
+
if (text) {
|
265
|
+
outputContainer.setValue(text)
|
266
|
+
} else {
|
267
|
+
outputContainer.setValue(options.placeholder)
|
268
|
+
}
|
269
|
+
}
|
245
270
|
|
246
|
-
|
247
|
-
|
248
|
-
|
271
|
+
// Inherit values for matchValue and value from text
|
272
|
+
function setDataDefaults(data){
|
273
|
+
return $.map(data, function(datum) {
|
274
|
+
return $.extend({ value: datum.text, matchValue: datum.text }, datum)
|
275
|
+
})
|
276
|
+
}
|
249
277
|
|
250
|
-
|
251
|
-
|
252
|
-
|
278
|
+
// Converts the dataFromSelect into a datum list for matching
|
279
|
+
function dataForMatching(processedQuery, data){
|
280
|
+
// If a query is present, include only select options that should be used when searching
|
281
|
+
// Else, include only options that should be visible when not searching
|
282
|
+
if (processedQuery) {
|
283
|
+
return $.map(data, function(datum){ if (datum.visibility != 'no-query' || datum.value == selectedValue) return datum })
|
284
|
+
} else {
|
285
|
+
return $.map(data, function(datum){ if (datum.visibility != 'query' || datum.value == selectedValue) return datum })
|
253
286
|
}
|
254
|
-
}
|
287
|
+
}
|
288
|
+
|
289
|
+
// Match against the datum.group and datum.matchValue
|
290
|
+
function datumPreprocessor(datum){
|
291
|
+
if (options.matchGroupNames && datum.group) {
|
292
|
+
return datum.group + " " + datum.matchValue
|
293
|
+
} else {
|
294
|
+
return datum.matchValue
|
295
|
+
}
|
296
|
+
}
|
255
297
|
|
256
|
-
//
|
257
|
-
|
298
|
+
// Adds group support and blank option hiding
|
299
|
+
function renderResults(data){
|
300
|
+
var context = this
|
301
|
+
var sourceArray = []
|
258
302
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
var groupNodes = reject(sourceArray, 'li[data-group="' + group + '"]')
|
263
|
-
var sublist = $('<ul class="sublist"></ul>').attr('data-group', group)
|
264
|
-
var sublistNode = $('<li></li>').append('<span class="sublist_name">' + group + '</span>')
|
303
|
+
$.each(data, function(index, datum){
|
304
|
+
// Add the group name so we can group items
|
305
|
+
var result = context.buildResult(index, datum).attr('data-group', datum.group)
|
265
306
|
|
266
|
-
|
267
|
-
|
307
|
+
// Omit blank option from results
|
308
|
+
if (!options.hideBlankOption || datum.value){
|
309
|
+
sourceArray.push(result)
|
310
|
+
}
|
311
|
+
})
|
268
312
|
|
269
|
-
|
270
|
-
|
313
|
+
// Arrange ungrouped list items
|
314
|
+
var destArray = reject(sourceArray, 'li:not([data-group])')
|
271
315
|
|
272
|
-
|
273
|
-
|
274
|
-
|
316
|
+
// Arrange list items into sub lists
|
317
|
+
while (sourceArray.length) {
|
318
|
+
var group = $(sourceArray[0]).attr('data-group')
|
319
|
+
var groupNodes = reject(sourceArray, 'li[data-group="' + group + '"]')
|
320
|
+
var sublist = $('<ul>', { class: "sublist", 'data-group': group })
|
321
|
+
var sublistNode = $('<li>', { role: "listitem", 'aria-label': group }).append($('<span>', { class: "sublist_name" }).html(group))
|
275
322
|
|
276
|
-
|
277
|
-
|
278
|
-
function reject(sourceArray, selector){
|
279
|
-
var dest = filter(sourceArray, selector)
|
280
|
-
var source = filter(sourceArray, selector, true)
|
281
|
-
sourceArray.splice(0, sourceArray.length)
|
282
|
-
sourceArray.push.apply(sourceArray, source)
|
283
|
-
return dest
|
284
|
-
}
|
323
|
+
sublist.append(groupNodes)
|
324
|
+
sublistNode.append(sublist)
|
285
325
|
|
286
|
-
|
287
|
-
|
288
|
-
}
|
326
|
+
destArray.push(sublistNode)
|
327
|
+
}
|
289
328
|
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
.data(datum) // Store the datum so we can get know what the value of the selected item is
|
329
|
+
this.view.toggleClass('empty', !destArray.length)
|
330
|
+
this.view.html(destArray)
|
331
|
+
}
|
294
332
|
|
295
|
-
|
296
|
-
|
333
|
+
// Removes elements from the sourcArray that match the selector
|
334
|
+
// Returns an array of removed elements
|
335
|
+
function reject(sourceArray, selector){
|
336
|
+
var dest = filter(sourceArray, selector)
|
337
|
+
var source = filter(sourceArray, selector, true)
|
338
|
+
sourceArray.splice(0, sourceArray.length)
|
339
|
+
sourceArray.push.apply(sourceArray, source)
|
340
|
+
return dest
|
341
|
+
}
|
297
342
|
|
298
|
-
|
343
|
+
function filter(sourceArray, selector, invert){
|
344
|
+
return $.grep(sourceArray, function(node){ return node.is(selector) }, invert)
|
345
|
+
}
|
299
346
|
|
300
|
-
|
301
|
-
|
347
|
+
function buildResult(index, datum){
|
348
|
+
var text = (options.treatBlankOptionAsPlaceholder ? datum.text || options.placeholder : datum.text);
|
302
349
|
|
303
|
-
|
304
|
-
|
305
|
-
|
350
|
+
var result = $('<li class="result" role="listitem" tabindex="-1"></li>') // Use -1 tabindex so that the result can be focusable but not tabbable.
|
351
|
+
.attr('id', (options.uberSelectId + "-" + index))
|
352
|
+
.text(text || String.fromCharCode(160)) // Insert text or
|
353
|
+
.data(datum) // Store the datum so we can get know what the value of the selected item is
|
306
354
|
|
307
|
-
|
355
|
+
if (datum.title) { result.attr('title', datum.title) }
|
356
|
+
if (datum.disabled) { result.addClass('disabled') }
|
308
357
|
|
309
|
-
|
310
|
-
$(selected).addClass('selected').removeClass('hidden')
|
358
|
+
options.resultPostprocessor(result, datum)
|
311
359
|
|
312
|
-
|
313
|
-
search.highlightResult(selected)
|
314
|
-
} else if (options.highlightByDefault) {
|
315
|
-
search.highlightResult(results.not('.hidden').not('.disabled').first())
|
360
|
+
return result
|
316
361
|
}
|
317
|
-
}
|
318
362
|
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
363
|
+
function markSelected(focus) {
|
364
|
+
focus = focus || false
|
365
|
+
var selected = getSelection()
|
366
|
+
var results = search.getResults()
|
367
|
+
|
368
|
+
$(results).filter('.selected').not(selected).removeClass('selected').attr('aria-selected', false)
|
369
|
+
|
370
|
+
// Ensure the selected result is unhidden
|
371
|
+
$(selected).addClass('selected').removeClass('hidden')
|
372
|
+
$(selected).attr('aria-selected', true)
|
373
|
+
|
374
|
+
if (selected) {
|
375
|
+
search.highlightResult(selected, { focus: focus })
|
376
|
+
} else if (options.highlightByDefault) {
|
377
|
+
search.highlightResult(results.not('.hidden').not('.disabled').first(), { focus: focus })
|
327
378
|
}
|
328
|
-
}
|
329
|
-
return selected
|
330
|
-
}
|
379
|
+
}
|
331
380
|
|
332
|
-
|
333
|
-
|
334
|
-
|
381
|
+
// Returns the selected element and its index
|
382
|
+
function getSelection(){
|
383
|
+
var results = search.getResults()
|
384
|
+
var selected
|
385
|
+
$.each(results, function(i, result){
|
386
|
+
if (selectedValue == valueFromResult(result)){
|
387
|
+
selected = result
|
388
|
+
return false
|
389
|
+
}
|
390
|
+
})
|
391
|
+
return selected
|
392
|
+
}
|
335
393
|
|
336
|
-
|
337
|
-
|
338
|
-
|
394
|
+
function valueFromResult(result){
|
395
|
+
return $(result).data('value')
|
396
|
+
}
|
397
|
+
|
398
|
+
function updateSelectedText(){
|
399
|
+
setSelectedText(textFromValue(selectedValue))
|
400
|
+
}
|
339
401
|
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
402
|
+
function textFromValue(value){
|
403
|
+
return $.map(data, function(datum) {
|
404
|
+
if (datum.value == value) {
|
405
|
+
return datum.selectedText || datum.text
|
406
|
+
}
|
407
|
+
})[0]
|
408
|
+
}
|
409
|
+
|
410
|
+
function updateMessages(){
|
411
|
+
messages.show()
|
412
|
+
if (!queryLength() && !resultsCount()){
|
413
|
+
messages.html(options.noDataText)
|
414
|
+
} else if (options.minQueryLength && options.minQueryMessage && queryLength() < options.minQueryLength){
|
415
|
+
messages.html(options.minQueryMessage === true ? 'Type at least ' + options.minQueryLength + (options.minQueryLength == 1 ? ' character' : ' characters') + ' to search' : options.minQueryMessage)
|
416
|
+
} else if (options.noResultsText && !resultsCount()){
|
417
|
+
messages.html(options.noResultsText)
|
418
|
+
} else {
|
419
|
+
messages.empty().hide()
|
344
420
|
}
|
345
|
-
}
|
346
|
-
}
|
421
|
+
}
|
347
422
|
|
348
|
-
|
349
|
-
|
350
|
-
if (!queryLength() && !resultsCount()){
|
351
|
-
messages.html(options.noDataText)
|
352
|
-
} else if (options.minQueryLength && options.minQueryMessage && queryLength() < options.minQueryLength){
|
353
|
-
messages.html(options.minQueryMessage === true ? 'Type at least ' + options.minQueryLength + (options.minQueryLength == 1 ? ' character' : ' characters') + ' to search' : options.minQueryMessage)
|
354
|
-
} else if (options.noResultsText && !resultsCount()){
|
355
|
-
messages.html(options.noResultsText)
|
356
|
-
} else {
|
357
|
-
messages.empty().hide()
|
423
|
+
function queryLength(){
|
424
|
+
return search.getQuery().length
|
358
425
|
}
|
359
|
-
}
|
360
426
|
|
361
|
-
|
362
|
-
|
363
|
-
|
427
|
+
function resultsCount(){
|
428
|
+
return search.getResults().length
|
429
|
+
}
|
364
430
|
|
365
|
-
|
366
|
-
|
367
|
-
|
431
|
+
function setOutputContainerAria() {
|
432
|
+
outputContainer.view.attr.apply(outputContainer.view, arguments)
|
433
|
+
}
|
368
434
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
}
|
435
|
+
// returns true if the event originated outside this component
|
436
|
+
function isEventOutside(event){
|
437
|
+
event = event.originalEvent || event // Handle both jQuery events and standard JS events
|
373
438
|
|
374
|
-
|
375
|
-
|
376
|
-
|
439
|
+
if (event.composed) { // Support UberSelect when used in the Shadow DOM
|
440
|
+
return !event.composedPath().includes(view[0])
|
441
|
+
} else {
|
442
|
+
return !$(event.target).closest(view).length
|
443
|
+
}
|
444
|
+
}
|
377
445
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
446
|
+
// Allow observer to be attached to the UberSearch itself
|
447
|
+
function triggerEvent(eventType, callbackArgs){
|
448
|
+
view.trigger(eventType, callbackArgs)
|
449
|
+
$(context).triggerHandler(eventType, callbackArgs)
|
450
|
+
}
|
383
451
|
|
384
|
-
|
452
|
+
// PUBLIC INTERFACE
|
385
453
|
|
386
|
-
|
387
|
-
}
|
454
|
+
$.extend(this, {view:view, searchField:searchField, setValue:setValue, getValue: getValue, setData:setData, setDisabled:setDisabled, getSelection:getSelection, options:options})
|
455
|
+
}
|
456
|
+
})(jQuery)
|