| /* |
| * 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; |
| } |
| |