Index: branches/5.2.x/core/admin_templates/js/ajax_dropdown.js =================================================================== --- branches/5.2.x/core/admin_templates/js/ajax_dropdown.js (revision 16152) +++ branches/5.2.x/core/admin_templates/js/ajax_dropdown.js (revision 16153) @@ -1,556 +1,549 @@ /* (c) Intechnic Europe 2007 http://www.intechnic.lv Author: Konstantin Tjuterev The class adds a dynamic drop-down to the edit-box Usage:

The AJAXDropDown constructor takes the following arguments: input_id - id of textbox to attach to suggest_url_callback - function which should return URL which returns XML of suggested values, function takes one argument - it is current value of the text_box [get_value_callback] - optional argument - function which returns the value to be set in the textbox, based on currently selected item (out of suggested). If the argument is skipped - the node value of element will be used as the value element in the suggestions XML may have unlimited number of attribuets, all the attributes will be set to the corresponding DIV element and this DIV element will be passed as argument to the get_value_callback, so one may use the argument to alter the value set into textbox as in the example above Response XML structure: The script will look for ALL elements with tagname = 'item', so basically the structure should look like this: Suggestion 1 Suggestion 2 Suggestion 3 Design & CSS The script will automatically display a div which width will match the width of the text-box You may control the div look by changing .suggest-box CSS selector The items inside the box will have .suggest-item and .suggest-item-over The default styles which may be used is: */ function AJAXDropDown(input_id, suggest_url_callback, get_value_callback, $custom_params) { this.useTags = false; this.maxWidth = false; if ($custom_params !== undefined) { $.extend(this, $custom_params); } this.Input = document.getElementById(input_id); if (!$( jq('#div_' + input_id) ).length) { $('
').appendTo('body'); // .insertAfter(this.Input); } this.Box = document.getElementById("div_"+input_id); this.KeyUpWaiting = false; this.KeyUpTimer = false; this.SuggestURLCallback = suggest_url_callback; this.GetValueCallback = $.isFunction(get_value_callback) ? get_value_callback : this.GetValue; this.BoxOpen = false; this.SelectedItem = false; var obj = this; $(document).ready( function() { obj.Init() } ); if (this.useTags) { this.tagValue = ''; this.prevTagValue = ''; this.tags = {}; this.saveTagPos = 0; this.newCaretPos = 0; this.originalCaretPos = 0; } } AJAXDropDown.prototype.Init = function() { // draw box // this.Box = document.createElement('DIV'); // document.body.appendChild(this.Box); // 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'; // add onkeyup var obj = this; $(this.Input) .attr('autocomplete', 'off') - .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; } var obj = this; this.BlurWaiting = true; this.BlurTimer = window.setTimeout( function() { obj.CloseBox(); this.BlurWaiting = false; }, 300 ); } AJAXDropDown.prototype.KeyUp = function(e) { switch (e.keyCode) { case 38: //arrow up if (!this.BoxOpen) { break; } this.SelectPrev(); break; case 40: //arrow down if (!this.BoxOpen) { break; } this.SelectNext(); break; case 27: //Escape if (this.BoxOpen) { this.Input.value = this.OriginalValue; if (this.useTags) { this.setCaretPosition(this.originalCaretPos); } this.CloseBox(); } break; case 13: //Enter this.CloseBox(); break; default: // 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() { obj.RequestSuggestions(); }, 300 ); } } 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.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.KeyUpWaiting = false; object.ClearItems(); 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(); } } AJAXDropDown.prototype.errorCallback = function (request, params, object) { this.KeyUpWaiting = false; } AJAXDropDown.prototype.ClearItems = function() { this.Box.scrollTop = 0; this.UnselectItem(); for (var i = this.Box.childNodes.length - 1; i >= 0; i--) { this.Box.removeChild(this.Box.childNodes[i]); } } AJAXDropDown.prototype.OpenBox = function() { var $box = $(this.Box); var $input = $(this.Input); var $input_position = $(this.Input).offset(); $box.css('height', 'auto'); // try to place suggest-box below input var $input_width = $input.outerWidth(); var $input_height = $input.outerHeight(); if (this.maxWidth === false) { $new_box_width = $input.width(); if (document.all) { $new_box_width += ($input_width - $input.innerWidth()); // add borders for IE } } else { var $new_box_width = this.maxWidth; } if ($box.width() < $new_box_width) { // suggest-box width at lease same as input's width $box.css('width', $new_box_width); } var $box_left = $input_position.left; var $box_top = $input_position.top + $input_height; var $box_height = $box.outerHeight(); if ($box_top + $box_height > document.body.offsetHeight) { // move above the box $box_top -= ($input_height + $box_height); } /*var $scroll_container = $($input).parents('#scroll_container_container'); var $scroll_container_offset_top = $scroll_container.length > 0 ? $scroll_container.scrollTop() - $scroll_container.offset().top : 0; $box_top += $scroll_container_offset_top;*/ $(this.Box) .css( { left: $box_left, top: $box_top } ) .show(); this.BoxOpen = true; } AJAXDropDown.prototype.CloseBox = function() { if (!this.BoxOpen) { return; } this.Box.style.display = 'none'; this.BoxOpen = false; } 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); } item.className = 'suggest-item'; item.innerHTML = value; var obj = this; $(item) .mousemove( function() { obj.SelectItem(item) } ) .mousedown( function() { obj.ClickItem(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() { if (!this.SelectedItem) { this.SelectItem(this.Box.firstChild, true); return; } if (isset(this.SelectedItem.nextSibling)) { 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() { 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; } item.className = 'suggest-item'; this.SelectedItem = false; } AJAXDropDown.prototype.SelectItem = function(item, setvalue) { if (this.SelectedItem) { this.UnselectItem(this.SelectedItem); } item.className = 'suggest-item-over'; this.SelectedItem = item; // item.scrollIntoView(false); if (setvalue) { this.Input.value = this.GetValueCallback(item); if (this.useTags) { this.setCaretPosition(this.newCaretPos); } } } 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; }