While waiting for feedback on Part I, let's get going with part II.
In this article, we will try to find out how we can replace the classical style of handling javascript events by the "jQuery" way of handling events.
Introduction
In normal javascript style, events are generally handled through an attribute inside the element.
For example, a click event will be handled by the onclick event handler of the element.
QCubed/Javascript way:
<button type="button" name="c2" id="c2" class="button" onclick="qc.pA('SampleForm', 'c2', 'QClickEvent', '', ''); return false;" > Click Me </button>jQuery way:
<script type="text/javascript">
$j(document).ready(function() {
$j("#c2").click(function(e){
qc.pA('SampleForm', 'c2', 'QClickEvent', '', '');
return false;
})
});
</script>How we do this?
Remove actions from control
This is done easily, and can be accomplished using 2 ways, both in QControlBase.class:
first, line 627: comment out:
/* if ($blnIncludeAction)
$strToReturn .= $this->GetActionAttributes();
*/or second, we could set $blnIncludeAction to false by default ?
line 610:
instead of
public function GetAttributes($blnIncludeCustom = true, $blnIncludeAction = true) { public function GetAttributes($blnIncludeCustom = true, $blnIncludeAction = false) {Let me know your preferences...
Include jQuery javascript
This is a bit more trickier :)
Warning: code below is far from finalized, it is more a proof of concept which we can use to build upon!
What we want is to render our script after QCubed has finished initializing.
The rendering of these scripts occur in QFormBase.class.php
Around line 1200, we insert this code:
$strjQueryEndScript = '';
foreach ($this->GetAllControls() as $objControl)
if ($objControl->Rendered)
$strjQueryEndScript .= $objControl->GetActionAttributes();
$strjQueryScript = sprintf('<script type="text/javascript"> $j(document).ready(function() { %s });</script>',$strjQueryEndScript);
$strEndScript .= $strjQueryScript;First, we go through all controls that have been rendered in the page.
Foreach of those controls, we get the actions.
Once all controls have been rendered, we put all the actions in the document.ready of jQuery.
Last, we append the full jQuery script to the original jQueryScript.
This will render something like this:
<script type="text/javascript">
$j(document).ready(function() {
onclick="qc.pA('SampleForm', 'c2', 'QClickEvent', '', '');
return false;"
});
</script>Almost there! The only thing we need to change is getting rid of that onclick= piece and replace it with $j("#c2").click(function(e){ })
Adding support for jQuery Events and Actions
Instead of the 'onclick' event, we now need 'click'.
One can find all supported Events in the file _events.inc.php
For this article, I will limit the support for events to the clickevent, but the other events will need the same adjustments:
Around line 71, we have:
class QClickEvent extends QEvent {
const EventName = 'onclick';
protected $strJavaScriptEvent = 'onclick';
}To include the jQuery equivalent, we now have:
class QClickEvent extends QEvent {
const EventName = 'onclick';
const JQueryEventName = 'click';
protected $strJavaScriptEvent = 'onclick';
protected $strJQueryEvent = 'click';
}Now that we have the JQueryEvent, we also need a getter for this, wich is done in the QEvent base class (in the same file).
abstract class QEvent extends QBaseClass {
protected $strJavaScriptEvent;
protected $strJQueryEvent;
....and
public function __get($strName) {
switch ($strName) {
case 'JQueryEvent':
return $this->strJQueryEvent;
case 'JavaScriptEvent':
return $this->strJavaScriptEvent;
...Finally, the $objControl->GetActionAttributes(); routine from the code in the previous paragraph above will eventually take us to
QAction::RenderActions($this, $strEventName, $this->objActionArray[$strEventName]);Inside this routine, Here, we need to change the output from (around line 50):
return sprintf('%s="%s" ', $strEventName, substr($strToReturn, 1));to
return sprintf('$j("#%s").%s(function(e){%s})', $objControl->ControlId, $strEventName, substr($strToReturn, 1));Full code:
if (strlen($strToReturn))
return sprintf('$j("#%s").%s(function(e){%s});', $objControl->ControlId, $strEventName, substr($strToReturn, 1));
else
return null;But before hitting this code, we see that $strEventName should already contain the value 'click' (now 'onclick').
That is put in when AddAction is called during the adding of the event:
Around line 310:
public function AddAction($objEvent, $objAction) {
....
// Pull out the Event Name
$strEventName = $objEvent->JavaScriptEvent;
...
}Now, we don't want the JavaScriptEvent any more, but the newly create 'JQueryEvent':
public function AddAction($objEvent, $objAction) {
....
// Pull out the Event Name
$strEventName = $objEvent->JQueryEvent;
...
}Finally, a last check performed by QCubed is to find a match between the event that was passed and the Action that is associated with it:
public static function RenderActions(QControl $objControl, $strEventName, $objActions) {
...
if ($objActions && count($objActions)) foreach ($objActions as $objAction) {
if ($objAction->objEvent->JavaScriptEvent != $strEventName)
throw new Exception('Invalid Action Event in this entry in the ActionArray');
...Again, we don't want JavaScriptEvent, but JqueryEvent:
public static function RenderActions(QControl $objControl, $strEventName, $objActions) {
...
if ($objActions && count($objActions)) foreach ($objActions as $objAction) {
if ($objAction->objEvent->JQueryEvent != $strEventName)
throw new Exception('Invalid Action Event in this entry in the ActionArray');
...Conclusions
In order to migrate all the events to jQuery, we will need to modify _events.inc.php so they contain all jQuery equivalents of the event we want to support.
In addition to the existing events, we can also add in new events previously not supported.
Q/A
- should we make it an option NOT to use jQuery at all in QCubed? Allow fallback to the old way? This would increase complexity of the code, and also more difficult to maintain the code, but would be nice to have an intermediate version which supports both, and as a final step, go jQuery all the way.
- what are the constants used for in the QClickEvent?
Any comments/feedback greatly appreciated.

thanks for the feedback.
i'll be integrating the other events, do some testing and release a patch for this part.
currently strugling to get all the javascript for movable and sizeable controls, but backwards compatiblity will become an issue here. But we'll see where it goes.
greetings,
Kristof
Forget about moveable and sizeable controls. That's very secondary. These are arcane things that barely anyone used in the QCodo world; if you have to break back compat - or even release QCubed 2.0 without these controls initially - it's totally no big deal.
Great, here we go. :)
This looks great, and I like the approach. I see no reason to keep the old methods around. Even if someone ports their app to 2.0, I can't see these low-level changes affecting 99% of the apps running them.
"Allow fallback to the old way?"
Would say no if this is for 2.0. Anyone that does want the old way could use 1.0 or 1.1.
looks great
i also would vote no fallback to keep the framework free from clutter
No fallback on the old way. Definitely jQuery all the way. That said, use jQuery in noConflict() mode - so that other javascript library plugins would be possible.
Just a tought here regarding the jQuery chaining and if that would be useful in QCubed. I've written a little example like this:
<?phprequire_once('jQuery.class.php');
$j = new JQuery('.run');
$j->animate('{opacity: "0.1", left: "+=400"}', 1200)->animate('{opacity: "0.4", top: "+=160", height: "20", width: "20"}', "slow")->
slideUp()->slideDown('slow');
$output = $j->output();
echo "jQuery output: $output\n";
?>
And that script would output:
jQuery output: $(".run").animate({opacity: "0.1", left: "+=400"}, 1200).animate({opacity: "0.4", top: "+=160", height: "20", width: "20"}, "slow").slideUp().slideDown("slow");
Don't know know where this would be useful, I'm just brainstorming here as I'm excited that jQuery will be the JS framework for QCubed and some really useful stuff can be used from it.