diff options
Diffstat (limited to 'framework/Web/UI/TPage.php')
-rw-r--r-- | framework/Web/UI/TPage.php | 505 |
1 files changed, 272 insertions, 233 deletions
diff --git a/framework/Web/UI/TPage.php b/framework/Web/UI/TPage.php index 4c04f597..42f495cf 100644 --- a/framework/Web/UI/TPage.php +++ b/framework/Web/UI/TPage.php @@ -5,6 +5,24 @@ Prado::using('System.Web.UI.WebControls.*'); class TPage extends TTemplateControl
{
+ const FIELD_POSTBACK_TARGET='PRADO_POSTBACK_TARGET';
+ const FIELD_POSTBACK_PARAMETER='PRADO_POSTBACK_PARAMETER';
+ const FIELD_LASTFOCUS='PRADO_LASTFOCUS';
+ const FIELD_PAGESTATE='PRADO_PAGESTATE';
+ const FIELD_SCROLLX='PRADO_SCROLLX';
+ const FIELD_SCROLLY='PRADO_SCROLLY';
+ /**
+ * @var array system post fields
+ */
+ private static $_systemPostFields=array(
+ self::FIELD_POSTBACK_TARGET=>true,
+ self::FIELD_POSTBACK_PARAMETER=>true,
+ self::FIELD_LASTFOCUS=>true,
+ self::FIELD_PAGESTATE=>true,
+ self::FIELD_SCROLLX=>true,
+ self::FIELD_SCROLLY=>true,
+ '__PREVPAGE','__CALLBACKID','__CALLBACKPARAM'
+ );
/**
* @var TApplication application instance
*/
@@ -49,27 +67,47 @@ class TPage extends TTemplateControl * @var TMap postback data that is not handled during first invocation of LoadPostData.
*/
private $_restPostData;
+ /**
+ * @var array list of controls whose data have been changed due to the postback
+ */
+ private $_controlsPostDataChanged=array();
+ /**
+ * @var array list of controls that need to load post data in the current request
+ */
+ private $_controlsRequiringPostBack=array();
+ /**
+ * @var array list of controls that need to load post data in the next postback
+ */
+ private $_controlsRegisteredForPostBack=array();
+ /**
+ * @var TControl control that needs to raise postback event
+ */
+ private $_postBackEventTarget=null;
+ /**
+ * @var mixed postback event parameter
+ */
+ private $_postBackEventParameter=null;
+ /**
+ * @var boolean whether form has rendered
+ */
+ private $_formRendered=false;
+ /**
+ * @var boolean whether the current rendering is within a form
+ */
+ private $_inFormRender=false;
+ /**
+ * @var TControl the control to be focused when the page is sent back to user
+ */
+ private $_focusedControl=null;
+ /**
+ * @var boolean whether or not to maintain page scroll position
+ */
+ private $_maintainScrollPosition=false;
private $_maxPageStateFieldLength=10;
private $_enableViewStateMac=true;
- private $_performPreRendering=true;
- private $_performRendering=true;
-
- private $_formRendered=false;
- private $_inFormRender=false;
- private $_requirePostBackScript=false;
- private $_postBackScriptRendered=false;
private $_isCrossPagePostBack=false;
private $_previousPagePath='';
- private $_preInitWorkComplete=false;
- private $_changedPostDataConsumers=array();
- private $_controlsRequiringPostBack=array();
- private $_registeredControlThatRequireRaiseEvent=null;
- private $_registeredControlsThatRequirePostBack=null;
- private $_autoPostBackControl=null;
- private $_webFormsScriptRendered=false;
- private $_requireWebFormsScript=false;
- private static $_systemPostFields=array('__EVENTTARGET','__EVENTPARAM','__STATE','__PREVPAGE','__CALLBACKID','__CALLBACKPARAM','__LASTFOCUS');
/**
* Constructor.
@@ -90,6 +128,48 @@ class TPage extends TTemplateControl }
/**
+ * Runs through the page lifecycles.
+ * This method runs through the page lifecycles.
+ * @param THtmlTextWriter the HTML writer
+ */
+ public function run($writer)
+ {
+ $this->determinePostBackMode();
+
+ $this->onPreInit(null);
+ $this->initRecursive();
+ $this->onInitComplete(null);
+
+ if($this->getIsPostBack())
+ {
+ $this->_restPostData=new TMap;
+ $this->loadPageState();
+ $this->processPostData($this->_postData,true);
+ $this->onPreLoad(null);
+ $this->loadRecursive();
+ $this->processPostData($this->_restPostData,false);
+ $this->raiseChangedEvents();
+ $this->raisePostBackEvent();
+ $this->onLoadComplete(null);
+ }
+ else
+ {
+ $this->onPreLoad(null);
+ $this->loadRecursive();
+ $this->onLoadComplete(null);
+ }
+
+ $this->preRenderRecursive();
+ $this->onPreRenderComplete(null);
+
+ $this->savePageState();
+ $this->onSaveStateComplete(null);
+
+ $this->renderControl($writer);
+ $this->unloadRecursive();
+ }
+
+ /**
* Loads and parses the page template.
* This method overrides the parent implementation by allowing loading
* a page template from a specified template file.
@@ -378,7 +458,7 @@ class TPage extends TTemplateControl private function determinePostBackMode()
{
$postData=$this->_application->getRequest()->getItems();
- if($postData->contains(TClientScriptManager::FIELD_PAGE_STATE) || $postData->contains(TClientScriptManager::FIELD_POSTBACK_TARGET))
+ if($postData->contains(self::FIELD_PAGESTATE) || $postData->contains(self::FIELD_POSTBACK_TARGET))
$this->_postData=$postData;
}
@@ -399,6 +479,30 @@ class TPage extends TTemplateControl }
/**
+ * This method is invoked when control state is to be saved.
+ * You can override this method to do last step state saving.
+ * Parent implementation must be invoked.
+ * @param TEventParameter event parameter
+ */
+ protected function onSaveState($param)
+ {
+ parent::onSaveState($param);
+ $this->setViewState('ControlsRequiringPostBack',$this->_controlsRegisteredForPostBack,array());
+ }
+
+ /**
+ * This method is invoked right after the control has loaded its state.
+ * You can override this method to initialize data from the control state.
+ * Parent implementation must be invoked.
+ * @param TEventParameter
+ */
+ protected function onLoadState($param)
+ {
+ $this->_controlsRequiringPostBack=$this->getViewState('ControlsRequiringPostBack',array());
+ parent::onLoadState($param);
+ }
+
+ /**
* Loads page state from persistent storage.
*/
protected function loadPageState()
@@ -416,212 +520,100 @@ class TPage extends TTemplateControl $this->getPageStatePersister()->save($state);
}
- public function RegisterEnabledControl($control)
- {
- $this->getEna.EnabledControls.Add(control);
- }
-
-
-
/**
- * @internal
+ * @param string the field name
+ * @return boolean whether the specified field is a system field in postback data
*/
- public function registerPostBackScript()
+ protected function isSystemPostField($field)
{
- if($this->getClientSupportsJavaScript() && !$this->_postBackScriptRendered)
- {
- if(!$this->_requirePostBackScript)
- {
- $this->getClientScript()->registerHiddenField('__EVENTTARGET','');
- $this->getClientScript()->registerHiddenField('__EVENTPARAM','');
- $this->_requirePostBackScript=true;
- }
- }
- }
-
- public function registerWebFormsScript()
- {
- if($this->getClientSupportsJavaScript() && !$this->_webFormsScriptRendered)
- {
- $this->registerPostBackScript();
- $this->_requireWebFormsScript=true;
- }
- }
-
-
- public function ensureRenderInForm($control)
- {
- if(!$this->_inFormRender)
- throw new THttpException('control_not_in_form',$control->getUniqueID());
- }
-
- private function renderPostBackScript($writer)
- {
- $id=$this->_form->getUniqueID();
- $str=<<<EOD
-\n<script type="text/javascript">
-<!--
-var theForm=document.forms['$id'];
-if(!theForm)
- theForm=document.$id;
-function __doPostBack(eventTarget,eventParam) {
- if(!theForm.onsubmit || (theForm.onsubmit()!=false)) {
- theForm.__EVENTTARGET.value = eventTarget;
- theForm.__EVENTPARAM.value = eventParam;
- theForm.submit();
- }
-}
-// -->
-</script>\n
-EOD;
- $writer->write($str);
- $this->_postBackScriptRendered=true;
- }
-
- private function renderWebFormsScript($writer)
- {
- $writer->write("\n<script src=\"js/WebForms.js\" type=\"text/javascript\"></script>\n");
- $this->_webFormsScriptRendered=true;
- }
-
- final public function getClientSupportsJavaScript()
- {
- // todo
- return true;
+ return isset(self::$_systemPostFields[$field]);
}
/**
- * @internal
+ * Registers a control for loading post data in the next postback.
+ * @param TControl control registered for loading post data
*/
- final public function beginFormRender($writer)
- {
- if($this->_formRendered)
- throw new THttpException('multiple_form_not_allowed');
- $this->_formRendered=true;
- $this->_inFormRender=true;
-
- $this->getClientScript()->renderHiddenFields($writer);
- if($this->getClientSupportsJavaScript())
- {
- /*
- if($this->getMaintainScrollPositionOnPostBack() && !$this->_requireScrollScript)
- {
- $cs=$this->getClientScript();
- $cs->registerHiddenField('_SCROLLPOSITIONX',$this->_scrollPositionX);
- $cs->registerHiddenField('_SCROLLPOSITIONY',$this->_scrollPositionY);
- $cs->registerStartupScript(get_class($this),"PageScrollPositionScript", "\r\nvar WebForm_ScrollPositionSubmit = theForm.submit;\r\ntheForm.submit = WebForm_SaveScrollPositionSubmit;\r\n\r\nvar WebForm_ScrollPositionOnSubmit = theForm.onsubmit;\r\ntheForm.onsubmit = WebForm_SaveScrollPositionOnSubmit;\r\n\r\nvar WebForm_ScrollPositionLoad = window.onload;\r\nwindow.onload = WebForm_RestoreScrollPosition;\r\n", true);
- $this->registerWebFormScript();
- $this->_requireScrollScript=true;
- }
- */
- if($this->_requirePostBackScript)
- $this->renderPostBackScript($writer,$this->_form->getUniqueID());
- if($this->_requireWebFormsScript)
- $this->renderWebFormsScript($writer);
- }
- $this->getClientScript()->renderScriptBlocks($writer);
- // todo: more ....
- }
-
- final public function getIsPostBackEventControlRegistered()
+ public function registerRequiresPostBack(TControl $control)
{
- return $this->_registeredControlThatRequireRaiseEvent!==null;
+ $this->_controlsRegisteredForPostBack[$control->getUniqueID()]=true;
}
/**
- * @internal
+ * @return TControl the control responsible for the current postback event, null if nonexistent
*/
- final public function endFormRender($writer)
+ public function getPostBackEventTarget()
{
- $cs=$this->getClientScript();
- if($this->getClientSupportsJavaScript())
- $cs->renderArrayDeclarations($writer);
- $cs->renderHiddenFields($writer);
- if($this->getClientSupportsJavaScript())
+ if($this->_postBackEventTarget===null)
{
- if($this->_requirePostBackScript && !$this->_postBackScriptRendered)
- $this->renderPostBackScript($writer);
- if($this->_requireWebFormsScript && !$this->_webFormsScriptRendered)
- $this->renderWebFormsScript($writer);
+ $eventTarget=$this->_postData->itemAt(self::FIELD_POSTBACK_TARGET);
+ if(!empty($eventTarget))
+ $this->_postBackEventTarget=$this->findControl($eventTarget);
}
- $cs->renderStartupScripts($writer);
- $this->_inFormRender=false;
- }
-
- final public function getClientOnSubmitEvent()
- {
- // todo
- if($this->getClientScript()->getHasSubmitStatements())
- return 'javascript:return WebForm_OnSubmit();';
- else
- return '';
- }
-
- protected function initializeCulture()
- {
+ return $this->_postBackEventTarget;
}
/**
- * @internal
+ * Registers a control to raise postback event in the current request.
+ * @param TControl control registered to raise postback event.
*/
- public function loadScrollPosition()
+ public function setPostBackEventTarget(TControl $control)
{
- if($this->_previousPagePath==='' && $this->_requestValueCollection)
- {
- if(isset($_REQUEST['__SCROLLPOSITIONX']))
- $this->_scrollPositionX=(integer)$_REQUEST['__SCROLLPOSITIONX'];
- if(isset($_REQUEST['__SCROLLPOSITIONY']))
- $this->_scrollPositionX=(integer)$_REQUEST['__SCROLLPOSITIONY'];
- }
+ $this->_postBackEventTarget=$control;
}
-
- final public function registerAsyncTask()
+ /**
+ * @return mixed postback event parameter
+ */
+ public function getPostBackEventParameter()
{
+ if($this->_postBackEventParameter===null)
+ $this->_postBackEventParameter=$this->_postData->itemAt(self::FIELD_POSTBACK_PARAMETER);
+ return $this->_postBackEventParameter;
}
- final public function registerRequiresPostBack($control)
+ /**
+ * @param mixed postback event parameter
+ */
+ public function setPostBackEventParameter($value)
{
- if(!$this->_registeredControlsThatRequirePostBack)
- $this->_registeredControlsThatRequirePostBack=new TList;
- $this->_registeredControlsThatRequirePostBack->add($control->getUniqueID());
+ $this->_postBackEventParameter=$value;
}
- final public function registerRequiresRaiseEvent($control)
+ /**
+ * Registers a control as the
+ */
+ public function registerAutoPostBackControl(TControl $control)
{
- $this->_registeredControlThatRequireRaiseEvent=$control;
+ $this->_autoPostBackControl=$control;
}
+ /**
+ * Processes post data.
+ * @param TMap post data to be processed
+ * @param boolean whether this method is invoked before {@link onLoad Load}.
+ */
protected function processPostData($postData,$beforeLoad)
{
- $eventTarget=$postData->itemAt('__EVENTTARGET');
+ if($beforeLoad)
+ $this->_restPostData=new TMap;
foreach($postData as $key=>$value)
{
- if(in_array($key,self::$_systemPostFields))
+ if($this->isSystemPostField($key))
continue;
else if($control=$this->findControl($key))
{
if($control instanceof IPostBackDataHandler)
{
- if($control->loadPostData($key,$this->_postData))
- $this->_changedPostDataConsumers[]=$control;
- unset($this->_controlsRequiringPostBack[$key]);
- }
- else
- {
- if(empty($eventTarget))
- {
- if($control instanceof IPostBackEventHandler)
- $this->registerRequiresRaiseEvent($control);
- }
- else
- unset($this->_controlsRequiringPostBack[$key]);
+ if($control->loadPostData($key,$postData))
+ $this->_controlsPostDataChanged[]=$control;
}
+ else if($control instanceof IPostBackEventHandler)
+ $this->setPostBackEventTarget($control);
+ unset($this->_controlsRequiringPostBack[$key]);
}
else if($beforeLoad)
$this->_restPostData->add($key,$value);
}
- $list=new TMap;
foreach($this->_controlsRequiringPostBack as $key=>$value)
{
if($control=$this->findControl($key))
@@ -629,93 +621,140 @@ EOD; if($control instanceof IPostBackDataHandler)
{
if($control->loadPostData($key,$this->_postData))
- $this->_changedPostDataConsumers->add($control);
+ $this->_controlsPostDataChanged[]=$control;
}
else
- throw new THttpException('postback_control_not_found',$key);
+ throw new TInvalidDataValueException('page_postbackcontrol_invalid',$key);
+ unset($this->_controlsRequiringPostBack[$key]);
}
- else if($beforeLoad)
- $list->add($key,null);
}
- $this->_controlsRequiringPostBack=$list;
}
- final public function getAutoPostBackControl()
+ /**
+ * Raises PostDataChangedEvent for controls whose data have been changed due to the postback.
+ */
+ private function raiseChangedEvents()
{
- return $this->_autoPostBackControl;
+ foreach($this->_controlsPostDataChanged as $control)
+ $control->raisePostDataChangedEvent();
}
- final public function setAutoPostBackControl($control)
+ /**
+ * Raises PostBack event.
+ */
+ private function raisePostBackEvent()
{
- $this->_autoPostBackControl=$control;
+ if(($postBackHandler=$this->getPostBackEventTarget())===null)
+ $this->validate();
+ else if($postBackHandler instanceof IPostBackEventHandler)
+ $postBackHandler->raisePostBackEvent($this->getPostBackEventParameter());
}
- private function raiseChangedEvents()
+ /**
+ * Ensures the control is rendered within a form.
+ * @param TControl the control to be rendered
+ * @throws TInvalidConfigurationException if the control is outside of the form
+ */
+ public function ensureRenderInForm($control)
{
- foreach($this->_changedPostDataConsumers as $control)
- $control->raisePostDataChangedEvent();
+ if(!$this->_inFormRender)
+ throw new TInvalidConfigurationException('page_control_outofform',get_class($control),$control->getID(false));
+ }
+
+ /**
+ * @internal
+ */
+ public function beginFormRender($writer)
+ {
+ if($this->_formRendered)
+ throw new TInvalidConfigurationException('page_singleform_required');
+ $this->_formRendered=true;
+ $this->_inFormRender=true;
+ $this->getClientScript()->renderBeginScripts($writer);
}
- private function raisePostBackEvent($postData)
+ /**
+ * @internal
+ */
+ public function endFormRender($writer)
{
- if($this->_registeredControlThatRequireRaiseEvent)
- {
- $this->_registeredControlThatRequireRaiseEvent->raisePostBackEvent(null);
- }
- else
+ $cs=$this->getClientScript();
+ if($this->getClientSupportsJavaScript())
{
- $eventTarget=$postData->itemAt('__EVENTTARGET');
- if(!empty($eventTarget) || $this->getAutoPostBackControl())
+ if($this->_focusedControl && $this->_focusedControl->getVisible(true))
+ $cs->registerFocusScript($this->_focusedControl->getClientID());
+ else if($this->_postData && ($lastFocus=$this->_postData->itemAt(self::FIELD_LASTFOCUS))!==null)
+ $cs->registerFocusScript($lastFocus);
+ if($this->_maintainScrollPosition && $this->_postData)
{
- if(!empty($eventTarget))
- $control=$this->findControl($eventTarget);
- else
- $control=null;
- if($control instanceof IPostBackEventHandler)
- $control->raisePostBackEvent($postData->itemAt('__EVENTPARAM'));
+ $x=TPropertyValue::ensureInteger($this->_postData->itemAt(self::PRADO_SCROLLX));
+ $y=TPropertyValue::ensureInteger($this->_postData->itemAt(self::PRADO_SCROLLY));
+ $cs->registerScrollScript($x,$y);
}
- else
- $this->validate();
+ $cs->renderHiddenFields($writer);
+ $cs->renderArrayDeclarations($writer);
+ $cs->renderExpandoAttributes($writer);
+ $cs->renderScriptIncludes($writer);
+ $cs->renderEndScripts($writer);
}
+ else
+ $cs->renderHiddenFields($writer);
+ $this->_inFormRender=false;
}
- public function run($writer)
+ public function setFocus(TControl $value)
{
- $this->determinePostBackMode();
- $this->_restPostData=new TMap;
+ $this->_focusedControl=$value;
+ }
- $this->onPreInit(null);
- $this->_preInitWorkComplete=true;
+ public function getMaintainScrollPosition()
+ {
+ return $this->_maintainScrollPosition;
+ }
- $this->initRecursive(null);
- $this->onInitComplete(null);
+ public function setMaintainScrollPosition($value)
+ {
+ $this->_maintainScrollPosition=TPropertyValue::ensureBoolean($value);
+ }
- if($this->getIsPostBack())
- {
- $this->loadPageState();
- $this->processPostData($this->_postData,true);
- }
+ public function getClientSupportsJavaScript()
+ {
+ // todo
+ return true;
+ }
- $this->onPreLoad(null);
- $this->loadRecursive(null);
- if($this->getIsPostBack())
- {
- $this->processPostData($this->_restPostData,false);
- $this->raiseChangedEvents();
- $this->raisePostBackEvent($this->_postData);
- }
- $this->onLoadComplete(null);
- $this->preRenderRecursive();
- $this->onPreRenderComplete(null);
+ public function getClientOnSubmitEvent()
+ {
+ // todo
+ if($this->getClientScript()->getHasSubmitStatements())
+ return 'javascript:return WebForm_OnSubmit();';
+ else
+ return '';
+ }
- $this->savePageState();
- $this->onSaveStateComplete(null);
+ protected function initializeCulture()
+ {
+ }
- $this->renderControl($writer);
- $this->unloadRecursive();
+ /**
+ * @internal
+ */
+ public function loadScrollPosition()
+ {
+ if($this->_previousPagePath==='' && $this->_requestValueCollection)
+ {
+ if(isset($_REQUEST['__SCROLLPOSITIONX']))
+ $this->_scrollPositionX=(integer)$_REQUEST['__SCROLLPOSITIONX'];
+ if(isset($_REQUEST['__SCROLLPOSITIONY']))
+ $this->_scrollPositionX=(integer)$_REQUEST['__SCROLLPOSITIONY'];
+ }
}
+
+ final public function registerAsyncTask()
+ {
+ }
}
?>
\ No newline at end of file |