forked from madrobby/scripty2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add choiceAttribute option to allow objects as choices and listTempla…
…te option to allow custom list item schema
- Loading branch information
Showing
2 changed files
with
96 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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,80 +128,86 @@ | |
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 | ||
* | ||
* Triggers an update of the choices presented to the user. If results | ||
* 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, "<b>$&</b>") : | ||
result; | ||
li = new Element('li').update(text); | ||
value.replace(needle, "<b>$&</b>") : | ||
value; | ||
|
||
li = new Element('li').update(Object.isUndefined(opt.listTemplate) ? text : opt.listTemplate.evaluate({text: text, object: result})); | ||
This comment has been minimized.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
savetheclocktower
|
||
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,42 +219,42 @@ | |
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', { | ||
instance: this, | ||
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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
2 comments
on commit 586102b
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like this. Suggestions:
- Can we call it
choiceTemplate
instead oflistTemplate
? - How about changing
choiceAttribute
tochoiceValue
and have it be a function? The default function could bePrototype.K
or else a simple function that callstoString
.
choiceValue: function(choice) { return choice.name; }
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good to me, that makes things cleaner and even more flexible.
Object.isTemplate would be sweet, shall a patch to Prototype be useful?