uber_select_rails 0.6.0 → 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 +381 -296
- 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 -100
- data/vendor/assets/javascript/uber_select/javascript/search_field.js +0 -84
- data/vendor/assets/javascript/uber_select/javascript/string_extensions.js +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4e848d17e956bbe5c52f9f7e299e47cc2cb296755ae8ce7cbf94877b5a4857ca
|
4
|
+
data.tar.gz: 87fdd8c564c2960b8d20015245aeb48b28b8c2ee7b7e12e42fd393eb49ebaeec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4500a0a600213b393da36f7676deee006ddd301f4f258d336017e2d0260182615da1a75ccda716f231049e357049a23a25b1b26a88597d844f39423babb202b7
|
7
|
+
data.tar.gz: 38d0c07a95368fcdaaa6d3338f21e572a846cf70142353e2a1ec365e6f4a9222c3ac060cd1cca6a4321b4aa1866b62421c3b1ab887e148a89801497975cda09b
|
@@ -24,15 +24,15 @@ you can use UberSelect to gussy up forms, without changing any of the underlying
|
|
24
24
|
$('.my_selects').uberSelect(options);
|
25
25
|
```
|
26
26
|
|
27
|
-
#### Attributes <a name="
|
27
|
+
#### Attributes <a name="uber-search-attributes"></a>
|
28
28
|
|
29
|
-
|
29
|
+
Attributes on the outermost element can be specified by setting the `data-uber-attributes` attribute on the `<select>` element. Values should be passed
|
30
30
|
as a JSON string of key/value pairs where the key is the attribute name and the value is the attribute value.
|
31
31
|
|
32
32
|
#### Options
|
33
33
|
|
34
34
|
Options can be specified by setting the `data-uber-options` attribute on the `<select>` element. Values should be passed
|
35
|
-
as a JSON string. All options on the are passed to the underlying UberSearch, see [UberSearch options](#
|
35
|
+
as a JSON string. All options on the are passed to the underlying UberSearch, see [UberSearch options](#uber-search-options).
|
36
36
|
|
37
37
|
- ##### prepopulateSearchOnOpen
|
38
38
|
|
@@ -76,7 +76,7 @@ as a JSON string. All options on the are passed to the underlying UberSearch, se
|
|
76
76
|
|
77
77
|
- ##### optionFromDatum
|
78
78
|
|
79
|
-
A function that is used to customize the
|
79
|
+
A function that is used to customize the option's value and text built from a JSON response. `datum` is a single result returned from the JSON response.
|
80
80
|
|
81
81
|
The function signature is as follows:
|
82
82
|
|
@@ -98,6 +98,13 @@ as a JSON string. All options on the are passed to the underlying UberSearch, se
|
|
98
98
|
|
99
99
|
Add an aria-label attribute with this value to the uber_select element.
|
100
100
|
|
101
|
+
#### Option Data Attributes
|
102
|
+
`<option>` elements can each use data-attributes to control datum properties. See [UberSearch data](#uber-search-data).
|
103
|
+
|
104
|
+
- `data-match-value`
|
105
|
+
- `data-visibility`
|
106
|
+
- `data-selected-text`
|
107
|
+
|
101
108
|
#### Events Triggered
|
102
109
|
|
103
110
|
- ##### uber-select:ready
|
@@ -129,7 +136,7 @@ being used in purely in JS, and not being linked to a `<select>` element in a fo
|
|
129
136
|
new UberSearch(data, options);
|
130
137
|
```
|
131
138
|
|
132
|
-
#### Data
|
139
|
+
#### Data <a name="uber-search-data"></a>
|
133
140
|
|
134
141
|
Data is an array of objects. Each object may have the following properties:
|
135
142
|
|
@@ -180,7 +187,7 @@ Data is an array of objects. Each object may have the following properties:
|
|
180
187
|
Returns the currently selected element from the search results.
|
181
188
|
|
182
189
|
|
183
|
-
#### Options <a name="
|
190
|
+
#### Options <a name="uber-search-options"></a>
|
184
191
|
|
185
192
|
Options can be specified by setting the `data-uber-options` attribute on the `<select>` element. Values should be passed
|
186
193
|
as a JSON string.
|
@@ -297,7 +304,7 @@ as a JSON string.
|
|
297
304
|
|
298
305
|
- ##### onRender
|
299
306
|
|
300
|
-
A function to run when the results container is rendered. If the result returns false, the default `
|
307
|
+
A function to run when the results container is rendered. If the result returns false, the default `render` event
|
301
308
|
handler is not run and the event is cancelled.
|
302
309
|
|
303
310
|
The function signature is as follows:
|
@@ -328,6 +335,20 @@ as a JSON string.
|
|
328
335
|
function(value) { }
|
329
336
|
```
|
330
337
|
|
338
|
+
- ##### matchGroupNames
|
339
|
+
|
340
|
+
Toggles whether or not to match results using their `datum.group` in addition to their `datum.matchValue`. When `true`
|
341
|
+
the searches that match the group name will cause options within that group to appear.
|
342
|
+
|
343
|
+
Default: `false`
|
344
|
+
|
345
|
+
- ##### alwaysOpen
|
346
|
+
|
347
|
+
Toggles whether options page is always shown open. When `true` the pane will start open and will not close when a option is selected
|
348
|
+
nor via any clicks on the select itself.
|
349
|
+
|
350
|
+
Default: `false`
|
351
|
+
|
331
352
|
- ##### outputContainer (Deprecated)
|
332
353
|
|
333
354
|
An object that receives the output once a result is selected. Must respond to `setValue(value)` and `view()`. This object serves to
|
@@ -66,10 +66,10 @@
|
|
66
66
|
$(select).append(optionsFromData(data))
|
67
67
|
updateSelectValue(options.value)
|
68
68
|
uberSearch.setData(dataFromSelect(select))
|
69
|
-
|
69
|
+
triggerEvent(eventsTriggered.ready)
|
70
70
|
})
|
71
71
|
} else {
|
72
|
-
|
72
|
+
triggerEvent(eventsTriggered.ready)
|
73
73
|
}
|
74
74
|
|
75
75
|
|
@@ -158,7 +158,7 @@
|
|
158
158
|
var before = $(select).val()
|
159
159
|
$(select).val(value)
|
160
160
|
var after = $(select).val() // Read value the same way instead of comparing to `value` so the same coercion is applied
|
161
|
-
if (before != after) {
|
161
|
+
if (before != after) { triggerEvent('change') } // Only trigger a change if the value has actually changed
|
162
162
|
}
|
163
163
|
|
164
164
|
// Selects the option with an emptystring value, or the first option if there is no blank option
|
@@ -169,7 +169,8 @@
|
|
169
169
|
if (!selectValue) { return }
|
170
170
|
|
171
171
|
// Clear the value
|
172
|
-
$(select).val('')
|
172
|
+
$(select).val('')
|
173
|
+
triggerEvent('change')
|
173
174
|
|
174
175
|
// If that cleared it then we're done, otherwise, select the first option
|
175
176
|
if ($(select).find('option:selected').length){ return }
|
@@ -180,13 +181,18 @@
|
|
180
181
|
if (firstOptionValue == selectValue) { return }
|
181
182
|
|
182
183
|
// Select the first option
|
183
|
-
$(select).val(firstOptionValue)
|
184
|
+
$(select).val(firstOptionValue)
|
185
|
+
triggerEvent('change')
|
184
186
|
}
|
185
187
|
|
186
188
|
// Hide the select, but keep its width to allow it to set the min width of the uber select
|
187
189
|
// NOTE: IE doesn't like 0 height, so give it 1px height and then offset
|
188
190
|
function hideSelect(){
|
189
|
-
$(select).wrap($('<div>').css({visibility: 'hidden', height: '1px', marginTop: '-1px', pointerEvents: 'none'}).addClass('select_width_spacer'))
|
191
|
+
$(select).wrap($('<div>').css({visibility: 'hidden', height: '1px', marginTop: '-1px', pointerEvents: 'none'}).addClass('select_width_spacer').attr('aria-hidden', true))
|
192
|
+
}
|
193
|
+
|
194
|
+
function triggerEvent(eventType) {
|
195
|
+
return select.dispatchEvent(new CustomEvent(eventType, { bubbles: true }));
|
190
196
|
}
|
191
197
|
})
|
192
198
|
|
@@ -0,0 +1,160 @@
|
|
1
|
+
(function($) {
|
2
|
+
UberSearch.List = function(options) {
|
3
|
+
var context = this
|
4
|
+
|
5
|
+
var view = this.view = $('<ul class="results"></ul>')
|
6
|
+
|
7
|
+
|
8
|
+
// BEHAVIOUR
|
9
|
+
|
10
|
+
// Handle up and down arrow key presses
|
11
|
+
$(view).on('keydown', function(event){
|
12
|
+
switch (event.which) {
|
13
|
+
case 38: // Up Arrow
|
14
|
+
stepHighlight(-1, true)
|
15
|
+
return false
|
16
|
+
case 40: // Down Arrow
|
17
|
+
stepHighlight(1)
|
18
|
+
return false
|
19
|
+
case 32: // Space
|
20
|
+
if (highlightedResult().length) {
|
21
|
+
highlightedResult().click()
|
22
|
+
}
|
23
|
+
return false
|
24
|
+
case 13: // Enter
|
25
|
+
if (highlightedResult().length) {
|
26
|
+
highlightedResult().click()
|
27
|
+
} else {
|
28
|
+
$(this).trigger('noHighlightSubmit')
|
29
|
+
}
|
30
|
+
return false
|
31
|
+
}
|
32
|
+
})
|
33
|
+
|
34
|
+
// When a list item is hovered
|
35
|
+
$(view).on('mouseenter focus', '.result:not(.disabled)', function(){
|
36
|
+
if ($(this).hasClass('highlighted')) { return }
|
37
|
+
unhighlightResults()
|
38
|
+
highlightResult(this, {scroll: false})
|
39
|
+
})
|
40
|
+
|
41
|
+
|
42
|
+
// HELPER FUNCTIONS
|
43
|
+
|
44
|
+
this.getResults = function(){
|
45
|
+
return $(view).find('.result')
|
46
|
+
}
|
47
|
+
|
48
|
+
this.renderResults = function(data){
|
49
|
+
var results = $.map(data, function(datum){
|
50
|
+
return context.buildResult(datum)
|
51
|
+
})
|
52
|
+
|
53
|
+
view.toggleClass('empty', !data.length)
|
54
|
+
|
55
|
+
view.html(results)
|
56
|
+
}
|
57
|
+
|
58
|
+
this.unhighlightResults = unhighlightResults
|
59
|
+
this.highlightResult = highlightResult
|
60
|
+
this.stepHighlight = stepHighlight
|
61
|
+
this.setHighlight = setHighlight
|
62
|
+
|
63
|
+
function stepHighlight(amount, allowUnhighlight){
|
64
|
+
var index = selectableResults().index(highlightedResult())
|
65
|
+
setHighlight(index + amount, { allowUnhighlight: allowUnhighlight })
|
66
|
+
}
|
67
|
+
|
68
|
+
function setHighlight(index, options) {
|
69
|
+
options = $.extend({}, options)
|
70
|
+
|
71
|
+
var result = selectableResults()[index]
|
72
|
+
|
73
|
+
if (result){
|
74
|
+
unhighlightResults({ blur: !options.focus })
|
75
|
+
highlightResult(result, { focus: options.focus })
|
76
|
+
} else if (options.allowUnhighlight) {
|
77
|
+
unhighlightResults({ blur: !options.focus })
|
78
|
+
}
|
79
|
+
triggerEvent('setHighlight', [result, index])
|
80
|
+
}
|
81
|
+
|
82
|
+
function highlightResult(result, options) {
|
83
|
+
result = $(result)
|
84
|
+
options = $.extend({
|
85
|
+
scroll: true,
|
86
|
+
focus: true
|
87
|
+
}, options)
|
88
|
+
|
89
|
+
if (!result.length) {
|
90
|
+
return
|
91
|
+
}
|
92
|
+
|
93
|
+
result.addClass('highlighted')
|
94
|
+
|
95
|
+
if (options.focus) {
|
96
|
+
result.focus()
|
97
|
+
}
|
98
|
+
|
99
|
+
if (options.scroll){
|
100
|
+
scrollResultIntoView(result)
|
101
|
+
}
|
102
|
+
}
|
103
|
+
|
104
|
+
function unhighlightResults(options){
|
105
|
+
options = $.extend({
|
106
|
+
blur: true
|
107
|
+
}, options)
|
108
|
+
|
109
|
+
var result = highlightedResult()
|
110
|
+
result.removeClass('highlighted')
|
111
|
+
|
112
|
+
if (options.blur) {
|
113
|
+
result.blur()
|
114
|
+
}
|
115
|
+
}
|
116
|
+
|
117
|
+
function highlightedResult(){
|
118
|
+
return results().filter('.highlighted')
|
119
|
+
}
|
120
|
+
|
121
|
+
function selectableResults(){
|
122
|
+
return visibleResults().not('.disabled')
|
123
|
+
}
|
124
|
+
|
125
|
+
function visibleResults(){
|
126
|
+
return results().not('.hidden')
|
127
|
+
}
|
128
|
+
|
129
|
+
function results(){
|
130
|
+
return view.find('.result')
|
131
|
+
}
|
132
|
+
|
133
|
+
function scrollResultIntoView(result){
|
134
|
+
result = $(result)
|
135
|
+
var container = result.closest('.results').css('position', 'relative') // Ensure the results container is positioned so offset is calculated correctly
|
136
|
+
var containerHeight = container.outerHeight()
|
137
|
+
var containerTop = container.get(0).scrollTop
|
138
|
+
var containerBottom = containerTop + containerHeight
|
139
|
+
var resultHeight = result.outerHeight()
|
140
|
+
var resultTop = result.get(0).offsetTop
|
141
|
+
var resultBottom = resultTop + resultHeight
|
142
|
+
|
143
|
+
if (containerBottom < resultBottom){
|
144
|
+
container.scrollTop(resultBottom - containerHeight)
|
145
|
+
} else if (containerTop > resultTop){
|
146
|
+
container.scrollTop(resultTop)
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
|
151
|
+
// Allow observer to be attached to the UberSearch itself
|
152
|
+
function triggerEvent(eventType, callbackArgs){
|
153
|
+
view.trigger(eventType, callbackArgs)
|
154
|
+
$(context).triggerHandler(eventType, callbackArgs)
|
155
|
+
}
|
156
|
+
|
157
|
+
// INITIALIZATION
|
158
|
+
$.extend(this, options) // Allow overriding of functions
|
159
|
+
}
|
160
|
+
})(jQuery)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
(function($) {
|
2
|
+
UberSearch.OutputContainer = function(options){
|
3
|
+
options = $.extend({}, options)
|
4
|
+
var view = $('<span class="selected_text_container" aria-expanded="false" aria-activedescendant aria-haspopup="listbox" role="combobox" tabindex="0"></span>')
|
5
|
+
var selectedText = $('<span class="selected_text"></span>').appendTo(view)
|
6
|
+
var selectCaret = $('<span class="select_caret"></span>').appendTo(view).html(options.selectCaret)
|
7
|
+
|
8
|
+
// INITIALIZATION
|
9
|
+
|
10
|
+
if (options.ariaLabel) { view.attr("aria-label", options.ariaLabel) }
|
11
|
+
setValue()
|
12
|
+
|
13
|
+
// HELPER FUNCTIONS
|
14
|
+
|
15
|
+
function setValue(value){
|
16
|
+
selectedText.text(value || String.fromCharCode(160)); // Insert value or
|
17
|
+
|
18
|
+
view.toggleClass('empty', !value)
|
19
|
+
}
|
20
|
+
|
21
|
+
function setDisabled(boolean) {
|
22
|
+
view.toggleClass('disabled', boolean)
|
23
|
+
}
|
24
|
+
|
25
|
+
// PUBLIC INTERFACE
|
26
|
+
|
27
|
+
$.extend(this, {view: view, setValue: setValue, setDisabled: setDisabled})
|
28
|
+
}
|
29
|
+
})(jQuery)
|
@@ -0,0 +1,93 @@
|
|
1
|
+
(function($) {
|
2
|
+
UberSearch.Pane = function() {
|
3
|
+
var eventsTriggered = {
|
4
|
+
shown: 'shown',
|
5
|
+
hidden: 'hidden'
|
6
|
+
}
|
7
|
+
|
8
|
+
var context = this
|
9
|
+
var model = {}
|
10
|
+
var isOpen = false
|
11
|
+
var view = $('<div class="pane" role="listbox"></div>').toggle(isOpen)
|
12
|
+
var innerPane = $('<div class="pane_inner"></div>').appendTo(view)
|
13
|
+
|
14
|
+
|
15
|
+
// PUBLIC INTERFACE
|
16
|
+
|
17
|
+
$.extend(this, {
|
18
|
+
model: model,
|
19
|
+
view: view,
|
20
|
+
addContent: addContent,
|
21
|
+
removeContent: removeContent,
|
22
|
+
show: show,
|
23
|
+
hide: hide,
|
24
|
+
toggle: toggle,
|
25
|
+
isOpen: paneIsOpen,
|
26
|
+
isClosed: paneIsClosed
|
27
|
+
})
|
28
|
+
|
29
|
+
|
30
|
+
// BEHAVIOUR
|
31
|
+
|
32
|
+
// Make it possible to have elements in the pane that close it
|
33
|
+
view.on('click', '[data-behaviour~=close-pane]', function(event){
|
34
|
+
context.hide()
|
35
|
+
})
|
36
|
+
|
37
|
+
// Close the pane when the user presses escape
|
38
|
+
$(document).on('keyup', function(event){
|
39
|
+
if (event.which == 27 && isOpen){
|
40
|
+
context.hide()
|
41
|
+
return false
|
42
|
+
}
|
43
|
+
})
|
44
|
+
|
45
|
+
|
46
|
+
// HELPER FUNCTIONS
|
47
|
+
|
48
|
+
function paneIsOpen(){
|
49
|
+
return isOpen
|
50
|
+
}
|
51
|
+
|
52
|
+
function paneIsClosed(){
|
53
|
+
return !isOpen
|
54
|
+
}
|
55
|
+
|
56
|
+
function addContent(name, content){
|
57
|
+
model[name] = content
|
58
|
+
innerPane.append(content)
|
59
|
+
}
|
60
|
+
|
61
|
+
function removeContent(name){
|
62
|
+
$(model[name]).remove()
|
63
|
+
delete model['name']
|
64
|
+
}
|
65
|
+
|
66
|
+
function show(){
|
67
|
+
if (isOpen) { return }
|
68
|
+
isOpen = true
|
69
|
+
view.show()
|
70
|
+
view.addClass('showing')
|
71
|
+
triggerEvent(eventsTriggered.shown)
|
72
|
+
}
|
73
|
+
function hide(){
|
74
|
+
if (!isOpen) { return }
|
75
|
+
isOpen = false
|
76
|
+
view.hide()
|
77
|
+
view.removeClass('showing')
|
78
|
+
triggerEvent(eventsTriggered.hidden)
|
79
|
+
}
|
80
|
+
function toggle(){
|
81
|
+
if (isOpen) {
|
82
|
+
context.hide()
|
83
|
+
} else {
|
84
|
+
context.show()
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
function triggerEvent(eventType, callbackArgs){
|
89
|
+
view.trigger(eventType, callbackArgs)
|
90
|
+
$(context).triggerHandler(eventType, callbackArgs)
|
91
|
+
}
|
92
|
+
}
|
93
|
+
})(jQuery)
|
@@ -0,0 +1,77 @@
|
|
1
|
+
(function($) {
|
2
|
+
UberSearch.Search = function(queryInput, resultsContainer, options){
|
3
|
+
var context = this
|
4
|
+
var model = new UberSearch.SearchModel(options.model)
|
5
|
+
var list = new UberSearch.List(options.view)
|
6
|
+
var resultsRendered = false
|
7
|
+
|
8
|
+
// HELPER FUNCTIONS
|
9
|
+
|
10
|
+
this.setData = function(data){
|
11
|
+
model.setData(data)
|
12
|
+
}
|
13
|
+
|
14
|
+
this.renderResults = function(){
|
15
|
+
list.renderResults(model.getResults())
|
16
|
+
$(this).trigger('renderedResults')
|
17
|
+
resultsRendered = true
|
18
|
+
}
|
19
|
+
|
20
|
+
this.getQuery = function(){
|
21
|
+
return model.getQuery()
|
22
|
+
}
|
23
|
+
|
24
|
+
this.getResults = function(){
|
25
|
+
return list.getResults()
|
26
|
+
}
|
27
|
+
|
28
|
+
this.clear = function(){
|
29
|
+
if (!resultsRendered){
|
30
|
+
this.renderResults()
|
31
|
+
}
|
32
|
+
|
33
|
+
if (queryInput.val() === '') {
|
34
|
+
list.unhighlightResults()
|
35
|
+
} else {
|
36
|
+
queryInput.val('').change()
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
this.highlightResult = function(element, options) {
|
41
|
+
list.unhighlightResults()
|
42
|
+
list.highlightResult(element, options)
|
43
|
+
}
|
44
|
+
|
45
|
+
this.stepHighlight = list.stepHighlight
|
46
|
+
this.setHighlight = list.setHighlight
|
47
|
+
|
48
|
+
|
49
|
+
// BEHAVIOUR
|
50
|
+
|
51
|
+
$(queryInput).on('searchInput', function(){
|
52
|
+
model.setQuery(this.value)
|
53
|
+
})
|
54
|
+
|
55
|
+
// Forward navigating away from queryInput
|
56
|
+
$(queryInput).on('inputDownArrow', function() {
|
57
|
+
$(context).trigger('inputDownArrow')
|
58
|
+
})
|
59
|
+
|
60
|
+
$(model).on('resultsUpdated', function(){
|
61
|
+
context.renderResults()
|
62
|
+
})
|
63
|
+
|
64
|
+
// Forward query change
|
65
|
+
$(model).on('queryChanged', function(){
|
66
|
+
$(context).trigger('queryChanged')
|
67
|
+
})
|
68
|
+
|
69
|
+
|
70
|
+
// INITIALIZATION
|
71
|
+
|
72
|
+
resultsContainer.html(list.view)
|
73
|
+
|
74
|
+
|
75
|
+
// PROTOTYPES
|
76
|
+
}
|
77
|
+
})(jQuery)
|
@@ -0,0 +1,96 @@
|
|
1
|
+
(function($) {
|
2
|
+
UberSearch.SearchField = function(options){
|
3
|
+
options = $.extend({
|
4
|
+
placeholder: 'Type to Search',
|
5
|
+
searchInputAttributes: { 'aria-label': "Type to Search" },
|
6
|
+
clearButton:'✕' // Text content of clear search button
|
7
|
+
}, options)
|
8
|
+
|
9
|
+
var inputAttrs = {}
|
10
|
+
$.extend(inputAttrs, { placeholder: options.placeholder }, options.searchInputAttributes)
|
11
|
+
|
12
|
+
var context = this
|
13
|
+
var input = this.input = $('<input type="search" class="search_input">').attr(inputAttrs)
|
14
|
+
var value = input.val()
|
15
|
+
var clearButton = this.clearButton = $('<span class="clear_search_button"></span>').html(options.clearButton)
|
16
|
+
var view = this.view = $('<span class="search_field_container"></span>').append(input).append(clearButton)
|
17
|
+
var eventNames = isOnInputSupported() ? 'input change' : 'keyup change'
|
18
|
+
|
19
|
+
|
20
|
+
// PUBLIC INTERFACE
|
21
|
+
|
22
|
+
$.extend(this, {refresh: refresh})
|
23
|
+
|
24
|
+
|
25
|
+
// BEHAVIOUR
|
26
|
+
|
27
|
+
// When a change is detected
|
28
|
+
input.on(eventNames, function() {
|
29
|
+
refresh() // Always refresh on input in case something has altered the state without informing us
|
30
|
+
|
31
|
+
if (input.val() == value) { return }
|
32
|
+
|
33
|
+
triggerEvent('searchInput')
|
34
|
+
value = input.val()
|
35
|
+
})
|
36
|
+
|
37
|
+
// When the clear button is pressed
|
38
|
+
clearButton.on('click', function(){
|
39
|
+
input.val('')
|
40
|
+
refresh()
|
41
|
+
input.focus()
|
42
|
+
triggerEvent('searchInput')
|
43
|
+
triggerEvent('clear')
|
44
|
+
})
|
45
|
+
|
46
|
+
// When the enter button is pressed
|
47
|
+
input.on('keydown', function(event){
|
48
|
+
if (event.which == 13){
|
49
|
+
triggerEvent('querySubmit')
|
50
|
+
}
|
51
|
+
|
52
|
+
if (event.which == 38) { // Up Arrow
|
53
|
+
triggerEvent('inputUpArrow')
|
54
|
+
return false
|
55
|
+
}
|
56
|
+
|
57
|
+
if (event.which == 40){ // Down Arrow
|
58
|
+
triggerEvent('inputDownArrow')
|
59
|
+
return false
|
60
|
+
}
|
61
|
+
})
|
62
|
+
|
63
|
+
|
64
|
+
// HELPER FUNCTIONS
|
65
|
+
|
66
|
+
function refresh(){
|
67
|
+
updateClearButtonVisiblity()
|
68
|
+
updateSearchInputClass()
|
69
|
+
}
|
70
|
+
|
71
|
+
function updateSearchInputClass(){
|
72
|
+
input.toggleClass('empty', !input.val())
|
73
|
+
}
|
74
|
+
|
75
|
+
function isOnInputSupported(){
|
76
|
+
// IE 8 and 9 are the only common browsers that don't completely support oninput
|
77
|
+
// IE 10 and 11 fire the input event when the element is focussed, treat them as unsupported as well
|
78
|
+
return !(navigator.userAgent.indexOf('MSIE')!==-1 || navigator.appVersion.indexOf('Trident/') > 0) // SOURCE: http://stackoverflow.com/questions/21825157/internet-explorer-11-detection
|
79
|
+
}
|
80
|
+
|
81
|
+
function updateClearButtonVisiblity(){
|
82
|
+
clearButton.toggle(!!input.val().length)
|
83
|
+
}
|
84
|
+
|
85
|
+
// Allow observer to be attached to the SearchField itself
|
86
|
+
function triggerEvent(eventType, callbackArgs){
|
87
|
+
input.trigger(eventType, callbackArgs)
|
88
|
+
$(context).triggerHandler(eventType, callbackArgs)
|
89
|
+
}
|
90
|
+
|
91
|
+
|
92
|
+
// INITIALIZATION
|
93
|
+
|
94
|
+
refresh()
|
95
|
+
}
|
96
|
+
})(jQuery)
|