From 3a30ede1c03fdd097398b14734822f7ce8e46b6b Mon Sep 17 00:00:00 2001 From: wei <> Date: Sat, 17 Jun 2006 10:28:26 +0000 Subject: Update TAutoComplete, OnSuggest event for getting suggestions. --- framework/Web/Javascripts/extended/base.js | 141 +++++++++++++++--- framework/Web/Javascripts/js/ajax.js | 16 ++- framework/Web/Javascripts/js/prado.js | 10 +- framework/Web/Javascripts/prado/activecontrols3.js | 65 ++++++--- framework/Web/Javascripts/prado/controls.js | 4 +- framework/Web/UI/ActiveControls/TAutoComplete.php | 159 +++++++++++++++++---- 6 files changed, 322 insertions(+), 73 deletions(-) (limited to 'framework') diff --git a/framework/Web/Javascripts/extended/base.js b/framework/Web/Javascripts/extended/base.js index d7fabdd0..d88f82db 100644 --- a/framework/Web/Javascripts/extended/base.js +++ b/framework/Web/Javascripts/extended/base.js @@ -29,15 +29,22 @@ Class.extend = function(base, definition) } /* - Base, version 1.0.1 + Base, version 1.0.2 Copyright 2006, Dean Edwards License: http://creativecommons.org/licenses/LGPL/2.1/ */ - -function Base() { +/* +var Base = function() { + if (arguments.length) { + if (this == window) { // cast an object to this class + Base.prototype.extend.call(arguments[0], arguments.callee.prototype); + } else { + this.extend(arguments[0]); + } + } }; -Base.version = "1.0.1"; +Base.version = "1.0.2"; Base.prototype = { extend: function(source, value) { @@ -46,13 +53,16 @@ Base.prototype = { var ancestor = this[source]; // overriding? if ((ancestor instanceof Function) && (value instanceof Function) && - ancestor.valueOf() != value.valueOf() && /\binherit\b/.test(value)) { + ancestor.valueOf() != value.valueOf() && /\bbase\b/.test(value)) { var method = value; + // var _prototype = this.constructor.prototype; + // var fromPrototype = !Base._prototyping && _prototype[source] == ancestor; value = function() { - var previous = this.inherit; - this.inherit = ancestor; + var previous = this.base; + // this.base = fromPrototype ? _prototype[source] : ancestor; + this.base = ancestor; var returnValue = method.apply(this, arguments); - this.inherit = previous; + this.base = previous; return returnValue; }; // point to the underlying method @@ -85,18 +95,14 @@ Base.prototype = { return this; }, - inherit: function() { + base: function() { // call this method from any other method to invoke that method's ancestor } }; -Base.extend = function(_instance, _static) { +Base.extend = function(_instance, _static) { var extend = Base.prototype.extend; if (!_instance) _instance = {}; - // create the constructor - if (_instance.constructor == Object) { - _instance.constructor = new Function; - } // build the prototype Base._prototyping = true; var _prototype = new this; @@ -112,13 +118,116 @@ Base.extend = function(_instance, _static) { klass.prototype = _prototype; // build the class interface klass.extend = this.extend; + klass.implement = this.implement; klass.toString = function() { return String(constructor); }; extend.call(klass, _static); - // support singletons + // single instance var object = constructor ? klass : _prototype; // class initialisation if (object.init instanceof Function) object.init(); return object; -}; \ No newline at end of file +}; + +Base.implement = function(_interface) { + if (_interface instanceof Function) _interface = _interface.prototype; + this.prototype.extend(_interface); +}; +*/ + +/* + * Signals and Slots for Prototype: Easy custom javascript events + * http://tetlaw.id.au/view/blog/signals-and-slots-for-prototype-easy-custom-javascript-events + * Andrew Tetlaw + * Version 1 (2006-05-03) + * + * http://creativecommons.org/licenses/by-sa/2.5/ + */ +Signal = { + throwErrors : true, + MT : function(){ return true }, + connect : function(obj1, func1, obj2, func2, options) { + var options = Object.extend({ + connectOnce : false + }, options || {}); + if(typeof func1 != 'string' || typeof func2 != 'string') return; + + var sigObj = obj1 || window; + var slotObj = obj2 || window; + var signame = func1+'__signal_'; + var slotsname = func1+'__slots_'; + if(!sigObj[signame]) { + // having the slotFunc in a var and setting it by using an anonymous function in this way + // is apparently a good way to prevent memory leaks in IE if the objects are DOM nodes. + var slotFunc = function() { + var args = arguments; + var result = sigObj[signame].apply(sigObj,args); + sigObj[slotsname].each(function(slot){ + try { + if(slot && slot[0]) { // testing for null, a disconnect may have nulled this slot + slot[0][slot[1]].apply(slot[0],args); //[0] = obj, [1] = func name + } + } catch(e) { + if(Signal.throwErrors) throw e; + } + }); + return result; + }; + (function() { + sigObj[slotsname] = $A([]); + sigObj[signame] = sigObj[func1] || Signal.MT; + sigObj[func1] = slotFunc; + })(); + } + var con = (sigObj[slotsname].length > 0) ? + (options.connectOnce ? !sigObj[slotsname].any(function(slot) { return (slot[0] == slotObj && slot[1] == func2) }) : true) : + true; + if(con) { + sigObj[slotsname].push([slotObj,func2]); + } + }, + connectOnce : function(obj1, func1, obj2, func2, options) { + Signal.connect(obj1, func1, obj2, func2, Object.extend(options || {}, {connectOnce : true})) + }, + disconnect : function(obj1, func1, obj2, func2, options) { + var options = Object.extend({ + disconnectAll : false + }, options || {}); + if(typeof func1 != 'string' || typeof func2 != 'string') return; + + var sigObj = obj1 || window; + var slotObj = obj2 || window; + var signame = func1+'__signal_'; + var slotsname = func1+'__slots_'; + + // I null them in this way so that any currectly active signal will read a null slot, + // otherwise the slot will be applied even though it's been disconnected + if(sigObj[slotsname]) { + if(options.disconnectAll) { + sigObj[slotsname] = sigObj[slotsname].collect(function(slot) { + if(slot[0] == slotObj && slot[1] == func2) { + slot[0] = null; + return null; + } else { + return slot; + } + }).compact(); + } else { + var idx = -1; + sigObj[slotsname] = sigObj[slotsname].collect(function(slot, index) { + if(slot[0] == slotObj && slot[1] == func2 && idx < 0) { //disconnect first match + idx = index; + slot[0] = null; + return null; + } else { + return slot; + } + }).compact(); + } + } + }, + disconnectAll : function(obj1, func1, obj2, func2, options) { + Signal.disconnect(obj1, func1, obj2, func2, Object.extend(options || {}, {disconnectAll : true})) + } +} diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/ajax.js index bfc080e9..fea7e573 100644 --- a/framework/Web/Javascripts/js/ajax.js +++ b/framework/Web/Javascripts/js/ajax.js @@ -189,11 +189,15 @@ if(this.saving)return;this.effect=new Effect.Highlight(this.element,{startcolor: this.editing=false;this.saving=false;this.oldInnerHTML=null;this.onLeaveEditMode();},onComplete:function(transport){this.leaveEditMode();this.options.onComplete.bind(this)(transport,this.element);},onEnterEditMode:function(){},onLeaveEditMode:function(){},dispose:function(){if(this.oldInnerHTML){this.element.innerHTML=this.oldInnerHTML;} this.leaveEditMode();Event.stopObserving(this.element,'click',this.onclickListener);Event.stopObserving(this.element,'mouseover',this.mouseoverListener);Event.stopObserving(this.element,'mouseout',this.mouseoutListener);if(this.options.externalControl){Event.stopObserving(this.options.externalControl,'click',this.onclickListener);Event.stopObserving(this.options.externalControl,'mouseover',this.mouseoverListener);Event.stopObserving(this.options.externalControl,'mouseout',this.mouseoutListener);}}};Ajax.InPlaceCollectionEditor=Class.create();Object.extend(Ajax.InPlaceCollectionEditor.prototype,Ajax.InPlaceEditor.prototype);Object.extend(Ajax.InPlaceCollectionEditor.prototype,{createEditField:function(){if(!this.cached_selectTag){var selectTag=document.createElement("select");var collection=this.options.collection||[];var optionTag;collection.each(function(e,i){optionTag=document.createElement("option");optionTag.value=(e instanceof Array)?e[0]:e;if(this.options.value==optionTag.value)optionTag.selected=true;optionTag.appendChild(document.createTextNode((e instanceof Array)?e[1]:e));selectTag.appendChild(optionTag);}.bind(this));this.cached_selectTag=selectTag;} this.editField=this.cached_selectTag;if(this.options.loadTextURL)this.loadExternalText();this.form.appendChild(this.editField);this.options.callback=function(form,value){return"value="+encodeURIComponent(value);}}});Form.Element.DelayedObserver=Class.create();Form.Element.DelayedObserver.prototype={initialize:function(element,delay,callback){this.delay=delay||0.5;this.element=$(element);this.callback=callback;this.timer=null;this.lastValue=$F(this.element);Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));},delayedListener:function(event){if(this.lastValue==$F(this.element))return;if(this.timer)clearTimeout(this.timer);this.timer=setTimeout(this.onTimerEvent.bind(this),this.delay*1000);this.lastValue=$F(this.element);},onTimerEvent:function(){this.timer=null;this.callback(this.element,$F(this.element));}};Prado.WebUI.CallbackControl=Class.extend(Prado.WebUI.PostBackControl,{onPostBack:function(event,options) -{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}});Prado.WebUI.TActiveButton=Class.extend(Prado.WebUI.CallbackControl);Prado.WebUI.TAutoComplete=Class.extend(Autocompleter.Base,{initialize:function(options) -{this.options=options;this.baseInitialize(options.ID,options.ResultPanel,options);Object.extend(this.options,{onSuccess:this.onComplete.bind(this)});},getUpdatedChoices:function() -{Prado.Callback(this.options.EventTarget,this.getToken(),null,this.options);},onComplete:function(request,boundary) -{result=Prado.Element.extractContent(request.responseText,boundary);if(typeof(result)=="string"&&result.length>0) -this.updateChoices(result);}});Prado.WebUI.TActiveTextBox=Class.extend(Prado.WebUI.TTextBox,{onInit:function(options) +{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}});Prado.WebUI.TActiveButton=Class.extend(Prado.WebUI.CallbackControl);Prado.WebUI.TActiveTextBox=Class.extend(Prado.WebUI.TTextBox,{onInit:function(options) {if(options['TextMode']!='MultiLine') Event.observe(this.element,"keydown",this.handleReturnKey.bind(this));Event.observe(this.element,"change",this.doCallback.bindEvent(this,options));},doCallback:function(event,options) -{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}}); \ No newline at end of file +{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}});Prado.WebUI.TAutoComplete=Class.extend(Autocompleter.Base,Prado.WebUI.TActiveTextBox.prototype);Prado.WebUI.TAutoComplete=Class.extend(Prado.WebUI.TAutoComplete,{initialize:function(options) +{this.options=options;this.baseInitialize(options.ID,options.ResultPanel,options);Object.extend(this.options,{onSuccess:this.onComplete.bind(this)});if(options.AutoPostBack) +this.onInit(options);},doCallback:function(event,options) +{if(!this.active) +{new Prado.CallbackRequest(options.EventTarget,options);Event.stop(event);}},onClick:function(event) +{var element=Event.findElement(event,'LI');this.index=element.autocompleteIndex;this.selectEntry();this.hide();Event.fireEvent(this.element,"change");},getUpdatedChoices:function() +{options=new Array(this.getToken(),"__TAutComplete_onSuggest__");Prado.Callback(this.options.EventTarget,options,null,this.options);},onComplete:function(request,boundary) +{result=Prado.Element.extractContent(request.responseText,boundary);if(typeof(result)=="string"&&result.length>0) +this.updateChoices(result);}}); \ No newline at end of file diff --git a/framework/Web/Javascripts/js/prado.js b/framework/Web/Javascripts/js/prado.js index d0744649..38160fee 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -15,11 +15,9 @@ Function.prototype.bindEvent=function() Class.extend=function(base,definition) {var component=Class.create();Object.extend(component.prototype,base.prototype);if(definition) Object.extend(component.prototype,definition);return component;} -function Base(){};Base.version="1.0.1";Base.prototype={extend:function(source,value){var extend=Base.prototype.extend;if(arguments.length==2){var ancestor=this[source];if((ancestor instanceof Function)&&(value instanceof Function)&&ancestor.valueOf()!=value.valueOf()&&/\binherit\b/.test(value)){var method=value;value=function(){var previous=this.inherit;this.inherit=ancestor;var returnValue=method.apply(this,arguments);this.inherit=previous;return returnValue;};value.valueOf=function(){return method;};value.toString=function(){return String(method);};} -return this[source]=value;}else if(source){var _prototype={toSource:null};var _protected=["toString","valueOf"];if(Base._prototyping)_protected[2]="constructor";for(var i=0;(name=_protected[i]);i++){if(source[name]!=_prototype[name]){extend.call(this,name,source[name]);}} -for(var name in source){if(!_prototype[name]){extend.call(this,name,source[name]);}}} -return this;},inherit:function(){}};Base.extend=function(_instance,_static){var extend=Base.prototype.extend;if(!_instance)_instance={};if(_instance.constructor==Object){_instance.constructor=new Function;} -Base._prototyping=true;var _prototype=new this;extend.call(_prototype,_instance);var constructor=_prototype.constructor;_prototype.constructor=this;delete Base._prototyping;var klass=function(){if(!Base._prototyping)constructor.apply(this,arguments);this.constructor=klass;};klass.prototype=_prototype;klass.extend=this.extend;klass.toString=function(){return String(constructor);};extend.call(klass,_static);var object=constructor?klass:_prototype;if(object.init instanceof Function)object.init();return object;};Object.extend(String.prototype,{gsub:function(pattern,replacement){var result='',source=this,match;replacement=arguments.callee.prepareReplacement(replacement);while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=(replacement(match)||'').toString();source=source.slice(match.index+match[0].length);}else{result+=source,source='';}} +Signal={throwErrors:true,MT:function(){return true},connect:function(obj1,func1,obj2,func2,options){var options=Object.extend({connectOnce:false},options||{});if(typeof func1!='string'||typeof func2!='string')return;var sigObj=obj1||window;var slotObj=obj2||window;var signame=func1+'__signal_';var slotsname=func1+'__slots_';if(!sigObj[signame]){var slotFunc=function(){var args=arguments;var result=sigObj[signame].apply(sigObj,args);sigObj[slotsname].each(function(slot){try{if(slot&&slot[0]){slot[0][slot[1]].apply(slot[0],args);}}catch(e){if(Signal.throwErrors)throw e;}});return result;};(function(){sigObj[slotsname]=$A([]);sigObj[signame]=sigObj[func1]||Signal.MT;sigObj[func1]=slotFunc;})();} +var con=(sigObj[slotsname].length>0)?(options.connectOnce?!sigObj[slotsname].any(function(slot){return(slot[0]==slotObj&&slot[1]==func2)}):true):true;if(con){sigObj[slotsname].push([slotObj,func2]);}},connectOnce:function(obj1,func1,obj2,func2,options){Signal.connect(obj1,func1,obj2,func2,Object.extend(options||{},{connectOnce:true}))},disconnect:function(obj1,func1,obj2,func2,options){var options=Object.extend({disconnectAll:false},options||{});if(typeof func1!='string'||typeof func2!='string')return;var sigObj=obj1||window;var slotObj=obj2||window;var signame=func1+'__signal_';var slotsname=func1+'__slots_';if(sigObj[slotsname]){if(options.disconnectAll){sigObj[slotsname]=sigObj[slotsname].collect(function(slot){if(slot[0]==slotObj&&slot[1]==func2){slot[0]=null;return null;}else{return slot;}}).compact();}else{var idx=-1;sigObj[slotsname]=sigObj[slotsname].collect(function(slot,index){if(slot[0]==slotObj&&slot[1]==func2&&idx<0){idx=index;slot[0]=null;return null;}else{return slot;}}).compact();}}},disconnectAll:function(obj1,func1,obj2,func2,options){Signal.disconnect(obj1,func1,obj2,func2,Object.extend(options||{},{disconnectAll:true}))}} +Object.extend(String.prototype,{gsub:function(pattern,replacement){var result='',source=this,match;replacement=arguments.callee.prepareReplacement(replacement);while(source.length>0){if(match=source.match(pattern)){result+=source.slice(0,match.index);result+=(replacement(match)||'').toString();source=source.slice(match.index+match[0].length);}else{result+=source,source='';}} return result;},sub:function(pattern,replacement,count){replacement=this.gsub.prepareReplacement(replacement);count=count===undefined?1:count;return this.gsub(pattern,function(match){if(--count<0)return match[0];return replacement(match);});},scan:function(pattern,iterator){this.gsub(pattern,iterator);return this;},truncate:function(length,truncation){length=length||30;truncation=truncation===undefined?'...':truncation;return this.length>length?this.slice(0,length-truncation.length)+truncation:this;},strip:function(){return this.replace(/^\s+/,'').replace(/\s+$/,'');},stripTags:function(){return this.replace(/<\/?[^>]+>/gi,'');},stripScripts:function(){return this.replace(new RegExp(Prototype.ScriptFragment,'img'),'');},extractScripts:function(){var matchAll=new RegExp(Prototype.ScriptFragment,'img');var matchOne=new RegExp(Prototype.ScriptFragment,'im');return(this.match(matchAll)||[]).map(function(scriptTag){return(scriptTag.match(matchOne)||['',''])[1];});},evalScripts:function(){return this.extractScripts().map(function(script){return eval(script)});},escapeHTML:function(){var div=document.createElement('div');var text=document.createTextNode(this);div.appendChild(text);return div.innerHTML;},unescapeHTML:function(){var div=document.createElement('div');div.innerHTML=this.stripTags();return div.childNodes[0]?div.childNodes[0].nodeValue:'';},toQueryParams:function(){var pairs=this.match(/^\??(.*)$/)[1].split('&');return pairs.inject({},function(params,pairString){var pair=pairString.split('=');params[pair[0]]=pair[1];return params;});},toArray:function(){return this.split('');},camelize:function(){var oStringList=this.split('-');if(oStringList.length==1)return oStringList[0];var camelizedString=this.indexOf('-')==0?oStringList[0].charAt(0).toUpperCase()+oStringList[0].substring(1):oStringList[0];for(var i=1,len=oStringList.length;i 0) this.updateChoices(result); } -}); - -Prado.WebUI.TActiveTextBox = Class.extend(Prado.WebUI.TTextBox, -{ - onInit : function(options) - { - if(options['TextMode'] != 'MultiLine') - Event.observe(this.element, "keydown", this.handleReturnKey.bind(this)); - Event.observe(this.element, "change", this.doCallback.bindEvent(this,options)); - }, - - doCallback : function(event, options) - { - new Prado.CallbackRequest(options.EventTarget, options); - Event.stop(event); - } -}); +}); \ No newline at end of file diff --git a/framework/Web/Javascripts/prado/controls.js b/framework/Web/Javascripts/prado/controls.js index 2497c586..285374a3 100644 --- a/framework/Web/Javascripts/prado/controls.js +++ b/framework/Web/Javascripts/prado/controls.js @@ -53,10 +53,10 @@ Prado.WebUI.PostBackControl.prototype = this._elementOnClick = this.element.onclick; this.element.onclick = null; } - Event.observe(this.element, "click", this.onClick.bindEvent(this,options)); + Event.observe(this.element, "click", this.elementClicked.bindEvent(this,options)); }, - onClick : function(event, options) + elementClicked : function(event, options) { var src = Event.element(event); var doPostBack = true; diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index 601894ff..55ffde04 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -1,77 +1,171 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ : $ + * @package System.Web.UI.ActiveControls */ +/** + * TAutoComplete class. + * + * TAutoComplete is a textbox that provides a list of suggestion on + * the current partial word typed in the textbox. The suggestions are + * requested using callbacks, and raises the {@link onSuggestion OnSuggestion} + * event. The events of the TActiveText (from which TAutoComplete is extended from) + * and {@link onSuggestion OnSuggestion} are mutually exculsive. That is, + * if {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} + * events are raise, then {@link onSuggestion OnSuggestion} will not be raise, and + * vice versa. + * + * The list of suggestions should be set in the {@link onSuggestion OnSuggestion} + * event handler. The partial word to match the suggestion is in the + * {@link TCallbackEventParameter::getParameter TCallbackEventParameter::Parameter} + * property. The datasource of the TAutoComplete must be set using {@link setDataSource} + * method. This sets the datasource for the suggestions repeater, available through + * the {@link getSuggestions Suggestions} property. Header, footer templates and + * other properties of the repeater can be access via the {@link getSuggestions Suggestions} + * property (e.g. they can be set in the .page templates). + * + * To return the list of suggestions back to the browser, in your {@link onSuggestion OnSuggestion} + * event handler, do + * + * function autocomplete_suggestion($sender, $param) + * { + * $token = $param->getParameter(); //the partial word to match + * $sender->setDataSource($this->getSuggestionsFor($token)); //set suggestions + * $sender->dataBind(); + * $sender->flush($param->getOutput()); //sends suggestion back to browser. + * } + * + * + * TAutoComplete allows multiple suggestions within one textbox with each + * word or phrase separated by any characters specified in the + * {@link setSeparator Separator} property. The {@link setFrequency Frequency} + * and {@link setMinChars MinChars} properties sets the delay and minimum number + * of characters typed, respectively, before requesting for sugggestions. + * + * Use {@link onTextChange OnTextChange} and/or {@link onCallback OnCallback} events + * to handle post backs due to {@link setAutoPostBack AutoPostBack}. + * + * In the {@link getSuggestions Suggestions} TRepater item template, all HTML text elements + * are considered as text for the suggestion. Text within HTML elements with CSS class name + * "informal" are ignored as text for suggestions. + * + * @author Wei Zhuo + * @version $Revision: $ Mon Jun 19 03:50:05 EST 2006 $ + * @package System + * @since 3.0 + */ class TAutoComplete extends TActiveTextBox implements INamingContainer { /** * @var ITemplate template for repeater items */ private $_repeater=null; + /** + * @var TPanel result panel holding the suggestion items. + */ private $_resultPanel=null; + /** + * @return string word or token separators (delimiters). + */ public function getSeparator() { return $this->getViewState('tokens', ''); } + /** + * @return string word or token separators (delimiters). + */ public function setSeparator($value) { $this->setViewState('tokens', TPropertyValue::ensureString($value), ''); } + /** + * @return float maximum delay (in seconds) before requesting a suggestion. + */ public function getFrequency() { return $this->getViewState('frequency', ''); } + /** + * @param float maximum delay (in seconds) before requesting a suggestion. + * Default is 0.4. + */ public function setFrequency($value) { $this->setViewState('frequency', TPropertyValue::ensureFloat($value),''); } + /** + * @return integer minimum number of characters before requesting a suggestion. + */ public function getMinChars() { return $this->getViewState('minChars',''); } + /** + * @param integer minimum number of characters before requesting a suggestion. + */ public function setMinChars($value) { $this->setViewState('minChars', TPropertyValue::ensureInteger($value), ''); } /** - * Raises the callback event. This method is required by {@link - * ICallbackEventHandler} interface. If {@link getCausesValidation - * CausesValidation} is true, it will invoke the page's {@link TPage:: - * validate validate} method first. It will raise {@link onCallback - * OnCallback} event and then the {@link onClick OnClick} event. This method - * is mainly used by framework and control developers. + * Raises the callback event. This method is overrides the parent implementation. + * If {@link setAutoPostBack AutoPostBack} is enabled it will raise + * {@link onTextChanged OnTextChanged} event event and then the + * {@link onCallback OnCallback} event. The {@link onSuggest OnSuggest} event is + * raise if the request is to find sugggestions, the {@link onTextChanged OnTextChanged} + * and {@link onCallback OnCallback} events are NOT raised. + * This method is mainly used by framework and control developers. * @param TCallbackEventParameter the event parameter */ public function raiseCallbackEvent($param) { - $this->onCallback($param); + $token = $param->getParameter(); + if(is_array($token) && count($token) == 2 && $token[1] === '__TAutComplete_onSuggest__') + { + $parameter = new TCallbackEventParameter($this->getResponse(), $token[0]); + $this->onSuggest($parameter); + } + else if($this->getAutoPostBack()) + parent::raiseCallbackEvent($param); } /** - * This method is invoked when a callback is requested. The method raises - * 'OnCallback' event to fire up the event handlers. If you override this + * This method is invoked when a autocomplete suggestion is requested. + * The method raises 'OnSuggest' event. If you override this * method, be sure to call the parent implementation so that the event * handler can be invoked. * @param TCallbackEventParameter event parameter to be passed to the event handlers */ - public function onCallback($param) + public function onSuggest($param) { - $this->raiseEvent('OnCallback', $this, $param); + $this->raiseEvent('OnSuggest', $this, $param); } - + + /** + * @param array data source for suggestions. + */ public function setDataSource($data) { $this->getSuggestions()->setDataSource($data); } + /** + * @return TPanel suggestion results panel. + */ public function getResultPanel() { if(is_null($this->_resultPanel)) @@ -79,6 +173,9 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer return $this->_resultPanel; } + /** + * @return TPanel new instance of result panel. Default uses TPanel. + */ protected function createResultPanel() { $panel = Prado::createComponent('System.Web.UI.WebControls.TPanel'); @@ -98,7 +195,7 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer } /** - * + * @return TRepeater new instance of TRepater to render the list of suggestions. */ protected function createRepeater() { @@ -110,6 +207,9 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer return $repeater; } + /** + * Renders the end tag and registers javascript effects library. + */ public function renderEndTag($writer) { $this->getPage()->getClientScript()->registerPradoScript('effects'); @@ -117,22 +217,29 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer $this->renderResultPanel($writer); } - public function renderResultPanel($writer) + /** + * Renders the result panel. + * @param THtmlWriter the renderer. + */ + protected function renderResultPanel($writer) { $this->getResultPanel()->render($writer); } - public function render($writer) + /** + * Flush and returns the suggestions content back to the browser client. + * @param THtmlWriter the renderer. + */ + public function flush($writer) { - if($this->getPage()->getIsCallback()) - { - if($this->getActiveControl()->canUpdateClientSide()) + if($this->getActiveControl()->canUpdateClientSide()) $this->renderSuggestions($writer); - } - else - parent::render($writer); } + /** + * Renders the suggestions repeater. + * @param THtmlWriter the renderer. + */ protected function renderSuggestions($writer) { if($this->getSuggestions()->getItems()->getCount() > 0) @@ -151,11 +258,15 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer $this->getActiveControl()->getClientSide()->setEnablePageStateUpdate(false); if(strlen($string = $this->getSeparator())) { + $string = strtr($string,array('\t'=>"\t",'\n'=>"\n",'\r'=>"\r")); $token = preg_split('//', $string, -1, PREG_SPLIT_NO_EMPTY); $options['tokens'] = TJavascript::encode($token,false); } if($this->getAutoPostBack()) - $options = array_merge($options,$this->getPostBackOptions()); + { + $options = array_merge($options,$this->getPostBackOptions()); + $options['AutoPostBack'] = true; + } $options['ResultPanel'] = $this->getResultPanel()->getClientID(); $options['ID'] = $this->getClientID(); $options['EventTarget'] = $this->getUniqueID(); -- cgit v1.2.3