qcubed/framework

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

Summary

Maintainability
D
2 days
Test Coverage
<?php
    /**
     * This file contains the QTreeNav class.
     *
     * @package Controls
     */

    /**
     * @package Controls
     *
     * @property mixed $SelectedItem
     * @property-read mixed $SelectedValue
     * @property-read mixed $ChildItemArray
     * @property mixed $ItemArray
     * @property string $ItemCssStyle
     * @property string $ItemSelectedCssStyle
     * @property string $ItemHoverCssStyle
     * @property integer $IndentWidth
     * @property integer $ItemHeight
     * @property integer $ItemWidth
     * @property boolean $ExpandOnSelect
     * @property-write mixed $ItemExpanded
     */
    class QTreeNav extends QControl {
        protected $strJavaScripts = 'treenav.js';

        protected $strItemCssStyle = 'treenav_item';
        protected $strItemSelectedCssStyle = 'treenav_item treenav_item_selected';
        protected $strItemHoverCssStyle = 'treenav_item treenav_item_hover';
        protected $strItemSelectedHoverCssStyle = 'treenav_item treenav_item_hover treenav_item_selected';

        protected $strLabelForRequired;
        protected $strLabelForRequiredUnnamed;

        protected $intIndentWidth = 15;
        protected $intItemHeight = 15;
        protected $intItemWidth = 0;

        protected $objChildItemArray = array();
        protected $objItemArray = array();
        protected $intNextItemId = 1;
        protected $objSelectedTreeNavItem = null;
        protected $strLoader = null;
        protected $objLoaderParent = null;

        protected $blnIsBlockElement = true;
        protected $blnExpandOnSelect = true;
        protected $blnAllowHover = false;

        public function __construct($objParentObject, $strControlId = null) {
            parent::__construct($objParentObject, $strControlId);

            $this->strLabelForRequired = QApplication::Translate('%s is required');
            $this->strLabelForRequiredUnnamed = QApplication::Translate('Required');
        }

        public function SetLoader($strFuncName, $objParent)
        {
            $this->strLoader = $strFuncName;
            $this->objLoaderParent = $objParent;
        }

        protected function GetItemHtml($objItem) {
            $strItemId = $this->strControlId . '_' . $objItem->ItemId;

            $objChildren = $objItem->ChildItemArray;
            $intChildCount = count($objChildren);

            $strSubNodeHtml = '';
            $strImageHtml = '';
            $strLabelHtml = '';

            if ($intChildCount || $objItem->HasChildren) {
                // This Item has Children -- Must show either Collapsed or Expanded icon
                if ($objItem->Expanded) {
                    $strImageHtml = sprintf('<span style="margin-right: 2px;"><img id="%s_image" src="%s/treenav_expanded.png" width="11" height="11" alt="" style="position: relative; top: 2px; cursor: pointer;" onclick="treenavToggleImage(\'%s\')"/></span>',
                        $strItemId, __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__, $strItemId);

                    if($intChildCount == 0 && null !== $this->strLoader && null !== $this->objLoaderParent)
                    {
                        //this is a dynamic item, call the loader
                        $strLoader = $this->strLoader;
                        $this->objLoaderParent->$strLoader($objItem);
                        
                        //now it should have children. Be sure to use them.
                        $objChildren = $objItem->ChildItemArray;
                        $intChildCount = count($objChildren);
                    }

                    for ($intIndex = 0; $intIndex < $intChildCount; $intIndex++) {
                        $objChildItem = $objChildren[$intIndex];
                        $strChildItemId = $this->strControlId . '_' . $objChildItem->ItemId;
                        $strSubNodeHtml .= sprintf('<div id="%s">%s</div>', $strChildItemId, $this->GetItemHtml($objChildItem, $strChildItemId));
                    }

                    $strSubNodeHtml = sprintf('<div id="%s_sub" style="margin-left: %spx;">%s</div>',
                        $strItemId,
                        $this->intIndentWidth,
                        $strSubNodeHtml);
                } else {
                    $strSubNodeHtml = sprintf('<div id="%s_sub" style="margin-left: %spx; display: none;"><span class="%s" style="cursor: auto;">%s...</span></div>',
                        $strItemId,
                        $this->intIndentWidth,
                        $this->strItemCssStyle,
                        QApplication::Translate('Loading')
                    );

                    $strCommand = sprintf('onclick="treenavToggleImage(\'%s\'); qc.pA(\'%s\', \'%s\', \'QTreeNav_Expand\', \'%s\')"',
                        $strItemId,
                        $this->objForm->FormId,
                        $this->strControlId,
                        $strItemId
                    );

                    $strImageHtml = sprintf('<span style="margin-right: 2px;"><img id="%s_image" src="%s/treenav_not_expanded.png" width="11" height="11" alt="" style="position: relative; top: 2px; cursor: pointer;" %s/></span>',
                        $strItemId, __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__, $strCommand);
                }

            } else {
                // No Children -- we are displaying an End Node
                $strImageHtml = '<span style="margin-right: 2px;"><img src="' . __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__ .
                    '/treenav_child.png" width="11" height="11" alt="" style="position: relative; top: 2px;"/></span>';
            }

            $strCommand = sprintf('onclick="qc.pA(\'%s\', \'%s\', \'QChangeEvent\', \'%s\', \'%s\')"',
                $this->objForm->FormId,
                $this->strControlId,
        $strItemId,
        $this->objForm->DefaultWaitIcon->ControlId
      );
            if($this->blnAllowHover)
                $strLabelHtml = sprintf('<span id="%s_label" class="%s" %s>%s</span>',
                    $strItemId,
                    ($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
                    $strItemId, ($objItem->Selected) ? $this->strItemSelectedHoverCssStyle : $this->strItemHoverCssStyle ,
                    $strItemId, ($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
                    $strCommand,
                    $objItem->Name);
            else
                $strLabelHtml = sprintf('<span id="%s_label" class="%s" %s>%s</span>',
                    $strItemId,
                    ($objItem->Selected) ? $this->strItemSelectedCssStyle : $this->strItemCssStyle,
                    $strCommand,
                    $objItem->Name);


            if ($this->intItemWidth > 0)
                return sprintf('<div style="height: %spx; width: %spx;">%s%s</div>%s',
                    $this->intItemHeight, $this->intItemWidth, $strImageHtml, $strLabelHtml, $strSubNodeHtml);
            else
                return sprintf('<div style="height: %spx;">%s%s</div>%s',
                    $this->intItemHeight, $strImageHtml, $strLabelHtml, $strSubNodeHtml);
        }

        public function AddChildItem(QTreeNavItem $objItem) {
            array_push($this->objChildItemArray, $objItem);
        }

        public function AddItem(QTreeNavItem $objItem) {
            if (array_key_exists($objItem->ItemId, $this->objItemArray))
                throw new QCallerException('Item Id already exists in QTreeNav ' . $this->strControlId . ': ' . $objItem->ItemId, 2);
            $this->objItemArray[$objItem->ItemId] = $objItem;
        }

        public function GetItem($strItemId) {
            if (strpos($strItemId, '_') !== false) {
                $intIndexArray = explode('_', $strItemId);
                $strItemId = $intIndexArray[1];
            }

            if (array_key_exists($strItemId, $this->objItemArray))
                return $this->objItemArray[$strItemId];
            else
                return null;
        }

        public function GenerateItemId() {
            $strToReturn = 'i' . $this->intNextItemId;
            $this->intNextItemId++;
            return $strToReturn;
        }

        public function Clear()
        {
            $this->objChildItemArray = array();
            $this->objItemArray = array();
            $this->intNextItemId = 1;
            $this->objSelectedTreeNavItem = null;
            $this->blnModified = true;
        }

        public function ParsePostData() {
            if (array_key_exists('Qform__FormControl', $_POST) && ($_POST['Qform__FormControl'] == $this->strControlId)) {
                if ($_POST['Qform__FormEvent'] == 'QChangeEvent') {
                    $strParameter = $_POST['Qform__FormParameter'];
                    $objItem = $this->GetItem($strParameter);

                    $this->SelectedItem = $objItem;

                    $strItemHtml = $this->GetItemHtml($objItem, $strParameter);
                    $strItemHtml = addslashes($strItemHtml);
                    QApplication::ExecuteJavaScript('treenavRedrawElement("' . $strParameter . '", "' . $strItemHtml . '")');
                } else if ($_POST['Qform__FormEvent'] == 'QTreeNav_Expand') {
                    $strParameter = $_POST['Qform__FormParameter'];
                    $objItem = $this->GetItem($strParameter);
                    $objItem->Expanded = true;

                    $strItemHtml = $this->GetItemHtml($objItem, $strParameter);
                    $strItemHtml = addslashes($strItemHtml);
                    QApplication::ExecuteJavaScript('treenavRedrawElement("' . $strParameter . '", "' . $strItemHtml . '")');
                }
            }
        }

        public function Validate() {
            // Check for Required
            if ($this->blnRequired && $this->SelectedItem === null)
            {
                if ($this->strName)
                    $this->ValidationError = sprintf($this->strLabelForRequired, $this->strName);
                else
                    $this->ValidationError = $this->strLabelForRequiredUnnamed;
                return false;
            }

            return true;
        }

        public function GetControlHtml() {
            $strAttributes = $this->GetAttributes();
            $strStyles = $this->GetStyleAttributes();

            if ($strStyles)
                $strStyles = sprintf(' style="%s"', $strStyles);

            $strItemHtml = '';
            foreach ($this->objChildItemArray as $objItem) {
                $strItemId = $this->strControlId . '_' . $objItem->ItemId;
                $strItemHtml .= '<div id="' . $strItemId . '">' . $this->GetItemHtml($objItem) . '</div>';
            }

            return sprintf('<div id="%s" %s%s>%s</div>',
                $this->strControlId,
                $strAttributes,
                $strStyles,
                $strItemHtml);
        }


        /////////////////////////
        // Public Properties: GET
        /////////////////////////
        public function __get($strName) {
            switch ($strName) {
                case "SelectedItem": return $this->objSelectedTreeNavItem;
                case "SelectedValue":
                    if ($this->objSelectedTreeNavItem)
                        return $this->objSelectedTreeNavItem->Value;
                    else
                        return null;
                case "ChildItemArray": return (array) $this->objChildItemArray;
                case "ItemArray": return (array) $this->objItemArray;

                case "ItemCssStyle": return $this->strItemCssStyle;
                case "ItemSelectedCssStyle": return $this->strItemSelectedCssStyle;
                case "ItemHoverCssStyle": return $this->strItemHoverCssStyle;

                case "IndentWidth": return $this->intIndentWidth;
                case "ItemHeight": return $this->intItemHeight;
                case "ItemWidth": return $this->intItemWidth;

                case "ExpandOnSelect": return $this->blnExpandOnSelect;

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

        /////////////////////////
        // Public Properties: SET
        /////////////////////////
        public function __set($strName, $mixValue) {
            switch ($strName) {
                case "ItemCssStyle":
                    try {
                        $this->strItemCssStyle = QType::Cast($mixValue, QType::String);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "ItemSelectedCssStyle":
                    try {
                        $this->strItemSelectedCssStyle = QType::Cast($mixValue, QType::String);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "ItemHoverCssStyle":
                    try {
                        $this->strItemHoverCssStyle = QType::Cast($mixValue, QType::String);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "IndentWidth":
                    try {
                        $this->intIndentWidth = QType::Cast($mixValue, QType::Integer);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "ItemHeight":
                    try {
                        $this->intItemHeight = QType::Cast($mixValue, QType::Integer);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "ItemWidth":
                    try {
                        $this->intItemWidth = QType::Cast($mixValue, QType::Integer);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }
                case "ExpandOnSelect":
                    try {
                        $this->blnExpandOnSelect = QType::Cast($mixValue, QType::Boolean);
                        break;
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }

                case "SelectedItem":
                    try {
                        $objItem = QType::Cast($mixValue, "QTreeNavItem");
                    } catch (QInvalidCastException $objExc) {
                        $objExc->IncrementOffset();
                        throw $objExc;
                    }

                    // If the currently selected item is $objItem, then do nothing
                    if ($objItem && $this->objSelectedTreeNavItem && ((string) $this->objSelectedTreeNavItem->ItemId == (string) $objItem->ItemId))
                        return $objItem;

                    // Deselect the Old (if applicable)
                    if ($this->objSelectedTreeNavItem) {
                        // if we are in an AJAX response scenario, we MUST remember to use a javascript update call
                        // to "deselect" the old selected item
                        QApplication::ExecuteJavaScript(sprintf("treenavItemUnselect('%s_%s_label', '%s')", $this->strControlId, $this->objSelectedTreeNavItem->ItemId, $this->strItemCssStyle));

                        // Update deselection in the form state, too
                        $this->objSelectedTreeNavItem->Selected = false;
                    }

                    if ($this->objSelectedTreeNavItem = $objItem) {
                        $objItem->Selected = true;

                        if ($this->blnExpandOnSelect)
                            $objItem->Expanded = true;

                        // Ensure that all parents and ancestors are expanded
                        $objParent = $this->GetItem($objItem->ParentItemId);

                        while ($objParent) {
                            $objParent->Expanded = true;
                            $objParent = $this->GetItem($objParent->ParentItemId);
                        }
                    }

                    return $objItem;

                case "ItemExpanded":
                    $strTokenArray = explode(' ', $mixValue);
                    $objItem = $this->GetItem($strTokenArray[0]);
                    $objItem->Expanded = $strTokenArray[1];
                    return $strTokenArray[1];

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