Index: ajax_dropdown.js
===================================================================
--- ajax_dropdown.js (revision 13557)
+++ ajax_dropdown.js (working copy)
@@ -69,101 +69,293 @@
*/
-function AJAXDropDown(input_id, suggest_url_callback, get_value_callback)
-{
+function AJAXDropDown(input_id, suggest_url_callback, get_value_callback, $use_tags) {
this.Input = document.getElementById(input_id);
+
+ if (!$( jq('#div_' + input_id) ).length) {
+ $('<div id="div_' + input_id + '"></div>').insertAfter(this.Input);
+ }
+
+ this.Box = document.getElementById("div_"+input_id);
this.KeyUpWaiting = false;
this.KeyUpTimer = false;
- this.Box = '';
this.SuggestURLCallback = suggest_url_callback;
- if (!get_value_callback) get_value_callback = this.GetValue;
- this.GetValueCallback = get_value_callback;
+ this.GetValueCallback = $.isFunction(get_value_callback) ? get_value_callback : this.GetValue;
this.BoxOpen = false;
this.SelectedItem = false;
+
var obj = this;
- addLoadEvent(function() {obj.Init()});
+
+ $(document).ready(
+ function() {
+ obj.Init()
+ }
+ );
+
+ this.useTags = $use_tags === undefined ? false : $tags;
+
+ if (this.useTags) {
+ this.tagValue = '';
+ this.prevTagValue = '';
+ this.tags = {};
+ this.saveTagPos = 0;
+ this.newCaretPos = 0;
+ this.originalCaretPos = 0;
+ }
}
-AJAXDropDown.prototype.Init = function()
-{
+AJAXDropDown.prototype.Init = function() {
// draw box
- this.Box = document.createElement('DIV');
- document.body.appendChild(this.Box);
+ // this.Box = document.createElement('DIV');
+ // document.body.appendChild(this.Box);
+ // this.Box = addElement(this.Input.parentNode, 'div');
-// this.Box = addElement(this.Input.parentNode, 'div');
this.Box.style.display = 'none';
this.Box.style.zIndex = 99;
this.Box.style.position = 'absolute';
this.Box.style.overflow = 'auto';
- this.Box.className = 'suggest-box'
+ this.Box.className = 'suggest-box';
this.Input.setAttribute('autocomplete', 'off');
// add onkeyup
var obj = this;
- addEvent(this.Input, 'keyup', function(ev) {obj.KeyUp(ev)})
- addEvent(this.Input, 'blur', function(ev) {obj.Blur(ev)})
- addEvent(this.Box, 'scroll', function(ev) {if (obj.BlurWaiting) {window.clearTimeout(obj.BlurTimer);}});
- addEvent(this.Box, 'mouseup', function(ev) {obj.BlurWaiting = false});
+
+ $(this.Input)
+ .keydown (
+ function($e) {
+ if ($e.keyCode == 13) {
+ return false;
+ }
+ }
+ )
+ .keyup ( function(ev) { obj.KeyUp(ev) } )
+ .blur ( function(ev) { obj.Blur(ev) } );
+
+ $(this.Box)
+ .scroll(
+ function(ev) {
+ if (obj.BlurWaiting) {
+ window.clearTimeout(obj.BlurTimer);
+ }
+ }
+ )
+ .mouseup( function(ev) { obj.BlurWaiting = false } );
}
-AJAXDropDown.prototype.Blur = function(ev)
-{
- if (this.BlurWaiting) return;
+AJAXDropDown.prototype.Blur = function(ev) {
+ if (this.BlurWaiting) {
+ return;
+ }
+
+ var obj = this;
this.BlurWaiting = true;
- var obj = this;
- this.BlurTimer = window.setTimeout(function() {
+
+ this.BlurTimer = window.setTimeout(
+ function() {
obj.CloseBox();
this.BlurWaiting = false;
- }, 300)
+ }, 300
+ );
}
-AJAXDropDown.prototype.KeyUp = function(ev)
-{
- var e = !is.ie ? ev : window.event;
+AJAXDropDown.prototype.KeyUp = function(e) {
switch (e.keyCode) {
case 38: //arrow up
- if (!this.BoxOpen) break;
+ if (!this.BoxOpen) {
+ break;
+ }
this.SelectPrev();
break;
+
case 40: //arrow down
- if (!this.BoxOpen) break;
+ if (!this.BoxOpen) {
+ break;
+ }
this.SelectNext();
break;
- case 27: //Enter
- this.Input.value = this.OriginalValue;
- this.CloseBox();
+
+ case 27: //Escape
+ if (this.BoxOpen) {
+ this.Input.value = this.OriginalValue;
+
+ if (this.useTags) {
+ this.setCaretPosition(this.originalCaretPos);
+ }
+
+ this.CloseBox();
+ }
break;
- case 13: //Escape
+ case 13: //Enter
this.CloseBox();
break;
+
default:
- if (this.Input.value == '') return;
+// if (this.Input.value == '') return;
+ if (this.useTags) {
+ this.setTagValue();
+ }
+
var obj = this;
if (this.KeyUpWaiting && this.KeyUpTimer) {
window.clearTimeout(this.KeyUpTimer);
this.KeyUpTimer = false;
}
+
this.KeyUpWaiting = true;
- this.KeyUpTimer = window.setTimeout(function() {
+ this.KeyUpTimer = window.setTimeout(
+ function() {
obj.RequestSuggestions();
- }, 300)
+ }, 300
+ );
}
}
-AJAXDropDown.prototype.RequestSuggestions = function()
-{
- Request.makeRequest(this.SuggestURLCallback(this.Input.value), false, '', this.successCallback, this.errorCallback, 'reload', this);
+AJAXDropDown.prototype.parseTagString = function(tag_string) {
+ var value_end = tag_string.match(/[ ]*$/)[0];
+ tag_string += ',';
+
+ var rex = new RegExp("(^[ ]*\|[ ]*,[ ]*)", "g");
+ var separators = tag_string.match(rex);
+
+ var start = 0;
+ var end = 0;
+
+ for (var i = 0; i < separators.length - 1; i++) {
+ the_tag = tag_string.substring(0, tag_string.search(/[ ]*,[ ]*/)).replace(rex.compile('^' + separators[i]), '');
+
+ this.tags[i] = {
+ 'separator' : separators[i],
+ 'tag' : the_tag,
+ 'start' : start,
+ 'end' : end + the_tag.length
+ }
+
+ start += the_tag.length + separators[i + 1].length;
+ end += the_tag.length + separators[i + 1].length;
+ tag_string = tag_string.substring(tag_string.search(/[ ]*,[ ]*/) + separators[i+1].length);
+ }
+
+ this.tags['count'] = i;
+ this.tags['value_end'] = value_end;
}
-AJAXDropDown.prototype.successCallback = function (request, params, object) {
+
+AJAXDropDown.prototype.getCaretPosition = function() {
+ var element = this.Input;
+
+ if (document.selection) {
+ // The current selection
+ var range = document.selection.createRange();
+ // We'll use this as a 'dummy'
+ var stored_range = range.duplicate();
+ // Select all text
+ stored_range.moveToElementText(element);
+ // Now move 'dummy' end point to end point of original range
+ stored_range.setEndPoint('EndToEnd', range);
+ // Now we can calculate start and end points
+ element.selectionStart = stored_range.text.length - range.text.length;
+ element.selectionEnd = element.selectionStart + range.text.length;
+ }
+
+ return element.selectionEnd;
+}
+
+AJAXDropDown.prototype.setCaretPosition = function(caretPos) {
+ if (this.Input.createTextRange) {
+ var range = this.Input.createTextRange();
+ range.move('character', caretPos);
+ range.select();
+ }
+ else {
+ if (this.Input.selectionStart) {
+ this.Input.focus();
+ this.Input.setSelectionRange(caretPos, caretPos);
+ }
+ else {
+ this.Input.focus();
+ }
+ }
+}
+
+AJAXDropDown.prototype.setTagValue = function() {
+ var endPos = this.getCaretPosition();
+
+ this.parseTagString(this.Input.value);
+
+ for (pos = 0; pos < this.tags.count; pos++) {
+ if (endPos >= this.tags[pos].start && endPos <= this.tags[pos].end) {
+ this.tagValue = this.tags[pos].tag;
+ this.saveTagPos = pos;
+ break;
+ }
+ }
+
+ this.originalCaretPos = endPos;
+}
+
+
+AJAXDropDown.prototype.RequestSuggestions = function() {
+ var $me = this;
+
+ if (this.useTags) {
+ if (this.tagValue == '') {
+ this.CloseBox();
+ return;
+ }
+
+ if (this.prevTagValue != this.tagValue) {
+ this.prevTagValue = this.tagValue;
+
+ var $me = this;
+ var $url = this.SuggestURLCallback(this.tagValue);
+
+ if ($url === false) {
+ // do nothing
+ return ;
+ }
+
+ $.get(
+ $url,
+ function ($data) {
+ $me.successCallback($data, 'reload', $me);
+ },
+ 'xml'
+ );
+ }
+ }
+ else {
+ var $url = this.SuggestURLCallback(this.Input.value);
+
+ if ($url === false) {
+ // do nothing
+ return ;
+ }
+
+ $.get(
+ $url,
+ function ($data) {
+ $me.successCallback($data, 'reload', $me);
+ },
+ 'xml'
+ );
+ }
+}
+
+AJAXDropDown.prototype.successCallback = function ($xml, params, object) {
object.OriginalValue = object.Input.value;
- object.OpenBox();
object.KeyUpWaiting = false;
- var items = request.responseXML.getElementsByTagName('item');
object.ClearItems();
- for (var i=0; i<items.length; i++)
- {
- object.AddItem(items[i].firstChild.nodeValue, items[i].attributes);
+
+ var items = $xml.getElementsByTagName('item');
+
+ if (items.length > 0) {
+ for (var i = 0; i < items.length; i++) {
+ object.AddItem(items[i].firstChild.nodeValue, items[i].attributes);
+ }
+
+ object.OpenBox();
+ } else {
+ object.CloseBox();
}
}
@@ -171,108 +363,185 @@
this.KeyUpWaiting = false;
}
-AJAXDropDown.prototype.ClearItems = function()
-{
+AJAXDropDown.prototype.ClearItems = function() {
this.Box.scrollTop = 0;
this.UnselectItem();
- for (var i=this.Box.childNodes.length-1; i>=0; i--)
- {
+
+ for (var i = this.Box.childNodes.length - 1; i >= 0; i--) {
this.Box.removeChild(this.Box.childNodes[i]);
}
}
-AJAXDropDown.prototype.OpenBox = function()
-{
+
+AJAXDropDown.prototype.getBoxHeight = function() {
+ if (this.Box.style.display != 'block') {
+ this.Box.style.display ='block';
+ var box_height = this.Box.clientHeight;
+ this.Box.style.display = 'none';
+
+ return box_height;
+ }
+
+ return this.Box.clientHeight;
+}
+
+AJAXDropDown.prototype.OpenBox = function() {
+ this.Box.style.height = 'auto';
+
var pos = findPos(this.Input);
var dim = getDimensions(this.Input);
- this.Box.style.left = pos[0] + 'px';
- this.Box.style.top = (pos[1] + dim.innerHeight + dim.borders[0] + dim.borders[2]) + 'px';
- this.Box.style.width = (dim.innerWidth + dim.borders[1] + (is.ie ? dim.borders[3] : 0 ) ) + 'px';
+
+ var input_height = dim.innerHeight + dim.borders[0] + dim.borders[2];
+ var input_width = dim.innerWidth + dim.borders[1] + (document.all ? dim.borders[3] : 0 );
+
+ this.Box.style.width = input_width + 'px';
+ var box_height = this.getBoxHeight();
+
+ var box_left = pos[0];
+ var box_top = pos[1] + input_height;
+ var container = document.body;
+
+ var scroll_container = $('#scroll_container').get(0);
+
+ if (scroll_container) {
+ var xpos = findPos(scroll_container);
+ box_left -= xpos[0];
+ box_top -= xpos[1];
+ container = scroll_container;
+ }
+
+ if (box_top + box_height > container.offsetHeight) {
+ box_top = box_top - input_height - box_height;
+ }
+
+ this.Box.style.left = box_left + 'px';
+ this.Box.style.top = box_top + 'px';
+
this.Box.style.display = 'block';
-// alert('box opened at '+this.Box.style.left+','+this.Box.style.top+' pos x:'+pos[0])
this.BoxOpen = true;
}
-AJAXDropDown.prototype.CloseBox = function()
-{
- if (!this.BoxOpen) return;
+AJAXDropDown.prototype.CloseBox = function() {
+ if (!this.BoxOpen) {
+ return;
+ }
+
this.Box.style.display = 'none';
this.BoxOpen = false;
}
-AJAXDropDown.prototype.AddItem = function(value, attributes)
-{
+AJAXDropDown.prototype.AddItem = function(value, attributes) {
var item = addElement(this.Box, 'div');
- for (var i=0; i<attributes.length; i++) {
- item.setAttribute(attributes[i].nodeName, attributes[i].nodeValue)
+ for (var i = 0; i < attributes.length; i++) {
+ item.setAttribute(attributes[i].nodeName, attributes[i].nodeValue);
}
+
item.className = 'suggest-item';
item.innerHTML = value;
var obj = this;
- addEvent(item, 'mousemove', function() {obj.SelectItem(item)});
- addEvent(item, 'mousedown', function() {obj.ClickItem(item)});
+
+ $(item)
+ .mousemove( function() { obj.SelectItem(item) } )
+ .mousedown( function() { obj.ClickItem(item) } );
}
-AJAXDropDown.prototype.ClickItem = function(item)
-{
+AJAXDropDown.prototype.ClickItem = function(item) {
this.Input.value = this.GetValueCallback(item);
+
+ if (this.useTags) {
+ this.setCaretPosition(this.newCaretPos);
+ }
+
this.CloseBox();
}
-AJAXDropDown.prototype.SelectNext = function()
-{
+AJAXDropDown.prototype.SelectNext = function() {
if (!this.SelectedItem) {
this.SelectItem(this.Box.firstChild, true);
return;
}
+
if (isset(this.SelectedItem.nextSibling)) {
- this.SelectItem(this.SelectedItem.nextSibling, true)
+ this.SelectItem(this.SelectedItem.nextSibling, true);
}
else { // down from last
this.UnselectItem();
this.Input.value = this.OriginalValue;
+
+ if (this.useTags) {
+ this.setCaretPosition(this.originalCaretPos);
+ }
}
}
-AJAXDropDown.prototype.SelectPrev = function()
-{
+AJAXDropDown.prototype.SelectPrev = function() {
if (!this.SelectedItem) {
this.SelectItem(this.Box.lastChild, true);
return;
}
+
if (isset(this.SelectedItem.previousSibling)) {
this.SelectItem(this.SelectedItem.previousSibling, true)
}
else { // up from first
this.UnselectItem();
this.Input.value = this.OriginalValue;
+
+ if (this.useTags) {
+ this.setCaretPosition(this.originalCaretPos);
+ }
}
}
-AJAXDropDown.prototype.UnselectItem = function(item)
-{
- if (!item) item = this.SelectedItem;
- if (!item) return;
+AJAXDropDown.prototype.UnselectItem = function(item) {
+ if (!item) {
+ item = this.SelectedItem;
+ }
+
+ if (!item) {
+ return;
+ }
+
item.className = 'suggest-item';
this.SelectedItem = false;
}
-
-AJAXDropDown.prototype.SelectItem = function(item, setvalue)
-{
+AJAXDropDown.prototype.SelectItem = function(item, setvalue) {
if (this.SelectedItem) {
this.UnselectItem(this.SelectedItem);
}
+
item.className = 'suggest-item-over';
this.SelectedItem = item;
- item.scrollIntoView(false);
+// item.scrollIntoView(false);
if (setvalue) {
this.Input.value = this.GetValueCallback(item);
+
+ if (this.useTags) {
+ this.setCaretPosition(this.newCaretPos);
+ }
}
}
-AJAXDropDown.prototype.GetValue = function(item)
-{
- return item.innerHTML;
-}
+AJAXDropDown.prototype.GetValue = function(item) {
+ if (!this.useTags) {
+ return item.innerHTML;
+ }
+
+ var res = '';
+
+ for (pos = 0; pos < this.tags.count; pos++) {
+ res += this.tags[pos].separator;
+
+ if (pos == this.saveTagPos) {
+ res += item.innerHTML;
+ this.prevTagValue = item.innerHTML;
+ this.newCaretPos = res.length;
+ } else {
+ res += this.tags[pos].tag;
+ }
+ }
+
+ return res;
+}
\ No newline at end of file