From b3ceed048bb533a00bbea542f7c12b49c8c83d9b Mon Sep 17 00:00:00 2001 From: wei <> Date: Sat, 17 Jun 2006 01:55:05 +0000 Subject: Update changes to active controls, add FT tests for active controls, add comments. --- framework/Exceptions/messages.txt | 2 + framework/Web/Javascripts/js/ajax.js | 33 +- framework/Web/Javascripts/js/prado.js | 9 +- framework/Web/Javascripts/prado/activecontrols3.js | 16 + framework/Web/Javascripts/prado/ajax3.js | 57 ++- framework/Web/Javascripts/prado/element.js | 15 +- framework/Web/UI/ActiveControls/TActiveButton.php | 24 +- .../UI/ActiveControls/TActiveControlAdapter.php | 95 ++++- framework/Web/UI/ActiveControls/TActiveLabel.php | 2 +- .../Web/UI/ActiveControls/TActivePageAdapter.php | 109 ++++-- framework/Web/UI/ActiveControls/TActivePanel.php | 50 ++- framework/Web/UI/ActiveControls/TActiveTextBox.php | 69 +++- framework/Web/UI/ActiveControls/TAutoComplete.php | 2 +- .../Web/UI/ActiveControls/TBaseActiveControl.php | 49 ++- framework/Web/UI/ActiveControls/TCallback.php | 2 +- .../UI/ActiveControls/TCallbackClientScript.php | 23 +- .../ActiveControls/TCallbackClientSideOptions.php | 14 +- .../Web/UI/ActiveControls/TCallbackOptions.php | 26 +- .../UI/ActiveControls/TCallbackResponseAdapter.php | 78 +++- framework/Web/UI/TClientScriptManager.php | 16 + framework/Web/UI/TPage.php | 2 + framework/Web/UI/WebControls/TBaseValidator.php | 11 +- framework/Web/UI/WebControls/TClientScript.php | 39 ++ framework/interfaces.php | 10 +- framework/prado-cli.php | 413 +++++++++++++++++++++ 25 files changed, 1019 insertions(+), 147 deletions(-) create mode 100755 framework/prado-cli.php (limited to 'framework') diff --git a/framework/Exceptions/messages.txt b/framework/Exceptions/messages.txt index 257a4914..5fa94202 100644 --- a/framework/Exceptions/messages.txt +++ b/framework/Exceptions/messages.txt @@ -307,6 +307,8 @@ callback_not_support_no_priority_state_update = Callback request does not suppor callback_invalid_callback_options = '{1}' is not a valid TCallbackOptions control for Callback control '{0}'. callback_invalid_clientside_options = Callback ClientSide property must be either a string that is the ID of a TCallbackOptions control or an instance of TCallbackClientSideOptions.======= callback_not_support_no_priority_state_update = Callback request does not support unprioritized pagestate update. +callback_invalid_handler = Invalid callback handler, control {0} must implement ICallbackEventHandler. +callback_invalid_target = Invalid callback target, no such control with ID {0}. xmltransform_xslextension_required = TXmlTransform require the PHP's XSL extension xmltransform_transformpath_invalid = TransformPath '{0}' is invalid. diff --git a/framework/Web/Javascripts/js/ajax.js b/framework/Web/Javascripts/js/ajax.js index 6e6d0afb..bfc080e9 100644 --- a/framework/Web/Javascripts/js/ajax.js +++ b/framework/Web/Javascripts/js/ajax.js @@ -33,20 +33,16 @@ this.transport.onreadystatechange=Prototype.emptyFunction;},getHeaderData:functi {var json=this.header(name);return eval('('+json+')');} catch(e) {if(typeof(json)=="string") -{Logger.info("using json") -return Prado.CallbackRequest.decode(json);}}}});Prado.CallbackRequest=Class.create();Object.extend(Prado.CallbackRequest,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',FIELD_CALLBACK_PAGESTATE:'PRADO_PAGESTATE',FIELD_POSTBACK_TARGET:'PRADO_POSTBACK_TARGET',FIELD_POSTBACK_PARAMETER:'PRADO_POSTBACK_PARAMETER',PostDataLoaders:[],DATA_HEADER:'X-PRADO-DATA',ACTION_HEADER:'X-PRADO-ACTIONS',ERROR_HEADER:'X-PRADO-ERROR',PAGESTATE_HEADER:'X-PRADO-PAGESTATE',requestInProgress:null,dispatchActions:function(transport,actions) +return Prado.CallbackRequest.decode(json);}}});Prado.CallbackRequest=Class.create();Object.extend(Prado.CallbackRequest,{FIELD_CALLBACK_TARGET:'PRADO_CALLBACK_TARGET',FIELD_CALLBACK_PARAMETER:'PRADO_CALLBACK_PARAMETER',FIELD_CALLBACK_PAGESTATE:'PRADO_PAGESTATE',FIELD_POSTBACK_TARGET:'PRADO_POSTBACK_TARGET',FIELD_POSTBACK_PARAMETER:'PRADO_POSTBACK_PARAMETER',PostDataLoaders:[],DATA_HEADER:'X-PRADO-DATA',ACTION_HEADER:'X-PRADO-ACTIONS',ERROR_HEADER:'X-PRADO-ERROR',PAGESTATE_HEADER:'X-PRADO-PAGESTATE',requestInProgress:null,addPostLoaders:function(ids) +{this.PostDataLoaders=this.PostDataLoaders.concat(ids);},dispatchActions:function(transport,actions) {if(actions&&actions.length>0) actions.each(this.__run.bind(this,transport));},__run:function(transport,command) {for(var method in command) -{if(command[method][0]) -{var id=command[method][0];if($(id)||id.indexOf("[]")>-1) {try {method.toFunction().apply(this,command[method].concat(transport));} catch(e) {if(typeof(Logger)!="undefined") -Prado.CallbackRequest.Exception.onException(null,e);}} -else if(typeof(Logger)!="undefined") -{Logger.error("Error in executing callback response:","Unable to find HTML element with ID '"+id+"' before executing "+method+"().");}}}},Exception:{"on500":function(request,transport,data) +Prado.CallbackRequest.Exception.onException(null,e);}}},Exception:{"on500":function(request,transport,data) {var e=request.getHeaderData(Prado.CallbackRequest.ERROR_HEADER);Logger.error("Callback Server Error "+e.code,this.formatException(e));},'on200':function(request,transport,data) {if(transport.status<500) {var msg='HTTP '+transport.status+" with response : \n";msg+=transport.responseText+"\n";msg+="Data : \n"+inspect(data)+"\n";msg+="Actions : \n";data=request.getHeaderData(Prado.CallbackRequest.ACTION_HEADER);if(data&&data.length>0) @@ -63,19 +59,17 @@ msg+=e.version+" "+e.time+"\n";return msg;}},encode:function(data) {if(typeof(data)=="string"&&data.trim().length>0) return Prado.JSON.parse(data);else return null;},dispatchPriorityRequest:function(callback) -{Logger.info("priority request "+callback.id) -this.abortRequestInProgress();callback.request=new Ajax.Request(callback.url,callback.options);callback.timeout=setTimeout(function() -{Logger.warn("priority timeout");Prado.CallbackRequest.abortRequestInProgress();},callback.options.RequestTimeOut);this.requestInProgress=callback;Logger.info("dispatched "+this.requestInProgress)},dispatchNormalRequest:function(callback) -{Logger.info("dispatching normal request");new Ajax.Request(callback.url,callback.options);},abortRequestInProgress:function() -{inProgress=Prado.CallbackRequest.requestInProgress;Logger.info("aborting ... "+inProgress);if(inProgress) -{Logger.warn("aborted "+inProgress.id) -inProgress.request.transport.abort();clearTimeout(inProgress.timeout);Prado.CallbackRequest.requestInProgress=null;return true;} +{this.abortRequestInProgress();callback.request=new Ajax.Request(callback.url,callback.options);callback.timeout=setTimeout(function() +{Prado.CallbackRequest.abortRequestInProgress();},callback.options.RequestTimeOut);this.requestInProgress=callback;},dispatchNormalRequest:function(callback) +{new Ajax.Request(callback.url,callback.options);},abortRequestInProgress:function() +{inProgress=Prado.CallbackRequest.requestInProgress;if(inProgress) +{inProgress.request.transport.abort();clearTimeout(inProgress.timeout);Prado.CallbackRequest.requestInProgress=null;return true;} return false;},updatePageState:function(request,transport) {pagestate=$(this.FIELD_CALLBACK_PAGESTATE);if(request.options.EnablePageStateUpdate&&request.options.HasPriority&&pagestate) {data=request.header(this.PAGESTATE_HEADER);if(typeof(data)=="string"&&data.length>0) -{Logger.warn("updating page state");pagestate.value=data;} -else -{Logger.debug("Bad page state:"+data);}}}}) +pagestate.value=data;else +{if(typeof(Logger)!="undefined") +Logger.debug("Bad page state:"+data);}}}}) Ajax.Responders.register({onComplete:function(request) {if(request.options.HasPriority) Prado.CallbackRequest.abortRequestInProgress();}});Event.OnLoad(function() @@ -199,4 +193,7 @@ this.editField=this.cached_selectTag;if(this.options.loadTextURL)this.loadExtern {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);}}); \ No newline at end of file +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/js/prado.js b/framework/Web/Javascripts/js/prado.js index 65ffd1b8..d0744649 100644 --- a/framework/Web/Javascripts/js/prado.js +++ b/framework/Web/Javascripts/js/prado.js @@ -267,10 +267,15 @@ setTimeout(function(){obj.focus();},100);return false;},replace:function(element {if(boundary) {result=Prado.Element.extractContent(transport.responseText,boundary);if(result!=null) content=result;} -method.toFunction().apply(this,[element,content]);},extractContent:function(text,boundary) +if(typeof(element)=="string") +{if($(element)) +method.toFunction().apply(this,[element,content]);} +else +{method.toFunction().apply(this,[content]);}},extractContent:function(text,boundary) {f=RegExp('()([\\s\\S\\w\\W]*)()',"m");result=text.match(f);if(result&&result.length>=2) return result[2];else -return null;}} +return null;},evaluateScript:function(content) +{content.evalScripts();}} Prado.Element.Selection={inputValue:function(el,value) {switch(el.type.toLowerCase()) {case'checkbox':case'radio':return el.checked=value;}},selectValue:function(el,value) diff --git a/framework/Web/Javascripts/prado/activecontrols3.js b/framework/Web/Javascripts/prado/activecontrols3.js index 4be2779a..6fbde405 100644 --- a/framework/Web/Javascripts/prado/activecontrols3.js +++ b/framework/Web/Javascripts/prado/activecontrols3.js @@ -42,3 +42,19 @@ Prado.WebUI.TAutoComplete = Class.extend(Autocompleter.Base, 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); + } +}); diff --git a/framework/Web/Javascripts/prado/ajax3.js b/framework/Web/Javascripts/prado/ajax3.js index 1e832889..adf8534a 100644 --- a/framework/Web/Javascripts/prado/ajax3.js +++ b/framework/Web/Javascripts/prado/ajax3.js @@ -19,7 +19,7 @@ Object.extend(Ajax.Request.prototype, Prado.CallbackRequest.updatePageState(this,transport); Ajax.Responders.dispatch('on' + transport.status, this, transport, json); Prado.CallbackRequest.dispatchActions(transport,this.getHeaderData(Prado.CallbackRequest.ACTION_HEADER)); - + (this.options['on' + this.transport.status] || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')] || Prototype.emptyFunction)(transport, json); @@ -57,10 +57,7 @@ Object.extend(Ajax.Request.prototype, catch (e) { if(typeof(json) == "string") - { - Logger.info("using json") return Prado.CallbackRequest.decode(json); - } } } }); @@ -117,6 +114,14 @@ Object.extend(Prado.CallbackRequest, */ requestInProgress : null, + /** + * Add ids of inputs element to post in the request. + */ + addPostLoaders : function(ids) + { + this.PostDataLoaders = this.PostDataLoaders.concat(ids); + }, + /** * Dispatch callback response actions. */ @@ -133,26 +138,14 @@ Object.extend(Prado.CallbackRequest, { for(var method in command) { - if(command[method][0]) + try { - var id = command[method][0]; - if($(id) || id.indexOf("[]") > -1) - { - try - { - method.toFunction().apply(this,command[method].concat(transport)); - } - catch(e) - { - if(typeof(Logger) != "undefined") - Prado.CallbackRequest.Exception.onException(null,e); - } - } - else if(typeof(Logger) != "undefined") - { - Logger.error("Error in executing callback response:", - "Unable to find HTML element with ID '"+id+"' before executing "+method+"()."); - } + method.toFunction().apply(this,command[method].concat(transport)); + } + catch(e) + { + if(typeof(Logger) != "undefined") + Prado.CallbackRequest.Exception.onException(null,e); } } }, @@ -251,18 +244,18 @@ Object.extend(Prado.CallbackRequest, */ dispatchPriorityRequest : function(callback) { - Logger.info("priority request "+callback.id) + //Logger.info("priority request "+callback.id) this.abortRequestInProgress(); callback.request = new Ajax.Request(callback.url, callback.options); callback.timeout = setTimeout(function() { - Logger.warn("priority timeout"); + // Logger.warn("priority timeout"); Prado.CallbackRequest.abortRequestInProgress(); },callback.options.RequestTimeOut); this.requestInProgress = callback; - Logger.info("dispatched "+this.requestInProgress) + //Logger.info("dispatched "+this.requestInProgress) }, /** @@ -270,7 +263,7 @@ Object.extend(Prado.CallbackRequest, */ dispatchNormalRequest : function(callback) { - Logger.info("dispatching normal request"); + // Logger.info("dispatching normal request"); new Ajax.Request(callback.url, callback.options); }, @@ -280,10 +273,10 @@ Object.extend(Prado.CallbackRequest, abortRequestInProgress : function() { inProgress = Prado.CallbackRequest.requestInProgress; - Logger.info("aborting ... "+inProgress); + //Logger.info("aborting ... "+inProgress); if(inProgress) { - Logger.warn("aborted "+inProgress.id) + // Logger.warn("aborted "+inProgress.id) inProgress.request.transport.abort(); clearTimeout(inProgress.timeout); Prado.CallbackRequest.requestInProgress = null; @@ -303,13 +296,11 @@ Object.extend(Prado.CallbackRequest, { data = request.header(this.PAGESTATE_HEADER); if(typeof(data) == "string" && data.length > 0) - { - Logger.warn("updating page state"); pagestate.value = data; - } else { - Logger.debug("Bad page state:"+data); + if(typeof(Logger) != "undefined") + Logger.debug("Bad page state:"+data); } } } diff --git a/framework/Web/Javascripts/prado/element.js b/framework/Web/Javascripts/prado/element.js index 0ff0e8fa..5c76d4e6 100644 --- a/framework/Web/Javascripts/prado/element.js +++ b/framework/Web/Javascripts/prado/element.js @@ -71,7 +71,15 @@ Prado.Element = if(result != null) content = result; } - method.toFunction().apply(this,[element,content]); + if(typeof(element) == "string") + { + if($(element)) + method.toFunction().apply(this,[element,content]); + } + else + { + method.toFunction().apply(this,[content]); + } }, extractContent : function(text, boundary) @@ -82,6 +90,11 @@ Prado.Element = return result[2]; else return null; + }, + + evaluateScript : function(content) + { + content.evalScripts(); } } diff --git a/framework/Web/UI/ActiveControls/TActiveButton.php b/framework/Web/UI/ActiveControls/TActiveButton.php index f4bfc678..81744057 100644 --- a/framework/Web/UI/ActiveControls/TActiveButton.php +++ b/framework/Web/UI/ActiveControls/TActiveButton.php @@ -7,9 +7,11 @@ * @copyright Copyright © 2006 PradoSoft * @license http://www.pradosoft.com/license/ * @version $Revision: $ $Date: $ - * @package System.Web.UI.WebControls + * @package System.Web.UI.ActiveControls */ +Prado::using('System.Web.UI.ActiveControls.TActiveControlAdapter'); + /** * TActiveButton is the active control counter part to TButton. * @@ -17,7 +19,7 @@ * callback request is initiated. * * The {@link onCallback OnCallback} event is raised during a callback request - * and it is raise before the {@link onClick OnClick} event. + * and it is raise after the {@link onClick OnClick} event. * * When the {@link TBaseActiveCallbackControl::setEnableUpdate ActiveControl.EnableUpdate} * property is true, changing the {@link setText Text} property during callback request @@ -28,7 +30,7 @@ * @package System.Web.UI.ActiveControls * @since 3.0 */ -class TActiveButton extends TButton implements ICallbackEventHandler +class TActiveButton extends TButton implements ICallbackEventHandler, IActiveControl { /** * Creates a new callback control, sets the adapter to @@ -53,15 +55,15 @@ class TActiveButton extends TButton implements ICallbackEventHandler * 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 first and then the {@link onClick OnClick} event. + * validate validate} method first. It will raise {@link onClick + * OnClick} event first and then the {@link onCallback OnCallback} event. * This method is mainly used by framework and control developers. * @param TCallbackEventParameter the event parameter */ public function raiseCallbackEvent($param) { - $this->onCallback($param); $this->raisePostBackEvent($param); + $this->onCallback($param); } /** @@ -115,16 +117,6 @@ class TActiveButton extends TButton implements ICallbackEventHandler { return 'Prado.WebUI.TActiveButton'; } - - /** - * Gets the name of the javascript class responsible for performing postback for this control. - * This method overrides the parent implementation. - * @return string the javascript class name - */ - protected function getClientClassName() - { - return 'Prado.WebUI.TActiveButton'; - } } ?> \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php index ab842976..de6310a6 100644 --- a/framework/Web/UI/ActiveControls/TActiveControlAdapter.php +++ b/framework/Web/UI/ActiveControls/TActiveControlAdapter.php @@ -1,26 +1,68 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ : $ + * @package System.Web.UI.ActiveControls + */ + /* - * Created on 29/04/2006 + * Load common active control options. */ Prado::using('System.Web.UI.ActiveControls.TBaseActiveControl'); +/** + * TActiveControlAdapter class. + * + * Customize the parent TControl class for active control classes. + * TActiveControlAdapter instantiates a common base active control class + * throught the {@link getBaseActiveControl BaseActiveControl} property. + * The type of BaseActiveControl can be provided in the second parameter in the + * constructor. Default is TBaseActiveControl or TBaseActiveCallbackControl if + * the control adapted implements ICallbackEventHandler. + * + * TActiveControlAdapter will tracking viewstate changes to update the + * corresponding client-side properties. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 20:35:34 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ class TActiveControlAdapter extends TControlAdapter { - private static $_renderedPosts = false; - + /** + * @var string base active control class name. + */ private $_activeControlType; - + /** + * @var TBaseActiveControl base active control instance. + */ private $_baseActiveControl; - + /** + * @var TCallbackPageStateTracker view state tracker. + */ private $_stateTracker; - public function __construct($control, $baseCallbackClass=null) + /** + * Constructor. + * @param IActiveControl active control to adapt. + * @param string Base active control class name. + */ + public function __construct(IActiveControl $control, $baseCallbackClass=null) { parent::__construct($control); - $this->setBaseControlType($baseCallbackClass); + $this->setBaseControlClass($baseCallbackClass); } - private function setBaseControlType($type) + /** + * @param string base active control instance + */ + protected function setBaseControlClass($type) { if(is_null($type)) { @@ -30,22 +72,21 @@ class TActiveControlAdapter extends TControlAdapter $this->_activeControlType = 'TBaseActiveControl'; } else - { $this->_activeControlType = $type; - } } /** - * Render the callback request post data loaders once only. + * Renders the callback client scripts. */ public function render($writer) { $this->renderCallbackClientScripts(); parent::render($writer); - if($this->getPage()->getIsCallback()) - $this->getPage()->getCallbackClient()->replace($this->getControl(), $writer); } + /** + * Register the callback clientscripts and sets the post loader IDs. + */ protected function renderCallbackClientScripts() { $cs = $this->getPage()->getClientScript(); @@ -54,11 +95,14 @@ class TActiveControlAdapter extends TControlAdapter { $cs->registerPradoScript('ajax'); $options = TJavascript::encode($this->getPage()->getPostDataLoaders(),false); - $script = "Prado.CallbackRequest.PostDataLoaders = {$options};"; + $script = "Prado.CallbackRequest.addPostLoaders({$options});"; $cs->registerEndScript($key, $script); } } + /** + * @return TBaseActiveControl Common active control options. + */ public function getBaseActiveControl() { if(is_null($this->_baseActiveControl)) @@ -69,6 +113,9 @@ class TActiveControlAdapter extends TControlAdapter return $this->_baseActiveControl; } + /** + * @return boolean true if the viewstate needs to be tracked. + */ protected function getIsTrackingPageState() { if($this->getPage()->getIsCallback()) @@ -83,6 +130,10 @@ class TActiveControlAdapter extends TControlAdapter return false; } + /** + * Loads additional persistent control state. Starts viewstate tracking + * if necessary. + */ public function loadState() { if($this->getIsTrackingPageState()) @@ -93,6 +144,10 @@ class TActiveControlAdapter extends TControlAdapter parent::loadState(); } + /** + * Saves additional persistent control state. Respond to viewstate changes + * if necessary. + */ public function saveState() { if(!is_null($this->_stateTracker) @@ -104,6 +159,18 @@ class TActiveControlAdapter extends TControlAdapter } } +/** + * TCallbackPageStateTracker class. + * + * Tracking changes to the page state during callback. + * + * @todo Complete this class! (Wei) + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 20:51:25 EST 2006 $ + * @package System + * @since 3.0 + */ class TCallbackPageStateTracker { private $_states = array('Visible', 'Enabled', 'Attributes', 'Style', 'TabIndex', 'ToolTip', 'AccessKey'); diff --git a/framework/Web/UI/ActiveControls/TActiveLabel.php b/framework/Web/UI/ActiveControls/TActiveLabel.php index c1cb1fba..f54e0b98 100644 --- a/framework/Web/UI/ActiveControls/TActiveLabel.php +++ b/framework/Web/UI/ActiveControls/TActiveLabel.php @@ -25,7 +25,7 @@ * @package System.Web.UI.ActiveControls * @since 3.0 */ -class TActiveLabel extends TLabel +class TActiveLabel extends TLabel implements IActiveControl { /** * Creates a new callback control, sets the adapter to diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index 77d8a7fe..90c64820 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -1,6 +1,6 @@ * @link http://www.pradosoft.com/ @@ -9,11 +9,16 @@ * @version $Revision: $ $Date: $ * @package System.Web.UI.ActiveControls */ - + +/** + * Load callback response adapter class. + */ +Prado::using('System.Web.UI.ActiveControls.TCallbackResponseAdapter'); + /** * TActivePageAdapter class. * - * Callback request page handler. + * Callback request handler. * * @author Wei Zhuo * @version $Revision: $ $Date: $ @@ -22,9 +27,21 @@ */ class TActivePageAdapter extends TControlAdapter { + /** + * Callback response data header name. + */ const CALLBACK_DATA_HEADER = 'X-PRADO-DATA'; + /** + * Callback response client-side action header name. + */ const CALLBACK_ACTION_HEADER = 'X-PRADO-ACTIONS'; + /** + * Callback error header name. + */ const CALLBACK_ERROR_HEADER = 'X-PRADO-ERROR'; + /** + * Callback page state header name. + */ const CALLBACK_PAGESTATE_HEADER = 'X-PRADO-PAGESTATE'; /** @@ -57,6 +74,7 @@ class TActivePageAdapter extends TControlAdapter /** * Process the callback request. + * @param THtmlWriter html content writer. */ public function processCallbackEvent($writer) { @@ -64,6 +82,9 @@ class TActivePageAdapter extends TControlAdapter $this->raiseCallbackEvent(); } + /** + * Trap errors and exceptions to be handled by TCallbackErrorHandler. + */ protected function trapCallbackErrorsExceptions() { $this->getApplication()->setErrorHandler(new TCallbackErrorHandler); @@ -71,6 +92,7 @@ class TActivePageAdapter extends TControlAdapter /** * Render the callback response. + * @param THtmlWriter html content writer. */ public function renderCallbackResponse($writer) { @@ -81,14 +103,12 @@ class TActivePageAdapter extends TControlAdapter /** * Renders the callback response by adding additional callback data and * javascript actions in the header and page state if required. + * @param THtmlWriter html content writer. */ protected function renderResponse($writer) { $response = $this->getResponse(); - $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute()->toArray(); - $actions = TJavascript::jsonEncode($executeJavascript); - $response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); - + //send response data in header if($response->getHasAdapter()) { @@ -109,12 +129,29 @@ class TActivePageAdapter extends TControlAdapter $response->appendHeader(self::CALLBACK_PAGESTATE_HEADER.': '.$pagestate); } } + + //safari must receive at least 1 byte of data. + $writer->write(" "); + + //output the end javascript + if($this->getPage()->getClientScript()->hasEndScripts()) + { + $writer = $response->createHtmlWriter(); + $this->getPage()->getClientScript()->renderEndScripts($writer); + $this->getPage()->getCallbackClient()->evaluateScript($writer); + } + + //output the actions + $executeJavascript = $this->getCallbackClientHandler()->getClientFunctionsToExecute()->toArray(); + $actions = TJavascript::jsonEncode($executeJavascript); + $response->appendHeader(self::CALLBACK_ACTION_HEADER.': '.$actions); + } /** * Trys to find the callback event handler and raise its callback event. - * @throws TInvalidCallbackRequestException if call back target is not found. - * @throws TInvalidCallbackHandlerException if the requested target does not + * @throws TInvalidCallbackException if call back target is not found. + * @throws TInvalidCallbackException if the requested target does not * implement ICallbackEventHandler. */ private function raiseCallbackEvent() @@ -129,13 +166,14 @@ class TActivePageAdapter extends TControlAdapter } else { - throw new TInvalidCallbackHandlerException($callbackHandler->getUniqueID()); + throw new TInvalidCallbackException( + 'callback_invalid_handler', $callbackHandler->getUniqueID()); } } else { $target = $this->getRequest()->itemAt(TPage::FIELD_CALLBACK_TARGET); - throw new TInvalidCallbackRequestException($target); + throw new TInvalidCallbackException('callback_invalid_target', $target); } } @@ -164,7 +202,7 @@ class TActivePageAdapter extends TControlAdapter } /** - * Callback parameter is decoded assuming JSON encoding. + * Gets callback parameter. JSON encoding is assumed. * @return string postback event parameter */ public function getCallbackEventParameter() @@ -187,7 +225,7 @@ class TActivePageAdapter extends TControlAdapter } /** - * Gets the callback client script handler that allows javascript functions + * Gets the callback client script handler. It handlers the javascript functions * to be executed during the callback response. * @return TCallbackClientScript callback client handler. */ @@ -267,16 +305,34 @@ class TCallbackEventParameter extends TEventParameter } } +/** + * TCallbackErrorHandler class. + * + * Captures errors and exceptions and send them back during callback response. + * When the application is in debug mode, the error and exception stack trace + * are shown. A TJavascriptLogger must be present on the client-side to view + * the error stack trace. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 19:11:47 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ class TCallbackErrorHandler extends TErrorHandler { + /** + * Displays the exceptions to the client-side TJavascriptLogger. + * A HTTP 500 status code is sent and the stack trace is sent as JSON encoded. + * @param Exception exception details. + */ protected function displayException($exception) { if($this->getApplication()->getMode()===TApplication::STATE_DEBUG) { $response = $this->getApplication()->getResponse(); - $data = TJavascript::jsonEncode($this->getExceptionData($exception)); + $trace = TJavascript::jsonEncode($this->getExceptionStackTrace($exception)); $response->appendHeader('HTTP/1.0 500 Internal Error'); - $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$data); + $response->appendHeader(TActivePageAdapter::CALLBACK_ERROR_HEADER.': '.$trace); } else { @@ -286,9 +342,13 @@ class TCallbackErrorHandler extends TErrorHandler $this->getApplication()->getResponse()->flush(); } - private function getExceptionData($exception) + /** + * @param Exception exception details. + * @return array exception stack trace details. + */ + private function getExceptionStackTrace($exception) { - $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 505; + $data['code']=$exception->getCode() > 0 ? $exception->getCode() : 500; $data['file']=$exception->getFile(); $data['line']=$exception->getLine(); $data['trace']=$exception->getTrace(); @@ -310,13 +370,16 @@ class TCallbackErrorHandler extends TErrorHandler } } -class TInvalidCallbackHandlerException extends TException +/** + * TInvalidCallbackException class. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 19:17:13 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +class TInvalidCallbackException extends TException { - } -class TInvalidCallbackRequestException extends TException -{ -} - ?> \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActivePanel.php b/framework/Web/UI/ActiveControls/TActivePanel.php index db1d8a7d..787b65d3 100644 --- a/framework/Web/UI/ActiveControls/TActivePanel.php +++ b/framework/Web/UI/ActiveControls/TActivePanel.php @@ -1,9 +1,37 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version : : + * @package System.Web.UI.ActiveControls */ -class TActivePanel extends TPanel +/** + * TActivePanel is the TPanel active control counterpart. + * + * TActivePanel allows the client-side panel contents to be updated during a + * callback response using the {@link flush} method. + * + * Example: Assume $param is an instance of TCallbackEventParameter attached to + * the OnCallback event a TCallback with ID "callback1", and + * "panel1" is the ID of a TActivePanel. + * + * function callback1_requested($sender, $param) + * { + * $this->panel1->flush($param->getOutput()); + * } + * + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 01:23:54 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +class TActivePanel extends TPanel implements IActiveControl { /** * Creates a new callback control, sets the adapter to @@ -16,10 +44,26 @@ class TActivePanel extends TPanel $this->setAdapter(new TActiveControlAdapter($this)); } + /** + * @return TBaseActiveControl standard active control options. + */ public function getActiveControl() { return $this->getAdapter()->getBaseActiveControl(); } + + /** + * Renders and replaces the panel's content on the client-side. + * @param THtmlWriter html writer + */ + public function flush($writer) + { + if($this->getActiveControl()->canUpdateClientSide()) + { + $this->render($writer); + $this->getPage()->getCallbackClient()->replaceContent($this,$writer); + } + } } ?> \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TActiveTextBox.php b/framework/Web/UI/ActiveControls/TActiveTextBox.php index ece08e11..2e207f92 100644 --- a/framework/Web/UI/ActiveControls/TActiveTextBox.php +++ b/framework/Web/UI/ActiveControls/TActiveTextBox.php @@ -1,9 +1,30 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ : $ + * @package System.Web.UI.ActiveControls */ -class TActiveTextBox extends TTextBox +/** + * TActiveTextBox class. + * + * TActiveTextBox allows the {@link setText Text} property of the textbox to + * be changed during callback. When {@link setAutoPostBack AutoPostBack} property + * is true, changes to the textbox contents will perform a callback request causing + * {@link onTextChanged OnTextChange} to be fired first followed by {@link onCallback OnCallback} + * event. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 20:05:16 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ +class TActiveTextBox extends TTextBox implements ICallbackEventHandler, IActiveControl { /** * Creates a new callback control, sets the adapter to @@ -15,7 +36,10 @@ class TActiveTextBox extends TTextBox parent::__construct(); $this->setAdapter(new TActiveControlAdapter($this)); } - + + /** + * @return TBaseActiveCallbackControl standard callback control options. + */ public function getActiveControl() { return $this->getAdapter()->getBaseActiveControl(); @@ -31,6 +55,43 @@ class TActiveTextBox extends TTextBox if($this->getActiveControl()->canUpdateClientSide() && $this->getHasLoadedPostData()) $this->getPage()->getCallbackClient()->setValue($this, $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 onTextChanged + * onTextChanged} event first and then the {@link onCallback OnCallback} event. + * This method is mainly used by framework and control developers. + * @param TCallbackEventParameter the event parameter + */ + public function raiseCallbackEvent($param) + { + $this->raisePostDataChangedEvent($param); + $this->onCallback($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 + * 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) + { + $this->raiseEvent('OnCallback', $this, $param); + } + + /** + * Renders the javascript for textbox. + */ + protected function renderClientControlScript($writer) + { + $writer->addAttribute('id',$this->getClientID()); + $this->getActiveControl()->registerCallbackClientScript( + $this->getClientClassName(), $this->getPostBackOptions()); + } /** * Gets the name of the javascript class responsible for performing postback for this control. diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index 63b1d089..601894ff 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -3,7 +3,7 @@ * Created on 7/05/2006 */ -class TAutoComplete extends TActiveTextBox implements ICallbackEventHandler, INamingContainer +class TAutoComplete extends TActiveTextBox implements INamingContainer { /** * @var ITemplate template for repeater items diff --git a/framework/Web/UI/ActiveControls/TBaseActiveControl.php b/framework/Web/UI/ActiveControls/TBaseActiveControl.php index 13d953c8..71d50a67 100644 --- a/framework/Web/UI/ActiveControls/TBaseActiveControl.php +++ b/framework/Web/UI/ActiveControls/TBaseActiveControl.php @@ -10,6 +10,8 @@ * @package System.Web.UI.ActiveControls */ +Prado::using('System.Web.UI.ActiveControls.TCallbackClientSideOptions'); + /** * TBaseActiveControl class provided additional basic property for every * active control. An instance of TBaseActiveControl or its decendent @@ -163,42 +165,66 @@ class TBaseActiveCallbackControl extends TBaseActiveControl } return $client; } + + /** + * Sets the client side options. Can only be set when client side is null. + * @param TCallbackClientSideOptions client side options. + */ + public function setClientSide($client) + { + if(is_null($this->getOption('ClientSide'))) + $this->setOption('ClientSide', $client); + else + throw new TConfigurationException( + 'active_controls_client_side_exists', $this->getControl()->getID()); + } /** * @return TCallbackClientSideOptions callback client-side options. */ protected function createClientSideOptions() { - if(($id=$this->getCallbackOptionID())!=='' - && ($control=$this->getControl()->findControl($id))!==null) - { - if($control instanceof TCallbackOptions) - return $control->getClientSide(); - } return new TCallbackClientSideOptions; } /** - * Sets the ID of a TCallbackOptions component to duplicate the client-side + * Sets default callback options. Takes the ID of a TCallbackOptions + * component to duplicate the client-side * options for this control. The {@link getClientSide ClientSide} * subproperties has precendent over the CallbackOptions property. * @param string ID of a TCallbackOptions control from which ClientSide * options are cloned. */ - public function setCallbackOptionID($value) + public function setCallbackOptions($value) { $this->setOption('CallbackOptions', $value, ''); } /** * @return string ID of a TCallbackOptions control from which ClientSide - * options are cloned. + * options are duplicated. */ - public function getCallbackOptionID() + public function getCallbackOptions() { return $this->getOption('CallbackOptions', ''); } + /** + * Returns an array of default callback client-side options. The default options + * are obtained from the client-side options of a TCallbackOptions control with + * ID specified by {@link setCallbackOptionsID CallbackOptionsID}. + * @return array list of default callback client-side options. + */ + protected function getDefaultClientSideOptions() + { + if(($id=$this->getCallbackOptions())!=='' + && ($control=$this->getControl()->findControl($id))!==null + && $control instanceof TCallbackOptions) + return $control->getClientSide()->getOptions()->toArray(); + else + return array(); + } + /** * @return boolean whether callback event trigger by this button will cause * input validation, default is true @@ -255,7 +281,8 @@ class TBaseActiveCallbackControl extends TBaseActiveControl */ protected function getClientSideOptions() { - $options = $this->getClientSide()->getOptions()->toArray(); + $default = $this->getDefaultClientSideOptions(); + $options = array_merge($default,$this->getClientSide()->getOptions()->toArray()); $validate = $this->getCausesValidation(); $options['CausesValidation']= $validate ? '' : false; $options['ValidationGroup']=$this->getValidationGroup(); diff --git a/framework/Web/UI/ActiveControls/TCallback.php b/framework/Web/UI/ActiveControls/TCallback.php index c42b2cd7..3c7d70c5 100644 --- a/framework/Web/UI/ActiveControls/TCallback.php +++ b/framework/Web/UI/ActiveControls/TCallback.php @@ -35,7 +35,7 @@ * @package System.Web.UI.ActiveControls * @since 3.0 */ -class TCallback extends TControl implements ICallbackEventHandler +class TCallback extends TControl implements ICallbackEventHandler, IActiveControl { /** * Creates a new callback control, sets the adapter to diff --git a/framework/Web/UI/ActiveControls/TCallbackClientScript.php b/framework/Web/UI/ActiveControls/TCallbackClientScript.php index 5d317c8a..d83bf90a 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientScript.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientScript.php @@ -313,7 +313,7 @@ class TCallbackClientScript extends TApplicationComponent * @see insertBefore * @see insertAfter */ - public function replace($element, $content, $method="Element.replace", $boundary=null) + protected function replace($element, $content, $method="Element.replace", $boundary=null) { if($content instanceof TControl) { @@ -325,11 +325,30 @@ class TCallbackClientScript extends TApplicationComponent $boundary = $this->getResponseContentBoundary($content); $content = null; } - + $this->callClientFunction('Prado.Element.replace', array($element, $method, $content, $boundary)); } + /** + * Replace the content of an element with new content contained in writer. + * @param TControl|string control element or HTML element id. + * @param THtmlWriter writer for the content. + */ + public function replaceContent($element,$writer) + { + $this->replace($element, $writer); + } + + /** + * Evaluate a block of javascript enclosed in a boundary. + * @param THtmlWriter writer for the content. + */ + public function evaluateScript($writer) + { + $this->replace(null, $writer, 'Prado.Element.evaluateScript'); + } + /** * Renders the control and return the content boundary from * TCallbackResponseWriter. This method should only be used by framework diff --git a/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php index 3f54e013..e8c2dc26 100644 --- a/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackClientSideOptions.php @@ -266,11 +266,17 @@ class TCallbackClientSideOptions extends TClientSideOptions return is_null($option) ? true : $option; } + /** + * @return string post back target ID + */ public function getPostBackTarget() { return $this->getOption('EventTarget'); } + /** + * @param string post back target ID + */ public function setPostBackTarget($value) { if($value instanceof TControl) @@ -278,17 +284,21 @@ class TCallbackClientSideOptions extends TClientSideOptions $this->setOption('EventTarget', $value); } + /** + * @return string post back event parameter. + */ public function getPostBackParameter() { return $this->getOption('EventParameter'); } + /** + * @param string post back event parameter. + */ public function setPostBackParameter($value) { $this->setOption('EventParameter', $value); } - - } ?> \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TCallbackOptions.php b/framework/Web/UI/ActiveControls/TCallbackOptions.php index fc0becb5..588cbf8a 100644 --- a/framework/Web/UI/ActiveControls/TCallbackOptions.php +++ b/framework/Web/UI/ActiveControls/TCallbackOptions.php @@ -1,10 +1,31 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version $Revision: $ : $ + * @package System.Web.UI.ActiveControls */ +/** + * TCallbackOptions class. + * + * TCallbackOptions allows common set of callback client-side options + * to be attached to other active controls. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 08:10:50 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ class TCallbackOptions extends TControl { + /** + * @var TCallbackClientSideOptions client side callback options. + */ private $_clientSide; /** @@ -28,7 +49,6 @@ class TCallbackOptions extends TControl { return new TCallbackClientSideOptions; } - } ?> \ No newline at end of file diff --git a/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php b/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php index d1ec67ad..1f11f973 100755 --- a/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php +++ b/framework/Web/UI/ActiveControls/TCallbackResponseAdapter.php @@ -1,14 +1,48 @@ + * @link http://www.pradosoft.com/ + * @copyright Copyright © 2006 PradoSoft + * @license http://www.pradosoft.com/license/ + * @version 3.0 + * @package System.Web.UI.ActiveControls + */ /** + * TCallbackResponseAdapter alters the THttpResponse's outputs. * + * A TCallbackResponseWriter is used instead of the TTextWrite when + * createHtmlWriter is called. Each call to createHtmlWriter will create + * a new TCallbackResponseWriter. When flushContent() is called each + * instance of TCallbackResponseWriter's content is flushed. + * + * The callback response data can be set using the {@link setResponseData ResponseData} + * property. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 07:52:14 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 */ class TCallbackResponseAdapter extends THttpResponseAdapter { + /** + * @TCallbackResponseWriter[] list of writers. + */ private $_writers=array(); - + /** + * @mixed callback response data. + */ private $_data; + /** + * Returns a new instance of THtmlWriter. + * An instance of TCallbackResponseWriter is created to hold the content. + * @param string writer class name. + * @param THttpResponse http response handler. + */ public function createNewHtmlWriter($type,$response) { $writer = new TCallbackResponseWriter(); @@ -16,6 +50,9 @@ class TCallbackResponseAdapter extends THttpResponseAdapter return parent::createNewHtmlWriter($type,$writer); } + /** + * Flushes the contents in the writers. + */ public function flushContent() { foreach($this->_writers as $writer) @@ -23,36 +60,75 @@ class TCallbackResponseAdapter extends THttpResponseAdapter parent::flushContent(); } + /** + * @param mixed callback response data. + */ public function setResponseData($data) { $this->_data = $data; } + /** + * @return mixed callback response data. + */ public function getResponseData() { return $this->_data; } } +/** + * TCallbackResponseWriter class. + * + * TCallbackResponseWriter class enclosed a chunck of content within a + * html comment boundary. This allows multiple chuncks of content to return + * in the callback response and update multiple HTML elements. + * + * The {@link setBoundary Boundary} property sets boundary identifier in the + * HTML comment that forms the boundary. By default, the boundary identifier + * is generated from the object instance ID. + * + * @author Wei Zhuo + * @version $Revision: $ Sun Jun 18 08:02:21 EST 2006 $ + * @package System.Web.UI.ActiveControls + * @since 3.0 + */ class TCallbackResponseWriter extends TTextWriter { + /** + * @var string boundary ID + */ private $_boundary; + /** + * Constructor. Generates boundary ID using object instance ID. + */ public function __construct() { $this->_boundary = sprintf('%x',crc32((string)$this)); } + /** + * @return string boundary identifier. + */ public function getBoundary() { return $this->_boundary; } + /** + * @param string boundary identifier. + */ public function setBoundary($value) { $this->_boundary = $value; } + /** + * Returns the text content wrapped within a HTML comment with boundary + * identifier as its comment content. + * @return string text content chunck. + */ public function flush() { $content = ''; diff --git a/framework/Web/UI/TClientScriptManager.php b/framework/Web/UI/TClientScriptManager.php index eb5e445a..8f43fbab 100644 --- a/framework/Web/UI/TClientScriptManager.php +++ b/framework/Web/UI/TClientScriptManager.php @@ -429,6 +429,22 @@ class TClientScriptManager extends TApplicationComponent { return isset($this->_endScripts[$key]); } + + /** + * @return boolean true if any end scripts are registered. + */ + public function hasEndScripts() + { + return count($this->_endScripts) > 0; + } + + /** + * @return boolean true if any begin scripts are registered. + */ + public function hasBeginScripts() + { + return count($this->_beginScripts) > 0; + } /** * @param string a unique key diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index a1d64765..838d0342 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -269,6 +269,8 @@ class TPage extends TTemplateControl */ protected function processCallbackRequest($writer) { + Prado::using('System.Web.UI.ActiveControls.TActivePageAdapter'); + $this->setAdapter(new TActivePageAdapter($this)); Prado::trace("Page onPreInit()",'System.Web.UI.TPage'); diff --git a/framework/Web/UI/WebControls/TBaseValidator.php b/framework/Web/UI/WebControls/TBaseValidator.php index 1194089e..a0801d4c 100644 --- a/framework/Web/UI/WebControls/TBaseValidator.php +++ b/framework/Web/UI/WebControls/TBaseValidator.php @@ -124,7 +124,8 @@ abstract class TBaseValidator extends TLabel implements IValidator } /** - * Adds attributes to renderer. + * Adds attributes to renderer. Calls parent implementation and renders the + * client control scripts. * @param THtmlWriter the renderer */ protected function addAttributesToRender($writer) @@ -137,6 +138,7 @@ abstract class TBaseValidator extends TLabel implements IValidator $writer->addStyleAttribute('visibility','hidden'); $writer->addAttribute('id',$this->getClientID()); parent::addAttributesToRender($writer); + $this->renderClientControlScript($writer); } /** @@ -217,11 +219,10 @@ abstract class TBaseValidator extends TLabel implements IValidator * Renders the javascript code to the end script. * If you override this method, be sure to call the parent implementation * so that the event handlers can be invoked. - * @param TEventParameter event parameter to be passed to the event handlers + * @param THtmlWriter the renderer */ - public function onPreRender($param) + public function renderClientControlScript($writer) { - parent::onPreRender($param); $scripts = $this->getPage()->getClientScript(); $formID=$this->getPage()->getForm()->getClientID(); $scriptKey = "TBaseValidator:$formID"; @@ -236,7 +237,7 @@ abstract class TBaseValidator extends TLabel implements IValidator $this->registerClientScriptValidator(); $this->updateControlCssClass(); } - + /** * Update the ControlToValidate component's css class depending * if the ControlCssClass property is set, and whether this is valid. diff --git a/framework/Web/UI/WebControls/TClientScript.php b/framework/Web/UI/WebControls/TClientScript.php index 23aa1425..020b44b8 100644 --- a/framework/Web/UI/WebControls/TClientScript.php +++ b/framework/Web/UI/WebControls/TClientScript.php @@ -22,6 +22,11 @@ * * * + * The {@link setPreRenderControlTypes PreRenderControlTypes} property can + * be used to specify that controls type/class names that should pre-render itself + * even though they may not be rendered on the page. This is useful to publish + * controls that require assets and is only visible after a callback response. + * * @TODO May be use it to include stylesheets as well. * * @author Wei Zhuo @@ -53,6 +58,23 @@ class TClientScript extends TControl $this->setViewState('PradoScripts', $value, ''); } + /** + * @param string comma delimited list of controls that wish to be prerendered + * so as to publish its assets. + */ + public function setPreRenderControlTypes($value) + { + $this->setViewState('PreRenderControls', $value); + } + + /** + * @return string comma delimited list of controls types that require prerendering. + */ + public function getPreRenderControlTypes() + { + return $this->getViewState('PreRenderControls', ''); + } + /** * Calls the client script manager to add each of the requested client * script libraries. @@ -69,6 +91,23 @@ class TClientScript extends TControl if(strlen($script) > 0) $cs->registerPradoScript($script); } + $this->preRenderControls($param); + } + + /** + * PreRender other controls to allow them to publish their assets. Useful + * when callback response components that require assets to be present on the page. + * @param mixed event paramater + */ + protected function preRenderControls($param) + { + $types = preg_split('/,|\s+/', $this->getPreRenderControlTypes()); + foreach($types as $type) + { + $control = Prado::createComponent($type); + $control->setPage($this->getPage()); + $control->onPreRender($param); + } } } diff --git a/framework/interfaces.php b/framework/interfaces.php index d87cc20f..b28d3881 100644 --- a/framework/interfaces.php +++ b/framework/interfaces.php @@ -292,7 +292,10 @@ interface IBindable */ interface IActiveControl { - + /** + * @return TBaseActiveControl Active control properties. + */ + public function getActiveControl(); } /** @@ -315,11 +318,6 @@ interface ICallbackEventHandler * @param TCallbackEventParameter the parameter associated with the callback event */ public function raiseCallbackEvent($eventArgument); - - /** - * @return TBaseActiveControl Active control properties. - */ - public function getActiveControl(); } ?> \ No newline at end of file diff --git a/framework/prado-cli.php b/framework/prado-cli.php new file mode 100755 index 00000000..f8ce826c --- /dev/null +++ b/framework/prado-cli.php @@ -0,0 +1,413 @@ +#!/usr/bin/php +', 'create a new project '); +$console = new ConsoleOptions($options, $_SERVER['argv']); + +if($console->hasOptions()) +{ + if($dir = $console->getOption('create')) + create_new_prado_project($dir); +} +else +{ + $details = $console->renderOptions(); +echo << +Options: +$details +Example: php prado-cli.php -c ./example1 + + +EOD; +} + +/** + * Functions to create new prado project. + */ + +function create_new_prado_project($dir) +{ + if(strlen(trim($dir)) == 0) + return; + + $rootPath = realpath(dirname(trim($dir))); + + $basePath = $rootPath.'/'.basename($dir); + $assetPath = $basePath.'/assets'; + $protectedPath = $basePath.'/protected'; + $runtimePath = $basePath.'/protected/runtime'; + $pagesPath = $protectedPath.'/pages'; + + $indexFile = $basePath.'/index.php'; + $htaccessFile = $protectedPath.'/.htaccess'; + $defaultPageFile = $pagesPath.'/Home.page'; + + create_directory($basePath, 0755); + create_directory($assetPath,0777); + create_directory($protectedPath,0755); + create_directory($runtimePath,0777); + create_directory($pagesPath,0755); + + file_put_contents($indexFile, render_index_file()); + file_put_contents($htaccessFile, render_htaccess_file()); + file_put_contents($defaultPageFile, render_default_page()); +} + +function create_directory($dir, $mask) +{ + if(!is_dir($dir)) + mkdir($dir); + if(is_dir($dir)) + chmod($dir, $mask); +} + +function render_index_file() +{ + $framework = realpath(dirname(__FILE__)).'/prado.php'; +return 'run(); + +?>'; +} + +function render_htaccess_file() +{ + return 'deny from all'; +} + + +function render_default_page() +{ +return <<Welcome to Prado! +EOD; +} + +/* vim: set expandtab tabstop=4 shiftwidth=4: */ +// +----------------------------------------------------------------------+ +// | PHP Version 4 | +// +----------------------------------------------------------------------+ +// | Copyright (c) 1997-2003 The PHP Group | +// +----------------------------------------------------------------------+ +// | This source file is subject to version 3.0 of the PHP license, | +// | that is bundled with this package in the file LICENSE, and is | +// | available through the world-wide-web at the following url: | +// | http://www.php.net/license/3_0.txt. | +// | If you did not receive a copy of the PHP license and are unable to | +// | obtain it through the world-wide-web, please send a note to | +// | license@php.net so we can mail you a copy immediately. | +// +----------------------------------------------------------------------+ +// | Author: Andrei Zmievski | +// +----------------------------------------------------------------------+ +// +// $Id: Getopt.php,v 1.21.4.7 2003/12/05 21:57:01 andrei Exp $ + +/** + * Command-line options parsing class. + * + * @author Andrei Zmievski + * @author Wei Zhuo + */ +class ConsoleOptions +{ + private $_short_options; + private $_long_options; + private $_options; + private $_args; + private $_values; + + /** + * @param array list of options with the following format + * option['name'] = array('short', 'long', 'parameter', 'description') + * see @link setOptions} for details on 'short' and 'long' string details. + */ + public function __construct($options, $args) + { + $short = ''; + $long = array(); + foreach($options as $option) + { + $short .= $option[0]; + $long[] = $option[1]; + } + $this->setOptions($short,$long); + $this->_options = $options; + $this->_args = $args; + } + + /** + * The second parameter is a string of allowed short options. Each of the + * option letters can be followed by a colon ':' to specify that the option + * requires an argument, or a double colon '::' to specify that the option + * takes an optional argument. + * + * The third argument is an optional array of allowed long options. The + * leading '--' should not be included in the option name. Options that + * require an argument should be followed by '=', and options that take an + * option argument should be followed by '=='. + * + * @param string $short_options specifies the list of allowed short options + * @param array $long_options specifies the list of allowed long options + */ + protected function setOptions($short_options, $long_options=null) + { + $this->_short_options = $short_options; + $this->_long_options = $long_options; + } + + /** + * @return string list of options and its descriptions + */ + public function renderOptions() + { + $options = array(); + $descriptions = array(); + $max = 0; + foreach($this->_options as $option) + { + $short = str_replace(':','',$option[0]); + $long = str_replace('=','',$option[1]); + $desc = $option[2]; + $details = " -{$short}, --{$long} {$desc}"; + if(($len = strlen($details)) > $max) + $max = $len; + $options[] = $details; + $descriptions[] = $option[3]; + } + + $content = ''; + for($i = 0, $k = count($options); $i < $k; $i++) + $content .= str_pad($options[$i],$max+3).$descriptions[$i]."\n"; + return $content; + } + + /** + * @param string argument name + * @return string argument value + */ + public function getOption($name) + { + if(is_null($this->_values)) + $this->_values = $this->getNamedOptions(); + return isset($this->_values[$name]) ? $this->_values[$name] : null; + } + + /** + * @return array list of all options given. + */ + public function getOptions() + { + if(is_null($this->_values)) + $this->_values = $this->getNamedOptions(); + return $this->_values; + } + + /** + * @return boolean true if one or more options are given. + */ + public function hasOptions() + { + if(is_null($this->_values)) + $this->_values = $this->getNamedOptions(); + return count($this->_values) > 0; + } + + /** + * Parse the options from args into named arguements. + */ + protected function getNamedOptions() + { + $options = array(); + $values = $this->parseOptions($this->_args); + foreach($values[0] as $value) + { + foreach($this->_options as $name => $option) + { + if(strpos($option[0],$value[0]) !== false + || strpos($option[1], $value[0]) !== false) + $options[$name] = $value[1]; + } + } + return $options; + } + + /** + * Gets the command-line options. + * + * The parameter to this function should be the list of command-line + * arguments without the leading reference to the running program. + * + * The return value is an array of two elements: the list of parsed + * options and the list of non-option command-line arguments. Each entry in + * the list of parsed options is a pair of elements - the first one + * specifies the option, and the second one specifies the option argument, + * if there was one. + * + * Long and short options can be mixed. + * + * @param array $args an array of command-line arguments + * @return array two-element array containing the list of parsed options and + * the non-option arguments + */ + public function parseOptions($args) + { + if (empty($args)) + return array(array(), array()); + + $short_options = $this->_short_options; + $long_options = $this->_long_options; + + $opts = array(); + $non_opts = array(); + + settype($args, 'array'); + + if ($long_options) + sort($long_options); + + if (isset($args[0]{0}) && $args[0]{0} != '-') + array_shift($args); + + reset($args); + + while (list($i, $arg) = each($args)) + { + + /* The special element '--' means explicit end of + options. Treat the rest of the arguments as non-options + and end the loop. */ + if ($arg == '--') { + $non_opts = array_merge($non_opts, array_slice($args, $i + 1)); + break; + } + + if ($arg{0} != '-' || (strlen($arg) > 1 && $arg{1} == '-' && !$long_options)) + { + $non_opts = array_merge($non_opts, array_slice($args, $i)); + break; + } + elseif (strlen($arg) > 1 && $arg{1} == '-') + $this->parseLongOption(substr($arg, 2), $long_options, $opts, $args); + else + $this->parseShortOption(substr($arg, 1), $short_options, $opts, $args); + } + + return array($opts, $non_opts); + } + + private function parseShortOption($arg, $short_options, &$opts, &$args) + { + for ($i = 0; $i < strlen($arg); $i++) + { + $opt = $arg{$i}; + $opt_arg = null; + + /* Try to find the short option in the specifier string. */ + if (($spec = strstr($short_options, $opt)) === false || $arg{$i} == ':') + throw new Exception("Console_Getopt: unrecognized option -- $opt"); + + if (strlen($spec) > 1 && $spec{1} == ':') + { + if (strlen($spec) > 2 && $spec{2} == ':') + { + if ($i + 1 < strlen($arg)) + { + /* Option takes an optional argument. Use the remainder of + the arg string if there is anything left. */ + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + } + else + { + /* Option requires an argument. Use the remainder of the arg + string if there is anything left. */ + if ($i + 1 < strlen($arg)) + { + $opts[] = array($opt, substr($arg, $i + 1)); + break; + } + else if (list(, $opt_arg) = each($args)) + /* Else use the next argument. */; + else + throw new Exception("Console_Getopt: option requires an argument -- $opt"); + } + } + + $opts[] = array($opt, $opt_arg); + } + } + + private function parseLongOption($arg, $long_options, &$opts, &$args) + { + @list($opt, $opt_arg) = explode('=', $arg); + $opt_len = strlen($opt); + + for ($i = 0; $i < count($long_options); $i++) + { + $long_opt = $long_options[$i]; + $opt_start = substr($long_opt, 0, $opt_len); + + /* Option doesn't match. Go on to the next one. */ + if ($opt_start != $opt) + continue; + + $opt_rest = substr($long_opt, $opt_len); + + /* Check that the options uniquely matches one of the allowed + options. */ + if ($opt_rest != '' && $opt{0} != '=' && + $i + 1 < count($long_options) && + $opt == substr($long_options[$i+1], 0, $opt_len)) + { + throw new Exception("Console_Getopt: option --$opt is ambiguous"); + } + + if (substr($long_opt, -1) == '=') + { + if (substr($long_opt, -2) != '==') + { + /* Long option requires an argument. + Take the next argument if one wasn't specified. */; + if (!strlen($opt_arg) && !(list(, $opt_arg) = each($args))) + throw new Exception("Console_Getopt: option --$opt requires an argument"); + } + } + else if ($opt_arg) + throw new Exception("Console_Getopt: option --$opt doesn't allow an argument"); + + $opts[] = array($opt, $opt_arg); + return; + } + + throw new Exception("Console_Getopt: unrecognized option --$opt"); + } +} + + +?> \ No newline at end of file -- cgit v1.2.3