blob: a4e26e54eef3c2c95cab71f29761a58480b53175 [file] [log] [blame]
/*
* Copyright 2004-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Graeme Rocher (graemerocher@yahoo.co.uk)
*
*/
var Grails = {
Version: '0.1a-alphha'
}
// additional prototype
String.prototype.trim = function() {
return this.replace(/^\s*|\s*$/g,"");
}
Grails.InnerHTMLUpdater = Class.create();
Grails.InnerHTMLUpdater.prototype = {
initialize: function(element) {
this.element = element;
this.onComplete = null;
this.options = null;
this.debug = false;
if(arguments.length > 1) {
this.options = arguments[1];
}
if(this.options != null) {
this.onComplete = this.options.complete;
if(this.options.debug != undefined) {
this.debug = this.options.debug;
}
}
},
ajaxUpdate: function(ajaxResponse) {
if(this.debug) {
alert( "Ajax Results: " + RicoUtil.getContentAsString(ajaxResponse) );
}
$(this.element).innerHTML = RicoUtil.getContentAsString(ajaxResponse);
if(this.onComplete != null) {
this.onComplete( this.element );
}
}
}
/**
* DHTMLHelper class for attaching events, locating positions and creating elements etc.
*/
Grails.DHTMLHelper = Class.create();
Grails.DHTMLHelper.prototype = {
initialize: function(){
this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
},
createElement: function(type,container) {
var el = null;
if (document.createElementNS) {
el = document.createElementNS("http://www.w3.org/1999/xhtml", type);
} else {
el = document.createElement(type);
}
if (container != undefined) {
container.appendChild(el);
}
return el;
},
getAbsolutePos: function(el) {
var SL = 0, ST = 0;
var is_div = /^div$/i.test(el.tagName);
if (is_div && el.scrollLeft)
SL = el.scrollLeft;
if (is_div && el.scrollTop)
ST = el.scrollTop;
var r = { x: el.offsetLeft - SL, y: el.offsetTop - ST };
if (el.offsetParent) {
var tmp = this.getAbsolutePos(el.offsetParent);
r.x += tmp.x;
r.y += tmp.y;
}
return r;
},
removeClass: function(element, className) {
if (!(element && element.className)) {
return;
}
var classNames = element.className.split(" ");
var a = new Array();
for(var i = 0; i < classNames.length; i++) {
if(classNames[i] != className)
a.push(classNames[i]);
}
element.className = a.join(" ");
},
addClass: function(element, className) {
this.removeClass(element, className);
element.className += " " + className;
},
addEvent: function(el, evname, func) {
if (el.attachEvent) { // IE
el.attachEvent("on" + evname, func);
} else if (el.addEventListener) { // Gecko / W3C
el.addEventListener(evname, func, true);
} else {
el["on" + evname] = func;
}
},
removeEvent: function(el, evname, func) {
if (el.detachEvent) { // IE
el.detachEvent("on" + evname, func);
} else if (el.removeEventListener) { // Gecko / W3C
el.removeEventListener(evname, func, true);
} else {
el["on" + evname] = null;
}
}
}
var dhtmlHelper = new Grails.DHTMLHelper();
/**
* Creates an auto-complete field from the supplied parameters
* @param container The parent of the element to place the results into
* @param id The id of the div to place the results into
* @param field The field which requires auto-complete
* @param delegate A function which takes the field as an argument
* @param parent An options map
*/
Grails.AutoComplete = Class.create();
Grails.AutoComplete.prototype = {
initialize: function(id, field, url,options) {
this.container = document.body;
this.id = id;
this.autoCompleteField = $(field);
this.url = url;
this.options = options;
this.minLength = 1;
this.onDisplay = null;
this.onSelect = null;
this.cache = new Array();
this.useCache = true;
this.useCSS = false;
this.fontColor = "darkblue";
this.bgColor = "white";
this.overColor = "white";
this.overBgColor = "darkblue";
this.appendResult = false;
if(this.options != null) {
if(this.options.parent != null)
this.container = $(parent);
if(this.options.display != null)
this.onDisplay = this.options.display;
if(this.options.minLength != null)
this.minLength = this.options.minLength;
if(this.options.appendResult != null)
this.appendResult = this.options.appendResult;
if(this.options.select != null)
this.onSelect = this.options.select;
if(this.options.css != null)
this.useCSS = this.options.css;
if(this.options.cache != null)
this.useCache = this.options.cache;
if(this.options.fontColor != null)
this.fontColor = this.options.fontColor;
if(this.options.bgColor != null)
this.bgColor = this.options.bgColor;
if(this.options.overColor != null)
this.overColor = this.options.overColor;
if(this.options.overBgColor != null)
this.overBgColor = this.options.overBgColor;
}
this._registerRequestInfo();
this._createAutoCompleteField();
},
_registerRequestInfo: function() {
ajaxEngine.registerRequest(this.id,this.url);
ajaxEngine.registerAjaxObject(this.id, this);
},
_createAutoCompleteField: function() {
var parent = this.container;
var el = dhtmlHelper.createElement("div", parent);
el.id = this.id;
el.className = "autoComplete " + this.id;
el.style.display = "none";
el.style.position = "absolute";
dhtmlHelper.addEvent(
this.autoCompleteField,
"blur",
function(e) {
if(el != null)
setTimeout( function() { el.style.display = "none" }, 500)
}
)
dhtmlHelper.addEvent(
this.autoCompleteField,
"keyup",
this._keyUpEventHandler.bindAsEventListener(this)
);
},
_keyUpEventHandler: function(e) {
if(!e)e = window.event;
if(e.keyCode == 40 || e.keyCode == 38) {
var autoCompleteResults = document.getElementsByTagAndClassName("div", "autoCompleteResult");
var autoCompleteResultsSelected = document.getElementsByTagAndClassName("div", "autoCompleteResultOver");
// if none are selected select the first result
if(autoCompleteResultsSelected.length == 0) {
if(autoCompleteResults.length > 0){
dhtmlHelper.removeClass(autoCompleteResults[0],"autoCompleteResult");
dhtmlHelper.addClass(autoCompleteResults[0],"autoCompleteResultOver");
if(this.appendResult)
this._handleTextSelection(autoCompleteResults[0]);
}
}
// otherwise get the current selected index and select the next one
else {
var selectedIndex = 0;
var acHolder = autoCompleteResultsSelected[0].parentNode;
var acHolderChildren = acHolder.getElementsByTagName("div");
for(var i = 0; i < acHolderChildren.length;i++ ) {
if(autoCompleteResultsSelected[0] == acHolderChildren[i]) {
selectedIndex = i;
break;
}
}
dhtmlHelper.removeClass(acHolderChildren[selectedIndex],"autoCompleteResultOver");
dhtmlHelper.addClass(acHolderChildren[selectedIndex],"autoCompleteResult");
if(e.keyCode == 40) {
selectedIndex++;
}else {
selectedIndex--;
}
if(selectedIndex > -1 && selectedIndex < acHolderChildren.length) {
dhtmlHelper.removeClass(acHolderChildren[selectedIndex],"autoCompleteResult");
dhtmlHelper.addClass(acHolderChildren[selectedIndex],"autoCompleteResultOver");
if(this.appendResult)
this._handleTextSelection(acHolderChildren[selectedIndex]);
}
}
}
else if(e.keyCode == 13 && !this.appendResult) {
var autoCompleteResultsSelected = document.getElementsByTagAndClassName("div", "autoCompleteResultOver");
if(autoCompleteResultsSelected.length > 0) {
// call click handler pretending this is an event
this._resultClickHandler( { target:autoCompleteResultsSelected[0] } );
}
}
// if search if enter is not pressed
else if(e.keyCode != 13) {
if(this.autoCompleteField.value.length > this.minLength) {
if(this.useCache) {
// if available in the cache
if(this.cache[this.autoCompleteField.value] != null) {
this._displayCachedResults(this.autoCompleteField.value);
}
else {
ajaxEngine.sendRequest(this.id,
this.autoCompleteField.name + "=" + this.autoCompleteField.value);
}
}
else {
ajaxEngine.sendRequest(this.id,
this.autoCompleteField.name + "=" + this.autoCompleteField.value);
}
}
}
},
ajaxUpdate: function(ajaxResponse) {
// cache the response
var ajaxResponseAsString = RicoUtil.getContentAsString(ajaxResponse);
this.cache[this.autoCompleteField.value] = ajaxResponseAsString;
$(this.id).innerHTML = ajaxResponseAsString;
var autoCompleteResults = document.getElementsByTagAndClassName("div", "autoCompleteResult");
var autoCompleteResultsSelected = document.getElementsByTagAndClassName("div", "autoCompleteResultOver");
// if none are selected select the first result
if(autoCompleteResultsSelected.length == 0 && this.appendResult) {
if(autoCompleteResults.length > 0){
dhtmlHelper.removeClass(autoCompleteResults[0],"autoCompleteResult");
dhtmlHelper.addClass(autoCompleteResults[0],"autoCompleteResultOver");
this._handleTextSelection(autoCompleteResults[0]);
}
}
// attach behaviours
this._attachResultBehaviours(autoCompleteResults);
// display the results
this._displayResults(autoCompleteResults);
},
_displayCachedResults: function(key) {
var el = $(this.id);
el.innerHTML = this.cache[this.autoCompleteField.value];
var autoCompleteResults = document.getElementsByTagAndClassName("div", "autoCompleteResult");
var autoCompleteResultsSelected = document.getElementsByTagAndClassName("div", "autoCompleteResultOver");
// if none are selected select the first result
if(autoCompleteResultsSelected.length == 0 && this.appendResult) {
if(autoCompleteResults.length > 0){
dhtmlHelper.removeClass(autoCompleteResults[0],"autoCompleteResult");
dhtmlHelper.addClass(autoCompleteResults[0],"autoCompleteResultOver");
this._handleTextSelection(autoCompleteResults[0]);
}
}
// attach behaviours
this._attachResultBehaviours(autoCompleteResults);
// display the results
this._displayResults(autoCompleteResults);
},
_handleTextSelection: function(selectedResult) {
var resultContent = selectedResult.innerHTML;
if(resultContent.indexOf(this.autoCompleteField.value) == 0) {
var i = resultContent.indexOf( this.autoCompleteField.value );
var appendValue = resultContent.substring((i + this.autoCompleteField.value.length),resultContent.length);
this.autoCompleteField.value = this.autoCompleteField.value + appendValue;
}
else {
if(dhtmlHelper.isIE) {
if(document.selection != null){
var selectionRange = document.selection.createRange();
selectionRange.text = "";
}
var oldLength = this.autoCompleteField.value.length;
this.autoCompleteField.value = this.autoCompleteField.value + resultContent;
var range = this.autoCompleteField.createTextRange();
range.moveStart("character",oldLength);
range.moveEnd("character",this.autoCompleteField.value.length);
range.select();
}
else {
if (this.autoCompleteField.selectionEnd > 0) {
this.autoCompleteField.value = this.autoCompleteField.value.substring(0,this.autoCompleteField.selectionStart);
}
var oldLength = this.autoCompleteField.value.length;
this.autoCompleteField.value = this.autoCompleteField.value + resultContent;
this.autoCompleteField.selectionStart = oldLength;
this.autoCompleteField.selectionEnd = this.autoCompleteField.value.length;
}
}
},
_displayResults: function(results) {
var pos = dhtmlHelper.getAbsolutePos(this.autoCompleteField);
var el = $(this.id);
// position results pane
el.style.left = pos.x + "px";
el.style.top = (pos.y + this.autoCompleteField.offsetHeight) + "px";
// if there are no results hide
if(results.length == 0) {
el.style.display = "none";
return;
}
else {
el.style.display = "";
if(this.onDisplay != null) {
this.onDisplay( el );
}
}
},
_attachResultBehaviours: function(results) {
for(var i = 0; i < results.length;i++) {
dhtmlHelper.addEvent(
results[i],
"mouseover",
this._resultMouseOverHandler.bindAsEventListener(this)
);
dhtmlHelper.addEvent(
results[i],
"mouseout",
this._resultMouseOutHandler.bindAsEventListener(this)
);
dhtmlHelper.addEvent(
results[i],
"click",
this._resultClickHandler.bindAsEventListener(this)
);
}
},
_resultMouseOverHandler: function(e) {
if(!e)e = window.event;
var src = e.srcElement;
if(!src)src = e.target;
dhtmlHelper.removeClass(src,"autoCompleteResult");
dhtmlHelper.addClass(src,"autoCompleteResultOver");
if(!this.useCSS) {
src.style.bgColor = this.overBgColor;
src.style.fontColor = this.overColor;
}
},
_resultMouseOutHandler: function(e) {
if(!e)e = window.event;
var src = e.srcElement;
if(!src)src = e.target;
dhtmlHelper.removeClass(src,"autoCompleteResultOver");
dhtmlHelper.addClass(src,"autoCompleteResult");
if(!this.useCSS) {
src.style.bgColor = this.bgColor;
src.style.fontColor = this.fontColor;
}
},
_resultClickHandler: function(e) {
if(!e)e = window.event;
var src = e.srcElement;
if(!src)src = e.target;
if(this.appendResult) {
this._handleTextSelection(src);
}
else {
this.autoCompleteField.value = src.innerHTML;
}
$(this.id).style.display = "none";
if(this.onSelect != null) {
this.onSelect( $(this.id), src );
}
}
}
function createParamsForFields(fieldClass) {
var inputs = document.getElementsByTagAndClassName("INPUT", fieldClass);
var selects = document.getElementsByTagAndClassName("SELECT", fieldClass);
var params = new Array();
// process inputs first
for(var i = 0; i < inputs.length; i++ ) {
// if its a radio button only include checked value
if(inputs[i].type == "radio") {
if(inputs[i].checked) {
params[i] = {
name: inputs[i].name,
value: inputs[i].value
};
}
}
else if(inputs[i].type == "checkbox") {
if(inputs[i].checked) {
params[i] = {
name: inputs[i].name,
value: true
};
}
else {
params[i] = {
name: inputs[i].name,
value: false
};
}
}
else {
params[i] = {
name: inputs[i].name,
value: inputs[i].value
};
}
}
// process selects
for(var i = 0, j = params.length; i < selects.length; i++,j++) {
params[j] = {
name: selects[i].name,
value: selects[i].options[ selects[i].selectedIndex ].value
};
}
return params;
}
/**
* function for retrieving elements for the tag and className from an HTML element
*/
function getElementsByTagAndClassName(element,tagName, className) {
if ( tagName == null )
tagName = '*';
var children = element.getElementsByTagName(tagName) || document.all;
var elements = new Array();
if ( className == null )
return children;
for (var i = 0; i < children.length; i++) {
var child = children[i];
if(child.className == null) {
continue;
}
var classNames = child.className.split(' ');
for (var j = 0; j < classNames.length; j++) {
if (classNames[j] == className) {
elements.push(child);
break;
}
}
}
return elements;
}