qcubed/framework

View on GitHub
includes/base_controls/_actions.inc.php

Summary

Maintainability
F
4 days
Test Coverage
<?php
    /**
     * This file contains all basic action classes: QAction, QServerAction, QAjaxAction, etc.
     *
     * @package Actions
     * @filesource
     */

    /**
     * Base class for all other Actions.
     *
     * @package Actions
     * @property QEvent $Event Any QEvent derivated class instance
     */
    abstract class QAction extends QBaseClass {
        /**
         * Abstract method, implemented in derived classes. Returns the JS needed for the action to work
         *
         * @param QControl $objControl
         *
         * @return mixed
         */
        abstract public function RenderScript(QControl $objControl);

        /** @var QEvent Event object which will fire this action */
        protected $objEvent;

        /**
         * @param QControl|QControlBase $objControl   QControl for which the actions have to be rendered
         * @param string                $strEventName Name of the event for which the actions have to be rendered
         * @param QAction[]             $objActions   Array of actionss
         *
         * @return null|string
         * @throws Exception
         */
        public static function RenderActions(QControl $objControl, $strEventName, $objActions) {
            $strToReturn = '';
            $strJqUiProperty = null;

            if ($objControl->ActionsMustTerminate) {
                $strToReturn .= ' event.preventDefault();' . _nl();
            }

            if ($objActions && count($objActions)) {
                foreach ($objActions as $objAction) {
                    if ($objAction->objEvent->EventName != $strEventName) {
                        throw new Exception('Invalid Action Event in this entry in the ActionArray');
                    }

                    if ($objAction->objEvent instanceof QJqUiPropertyEvent) {
                        $strJqUiProperty = $objAction->objEvent->JqProperty;
                    }

                    $strCode = ' ' . $objAction->RenderScript($objControl);

                    if ($objAction->objEvent->Block) {
                        $strCode .= _nl() . 'qc.blockEvents = true;';
                    }


                    if ($objAction->objEvent->Delay > 0) {
                        $strCode = sprintf(" qcubed.setTimeout('%s', \$j.proxy(function(){%s},this), %s);",
                            $objControl->ControlId,
                            _nl() . _indent(trim($strCode)) . _nl(),
                            $objAction->objEvent->Delay);
                    }

                    // Add Condition (if applicable)
                    if (strlen($objAction->objEvent->Condition)) {
                        $strCode = sprintf(' if (%s) {%s}', $objAction->objEvent->Condition, _nl() . _indent(trim($strCode)) . _nl());
                    }

                    $strCode .= _nl();

                    // Append it to the Return Value
                    $strToReturn .= $strCode;
                }
            }

            if (strlen($strToReturn)) {
                $strToReturn = _nl() . _indent($strToReturn);


                if ($strJqUiProperty) {
                    $strOut = sprintf('$j("#%s").%s("option", {%s: function(event, ui){%s}});',
                        $objControl->getJqControlId(),
                        $objControl->getJqSetupFunction(),
                        $strJqUiProperty,
                        $strToReturn);
                } elseif ($objControl instanceof QControlProxy) {
                    if ($objControl->TargetControlId) {
                        // Deprecated.
                        $strOut = sprintf('$j("#%s").on("%s", function(event, ui){%s});', $objControl->TargetControlId, $strEventName, $strToReturn);
                    } else {
                        $strOut = sprintf('$j("#%s").on("%s", "[data-qpxy=\'%s\']", function(event, ui){%s});', $objControl->Form->FormId, $strEventName, $objControl->ControlId, $strToReturn);
                    }
                } else {
                    $strOut = sprintf('$j("#%s").on("%s", function(event, ui){%s});',
                        $objControl->getJqControlId(),
                        $strEventName, $strToReturn);

                }

                if (isset($strOut)) {
                    if (!QApplication::$Minimize) {
                        // Render a comment
                        $strOut = _nl() .  _nl() .
                            sprintf ('/*** Event: %s  Control Type: %s, Control Name: %s, Control Id: %s  ***/', $strEventName, get_class($objControl), $objControl->Name, $objControl->ControlId) .
                            _nl() .
                            _indent($strOut) .
                            _nl() . _nl();
                    }
                    return $strOut;
                }
            }

            return null;
        }

        /**
         * PHP Magic function to set the property values of an object of the class
         * In this case, we only have 'Event' property to be set
         *
         * @param string $strName  Name of the property
         * @param string $mixValue Value of the property
         *
         * @throws QCallerException
         * @return mixed|null|string
         */
        public function __set($strName, $mixValue) {
            switch ($strName) {
                case 'Event':
                    return ($this->objEvent = QType::Cast($mixValue, 'QEvent'));

                default:
                    try {
                        return parent::__set($strName, $mixValue);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * PHP Magic function to get the property values of an object of the class
         * In this case, we only have 'Event' property to be set
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'Event':
                    return $this->objEvent;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }
    }

    /**
     * Server actions are handled through a full-page postback.
     *
     * @package                                        Actions
     * @property-read string $MethodName               Name of the associated action handling method
     * @property-read mixed  $CausesValidationOverride An override for CausesValidation property (if supplied)
     * @property-read string $JsReturnParam            The parameter to be returned
     *                                                 (overrides the Control's ActionParameter)
     */
    class QServerAction extends QAction {
        /** @var string Name of the method in the form to be called */
        protected $strMethodName;
        /**
         * @var mixed A constant from QCausesValidation enumeration class
         *            It is set in the constructor via the corresponding argument
         */
        protected $mixCausesValidationOverride;
        /** @var string An over-ride for the Control's ActionParameter */
        protected $strJsReturnParam;

        /**
         * @param string $strMethodName                The method name which is to be assigned as the event handler
         *                                             (for the event being created)
         * @param string $mixCausesValidationOverride  A constant from QCausesValidation
         *                                             (or $this or an array of QControls)
         * @param string $strJsReturnParam             The parameter to be returned when this event occurs
         *                                             (this is an override for the control's ActionParameter)
         */
        public function __construct($strMethodName = null, $mixCausesValidationOverride = null,
                                    $strJsReturnParam = '') {
            $this->strMethodName = $strMethodName;
            $this->mixCausesValidationOverride = $mixCausesValidationOverride;
            $this->strJsReturnParam = $strJsReturnParam;
        }

        /**
         * PHP Magic function to get the property values of an object of the class
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'MethodName':
                    return $this->strMethodName;
                case 'CausesValidationOverride':
                    return $this->mixCausesValidationOverride;
                case 'JsReturnParam':
                    return $this->strJsReturnParam;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Determines the ActionParameter associated with the action and returns it
         *
         * @param QControlBase $objControl
         *
         * @return string The action parameter
         */
        protected function getActionParameter($objControl) {
            if ($objActionParameter = $this->strJsReturnParam) {
                return $objActionParameter;
            }
            if ($objActionParameter = $this->objEvent->JsReturnParam) {
                return $objActionParameter;
            }
            $objActionParameter = $objControl->ActionParameter;
            if ($objActionParameter instanceof QJsClosure) {
                return '(' . $objActionParameter->toJsObject() . ').call(this)';
            }

            return "'" . addslashes($objActionParameter) . "'";
        }

        /**
         * Returns the JS which will be called on the client side
         * which will result in the event handler being called
         *
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.pB('%s', '%s', '%s', %s);",
                $objControl->Form->FormId, $objControl->ControlId, get_class($this->objEvent), $this->getActionParameter($objControl));
        }
    }

    /**
     * The QAjaxAction responds to events with ajax calls, which refresh a portion of a web page without reloading
     * the entire page. They generally are faster than server requests and give a better user experience.
     * 
     * The QAjaxAction will associate a callback (strMethodName) with an event as part of an AddAction call. The callback will be 
     * a method in the current QForm object. To associate a method that is part of a QControl, or any kind of a callback,
     * use a QAjaxControlAction.
     * 
     * The wait icon is a spinning gif file that can be overlayed on top of the control to show that the control is in
     * a "loading" state. TODO: Convert this to a FontAwesome animated icon.
     *
     * mixCausesValidationOverride allows you to selectively say whether this action causes a validation, and on what subset of controls.
     * 
     * strJsReturnParam is a javascript string that specifies what the action parameter will be, if you don't want the default.
     * 
     * blnAsync lets you respond to the event asynchronously. Use care when setting this to true. Normally, qcubed will
     * put events in a queue and wait for each event to return a result before executing the next event. Most of the time,
     * the user experience is fine with this. However, there are times when events might be firing quickly and you do
     * not want to wait. However, your QFormState handler must be able to handle asynchronous events.
     * The default QFormStateHandler cannot do this, so you will need to use a different one.
     *
     * @property-read           $MethodName               Name of the (event-handler) method to be called
     *              the event handler - function containing the actual code for the Ajax action
     * @property-read QWaitIcon $WaitIconControl          the waiting icon control for this Ajax Action
     * @property-read mixed     $CausesValidationOverride what kind of validation over-ride is to be implemented
     *              on this action.(See the QCausesValidation class and QFormBase class to understand in greater depth)
     * @property-read string    JsReturnParam             The line of javascript which would set the 'strParameter' value on the
     *              client-side when the action occurs!
     *              (see /assets/_core/php/examples/other_controls/js_return_param_example.php for example)
     * @property-read string    Id                        The Ajax Action ID for this action.
     * @package     Actions
     */
    class QAjaxAction extends QAction {
        /** @var string Ajax Action ID */
        protected $strId;
        /** @var string The event handler function name */
        protected $strMethodName;
        /** @var QWaitIcon Wait Icon to be used for this particular action */
        protected $objWaitIconControl;

        protected $blnAsync = false;
        /**
         * @var mixed what kind of validation over-ride is to be implemented
         *              (See the QCausesValidation class and QFormBase class to understand in greater depth)
         */
        protected $mixCausesValidationOverride;
        /**
         * @var string the line of javascript which would set the 'strParameter' value on the
         *              client-side when the action occurs!
         */
        protected $strJsReturnParam;

        /**
         * AjaxAction constructor. 
         * @param string           $strMethodName               Name of the event handler function to be called
         * @param string|QWaitIcon $objWaitIconControl          Wait Icon for the action
         * @param null|mixed       $mixCausesValidationOverride what kind of validation over-ride is to be implemented
         * @param string           $strJsReturnParam            the line of javascript which would set the 'strParameter' value on the
         *                                                      client-side when the action occurs!
         * @param boolean             $blnAsync                    True to have the events for this action fire asynchronously.
         *                                                         Be careful when setting this to true. See class description.
         */
        public function __construct($strMethodName = null, $objWaitIconControl = 'default',
                                    $mixCausesValidationOverride = null, $strJsReturnParam = "", $blnAsync = false) {
            $this->strId = null;
            $this->strMethodName = $strMethodName;
            $this->objWaitIconControl = $objWaitIconControl;
            $this->mixCausesValidationOverride = $mixCausesValidationOverride;
            $this->strJsReturnParam = $strJsReturnParam;
            $this->blnAsync = $blnAsync;
        }

        public function __clone() {
            $this->strId = null; //we are a fresh clone, lets reset the id and get our own later (in RenderScript)
        }

        /**
         * PHP Magic function to get the property values of a class object
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'MethodName':
                    return $this->strMethodName;
                case 'WaitIconControl':
                    return $this->objWaitIconControl;
                case 'CausesValidationOverride':
                    return $this->mixCausesValidationOverride;
                case 'JsReturnParam':
                    return $this->strJsReturnParam;
                case 'Id':
                    return $this->strId;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Returns the control's ActionParameter in string format
         *
         * @param QControl $objControl
         *
         * @return string
         */
        protected function getActionParameter($objControl) {
            if ($objActionParameter = $this->strJsReturnParam) {
                return $objActionParameter;
            }
            if ($objActionParameter = $this->objEvent->JsReturnParam) {
                return $objActionParameter;
            }
            $objActionParameter = $objControl->ActionParameter;
            if ($objActionParameter instanceof QJsClosure) {
                return '(' . $objActionParameter->toJsObject() . ').call(this)';
            }

            return "'" . addslashes($objActionParameter) . "'";
        }

        /**
         * Returns the RenderScript script for the action.
         * The returned script is to be executed on the client side when the action is executed
         * (in this case qc.pA function is executed)
         *
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            $strWaitIconControlId = null;
            if ($this->strId == null) {
                $this->strId = $objControl->Form->GenerateAjaxActionId();
            }

            if ((gettype($this->objWaitIconControl) == 'string') && ($this->objWaitIconControl == 'default')) {
                if ($objControl->Form->DefaultWaitIcon) {
                    $strWaitIconControlId = $objControl->Form->DefaultWaitIcon->ControlId;
                }
            } else {
                if ($this->objWaitIconControl) {
                    $strWaitIconControlId = $this->objWaitIconControl->ControlId;
                }
            }

            return sprintf("qc.pA('%s', '%s', '%s#%s', %s, '%s', %s);",
                $objControl->Form->FormId, $objControl->ControlId, addslashes(get_class($this->objEvent)), $this->strId,
                $this->getActionParameter($objControl), $strWaitIconControlId, $this->blnAsync ? 'true' : 'false');
        }
    }

    /**
     * Server control action is identical to server action, except
     * the handler for it is defined NOT in the form, but in a control.
     *
     * @package Actions
     */
    class QServerControlAction extends QServerAction {
        /**
         * @param QControl $objControl                  Control where the action handler is defined
         * @param string   $strMethodName               Name of the method which acts as the action handler
         * @param mixed    $mixCausesValidationOverride Override for CausesValidation (if needed)
         * @param string   $strJsReturnParam            Override for ActionParameter
         */
        public function __construct(QControl $objControl, $strMethodName, $mixCausesValidationOverride = null,
                                    $strJsReturnParam = "") {
            parent::__construct($objControl->ControlId . ':' . $strMethodName, $mixCausesValidationOverride, $strJsReturnParam);
        }
    }

    /**
     * Ajax control action is identical to Ajax action, except
     * the handler for it is defined NOT on the form host, but on a QControl.
     *
     * @package Actions
     */
    class QAjaxControlAction extends QAjaxAction {
        /**
         * @param QControl $objControl                  Control where the action handler is defined
         * @param string   $strMethodName               Name of the action handler method
         * @param string   $objWaitIconControl          The wait icon to be implemented
         * @param null     $mixCausesValidationOverride Override for CausesValidation (if needed)
         * @param string   $strJsReturnParam            Override for ActionParameter
         * @param boolean  $blnAsync                    True to have the events for this action fire asynchronously
         */
        public function __construct(QControl $objControl, $strMethodName, $objWaitIconControl = 'default',
                                    $mixCausesValidationOverride = null, $strJsReturnParam = "", $blnAsync = false) {
            assert($objControl->ControlId != '');    // Are you adding an action before adding the control to the form?
            parent::__construct($objControl->ControlId . ':' . $strMethodName, $objWaitIconControl, $mixCausesValidationOverride, $strJsReturnParam, $blnAsync);
        }
    }

    /**
     * Client-side action - no postbacks of any kind are performed.
     * All handling activity happens in Javascript.
     *
     * @package Actions
     */
    class QRedirectAction extends QAction {
        /** @var string JS to be run on the client side */
        protected $strJavaScript;

        /**
         * possible values:
         * http://google.com
         * index.php?page=view
         * /foo/bar/woot.html
         *
         * @param string $strUrl
         */
        public function __construct($strUrl) {
            $this->strJavaScript = sprintf("document.location.href ='%s'", trim($strUrl));
        }

        /**
         * PHP Magic function to get the property values of a class object
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'JavaScript':
                    return $this->strJavaScript;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Returns the JS which runs on the client side
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            return sprintf('%s;', $this->strJavaScript);
        }
    }

    /**
     * Client-side action - no postbacks of any kind are performed.
     * All handling activity happens in Javascript.
     *
     * @package Actions
     */
    class QJavaScriptAction extends QAction {
        /** @var string JS to be run on the client side */
        protected $strJavaScript;

        /**
         * The constructor
         * @param string $strJavaScript JS which is to be executed on the client side
         */
        public function __construct($strJavaScript) {
            $this->strJavaScript = trim($strJavaScript);
            if (QString::LastCharacter($this->strJavaScript) == ';') {
                $this->strJavaScript = substr($this->strJavaScript, 0, strlen($this->strJavaScript) - 1);
            }
        }

        /**
         * PHP Magic function to get the property values of a class object
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'JavaScript':
                    return $this->strJavaScript;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Returns the JS which will be executed on the client side
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            return sprintf('%s;', $this->strJavaScript);
        }
    }

    /**
     * This action works as a if-else stopper for another action.
     * This action should be added to a control with the same event type before another action of that event type
     * Doing so brings up a JavaScript Confirmation box in front of the user.
     * If the user clicks on 'OK', then the next next action is executed (and any actions after that as well)
     * If the user clicks on 'Cancel', then next/rest of the action(s) are not executed
     * @package Actions
     */
    class QConfirmAction extends QAction {
        /** @var string Message to be shown to the user on the confirmation prompt */
        protected $strMessage;

        /**
         * Constructor of the function
         * @param string $strMessage Message which is to be set as the confirmation prompt message
         */
        public function __construct($strMessage) {
            $this->strMessage = $strMessage;
        }

        /**
         * PHP Magic function to get the property values of an object of the class
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'Message':
                    return $this->strMessage;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Returns the JS to be executed on the client side
         * @param QControl $objControl
         *
         * @return string The JS to be executed
         */
        public function RenderScript(QControl $objControl) {
            $strMessage = JavaScriptHelper::toJsObject($this->strMessage);

            return sprintf("if (!confirm(%s)) return false;", $strMessage);
        }
    }

    /**
     * Displays an alert to the user
     *
     * @package Actions
     */
    class QAlertAction extends QAction {
        /** @var string Message to be shown as the alert */
        protected $strMessage;

        /**
         * Constructor
         *
         * @param string $strMessage Message to be shown as the alert
         */
        public function __construct($strMessage) {
            $this->strMessage = $strMessage;
        }

        /**
         * PHP Magic function to get the property values of an object of the class
         *
         * @param string $strName Name of the property
         *
         * @return mixed|null|string
         * @throws QCallerException
         */
        public function __get($strName) {
            switch ($strName) {
                case 'Message':
                    return $this->strMessage;
                default:
                    try {
                        return parent::__get($strName);
                    } catch (QCallerException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
            }
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            $strMessage = JavaScriptHelper::toJsObject($this->strMessage);

            return sprintf("alert(%s);", $strMessage);
        }
    }

    /**
     * @package Actions
     */
    class QResetTimerAction extends QAction {
        /**
         * Returns the JavaScript to be executed on the client side (to clear the timeout on the control)
         *
         * @param QControl $objControl Control on which the timeout has to be cleared
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qcubed.clearTimeout('%s');", $objControl->ControlId);
        }
    }

    /**
     * Prevents the default action on an event.
     *
     * E.g. If you have a click action added to a label whose text is a link, clicking it will take the action
     * but also take you to the link pointed by the label. You can add a QTerminateAction after all QClickEvent
     * handlers and that will make sure that action handlers are triggered but the browser does not navigate
     * the user to the link pointed by the label
     *
     * @package Actions
     */
    class QTerminateAction extends QAction {
        /**
         * Returns the JS for the browser
         *
         * @param QControl $objControl
         *
         * @return string JS to prevent the default action
         */
        public function RenderScript(QControl $objControl) {
            return 'event.preventDefault();';
        }
    }

    /**
     * Prevents the event from bubbling up the DOM tree, preventing any parent
     * handlers from being notified of the event.
     *
     * @package Actions
     */
    class QStopPropagationAction extends QAction {
        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string Client side JS
         */
        public function RenderScript(QControl $objControl) {
            return 'event.stopPropagation();';
        }
    }

    /**
     * Toggle the Disaply of a control
     *
     * @package Actions
     */
    class QToggleDisplayAction extends QAction {
        /** @var string Control ID of the control */
        protected $strControlId = null;
        /** @var boolean|null Enforce 'show' or 'hide' action */
        protected $blnDisplay = null;

        /**
         * @param QControl|QControlBase $objControl
         * @param bool                  $blnDisplay
         *
         * @throws Exception|QCallerException|QInvalidCastException
         */
        public function __construct($objControl, $blnDisplay = null) {
            if (!($objControl instanceof QControl)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QControl');
            }

            $this->strControlId = $objControl->ControlId;

            if (!is_null($blnDisplay)) {
                $this->blnDisplay = QType::Cast($blnDisplay, QType::Boolean);
            }
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string Returns the JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            if ($this->blnDisplay === true) {
                $strShowOrHide = 'show';
            } else {
                if ($this->blnDisplay === false) {
                    $strShowOrHide = 'hide';
                } else {
                    $strShowOrHide = '';
                }
            }

            return sprintf("qc.getW('%s').toggleDisplay('%s');",
                $this->strControlId, $strShowOrHide);
        }
    }

    /**
     * Toggle the 'enabled' status of a control
     * NOTE: It does not change the Enabled property on the server side
     *
     * @package Actions
     */
    class QToggleEnableAction extends QAction {
        /** @var null|string Control ID of the control to be Enabled/Disabled */
        protected $strControlId = null;
        /** @var boolean|null Enforce the Enabling or Disabling action */
        protected $blnEnabled = null;

        /**
         * @param QControl|QControlBase $objControl
         * @param boolean               $blnEnabled
         *
         * @throws Exception|QCallerException|QInvalidCastException
         */
        public function __construct($objControl, $blnEnabled = null) {
            if (!($objControl instanceof QControl)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QControl');
            }

            $this->strControlId = $objControl->ControlId;

            if (!is_null($blnEnabled)) {
                $this->blnEnabled = QType::Cast($blnEnabled, QType::Boolean);
            }
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string Client side JS
         */
        public function RenderScript(QControl $objControl) {
            if ($this->blnEnabled === true) {
                $strEnableOrDisable = 'enable';
            } else {
                if ($this->blnEnabled === false) {
                    $strEnableOrDisable = 'disable';
                } else {
                    $strEnableOrDisable = '';
                }
            }

            return sprintf("qc.getW('%s').toggleEnabled('%s');", $this->strControlId, $strEnableOrDisable);
        }
    }

    /**
     * Registers the click position on a control
     *
     * @package Actions
     */
    class QRegisterClickPositionAction extends QAction {
        /** @var null|string Control ID of the control on which the click position has to be registered */
        protected $strControlId = null;

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.getW('%s').registerClickPosition(event);", $objControl->ControlId);
        }
    }

    /**
     * Shows a dialog box (QDialogBox)
     *
     * @package Actions
     */
    class QShowDialogBox extends QAction {
        /** @var null|string Control ID of the dialog box (QDialogBox) */
        protected $strControlId = null;
        /**
         * @var null|string The Javascript that executes on the client side
         *                  For this action, this string contains the JS to show the dialog box
         */
        protected $strJavaScript = null;

        /**
         * Constructor method
         *
         * @param QDialogBox $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QDialogBox)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QDialogBox');
            }

            $this->strControlId = $objControl->ControlId;
            $this->strJavaScript = $objControl->GetShowDialogJavaScript();
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string JS to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return (sprintf('%s; qcubed.recordControlModification("%s", "Display", "1");', $this->strJavaScript, $this->strControlId));
        }
    }

    /**
     * Hides a dialog box (QDialogBox)
     *
     * @package Actions
     */
    class QHideDialogBox extends QAction {
        /** @var null|string The JS for hiding the dialog box */
        protected $strJavaScript = null;

        /**
         * Constructor
         *
         * @param QDialogBox $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QDialogBox)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QDialogBox');
            }

            $this->strJavaScript = $objControl->GetHideDialogJavaScript();
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return null|string JS to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return $this->strJavaScript;
        }
    }

    /**
     * Shows a QDialog
     * This is the JQuery UI alternative to show dialog
     *
     * @package Actions
     */
    class QShowDialog extends QAction {
        /** @var null|string The JS to show the dialog */
        protected $strJavaScript = null;

        /**
         * Constructor
         *
         * @param QDialog $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QDialog)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QDialog');
            }

            $strControlId = $objControl->getJqControlId();
            $this->strJavaScript = sprintf('jQuery("#%s").dialog("open");', $strControlId);
        }

        /**
         * Returns the JavaScript to be executed on the client side for opening/showing the dialog
         *
         * @param QControl $objControl
         *
         * @return null|string JS that will be run on the client side
         */
        public function RenderScript(QControl $objControl) {
            return $this->strJavaScript;
        }
    }

    /**
     * Hiding a JQuery UI Dialog (QDialog)
     *
     * @package Actions
     */
    class QHideDialog extends QAction {
        /** @var null|string JS to be executed on the client side for closing the dialog */
        protected $strJavaScript = null;

        /**
         * Constructor
         *
         * @param QDialog $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QDialog)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QDialog');
            }

            $strControlId = $objControl->getJqControlId();
            $this->strJavaScript = sprintf('jQuery("#%s").dialog("close");', $strControlId);
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return null|string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return $this->strJavaScript;
        }
    }

    /**
     * Puts focus on a Control (on the client side/browser)
     * @package Actions
     */
    class QFocusControlAction extends QAction {
        /** @var null|string Control ID of the control on which focus is to be put */
        protected $strControlId = null;

        /**
         * Constructor
         *
         * @param QControl $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QControl)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QControl');
            }

            $this->strControlId = $objControl->ControlId;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            // for firefox focus is special when in a blur or in a focusout event
            // http://stackoverflow.com/questions/7046798/jquery-focus-fails-on-firefox/7046837#7046837
            return sprintf("setTimeout(function(){qc.getW('%s').focus();},0);", $this->strControlId);
        }
    }

    /**
     * Blurs (JS blur, not visual blur) a control on server side (i.e. removes focus from that control)
     *
     * @package Actions
     */
    class QBlurControlAction extends QAction {
        /** @var null|string Control ID of the control from which focus has to be removed */
        protected $strControlId = null;

        /**
         * Constructor
         *
         * @param QControl $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QControl)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QControl');
            }

            $this->strControlId = $objControl->ControlId;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.getW('%s').blur();", $this->strControlId);
        }
    }

    /**
     * Selects contents inside a QTextBox on the client-side/browser
     * @package Actions
     */
    class QSelectControlAction extends QAction {
        /** @var null|string Control ID of the QTextBox which is to be selected */
        protected $strControlId = null;

        /**
         * Constructor
         *
         * @param QTextBox $objControl
         *
         * @throws QCallerException
         */
        public function __construct($objControl) {
            if (!($objControl instanceof QTextBox)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QTextBox');
            }

            $this->strControlId = $objControl->ControlId;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.getW('%s').select();", $this->strControlId);
        }
    }

    /**
     * Can add or remove an extra CSS class from a control.
     * Should be used mostly for temporary purposes such as 'hovering' over a control
     *
     * @package Actions
     */
    class QCssClassAction extends QAction {
        /** @var null|string The CSS class to be added to the control */
        protected $strTemporaryCssClass = null;
        /** @var bool Should the CSS class be applied by removing the previous one? */
        protected $blnOverride = false;

        /**
         * Constructor
         *
         * @param null|string $strTemporaryCssClass The temporary class to be added to the control
         *                                          If null, it will reset the CSS classes to the previous set
         * @param bool        $blnOverride          Should the previously set classes be removed (true) or not (false)
         *                                          This will not reset the CSS class on the server side
         */
        public function __construct($strTemporaryCssClass = null, $blnOverride = false) {
            $this->strTemporaryCssClass = $strTemporaryCssClass;
            $this->blnOverride = $blnOverride;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string The JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            // Specified a Temporary Css Class to use?
            if (is_null($this->strTemporaryCssClass)) {
                // No Temporary CSS Class -- use the Control's already-defined one
                return sprintf("qc.getC('%s').className = '%s';", $objControl->ControlId, $objControl->CssClass);
            } else {
                // Are we overriding or are we displaying this temporary css class outright?
                if ($this->blnOverride) {
                    // Overriding
                    return sprintf("qc.getC('%s').className = '%s %s';", $objControl->ControlId, $objControl->CssClass, $this->strTemporaryCssClass);
                } else {
                    // Use Temp Css Class Outright
                    return sprintf("qc.getC('%s').className = '%s';", $objControl->ControlId, $this->strTemporaryCssClass);
                }
            }
        }
    }


    /**
     * Toggles the given class on the objects identified by the given jQuery selector. If no selector given, then
     * the trigger control is toggled.
     *
     * @package Actions
     */
    class QToggleCssClassAction extends QAction {
        protected $strCssClass;
        protected $strTargetSelector;

        public function __construct($strCssClass, $strTargetSelector = null) {
            $this->strCssClass = $strCssClass;
            $this->strTargetSelector = $strTargetSelector;
        }

        public function RenderScript(QControl $objControl) {
            // Specified a Temporary Css Class to use?
            if ($this->strTargetSelector) {
                $strSelector = $this->strTargetSelector;
            } else {
                $strSelector = '#' . $objControl->ControlId;
            }
            return sprintf("jQuery('%s').toggleClass('%s');", $strSelector, $this->strCssClass);
        }
    }


    /**
     * Sets the CSS class of a control on the client side (does not update the server side)
     *
     * @package Actions
     */
    class QCssAction extends QAction {
        /** @var string CSS property to be set */
        protected $strCssProperty = null;
        /** @var string Value to which the CSS property should be set */
        protected $strCssValue = null;
        /**
         * @var null|string The control ID for which the action should be done.
         *                  By default, it is applied to the QControl to which the action is added.
         */
        protected $strControlId = null;

        /**
         * Constructor
         *
         * @param string        $strCssProperty
         * @param string        $strCssValue
         * @param null|QControl $objControl
         */
        public function __construct($strCssProperty, $strCssValue, $objControl = null) {
            $this->strCssProperty = $strCssProperty;
            $this->strCssValue = $strCssValue;
            if ($objControl) {
                $this->strControlId = $objControl->ControlId;
            }
        }

        /**
         * Returns the JavaScript to be executed on the client side
         *
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side for setting the CSS
         */
        public function RenderScript(QControl $objControl) {
            if ($this->strControlId == null) {
                $this->strControlId = $objControl->ControlId;
            }

            // Specified a Temporary Css Class to use?
            return sprintf('$j("#%s").css("%s", "%s"); ', $this->strControlId, $this->strCssProperty, $this->strCssValue);
        }
    }

    /**
     * Shows a Calendar Control
     *
     * @package Actions
     */
    class QShowCalendarAction extends QAction {
        /** @var null|string Control ID of the calendar */
        protected $strControlId = null;

        /**
         * @param QCalendar $calControl
         *
         * @throws QCallerException
         */
        public function __construct($calControl) {
            if (!($calControl instanceof QCalendar)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QCalendar');
            }
            $this->strControlId = $calControl->ControlId;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.getC('%s').showCalendar();", $this->strControlId);
        }
    }

    /**
     * Hides a calendar control
     *
     * @package Actions
     */
    class QHideCalendarAction extends QAction {
        /** @var null|string Control ID of the calendar control */
        protected $strControlId = null;

        /**
         * Constructor
         * @param QCalendar $calControl
         *
         * @throws QCallerException
         */
        public function __construct($calControl) {
            if (!($calControl instanceof QCalendar)) {
                throw new QCallerException('First parameter of constructor is expecting an object of type QCalendar');
            }
            $this->strControlId = $calControl->ControlId;
        }

        /**
         * Returns the JavaScript to be executed on the client side
         * @param QControl $objControl
         *
         * @return string JavaScript to be executed on the client side
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("qc.getC('%s').hideCalendar();", $this->strControlId);
        }
    }

    /**
     * Sets the javascript value of a control in the form. The value has to be known ahead of time. Useful for
     * automatically clearing a text field when it receives focus, for example.
     */
    class QSetValueAction extends QAction {
        protected $strControlId = null;
        protected $strValue = "";

        public function __construct($objControl, $strValue = "") {
            $this->strControlId = $objControl->ControlId;
            $this->strValue = $strValue;
        }

        /**
         * @param QControl $objControl
         * @return mixed|string
         */
        public function RenderScript(QControl $objControl) {
            return sprintf("jQuery('#%s').val('%s');", $this->strControlId, $this->strValue);
        }
    }