(function() { var DOWN, ENTER, ESCAPE, Evented, SPACE, Select, UP, addClass, clickEvent, extend, getBounds, getFocusedSelect, hasClass, isRepeatedChar, lastCharacter, removeClass, searchText, searchTextTimeout, touchDevice, useNative, _ref, __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, __hasProp = {}.hasOwnProperty, __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; _ref = Tether.Utils, extend = _ref.extend, addClass = _ref.addClass, removeClass = _ref.removeClass, hasClass = _ref.hasClass, getBounds = _ref.getBounds, Evented = _ref.Evented; ENTER = 13; ESCAPE = 27; SPACE = 32; UP = 38; DOWN = 40; touchDevice = 'ontouchstart' in document.documentElement; clickEvent = touchDevice ? 'touchstart' : 'click'; useNative = function() { return touchDevice && (innerWidth <= 640 || innerHeight <= 640); }; isRepeatedChar = function(str) { return Array.prototype.reduce.call(str, function(a, b) { if (a === b) { return b; } else { return false; } }); }; getFocusedSelect = function() { var _ref1; return (_ref1 = document.querySelector('.select-target-focused')) != null ? _ref1.selectInstance : void 0; }; searchText = ''; searchTextTimeout = void 0; lastCharacter = void 0; document.addEventListener('keypress', function(e) { var options, repeatedOptions, select, selected; if (!(select = getFocusedSelect())) { return; } if (e.charCode === 0) { return; } if (e.keyCode === SPACE) { e.preventDefault(); } clearTimeout(searchTextTimeout); searchTextTimeout = setTimeout(function() { return searchText = ''; }, 500); searchText += String.fromCharCode(e.charCode); options = select.findOptionsByPrefix(searchText); if (options.length === 1) { select.selectOption(options[0]); return; } if (searchText.length > 1 && isRepeatedChar(searchText)) { repeatedOptions = select.findOptionsByPrefix(searchText[0]); if (repeatedOptions.length) { selected = repeatedOptions.indexOf(select.getChosen()); selected += 1; selected = selected % repeatedOptions.length; select.selectOption(repeatedOptions[selected]); return; } } if (options.length) { select.selectOption(options[0]); } }); document.addEventListener('keydown', function(e) { var select, _ref1, _ref2; if (!(select = getFocusedSelect())) { return; } if ((_ref1 = e.keyCode) === UP || _ref1 === DOWN || _ref1 === ESCAPE) { e.preventDefault(); } if (select.isOpen()) { switch (e.keyCode) { case UP: case DOWN: return select.moveHighlight(e.keyCode); case ENTER: return select.selectHighlightedOption(); case ESCAPE: select.close(); return select.target.focus(); } } else { if ((_ref2 = e.keyCode) === UP || _ref2 === DOWN || _ref2 === SPACE) { return select.open(); } } }); Select = (function(_super) { __extends(Select, _super); Select.defaults = { alignToHighlighed: 'auto', className: 'select-theme-default' }; function Select(options) { this.options = options; this.update = __bind(this.update, this); this.options = extend({}, Select.defaults, this.options); this.select = this.options.el; if (this.select.selectInstance != null) { throw new Error("This element has already been turned into a Select"); } this.setupTarget(); this.renderTarget(); this.setupDrop(); this.renderDrop(); this.setupSelect(); this.setupTether(); this.bindClick(); this.bindMutationEvents(); this.value = this.select.value; } Select.prototype.useNative = function() { return this.options.useNative === true || (useNative() && this.options.useNative !== false); }; Select.prototype.setupTarget = function() { var tabIndex, _this = this; this.target = document.createElement('a'); this.target.href = 'javascript:;'; addClass(this.target, 'select-target'); tabIndex = this.select.getAttribute('tabindex') || 0; this.target.setAttribute('tabindex', tabIndex); if (this.options.className) { addClass(this.target, this.options.className); } this.target.selectInstance = this; this.target.addEventListener('click', function() { if (!_this.isOpen()) { return _this.target.focus(); } else { return _this.target.blur(); } }); this.target.addEventListener('focus', function() { return addClass(_this.target, 'select-target-focused'); }); this.target.addEventListener('blur', function(e) { if (_this.isOpen()) { if (e.relatedTarget && !_this.drop.contains(e.relatedTarget)) { _this.close(); } } return removeClass(_this.target, 'select-target-focused'); }); return this.select.parentNode.insertBefore(this.target, this.select.nextSibling); }; Select.prototype.setupDrop = function() { var _this = this; this.drop = document.createElement('div'); addClass(this.drop, 'select'); if (this.options.className) { addClass(this.drop, this.options.className); } document.body.appendChild(this.drop); this.drop.addEventListener('click', function(e) { if (hasClass(e.target, 'select-option')) { _this.pickOption(e.target); } return e.stopPropagation(); }); this.drop.addEventListener('mousemove', function(e) { if (hasClass(e.target, 'select-option')) { return _this.highlightOption(e.target); } }); this.content = document.createElement('div'); addClass(this.content, 'select-content'); return this.drop.appendChild(this.content); }; Select.prototype.open = function() { var positionSelectStyle, selectedOption, _this = this; addClass(this.target, 'select-open'); if (this.useNative()) { this.select.style.display = 'block'; setTimeout(function() { var event; event = document.createEvent("MouseEvents"); event.initEvent("mousedown", true, true); return _this.select.dispatchEvent(event); }); return; } addClass(this.drop, 'select-open'); setTimeout(function() { return _this.tether.enable(); }); selectedOption = this.drop.querySelector('.select-option-selected'); if (!selectedOption) { return; } this.highlightOption(selectedOption); this.scrollDropContentToOption(selectedOption); positionSelectStyle = function() { var dropBounds, offset, optionBounds; if (hasClass(_this.drop, 'tether-abutted-left') || hasClass(_this.drop, 'tether-abutted-bottom')) { dropBounds = getBounds(_this.drop); optionBounds = getBounds(selectedOption); offset = dropBounds.top - (optionBounds.top + optionBounds.height); return _this.drop.style.top = (parseFloat(_this.drop.style.top) || 0) + offset + 'px'; } }; if (this.options.alignToHighlighted === 'always' || (this.options.alignToHighlighted === 'auto' && this.content.scrollHeight <= this.content.clientHeight)) { setTimeout(positionSelectStyle); } return this.trigger('open'); }; Select.prototype.close = function() { removeClass(this.target, 'select-open'); if (this.useNative()) { this.select.style.display = 'none'; return; } this.tether.disable(); removeClass(this.drop, 'select-open'); return this.trigger('close'); }; Select.prototype.toggle = function() { if (this.isOpen()) { return this.close(); } else { return this.open(); } }; Select.prototype.isOpen = function() { return hasClass(this.drop, 'select-open'); }; Select.prototype.bindClick = function() { var _this = this; this.target.addEventListener(clickEvent, function(e) { e.preventDefault(); return _this.toggle(); }); return document.addEventListener(clickEvent, function(event) { if (!_this.isOpen()) { return; } if (event.target === _this.drop || _this.drop.contains(event.target)) { return; } if (event.target === _this.target || _this.target.contains(event.target)) { return; } return _this.close(); }); }; Select.prototype.setupTether = function() { return this.tether = new Tether(extend({ element: this.drop, target: this.target, attachment: 'top left', targetAttachment: 'bottom left', classPrefix: 'select', constraints: [ { to: 'window', attachment: 'together' } ] }, this.options.tetherOptions)); }; Select.prototype.renderTarget = function() { var option, _i, _len, _ref1; this.target.innerHTML = ''; _ref1 = this.select.querySelectorAll('option'); for (_i = 0, _len = _ref1.length; _i < _len; _i++) { option = _ref1[_i]; if (option.selected) { this.target.innerHTML = option.innerHTML; break; } } return this.target.appendChild(document.createElement('b')); }; Select.prototype.renderDrop = function() { var el, option, optionList, _i, _len, _ref1; optionList = document.createElement('ul'); addClass(optionList, 'select-options'); _ref1 = this.select.querySelectorAll('option'); for (_i = 0, _len = _ref1.length; _i < _len; _i++) { el = _ref1[_i]; option = document.createElement('li'); addClass(option, 'select-option'); option.setAttribute('data-value', el.value); option.innerHTML = el.innerHTML; if (el.selected) { addClass(option, 'select-option-selected'); } optionList.appendChild(option); } this.content.innerHTML = ''; return this.content.appendChild(optionList); }; Select.prototype.update = function() { this.renderDrop(); return this.renderTarget(); }; Select.prototype.setupSelect = function() { this.select.selectInstance = this; addClass(this.select, 'select-select'); return this.select.addEventListener('change', this.update); }; Select.prototype.bindMutationEvents = function() { if (window.MutationObserver != null) { this.observer = new MutationObserver(this.update); return this.observer.observe(this.select, { childList: true, attributes: true, characterData: true, subtree: true }); } else { return this.select.addEventListener('DOMSubtreeModified', this.update); } }; Select.prototype.findOptionsByPrefix = function(text) { var options; options = this.drop.querySelectorAll('.select-option'); text = text.toLowerCase(); return Array.prototype.filter.call(options, function(option) { return option.innerHTML.toLowerCase().substr(0, text.length) === text; }); }; Select.prototype.findOptionsByValue = function(val) { var options; options = this.drop.querySelectorAll('.select-option'); return Array.prototype.filter.call(options, function(option) { return option.getAttribute('data-value') === val; }); }; Select.prototype.getChosen = function() { if (this.isOpen()) { return this.drop.querySelector('.select-option-highlight'); } else { return this.drop.querySelector('.select-option-selected'); } }; Select.prototype.selectOption = function(option) { if (this.isOpen()) { this.highlightOption(option); return this.scrollDropContentToOption(option); } else { return this.pickOption(option, false); } }; Select.prototype.resetSelection = function() { return this.selectOption(this.drop.querySelector('.select-option')); }; Select.prototype.highlightOption = function(option) { var highlighted; highlighted = this.drop.querySelector('.select-option-highlight'); if (highlighted != null) { removeClass(highlighted, 'select-option-highlight'); } addClass(option, 'select-option-highlight'); return this.trigger('highlight', { option: option }); }; Select.prototype.moveHighlight = function(directionKeyCode) { var highlighted, highlightedIndex, newHighlight, options; if (!(highlighted = this.drop.querySelector('.select-option-highlight'))) { this.highlightOption(this.drop.querySelector('.select-option')); return; } options = this.drop.querySelectorAll('.select-option'); highlightedIndex = Array.prototype.indexOf.call(options, highlighted); if (!(highlightedIndex >= 0)) { return; } if (directionKeyCode === UP) { highlightedIndex -= 1; } else { highlightedIndex += 1; } if (highlightedIndex < 0 || highlightedIndex >= options.length) { return; } newHighlight = options[highlightedIndex]; this.highlightOption(newHighlight); return this.scrollDropContentToOption(newHighlight); }; Select.prototype.scrollDropContentToOption = function(option) { var contentBounds, optionBounds; if (this.content.scrollHeight > this.content.clientHeight) { contentBounds = getBounds(this.content); optionBounds = getBounds(option); return this.content.scrollTop = optionBounds.top - (contentBounds.top - this.content.scrollTop); } }; Select.prototype.selectHighlightedOption = function() { return this.pickOption(this.drop.querySelector('.select-option-highlight')); }; Select.prototype.pickOption = function(option, close) { var _this = this; if (close == null) { close = true; } this.value = this.select.value = option.getAttribute('data-value'); this.triggerChange(); if (close) { return setTimeout(function() { _this.close(); return _this.target.focus(); }); } }; Select.prototype.triggerChange = function() { var event; event = document.createEvent("HTMLEvents"); event.initEvent("change", true, false); this.select.dispatchEvent(event); return this.trigger('change', { value: this.select.value }); }; Select.prototype.change = function(val) { var options; options = this.findOptionsByValue(val); if (!options.length) { throw new Error("Select Error: An option with the value \"" + val + "\" doesn't exist"); } return this.pickOption(options[0], false); }; return Select; })(Evented); Select.init = function(options) { var el, _i, _len, _ref1, _results; if (options == null) { options = {}; } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', function() { return Select.init(options); }); return; } if (options.selector == null) { options.selector = 'select'; } _ref1 = document.querySelectorAll(options.selector); _results = []; for (_i = 0, _len = _ref1.length; _i < _len; _i++) { el = _ref1[_i]; if (!el.selectInstance) { _results.push(new Select(extend({ el: el }, options))); } else { _results.push(void 0); } } return _results; }; window.Select = Select; }).call(this);