steeffeen/FancyManiaLinks

View on GitHub
FML/Script/Features/Paging.php

Summary

Maintainability
C
1 day
Test Coverage
F
44%
<?php

namespace FML\Script\Features;

use FML\Controls\Control;
use FML\Controls\Label;
use FML\Script\Builder;
use FML\Script\Script;
use FML\Script\ScriptInclude;
use FML\Script\ScriptLabel;

/**
 * Script Feature realizing a mechanism for browsing through Pages
 *
 * @author    steeffeen <mail@steeffeen.com>
 * @copyright FancyManiaLinks Copyright © 2017 Steffen Schröder
 * @license   http://www.gnu.org/licenses/ GNU General Public License, Version 3
 */
class Paging extends ScriptFeature
{

    /*
     * Constants
     */
    const VAR_CURRENT_PAGE             = "FML_Paging_CurrentPage";
    const FUNCTION_UPDATE_CURRENT_PAGE = "FML_UpdateCurrentPage";

    /**
     * @var Label $label Page number Label
     */
    protected $label = null;

    /**
     * @var PagingPage[] $pages Pages
     */
    protected $pages = array();

    /**
     * @var PagingButton[] $buttons Paging Buttons
     */
    protected $buttons = array();

    /**
     * @var int $startPageNumber Start Page number
     */
    protected $startPageNumber = null;

    /**
     * @var int $customMaxPageNumber Custom maximum page number
     */
    protected $customMaxPageNumber = null;

    /**
     * @var string $previousChunkAction Previous chunk action name
     */
    protected $previousChunkAction = null;

    /**
     * @var string $nextChunkAction Next chunk action name
     */
    protected $nextChunkAction = null;

    /**
     * @var bool $chunkActionAppendsPageNumber Chunk action appended with Page number
     */
    protected $chunkActionAppendsPageNumber = null;

    /**
     * Construct a new Paging
     *
     * @api
     * @param Label          $label   (optional) Page number Label
     * @param PagingPage[]   $pages   (optional) Pages
     * @param PagingButton[] $buttons (optional) Pageing Buttons
     */
    public function __construct(Label $label = null, array $pages = null, array $buttons = null)
    {
        if ($label) {
            $this->setLabel($label);
        }
        if ($pages) {
            $this->setPages($pages);
        }
        if ($buttons) {
            $this->setButtons($buttons);
        }
    }

    /**
     * Get the Label showing the Page number
     *
     * @api
     * @return Label
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * Set the Label showing the Page number
     *
     * @api
     * @param Label $label Page number Label
     * @return static
     */
    public function setLabel(Label $label)
    {
        $label->checkId();
        $this->label = $label;
        return $this;
    }

    /**
     * Get the Pages
     *
     * @api
     * @return PagingPage[]
     */
    public function getPages()
    {
        return $this->pages;
    }

    /**
     * Add a new Page Control
     *
     * @api
     * @param Control $pageControl Page Control
     * @param string  $pageNumber  (optional) Page number
     * @return static
     */
    public function addPageControl(Control $pageControl, $pageNumber = null)
    {
        if ($pageNumber === null) {
            $pageNumber = count($this->pages) + 1;
        }
        $page = new PagingPage($pageControl, $pageNumber);
        return $this->addPage($page);
    }

    /**
     * Add a new Page
     *
     * @api
     * @param PagingPage $page Page
     * @return static
     */
    public function addPage(PagingPage $page)
    {
        if (!in_array($page, $this->pages, true)) {
            array_push($this->pages, $page);
        }
        return $this;
    }

    /**
     * Add new Pages
     *
     * @api
     * @param PagingPage[] $pages Pages
     * @return static
     */
    public function setPages(array $pages)
    {
        $this->pages = array();
        foreach ($pages as $page) {
            $this->addPage($page);
        }
        return $this;
    }

    /**
     * Get the Buttons
     *
     * @api
     * @return PagingButton[]
     */
    public function getButtons()
    {
        return $this->buttons;
    }

    /**
     * Add a new Button Control to browse through the Pages
     *
     * @api
     * @param Control $buttonControl Button used for browsing
     * @param int     $browseAction  (optional) Number of browsed Pages per click
     * @return static
     */
    public function addButtonControl(Control $buttonControl, $browseAction = null)
    {
        if ($browseAction === null) {
            $buttonCount = count($this->buttons);
            if ($buttonCount % 2 === 0) {
                $browseAction = $buttonCount / 2 + 1;
            } else {
                $browseAction = $buttonCount / -2 - 1;
            }
        }
        $button = new PagingButton($buttonControl, $browseAction);
        return $this->addButton($button);
    }

    /**
     * Add a new Button to browse through Pages
     *
     * @api
     * @param PagingButton $button Paging Button
     * @return static
     */
    public function addButton(PagingButton $button)
    {
        if (!in_array($button, $this->buttons, true)) {
            array_push($this->buttons, $button);
        }
        return $this;
    }

    /**
     * Set the Paging Buttons
     *
     * @api
     * @param PagingButton[] $buttons Paging Buttons
     * @return static
     */
    public function setButtons(array $buttons)
    {
        $this->buttons = array();
        foreach ($buttons as $button) {
            $this->addButton($button);
        }
        return $this;
    }

    /**
     * Get the start Page number
     *
     * @api
     * @return int
     */
    public function getStartPageNumber()
    {
        return $this->startPageNumber;
    }

    /**
     * Set the start Page number
     *
     * @api
     * @param int $startPageNumber Page number to start with
     * @return static
     */
    public function setStartPageNumber($startPageNumber)
    {
        $this->startPageNumber = (int)$startPageNumber;
        return $this;
    }

    /**
     * Get a custom maximum Page number for using chunks
     *
     * @api
     * @return int
     */
    public function getCustomMaxPageNumber()
    {
        return $this->customMaxPageNumber;
    }

    /**
     * Set a custom maximum Page number for using chunks
     *
     * @api
     * @param int $maxPageNumber Custom maximum Page number
     * @return static
     */
    public function setCustomMaxPageNumber($maxPageNumber)
    {
        $this->customMaxPageNumber = (int)$maxPageNumber;
        return $this;
    }

    /**
     * Get the action triggered when the previous chunk is needed
     *
     * @api
     * @return string
     */
    public function getPreviousChunkAction()
    {
        return $this->previousChunkAction;
    }

    /**
     * Set the action triggered when the previous chunk is needed
     *
     * @api
     * @param string $previousChunkAction Triggered action
     * @return static
     */
    public function setPreviousChunkAction($previousChunkAction)
    {
        $this->previousChunkAction = (string)$previousChunkAction;
        return $this;
    }

    /**
     * Get the action triggered when the next chunk is needed
     *
     * @api
     * @return string
     */
    public function getNextChunkAction()
    {
        return $this->nextChunkAction;
    }

    /**
     * Set the action triggered when the next chunk is needed
     *
     * @api
     * @param string $nextChunkAction Triggered action
     * @return static
     */
    public function setNextChunkAction($nextChunkAction)
    {
        $this->nextChunkAction = (string)$nextChunkAction;
        return $this;
    }

    /**
     * Set the actions triggered when another chunk is needed
     *
     * @api
     * @param string $chunkAction Triggered action
     * @return static
     */
    public function setChunkActions($chunkAction)
    {
        return $this->setNextChunkAction($chunkAction)
                    ->setPreviousChunkAction($chunkAction);
    }

    /**
     * Get if the chunk action should append the needed Page number
     *
     * @api
     * @return bool
     */
    public function getChunkActionAppendsPageNumber()
    {
        return $this->chunkActionAppendsPageNumber;
    }

    /**
     * Set if the chunk action should append the needed Page number
     *
     * @api
     * @param bool $appendPageNumber Append the needed Page number
     * @return static
     */
    public function setChunkActionAppendsPageNumber($appendPageNumber)
    {
        $this->chunkActionAppendsPageNumber = (bool)$appendPageNumber;
        return $this;
    }

    /**
     * @see ScriptFeature::prepare()
     */
    public function prepare(Script $script)
    {
        if (empty($this->pages)) {
            return $this;
        }
        $script->setScriptInclude(ScriptInclude::TEXTLIB);

        $currentPageVariable = self::VAR_CURRENT_PAGE;
        $updatePageFunction  = self::FUNCTION_UPDATE_CURRENT_PAGE;

        $minPageNumber   = 1;
        $startPageNumber = (is_int($this->startPageNumber) ? $this->startPageNumber : $minPageNumber);
        $maxPage         = $this->getMaxPage();
        $maxPageNumber   = $this->customMaxPageNumber;
        if (!is_int($maxPageNumber)) {
            $maxPageNumber = $maxPage->getPageNumber();
        }

        $pagingId    = $maxPage->getControl()
                               ->getId(true, true);
        $pagingId    = Builder::escapeText($maxPage->getControl()
                                                   ->getId());
        $pageLabelId = Builder::EMPTY_STRING;
        if ($this->label) {
            $pageLabelId = Builder::escapeText($this->label->getId());
        }

        $pagesArrayText       = $this->getPagesArrayText();
        $pageButtonsArrayText = $this->getPageButtonsArrayText();

        $previousChunkAction          = Builder::escapeText($this->previousChunkAction);
        $nextChunkAction              = Builder::escapeText($this->nextChunkAction);
        $chunkActionAppendsPageNumber = Builder::getBoolean($this->chunkActionAppendsPageNumber);

        // Init
        $initScriptText = "
declare {$currentPageVariable} for This = Integer[Text];
{$currentPageVariable}[{$pagingId}] = {$startPageNumber};
{$updatePageFunction}({$pagingId}, {$pageLabelId}, 0, {$minPageNumber}, {$maxPageNumber}, {$pagesArrayText}, {$previousChunkAction}, {$nextChunkAction}, {$chunkActionAppendsPageNumber});";
        $script->appendGenericScriptLabel(ScriptLabel::ONINIT, $initScriptText, true);

        // MouseClick
        $clickScriptText = "
declare PageButtons = {$pageButtonsArrayText};
if (PageButtons.existskey(Event.Control.ControlId)) {
    declare BrowseAction = PageButtons[Event.Control.ControlId];
    {$updatePageFunction}({$pagingId}, {$pageLabelId}, BrowseAction, {$minPageNumber}, {$maxPageNumber}, {$pagesArrayText}, {$previousChunkAction}, {$nextChunkAction}, {$chunkActionAppendsPageNumber});
}";
        $script->appendGenericScriptLabel(ScriptLabel::MOUSECLICK, $clickScriptText, true);

        // Update function
        $functionText = "
Void {$updatePageFunction}(Text _PagingId, Text _PageLabelId, Integer _BrowseAction, Integer _MinPageNumber, Integer _MaxPageNumber, Text[Integer] _Pages, Text _PreviousChunkAction, Text _NextChunkAction, Boolean _ChunkActionAppendPageNumber) {
    declare {$currentPageVariable} for This = Integer[Text];
    if (!{$currentPageVariable}.existskey(_PagingId)) return;
    declare NewPageNumber = {$currentPageVariable}[_PagingId] + _BrowseAction;
    if (NewPageNumber < _MinPageNumber) {
        NewPageNumber = _MinPageNumber;
    } else if (NewPageNumber > _MaxPageNumber) {
        NewPageNumber = _MaxPageNumber;
    }
    {$currentPageVariable}[_PagingId] = NewPageNumber;
    if (_Pages.existskey(NewPageNumber)) {
        foreach (PageNumber => PageId in _Pages) {
            declare PageControl <=> Page.GetFirstChild(PageId);
            PageControl.Visible = (PageNumber == NewPageNumber);
        }
        if (_PageLabelId != \"\") {
            declare PageLabel <=> (Page.GetFirstChild(_PageLabelId) as CMlLabel);
            PageLabel.Value = NewPageNumber^\"/\"^_MaxPageNumber;
        }
    } else {
        declare Text ChunkAction;
        if (_BrowseAction < 0) {
            ChunkAction = _PreviousChunkAction;
        } else {
            ChunkAction = _NextChunkAction;
        }
        if (_ChunkActionAppendPageNumber) {
            ChunkAction ^= NewPageNumber;
        }
        TriggerPageAction(ChunkAction);
    }
}";
        $script->addScriptFunction($updatePageFunction, $functionText);
        return $this;
    }

    /**
     * Get the minimum Page
     *
     * @return PagingPage
     */
    protected function getMinPage()
    {
        $minPageNumber = null;
        $minPage       = null;
        foreach ($this->pages as $page) {
            $pageNumber = $page->getPageNumber();
            if ($minPageNumber === null || $pageNumber < $minPageNumber) {
                $minPageNumber = $pageNumber;
                $minPage       = $page;
            }
        }
        return $minPage;
    }

    /**
     * Get the maximum Page
     *
     * @return PagingPage
     */
    protected function getMaxPage()
    {
        $maxPageNumber = null;
        $maxPage       = null;
        foreach ($this->pages as $page) {
            $pageNumber = $page->getPageNumber();
            if ($maxPageNumber === null || $pageNumber > $maxPageNumber) {
                $maxPageNumber = $pageNumber;
                $maxPage       = $page;
            }
        }
        return $maxPage;
    }

    /**
     * Build the array text for the Pages
     *
     * @return string
     */
    protected function getPagesArrayText()
    {
        if (empty($this->pages)) {
            return Builder::getArray(array(0 => ''), true);
        }
        $pages = array();
        foreach ($this->pages as $page) {
            $pages[$page->getPageNumber()] = $page->getControl()
                                                  ->getId();
        }
        return Builder::getArray($pages, true);
    }

    /**
     * Build the array text for the Page Buttons
     *
     * @return string
     */
    protected function getPageButtonsArrayText()
    {
        if (empty($this->buttons)) {
            return Builder::getArray(array('' => 0), true);
        }
        $pageButtons = array();
        foreach ($this->buttons as $pageButton) {
            $pageButtons[$pageButton->getControl()
                                    ->getId()] = $pageButton->getPagingCount();
        }
        return Builder::getArray($pageButtons, true);
    }

}