Rather than submitting actual patches here you should submit them on a ticket in trac[1].
Be sure to include [PATCH] in the title so it comes up in the report of pending patches. If you only post it here the patch may end up getting lost. :) Lee [1] http://trac.turbogears.org/ On 12/27/05, Koba <[EMAIL PROTECTED]> wrote: > I forgot to include the files :P > > The diff was made against revision 381 (shouldn't matter). > The images go to the turbogears/widgets/static folder. > > > > > Index: turbogears/widgets/base.py > =================================================================== > --- turbogears/widgets/base.py (revision 381) > +++ turbogears/widgets/base.py (working copy) > @@ -474,8 +474,9 @@ > <script language="JavaScript" type="text/JavaScript"> > addLoadEvent(new > AutoCompleteManager('${widget.name}','${widget.searchController}').initialize); > </script> > -<input type="text" name="autoComplete${widget.name}" > id="autoComplete${widget.name}" value="${widget.value}"/> > -<div name="autoCompleteResults${widget.name}" > id="autoCompleteResults${widget.name}"/> > +<input type="text" name="autoComplete${widget.name}" > id="autoComplete${widget.name}" value="${widget.value}" > py:attrs="widget.attrs"/> > +<img name="autoCompleteSpinner${widget.name}" > id="autoCompleteSpinner${widget.name}" > src="/${std.url('tg_widgets/turbogears.widgets/spinnerstopped.png')}" > src2="/${std.url('tg_widgets/turbogears.widgets/spinner.gif')}" /> > +<div class="autoTextResults" name="autoCompleteResults${widget.name}" > id="autoCompleteResults${widget.name}"/> > </div> > """ > javascript = [mochikit, JSLink(static,"autocompletefield.js")] > Index: turbogears/widgets/static/autocompletefield.js > =================================================================== > --- turbogears/widgets/static/autocompletefield.js (revision 381) > +++ turbogears/widgets/static/autocompletefield.js (working copy) > @@ -6,19 +6,51 @@ > this.numResultRows = 0; > this.specialKeyPressed = false; > this.isShowingResults = false; > + this.hasFocus = false; > + this.processCount = 0; > + this.lastSearch = null; > + this.spinner = null; > + this.spinnerStatus = 'off'; > bindMethods(this); > }; > > AutoCompleteManager.prototype.initialize = function() { > // Text field must be set after page renders > this.textField = getElement("autoComplete" + this.id); > + this.spinner = getElement("autoCompleteSpinner"+this.id); > + this.spinnerOnImg = getNodeAttribute(this.spinner,'src2'); > + this.spinnerOffImg = getNodeAttribute(this.spinner,'src'); > > updateNodeAttributes(this.textField, { > "onkeyup": this.theKeyUp, > - "onkeydown": this.theKeyPress > + "onkeydown": this.theKeyPress, > + "onblur": this.lostFocus, > + "onfocus": this.gotFocus > }); > + > }; > > +AutoCompleteManager.prototype.spinnerToggle = function(newStatus) { > + if (this.spinner && this.spinnerStatus != newStatus) { > + if (this.spinnerStatus == 'on') { > + this.spinnerStatus = 'off'; > + this.spinner.src = this.spinnerOffImg; > + } else { > + this.spinnerStatus = 'on'; > + this.spinner.src = this.spinnerOnImg; > + } > + } > +} > + > +AutoCompleteManager.prototype.lostFocus = function(event) { > + this.hasFocus = false; > + this.clearResults(); > +} > + > +AutoCompleteManager.prototype.gotFocus = function(event) { > + this.hasFocus = true; > +} > + > AutoCompleteManager.prototype.theKeyPress = function(event) { > // Deal with crappy browser implementations > event=event||window.event; > @@ -59,7 +91,7 @@ > } > this.updateSelectedResult(); > this.specialKeyPressed = true; > - break; > + break; > default: > //pass > } > @@ -87,18 +119,38 @@ > // Clear out our result tracking > this.selectedResultRow = 0; > this.numResultRows = 0; > + this.lastSearch = null; > } > > AutoCompleteManager.prototype.theKeyUp = function(event) { > - // Stop processing if a special key has been preseed. > - if (this.specialKeyPressed) return false; > + // Stop processing if a special key has been preseed. Or if the last > search requested the same string > + if (this.specialKeyPressed | (this.textField.value==this.lastSearch)) > return false; > + > + // if this.textField.value is empty we don't need to send a request. > We have to clear the list. > + if (!this.textField.value) { > + this.clearResults(); > + return false; > + } > > // Get what we are searching for > var textFieldValue = this.textField.value; > > var performMagic = function(autoCompleteManager, result) { > - var fancyTable = TABLE({"border":0, "style": > {"position":"absolute"}},null); > - var fancyTableBody = TBODY(null,null); > + // if the field lost focus while processing this request, > don't do anything > + if (!autoCompleteManager.hasFocus) { > + autoCompleteManager.updateSelectedResult(); > + autoCompleteManager.processCount = > autoCompleteManager.processCount - 1; > + if (autoCompleteManager.processCount == 0) { > + autoCompleteManager.spinnerToggle('off'); > + } > + return false; > + } > + > + var fancyTable = TABLE({"class": "autoTextTable", > + "name":"autoCompleteTable"+autoCompleteManager.id, > + "id":"autoCompleteTable"+autoCompleteManager.id}, > + null); > + var fancyTableBody = TBODY(null,null); > > // Track number of result rows and reset the selected one to > the first > autoCompleteManager.numResultRows = result.textItems.length; > @@ -108,18 +160,24 @@ > autoCompleteManager.isShowingResults = false > for( var i in result.textItems ) { > var currentItem = result.textItems[i]; > - // What part of the text do we bold? > - var searchedText = > currentItem.toLowerCase().match(textFieldValue.toLowerCase()); > - var end = searchedText.index + searchedText[0].length; > - > - // Split each item into [String Start] [{BOLD{Matched > Text] and [String Remaining] > - var currentRow = TR({"class": "autoTextNormalRow", > - > "name":"autoComplete"+autoCompleteManager.id+i, > - > "id":"autoComplete"+autoCompleteManager.id+i}, > - TD(null, > - > SPAN({"class": "autoTextHighlight"}, currentItem.substr(searchedText.index, > searchedText[0].length)), > - > SPAN(null, currentItem.substr(end)) > - )); > + if (typeof result.options!="undefined" && > result.options.highlight) { > + var searchedText = > currentItem.toLowerCase().match(textFieldValue.toLowerCase()); > + var end = searchedText.index + > searchedText[0].length; > + var currentRow = TR({"class": > "autoTextNormalRow", > + > "name":"autoComplete"+autoCompleteManager.id+i, > + > "id":"autoComplete"+autoCompleteManager.id+i}, > + > TD(null, > + > SPAN({"class": "autoTextHighlight"}, currentItem.substr(searchedText.index, > searchedText[0].length)), > + > SPAN(null, currentItem.substr(end)) > + )); > + } else { > + var currentRow = TR({"class": > "autoTextNormalRow", > + > "name":"autoComplete"+autoCompleteManager.id+i, > + > "id":"autoComplete"+autoCompleteManager.id+i}, > + > TD(null, > + > SPAN(null, currentItem) > + )); > + } > appendChildNodes(fancyTableBody, currentRow); > > // Found at least 1 result > @@ -131,16 +189,53 @@ > var resultsHolder = > getElement("autoCompleteResults"+autoCompleteManager.id); > replaceChildNodes(resultsHolder, fancyTable); > > + var textField = getElement(autoCompleteManager.textField); > + var p = > document.getElementById("autoCompleteTable"+autoCompleteManager.id); > + if (p) { > + p.style.left = getLeft(textField)+"px"; > + p.style.top = getBottom(textField)+"px"; > + } > + > autoCompleteManager.updateSelectedResult(); > + autoCompleteManager.processCount = > autoCompleteManager.processCount - 1; > + if (autoCompleteManager.processCount == 0) { > + autoCompleteManager.spinnerToggle('off'); > + } > } > + > + this.processCount = this.processCount + 1; > + this.spinnerToggle('on'); > > + this.lastSearch = textFieldValue; > + > var params = { "tg_format" : "json", > "tg_random" : new Date().getTime(), > "searchString" : textFieldValue }; > - > + > // Grab a list of items that meets our search query, then pass it > with the AutoCompleteManager > var d = loadJSONDoc(this.searchController + "?" + > queryString(params)); > d.addCallback(performMagic, this); > + > + return true; > +}; > > - return true; > -}; > \ No newline at end of file > +function getLeft(s) { > + return getParentOffset(s,"offsetLeft") > +} > + > +function getTop(s) { > + return getParentOffset(s,"offsetTop") > +} > + > +function getBottom(s) { > + return s.offsetHeight+getTop(s) > +} > + > +function getParentOffset(s,offsetType) { > + var parentOffset=0; > + while(s) { > + parentOffset+=s[offsetType]; > + s=s.offsetParent > + } > + return parentOffset > +} > Index: turbogears/widgets/static/autocompletefield.css > =================================================================== > --- turbogears/widgets/static/autocompletefield.css (revision 381) > +++ turbogears/widgets/static/autocompletefield.css (working copy) > @@ -1,15 +1,28 @@ > -.autoTextHighlight { > - font-weight: bold; > -} > - > .autoTextResults { > position: absolute; > z-index: 100; > - background-color: #FF0000; > } > + > +.autoTextTable { > + background-color: #ffffff; > + border-top: 1px solid black; > + border-bottom: 1px solid black; > + border-left: 1px solid black; > + border-right: 1px solid black; > +} > + > .autoTextNormalRow { > - background-color: #CEE7FF; > + font-family: arial, sans-serif; > + font-size: 13px; > } > + > .autoTextSelectedRow { > - background-color: #59ACFF; > + background-color: #3366cc; > + color: #ffffff; > + font-family: arial, sans-serif; > + font-size: 13px; > } > + > +.autoTextHighlight { > + font-weight: bold; > +} > > > >

