Page Menu
Home
In-Portal Phabricator
Search
Configure Global Search
Log In
Files
F847685
in-portal
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Sat, Apr 19, 11:47 AM
Size
16 KB
Mime Type
text/x-diff
Expires
Mon, Apr 21, 11:47 AM (17 h, 35 m)
Engine
blob
Format
Raw Data
Handle
602488
Attached To
rINP In-Portal
in-portal
View Options
Index: branches/5.1.x/core/admin_templates/js/ajax_dropdown.js
===================================================================
--- branches/5.1.x/core/admin_templates/js/ajax_dropdown.js (revision 13595)
+++ branches/5.1.x/core/admin_templates/js/ajax_dropdown.js (revision 13596)
@@ -1,278 +1,547 @@
/*
(c) Intechnic Europe 2007 http://www.intechnic.lv
Author: Konstantin Tjuterev
The class adds a dynamic drop-down to the edit-box
Usage:
<input type="text" id="combo_input" style="width: 200px"><br/>
<input type="text" id="combo_input1" style="width: 200px"><br/>
<script type="text/javascript">
new AJAXDropDown('combo_input', function(cur_value) {return 'items.xml?'}, function(item) {
var val = item.getAttribute('a');
return val ? val : item.innerHTML;
});
new AJAXDropDown('combo_input1', function(cur_value) {return 'items1.xml?cur='+encodeURIComponent(cur_value)});
new AJAXDropDown('curr_search_keyword', function(cur_value) {
var $url = '<inp2:m_Link template="dummy" pass="m,curr" curr_event="OnSuggestValues" field="Name" cur_value="#CUR_VALUE#" no_amp="1"/>';
return $url.replace('#CUR_VALUE#', encodeURIComponent(cur_value));
}
);
</script>
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
<item> element will be used as the value
<item> 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:
<suggestions>
<item>Suggestion 1</item>
<item>Suggestion 2</item>
<item>Suggestion 3</item>
</suggestions>
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:
<style type="text/css">
.suggest-box {
border: 1px solid #999;
background-color: #fff;
}
.suggest-item, .suggest-item-over {
padding: 1px 2px 0px 2px;
font-family: arial,verdana;
font-size: 12px;
}
.suggest-item-over {
background-color: #3366CC;
color: #fff;
}
</style>
*/
-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;
- this.BlurWaiting = true;
+AJAXDropDown.prototype.Blur = function(ev) {
+ if (this.BlurWaiting) {
+ return;
+ }
+
var obj = this;
- this.BlurTimer = window.setTimeout(function() {
+ this.BlurWaiting = true;
+
+ 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.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.successCallback = function (request, params, object) {
+
+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();
}
}
AJAXDropDown.prototype.errorCallback = function (request, params, object) {
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;
}
Event Timeline
Log In to Comment