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) { + $('
').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 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