qcubed/framework

View on GitHub
includes/base_controls/QHtmlAttributeManagerBase.class.php

Summary

Maintainability
F
1 wk
Test Coverage
<?php
/**
 * QHtmlAttributeManager class
 *
 * This class is designed to be used as a base class for controls and other classes that need to manage HTML attributes.
 *
 * @package Controls
 */

/**
 * A base class for objects that manage html attributes. Uses array functions and defines a couple of arrays to manage
 * the attributes. Values will be html escaped when printed.
 *
 * Includes:
 * - helper functions to manage the class, style, and data-* attributes specially.
 * - helper functions to manage 'name-*' classes that are found in css frameworks like
 *   Bootstrap and Foundation.
 * - helpers for __get and __set functions, partially for backwards compatibility and also to make the setting
 *   of some attributes and styles easier (so you don't have to remember how to set them).
 *
 * Usage: Use the helper functions to setup your styles, classes, data-* and other attributes, then call
 *          renderHtmlAttributes() to render the attributes to insert them into a tag.
 *
 *
 * @property string $AccessKey allows you to specify what Alt-Letter combination will automatically focus that control on the form
 * @property string $BackColor sets the CSS background-color of the control
 * @property string $BorderColor sets the CSS border-color of the control
 * @property string $BorderWidth sets the CSS border-width of the control
 * @property string $BorderStyle is used to set CSS border-style by {@link QBorderStyle}
 * @property string $BorderCollapse    defines the BorderCollapse css style for a table
 * @property string $CssClass sets or returns the CSS class for this control. When setting, if you precede the class name
 *  with a plus sign (+), it will add the class(es) to the currently existing classes, rather than replace them. Can add or
 *  set more than one class at once by separating names with a space.
 * @property string $Cursor is used to set CSS cursor property by {@link QCursor}
 * @property boolean $Display shows or hides the control using the CSS display property.  In either case, the control is
 *  still rendered on the page. See the Visible property if you wish to not render a control.
 * @property string $DisplayStyle is used to set CSS display property by {@link QDisplayStyle}
 * @property boolean $Enabled specifies whether or not this is enabled (it will grey out the control and make it
 *  inoperable if set to true)
 * @property boolean $FontBold sets the font bold or normal
 * @property boolean $FontItalic sets the Font italic or normal
 * @property string $FontNames sets the name of used fonts
 * @property boolean $FontOverline
 * @property string $FontSize sets the font-size of the control
 * @property boolean $FontStrikeout
 * @property boolean $FontUnderline sets the font underlined
 * @property string $ForeColor sets the CSS color property, which controls text color
 * @property string $Height
 * @property string $Left CSS left property
 * @property integer $Opacity sets the opacity of the control (0-100)
 * @property string $Overflow is used to set CSS overflow property by {@link QOverflow}
 * @property string $Position is used to set CSS position property by {@link QPosition}
 * @property integer $TabIndex specifies the index/tab order on a form
 * @property string $ToolTip specifies the text to be displayed when the mouse is hovering over the control
 * @property string $Top
 * @property string $Width
 * @property string $HorizontalAlign sets the CSS text-align property
 * @property string $VerticalAlign sets the CSS vertical-align property
 * @property-write mixed $Padding sets the CSS padding property. Will accepts a string, which is passed verbatim, or
 *  an array, either numerically indexed, in which case it is in top, right, bottom, left order, or keyed with the
 *  names 'top', 'right', 'bottom', 'left'.
 * @property-write mixed $Margin sets the CSS margin property. Will accepts a string, which is passed verbatim, or
 *  an array, either numerically indexed, in which case it is in top, right, bottom, left order, or keyed with the
 *  names 'top', 'right', 'bottom', 'left'
 * @property-write array $Data a key/value array of data-* items to set. Keys can be in camelCase notation, in which case they will be
 *  converted to dashed notation. Use GetDataAttribute() to retrieve the value of a data attribute.
 * @property boolean $NoWrap sets the CSS white-space  property to nowrap
 * @property boolean $ReadOnly is the "readonly" html attribute (making a textbox "ReadOnly" is  similar to setting the textbox to Enabled
 *  Readonly textboxes are selectedable, and their values get posted. Disabled textboxes are not selectable and values do not post.
 * @property string $AltText text used for 'alt' attribute in images
 * @property string $OrderedListType type for ordered lists. Expects a QOrderedListType.
 * @property string $UnorderedListStyle style for unordered lists. Expects a QUnorderedListType.
 */

class QHtmlAttributeManagerBase extends QBaseClass {
    /** @var  array attributes stored in PHP native format so they can be retrieved. Escaping happens when they are drawn. */
    protected $attributes = array();
    protected $styles = array();

    /**
     * Sets the given html attribute to the given value.
     * @param $strName
     * @param $strValue
     * @return boolean true if the attribute changed value
     */
    public function SetHtmlAttribute ($strName, $strValue) {
        if (!is_null($strValue)) {
            if (!isset($this->attributes[$strName]) || $this->attributes[$strName] !== $strValue) {
                // only make a change if it has actually changed value.
                $this->attributes[$strName] = $strValue;
                $this->MarkAsModified();
                return true;
            }
        } else {
            if (isset($this->attributes[$strName])) {
                unset($this->attributes[$strName]);
                $this->MarkAsModified();
                return true;
            }
        }
        return false;
    }

    /**
     * Removes the given html attribute.
     *
     * @param $strName
     */
    public function RemoveHtmlAttribute ($strName) {
        $this->SetHtmlAttribute($strName, null);
    }

    /**
     * Gets the Html Attribute, or return null if it does not exist.
     *
     * @param $strName
     * @return null
     */
    public function GetHtmlAttribute ($strName) {
        if (isset ($this->attributes[$strName])) {
            return $this->attributes[$strName];
        }
        else {
            return null;
        }
    }

    /**
     * Returns an attribute array, with styles rendered.
     * @param array|null $attributeOverrides
     * @param array|null $styleOverrides
     * @param array|null $selection an array of titles of attributes that you want the result to be limited to
     * @return array
     */
    public function GetHtmlAttributes($attributeOverrides = null, $styleOverrides = null, $selection = null) {
        $attributes = $this->attributes;
        if ($attributeOverrides) {
            $attributes = array_merge ($attributes, $attributeOverrides);
        }
        $strStyles = $this->RenderCssStyles($styleOverrides);
        if ($strStyles) {
            $attributes['style'] = $strStyles;
        }
        if ($selection) {
            $attributes = array_intersect_key($attributes, array_fill_keys($selection, 1));
        }
        return $attributes;
    }

    /**
     * Returns true if the given attribute is set.
     *
     * @param string $strName
     * @return bool
     */
    public function HasHtmlAttribute ($strName) {
        return (isset($this->attributes[$strName]));
    }

    /**
     * Sets the given value as an html "data-*" attribute. The named value will be retrievable in jQuery by using
     * '.data("$strName");'
     *
     * Note: Data name cases are handled specially in jQuery. Names are supposed to only be lower case. jQuery
     * converts dashed notation to camelCase.
     *
     * For example, if your html looks like this:
     *
     * <div id='test1' data-test-case="my test"></div>
     *
     * You would get that value in jQuery by doing:
     *
     * $j('#test1').data('testCase');
     *
     * Conversion to special html data-* name formatting is handled here automatically. So if you SetDataAttribute('testCase') here,
     * you can get it using .data('testCase') in jQuery
     *
     * @param $strName
     * @param $strValue
     */
    public function SetDataAttribute ($strName, $strValue) {
        $strName = 'data-' . JavaScriptHelper::dataNameFromCamelCase($strName);
        $this->SetHtmlAttribute($strName, $strValue);
    }

    /**
     * Gets the data-* attribute value that was set previously in PHP.
     *
     * Does NOT call into javascript to return a value that was set on the browser side. You need to use another
     * mechanism to retrieve that.
     *
     * @param $strName
     * @return null
     */
    public function GetDataAttribute ($strName) {
        $strName = 'data-' . JavaScriptHelper::dataNameFromCamelCase($strName);
        return $this->GetHtmlAttribute($strName);
    }

    /**
     * Removes the given data attribute.
     * @param $strName
     */
    public function RemoveDataAttribute ($strName) {
        $strName = 'data-' . JavaScriptHelper::dataNameFromCamelCase($strName);
        $this->RemoveHtmlAttribute($strName);
    }



    /**
     * Sets the Css named value to the given property.
     *
     * If blnIsLength is true, we assume the value is a unit length specifier, and so send it to the QHtml helper
     * function, which has the ability to perform arithmetic operations on the old value.
     *
     * Will set the value of the parent classes blnModified value if something changes.
     *
     * @param string $strName
     * @param string $strValue
     * @param bool $blnIsLength true if this is a unit specifier e.g. 2px
     * @return bool true if the style was actually changed (vs. it was already set that way)
     */
    public function SetCssStyle ($strName, $strValue, $blnIsLength = false) {
        $ret = false;
        if (!is_null($strValue)) {
            if ($blnIsLength) {
                if (isset($this->styles[$strName])) {
                    $oldValue = $this->styles[$strName];
                } else {
                    $oldValue = '';
                }
                if (QHtml::SetLength ($oldValue, $strValue)) {
                    $ret = true;
                    $this->MarkAsModified();
                    $this->styles[$strName] = $oldValue; // oldValue was updated
                }
            }
            elseif (!isset($this->styles[$strName]) || $this->styles[$strName] !== $strValue) {
                $this->styles[$strName] = $strValue;
                $this->MarkAsModified();
                $ret = true;
            }
        } else {
            if (isset($this->styles[$strName])) {
                unset($this->styles[$strName]);
                $this->MarkAsModified();
                $ret = true;
            }
        }
        return $ret;
    }

    /**
     * Removes the given CSS style property.
     * @param $strName
     * @return bool true if the style was removed, false if it wasn't there to begin with
     */
    public function RemoveCssStyle ($strName) {
        return $this->SetCssStyle ($strName, null);
    }

    /**
     * Return true if the CSS style has been set.
     * @param $strName
     * @return bool
     */
    public function HasCssStyle ($strName) {
        return isset($this->styles[$strName]);
    }

    /**
     * Retrieves the given CSS style property value.
     * @param $strName
     * @return string|null
     */
    public function GetCssStyle ($strName) {
        if (isset ($this->styles[$strName])) {
            return $this->styles[$strName];
        }
        else {
            return null;
        }
    }

    /**
     * Sets a box style, like margin or padding. Can accept:
     *  - string, which will pass the string verbatim to the shortcut prefix (e.g. margin: 0px 1px)
     *  - array, which can accept two styles:
     *         - [top, right, bottom, left], or
     *         - ['top'=>top, 'bottom'=>bottom, etc.]
     *
     * When passing an array, missing items or null items will be ignored. Also, you can use arithmetic operations
     * to change the current value (provided you set a previous value).
     *
     * For example:
     * $obj->setCssBoxStyle ('padding', ['right'=>3]);
     * $obj->setCssBoxStyle ('padding', ['right'=>'*4']);
     * Will set the padding-right to 12.
     *
     * @param string $strPrefix
     * @param string|array $mixValue
     */
    public function SetCssBoxValue($strPrefix, $mixValue) {
        if (is_string($mixValue)) {
            // shortcut
            $this->SetCssStyle($strPrefix, $mixValue);
        } elseif (is_array($mixValue)) {
            if (array_key_exists(0, $mixValue)) {
                // top right bottom left, numerically indexed
                if (isset($mixValue[0])) $this->SetCssStyle($strPrefix. '-top', $mixValue[0], true);
                if (isset($mixValue[1])) $this->SetCssStyle($strPrefix. '-right', $mixValue[1], true);
                if (isset($mixValue[2])) $this->SetCssStyle($strPrefix. '-bottom', $mixValue[2], true);
                if (isset($mixValue[3])) $this->SetCssStyle($strPrefix. '-left', $mixValue[3], true);
            } else {
                // assume key/value
                if (isset($mixValue['top'])) $this->SetCssStyle($strPrefix. '-top', $mixValue['top'], true);
                if (isset($mixValue['right'])) $this->SetCssStyle($strPrefix. '-right', $mixValue['right'], true);
                if (isset($mixValue['bottom'])) $this->SetCssStyle($strPrefix. '-bottom', $mixValue['bottom'], true);
                if (isset($mixValue['left'])) $this->SetCssStyle($strPrefix. '-left', $mixValue['left'], true);
            }
        }
    }


    /**
     * Sets the CSS class to the given value(s). Multiple class names can be separated with a space. If you
     * preceed the class names with a plus(+) they will be added to the current class. If a minus (-) they
     * will be removed.
     *
     * @param $strNewClass
     */
    public function SetCssClass($strNewClass) {
        assert ('$strNewClass[0] != "+" || $strNewClass[1] == " "'); // If a plus sign, be sure to follow with a space. For consistency.
        if (substr($strNewClass, 0, 2) == '+ ') {
            $ret = $this->AddCssClass(substr($strNewClass, 2));
        }
        elseif (substr($strNewClass, 0, 2) == '- ') {
            $ret = $this->RemoveCssClass(substr($strNewClass, 2));
        }
        else {
            $ret = $this->SetHtmlAttribute('class', $strNewClass);
        }
        return $ret;
    }

    /**
     * Adds a css class name to the 'class' property. Prevents duplication.
     * @param string $strNewClass
     * @return bool true if the class was not already attached.
     */
    public function AddCssClass($strNewClass) {
        $ret = false;
        if (!$strNewClass) return false;

        $strClasses = $this->GetHtmlAttribute('class');
        if (is_null($strClasses)) {
            $strClasses= '';
        }
        if (QHtml::AddClass($strClasses, $strNewClass)) {
            $this->SetHtmlAttribute('class', $strClasses);
            $ret = true;
        }
        return $ret;
    }

    /**
     * This will remove a css class name from the 'class' property (if it exists).
     * @param string $strCssClass
     */
    public function RemoveCssClass($strCssClass) {
        if (!$strCssClass) return;
        $strClasses = $this->GetHtmlAttribute('class');
        if ($strClasses && QHtml::RemoveClass($strClasses, $strCssClass)) {
            $this->SetHtmlAttribute('class', $strClasses);
        }
    }

    /**
     * Many CSS frameworks use families of classes, which are built up from a base family name. For example,
     * Bootstrap uses 'col-lg-6' to represent a column that is 6 units wide on large screens and Foundation
     * uses 'large-6' to do the same thing. This utility removes classes that start with a particular prefix
     * to remove whatever sizing class was specified.
     *
     * @param string $strPrefix
     */
    public function RemoveCssClassesByPrefix($strPrefix) {
        if (!$strPrefix) return;
        $strClasses = $this->GetHtmlAttribute('class');
        if ($strClasses && QHtml::RemoveClassesByPrefix($strClasses, $strPrefix)) {
            $this->SetHtmlAttribute('class', $strClasses);
        }
    }


    /**
     * Return true if the given class is in the attribute list.
     *
     * @param $strClass
     * @return bool
     */
    public function HasCssClass($strClass) {
        if (!isset($this->attributes['class'])) return false;
        $strClasses = explode (' ', $this->attributes['class']);
        return (in_array($strClass, $strClasses));
    }

    /**
     * Permanently overrides the current styles with a new set of attributes and styles.
     *
     * @param QHtmlAttributeManager $objNewStyles
     */
    protected function Override(QHtmlAttributeManager $objNewStyles) {
        $this->attributes = array_merge($this->attributes, $objNewStyles->attributes);
        $this->styles = array_merge($this->styles, $objNewStyles->styles);
    }

    /**
     * Mark the parent class as modified. The host class must implement this if this functionality is desired.
     */
    public function MarkAsModified() {}

    /**
     * Returns the html for the attributes. Allows the given arrays to override the attributes and styles before
     * rendering.
     * @param null|string     $attributeOverrides
     * @param null|string     $styleOverrides
     * @return string
     */
    public function RenderHtmlAttributes($attributeOverrides = null, $styleOverrides = null) {
        return QHtml::RenderHtmlAttributes($this->GetHtmlAttributes($attributeOverrides, $styleOverrides));
    }

    /**
     * Returns the styles rendered as a css style string.
     * @param null|string     $styleOverrides
     * @return string
     */
    public function RenderCssStyles($styleOverrides = null) {
        $styles = $this->styles;
        if ($styleOverrides) {
            $styles = array_merge($styles, $styleOverrides);
        }
        return QHtml::RenderStyles($styles);
    }

    /**
     * Helper function to render the current attributes and styles in a tag. Overrides will be merged in with
     * current values before creating the output, but they will not affect the current values.
     *
     * @param $strTag
     * @param null|array         $attributeOverrides    key/value pairs of values for attribute overrides
     * @param null|array         $styleOverrides        key/value pairs of values for style overrides
     * @param null|string         $strInnerHtml        inner html to render. Will NOT be escaped.
     * @param bool                $blnIsVoidElement    true if it should not have innerHtml or a closing tag.
     * @return string            HTML out. Attributes will be escaped as needed, but innerHtml will be raw, so be careful.
     */
    protected function RenderTag($strTag, $attributeOverrides = null, $styleOverrides = null, $strInnerHtml = null, $blnIsVoidElement = false, $blnNoSpace = false) {
        $strAttributes = $this->RenderHtmlAttributes($attributeOverrides, $styleOverrides);
        return QHtml::RenderTag($strTag, $strAttributes, $strInnerHtml, $blnIsVoidElement, $blnNoSpace);
    }


    /**
     * PHP magic method.
     *
     * The attributes include general attributes that span across most kinds of QControls. Attributes that are specific
     * to a particular kind of tag might appear just in that control's definition.
     *
     * @param string $strName
     * @return mixed
     * @throws Exception|QCallerException
     */
    public function __get($strName) {
        switch ($strName) {
            // Styles
            case "BackColor": return $this->GetCssStyle('background-color');
            case "BorderColor": return $this->GetCssStyle('border-color');
            case "BorderStyle": return $this->GetCssStyle('border-style');
            case "BorderWidth": return $this->GetCssStyle('border-width');
            case "BorderCollapse": return $this->GetCssStyle('border-collapse');
            case "Display": return !($this->GetCssStyle('display') === QDisplayStyle::None);
            case "DisplayStyle": return $this->GetCssStyle('display');
            case "FontBold": return $this->GetCssStyle('font-weight') == 'bold';
            case "FontItalic": return $this->GetCssStyle('font-style') == 'italic';
            case "FontNames": return $this->GetCssStyle('font-family');
            case "FontOverline": return $this->GetCssStyle('text-decoration') == 'overline';
            case "FontStrikeout": return $this->GetCssStyle('text-decoration') == 'line-through';
            case "FontUnderline": return $this->GetCssStyle('text-decoration') == 'underline';
            case "FontSize": return $this->GetCssStyle('font-size');
            case "ForeColor": return $this->GetCssStyle('color');
            case "Opacity": return $this->GetCssStyle('opacity');
            case "Cursor": return $this->GetCssStyle('cursor');
            case "Height": return $this->GetCssStyle('height');
            case "Width": return $this->GetCssStyle('width');
            case "Overflow": return $this->GetCssStyle('overflow');
            case "Position": return $this->GetCssStyle('position');
            case "Top": return $this->GetCssStyle('top');
            case "Left": return $this->GetCssStyle('left');
            case "HorizontalAlign": return $this->GetCssStyle('text-align');
            case "VerticalAlign": return $this->GetCssStyle('vertical-align');
            case "Wrap": throw new Exception ("Wrap is deprecated. Use NoWrap instead");
            case "NoWrap": return $this->GetCssStyle('white-space') == 'nowrap';
            case "UnorderedListStyle": return $this->GetCssStyle('list-style-type');

            // Attributes
            case "CssClass": return $this->GetHtmlAttribute('class');
            case "AccessKey": return $this->GetHtmlAttribute('accesskey');
            case "Enabled": return $this->GetHtmlAttribute('disabled') === null;
            case "TabIndex": return $this->GetHtmlAttribute('tabindex');
            case "ToolTip": return $this->GetHtmlAttribute('title');
            case "ReadOnly": return $this->HasHtmlAttribute('readonly');
            case "AltText": return $this->HasHtmlAttribute('alt');
            case "OrderedListType": return $this->GetHtmlAttribute('type');

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


    /**
     * PHP magic method
     *
     * @param string $strName
     * @param string $mixValue
     *
     * @return mixed
     * @throws Exception|QCallerException|QInvalidCastException
     */
    public function __set($strName, $mixValue) {
        switch ($strName) {
            // Styles
            case "BackColor":
                try {
                    $this->SetCssStyle('background-color', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "BorderColor":
                try {
                    $this->SetCssStyle('border-color', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "BorderStyle":
                try {
                    $this->SetCssStyle('border-style', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "BorderWidth":
                try {
                    $this->SetCssStyle('border-width', QType::Cast($mixValue, QType::String), true);
                    if (!$this->HasCssStyle ('border-style')) {
                        $this->SetCssStyle('border-style', 'solid');
                    }
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "BorderCollapse":
                try {
                    $this->SetCssStyle('border-collapse', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Display": // QControl changes the meaning of this to a boolean
            case "DisplayStyle":
                if (is_bool($mixValue)) {
                    if ($mixValue) {
                        $this->RemoveCssStyle('display'); // do the default
                    }
                    else {
                        $this->SetCssStyle('display', QDisplayStyle::None);
                    }
                } else {
                    try {
                        $this->SetCssStyle('display', QType::Cast($mixValue, QType::String));
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                }

            case "FontBold":
                try {
                    $this->SetCssStyle('font-weight', QType::Cast($mixValue, QType::Boolean) ? 'bold' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "FontItalic":
                try {
                    $this->SetCssStyle('font-style', QType::Cast($mixValue, QType::Boolean) ? 'italic' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "FontNames":
                try {
                    $this->SetCssStyle('font-family', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "FontOverline":
                try {
                    $this->SetCssStyle('text-decoration', QType::Cast($mixValue, QType::Boolean) ? 'overline' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "FontStrikeout":
                try {
                    $this->SetCssStyle('text-decoration', QType::Cast($mixValue, QType::Boolean) ? 'line-through' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "FontUnderline":
                try {
                    $this->SetCssStyle('text-decoration', QType::Cast($mixValue, QType::Boolean) ? 'underline' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "FontSize":
                try {
                    $this->SetCssStyle('font-size', QType::Cast($mixValue, QType::String), true);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "ForeColor":
                try {
                    $this->SetCssStyle('color', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Opacity":
                try {
                    $mixValue = QType::Cast($mixValue, QType::Integer);
                    if (($mixValue < 0) || ($mixValue > 100)) {
                        throw new QCallerException('Opacity must be an integer value between 0 and 100');
                    }
                    $this->SetCssStyle('opacity', $mixValue);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Cursor":
                try {
                    $this->SetCssStyle('cursor', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Height":
                try {
                    $this->SetCssStyle('height', QType::Cast($mixValue, QType::String), true);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Width":
                try {
                    $this->SetCssStyle('width', QType::Cast($mixValue, QType::String), true);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Overflow":
                try {
                    $this->SetCssStyle('overflow', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Position":
                try {
                    $this->SetCssStyle('position', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Top":
                try {
                    $this->SetCssStyle('top', QType::Cast($mixValue, QType::String), true);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Left":
                try {
                    $this->SetCssStyle('left', QType::Cast($mixValue, QType::String), true);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "HorizontalAlign":
                try {
                    $this->SetCssStyle('text-align', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "VerticalAlign":
                try {
                    $this->SetCssStyle('vertical-align', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Wrap": // Wrap is now an actual attribute. Original developer used Wrap instead of NoWrap, not anticipating future change to HTML
                throw new QCallerException ("Wrap is deprecated. Use NoWrap instead");
                break;

            case "NoWrap":
                try {
                    $this->SetCssStyle('white-space', QType::Cast($mixValue, QType::Boolean) ? 'nowrap' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Padding": // top right bottom left
                $this->SetCssBoxValue('padding', $mixValue);
                break;

            case "Margin": // top right bottom left
                $this->SetCssBoxValue('margin', $mixValue);
                break;

            case "UnorderedListStyle":
                try {
                    $this->SetCssStyle('list-style-type', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            // Attributes
            case "CssClass":
                try {
                    $strCssClass = QType::Cast($mixValue, QType::String);
                    $this->SetCssClass($strCssClass);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "AccessKey":
                try {
                    $this->SetHtmlAttribute('accesskey', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "Enabled":
                try {
                    $this->SetHtmlAttribute('disabled',  QType::Cast($mixValue, QType::Boolean) ? null : 'disabled');
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            /* case "Required": Not supported consistently by browsers. We handle this inhouse */

            case "TabIndex":
                try {
                    $this->SetHtmlAttribute('tabindex', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }
            case "ToolTip":
                try {
                    $this->SetHtmlAttribute('title', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "Data":
                try {
                    $dataArray = QType::Cast($mixValue, QType::ArrayType);
                    foreach ($dataArray as $key=>$value) {
                        $this->SetDataAttribute($key, $value);
                    }
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "ReadOnly":
                try {
                    $this->SetHtmlAttribute('readonly',  QType::Cast($mixValue, QType::Boolean) ? 'readonly' : null);
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "AltText":
                try {
                    $this->SetHtmlAttribute('alt', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

            case "OrderedListType":
                try {
                    $this->SetHtmlAttribute('type', QType::Cast($mixValue, QType::String));
                    break;
                } catch (QInvalidCastException $objExc) {
                    $objExc->IncrementOffset();
                    throw $objExc;
                }

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