diff --git a/fancySelect.coffee b/fancySelect.coffee
index 26de6bd..1788755 100644
--- a/fancySelect.coffee
+++ b/fancySelect.coffee
@@ -1,23 +1,34 @@
-$ = window.jQuery || window.Zepto || window.$
+$ = window.jQuery or window.Zepto or window.$
-$.fn.fancySelect = (opts = {}) ->
+$.fn.fancySelect = (opts) ->
+ isiOS = undefined
+ settings = undefined
+ if opts == null
+ opts = {}
settings = $.extend({
forceiOS: false
includeBlank: false
optionTemplate: (optionEl) ->
- return optionEl.text()
+ optionEl.text()
triggerTemplate: (optionEl) ->
- return optionEl.text()
- }, opts)
-
- isiOS = !!navigator.userAgent.match /iP(hone|od|ad)/i
+ optionEl.text()
- return this.each ->
+ }, opts)
+ isiOS = ! !navigator.userAgent.match(/iP(hone|od|ad)/i)
+ @each ->
+ copyOptionsToList = undefined
+ disabled = undefined
+ options = undefined
+ sel = undefined
+ trigger = undefined
+ updateTriggerText = undefined
+ wrapper = undefined
+ searchTerm = ''
+ searchTimeout = undefined
sel = $(this)
- return if sel.hasClass('fancified') || sel[0].tagName != 'SELECT'
- sel.addClass('fancified')
-
- # hide the native select
+ if sel.hasClass('fancified') or sel[0].tagName != 'SELECT'
+ return
+ sel.addClass 'fancified'
sel.css
width: 1
height: 1
@@ -26,169 +37,179 @@ $.fn.fancySelect = (opts = {}) ->
top: 0
left: 0
opacity: 0
-
- # some global setup stuff
sel.wrap '
'
wrapper = sel.parent()
-
- wrapper.addClass(sel.data('class')) if sel.data('class')
-
+ if sel.data('class')
+ wrapper.addClass sel.data('class')
wrapper.append '
'
- wrapper.append '
' unless isiOS && !settings.forceiOS
-
- trigger = wrapper.find '.trigger'
- options = wrapper.find '.options'
-
- # disabled in markup?
+ if !(isiOS and !settings.forceiOS)
+ wrapper.append ''
+ trigger = wrapper.find('.trigger')
+ options = wrapper.find('.options')
disabled = sel.prop('disabled')
if disabled
wrapper.addClass 'disabled'
updateTriggerText = ->
+ triggerHtml = undefined
triggerHtml = settings.triggerTemplate(sel.find(':selected'))
- trigger.html(triggerHtml)
+ trigger.html triggerHtml
sel.on 'blur.fs', ->
- if trigger.hasClass 'open'
- setTimeout ->
+ if trigger.hasClass('open')
+ return setTimeout((->
trigger.trigger 'close.fs'
- , 120
-
+ ), 120)
+ return
trigger.on 'close.fs', ->
trigger.removeClass 'open'
options.removeClass 'open'
-
trigger.on 'click.fs', ->
- unless disabled
+ offParent = undefined
+ parent = undefined
+ if !disabled
trigger.toggleClass 'open'
-
- # fancySelect defaults to using native selects with a styled trigger on mobile
- # don't show the options if we're on mobile and haven't set `forceiOS`
- if isiOS && !settings.forceiOS
- if trigger.hasClass 'open'
- sel.focus()
+ if isiOS and !settings.forceiOS
+ if trigger.hasClass('open')
+ return sel.focus()
else
- if trigger.hasClass 'open'
+ if trigger.hasClass('open')
parent = trigger.parent()
offParent = parent.offsetParent()
-
- # TODO 20 is very static
- if (parent.offset().top + parent.outerHeight() + options.outerHeight() + 20) > $(window).height() + $(window).scrollTop()
+ if parent.offset().top + parent.outerHeight() + options.outerHeight() + 20 > $(window).height() + $(window).scrollTop()
options.addClass 'overflowing'
else
options.removeClass 'overflowing'
-
options.toggleClass 'open'
-
- sel.focus() unless isiOS
-
+ if !isiOS
+ return sel.focus()
+ return
sel.on 'enable', ->
sel.prop 'disabled', false
wrapper.removeClass 'disabled'
disabled = false
copyOptionsToList()
-
sel.on 'disable', ->
sel.prop 'disabled', true
wrapper.addClass 'disabled'
disabled = true
-
sel.on 'change.fs', (e) ->
- if e.originalEvent && e.originalEvent.isTrusted
- # discard firefox-only automatic event when hitting enter, we want to trigger our own
+ if e.originalEvent and e.originalEvent.isTrusted
e.stopPropagation()
else
updateTriggerText()
-
- # keyboard control
sel.on 'keydown', (e) ->
+ hovered = undefined
+ newHovered = undefined
+ w = undefined
w = e.which
hovered = options.find('.hover')
- hovered.removeClass('hover')
-
+ hovered.removeClass 'hover'
if !options.hasClass('open')
- if w in [13, 32, 38, 40] # enter, space, up, down
+ if w == 13 or w == 38 or w == 40
e.preventDefault()
- trigger.trigger 'click.fs'
+ return trigger.trigger('click.fs')
+ else
+ clearTimeout searchTimeout
+ searchTimeout = setTimeout((->
+ searchTerm = ''
+ return
+ ), 500)
+ searchTerm += String.fromCharCode(w).toLowerCase()
+ optCount = options.find('li').length + 1
+ i = 1
+ while i < optCount
+ current = options.find('li:nth-child(' + i + ')')
+ text = current.text()
+ if text.toLowerCase().indexOf(searchTerm) >= 0
+ current.trigger 'mousedown.fs'
+ return
+ i++
else
- if w == 38 # up
+ if w == 38
e.preventDefault()
- if hovered.length && hovered.index() > 0 # move up
- hovered.prev().addClass('hover')
- else # move to bottom
- options.find('li:last-child').addClass('hover')
- else if w == 40 # down
+ if hovered.length and hovered.index() > 0
+ hovered.prev().addClass 'hover'
+ else
+ options.find('li:last-child').addClass 'hover'
+ else if w == 40
e.preventDefault()
- if hovered.length && hovered.index() < options.find('li').length - 1 # move down
- hovered.next().addClass('hover')
- else # move to top
- options.find('li:first-child').addClass('hover')
- else if w == 27 # escape
+ if hovered.length and hovered.index() < options.find('li').length - 1
+ hovered.next().addClass 'hover'
+ else
+ options.find('li:first-child').addClass 'hover'
+ else if w == 27
e.preventDefault()
trigger.trigger 'click.fs'
- else if w in [13, 32] # enter, space
+ else if w == 13
e.preventDefault()
hovered.trigger 'mousedown.fs'
- else if w == 9 # tab
- if trigger.hasClass 'open' then trigger.trigger 'close.fs'
-
+ else if w == 9
+ if trigger.hasClass('open')
+ trigger.trigger 'close.fs'
+ else
+ clearTimeout searchTimeout
+ searchTimeout = setTimeout((->
+ `var current`
+ `var i`
+ searchTerm = ''
+ return
+ ), 500)
+ searchTerm += String.fromCharCode(w).toLowerCase()
+ optCount = options.find('li').length + 1
+ i = 1
+ while i < optCount
+ current = options.find('li:nth-child(' + i + ')')
+ text = current.text()
+ if text.toLowerCase().indexOf(searchTerm) >= 0
+ current.addClass 'hover'
+ return
+ i++
newHovered = options.find('.hover')
if newHovered.length
options.scrollTop 0
- options.scrollTop newHovered.position().top - 12
-
- # Handle item selection, and
- # Add class selected to selected item
+ return options.scrollTop(newHovered.position().top - 12)
+ return
options.on 'mousedown.fs', 'li', (e) ->
+ clicked = undefined
clicked = $(this)
-
- sel.val(clicked.data('raw-value'))
-
- sel.trigger('blur.fs').trigger('focus.fs') unless isiOS
-
- options.find('.selected').removeClass('selected')
+ sel.val clicked.data('raw-value')
+ if !isiOS
+ sel.trigger('blur.fs').trigger 'focus.fs'
+ options.find('.selected').removeClass 'selected'
clicked.addClass 'selected'
- trigger.addClass 'selected'
- return sel.val(clicked.data('raw-value')).trigger('change.fs').trigger('blur.fs').trigger('focus.fs')
-
- # handle mouse selection
+ trigger.toggleClass 'selected', clicked.data('raw-value') != ''
+ sel.val(clicked.data('raw-value')).trigger('change.fs').trigger('blur.fs').trigger 'focus.fs'
options.on 'mouseenter.fs', 'li', ->
+ hovered = undefined
+ nowHovered = undefined
nowHovered = $(this)
hovered = options.find('.hover')
hovered.removeClass 'hover'
-
nowHovered.addClass 'hover'
-
options.on 'mouseleave.fs', 'li', ->
- options.find('.hover').removeClass('hover')
+ options.find('.hover').removeClass 'hover'
copyOptionsToList = ->
- # update our trigger to reflect the select (it really already should, this is just a safety)
+ selOpts = undefined
updateTriggerText()
-
- return if isiOS && !settings.forceiOS
-
- # snag current options before we add a default one
- selOpts = sel.find 'option'
-
- # generate list of options for the fancySelect
-
+ if isiOS and !settings.forceiOS
+ return
+ selOpts = sel.find('option')
sel.find('option').each (i, opt) ->
+ optHtml = undefined
opt = $(opt)
-
- if !opt.prop('disabled') && (opt.val() || settings.includeBlank)
- # Generate the inner HTML for the option from our template
+ if !opt.prop('disabled') and (opt.val() or settings.includeBlank)
optHtml = settings.optionTemplate(opt)
-
- # Is there a select option on page load?
if opt.prop('selected')
- options.append "- #{optHtml}
"
+ return options.append('- ' + optHtml + '
')
else
- options.append "- #{optHtml}
"
+ return options.append('- ' + optHtml + '
')
+ return
- # for updating the list of options after initialization
sel.on 'update.fs', ->
wrapper.find('.options').empty()
copyOptionsToList()
-
copyOptionsToList()
+
+return
diff --git a/fancySelect.js b/fancySelect.js
index 4bd378a..65b88e7 100644
--- a/fancySelect.js
+++ b/fancySelect.js
@@ -1,4 +1,3 @@
-// Generated by CoffeeScript 1.4.0
(function() {
var $;
@@ -21,7 +20,7 @@
}, opts);
isiOS = !!navigator.userAgent.match(/iP(hone|od|ad)/i);
return this.each(function() {
- var copyOptionsToList, disabled, options, sel, trigger, updateTriggerText, wrapper;
+ var copyOptionsToList, disabled, options, sel, trigger, updateTriggerText, wrapper, searchTerm = '', searchTimeout;
sel = $(this);
if (sel.hasClass('fancified') || sel[0].tagName !== 'SELECT') {
return;
@@ -116,9 +115,24 @@
hovered = options.find('.hover');
hovered.removeClass('hover');
if (!options.hasClass('open')) {
- if (w === 13 || w === 32 || w === 38 || w === 40) {
+ if (w === 13 || w === 38 || w === 40) {
e.preventDefault();
return trigger.trigger('click.fs');
+ } else {
+ clearTimeout(searchTimeout);
+ searchTimeout = setTimeout(function() {
+ searchTerm = '';
+ }, 500);
+ searchTerm += String.fromCharCode(w).toLowerCase();
+ optCount = options.find('li').length + 1;
+ for(var i = 1; i < optCount; i ++) {
+ var current = options.find('li:nth-child(' + i + ')');
+ text = current.text();
+ if(text.toLowerCase().indexOf(searchTerm) >= 0) {
+ current.trigger('mousedown.fs');
+ return;
+ }
+ }
}
} else {
if (w === 38) {
@@ -138,13 +152,28 @@
} else if (w === 27) {
e.preventDefault();
trigger.trigger('click.fs');
- } else if (w === 13 || w === 32) {
+ } else if (w === 13) {
e.preventDefault();
hovered.trigger('mousedown.fs');
} else if (w === 9) {
if (trigger.hasClass('open')) {
trigger.trigger('close.fs');
}
+ } else {
+ clearTimeout(searchTimeout);
+ searchTimeout = setTimeout(function() {
+ searchTerm = '';
+ }, 500);
+ searchTerm += String.fromCharCode(w).toLowerCase();
+ optCount = options.find('li').length + 1;
+ for(var i = 1; i < optCount; i ++) {
+ var current = options.find('li:nth-child(' + i + ')');
+ text = current.text();
+ if(text.toLowerCase().indexOf(searchTerm) >= 0) {
+ current.addClass('hover');
+ return;
+ }
+ }
}
newHovered = options.find('.hover');
if (newHovered.length) {
@@ -162,7 +191,7 @@
}
options.find('.selected').removeClass('selected');
clicked.addClass('selected');
- trigger.addClass('selected');
+ trigger.toggleClass('selected', clicked.data('raw-value') !== '');
return sel.val(clicked.data('raw-value')).trigger('change.fs').trigger('blur.fs').trigger('focus.fs');
});
options.on('mouseenter.fs', 'li', function() {
@@ -202,5 +231,4 @@
return copyOptionsToList();
});
};
-
}).call(this);