diff options
23 files changed, 477 insertions, 62 deletions
diff --git a/.gitattributes b/.gitattributes index cb22cb99..6875bd34 100644 --- a/.gitattributes +++ b/.gitattributes @@ -761,7 +761,15 @@ demos/quickstart/protected/index/ZendSearch.php -text  demos/quickstart/protected/index/quickstart/_0.cfs -text  demos/quickstart/protected/index/quickstart/deletable -text  demos/quickstart/protected/index/quickstart/segments -text +demos/quickstart/protected/pages/ActiveControls/ActiveButton.page -text +demos/quickstart/protected/pages/ActiveControls/ActiveCheckBox.page -text  demos/quickstart/protected/pages/ActiveControls/Home.page -text +demos/quickstart/protected/pages/ActiveControls/Introduction.page -text +demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.page -text +demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.php -text +demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.page -text +demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.php -text +demos/quickstart/protected/pages/ActiveControls/Samples/config.xml -text  demos/quickstart/protected/pages/ActiveControls/TActiveButtonClass.png -text  demos/quickstart/protected/pages/ActiveControls/TActiveButtonClass.vsd -text  demos/quickstart/protected/pages/ActiveControls/postback-callback.png -text @@ -1946,6 +1954,8 @@ tests/FunctionalTests/features/protected/pages/I18N/config.xml -text  tests/FunctionalTests/features/protected/pages/RatingList.page -text  tests/FunctionalTests/features/protected/pages/ValidatorEffects.page -text  tests/FunctionalTests/index.php -text +tests/FunctionalTests/quickstart/ActiveControls/ActiveButtonTestCase.php -text +tests/FunctionalTests/quickstart/ActiveControls/ActiveCheckBoxTestCase.php -text  tests/FunctionalTests/quickstart/Advanced/I18N.php -text  tests/FunctionalTests/quickstart/Controls/BulletedListTestCase.php -text  tests/FunctionalTests/quickstart/Controls/ButtonTestCase.php -text diff --git a/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page b/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page new file mode 100644 index 00000000..7bbe05ea --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/ActiveButton.page @@ -0,0 +1,95 @@ +<com:TContent ID="body">
 +<!-- $Id$ -->
 +<h1>TActiveButton</h1>
 +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveButton" />
 +
 +<p><tt>TActiveButton</tt> is the active control counter part to
 +<a href="?page=Controls.Button">TButton</a>.
 +When a <tt>TActiveButton</tt> is clicked, rather than a normal post back request a
 +callback request is initiated. The <tt>OnCallback</tt> event is raised
 +during a callback request and it is raise <strong>after</strong>
 +the <tt>OnClick</tt> event.
 +</p>
 +
 +<p>When the <tt>ActiveControl.EnableUpdate</tt> property is true,
 +changing the <tt>Text</tt> property during a callback request will update
 +the button's caption on the client-side.</p>
 +
 +<p>Since the <tt>OnCallback</tt> event is raised only during a callback request,
 +the <tt>OnCallback</tt> event handler can be used to handle logic specifically
 +related to callback requests. The <tt>OnClick</tt> event handler is raised
 +when ever the button is clicked, even if javascript is disabled.</p>
 +
 +<p>The following example the use of both the <tt>OnClick</tt> and <tt>OnCallback</tt>
 +events of an <tt>TActiveButton</tt>.</p>
 +
 +<com:RunBar PagePath="ActiveControls.Samples.TActiveButton.Home" />
 +
 +<h2>TActiveButton Class Diagram</h2>
 +<p>The class diagram for <tt>TActiveButton</tt> is illustrated in the figure below.
 +Most active control that can perform callback request have a similar structure.
 +</p>
 +
 +<img src=<%~ TActiveButtonClass.png %> class="figure"
 +	alt="TActiveButton class diagram" title="TActiveButton class diagram" />
 +
 +<p><tt>TActiveButton</tt> is an extension of <a href="?page=Controls.Button">TButton</a>
 +and implements two additional interfaces <tt>ICallbackEventHandler</tt> and
 +<tt>IActiveControl</tt>. The <tt>TActiveButton</tt> contains an instance of
 +<a href="?page=ActiveControls.BaseActiveControl">TBaseActiveCallbackControl</a>
 +available through the <tt>ActiveControl</tt> property of <tt>TActiveButton</tt>.
 +The following example set the callback parameter of the <tt>TActiveButton</tt> when
 +a callback request is dispatched.
 +</p>
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TActiveButton
 +	Text="Click Me"
 +	OnCallback="button_callback"
 +	ActiveControl.CallbackParameter="value" />
 +</com:TTextHighlighter>
 +<p>In the <tt>OnCallback</tt> event handler method, the <tt>CallbackParameter</tt>
 +is available in the <tt>$param</tt> object.</p>
 +<com:TTextHighlighter Language="php" CssClass="source">
 +public function button_callback($sender, $param)
 +{
 +	echo $param->CallbackParameter; //outputs "value"
 +}
 +</com:TTextHighlighter>
 +
 +<h2>Adding Client Side Behaviour</h2>
 +
 +<p>With in the <tt>ActiveControl</tt> property is an instance of
 +<a href="?page=ActiveControls.CallbackClientSide">TCallbackClientSide</a> available
 +as a property <tt>ClientSide</tt> of <tt>ActiveControl</tt> or similarly
 +as a sub-property <tt>ActiveControl.ClientSide</tt> of <tt>TActiveButton</tt>.
 +The <tt>ClientSide</tt> property contains sub-properties, such as <tt>RequestTimeOut</tt>,
 +and client-side javascript event handler, such as <tt>OnLoading</tt>,
 +that are used by the client-side when making a callback request.
 +The following example demonstrates the toggling of a "loading" indicator
 +when the client-side is making a callback request.
 +</p>
 +
 +<com:TTextHighlighter Language="prado" CssClass="source">
 +<com:TClientSide PradoScripts="effects" />
 +<span id="callback_status">Loading...</span>
 +
 +<com:TActiveButton
 +	Text="Click Me"
 +	OnCallback="button_callback"
 +	ActiveControl.CallbackParameter="value" >
 +	<prop:ActiveControl.ClientSide
 +		OnLoading="Element.show('callback_status')"
 +		OnComplete="Element.hide('callback_status')" />
 +</com:TActiveButton>
 +</com:TTextHighlighter>
 +
 +<p>The example loads the "effects" javascript library using the
 +<a href="?page=Controls.ClientScript">TClientScript</a> component.
 +The <tt>ActiveControl.ClientSide.OnLoading</tt> property value contains
 +javascript statement that uses the "effects" library to show the "Loading..."
 +span tag. Similarly, <tt>ActiveControl.ClientSide.OnComplete</tt> property
 +value contains the javascript statement that hides the "Loading..." span tag.
 +See <a href="?page=ActiveControls.CallbackClientSide">TCallbackClientSide</a> for
 +further details on client-side property details.
 +</p>
 +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/ActiveCheckBox.page b/demos/quickstart/protected/pages/ActiveControls/ActiveCheckBox.page new file mode 100644 index 00000000..d66c48f5 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/ActiveCheckBox.page @@ -0,0 +1,23 @@ +<com:TContent ID="body">
 +<!-- $Id$ -->
 +<h1>TActiveCheckBox</h1>
 +<com:DocLink ClassPath="System.Web.UI.ActiveControls.TActiveCheckBox" />
 +
 +<p>
 +<tt>TActiveCheckBox</tt> is the active control counter part to
 +<a href="?page=Controls.CheckBox">TCheckbox</a>. The <tt>AutoPostBack</tt>
 + property of <tt>TActiveCheckBox</tt> is set to true by default.
 + Thus, when the checkbox is clicked the
 + <tt>OnCallback</tt> event is raise after the <tt>OnCheckedChanged</tt> event.
 + </p>
 +
 + <p>
 + The <tt>Text</tt> and <tt>Checked</tt> properties of <tt>TActiveCheckBox</tt>
 + can be changed during a callback request. The <tt>TextAlign</tt> property
 + of <tt>TActiveCheckBox</tt> <strong>can not</strong> be changed during
 + a callback request.
 +</p>
 +
 +<com:RunBar PagePath="ActiveControls.Samples.TActiveCheckBox.Home" />
 +
 +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Home.page b/demos/quickstart/protected/pages/ActiveControls/Home.page index e3f13640..9b9e3067 100644 --- a/demos/quickstart/protected/pages/ActiveControls/Home.page +++ b/demos/quickstart/protected/pages/ActiveControls/Home.page @@ -1,7 +1,22 @@  <com:TContent ID="body" >
 -<h1>ActiveControls (AJAX)</h1>
 -<p>Active Controls allows the browser to communicate with server
 -without refreshing the current page.
 +<!-- $Id$ -->
 +<h1>Active Controls (AJAX enabled Controls)</h1>
 +<p>See the <a href="?page=ActiveControls.Introduction">Introduction</a>
 +for a quick overview of the concept behind active controls (AJAX enabled controls).
 +Most active controls have a property of
 +<a href="?page=ActiveControls.BaseActiveControl">ActiveControl</a> and
 +a sub-property <a href="?page=ActiveControls.CallbackClientSide">ActiveControl.ClientSide</a>
 +that provides many properties to customize the controls. The
 +<a href="?page=TCallbackClientScript">CallbackClient</a> property of the
 +<tt>TPage</tt> class provides many methods to update and alter the client-side content
 +during a callback request. Active controls is reliant on a collection
 +of <a href="?page=ActiveControl.ClientSideJavascript">javascript classes</a>.
 +</p>
 +
 +<p>For a quick demo of active controls, try the <a href="?page=ActiveControls.ActiveButton">
 +TActiveButton</a> control.</p>
 +
 +<p>* the tutorial for this control is not completed yet.</p>
  <h2>Standard Active Controls</h2>
  <ul>
 @@ -17,60 +32,60 @@ without refreshing the current page.    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveCustomValidator">TActiveCustomValidator</a>
 +  * <a href="?page=ActiveControls.ActiveCustomValidator">TActiveCustomValidator</a>
    validates a particular control using a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveHyperLink">TActiveHyperLink</a>
 +  * <a href="?page=ActiveControls.ActiveHyperLink">TActiveHyperLink</a>
    represents a hyperlink on a Web page.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveImage">TActiveImage</a>
 +  * <a href="?page=ActiveControls.ActiveImage">TActiveImage</a>
    represents an image on a Web page.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveImageButton">TActiveImageButton</a>
 +  * <a href="?page=ActiveControls.ActiveImageButton">TActiveImageButton</a>
    represents a click button that has an image as the background.
    It is can be used to trigger a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveLabel">TActiveLabel</a>
 +  * <a href="?page=ActiveControls.ActiveLabel">TActiveLabel</a>
  	represents a label on a Web page.
  	The label can be customized via various CSS attributes.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveLinkButton">TActiveLinkButton</a>
 +  * <a href="?page=ActiveControls.ActiveLinkButton">TActiveLinkButton</a>
  	represents a hyperlink that can perform a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActivePanel">TActivePanel</a>
 +  * <a href="?page=ActiveControls.ActivePanel">TActivePanel</a>
    represents a container for other controls on a Web page. In HTML,
    it is displayed as a <div> element. The panel's contents
    can be replaced during a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveRadioButton">TActiveRadioButton</a>
 +  * <a href="?page=ActiveControls.ActiveRadioButton">TActiveRadioButton</a>
    represents a radiobutton on a Web page.
    It is mainly used in a group from which users make a choice. It can
    be used to perform a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveTextBox">TActiveTextBox</a>
 +  * <a href="?page=ActiveControls.ActiveTextBox">TActiveTextBox</a>
  	represents a text input field on a Web page.
  	It can collect single-line, multi-line or password text input from users.
  	It can be used to perform a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.CallbackOptions">TCallbackOptions</a>
 +  * <a href="?page=ActiveControls.CallbackOptions">TCallbackOptions</a>
    	callback options such as <tt>OnLoading</tt/> client-side event handlers.
    </li>
 @@ -79,26 +94,26 @@ without refreshing the current page.  <h2>Active List Controls</h2>
  <ul>
    <li>
 -  <a href="?page=ActiveControls.ActiveCheckBoxList">TActiveCheckBoxList</a>
 +  * <a href="?page=ActiveControls.ActiveCheckBoxList">TActiveCheckBoxList</a>
    displays a list of checkboxes on a Web page and each checkbox
    	can trigger a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveDropDownList">TActiveDropDownList</a>
 +  * <a href="?page=ActiveControls.ActiveDropDownList">TActiveDropDownList</a>
    displays a dropdown list box that allows users to select a
    single option from a few prespecified ones. It can be used
    to perform a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveListBox">TActiveListBox</a>
 +  * <a href="?page=ActiveControls.ActiveListBox">TActiveListBox</a>
    displays a list box that allows single or multiple selection. It can be used
    to perform a callback request.
    </li>
    <li>
 -  <a href="?page=ActiveControls.ActiveRadioButtonList">TActiveRadioButtonList</a>
 +  * <a href="?page=ActiveControls.ActiveRadioButtonList">TActiveRadioButtonList</a>
    is similar to TActiveCheckBoxList in every aspect except that each
    TActiveRadioButtonList displays a group of radiobuttons. Each radio button
    can perform a callback request.
 @@ -110,32 +125,32 @@ without refreshing the current page.  <ul>
  	<li>
 -	<a href="?page=ActiveControls.AutoComplete">TAutoComplete</a>
 +	* <a href="?page=ActiveControls.AutoComplete">TAutoComplete</a>
  	extends TActiveTextBox to offer text completion suggestions.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.Callback">TCallback</a>
 +	* <a href="?page=ActiveControls.Callback">TCallback</a>
  	a generic control that can perform callback requests.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.EventTriggeredCallback">TEventTriggeredCallback</a>
 +	* <a href="?page=ActiveControls.EventTriggeredCallback">TEventTriggeredCallback</a>
  	triggers a callback request based on HTML DOM events.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.InPlaceTextBox">TInPlaceTextBox</a>
 +	* <a href="?page=ActiveControls.InPlaceTextBox">TInPlaceTextBox</a>
  	represents a label that can be edited by clicked.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.TimeTriggeredCallback">TTimeTriggeredCallback</a>
 +	* <a href="?page=ActiveControls.TimeTriggeredCallback">TTimeTriggeredCallback</a>
  	triggers a callback request based on time elapsed.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.ValueTriggeredCallback">TValueTriggeredCallback</a>
 +	* <a href="?page=ActiveControls.ValueTriggeredCallback">TValueTriggeredCallback</a>
  	monitors (using a timer) an attribute of an HTML element and triggers a callback request
  	when the attribute value changes.
  	</li>
 @@ -291,48 +306,43 @@ without refreshing the current page.  realize the active controls.</p>
  <ul>
  	<li>
 -	<a href="?page=ActiveControls.ActiveControlAdapter">TActiveControlAdapter</a>
 +	* <a href="?page=ActiveControls.ActiveControlAdapter">TActiveControlAdapter</a>
  	tracks the viewstate values of the control and update differences of the client-side HTML
  	element attributes.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.ActiveListControlAdapter">TActiveListControlAdapter</a>
 +	* <a href="?page=ActiveControls.ActiveListControlAdapter">TActiveListControlAdapter</a>
  	allows the adapted list controls to change the selections on the client-side during
  	a callback request.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.ActivePageAdapter">TActivePageAdapter</a>
 +	* <a href="?page=ActiveControls.ActivePageAdapter">TActivePageAdapter</a>
  	process the page life-cycle for callback requests.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.BaseActiveControl">TBaseActiveControl</a>
 +	* <a href="?page=ActiveControls.BaseActiveControl">TBaseActiveControl</a>
  	common active control methods and options.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.CallbackClientScript">TCallbackClientScript</a>
 +	* <a href="?page=ActiveControls.CallbackClientScript">TCallbackClientScript</a>
  	methods to manipulate the client-side HTML elements, also includes methods
  	to invoke javascript Effects on HTML elements.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.CallbackClientSide">TCallbackClientSide</a>
 +	* <a href="?page=ActiveControls.CallbackClientSide">TCallbackClientSide</a>
  	common client-side callback request options, and client-side event handlers.
  	</li>
  	<li>
 -	<a href="?page=ActiveControls.CallbackResponseAdapter">TCallbackResponseAdapter</a>
 +	* <a href="?page=ActiveControls.CallbackResponseAdapter">TCallbackResponseAdapter</a>
  	HTTP response for callback requests.
  	</li>
  </ul>
 -<p>
 -<img src="<%~ postback-callback.png %>" class="figure" />
 -</p>
 -<p>
 -<img src="<%~ TActiveButtonClass.png %>" class="figure"/>
 -</p>
 +
  </com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Introduction.page b/demos/quickstart/protected/pages/ActiveControls/Introduction.page new file mode 100644 index 00000000..08edac33 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Introduction.page @@ -0,0 +1,8 @@ +<com:TContent ID="body">
 +<!-- $Id$ -->
 +<h1>Overview of Active Controls</h1>
 +
 +TODO:
 +
 +<img src=<%~ postback-callback.png %> class="figure" />
 +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.page b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.page new file mode 100644 index 00000000..259541a2 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.page @@ -0,0 +1,49 @@ +<com:TContent ID="body">
 +<!-- $Id$ -->
 +<h1>TActiveButton Samples (AJAX)</h1>
 +
 +<table class="sampletable">
 +
 +<tr><td class="samplenote">
 +A click button with <tt>OnClick</tt> event handler:
 +</td><td class="sampleaction">
 +<com:TActiveButton
 +	Text="click me"
 +	OnClick="buttonClicked" />
 +</td></tr>
 +
 +<tr><td class="samplenote">
 +A command button with <tt>OnCallback</tt>:
 +</td><td class="sampleaction">
 +<com:TActiveButton
 +	Text="click me"
 +	OnCommand="buttonClicked"
 +	CommandName="test"
 +	ActiveControl.CallbackParameter="value"
 +	CommandParameter="value"
 +	OnCallback="buttonCallback"
 +	/>
 +</td></tr>
 +
 +<tr><td class="samplenote">
 +A button causing validation with <tt>OnCallback</tt>:
 +</td><td class="sampleaction">
 +<com:TTextBox ID="TextBox" />
 +<com:TRequiredFieldValidator
 +	ControlToValidate="TextBox"
 +	Display="Dynamic"
 +	ErrorMessage="input required in the textbox"
 +	ValidationGroup="Group"
 +	/>
 +<com:TActiveButton
 +	Text="submit"
 +	OnClick="buttonClicked"
 +	OnCallback="buttonCallback"
 +	ValidationGroup="Group" />
 +</td></tr>
 +
 +</table>
 +
 +<com:TJavascriptLogger />
 +
 +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.php b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.php new file mode 100644 index 00000000..4a4e23ca --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveButton/Home.php @@ -0,0 +1,20 @@ +<?php
 +
 +// $Id$
 +class Home extends TPage
 +{
 +	public function buttonClicked($sender, $param)
 +	{
 +		if($param instanceof TCommandEventParameter)
 +			$sender->Text="Name: {$param->CommandName}, Param: {$param->CommandParameter}";
 +		else
 +			$sender->Text="I'm clicked";
 +	}
 +
 +	public function buttonCallback($sender, $param)
 +	{
 +		$sender->Text .= ' using callback';
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.page b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.page new file mode 100644 index 00000000..cbe58134 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.page @@ -0,0 +1,77 @@ +<com:TContent ID="body">
 +<!-- $Id$ -->
 +<h1>TActiveCheckBox Samples (AJAX)</h1>
 +
 +<table class="sampletable">
 +
 +<tr><td class="samplenote">
 +An active checkbox with <tt>OnCallback</tt>:
 +</td><td class="sampleaction">
 +<com:TActiveCheckBox
 +	AutoPostBack="true"
 +	Text="click me"
 +	OnCheckedChanged="checkboxClicked"
 +	OnCallback="checkboxCallback"
 +	/>
 +</td></tr>
 +
 +<tr><td class="samplenote">
 +A checkbox causing validation on a textbox:
 +</td><td class="sampleaction">
 +<com:TTextBox ID="TextBox" />
 +<com:TRequiredFieldValidator
 +	ControlToValidate="TextBox"
 +	Display="Dynamic"
 +	ErrorMessage="input required in the textbox"
 +	ValidationGroup="Group"
 +	/>
 +<com:TActiveCheckBox
 +	Text="submit"
 +	ValidationGroup="Group"
 +	OnCheckedChanged="checkboxClicked"
 +	OnCallback="checkboxCallback"
 +	/>
 +</td></tr>
 +
 +<tr><td class="samplenote">
 +A checkbox validated by a required field validator:
 +</td><td class="sampleaction">
 +<com:TActiveCheckBox
 +	ID="CheckBox"
 +	Text="Consent"
 +	AutoPostBack="false"
 +	OnCheckedChanged="checkboxClicked"
 +	ValidationGroup="Group2"
 +	/>
 +<com:TActiveButton Text="Submit" ValidationGroup="Group2" />
 +<com:TRequiredFieldValidator
 +	ControlToValidate="CheckBox"
 +	Display="Dynamic"
 +	ErrorMessage="You must consent."
 +	ValidationGroup="Group2"
 +	/>
 +</td></tr>
 +
 +<tr><td class="samplenote">
 +A checkbox validated by a required field validator:
 +</td><td class="sampleaction">
 +<com:TActiveCheckBox
 +	ID="CheckBox2"
 +	Text="Agree"
 +	Checked="true"
 +	OnCheckedChanged="checkboxClicked"
 +	ValidationGroup="Group3"
 +	/>
 +<com:TRequiredFieldValidator
 +	ControlToValidate="CheckBox2"
 +	Display="Dynamic"
 +	ErrorMessage="You must agree."
 +	ValidationGroup="Group3"
 +	/>
 +</td></tr>
 +
 +</table>
 +
 +<com:TJavascriptLogger />
 +
 +</com:TContent>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.php b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.php new file mode 100644 index 00000000..f0543695 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Samples/TActiveCheckBox/Home.php @@ -0,0 +1,16 @@ +<?php
 +// $Id$
 +class Home extends TPage
 +{
 +	public function checkboxClicked($sender,$param)
 +	{
 +		$sender->Text= $sender->ClientID . " clicked";
 +	}
 +
 +	public function checkboxCallback($sender, $param)
 +	{
 +		$sender->Text .= ' using callback';
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/demos/quickstart/protected/pages/ActiveControls/Samples/config.xml b/demos/quickstart/protected/pages/ActiveControls/Samples/config.xml new file mode 100644 index 00000000..d30f3ca6 --- /dev/null +++ b/demos/quickstart/protected/pages/ActiveControls/Samples/config.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="utf-8"?>
 +<!-- $Id$ -->
 +<configuration>
 +  <paths>
 +    <using namespace="System.Web.UI.ActiveControls.*" />
 +  </paths>
 +  <pages MasterClass="SampleLayout" />
 +</configuration>
\ No newline at end of file diff --git a/framework/Web/Javascripts/js/compressed/ajax.js b/framework/Web/Javascripts/js/compressed/ajax.js index 69652e6a..88a0a4b0 100644 --- a/framework/Web/Javascripts/js/compressed/ajax.js +++ b/framework/Web/Javascripts/js/compressed/ajax.js @@ -77,8 +77,8 @@ Ajax.Responders.register({onComplete:function(request)  Prado.CallbackRequest.abortRequestInProgress();}});Event.OnLoad(function()  {if(typeof Logger!="undefined")  Ajax.Responders.register(Prado.CallbackRequest.Exception);});Prado.CallbackRequest.prototype={url:window.location.href,options:{},id:null,request:null,initialize:function(id,options) -{this.id=id;this.options=Object.extend({RequestTimeOut:30000,EnablePageStateUpdate:true,HasPriority:true,CausesValidation:true,ValidationGroup:null,PostInputs:true},options||{});},setParameter:function(value) -{this.options['params']=value;},getParameter:function() +{this.id=id;this.options=Object.extend({RequestTimeOut:30000,EnablePageStateUpdate:true,HasPriority:true,CausesValidation:true,ValidationGroup:null,PostInputs:true},options||{});},setCallbackParameter:function(value) +{this.options['params']=value;},getCallbackParameter:function()  {return this.options['params'];},setRequestTimeOut:function(timeout)  {this.options['RequestTimeOut']=timeout;},getRequestTimeOut:function()  {return this.options['RequestTimeOut'];},setCausesValidation:function(validate) @@ -203,7 +203,8 @@ this.leaveEditMode();Event.stopObserving(this.element,'click',this.onclickListen  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)  {request=new Prado.CallbackRequest(options.EventTarget,options);request.dispatch();Event.stop(event);}});Prado.WebUI.TActiveButton=Class.extend(Prado.WebUI.CallbackControl);Prado.WebUI.TActiveLinkButton=Class.extend(Prado.WebUI.CallbackControl);Prado.WebUI.TActiveImageButton=Class.extend(Prado.WebUI.TImageButton,{onPostBack:function(event,options)  {this.addXYInput(event,options);request=new Prado.CallbackRequest(options.EventTarget,options);request.dispatch();Event.stop(event);}});Prado.WebUI.TActiveCheckBox=Class.extend(Prado.WebUI.CallbackControl,{onPostBack:function(event,options) -{request=new Prado.CallbackRequest(options.EventTarget,options);request.dispatch();}});Prado.WebUI.TActiveRadioButton=Class.extend(Prado.WebUI.TActiveCheckBox);Prado.WebUI.TActiveTextBox=Class.extend(Prado.WebUI.TTextBox,{onInit:function(options) +{request=new Prado.CallbackRequest(options.EventTarget,options);if(request.dispatch()==false) +Event.stop(event);}});Prado.WebUI.TActiveRadioButton=Class.extend(Prado.WebUI.TActiveCheckBox);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)  {request=new Prado.CallbackRequest(options.EventTarget,options);request.dispatch();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) @@ -248,7 +249,7 @@ Event.stop(event);}});Prado.WebUI.TValueTriggeredCallback=Base.extend({count:1,o  else  this.count=this.count+this.options.Decay;if(this.observing)  this.time=setTimeout(this.checkChanges.bind(this),parseInt(this.options.Interval*1000*this.count));}},doCallback:function(oldValue,newValue) -{request=new Prado.CallbackRequest(this.options.EventTarget,this.options);param={'OldValue':oldValue,'NewValue':newValue};request.setParameter(param);request.dispatch();}},{timers:{},register:function(timer) +{request=new Prado.CallbackRequest(this.options.EventTarget,this.options);param={'OldValue':oldValue,'NewValue':newValue};request.setCallbackParameter(param);request.dispatch();}},{timers:{},register:function(timer)  {this.timers[timer.options.ID]=timer;},stop:function(id)  {if(this.timers[id])  this.timers[id].stopObserving();}});Prado.WebUI.TInPlaceTextBox=Base.extend({isSaving:false,isEditing:false,editField:null,constructor:function(options) diff --git a/framework/Web/Javascripts/js/debug/ajax.js b/framework/Web/Javascripts/js/debug/ajax.js index 7d99cb57..9835e7e8 100644 --- a/framework/Web/Javascripts/js/debug/ajax.js +++ b/framework/Web/Javascripts/js/debug/ajax.js @@ -670,7 +670,7 @@ Prado.CallbackRequest.prototype =  	 * Sets the request parameter
  	 * @param {Object} parameter value
  	 */
 -	setParameter : function(value)
 +	setCallbackParameter : function(value)
  	{
  		this.options['params'] = value;
  	},
 @@ -678,7 +678,7 @@ Prado.CallbackRequest.prototype =  	/**
  	 * @return {Object} request paramater value.
  	 */
 -	getParameter : function()
 +	getCallbackParameter : function()
  	{
  		return this.options['params'];
  	},
 @@ -2026,7 +2026,8 @@ Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl,  	onPostBack : function(event, options)
  	{
  		request = new Prado.CallbackRequest(options.EventTarget, options);
 -		request.dispatch();
 +		if(request.dispatch()==false)
 +			Event.stop(event);
  	}
  });
 @@ -2316,7 +2317,7 @@ Prado.WebUI.TValueTriggeredCallback = Base.extend(  	{
  		request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
  		param = {'OldValue' : oldValue, 'NewValue' : newValue};
 -		request.setParameter(param);
 +		request.setCallbackParameter(param);
  		request.dispatch();
  	}
  },
 diff --git a/framework/Web/Javascripts/prado/activecontrols3.js b/framework/Web/Javascripts/prado/activecontrols3.js index 80c5fc67..e608e71e 100644 --- a/framework/Web/Javascripts/prado/activecontrols3.js +++ b/framework/Web/Javascripts/prado/activecontrols3.js @@ -38,7 +38,8 @@ Prado.WebUI.TActiveCheckBox = Class.extend(Prado.WebUI.CallbackControl,  	onPostBack : function(event, options)
  	{
  		request = new Prado.CallbackRequest(options.EventTarget, options);
 -		request.dispatch();
 +		if(request.dispatch()==false)
 +			Event.stop(event);
  	}
  });
 @@ -328,7 +329,7 @@ Prado.WebUI.TValueTriggeredCallback = Base.extend(  	{
  		request = new Prado.CallbackRequest(this.options.EventTarget, this.options);
  		param = {'OldValue' : oldValue, 'NewValue' : newValue};
 -		request.setParameter(param);
 +		request.setCallbackParameter(param);
  		request.dispatch();
  	}
  },
 diff --git a/framework/Web/Javascripts/prado/ajax3.js b/framework/Web/Javascripts/prado/ajax3.js index a11fe1aa..397382f3 100644 --- a/framework/Web/Javascripts/prado/ajax3.js +++ b/framework/Web/Javascripts/prado/ajax3.js @@ -382,7 +382,7 @@ Prado.CallbackRequest.prototype =  	 * Sets the request parameter
  	 * @param {Object} parameter value
  	 */
 -	setParameter : function(value)
 +	setCallbackParameter : function(value)
  	{
  		this.options['params'] = value;
  	},
 @@ -390,7 +390,7 @@ Prado.CallbackRequest.prototype =  	/**
  	 * @return {Object} request paramater value.
  	 */
 -	getParameter : function()
 +	getCallbackParameter : function()
  	{
  		return this.options['params'];
  	},
 diff --git a/framework/Web/UI/ActiveControls/TActiveCheckBox.php b/framework/Web/UI/ActiveControls/TActiveCheckBox.php index d6de63e3..7cf5c005 100644 --- a/framework/Web/UI/ActiveControls/TActiveCheckBox.php +++ b/framework/Web/UI/ActiveControls/TActiveCheckBox.php @@ -148,7 +148,7 @@ class TActiveCheckBox extends TCheckBox implements ICallbackEventHandler, IActiv  	protected function getDefaultLabelID()  	{  		if($attributes=$this->getViewState('LabelAttributes',null)) -			return $this->getLabelAttributes()->itemAt('id'); +			return TCheckBox::getLabelAttributes()->itemAt('id');  		else  			return $this->getClientID().'_label';  	} diff --git a/framework/Web/UI/ActiveControls/TActiveCustomValidator.php b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php index 7c4ab16b..80e594e2 100644 --- a/framework/Web/UI/ActiveControls/TActiveCustomValidator.php +++ b/framework/Web/UI/ActiveControls/TActiveCustomValidator.php @@ -85,7 +85,7 @@ class TActiveCustomValidator extends TCustomValidator   	public function raiseCallbackEvent($param)
  	{
  		$this->_isCallback = true;
 -		$result = $this->onServerValidate($param->getParameter());
 +		$result = $this->onServerValidate($param->getCallbackParameter());
  		$param->setData($result);
  		$this->onCallback($param);
  	}
 diff --git a/framework/Web/UI/ActiveControls/TActivePageAdapter.php b/framework/Web/UI/ActiveControls/TActivePageAdapter.php index c3ca8947..9274ddb7 100644 --- a/framework/Web/UI/ActiveControls/TActivePageAdapter.php +++ b/framework/Web/UI/ActiveControls/TActivePageAdapter.php @@ -242,7 +242,7 @@ class TActivePageAdapter extends TControlAdapter   * TCallbackEventParameter class.
   *
   * The TCallbackEventParameter provides the parameter passed during the callback
 - * requestion in the {@link getParameter Parameter} property. The
 + * requestion in the {@link getCallbackParameter CallbackParameter} property. The
   * callback response content (e.g. new HTML content) must be rendered
   * using an THtmlWriter obtained from the {@link getNewWriter NewWriter}
   * property, which returns a <b>NEW</b> instance of TCallbackResponseWriter.
 @@ -253,7 +253,7 @@ class TActivePageAdapter extends TControlAdapter   * use the same writer instance for the panels to be rendered.
   *
   * The response data (i.e., passing results back to the client-side
 - * callback handler function) can be set using {@link setData Data} property.
 + * callback handler function) can be set using {@link setResponseData ResponseData} property.
   *
   * @author Wei Zhuo <weizhuo[at]gamil[dot]com>
   * @version $Id$
 @@ -291,7 +291,7 @@ class TCallbackEventParameter extends TEventParameter  	/**
  	 * @return mixed callback request parameter.
  	 */
 -	public function getParameter()
 +	public function getCallbackParameter()
  	{
  		return $this->_parameter;
  	}
 @@ -299,7 +299,7 @@ class TCallbackEventParameter extends TEventParameter  	/**
  	 * @param mixed callback response data.
  	 */
 -	public function setData($value)
 +	public function setResponesData($value)
  	{
  		$this->_response->getAdapter()->setResponseData($value);
  	}
 @@ -307,7 +307,7 @@ class TCallbackEventParameter extends TEventParameter  	/**
  	 * @return mixed callback response data.
  	 */
 -	public function getData()
 +	public function getResponesData()
  	{
  		return $this->_response->getAdapter()->getResponseData();
  	}
 diff --git a/framework/Web/UI/ActiveControls/TAutoComplete.php b/framework/Web/UI/ActiveControls/TAutoComplete.php index dadb723a..080bb410 100644 --- a/framework/Web/UI/ActiveControls/TAutoComplete.php +++ b/framework/Web/UI/ActiveControls/TAutoComplete.php @@ -29,7 +29,7 @@ Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');   *
   * 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}
 + * {@link TCallbackEventParameter::getCallbackParameter TCallbackEventParameter::CallbackParameter}
   * 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
 @@ -41,7 +41,7 @@ Prado::using('System.Web.UI.ActiveControls.TActiveTextBox');   * <code>
   * function autocomplete_suggestion($sender, $param)
   * {
 - *   $token = $param->getParameter(); //the partial word to match
 + *   $token = $param->getCallbackParameter(); //the partial word to match
   *   $sender->setDataSource($this->getSuggestionsFor($token)); //set suggestions
   *   $sender->dataBind();
   *   $sender->render($param->getNewWriter()); //sends suggestion back to browser.
 @@ -138,7 +138,7 @@ class TAutoComplete extends TActiveTextBox implements INamingContainer  	 */
   	public function raiseCallbackEvent($param)
  	{
 -		$token = $param->getParameter();
 +		$token = $param->getCallbackParameter();
  		if(is_array($token) && count($token) == 2 && $token[1] === '__TAutoComplete_onSuggest__')
  		{
  			$parameter = new TCallbackEventParameter($this->getResponse(), $token[0]);
 diff --git a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php index 5bfd9456..236e43d5 100644 --- a/framework/Web/UI/ActiveControls/TInPlaceTextBox.php +++ b/framework/Web/UI/ActiveControls/TInPlaceTextBox.php @@ -162,7 +162,7 @@ class TInPlaceTextBox extends TActiveTextBox  	 */
  	public function onCallback($param)
  	{
 -		$action = $param->getParameter();
 +		$action = $param->getCallbackParameter();
  		if(is_array($action) && $action[0] === '__InlineEditor_loadExternalText__')
  		{
  			$parameter = new TCallbackEventParameter($this->getResponse(), $action[1]);
 diff --git a/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php index 21f61abe..997e9bdd 100644 --- a/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php +++ b/tests/FunctionalTests/active-controls/protected/pages/AutoCompleteTest.php @@ -7,10 +7,10 @@ class AutoCompleteTest extends TPage  {  	public function suggestCountries($sender, $param)  	{ -		$sender->setDataSource($this->matchCountries($param->getParameter())); +		$sender->setDataSource($this->matchCountries($param->getCallbackParameter()));  		$sender->dataBind();  		$sender->render($param->getNewWriter()); -		$this->label1->Text = "suggestion for ".$param->getParameter(); +		$this->label1->Text = "suggestion for ".$param->getCallbackParameter();  	}  	public function callback_requested($sender, $param) diff --git a/tests/FunctionalTests/active-controls/protected/pages/ValueTriggerCallbackTest.php b/tests/FunctionalTests/active-controls/protected/pages/ValueTriggerCallbackTest.php index c3c44252..a8f83187 100644 --- a/tests/FunctionalTests/active-controls/protected/pages/ValueTriggerCallbackTest.php +++ b/tests/FunctionalTests/active-controls/protected/pages/ValueTriggerCallbackTest.php @@ -4,7 +4,7 @@ class ValueTriggerCallbackTest extends TPage  {
  	function text1_changed($sender, $param)
  	{
 -		$values = $param->getParameter();
 +		$values = $param->getCallbackParameter();
  		$this->label1->Text = "Old  = ".$values->OldValue." : New Value = ".$values->NewValue;
  	}
  }
 diff --git a/tests/FunctionalTests/quickstart/ActiveControls/ActiveButtonTestCase.php b/tests/FunctionalTests/quickstart/ActiveControls/ActiveButtonTestCase.php new file mode 100644 index 00000000..8859d729 --- /dev/null +++ b/tests/FunctionalTests/quickstart/ActiveControls/ActiveButtonTestCase.php @@ -0,0 +1,38 @@ +<?php
 +//$Id$
 +class ActiveButtonTestCase extends SeleniumTestCase
 +{
 +	function test ()
 +	{
 +		$this->open("../../demos/quickstart/index.php?page=ActiveControls.Samples.TActiveButton.Home&notheme=true");
 +
 +		$this->verifyTitle("PRADO QuickStart Sample", "");
 +
 +		$this->assertTextPresent('TActiveButton Samples (AJAX)');
 +
 +		// a click button
 +		$this->verifyElementNotPresent("//input[@type='submit' and @value=\"I'm clicked\"]");
 +		$this->click("//input[@type='submit' and @value='click me']", "");
 +		$this->pause(800);
 +		$this->verifyElementPresent("//input[@type='submit' and @value=\"I'm clicked\"]");
 +
 +		// a command button
 +		$this->verifyElementNotPresent("//input[@type='submit' and @value=\"Name: test, Param: value using callback\"]");
 +		$this->click("//input[@type='submit' and @value='click me']", "");
 +		$this->pause(800);
 +		$this->verifyElementPresent("//input[@type='submit' and @value=\"Name: test, Param: value using callback\"]");
 +
 +		// a button causing validation
 +		$this->verifyNotVisible('ctl0_body_ctl2');
 +		$this->click("//input[@type='submit' and @value='submit']", "");
 +		$this->pause(800);
 +		$this->verifyVisible('ctl0_body_ctl2');
 +		$this->type("ctl0\$body\$TextBox", "test");
 +		$this->click("//input[@type='submit' and @value='submit']", "");
 +		$this->pause(800);
 +		$this->verifyNotVisible('ctl0_body_ctl2');
 +		$this->verifyElementPresent("//input[@type='submit' and @value=\"I'm clicked using callback\"]", "");
 +	}
 +}
 +
 +?>
\ No newline at end of file diff --git a/tests/FunctionalTests/quickstart/ActiveControls/ActiveCheckBoxTestCase.php b/tests/FunctionalTests/quickstart/ActiveControls/ActiveCheckBoxTestCase.php new file mode 100644 index 00000000..ce171e2b --- /dev/null +++ b/tests/FunctionalTests/quickstart/ActiveControls/ActiveCheckBoxTestCase.php @@ -0,0 +1,58 @@ +<?php
 +
 +//$Id$
 +class ActiveCheckBoxTestCase extends SeleniumTestCase
 +{
 +	function test ()
 +	{
 +		$this->open("../../demos/quickstart/index.php?page=ActiveControls.Samples.TActiveCheckBox.Home&notheme=true", "");
 +
 +		$this->verifyTitle("PRADO QuickStart Sample", "");
 +
 +		$this->assertTextPresent('TActiveCheckBox Samples (AJAX)');
 +
 +
 +		// an auto postback checkbox
 +		$this->verifyTextNotPresent("ctl0_body_ctl0 clicked using callback");
 +		$this->click("//input[@name='ctl0\$body\$ctl0']");
 +		$this->pause(800);
 +		$this->assertChecked("//input[@name='ctl0\$body\$ctl0']");
 +		$this->verifyTextPresent("ctl0_body_ctl0 clicked using callback");
 +		$this->click("//input[@name='ctl0\$body\$ctl0']");
 +		$this->pause(800);
 +		$this->verifyTextPresent("ctl0_body_ctl0 clicked using callback");
 +		$this->assertNotChecked("//input[@name='ctl0\$body\$ctl0']");
 +
 +		// a checkbox causing validation on a textbox
 +		$this->verifyNotVisible('ctl0_body_ctl1');
 +		$this->click("//input[@name='ctl0\$body\$ctl2']");
 +		$this->verifyVisible('ctl0_body_ctl1');
 +		$this->click("//input[@name='ctl0\$body\$ctl2']", "");
 +		$this->verifyVisible('ctl0_body_ctl3');
 +		$this->type("ctl0\$body\$TextBox", "test");
 +		$this->click("//input[@name='ctl0\$body\$ctl2']", "");
 +		$this->pause(800);
 +		$this->verifyNotVisible('ctl0_body_ctl1');
 +		$this->assertTextPresent("ctl0_body_ctl2 clicked using callback");
 +
 +		// a checkbox validated by a required field validator
 +		$this->assertNotChecked("//input[@name='ctl0\$body\$CheckBox']");
 +		$this->verifyNotVisible('ctl0_body_ctl4');
 +		$this->click("//input[@type='submit' and @value='Submit']", "");
 +		$this->verifyVisible('ctl0_body_ctl4');
 +		$this->click("//input[@name='ctl0\$body\$CheckBox']", "");
 +		$this->assertChecked("//input[@name='ctl0\$body\$CheckBox']");
 +		$this->click("//input[@type='submit' and @value='Submit']", "");
 +		$this->verifyNotVisible('ctl0_body_ctl4');
 +		$this->assertTextPresent("ctl0_body_CheckBox clicked");
 +
 +		// a checkbox validated by a required field validator using AutoPostBack
 +		$this->assertChecked("//input[@name='ctl0\$body\$CheckBox2']");
 +		$this->verifyNotVisible('ctl0_body_ctl5');
 +		$this->click("//input[@name='ctl0\$body\$CheckBox2']", "");
 +		$this->verifyVisible('ctl0_body_ctl5');
 +		$this->assertChecked("//input[@name='ctl0\$body\$CheckBox2']");
 +	}
 +}
 +
 +?>
\ No newline at end of file  | 
