<?php
namespace Perry\Representation\Eve\v1;

use \Perry\Representation\Reference as Reference;
use \Perry\Representation\Uri as Uri;
use \Perry\Representation\Base as Base;

class TournamentSeries extends Base
{
    public $matchesInProgress = [];

    public $redTeam;

    public $matchesWon;

    public $matches;

    public $self;

    public $winner;

    public $loser;

    public $length;

    public $blueTeam;

    public $structure;

    // by Warringer\Types\ArrayType
    public function setMatchesInProgress($matchesInProgress)
    {
        // by Warringer\Types\Reference
        $func = function ($value) { return new Reference($value); };

        foreach ($matchesInProgress as $key => $value) {
            $this->matchesInProgress[$key] = $func($value);
        }
    }

    // by Warringer\Types\Dict
    public function setRedTeam($redTeam)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['team'] = function ($value) { return new Reference($value); };
        $converters['isDecided'] = function ($value) { return $value; };
        $converters['isBye'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['team'] = isset($value->{'team'}) ? $converters['team']($value->{'team'}) : null;
            $return['isDecided'] = isset($value->{'isDecided'}) ? $converters['isDecided']($value->{'isDecided'}) : null;
            $return['isBye'] = isset($value->{'isBye'}) ? $converters['isBye']($value->{'isBye'}) : null;
            return $return;
        };
        $this->redTeam = $func($redTeam);
    }

    // by Warringer\Types\Dict
    public function setMatchesWon($matchesWon)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['redTeam'] = function ($value) { return $value; };
        $converters['blueTeam'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['redTeam'] = isset($value->{'redTeam'}) ? $converters['redTeam']($value->{'redTeam'}) : null;
            $return['blueTeam'] = isset($value->{'blueTeam'}) ? $converters['blueTeam']($value->{'blueTeam'}) : null;
            return $return;
        };
        $this->matchesWon = $func($matchesWon);
    }

    // by Warringer\Types\Reference
    public function setMatches($matches)
    {
        $this->matches = new Reference($matches);
    }

    // by Warringer\Types\Reference
    public function setSelf($self)
    {
        $this->self = new Reference($self);
    }

    // by Warringer\Types\Dict
    public function setWinner($winner)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['team'] = function ($value) { return new Reference($value); };
        $converters['isDecided'] = function ($value) { return $value; };
        $converters['isBye'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['team'] = isset($value->{'team'}) ? $converters['team']($value->{'team'}) : null;
            $return['isDecided'] = isset($value->{'isDecided'}) ? $converters['isDecided']($value->{'isDecided'}) : null;
            $return['isBye'] = isset($value->{'isBye'}) ? $converters['isBye']($value->{'isBye'}) : null;
            return $return;
        };
        $this->winner = $func($winner);
    }

    // by Warringer\Types\Dict
    public function setLoser($loser)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['team'] = function ($value) { return new Reference($value); };
        $converters['isDecided'] = function ($value) { return $value; };
        $converters['isBye'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['team'] = isset($value->{'team'}) ? $converters['team']($value->{'team'}) : null;
            $return['isDecided'] = isset($value->{'isDecided'}) ? $converters['isDecided']($value->{'isDecided'}) : null;
            $return['isBye'] = isset($value->{'isBye'}) ? $converters['isBye']($value->{'isBye'}) : null;
            return $return;
        };
        $this->loser = $func($loser);
    }

    // by Warringer\Types\Long
    public function setLength($length)
    {
        $this->length = $length;
    }

    // by Warringer\Types\Dict
    public function setBlueTeam($blueTeam)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['team'] = function ($value) { return new Reference($value); };
        $converters['isDecided'] = function ($value) { return $value; };
        $converters['isBye'] = function ($value) { return $value; };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['team'] = isset($value->{'team'}) ? $converters['team']($value->{'team'}) : null;
            $return['isDecided'] = isset($value->{'isDecided'}) ? $converters['isDecided']($value->{'isDecided'}) : null;
            $return['isBye'] = isset($value->{'isBye'}) ? $converters['isBye']($value->{'isBye'}) : null;
            return $return;
        };
        $this->blueTeam = $func($blueTeam);
    }

    // by Warringer\Types\Dict
    public function setStructure($structure)
    {
        // by Warringer\Types\Dict
        $converters = [];
        $converters['outgoingLoser'] = function ($value) { return new Reference($value); };
        $converters['outgoingWinner'] = function ($value) { return new Reference($value); };
        $converters['incomingRed'] = function ($value) { return new Reference($value); };
        $converters['incomingBlue'] = function ($value) { return new Reference($value); };

        $func = function ($value) use($converters) {
            $return = new \ArrayObject($value, \ArrayObject::ARRAY_AS_PROPS);
            $return['outgoingLoser'] = isset($value->{'outgoingLoser'}) ? $converters['outgoingLoser']($value->{'outgoingLoser'}) : null;
            $return['outgoingWinner'] = isset($value->{'outgoingWinner'}) ? $converters['outgoingWinner']($value->{'outgoingWinner'}) : null;
            $return['incomingRed'] = isset($value->{'incomingRed'}) ? $converters['incomingRed']($value->{'incomingRed'}) : null;
            $return['incomingBlue'] = isset($value->{'incomingBlue'}) ? $converters['incomingBlue']($value->{'incomingBlue'}) : null;
            return $return;
        };
        $this->structure = $func($structure);
    }

}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Services\Directions;

use Ivory\GoogleMap\Exception\DirectionsException;

/**
 * A directions response wraps the directions results (routes) & the response status.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class DirectionsResponse
{
    /** @var array */
    protected $routes;

    /** @var string */
    protected $status;

    /**
     * Create a directions response.
     *
     * @param array  $routes The response routes.
     * @param string $status The response status.
     */
    public function __construct(array $routes, $status)
    {
        $this->setRoutes($routes);
        $this->setStatus($status);
    }

    /**
     * Gets the directions routes.
     *
     * @return array The directions routes.
     */
    public function getRoutes()
    {
        return $this->routes;
    }

    /**
     * Sets the directions routes.
     *
     * @param array $routes The directions routes.
     */
    public function setRoutes(array $routes)
    {
        $this->routes = array();

        foreach ($routes as $route) {
            $this->addRoute($route);
        }
    }

    /**
     * Add a directions route.
     *
     * @param Ivory\GoogleMapBundle\Model\Services\Directions\DirectionsRoute $route The route to add.
     */
    public function addRoute(DirectionsRoute $route)
    {
        $this->routes[] = $route;
    }

    /**
     * Gets the directions response status.
     *
     * @return string The directions response status.
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * Sets the directions response status.
     *
     * @param string $status The directions response status.
     *
     * @throws \Ivory\GoogleMap\Exception\DirectionsException If the status is not valid.
     */
    public function setStatus($status)
    {
        if (!in_array($status, DirectionsStatus::getDirectionsStatus())) {
            throw DirectionsException::invalidDirectionsResponseStatus();
        }

        $this->status = $status;
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Controls;

use Ivory\GoogleMap\Controls\OverviewMapControl;
use Ivory\GoogleMap\Helper\AbstractHelper;

/**
 * Overview map control helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class OverviewMapControlHelper extends AbstractHelper
{
    /**
     * Renders an overview map control.
     *
     * @param \Ivory\GoogleMap\Controls\OverviewMapControl $overviewMapControl The overview map control.
     *
     * @return string The JS output.
     */
    public function render(OverviewMapControl $overviewMapControl)
    {
        return $this->jsonBuilder
            ->reset()
            ->setValue('[opened]', $overviewMapControl->isOpened())
            ->build();
    }
}


<?php
//------------------------
//· 模板类
//-------------------------
namespace denha;

class Template
{
    public $left  = '{';
    public $right = '}';
    public $viewPath;
    public $loadPath;
    public $content;
    public $editTime; // 最新修改时间

    public function __construct($viewPath)
    {
        $this->viewPath = $viewPath;
    }

    public function getContent()
    {
        $file          = fopen($this->viewPath, 'r');
        $this->content = fread($file, filesize($this->viewPath));
        $this->stampInclude();

        $this->stampForeach();

        //{$xxx} be echo $xxx;
        $this->content = preg_replace('/' . $this->left . '\$(.*?)' . $this->right . '/is', '<?php echo $\1; ?>', $this->content);
        //{default:$xxx|"xxx"}
        $this->content = preg_replace('/' . $this->left . 'default:\$(.*?)[|](.*?)' . $this->right . '/is', '<?php echo isset($\1) ? $\1 : \2 ; ?>', $this->content);
        //??$xx  be !isset($xx) ?: $xx
        $this->content = preg_replace('/' . $this->left . '\?\?(.*?)' . $this->right . '/is', '<?php echo !isset(\1) ? null : \1; ?>', $this->content);
        //机器翻译标签 {FY:xxx:en}
        $this->content = preg_replace('/' . $this->left . 'FY:\$(.*?):(.*?)' . $this->right . '/is', '<?php echo \'{FY:\'.$\1.\':\2}\'; ?>', $this->content);
        //替换php函数 {F:XXX}  be echo XXX;
        $this->content = preg_replace('/' . $this->left . 'F:(.*?)' . $this->right . '/', '<?php echo \1; ?>', $this->content); //替换php函数 {:XXX}  be echo XXX;
        $this->content = preg_replace('/' . $this->left . ':(.*?)' . $this->right . '/', '<?php echo \1; ?>', $this->content);
        //替换{if XXX}
        $this->content = preg_replace('/' . $this->left . 'if(.*?)' . $this->right . '/is', '<?php if(\1){; ?>', $this->content);
        //替换{else}
        $this->content = preg_replace('/' . $this->left . 'else' . $this->right . '/is', '<?php }else{ ?>', $this->content);
        //替换{elseif XXX}
        $this->content = preg_replace('/' . $this->left . 'elseif(.*?)' . $this->right . '/is', '<?php }elseif(\1){ ?>', $this->content);
        //替换{/if}
        $this->content = preg_replace('/' . $this->left . '\/if' . $this->right . '/is', '<?php } ?>', $this->content);

        // 替换配置字符串
        $replaceStr = config('view_replace_str');
        foreach ($replaceStr as $key => $value) {
            if (strpos($this->content, $key) !== false) {
                $this->content = str_replace($key, $value, $this->content);
            }
        }

        $this->saveFile();

    }

    // 获取最新文件修改时间
    public function editTimeDiff($time)
    {
        if (!$this->editTime) {
            $this->editTime = $time;
        }

        if ($this->editTime < $time) {
            $this->editTime = $time;
        }

        return $this->editTime;
    }

    public function saveFile()
    {
        //如果没有写入权限尝试修改权限 如果修改后还是失败 则跳过
        if (!isWritable(DATA_PATH)) {
            return false;
        }

        is_dir(DATA_TPL_PATH) ? '' : mkdir(DATA_TPL_PATH, 0755, true);

        $cacheMd5       = md5($this->viewPath);
        $this->loadPath = DATA_TPL_PATH . $cacheMd5 . '.php';
        $file           = fopen($this->loadPath, 'w');
        fwrite($file, $this->content);
        fclose($file);

        // $filemtime = $this->editTimeDiff(filemtime($this->viewPath)); // 获取最新同步修改时间
        $filemtime = filemtime($this->viewPath);

        touch($this->loadPath, $filemtime, $filemtime); // 更新缓存模板时间

    }

    public function stampInclude()
    {
        $regular = '#' . $this->left . 'include\s(.*?)' . $this->right . '#is';
        preg_match_all($regular, $this->content, $matches);
        if ($matches) {
            foreach ($matches[1] as $key => $value) {
                $value = trim(str_replace('/', DS, $value));
                if (!$value) {
                    $path = VIEW_PATH . DS . MODULE . DS . parseName(CONTROLLER, false) . DS . ACTION . '.html';
                }
                //绝对路径 appliaction目录开始
                elseif (stripos($value, DS) === 0) {
                    $path = VIEW_PATH . $value . '.html';
                }
                //相对路径
                else {
                    $path = VIEW_PATH . DS . MODULE . DS . $value . '.html';
                }

                $path = str_replace('\\', DS, $path);

                if (is_file($path)) {
                    $file      = fopen($path, 'r');
                    $content   = fread($file, filesize($path));
                    $filemtime = filemtime($path);

                    // 保存模板Include标签文件更新时间
                    // $cacheTime = cache('template_include_cache_time');
                    // $cacheTime = is_array($cacheTime) ? $cacheTime : [];

                    // $cacheTime['view'][md5($this->viewPath)][md5($path)] = $filemtime;
                    // $cacheTime['include'][md5($path)]                    = $path;
                    // cache('template_include_cache_time', $cacheTime);

                    // // 最新修改时间
                    // $this->editTimeDiff(filemtime($path));

                    //替换模板变量
                    $this->content = str_replace($matches[0][$key], $content, $this->content);
                    // 循环模板
                    $this->stampInclude();
                } else {

                }
            }
        }

    }

    public function stampForeach()
    {
        $regular = '#' . $this->left . 'loop\s(.*?)\s(.*?)' . $this->right . '#is';
        preg_match_all($regular, $this->content, $matches);
        if ($matches) {
            foreach ($matches[0] as $key => $value) {
                if (stripos($matches[2][$key], ' ') !== false) {
                    $rowTmp        = explode(' ', $matches[2][$key]);
                    $this->content = str_replace($matches[0][$key], '<?php if(!empty(' . $matches[1][$key] . ')){ foreach(' . $matches[1][$key] . ' as ' . trim($rowTmp[0]) . ' => ' . trim($rowTmp[1]) . '){ ?>', $this->content);
                } else {
                    $this->content = str_replace($matches[0][$key], '<?php if(!empty(' . $matches[1][$key] . ')){ foreach(' . $matches[1][$key] . ' as ' . $matches[2][$key] . '){ ?>', $this->content);
                }
            }

            $this->content = str_replace($this->left . '/loop' . $this->right, '<?php }} ?>', $this->content);
        }
    }
}


<?php

namespace webignition\InternetMediaType\Parser;

/**
 * Attempts to fix unparseable internet media types based purely on
 * observed invalid media type strings that, upon visual observation, can
 * be translated into something sensible
 */
class TypeFixer
{
    const COMMA_SEPARATED_TYPE_SEPARATOR = ', ';
    const TYPE_SUBTYPE_SEPARATOR = '/';

    /**
     * @var string
     */
    private $inputString;

    /**
     * @var int
     */
    private $position;

    public function setInputString(string $inputString)
    {
        $this->inputString = $inputString;
    }

    public function setPosition(int $position)
    {
        $this->position = $position;
    }

    public function fix(): ?string
    {
        return $this->getLongestString(array(
            $this->commaSeparatedTypeFix(),
            $this->spaceSeparatingTypeAndAttributeFix()
        ));
    }

    /**
     * Attempt to fix media types that are formatted as:
     *
     * type/subtype, type/subtype
     *
     * i.e. two media types comma-separated together
     *
     * If of this type, go for the longest valid option
     */
    private function commaSeparatedTypeFix(): ?string
    {
        if ($this->position === 0) {
            return null;
        }

        $separatorComparator = substr($this->inputString, $this->position - 1, 2);
        if ($separatorComparator !== self::COMMA_SEPARATED_TYPE_SEPARATOR) {
            return null;
        }

        $possibleTypeSubtypes = array(
            substr($this->inputString, 0, $this->position - 1),
            substr($this->inputString, $this->position + 1)
        );

        $typeSubtypes = array();

        foreach ($possibleTypeSubtypes as $possibleTypeSubtype) {
            $typeSubtype = $this->getTypeSubtypeFromPossibleTypeSubtype($possibleTypeSubtype);

            if (!in_array($typeSubtype, $typeSubtypes) && !is_null($typeSubtype)) {
                $typeSubtypes[] = $typeSubtype;
            }
        }

        return $this->getLongestString($typeSubtypes);
    }

    /**
     * @param string[] $strings
     *
     * @return string
     */
    private function getLongestString(array $strings): ?string
    {
        return array_reduce($strings, function ($a, $b) {
            return strlen($a) > strlen($b) ? $a : $b;
        });
    }

    private function getTypeSubtypeFromPossibleTypeSubtype(string $possibleTypeSubtype): ?string
    {
        try {
            $typeParser = new TypeParser();
            $type = $typeParser->parse($possibleTypeSubtype);

            $subtypeParser = new SubtypeParser();
            $subtype = $subtypeParser->parse($possibleTypeSubtype);

            return $type . self::TYPE_SUBTYPE_SEPARATOR . $subtype;
        } catch (\Exception $ex) {
            return null;
        }
    }

    /**
     * Attempt to fix media types that are formatted as:
     *
     * type/subtype attribute=value
     *
     * i.e. a media type and parameters separated by a space not a semicolon
     */
    private function spaceSeparatingTypeAndAttributeFix(): ?string
    {
        if ($this->position === 0) {
            return null;
        }

        if ($this->inputString[$this->position] !== ' ') {
            return null;
        }

        return $this->getTypeSubtypeFromPossibleTypeSubtype(
            trim(substr(
                $this->inputString,
                0,
                $this->position
            ), "\t\n\r\0\x0B,") . ';' . substr($this->inputString, $this->position + 1)
        );
    }
}


<?php
namespace Perry\Representation\Eve\v1;

use \Perry\Representation\Reference as Reference;
use \Perry\Representation\Uri as Uri;
use \Perry\Representation\Base as Base;

class ItemTypeCollection extends Base
{
    public $pageCount;

    public $items = [];

    public $totalCount;

    public $next;

    public $previous;

    // by Warringer\Types\Long
    public function setPageCount($pageCount)
    {
        $this->pageCount = $pageCount;
    }

    // by Warringer\Types\ArrayType
    public function setItems($items)
    {
        // by Warringer\Types\Reference
        $func = function ($value) { return new Reference($value); };

        foreach ($items as $key => $value) {
            $this->items[$key] = $func($value);
        }
    }

    // by Warringer\Types\Long
    public function setTotalCount($totalCount)
    {
        $this->totalCount = $totalCount;
    }

    // by Warringer\Types\Reference
    public function setNext($next)
    {
        $this->next = new Reference($next);
    }

    // by Warringer\Types\Reference
    public function setPrevious($previous)
    {
        $this->previous = new Reference($previous);
    }

}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Base;

use Ivory\GoogleMap\Base\Bound;
use Ivory\GoogleMap\Overlays\Circle;
use Ivory\GoogleMap\Overlays\EncodedPolyline;
use Ivory\GoogleMap\Overlays\GroundOverlay;
use Ivory\GoogleMap\Overlays\InfoWindow;
use Ivory\GoogleMap\Overlays\Marker;
use Ivory\GoogleMap\Overlays\Polyline;
use Ivory\GoogleMap\Overlays\Polygon;
use Ivory\GoogleMap\Overlays\Rectangle;

/**
 * Bound helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class BoundHelper
{
    /**
     * Renders the bound.
     *
     * @param \Ivory\GoogleMap\Base\Bound $bound The bound.
     *
     * @return string The JS output.
     */
    public function render(Bound $bound)
    {
        if ($bound->hasExtends() || !$bound->hasCoordinates()) {
            return sprintf('%s = new google.maps.LatLngBounds();'.PHP_EOL, $bound->getJavascriptVariable());
        }

        return sprintf(
            '%s = new google.maps.LatLngBounds(%s, %s);'.PHP_EOL,
            $bound->getJavascriptVariable(),
            $bound->getSouthWest()->getJavascriptVariable(),
            $bound->getNorthEast()->getJavascriptVariable()
        );
    }

    /**
     * Renders the bound's extend of a marker.
     *
     * @param \Ivory\GoogleMap\Base\Bound $bound The bound.
     *
     * @return string The JS output.
     */
    public function renderExtends(Bound $bound)
    {
        $output = array();

        foreach ($bound->getExtends() as $extend) {
            if (($extend instanceof Marker) || ($extend instanceof InfoWindow)) {
                $output[] = sprintf(
                    '%s.extend(%s.getPosition());'.PHP_EOL,
                    $bound->getJavascriptVariable(),
                    $extend->getJavascriptVariable()
                );
            } elseif (($extend instanceof Polyline)
                || ($extend instanceof EncodedPolyline)
                || ($extend instanceof Polygon)
            ) {
                $output[] = sprintf(
                    '%s.getPath().forEach(function(element){%s.extend(element)});'.PHP_EOL,
                    $extend->getJavascriptVariable(),
                    $bound->getJavascriptVariable()
                );
            } elseif (($extend instanceof Rectangle) || ($extend instanceof GroundOverlay)) {
                $output[] = sprintf(
                    '%s.union(%s);'.PHP_EOL,
                    $bound->getJavascriptVariable(),
                    $extend->getBound()->getJavascriptVariable()
                );
            } elseif ($extend instanceof Circle) {
                $output[] = sprintf(
                    '%s.union(%s.getBounds());'.PHP_EOL,
                    $bound->getJavascriptVariable(),
                    $extend->getJavascriptVariable()
                );
            }
        }

        return implode('', $output);
    }
}


<?php
/**
 * Benchmark utilities.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare (strict_types = 1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * Benchmark utilities.
 *
 * @since 150424 Benchmarking.
 */
class Benchmark extends Classes\Core\Base\Core
{
    /**
     * Start time.
     *
     * @since 150424
     *
     * @var float
     */
    protected $started = 0;

    /**
     * Stop time.
     *
     * @since 150424
     *
     * @var float
     */
    protected $stopped = 0;

    /**
     * Elapsed time.
     *
     * @since 150424
     *
     * @var float
     */
    protected $this_time = 0;

    /**
     * Last elapsed time.
     *
     * @since 150424
     *
     * @var float
     */
    protected $last_time = 0;

    /**
     * Stats.
     *
     * @since 150424
     *
     * @var string[]
     */
    protected $stats = [];

    /**
     * Class constructor.
     *
     * @since 150424 Initial release.
     *
     * @param Classes\App $App Instance of App.
     */
    public function __construct(Classes\App $App)
    {
        parent::__construct($App);

        if (!$this->c::isCli()) {
            throw $this->c::issue('Requires CLI mode.');
        }
    }

    /**
     * Start (initialize).
     *
     * @since 150424 Benchmarking.
     *
     * @param bool $save_last Save last time?
     */
    public function start(bool $save_last = true)
    {
        $this->stats = [];

        if ($save_last) {
            $this->last_time = $this->this_time;
        }
        $this->this_time = 0; // Init.
        $this->started   = microtime(true);
    }

    /**
     * Stop (and print).
     *
     * @since 150424 Benchmarking.
     *
     * @param bool $full_report Full report?
     */
    public function print(bool $full_report = false)
    {
        $this->stopped   = microtime(true);
        $this->this_time = $this->stopped - $this->started;

        if ($this->last_time) {
            $_percent_diff = $this->c::percentDiff($this->last_time, $this->this_time, 2);
            $_percent_diff = $_percent_diff > 0 ? abs($_percent_diff).'% slower' : abs($_percent_diff).'% faster';

            $this->addStatistic('Processing Time', number_format($this->this_time, 8, '.', ''));
            $this->addStatistic('Time Comparison', $_percent_diff.' than '.number_format($this->last_time, 8, '.', '')."\n");
            unset($_percent_diff); // Housekeeping.
        } else {
            $this->addStatistic('Processing Time', number_format($this->this_time, 8, '.', '')."\n");
        }
        if ($full_report) {
            $this->addStatistic('Memory', $this->c::bytesToAbbr(memory_get_usage()));
            $this->addStatistic('Peak Memory', $this->c::bytesToAbbr(memory_get_peak_usage())."\n");

            $this->addStatistic('PHP Version', PHP_VERSION);
            $this->addStatistic('PCRE Version', PCRE_VERSION."\n");

            $this->addStatistic('Di Version', Classes\Di::VERSION);
            $this->addStatistic('Core Version', Classes\App::VERSION);
            $this->addStatistic('App Version', $this->App::VERSION."\n");

            $this->addStatistic('Locale', setlocale(LC_ALL, 0));
            $this->addStatistic('Charset', ini_get('default_charset')."\n");

            $this->addStatistic('Script Basename', basename($GLOBALS['argv'][0]));
        }
        $this->printAllStatistics(); // Print all statistics.
    }

    /**
     * Print statistics.
     *
     * @since 150424 Benchmarking.
     */
    protected function printAllStatistics()
    {
        $longest_label_chars = 0; // Initialize.

        foreach ($this->stats as $_label => $_value) {
            $longest_label_chars = max($longest_label_chars, mb_strlen($_label));
        }
        echo "\n\n"; // Two lines down...
        echo '- Benchmark ----------------------------------------'."\n";

        $statistics = ''; // Initialize.

        foreach ($this->stats as $_label => $_value) {
            $_label_chars     = mb_strlen($_label);
            $_alignment_chars = $longest_label_chars - $_label_chars;
            $statistics .= $_label.':    '.str_repeat(' ', $_alignment_chars).$_value."\n";
        } // unset($_label_chars, $_alignment_chars);

        echo $this->c::mbTrim($statistics)."\n";

        echo '----------------------------------------------------'."\n\n";
    }

    /**
     * Add statistic.
     *
     * @since 150424 Benchmarking.
     *
     * @param string $label Label for this statistic.
     * @param string $value Value for this statistic.
     */
    protected function addStatistic(string $label, $value)
    {
        $this->stats[$label] = (string) $value;
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Overlays;

use Ivory\GoogleMap\Assets\AbstractOptionsAsset;

/**
 * Marker Cluster.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class MarkerCluster extends AbstractOptionsAsset
{
    /** @const string The default marker cluster type */
    const _DEFAULT = 'default';

    /** @const string The javascript marker cluster type */
    const MARKER_CLUSTER = 'marker_cluster';

    /** @var string */
    protected $type;

    /** @var array */
    protected $markers;

    /**
     * Creates a marker cluster.
     */
    public function __construct()
    {
        parent::__construct();

        $this->setPrefixJavascriptVariable('marker_cluster_');
        $this->setType(self::_DEFAULT);
        $this->markers = array();
    }

    /**
     * Gets the marker cluster type.
     *
     * @return string The marker cluster type.
     */
    public function getType()
    {
        return $this->type;
    }

    /**
     * Sets the marker cluster type.
     *
     * @param string $type The marker cluster type.
     */
    public function setType($type)
    {
        $this->type = $type;
    }

    /**
     * Checks if the cluster has marker.
     *
     * @return boolean TRUE if the cluster has marker else FALSE.
     */
    public function hasMarkers()
    {
        return !empty($this->markers);
    }

    /**
     * Gets the cluster markers.
     *
     * @return array The cluster markers.
     */
    public function getMarkers()
    {
        return $this->markers;
    }

    /**
     * Sets the cluster markers.
     *
     * @param array $markers The cluster markers.
     */
    public function setMarkers($markers)
    {
        $this->markers = array();

        foreach ($markers as $marker) {
            $this->addMarker($marker);
        }
    }

    /**
     * Adds a marker to the cluster.
     *
     * @param \Ivory\GoogleMap\Overlays\Marker $marker The marker to add.
     */
    public function addMarker(Marker $marker)
    {
        $this->markers[] = $marker;
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper\Overlays\MarkerCluster;

use Ivory\GoogleMap\Exception\HelperException;
use Ivory\GoogleMap\Map;
use Ivory\GoogleMap\Overlays\MarkerCluster;

/**
 * Marker cluster helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class MarkerClusterHelper implements MarkerClusterHelperInterface
{
    /** @var array */
    protected $helpers;

    /**
     * Creates a marker cluster helper.
     */
    public function __construct(array $helpers = array())
    {
        if (empty($helpers)) {
            $helpers = array(
                MarkerCluster::_DEFAULT       => new DefaultMarkerClusterHelper(),
                MarkerCluster::MARKER_CLUSTER => new JsMarkerClusterHelper(),
            );
        }

        $this->setHelpers($helpers);
    }

    /**
     * Checks if the marker cluster helper has helpers.
     *
     * @return boolean TRUE if the marker cluster helper has helpers else FALSE.
     */
    public function hasHelpers()
    {
        return !empty($this->helpers);
    }

    /**
     * Gets the marker cluster helper helpers.
     *
     * @return array The marker cluster helper helpers.
     */
    public function getHelpers()
    {
        return $this->helpers;
    }

    /**
     * Sets the marker cluster helper helpers.
     *
     * @param array $helpers The marker cluster helper helpers.
     */
    public function setHelpers(array $helpers)
    {
        $this->helpers = array();

        foreach ($helpers as $name => $helper) {
            $this->setHelper($name, $helper);
        }
    }

    /**
     * Checks if the marker cluster helper has a specific helper.
     *
     * @param string $name The marker cluster type.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface The marker cluster helper.
     */
    public function hasHelper($name)
    {
        return isset($this->helpers[$name]);
    }

    /**
     * Gets a specific marker cluster helper.
     *
     * @param string $name The marker cluster type.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the helper does not exist.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface The marker cluster helper.
     */
    public function getHelper($name)
    {
        if (!$this->hasHelper($name)) {
            throw HelperException::invalidMarkerClusterHelper();
        }

        return $this->helpers[$name];
    }

    /**
     * Sets a specific marker cluster helper.
     *
     * @param string                                                                      $name   The marker cluster type.
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface $helper The marker cluster helper.
     */
    public function setHelper($name, MarkerClusterHelperInterface $helper = null)
    {
        $this->helpers[$name] = $helper;
    }

    /**
     * {@inheritdoc}
     */
    public function render(MarkerCluster $markerCluster, Map $map)
    {
        return $this->getHelper($markerCluster->getType())->render($markerCluster, $map);
    }

    /**
     * {@inheritdoc}
     */
    public function renderMarkers(MarkerCluster $markerCluster, Map $map)
    {
        return $this->getHelper($markerCluster->getType())->renderMarkers($markerCluster, $map);
    }

    /**
     * {@inheritdoc}
     */
    public function renderLibraries(MarkerCluster $markerCluster, Map $map)
    {
        return $this->getHelper($markerCluster->getType())->renderLibraries($markerCluster, $map);
    }
}


<?php

/*
 * This file is part of the Ivory Google Map package.
 *
 * (c) Eric GELOEN <geloen.eric@gmail.com>
 *
 * For the full copyright and license information, please read the LICENSE
 * file that was distributed with this source code.
 */

namespace Ivory\GoogleMap\Helper;

use Ivory\GoogleMap\Exception\HelperException;
use Ivory\GoogleMap\Helper\Base\BoundHelper;
use Ivory\GoogleMap\Helper\Base\CoordinateHelper;
use Ivory\GoogleMap\Helper\Base\PointHelper;
use Ivory\GoogleMap\Helper\Base\SizeHelper;
use Ivory\GoogleMap\Helper\Controls\MapTypeControlHelper;
use Ivory\GoogleMap\Helper\Controls\OverviewMapControlHelper;
use Ivory\GoogleMap\Helper\Controls\PanControlHelper;
use Ivory\GoogleMap\Helper\Controls\RotateControlHelper;
use Ivory\GoogleMap\Helper\Controls\ScaleControlHelper;
use Ivory\GoogleMap\Helper\Controls\StreetViewControlHelper;
use Ivory\GoogleMap\Helper\Controls\ZoomControlHelper;
use Ivory\GoogleMap\Helper\Events\EventManagerHelper;
use Ivory\GoogleMap\Helper\Extension\CoreExtensionHelper;
use Ivory\GoogleMap\Helper\Extension\ExtensionHelperInterface;
use Ivory\GoogleMap\Helper\Layers\KMLLayerHelper;
use Ivory\GoogleMap\Helper\MapTypeIdHelper;
use Ivory\GoogleMap\Helper\Overlays\CircleHelper;
use Ivory\GoogleMap\Helper\Overlays\EncodedPolylineHelper;
use Ivory\GoogleMap\Helper\Overlays\GroundOverlayHelper;
use Ivory\GoogleMap\Helper\Overlays\InfoWindowHelper;
use Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelper;
use Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface;
use Ivory\GoogleMap\Helper\Overlays\MarkerImageHelper;
use Ivory\GoogleMap\Helper\Overlays\MarkerShapeHelper;
use Ivory\GoogleMap\Helper\Overlays\PolygonHelper;
use Ivory\GoogleMap\Helper\Overlays\PolylineHelper;
use Ivory\GoogleMap\Helper\Overlays\RectangleHelper;
use Ivory\GoogleMap\Map;

/**
 * Map helper.
 *
 * @author GeLo <geloen.eric@gmail.com>
 */
class MapHelper extends AbstractHelper
{
    /** @var \Ivory\GoogleMap\Helper\Base\CoordinateHelper */
    protected $coordinateHelper;

    /** @var \Ivory\GoogleMap\Helper\Base\BoundHelper */
    protected $boundHelper;

    /** @var \Ivory\GoogleMap\Helper\Base\PointHelper */
    protected $pointHelper;

    /** @var \Ivory\GoogleMap\Helper\Base\SizeHelper */
    protected $sizeHelper;

    /** @var \Ivory\GoogleMap\Helper\MapTypeIdHelper */
    protected $mapTypeIdHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\MapTypeControlHelper */
    protected $mapTypeControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\OverviewMapControlHelper */
    protected $overviewMapControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\PanControlHelper */
    protected $panControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\RotateControlHelper */
    protected $rotateControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\ScaleControlHelper */
    protected $scaleControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\StreetViewControlHelper */
    protected $streetViewControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Controls\ZoomControlHelper */
    protected $zoomControlHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface */
    protected $markerClusterHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\MarkerImageHelper */
    protected $markerImageHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\MarkerShapeHelper */
    protected $markerShapeHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\InfoWindowHelper */
    protected $infoWindowHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\PolylineHelper */
    protected $polylineHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\EncodedPolylineHelper */
    protected $encodedPolylineHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\PolygonHelper */
    protected $polygonHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\RectangleHelper */
    protected $rectangleHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\CircleHelper */
    protected $circleHelper;

    /** @var \Ivory\GoogleMap\Helper\Overlays\GroundOverlayHelper */
    protected $groundOverlayHelper;

    /** @var \Ivory\GoogleMap\Helper\Layers\KMLLayerHelper */
    protected $kmlLayerHelper;

    /** @var \Ivory\GoogleMap\Helper\Events\EventManagerHelper */
    protected $eventManagerHelper;

    /** @var array */
    protected $extensionHelpers;

    /**
     * Creates a map helper.
     *
     * @param \Ivory\GoogleMap\Helper\Base\CoordinateHelper                 $coordinateHelper         The coordinate helper.
     * @param \Ivory\GoogleMap\Helper\Base\BoundHelper                      $boundHelper              The bound helper.
     * @param \Ivory\GoogleMap\Helper\Base\PointHelper                      $pointHelper              The point helper.
     * @param \Ivory\GoogleMap\Helper\Base\SizeHelper                       $sizeHelper               The size helper.
     * @param \Ivory\GoogleMap\Helper\MapTypeIdHelper                       $mapTypeIdHelper          The map type id helper.
     * @param \Ivory\GoogleMap\Helper\Controls\MapTypeControlHelper         $mapTypeControlHelper     The map type control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\OverviewMapControlHelper     $overviewMapControlHelper The overview map control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\PanControlHelper             $panControlHelper         The pan control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\RotateControlHelper          $rotateControlHelper      The rotate control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\ScaleControlHelper           $scaleControlHelper       The scale control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\StreetViewControlHelper      $streetViewControlHelper  The street view control helper.
     * @param \Ivory\GoogleMap\Helper\Controls\ZoomControlHelper            $zoomControlHelper        The zoom control helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerClusterHelperInterface $markerClusterHelper      The marker cluster helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerImageHelper            $markerImageHelper        The marker image helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerShapeHelper            $markerShapeHelper        The marker shape helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\InfoWindowHelper             $infoWindowHelper         The info window helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\PolylineHelper               $polylineHelper           The polyline helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\EncodedPolylineHelper        $encodedPolylineHelper    The encoded polyline helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\PolygonHelper                $polygonHelper            The polygon helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\RectangleHelper              $rectangleHelper          The rectangle helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\CircleHelper                 $circleHelper             The circle helper.
     * @param \Ivory\GoogleMap\Helper\Overlays\GroundOverlayHelper          $groundOverlayHelper      The ground overlay helper.
     * @param \Ivory\GoogleMap\Helper\Layers\KMLLayerHelper                 $kmlLayerHelper           The KML layer helper.
     * @param \Ivory\GoogleMap\Helper\Events\EventManagerHelper             $eventManagerHelper       The event manager helper.
     * @param array                                                         $extensionHelpers         The extension helpers.
     */
    public function __construct(
        CoordinateHelper $coordinateHelper = null,
        BoundHelper $boundHelper = null,
        PointHelper $pointHelper = null,
        SizeHelper $sizeHelper = null,
        MapTypeIdHelper $mapTypeIdHelper = null,
        MapTypeControlHelper $mapTypeControlHelper = null,
        OverviewMapControlHelper $overviewMapControlHelper = null,
        PanControlHelper $panControlHelper = null,
        RotateControlHelper $rotateControlHelper = null,
        ScaleControlHelper $scaleControlHelper = null,
        StreetViewControlHelper $streetViewControlHelper = null,
        ZoomControlHelper $zoomControlHelper = null,
        MarkerClusterHelperInterface $markerClusterHelper = null,
        MarkerImageHelper $markerImageHelper = null,
        MarkerShapeHelper $markerShapeHelper = null,
        InfoWindowHelper $infoWindowHelper = null,
        PolylineHelper $polylineHelper = null,
        EncodedPolylineHelper $encodedPolylineHelper = null,
        PolygonHelper $polygonHelper = null,
        RectangleHelper $rectangleHelper = null,
        CircleHelper $circleHelper = null,
        GroundOverlayHelper $groundOverlayHelper = null,
        KMLLayerHelper $kmlLayerHelper = null,
        EventManagerHelper $eventManagerHelper = null,
        array $extensionHelpers = array()
    ) {
        parent::__construct();

        if ($coordinateHelper === null) {
            $coordinateHelper = new CoordinateHelper();
        }

        if ($boundHelper === null) {
            $boundHelper = new BoundHelper();
        }

        if ($pointHelper === null) {
            $pointHelper = new PointHelper();
        }

        if ($sizeHelper === null) {
            $sizeHelper = new SizeHelper();
        }

        if ($mapTypeIdHelper === null) {
            $mapTypeIdHelper = new MapTypeIdHelper();
        }

        if ($mapTypeControlHelper === null) {
            $mapTypeControlHelper = new MapTypeControlHelper();
        }

        if ($overviewMapControlHelper === null) {
            $overviewMapControlHelper = new OverviewMapControlHelper();
        }

        if ($panControlHelper === null) {
            $panControlHelper = new PanControlHelper();
        }

        if ($rotateControlHelper === null) {
            $rotateControlHelper = new RotateControlHelper();
        }

        if ($scaleControlHelper === null) {
            $scaleControlHelper = new ScaleControlHelper();
        }

        if ($streetViewControlHelper === null) {
            $streetViewControlHelper = new StreetViewControlHelper();
        }

        if ($zoomControlHelper === null) {
            $zoomControlHelper = new ZoomControlHelper();
        }

        if ($markerClusterHelper === null) {
            $markerClusterHelper = new MarkerClusterHelper();
        }

        if ($markerImageHelper === null) {
            $markerImageHelper = new MarkerImageHelper();
        }

        if ($markerShapeHelper === null) {
            $markerShapeHelper = new MarkerShapeHelper();
        }

        if ($infoWindowHelper === null) {
            $infoWindowHelper = new InfoWindowHelper();
        }

        if ($polylineHelper === null) {
            $polylineHelper = new PolylineHelper();
        }

        if ($encodedPolylineHelper === null) {
            $encodedPolylineHelper = new EncodedPolylineHelper();
        }

        if ($polygonHelper === null) {
            $polygonHelper = new PolygonHelper();
        }

        if ($rectangleHelper === null) {
            $rectangleHelper = new RectangleHelper();
        }

        if ($circleHelper === null) {
            $circleHelper = new CircleHelper();
        }

        if ($groundOverlayHelper === null) {
            $groundOverlayHelper = new GroundOverlayHelper();
        }

        if ($kmlLayerHelper === null) {
            $kmlLayerHelper = new KMLLayerHelper();
        }

        if ($eventManagerHelper === null) {
            $eventManagerHelper = new EventManagerHelper();
        }

        if (empty($extensionHelpers)) {
            $extensionHelpers['core'] = new CoreExtensionHelper();
        }

        $this->setCoordinateHelper($coordinateHelper);
        $this->setBoundHelper($boundHelper);
        $this->setPointHelper($pointHelper);
        $this->setSizeHelper($sizeHelper);

        $this->setMapTypeIdHelper($mapTypeIdHelper);
        $this->setMapTypeControlHelper($mapTypeControlHelper);
        $this->setOverviewMapControlHelper($overviewMapControlHelper);
        $this->setPanControlHelper($panControlHelper);
        $this->setRotateControlHelper($rotateControlHelper);
        $this->setScaleControlHelper($scaleControlHelper);
        $this->setStreetViewControlHelper($streetViewControlHelper);
        $this->setZoomControlHelper($zoomControlHelper);

        $this->setMarkerClusterHelper($markerClusterHelper);
        $this->setMarkerImageHelper($markerImageHelper);
        $this->setMarkerShapeHelper($markerShapeHelper);
        $this->setInfoWindowHelper($infoWindowHelper);
        $this->setPolylineHelper($polylineHelper);
        $this->setEncodedPolylineHelper($encodedPolylineHelper);
        $this->setPolygonHelper($polygonHelper);
        $this->setRectangleHelper($rectangleHelper);
        $this->setCircleHelper($circleHelper);
        $this->setGroundOverlayHelper($groundOverlayHelper);

        $this->setKmlLayerHelper($kmlLayerHelper);

        $this->setEventManagerHelper($eventManagerHelper);

        $this->setExtensionHelpers($extensionHelpers);
    }

    /**
     * Gets the coordinate helper.
     *
     * @return \Ivory\GoogleMap\Helper\Base\CoordinateHelper The coordinate helper.
     */
    public function getCoordinateHelper()
    {
        return $this->coordinateHelper;
    }

    /**
     * Sets the coordinate helper.
     *
     * @param \Ivory\GoogleMap\Helper\Base\CoordinateHelper $coordinateHelper The coordinate helper.
     */
    public function setCoordinateHelper(CoordinateHelper $coordinateHelper)
    {
        $this->coordinateHelper = $coordinateHelper;
    }

    /**
     * Gets the bound helper.
     *
     * @return \Ivory\GoogleMap\Helper\Base\BoundHelper The bound helper.
     */
    public function getBoundHelper()
    {
        return $this->boundHelper;
    }

    /**
     * Sets the bound helper.
     *
     * @param \Ivory\GoogleMap\Helper\Base\BoundHelper $boundHelper The bound helper.
     */
    public function setBoundHelper(BoundHelper $boundHelper)
    {
        $this->boundHelper = $boundHelper;
    }

    /**
     * Gets the point helper.
     *
     * @return \Ivory\GoogleMap\Helper\Base\PointHelper The point helper.
     */
    public function getPointHelper()
    {
        return $this->pointHelper;
    }

    /**
     * Sets the point helper.
     *
     * @param \Ivory\GoogleMap\Helper\Base\PointHelper $pointHelper The point helper.
     */
    public function setPointHelper(PointHelper $pointHelper)
    {
        $this->pointHelper = $pointHelper;
    }

    /**
     * Gets the size helper.
     *
     * @return \Ivory\GoogleMap\Helper\Base\SizeHelper The size helper.
     */
    public function getSizeHelper()
    {
        return $this->sizeHelper;
    }

    /**
     * Sets the size helper.
     *
     * @param \Ivory\GoogleMap\Helper\Base\SizeHelper $sizeHelper The size helper.
     */
    public function setSizeHelper(SizeHelper $sizeHelper)
    {
        $this->sizeHelper = $sizeHelper;
    }

    /**
     * Gets the map type id helper.
     *
     * @return \Ivory\GoogleMap\Helper\MapTypeIdHelper The map type id helper.
     */
    public function getMapTypeIdHelper()
    {
        return $this->mapTypeIdHelper;
    }

    /**
     * Sets the map type id helper.
     *
     * @param \Ivory\GoogleMap\Helper\MapTypeIdHelper $mapTypeIdHelper The map type id helper.
     */
    public function setMapTypeIdHelper(MapTypeIdHelper $mapTypeIdHelper)
    {
        $this->mapTypeIdHelper = $mapTypeIdHelper;
    }

    /**
     * Gets the map type control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\MapTypeControlHelper The map type control helper.
     */
    public function getMapTypeControlHelper()
    {
        return $this->mapTypeControlHelper;
    }

    /**
     * Sets the map type control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\MapTypeControlHelper $mapTypeControlHelper The map type control helper.
     */
    public function setMapTypeControlHelper(MapTypeControlHelper $mapTypeControlHelper)
    {
        $this->mapTypeControlHelper = $mapTypeControlHelper;
    }

    /**
     * Gets the overview map control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\OverviewMapControlHelper The overview map control helper.
     */
    public function getOverviewMapControlHelper()
    {
        return $this->overviewMapControlHelper;
    }

    /**
     * Sets the overview map control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\OverviewMapControlHelper $overviewMapControlHelper The overview map
     *                                                                                control helper.
     */
    public function setOverviewMapControlHelper(OverviewMapControlHelper $overviewMapControlHelper)
    {
        $this->overviewMapControlHelper = $overviewMapControlHelper;
    }

    /**
     * Gets the pan control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\PanControlHelper The pan control helper.
     */
    public function getPanControlHelper()
    {
        return $this->panControlHelper;
    }

    /**
     * Sets the pan control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\PanControlHelper $panControlHelper The pan control helper.
     */
    public function setPanControlHelper(PanControlHelper $panControlHelper)
    {
        $this->panControlHelper = $panControlHelper;
    }

    /**
     * Gets the rotate control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\RotateControlHelper The rotate control helper.
     */
    public function getRotateControlHelper()
    {
        return $this->rotateControlHelper;
    }

    /**
     * Sets the rotate control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\RotateControlHelper $rotateControlHelper The rotate control helper.
     */
    public function setRotateControlHelper(RotateControlHelper $rotateControlHelper)
    {
        $this->rotateControlHelper = $rotateControlHelper;
    }

    /**
     * Gets the scale control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\ScaleControlHelper The scale control helper.
     */
    public function getScaleControlHelper()
    {
        return $this->scaleControlHelper;
    }

    /**
     * Sets the scale control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\ScaleControlHelper $scaleControlHelper The scale control helper.
     */
    public function setScaleControlHelper(ScaleControlHelper $scaleControlHelper)
    {
        $this->scaleControlHelper = $scaleControlHelper;
    }

    /**
     * Gets the street view control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\StreetViewControlHelper The street view control helper.
     */
    public function getStreetViewControlHelper()
    {
        return $this->streetViewControlHelper;
    }

    /**
     * Sets the street view control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\StreetViewControlHelper $streetViewControlHelper The street view control
     *                                                                                helper.
     */
    public function setStreetViewControlHelper(StreetViewControlHelper $streetViewControlHelper)
    {
        $this->streetViewControlHelper = $streetViewControlHelper;
    }

    /**
     * Gets the zoom control helper.
     *
     * @return \Ivory\GoogleMap\Helper\Controls\ZoomControlHelper The zoom control helper.
     */
    public function getZoomControlHelper()
    {
        return $this->zoomControlHelper;
    }

    /**
     * Sets the zoom control helper.
     *
     * @param \Ivory\GoogleMap\Helper\Controls\ZoomControlHelper $zoomControlHelper The zoom control helper.
     */
    public function setZoomControlHelper(ZoomControlHelper $zoomControlHelper)
    {
        $this->zoomControlHelper = $zoomControlHelper;
    }

    /**
     * Gets the marker cluster helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface The marker cluster helper.
     */
    public function getMarkerClusterHelper()
    {
        return $this->markerClusterHelper;
    }

    /**
     * Sets the marker cluster helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerCluster\MarkerClusterHelperInterface $markerClusterHelper The marker cluster helper.
     */
    public function setMarkerClusterHelper(MarkerClusterHelperInterface $markerClusterHelper)
    {
        $this->markerClusterHelper = $markerClusterHelper;
    }

    /**
     * Gets the marker image helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\MarkerImageHelper The marker image helper.
     */
    public function getMarkerImageHelper()
    {
        return $this->markerImageHelper;
    }

    /**
     * Sets the marker image helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerImageHelper $markerImageHelper The marker image helper.
     */
    public function setMarkerImageHelper(MarkerImageHelper $markerImageHelper)
    {
        $this->markerImageHelper = $markerImageHelper;
    }

    /**
     * Gets the marker shape helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\MarkerShapeHelper The marker shape helper.
     */
    public function getMarkerShapeHelper()
    {
        return $this->markerShapeHelper;
    }

    /**
     * Sets the marker shape helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\MarkerShapeHelper $markerShapeHelper The marker shape helper.
     */
    public function setMarkerShapeHelper(MarkerShapeHelper $markerShapeHelper)
    {
        $this->markerShapeHelper = $markerShapeHelper;
    }

    /**
     * Gets the info window helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\InfoWindowHelper The info window helper.
     */
    public function getInfoWindowHelper()
    {
        return $this->infoWindowHelper;
    }

    /**
     * Sets the info window helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\InfoWindowHelper $infoWindowHelper The info window helper.
     */
    public function setInfoWindowHelper(InfoWindowHelper $infoWindowHelper)
    {
        $this->infoWindowHelper = $infoWindowHelper;
    }

    /**
     * Gets the polyline helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\PolylineHelper The polyline helper.
     */
    public function getPolylineHelper()
    {
        return $this->polylineHelper;
    }

    /**
     * Sets the polyline helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\PolylineHelper $polylineHelper The polyline helper.
     */
    public function setPolylineHelper(PolylineHelper $polylineHelper)
    {
        $this->polylineHelper = $polylineHelper;
    }

    /**
     * Gets the encoded polyline helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\EncodedPolylineHelper The encoded polyline helper.
     */
    public function getEncodedPolylineHelper()
    {
        return $this->encodedPolylineHelper;
    }

    /**
     * Sets the encoded polyline helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\EncodedPolylineHelper $encodedPolylineHelper The encoded polyline helper.
     */
    public function setEncodedPolylineHelper(EncodedPolylineHelper $encodedPolylineHelper)
    {
        $this->encodedPolylineHelper = $encodedPolylineHelper;
    }

    /**
     * Gets the polygon helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\PolygonHelper The polygon helper.
     */
    public function getPolygonHelper()
    {
        return $this->polygonHelper;
    }

    /**
     * Sets the polygon helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\PolygonHelper $polygonHelper The polygon helper.
     */
    public function setPolygonHelper(PolygonHelper $polygonHelper)
    {
        $this->polygonHelper = $polygonHelper;
    }

    /**
     * Gets the rectangle helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\RectangleHelper The rectangle helper.
     */
    public function getRectangleHelper()
    {
        return $this->rectangleHelper;
    }

    /**
     * Sets the rectangle helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\RectangleHelper $rectangleHelper The rectangle helper.
     */
    public function setRectangleHelper(RectangleHelper $rectangleHelper)
    {
        $this->rectangleHelper = $rectangleHelper;
    }

    /**
     * Gets the circle helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\CircleHelper The circle helper.
     */
    public function getCircleHelper()
    {
        return $this->circleHelper;
    }

    /**
     * Sets the circle helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\CircleHelper $circleHelper The circle helper.
     */
    public function setCircleHelper(CircleHelper $circleHelper)
    {
        $this->circleHelper = $circleHelper;
    }

    /**
     * Gets the ground overlay helper.
     *
     * @return \Ivory\GoogleMap\Helper\Overlays\GroundOverlayHelper The ground overlay helper.
     */
    public function getGroundOverlayHelper()
    {
        return $this->groundOverlayHelper;
    }

    /**
     * Sets the ground overlay helper.
     *
     * @param \Ivory\GoogleMap\Helper\Overlays\GroundOverlayHelper $groundOverlayHelper The ground overlay helper.
     */
    public function setGroundOverlayHelper(GroundOverlayHelper $groundOverlayHelper)
    {
        $this->groundOverlayHelper = $groundOverlayHelper;
    }

    /**
     * Gets the KML layer helper.
     *
     * @return \Ivory\GoogleMap\Helper\Layers\KMLLayerHelper The KML layer helper.
     */
    public function getKmlLayerHelper()
    {
        return $this->kmlLayerHelper;
    }

    /**
     * Sets the KML layer helper.
     *
     * @param \Ivory\GoogleMap\Helper\Layers\KMLLayerHelper $kmlLayerHelper The KML layer helper.
     */
    public function setKmlLayerHelper(KMLLayerHelper $kmlLayerHelper)
    {
        $this->kmlLayerHelper = $kmlLayerHelper;
    }

    /**
     * Gets the event manager helper.
     *
     * @return \Ivory\GoogleMap\Helper\Events\EventManagerHelper The event manager helper.
     */
    public function getEventManagerHelper()
    {
        return $this->eventManagerHelper;
    }

    /**
     * Sets the event manager helper.
     *
     * @param \Ivory\GoogleMap\Helper\Events\EventManagerHelper $eventManagerHelper The event manager helper.
     */
    public function setEventManagerHelper(EventManagerHelper $eventManagerHelper)
    {
        $this->eventManagerHelper = $eventManagerHelper;
    }

    /**
     * Checks if the map helper has extensions.
     *
     * @return boolean TRUE if the map helper has extensions else FALSE.
     */
    public function hasExtensionHelpers()
    {
        return !empty($this->extensionHelpers);
    }

    /**
     * Gets the map extension helpers.
     *
     * @return array The map extension helpers.
     */
    public function getExtensionHelpers()
    {
        return $this->extensionHelpers;
    }

    /**
     * Sets the map extension helpers.
     *
     * @param array $extensionHelpers The map extension helpers.
     */
    public function setExtensionHelpers(array $extensionHelpers)
    {
        $this->extensionHelpers = array();

        foreach ($extensionHelpers as $name => $extensionHelper) {
            $this->setExtensionHelper($name, $extensionHelper);
        }
    }

    /**
     * Checks if the map helper has a specific extension helper.
     *
     * @param string $name The extension helper name.
     *
     * @return boolean TRUE if the map helper has a specific extension helper else FALSE.
     */
    public function hasExtensionHelper($name)
    {
        return isset($this->extensionHelpers[$name]);
    }

    /**
     * Gets a specific extension helper.
     *
     * @param string $name The extension helper name.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the extension helper does not exist.
     *
     * @return \Ivory\GoogleMap\Helper\Extension\ExtensionHelperInterface The extension helper.
     */
    public function getExtensionHelper($name)
    {
        if (!$this->hasExtensionHelper($name)) {
            throw HelperException::invalidExtension($name);
        }

        return $this->extensionHelpers[$name];
    }

    /**
     * Sets an extension helper.
     *
     * @param string                                                     $name            The extension helper name.
     * @param \Ivory\GoogleMap\Helper\Extension\ExtensionHelperInterface $extensionHelper The extension helper.
     */
    public function setExtensionHelper($name, ExtensionHelperInterface $extensionHelper)
    {
        $this->extensionHelpers[$name] = $extensionHelper;
    }

    /**
     * Removes an extension helper.
     *
     * @param string $name The extension helper name.
     *
     * @throws \Ivory\GoogleMap\Exception\HelperException If the extension helper does not exist.
     */
    public function removeExtensionHelper($name)
    {
        if (!$this->hasExtensionHelper($name)) {
            throw HelperException::invalidExtension($name);
        }

        unset($this->extensionHelpers[$name]);
    }

    /**
     * Renders the html (container & stylesheets).
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The HTML output.
     */
    public function render(Map $map)
    {
        return implode('', array(
            $this->renderStylesheets($map),
            $this->renderHtmlContainer($map),
            $this->renderJavascripts($map)
        ));
    }

    /**
     * Renders the map container.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The HTML output.
     */
    public function renderHtmlContainer(Map $map)
    {
        return sprintf(
            '<div id="%s" style="width:%s;height:%s;"></div>'.PHP_EOL,
            $map->getHtmlContainerId(),
            $map->getStylesheetOption('width'),
            $map->getStylesheetOption('height')
        );
    }

    /**
     * Renders the html map stylesheets.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The HTML output.
     */
    public function renderStylesheets(Map $map)
    {
        $html = array();

        $html[] = '<style type="text/css" scoped>'.PHP_EOL;
        $html[] = '#'.$map->getHtmlContainerId().'{'.PHP_EOL;

        foreach ($map->getStylesheetOptions() as $option => $value) {
            $html[] = $option.':'.$value.';'.PHP_EOL;
        }

        $html[] = '}'.PHP_EOL;
        $html[] = '</style>'.PHP_EOL;

        return implode('', $html);
    }

    /**
     * Renders the map javascripts.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The HTML output.
     */
    public function renderJavascripts(Map $map)
    {
        $output = array();

        if (!$map->isAsync()) {
            $output[] = $this->renderJsLibraries($map);
        }

        $output[] = '<script type="text/javascript">'.PHP_EOL;
        $output[] = $this->renderJsBefore($map);
        $output[] = $this->renderJsContainer($map);
        $output[] = $this->renderJsAfter($map);
        $output[] = '</script>'.PHP_EOL;

        if ($map->isAsync()) {
            $output[] = $this->renderJsLibraries($map);
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript libraries.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The HTML output.
     */
    public function renderJsLibraries(Map $map)
    {
        $output = array();

        foreach ($this->getExtensionHelpers() as $extension) {
            $output[] = $extension->renderLibraries($map);
        }

        return implode('', $output);
    }

    /**
     * Renders JS code just before the generated one.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsBefore(Map $map)
    {
        $output = array();

        foreach ($this->getExtensionHelpers() as $extension) {
            $output[] = $extension->renderBefore($map);
        }

        return implode('', $output);
    }

    /**
     * Renders JS code just after the generated one.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsAfter(Map $map)
    {
        $output = array();

        foreach (array_reverse($this->getExtensionHelpers()) as $extension) {
            $output[] = $extension->renderAfter($map);
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainer(Map $map)
    {
        $output = array();

        $output[] = $this->renderJsContainerInit($map);

        $output[] = $this->renderJsContainerCoordinates($map);
        $output[] = $this->renderJsContainerBounds($map);
        $output[] = $this->renderJsContainerPoints($map);
        $output[] = $this->renderJsContainerSizes($map);

        $output[] = $this->renderJsContainerMap($map);

        $output[] = $this->renderJsContainerCircles($map);
        $output[] = $this->renderJsContainerEncodedPolylines($map);
        $output[] = $this->renderJsContainerGroundOverlays($map);
        $output[] = $this->renderJsContainerPolygons($map);
        $output[] = $this->renderJsContainerPolylines($map);
        $output[] = $this->renderJsContainerRectangles($map);
        $output[] = $this->renderJsContainerInfoWindows($map);
        $output[] = $this->renderJsContainerMarkerImages($map);
        $output[] = $this->renderJsContainerMarkerShapes($map);
        $output[] = $this->renderJsContainerMarkerCluster($map);

        $output[] = $this->renderJsContainerKMLLayers($map);

        $output[] = $this->renderJsContainerEventManager($map);

        $output[] = $this->renderJsContainerExtra($map);

        return implode('', $output);
    }

    /**
     * Renders the javascript container initialization (empty container).
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerInit(Map $map)
    {
        $this->jsonBuilder
            ->reset()
            ->setJsonEncodeOptions(JSON_FORCE_OBJECT)
            // Map
            ->setValue('[map]', null)
            // Base
            ->setValue('[coordinates]', array())
            ->setValue('[bounds]', array())
            ->setValue('[points]', array())
            ->setValue('[sizes]', array())
            // Overlays
            ->setValue('[circles]', array())
            ->setValue('[encoded_polylines]', array())
            ->setValue('[ground_overlays]', array())
            ->setValue('[polygons]', array())
            ->setValue('[polylines]', array())
            ->setValue('[rectangles]', array())
            ->setValue('[info_windows]', array())
            ->setValue('[marker_images]', array())
            ->setValue('[marker_shapes]', array())
            ->setValue('[markers]', array())
            ->setValue('[marker_cluster]', null)
            // Layers
            ->setValue('[kml_layers]', array())
            // Event Manager
            ->setValue('[event_manager][dom_events]', array())
            ->setValue('[event_manager][dom_events_once]', array())
            ->setValue('[event_manager][events]', array())
            ->setValue('[event_manager][events_once]', array())
            // Internal
            ->setValue('[closable_info_windows]', array())
            ->setValue(
                '[functions][to_array]',
                'function (object) { var array = []; for (var key in object) { array.push(object[key]); } return array; }',
                false
            );

        return sprintf('%s = %s;'.PHP_EOL, $this->getJsContainerName($map), $this->jsonBuilder->build());
    }

    /**
     * Renders the javascript container coordinates.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerCoordinates(Map $map)
    {
        $output = array();

        foreach ($this->computeCoordinates($map) as $coordinate) {
            $output[] = sprintf(
                '%s.coordinates.%s = %s',
                $this->getJsContainerName($map),
                $coordinate->getJavascriptVariable(),
                $this->coordinateHelper->render($coordinate)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container bounds.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerBounds(Map $map)
    {
        $output = array();

        foreach ($this->computeBounds($map) as $bound) {
            $output[] = sprintf(
                '%s.bounds.%s = %s',
                $this->getJsContainerName($map),
                $bound->getJavascriptVariable(),
                $this->boundHelper->render($bound)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container points.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerPoints(Map $map)
    {
        $output = array();

        foreach ($this->computePoints($map) as $point) {
            $output[] = sprintf(
                '%s.points.%s = %s',
                $this->getJsContainerName($map),
                $point->getJavascriptVariable(),
                $this->pointHelper->render($point)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container sizes.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerSizes(Map $map)
    {
        $output = array();

        foreach ($this->computeSizes($map) as $size) {
            $output[] = sprintf(
                '%s.sizes.%s = %s',
                $this->getJsContainerName($map),
                $size->getJavascriptVariable(),
                $this->sizeHelper->render($size)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerMap(Map $map)
    {
        return sprintf('%s.map = %s', $this->getJsContainerName($map), $this->renderMap($map));
    }

    /**
     * Renders the javascript container circles.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerCircles(Map $map)
    {
        $output = array();

        foreach ($map->getCircles() as $circle) {
            $output[] = sprintf(
                '%s.circles.%s = %s',
                $this->getJsContainerName($map),
                $circle->getJavascriptVariable(),
                $this->circleHelper->render($circle, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container encoded polylines.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerEncodedPolylines(Map $map)
    {
        $output = array();

        foreach ($map->getEncodedPolylines() as $encodedPolyline) {
            $output[] = sprintf(
                '%s.encoded_polylines.%s = %s',
                $this->getJsContainerName($map),
                $encodedPolyline->getJavascriptVariable(),
                $this->encodedPolylineHelper->render($encodedPolyline, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container ground overlays.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerGroundOverlays(Map $map)
    {
        $output = array();

        foreach ($map->getGroundOverlays() as $groundOverlay) {
            $output[] = sprintf(
                '%s.ground_overlays.%s = %s',
                $this->getJsContainerName($map),
                $groundOverlay->getJavascriptVariable(),
                $this->groundOverlayHelper->render($groundOverlay, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container polygons.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerPolygons(Map $map)
    {
        $output = array();

        foreach ($map->getPolygons() as $polygon) {
            $output[] = sprintf(
                '%s.polygons.%s = %s',
                $this->getJsContainerName($map),
                $polygon->getJavascriptVariable(),
                $this->polygonHelper->render($polygon, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container polylines.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerPolylines(Map $map)
    {
        $output = array();

        foreach ($map->getPolylines() as $polyline) {
            $output[] = sprintf(
                '%s.polylines.%s = %s',
                $this->getJsContainerName($map),
                $polyline->getJavascriptVariable(),
                $this->polylineHelper->render($polyline, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container rectangles.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerRectangles(Map $map)
    {
        $output = array();

        foreach ($map->getRectangles() as $rectangle) {
            $output[] = sprintf(
                '%s.rectangles.%s = %s',
                $this->getJsContainerName($map),
                $rectangle->getJavascriptVariable(),
                $this->rectangleHelper->render($rectangle, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container info windows.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerInfoWindows(Map $map)
    {
        $output = array();

        $mapInfoWindows = $map->getInfoWindows();
        $markerInfoWindows = $this->computeMarkerInfoWindows($map);

        foreach ($mapInfoWindows as $mapInfoWindow) {
            $output[] = sprintf(
                '%s.info_windows.%s = %s',
                $this->getJsContainerName($map),
                $mapInfoWindow->getJavascriptVariable(),
                $this->infoWindowHelper->render($mapInfoWindow)
            );
        }

        foreach ($markerInfoWindows as $markerInfoWindow) {
            $output[] = sprintf(
                '%s.info_windows.%s = %s',
                $this->getJsContainerName($map),
                $markerInfoWindow->getJavascriptVariable(),
                $this->infoWindowHelper->render($markerInfoWindow, false)
            );
        }

        foreach (array_merge($mapInfoWindows, $markerInfoWindows) as $infoWindow) {
            if ($infoWindow->isAutoClose()) {
                $output[] = sprintf(
                    '%s.closable_info_windows.%s = %s;'.PHP_EOL,
                    $this->getJsContainerName($map),
                    $infoWindow->getJavascriptVariable(),
                    $infoWindow->getJavascriptVariable()
                );
            }
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container maker images.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerMarkerImages(Map $map)
    {
        $output = array();

        foreach ($this->computeMarkerImages($map) as $markerImage) {
            $output[] = sprintf(
                '%s.marker_images.%s = %s',
                $this->getJsContainerName($map),
                $markerImage->getJavascriptVariable(),
                $this->markerImageHelper->render($markerImage)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container marker shapes.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerMarkerShapes(Map $map)
    {
        $output = array();

        foreach ($this->computeMarkerShapes($map) as $markerShape) {
            $output[] = sprintf(
                '%s.marker_shapes.%s = %s',
                $this->getJsContainerName($map),
                $markerShape->getJavascriptVariable(),
                $this->markerShapeHelper->render($markerShape)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container marker cluster.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerMarkerCluster(Map $map)
    {
        $output = array(
            $this->markerClusterHelper->renderMarkers($map->getMarkerCluster(), $map),
            $this->markerClusterHelper->render($map->getMarkerCluster(), $map),
        );

        return implode('', $output);
    }

    /**
     * Renders the javascript container KML layer.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerKMLLayers(Map $map)
    {
        $output = array();

        foreach ($map->getKMLLayers() as $kmlLayer) {
            $output[] = sprintf(
                '%s.kml_layers.%s = %s',
                $this->getJsContainerName($map),
                $kmlLayer->getJavascriptVariable(),
                $this->kmlLayerHelper->render($kmlLayer, $map)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the javascript container event manager.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerEventManager(Map $map)
    {
        $output = array();

        foreach ($map->getEventManager()->getDomEvents() as $domEvent) {
            $output[] = sprintf(
                '%s.event_manager.dom_events.%s = %s',
                $this->getJsContainerName($map),
                $domEvent->getJavascriptVariable(),
                $this->eventManagerHelper->renderDomEvent($domEvent)
            );
        }

        foreach ($map->getEventManager()->getDomEventsOnce() as $domEventOnce) {
            $output[] = sprintf(
                '%s.event_manager.dom_events_once.%s = %s',
                $this->getJsContainerName($map),
                $domEventOnce->getJavascriptVariable(),
                $this->eventManagerHelper->renderDomEventOnce($domEventOnce)
            );
        }

        foreach ($map->getEventManager()->getEvents() as $event) {
            $output[] = sprintf(
                '%s.event_manager.events.%s = %s',
                $this->getJsContainerName($map),
                $event->getJavascriptVariable(),
                $this->eventManagerHelper->renderEvent($event)
            );
        }

        foreach ($map->getEventManager()->getEventsOnce() as $eventOnce) {
            $output[] = sprintf(
                '%s.event_manager.events_once.%s = %s',
                $this->getJsContainerName($map),
                $eventOnce->getJavascriptVariable(),
                $this->eventManagerHelper->renderEventOnce($eventOnce)
            );
        }

        return implode('', $output);
    }

    /**
     * Renders the js container extra.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderJsContainerExtra(Map $map)
    {
        $output = array();

        foreach ($map->getInfoWindows() as $infoWindow) {
            if ($infoWindow->isOpen()) {
                $output[] = $this->infoWindowHelper->renderOpen($infoWindow, $map);
            }
        }

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasInfoWindow() && $marker->getInfoWindow()->isOpen()) {
                $output[] = $this->infoWindowHelper->renderOpen($marker->getInfoWindow(), $map, $marker);
            }
        }

        foreach ($this->computeBounds($map) as $bound) {
            if ($bound->hasExtends()) {
                $output[] = $this->boundHelper->renderExtends($bound);
            }
        }

        if ($map->isAutoZoom()) {
            $output[] = $this->renderMapBound($map);
        } else {
            $output[] = $this->renderMapCenter($map);
        }

        return implode('', $output);
    }

    /**
     * Renders the map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderMap(Map $map)
    {
        $mapTypeId = $map->getMapOption('mapTypeId');
        $map->removeMapOption('mapTypeId');

        $this->jsonBuilder
            ->reset()
            ->setValue('[mapTypeId]', $this->mapTypeIdHelper->render($mapTypeId), false);

        $this->renderMapControls($map);

        if ($map->isAutoZoom()) {
            $map->removeMapOption('zoom');
        }

        $this->jsonBuilder->setValues($map->getMapOptions());

        return sprintf(
            '%s = new google.maps.Map(document.getElementById("%s"), {
                zoom: 5,
                scrollwheel: false,
                mapTypeId: google.maps.MapTypeId.ROADMAP,
                zoomControl: true,
                mapTypeControl: false,
                scaleControl: false,
                panControl: false,
                navigationControl: false,
                streetViewControl: false,
                gestureHandling: "cooperative",
                styles: [{"featureType":"poi","elementType":"labels.text.fill","stylers":[{"color":"#747474"},{"lightness":"23"}]},{"featureType":"poi.attraction","elementType":"geometry.fill","stylers":[{"color":"#f38eb0"}]},{"featureType":"poi.government","elementType":"geometry.fill","stylers":[{"color":"#ced7db"}]},{"featureType":"poi.medical","elementType":"geometry.fill","stylers":[{"color":"#ffa5a8"}]},{"featureType":"poi.park","elementType":"geometry.fill","stylers":[{"color":"#c7e5c8"}]},{"featureType":"poi.place_of_worship","elementType":"geometry.fill","stylers":[{"color":"#d6cbc7"}]},{"featureType":"poi.school","elementType":"geometry.fill","stylers":[{"color":"#c4c9e8"}]},{"featureType":"poi.sports_complex","elementType":"geometry.fill","stylers":[{"color":"#b1eaf1"}]},{"featureType":"road","elementType":"geometry","stylers":[{"lightness":"100"}]},{"featureType":"road","elementType":"labels","stylers":[{"visibility":"off"},{"lightness":"100"}]},{"featureType":"road.highway","elementType":"geometry.fill","stylers":[{"color":"#ffd4a5"}]},{"featureType":"road.arterial","elementType":"geometry.fill","stylers":[{"color":"#ffe9d2"}]},{"featureType":"road.local","elementType":"all","stylers":[{"visibility":"simplified"}]},{"featureType":"road.local","elementType":"geometry.fill","stylers":[{"weight":"3.00"}]},{"featureType":"road.local","elementType":"geometry.stroke","stylers":[{"weight":"0.30"}]},{"featureType":"road.local","elementType":"labels.text","stylers":[{"visibility":"on"}]},{"featureType":"road.local","elementType":"labels.text.fill","stylers":[{"color":"#747474"},{"lightness":"36"}]},{"featureType":"road.local","elementType":"labels.text.stroke","stylers":[{"color":"#e9e5dc"},{"lightness":"30"}]},{"featureType":"transit.line","elementType":"geometry","stylers":[{"visibility":"on"},{"lightness":"100"}]},{"featureType":"water","elementType":"all","stylers":[{"color":"#d2e7f7"}]}]
            });'.PHP_EOL,
            $map->getJavascriptVariable(),
            $map->getHtmlContainerId()//,
            //$this->jsonBuilder->build()
        );
    }

    /**
     * Renders the map center.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string Ths JS output.
     */
    public function renderMapCenter(Map $map)
    {
        return sprintf(
            '%s.setCenter(%s);'.PHP_EOL,
            $map->getJavascriptVariable(),
            $map->getCenter()->getJavascriptVariable()
        );
    }

    /**
     * Renders the map bound.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return string The JS output.
     */
    public function renderMapBound(Map $map)
    {
        return sprintf(
            '%s.fitBounds(%s);'.PHP_EOL,
            $map->getJavascriptVariable(),
            $map->getBound()->getJavascriptVariable()
        );
    }

    /**
     * Computes the coordinates of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed coordinated.
     */
    protected function computeCoordinates(Map $map)
    {
        $coordinates = array();

        if (!$map->isAutoZoom() && !in_array($map->getCenter(), $coordinates)) {
            $coordinates[] = $map->getCenter();
        }

        foreach ($this->computeBounds($map) as $bound) {
            if (!$bound->hasExtends() && $bound->hasCoordinates()) {
                if (!in_array($bound->getSouthWest(), $coordinates)) {
                    $coordinates[] = $bound->getSouthWest();
                }

                if (!in_array($bound->getNorthEast(), $coordinates)) {
                    $coordinates[] = $bound->getNorthEast();
                }
            }
        }

        foreach ($map->getCircles() as $circle) {
            if (!in_array($circle->getCenter(), $coordinates)) {
                $coordinates[] = $circle->getCenter();
            }
        }

        foreach ($map->getInfoWindows() as $infoWindow) {
            if (!in_array($infoWindow->getPosition(), $coordinates)) {
                $coordinates[] = $infoWindow->getPosition();
            }
        }

        foreach ($map->getMarkers() as $marker) {
            if (!in_array($marker->getPosition(), $coordinates)) {
                $coordinates[] = $marker->getPosition();
            }
        }

        foreach ($map->getPolygons() as $polygon) {
            foreach ($polygon->getCoordinates() as $polygonCoordinate) {
                if (!in_array($polygonCoordinate, $coordinates)) {
                    $coordinates[] = $polygonCoordinate;
                }
            }
        }

        foreach ($map->getPolylines() as $polyline) {
            foreach ($polyline->getCoordinates() as $polylineCoordinate) {
                if (!in_array($polylineCoordinate, $coordinates)) {
                    $coordinates[] = $polylineCoordinate;
                }
            }
        }

        return $coordinates;
    }

    /**
     * Computes the bounds of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed bounds.
     */
    protected function computeBounds(Map $map)
    {
        $bounds = array();

        if ($map->isAutoZoom() && !in_array($map->getBound(), $bounds)) {
            $bounds[] = $map->getBound();
        }

        foreach ($map->getGroundOverlays() as $groundOverlay) {
            if (!in_array($groundOverlay->getBound(), $bounds)) {
                $bounds[] = $groundOverlay->getBound();
            }
        }

        foreach ($map->getRectangles() as $rectangle) {
            if (!in_array($rectangle->getBound(), $bounds)) {
                $bounds[] = $rectangle->getBound();
            }
        }

        return $bounds;
    }

    /**
     * Computes the points of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed points.
     */
    protected function computePoints(Map $map)
    {
        $points = array();

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasIcon()) {
                if ($marker->getIcon()->hasAnchor() && !in_array($marker->getIcon()->getAnchor(), $points)) {
                    $points[] = $marker->getIcon()->getAnchor();
                }

                if ($marker->getIcon()->hasOrigin() && !in_array($marker->getIcon()->getOrigin(), $points)) {
                    $points[] = $marker->getIcon()->getOrigin();
                }
            }

            if ($marker->hasShadow()) {
                if ($marker->getShadow()->hasAnchor() && !in_array($marker->getShadow()->getAnchor(), $points)) {
                    $points[] = $marker->getShadow()->getAnchor();
                }

                if ($marker->getShadow()->hasOrigin() && !in_array($marker->getShadow()->getOrigin(), $points)) {
                    $points[] = $marker->getShadow()->getOrigin();
                }
            }
        }

        return $points;
    }

    /**
     * Computes the sizes of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed sizes.
     */
    protected function computeSizes(Map $map)
    {
        $sizes = array();

        foreach (array_merge($map->getInfoWindows(), $this->computeMarkerInfoWindows($map)) as $infoWindow) {
            if ($infoWindow->hasPixelOffset() && !in_array($infoWindow->getPixelOffset(), $sizes)) {
                $sizes[] = $infoWindow->getPixelOffset();
            }
        }

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasIcon()) {
                if ($marker->getIcon()->hasSize() && !in_array($marker->getIcon()->getSize(), $sizes)) {
                    $sizes[] = $marker->getIcon()->getSize();
                }

                if ($marker->getIcon()->hasScaledSize() && !in_array($marker->getIcon()->getScaledSize(), $sizes)) {
                    $sizes[] = $marker->getIcon()->getScaledSize();
                }
            }

            if ($marker->hasShadow()) {
                if ($marker->getShadow()->hasSize() && !in_array($marker->getShadow()->getSize(), $sizes)) {
                    $sizes[] = $marker->getShadow()->getSize();
                }

                if ($marker->getShadow()->hasScaledSize() && !in_array($marker->getShadow()->getScaledSize(), $sizes)) {
                    $sizes[] = $marker->getShadow()->getScaledSize();
                }
            }
        }

        return $sizes;
    }

    /**
     * Computes the marker images of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed marker images.
     */
    protected function computeMarkerImages(Map $map)
    {
        $markerImages = array();

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasIcon() && !in_array($marker->getIcon(), $markerImages)) {
                $markerImages[] = $marker->getIcon();
            }

            if ($marker->hasShadow() && !in_array($marker->getShadow(), $markerImages)) {
                $markerImages[] = $marker->getShadow();
            }
        }

        return $markerImages;
    }

    /**
     * Computes the marker shapes of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed marker shapes.
     */
    protected function computeMarkerShapes(Map $map)
    {
        $markerShapes = array();

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasShape() && !in_array($marker->getShape(), $markerShapes)) {
                $markerShapes[] = $marker->getShape();
            }
        }

        return $markerShapes;
    }

    /**
     * Computes the marker info windows of a map.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     *
     * @return array The computed marker info windows.
     */
    protected function computeMarkerInfoWindows(Map $map)
    {
        $infoWinfows = array();

        foreach ($map->getMarkers() as $marker) {
            if ($marker->hasInfoWindow() && !in_array($marker->getInfoWindow(), $infoWinfows)) {
                $infoWinfows[] = $marker->getInfoWindow();
            }
        }

        return $infoWinfows;
    }

    /**
     * Renders the map controls in the json builder.
     *
     * @param \Ivory\GoogleMap\Map $map The map.
     */
    protected function renderMapControls(Map $map)
    {
        $controlNames = array(
            'MapTypeControl',
            'OverviewMapControl',
            'PanControl',
            'RotateControl',
            'ScaleControl',
            'StreetViewControl',
            'ZoomControl',
        );

        foreach ($controlNames as $controlName) {
            $controlHelper = lcfirst($controlName).'Helper';
            $this->renderMapControl($map, $controlName, $this->$controlHelper);
        }
    }

    /**
     * Renders a map control in the json builder.
     *
     * @param \Ivory\GoogleMap\Map $map           The map.
     * @param string               $controlName   The control name.
     * @param mixed                $controlHelper The control helper.
     */
    protected function renderMapControl(Map $map, $controlName, $controlHelper)
    {
        $lcFirstControlName = lcfirst($controlName);

        if (!$map->hasMapOption($lcFirstControlName)) {
            return;
        }

        $this->jsonBuilder->setValue(
            sprintf('[%s]', $lcFirstControlName),
            (bool) $map->getMapOption($lcFirstControlName)
        );

        if ($map->getMapOption($lcFirstControlName)) {
            $hasControlMethod = 'has'.$controlName;
            if ($map->$hasControlMethod()) {
                $getControlMethod = 'get'.$controlName;

                $this->jsonBuilder->setValue(
                    sprintf('[%sOptions]', $lcFirstControlName),
                    $controlHelper->render($map->$getControlMethod()),
                    false
                );
            }
        }

        $map->removeMapOption($lcFirstControlName);
    }
}


<?php
/**
 * URL scheme utilities.
 *
 * @author @jaswrks
 * @copyright WebSharks™
 */
declare (strict_types = 1);
namespace WebSharks\Core\Classes\Core\Utils;

use WebSharks\Core\Classes;
use WebSharks\Core\Interfaces;
use WebSharks\Core\Traits;
#
use WebSharks\Core\Classes\Core\Error;
use WebSharks\Core\Classes\Core\Base\Exception;
#
use function assert as debug;
use function get_defined_vars as vars;

/**
 * URL scheme utilities.
 *
 * @since 150424 Initial release.
 */
class UrlScheme extends Classes\Core\Base\Core implements Interfaces\UrlConstants
{
    /**
     * Set the scheme for a URL.
     *
     * @since 150424 Initial release.
     *
     * @param string $url    Absolute URL that includes a scheme (or a `//` scheme).
     * @param string $scheme ``|`//`, `current`, `default`, `relative`, `https`, `http`, or another.
     *
     * @return string $url URL w/ a specific scheme.
     */
    public function set(string $url, string $scheme = ''): string
    {
        if (!$scheme || $scheme === '//') {
            $scheme = '//'; # Inherits.
        } elseif ($scheme === 'current') {
            $scheme = $this->c::currentScheme();
        } elseif ($scheme === 'default') {
            $scheme = $this->App->Config->©urls['©default_scheme'];
        }
        if (!$scheme) {
            throw $this->c::issue('Empty scheme.');
        }
        if ($scheme === '//') {
            $url = preg_replace('/^'.$this::URL_REGEX_FRAG_SCHEME.'/u', '//', $url);
        } elseif ($scheme === 'relative') {
            $url = preg_replace('/^'.$this::URL_REGEX_FRAG_SCHEME.'[^\/]*/u', '', $url);
        } else {
            $url = preg_replace('/^'.$this::URL_REGEX_FRAG_SCHEME.'/u', $scheme.'://', $url);
        }
        return $url;
    }
}