From 586102b33ba68ad2697488233225628f7fb7e28f Mon Sep 17 00:00:00 2001 From: Sebastien Grosjean Date: Fri, 3 Sep 2010 17:43:26 +0300 Subject: [PATCH] Add choiceAttribute option to allow objects as choices and listTemplate option to allow custom list item schema --- src/ui/controls/autocompleter.js | 104 +++++++++++--------- test/functional/controls_autocompleter.html | 42 +++++++- 2 files changed, 96 insertions(+), 50 deletions(-) diff --git a/src/ui/controls/autocompleter.js b/src/ui/controls/autocompleter.js index 6b1ce55..10f773f 100644 --- a/src/ui/controls/autocompleter.js +++ b/src/ui/controls/autocompleter.js @@ -1,14 +1,14 @@ (function(UI) { - + /** section: scripty2 ui * class S2.UI.Autocompleter < S2.UI.Base * **/ UI.Autocompleter = Class.create(UI.Base, { NAME: "S2.UI.Autocompleter", - + /** * new S2.UI.Autocompleter(element, options) * @@ -17,25 +17,25 @@ initialize: function(element, options) { this.element = $(element); var opt = this.setOptions(options); - + UI.addClassNames(this.element, 'ui-widget ui-autocompleter'); - + this.input = this.element.down('input[type="text"]'); - + if (!this.input) { this.input = new Element('input', { type: 'text' }); this.element.insert(this.input); } - + this.input.insert({ before: this.button }); this.input.setAttribute('autocomplete', 'off'); - + this.name = opt.parameterName || this.input.readAttribute('name'); - + if (opt.choices) { this.choices = opt.choices.clone(); } - + this.menu = new UI.Menu(); this.element.insert(this.menu.element); @@ -47,17 +47,17 @@ top: (iLayout.get('top') + iLayout.get('margin-box-height')) + 'px' }); }).bind(this).defer(); - + this.observers = { blur: this._blur.bind(this), keyup: this._keyup.bind(this), keydown: this._keydown.bind(this), selected: this._selected.bind(this) }; - + this.addObservers(); }, - + addObservers: function() { this.input.observe('blur', this.observers.blur); this.input.observe('keyup', this.observers.keyup); @@ -66,19 +66,19 @@ this.menu.observe('ui:menu:selected', this.observers.selected); }, - + _schedule: function() { this._unschedule(); this._timeout = this._change.bind(this).delay(this.options.frequency); }, - + _unschedule: function() { if (this._timeout) window.clearTimeout(this._timeout); }, _keyup: function(event) { var value = this.input.getValue(); - + if (value) { if (value.blank() || value.length < this.options.minCharacters) { // Empty values mean the menu should be hidden and all timers @@ -95,16 +95,16 @@ this.menu.close(); this._unschedule(); } - + this._value = value; }, - + _keydown: function(event) { if (UI.modifierUsed(event)) return; if (!this.menu.isOpen()) return; - + var keyCode = event.keyCode || event.charCode; - + switch (event.keyCode) { case Event.KEY_UP: this.menu.moveHighlight(-1); @@ -128,21 +128,21 @@ break; } }, - + // TODO: Implement tokenizing. _getInput: function() { return this.input.getValue(); }, - + // TODO: Implement tokenizing. _setInput: function(value) { this.input.setValue(value); }, - + _change: function() { this.findChoices(); }, - + /** * S2.UI.Autocompleter#findChoices() -> undefined * @@ -150,58 +150,64 @@ * come from the server, this is an asynchronous operation. **/ findChoices: function() { - var value = this._getInput(); - var choices = this.choices || []; + var value = this._getInput(), + choices = this.choices || [], + choiceValue, + choiceAttribute = this.options.choiceAttribute; + var results = choices.inject([], function(memo, choice) { - if (choice.toLowerCase().include(value.toLowerCase())) { + choiceValue = Object.isUndefined(choiceAttribute) ? choice : choice[choiceAttribute] + if (choiceValue.toLowerCase().include(value.toLowerCase())) { memo.push(choice); } return memo; }); - + this.setChoices(results); }, - + setChoices: function(results) { this.results = results; this._updateMenu(results); }, - + _updateMenu: function(results) { var opt = this.options; - + this.menu.clear(); - + // Build a case-insensitive regexp for highlighting the substring match. var needle = new RegExp(RegExp.escape(this._value), 'i'); for (var i = 0, result, li, text; result = results[i]; i++) { + value = Object.isUndefined(opt.choiceAttribute) ? result : result[opt.choiceAttribute] + text = opt.highlightSubstring ? - result.replace(needle, "$&") : - result; - - li = new Element('li').update(text); + value.replace(needle, "$&") : + value; + + li = new Element('li').update(Object.isUndefined(opt.listTemplate) ? text : opt.listTemplate.evaluate({text: text, object: result})); li.store('ui.autocompleter.value', result); this.menu.addChoice(li); } - + if (results.length === 0) { this.menu.close(); } else { this.menu.open(); } }, - + _moveMenuChoice: function(delta) { var choices = this.list.down('li'); this._selectedIndex = (this._selectedIndex + delta).constrain( -1, this.results.length - 1); - + this._highlightMenuChoice(); }, - + _highlightMenuChoice: function(element) { var choices = this.list.select('li'), index; - + if (Object.isElement(element)) { index = choices.indexOf(element); } else if (Object.isNumber(element)) { @@ -213,13 +219,13 @@ UI.removeClassNames(choices, 'ui-state-active'); if (index === -1 || index === null) return; choices[index].addClassName('ui-state-active'); - + this._selectedIndex = index; }, - + _selected: function(event) { var memo = event.memo, li = memo.element; - + if (li) { var value = li.retrieve('ui.autocompleter.value'); var result = this.element.fire('ui:autocompleter:selected', { @@ -227,28 +233,28 @@ value: value }); if (result.stopped) return; - this._setInput(value); + this._setInput(Object.isUndefined(this.options.choiceAttribute) ? value : value[this.options.choiceAttribute]); } this.menu.close(); }, - + _blur: function(event) { this._unschedule(); this.menu.close(); } }); - + Object.extend(UI.Autocompleter, { DEFAULT_OPTIONS: { tokens: [], frequency: 0.4, minCharacters: 1, - + highlightSubstring: true, - + onShow: Prototype.K, onHide: Prototype.K } }); - + })(S2.UI); \ No newline at end of file diff --git a/test/functional/controls_autocompleter.html b/test/functional/controls_autocompleter.html index 61f6732..1e7dade 100644 --- a/test/functional/controls_autocompleter.html +++ b/test/functional/controls_autocompleter.html @@ -142,6 +142,46 @@

Standard Autocompleter

  • The matching substring of a given choice should be bold.
  • - + + +

    Autocompleter using Objects as choices and Template to style list items

    + + + + + + + +
    + The choices are the scripty2 contributors at test's creation date. It should behave like a standard autocompleter, with a few exceptions: + +
    \ No newline at end of file