Seditio Source
Root |
./othercms/croogo-4.0.7/vendor/cakephp/cakephp/src/View/Helper/HtmlHelper.php
<?php
/**
 * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
 * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
 * @link          https://cakephp.org CakePHP(tm) Project
 * @since         0.9.1
 * @license       https://opensource.org/licenses/mit-license.php MIT License
 */
namespace Cake\View\Helper;

use
Cake\Core\Configure;
use
Cake\Http\Response;
use
Cake\View\Helper;
use
Cake\View\StringTemplateTrait;
use
Cake\View\View;

/**
 * Html Helper class for easy use of HTML widgets.
 *
 * HtmlHelper encloses all methods needed while working with HTML pages.
 *
 * @property \Cake\View\Helper\UrlHelper $Url
 * @link https://book.cakephp.org/3/en/views/helpers/html.html
 */
class HtmlHelper extends Helper
{
    use
StringTemplateTrait;

   
/**
     * List of helpers used by this helper
     *
     * @var array
     */
   
public $helpers = ['Url'];

   
/**
     * Reference to the Response object
     *
     * @var \Cake\Http\Response
     */
   
public $response;

   
/**
     * Default config for this class
     *
     * @var array
     */
   
protected $_defaultConfig = [
       
'templates' => [
           
'meta' => '<meta{{attrs}}/>',
           
'metalink' => '<link href="{{url}}"{{attrs}}/>',
           
'link' => '<a href="{{url}}"{{attrs}}>{{content}}</a>',
           
'mailto' => '<a href="mailto:{{url}}"{{attrs}}>{{content}}</a>',
           
'image' => '<img src="{{url}}"{{attrs}}/>',
           
'tableheader' => '<th{{attrs}}>{{content}}</th>',
           
'tableheaderrow' => '<tr{{attrs}}>{{content}}</tr>',
           
'tablecell' => '<td{{attrs}}>{{content}}</td>',
           
'tablerow' => '<tr{{attrs}}>{{content}}</tr>',
           
'block' => '<div{{attrs}}>{{content}}</div>',
           
'blockstart' => '<div{{attrs}}>',
           
'blockend' => '</div>',
           
'tag' => '<{{tag}}{{attrs}}>{{content}}</{{tag}}>',
           
'tagstart' => '<{{tag}}{{attrs}}>',
           
'tagend' => '</{{tag}}>',
           
'tagselfclosing' => '<{{tag}}{{attrs}}/>',
           
'para' => '<p{{attrs}}>{{content}}</p>',
           
'parastart' => '<p{{attrs}}>',
           
'css' => '<link rel="{{rel}}" href="{{url}}"{{attrs}}/>',
           
'style' => '<style{{attrs}}>{{content}}</style>',
           
'charset' => '<meta charset="{{charset}}"/>',
           
'ul' => '<ul{{attrs}}>{{content}}</ul>',
           
'ol' => '<ol{{attrs}}>{{content}}</ol>',
           
'li' => '<li{{attrs}}>{{content}}</li>',
           
'javascriptblock' => '<script{{attrs}}>{{content}}</script>',
           
'javascriptstart' => '<script>',
           
'javascriptlink' => '<script src="{{url}}"{{attrs}}></script>',
           
'javascriptend' => '</script>',
           
'confirmJs' => '{{confirm}}',
        ],
    ];

   
/**
     * Breadcrumbs.
     *
     * @var array
     * @deprecated 3.3.6 Use the BreadcrumbsHelper instead
     */
   
protected $_crumbs = [];

   
/**
     * Names of script & css files that have been included once
     *
     * @var array
     */
   
protected $_includedAssets = [];

   
/**
     * Options for the currently opened script block buffer if any.
     *
     * @var array
     */
   
protected $_scriptBlockOptions = [];

   
/**
     * Document type definitions
     *
     * @var string[]
     */
   
protected $_docTypes = [
       
'html4-strict' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">',
       
'html4-trans' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">',
       
'html4-frame' => '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd">',
       
'html5' => '<!DOCTYPE html>',
       
'xhtml-strict' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">',
       
'xhtml-trans' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">',
       
'xhtml-frame' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">',
       
'xhtml11' => '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">',
    ];

   
/**
     * Constructor
     *
     * ### Settings
     *
     * - `templates` Either a filename to a config containing templates.
     *   Or an array of templates to load. See Cake\View\StringTemplate for
     *   template formatting.
     *
     * ### Customizing tag sets
     *
     * Using the `templates` option you can redefine the tag HtmlHelper will use.
     *
     * @param \Cake\View\View $View The View this helper is being attached to.
     * @param array $config Configuration settings for the helper.
     */
   
public function __construct(View $View, array $config = [])
    {
       
parent::__construct($View, $config);
       
$this->response = $this->_View->getResponse() ?: new Response();
    }

   
/**
     * Adds a link to the breadcrumbs array.
     *
     * @param string $name Text for link
     * @param string|array|null $link URL for link (if empty it won't be a link)
     * @param array $options Link attributes e.g. ['id' => 'selected']
     * @return $this
     * @see \Cake\View\Helper\HtmlHelper::link() for details on $options that can be used.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-breadcrumb-trails-with-htmlhelper
     * @deprecated 3.3.6 Use the BreadcrumbsHelper instead
     */
   
public function addCrumb($name, $link = null, array $options = [])
    {
       
deprecationWarning(
           
'HtmlHelper::addCrumb() is deprecated. ' .
           
'Use the BreadcrumbsHelper instead.'
       
);

       
$this->_crumbs[] = [$name, $link, $options];

        return
$this;
    }

   
/**
     * Returns a doctype string.
     *
     * Possible doctypes:
     *
     *  - html4-strict:  HTML4 Strict.
     *  - html4-trans:  HTML4 Transitional.
     *  - html4-frame:  HTML4 Frameset.
     *  - html5: HTML5. Default value.
     *  - xhtml-strict: XHTML1 Strict.
     *  - xhtml-trans: XHTML1 Transitional.
     *  - xhtml-frame: XHTML1 Frameset.
     *  - xhtml11: XHTML1.1.
     *
     * @param string $type Doctype to use.
     * @return string|null Doctype string
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-doctype-tags
     */
   
public function docType($type = 'html5')
    {
        if (isset(
$this->_docTypes[$type])) {
            return
$this->_docTypes[$type];
        }

        return
null;
    }

   
/**
     * Creates a link to an external resource and handles basic meta tags
     *
     * Create a meta tag that is output inline:
     *
     * ```
     * $this->Html->meta('icon', 'favicon.ico');
     * ```
     *
     * Append the meta tag to custom view block "meta":
     *
     * ```
     * $this->Html->meta('description', 'A great page', ['block' => true]);
     * ```
     *
     * Append the meta tag to custom view block:
     *
     * ```
     * $this->Html->meta('description', 'A great page', ['block' => 'metaTags']);
     * ```
     *
     * Create a custom meta tag:
     *
     * ```
     * $this->Html->meta(['property' => 'og:site_name', 'content' => 'CakePHP']);
     * ```
     *
     * ### Options
     *
     * - `block` - Set to true to append output to view block "meta" or provide
     *   custom block name.
     *
     * @param string|array $type The title of the external resource, Or an array of attributes for a
     *   custom meta tag.
     * @param string|array|null $content The address of the external resource or string for content attribute
     * @param array $options Other attributes for the generated tag. If the type attribute is html,
     *    rss, atom, or icon, the mime-type is returned.
     * @return string|null A completed `<link />` element, or null if the element was sent to a block.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-meta-tags
     */
   
public function meta($type, $content = null, array $options = [])
    {
        if (!
is_array($type)) {
           
$types = [
               
'rss' => ['type' => 'application/rss+xml', 'rel' => 'alternate', 'title' => $type, 'link' => $content],
               
'atom' => ['type' => 'application/atom+xml', 'title' => $type, 'link' => $content],
               
'icon' => ['type' => 'image/x-icon', 'rel' => 'icon', 'link' => $content],
               
'keywords' => ['name' => 'keywords', 'content' => $content],
               
'description' => ['name' => 'description', 'content' => $content],
               
'robots' => ['name' => 'robots', 'content' => $content],
               
'viewport' => ['name' => 'viewport', 'content' => $content],
               
'canonical' => ['rel' => 'canonical', 'link' => $content],
               
'next' => ['rel' => 'next', 'link' => $content],
               
'prev' => ['rel' => 'prev', 'link' => $content],
               
'first' => ['rel' => 'first', 'link' => $content],
               
'last' => ['rel' => 'last', 'link' => $content],
            ];

            if (
$type === 'icon' && $content === null) {
               
$types['icon']['link'] = 'favicon.ico';
            }

            if (isset(
$types[$type])) {
               
$type = $types[$type];
            } elseif (!isset(
$options['type']) && $content !== null) {
                if (
is_array($content) && isset($content['_ext'])) {
                   
$type = $types[$content['_ext']];
                } else {
                   
$type = ['name' => $type, 'content' => $content];
                }
            } elseif (isset(
$options['type'], $types[$options['type']])) {
               
$type = $types[$options['type']];
                unset(
$options['type']);
            } else {
               
$type = [];
            }
        }

       
$options += $type + ['block' => null];
       
$out = null;

        if (isset(
$options['link'])) {
           
$options['link'] = $this->Url->assetUrl($options['link']);
            if (isset(
$options['rel']) && $options['rel'] === 'icon') {
               
$out = $this->formatTemplate('metalink', [
                   
'url' => $options['link'],
                   
'attrs' => $this->templater()->formatAttributes($options, ['block', 'link']),
                ]);
               
$options['rel'] = 'shortcut icon';
            }
           
$out .= $this->formatTemplate('metalink', [
               
'url' => $options['link'],
               
'attrs' => $this->templater()->formatAttributes($options, ['block', 'link']),
            ]);
        } else {
           
$out = $this->formatTemplate('meta', [
               
'attrs' => $this->templater()->formatAttributes($options, ['block', 'type']),
            ]);
        }

        if (empty(
$options['block'])) {
            return
$out;
        }
        if (
$options['block'] === true) {
           
$options['block'] = __FUNCTION__;
        }
       
$this->_View->append($options['block'], $out);
    }

   
/**
     * Returns a charset META-tag.
     *
     * @param string|null $charset The character set to be used in the meta tag. If empty,
     *  The App.encoding value will be used. Example: "utf-8".
     * @return string A meta tag containing the specified character set.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-charset-tags
     */
   
public function charset($charset = null)
    {
        if (empty(
$charset)) {
           
$charset = strtolower(Configure::read('App.encoding'));
        }

        return
$this->formatTemplate('charset', [
           
'charset' => !empty($charset) ? $charset : 'utf-8',
        ]);
    }

   
/**
     * Creates an HTML link.
     *
     * If $url starts with "http://" this is treated as an external link. Else,
     * it is treated as a path to controller/action and parsed with the
     * UrlHelper::build() method.
     *
     * If the $url is empty, $title is used instead.
     *
     * ### Options
     *
     * - `escape` Set to false to disable escaping of title and attributes.
     * - `escapeTitle` Set to false to disable escaping of title. Takes precedence
     *   over value of `escape`)
     * - `confirm` JavaScript confirmation message.
     *
     * @param string|array $title The content to be wrapped by `<a>` tags.
     *   Can be an array if $url is null. If $url is null, $title will be used as both the URL and title.
     * @param string|array|null $url Cake-relative URL or array of URL parameters, or
     *   external URL (starts with http://)
     * @param array $options Array of options and HTML attributes.
     * @return string An `<a />` element.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-links
     */
   
public function link($title, $url = null, array $options = [])
    {
       
$escapeTitle = true;
        if (
$url !== null) {
           
$url = $this->Url->build($url, $options);
            unset(
$options['fullBase']);
        } else {
           
$url = $this->Url->build($title);
           
$title = htmlspecialchars_decode($url, ENT_QUOTES);
           
$title = h(urldecode($title));
           
$escapeTitle = false;
        }

        if (isset(
$options['escapeTitle'])) {
           
$escapeTitle = $options['escapeTitle'];
            unset(
$options['escapeTitle']);
        } elseif (isset(
$options['escape'])) {
           
$escapeTitle = $options['escape'];
        }

        if (
$escapeTitle === true) {
           
$title = h($title);
        } elseif (
is_string($escapeTitle)) {
           
$title = htmlentities($title, ENT_QUOTES, $escapeTitle);
        }

       
$templater = $this->templater();
       
$confirmMessage = null;
        if (isset(
$options['confirm'])) {
           
$confirmMessage = $options['confirm'];
            unset(
$options['confirm']);
        }
        if (
$confirmMessage) {
           
$confirm = $this->_confirm($confirmMessage, 'return true;', 'return false;', $options);
           
$options['onclick'] = $templater->format('confirmJs', [
               
'confirmMessage' => $this->_cleanConfirmMessage($confirmMessage),
               
'confirm' => $confirm,
            ]);
        }

        return
$templater->format('link', [
           
'url' => $url,
           
'attrs' => $templater->formatAttributes($options),
           
'content' => $title,
        ]);
    }

   
/**
     * Creates a link element for CSS stylesheets.
     *
     * ### Usage
     *
     * Include one CSS file:
     *
     * ```
     * echo $this->Html->css('styles.css');
     * ```
     *
     * Include multiple CSS files:
     *
     * ```
     * echo $this->Html->css(['one.css', 'two.css']);
     * ```
     *
     * Add the stylesheet to view block "css":
     *
     * ```
     * $this->Html->css('styles.css', ['block' => true]);
     * ```
     *
     * Add the stylesheet to a custom block:
     *
     * ```
     * $this->Html->css('styles.css', ['block' => 'layoutCss']);
     * ```
     *
     * ### Options
     *
     * - `block` Set to true to append output to view block "css" or provide
     *   custom block name.
     * - `once` Whether or not the css file should be checked for uniqueness. If true css
     *   files  will only be included once, use false to allow the same
     *   css to be included more than once per request.
     * - `plugin` False value will prevent parsing path as a plugin
     * - `rel` Defaults to 'stylesheet'. If equal to 'import' the stylesheet will be imported.
     * - `fullBase` If true the URL will get a full address for the css file.
     *
     * @param string|string[] $path The name of a CSS style sheet or an array containing names of
     *   CSS stylesheets. If `$path` is prefixed with '/', the path will be relative to the webroot
     *   of your application. Otherwise, the path will be relative to your CSS path, usually webroot/css.
     * @param array $options Array of options and HTML arguments.
     * @return string|null CSS `<link />` or `<style />` tag, depending on the type of link.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#linking-to-css-files
     */
   
public function css($path, array $options = [])
    {
       
$options += ['once' => true, 'block' => null, 'rel' => 'stylesheet'];

        if (
is_array($path)) {
           
$out = '';
            foreach (
$path as $i) {
               
$out .= "\n\t" . $this->css($i, $options);
            }
            if (empty(
$options['block'])) {
                return
$out . "\n";
            }

            return
null;
        }

        if (
strpos($path, '//') !== false) {
           
$url = $path;
        } else {
           
$url = $this->Url->css($path, $options);
           
$options = array_diff_key($options, ['fullBase' => null, 'pathPrefix' => null]);
        }

        if (
$options['once'] && isset($this->_includedAssets[__METHOD__][$path])) {
            return
null;
        }
        unset(
$options['once']);
       
$this->_includedAssets[__METHOD__][$path] = true;
       
$templater = $this->templater();

        if (
$options['rel'] === 'import') {
           
$out = $templater->format('style', [
               
'attrs' => $templater->formatAttributes($options, ['rel', 'block']),
               
'content' => '@import url(' . $url . ');',
            ]);
        } else {
           
$out = $templater->format('css', [
               
'rel' => $options['rel'],
               
'url' => $url,
               
'attrs' => $templater->formatAttributes($options, ['rel', 'block']),
            ]);
        }

        if (empty(
$options['block'])) {
            return
$out;
        }
        if (
$options['block'] === true) {
           
$options['block'] = __FUNCTION__;
        }
       
$this->_View->append($options['block'], $out);
    }

   
/**
     * Returns one or many `<script>` tags depending on the number of scripts given.
     *
     * If the filename is prefixed with "/", the path will be relative to the base path of your
     * application. Otherwise, the path will be relative to your JavaScript path, usually webroot/js.
     *
     * ### Usage
     *
     * Include one script file:
     *
     * ```
     * echo $this->Html->script('styles.js');
     * ```
     *
     * Include multiple script files:
     *
     * ```
     * echo $this->Html->script(['one.js', 'two.js']);
     * ```
     *
     * Add the script file to a custom block:
     *
     * ```
     * $this->Html->script('styles.js', ['block' => 'bodyScript']);
     * ```
     *
     * ### Options
     *
     * - `block` Set to true to append output to view block "script" or provide
     *   custom block name.
     * - `once` Whether or not the script should be checked for uniqueness. If true scripts will only be
     *   included once, use false to allow the same script to be included more than once per request.
     * - `plugin` False value will prevent parsing path as a plugin
     * - `fullBase` If true the url will get a full address for the script file.
     *
     * @param string|string[] $url String or array of javascript files to include
     * @param array $options Array of options, and html attributes see above.
     * @return string|null String of `<script />` tags or null if block is specified in options
     *   or if $once is true and the file has been included before.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#linking-to-javascript-files
     */
   
public function script($url, array $options = [])
    {
       
$defaults = ['block' => null, 'once' => true];
       
$options += $defaults;

        if (
is_array($url)) {
           
$out = '';
            foreach (
$url as $i) {
               
$out .= "\n\t" . $this->script($i, $options);
            }
            if (empty(
$options['block'])) {
                return
$out . "\n";
            }

            return
null;
        }

        if (
strpos($url, '//') === false) {
           
$url = $this->Url->script($url, $options);
           
$options = array_diff_key($options, ['fullBase' => null, 'pathPrefix' => null]);
        }
        if (
$options['once'] && isset($this->_includedAssets[__METHOD__][$url])) {
            return
null;
        }
       
$this->_includedAssets[__METHOD__][$url] = true;

       
$out = $this->formatTemplate('javascriptlink', [
           
'url' => $url,
           
'attrs' => $this->templater()->formatAttributes($options, ['block', 'once']),
        ]);

        if (empty(
$options['block'])) {
            return
$out;
        }
        if (
$options['block'] === true) {
           
$options['block'] = __FUNCTION__;
        }
       
$this->_View->append($options['block'], $out);
    }

   
/**
     * Wrap $script in a script tag.
     *
     * ### Options
     *
     * - `safe` (boolean) Whether or not the $script should be wrapped in `<![CDATA[ ]]>`.
     *   Defaults to `false`.
     * - `block` Set to true to append output to view block "script" or provide
     *   custom block name.
     *
     * @param string $script The script to wrap
     * @param array $options The options to use. Options not listed above will be
     *    treated as HTML attributes.
     * @return string|null String or null depending on the value of `$options['block']`
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-inline-javascript-blocks
     */
   
public function scriptBlock($script, array $options = [])
    {
       
$options += ['safe' => false, 'block' => null];
        if (
$options['safe']) {
           
$script = "\n" . '//<![CDATA[' . "\n" . $script . "\n" . '//]]>' . "\n";
        }
        unset(
$options['safe']);

       
$out = $this->formatTemplate('javascriptblock', [
           
'attrs' => $this->templater()->formatAttributes($options, ['block']),
           
'content' => $script,
        ]);

        if (empty(
$options['block'])) {
            return
$out;
        }
        if (
$options['block'] === true) {
           
$options['block'] = 'script';
        }
       
$this->_View->append($options['block'], $out);
    }

   
/**
     * Begin a script block that captures output until HtmlHelper::scriptEnd()
     * is called. This capturing block will capture all output between the methods
     * and create a scriptBlock from it.
     *
     * ### Options
     *
     * - `safe` (boolean) Whether or not the $script should be wrapped in `<![CDATA[ ]]>`.
     *   See scriptBlock().
     * - `block` Set to true to append output to view block "script" or provide
     *   custom block name.
     *
     * @param array $options Options for the code block.
     * @return void
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-inline-javascript-blocks
     */
   
public function scriptStart(array $options = [])
    {
       
$this->_scriptBlockOptions = $options;
       
ob_start();
    }

   
/**
     * End a Buffered section of JavaScript capturing.
     * Generates a script tag inline or appends to specified view block depending on
     * the settings used when the scriptBlock was started
     *
     * @return string|null Depending on the settings of scriptStart() either a script tag or null
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-inline-javascript-blocks
     */
   
public function scriptEnd()
    {
       
$buffer = ob_get_clean();
       
$options = $this->_scriptBlockOptions;
       
$this->_scriptBlockOptions = [];

        return
$this->scriptBlock($buffer, $options);
    }

   
/**
     * Builds CSS style data from an array of CSS properties
     *
     * ### Usage:
     *
     * ```
     * echo $this->Html->style(['margin' => '10px', 'padding' => '10px'], true);
     *
     * // creates
     * 'margin:10px;padding:10px;'
     * ```
     *
     * @param array $data Style data array, keys will be used as property names, values as property values.
     * @param bool $oneLine Whether or not the style block should be displayed on one line.
     * @return string CSS styling data
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-css-programatically
     */
   
public function style(array $data, $oneLine = true)
    {
       
$out = [];
        foreach (
$data as $key => $value) {
           
$out[] = $key . ':' . $value . ';';
        }
        if (
$oneLine) {
            return
implode(' ', $out);
        }

        return
implode("\n", $out);
    }

   
/**
     * Returns the breadcrumb trail as a sequence of &raquo;-separated links.
     *
     * If `$startText` is an array, the accepted keys are:
     *
     * - `text` Define the text/content for the link.
     * - `url` Define the target of the created link.
     *
     * All other keys will be passed to HtmlHelper::link() as the `$options` parameter.
     *
     * @param string $separator Text to separate crumbs.
     * @param string|array|bool $startText This will be the first crumb, if false it defaults to first crumb in array. Can
     *   also be an array, see above for details.
     * @return string|null Composed bread crumbs
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-breadcrumb-trails-with-htmlhelper
     * @deprecated 3.3.6 Use the BreadcrumbsHelper instead
     */
   
public function getCrumbs($separator = '&raquo;', $startText = false)
    {
       
deprecationWarning(
           
'HtmlHelper::getCrumbs() is deprecated. ' .
           
'Use the BreadcrumbsHelper instead.'
       
);

       
$crumbs = $this->_prepareCrumbs($startText);
        if (!empty(
$crumbs)) {
           
$out = [];
            foreach (
$crumbs as $crumb) {
                if (!empty(
$crumb[1])) {
                   
$out[] = $this->link($crumb[0], $crumb[1], $crumb[2]);
                } else {
                   
$out[] = $crumb[0];
                }
            }

            return
implode($separator, $out);
        }

        return
null;
    }

   
/**
     * Returns breadcrumbs as a (x)html list
     *
     * This method uses HtmlHelper::tag() to generate list and its elements. Works
     * similar to HtmlHelper::getCrumbs(), so it uses options which every
     * crumb was added with.
     *
     * ### Options
     *
     * - `separator` Separator content to insert in between breadcrumbs, defaults to ''
     * - `firstClass` Class for wrapper tag on the first breadcrumb, defaults to 'first'
     * - `lastClass` Class for wrapper tag on current active page, defaults to 'last'
     *
     * @param array $options Array of HTML attributes to apply to the generated list elements.
     * @param string|array|bool $startText This will be the first crumb, if false it defaults to first crumb in array. Can
     *   also be an array, see `HtmlHelper::getCrumbs` for details.
     * @return string|null Breadcrumbs HTML list.
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-breadcrumb-trails-with-htmlhelper
     * @deprecated 3.3.6 Use the BreadcrumbsHelper instead
     */
   
public function getCrumbList(array $options = [], $startText = false)
    {
       
deprecationWarning(
           
'HtmlHelper::getCrumbList() is deprecated. ' .
           
'Use the BreadcrumbsHelper instead.'
       
);

       
$defaults = ['firstClass' => 'first', 'lastClass' => 'last', 'separator' => '', 'escape' => true];
       
$options += $defaults;
       
$firstClass = $options['firstClass'];
       
$lastClass = $options['lastClass'];
       
$separator = $options['separator'];
       
$escape = $options['escape'];
        unset(
$options['firstClass'], $options['lastClass'], $options['separator'], $options['escape']);

       
$crumbs = $this->_prepareCrumbs($startText, $escape);
        if (empty(
$crumbs)) {
            return
null;
        }

       
$result = '';
       
$crumbCount = count($crumbs);
       
$ulOptions = $options;
        foreach (
$crumbs as $which => $crumb) {
           
$options = [];
            if (empty(
$crumb[1])) {
               
$elementContent = $crumb[0];
            } else {
               
$elementContent = $this->link($crumb[0], $crumb[1], $crumb[2]);
            }
            if (!
$which && $firstClass !== false) {
               
$options['class'] = $firstClass;
            } elseif (
$which == $crumbCount - 1 && $lastClass !== false) {
               
$options['class'] = $lastClass;
            }
            if (!empty(
$separator) && ($crumbCount - $which >= 2)) {
               
$elementContent .= $separator;
            }
           
$result .= $this->formatTemplate('li', [
               
'content' => $elementContent,
               
'attrs' => $this->templater()->formatAttributes($options),
            ]);
        }

        return
$this->formatTemplate('ul', [
           
'content' => $result,
           
'attrs' => $this->templater()->formatAttributes($ulOptions),
        ]);
    }

   
/**
     * Prepends startText to crumbs array if set
     *
     * @param string|array|bool $startText Text to prepend
     * @param bool $escape If the output should be escaped or not
     * @return array Crumb list including startText (if provided)
     * @deprecated 3.3.6 Use the BreadcrumbsHelper instead
     */
   
protected function _prepareCrumbs($startText, $escape = true)
    {
       
deprecationWarning(
           
'HtmlHelper::_prepareCrumbs() is deprecated. ' .
           
'Use the BreadcrumbsHelper instead.'
       
);

       
$crumbs = $this->_crumbs;
        if (
$startText) {
            if (!
is_array($startText)) {
               
$startText = [
                   
'url' => '/',
                   
'text' => $startText,
                ];
            }
           
$startText += ['url' => '/', 'text' => __d('cake', 'Home')];
            list(
$url, $text) = [$startText['url'], $startText['text']];
            unset(
$startText['url'], $startText['text']);
           
array_unshift($crumbs, [$text, $url, $startText + ['escape' => $escape]]);
        }

        return
$crumbs;
    }

   
/**
     * Creates a formatted IMG element.
     *
     * This method will set an empty alt attribute if one is not supplied.
     *
     * ### Usage:
     *
     * Create a regular image:
     *
     * ```
     * echo $this->Html->image('cake_icon.png', ['alt' => 'CakePHP']);
     * ```
     *
     * Create an image link:
     *
     * ```
     * echo $this->Html->image('cake_icon.png', ['alt' => 'CakePHP', 'url' => 'https://cakephp.org']);
     * ```
     *
     * ### Options:
     *
     * - `url` If provided an image link will be generated and the link will point at
     *   `$options['url']`.
     * - `fullBase` If true the src attribute will get a full address for the image file.
     * - `plugin` False value will prevent parsing path as a plugin
     *
     * @param string|array $path Path to the image file, relative to the app/webroot/img/ directory.
     * @param array $options Array of HTML attributes. See above for special options.
     * @return string completed img tag
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#linking-to-images
     */
   
public function image($path, array $options = [])
    {
       
$path = $this->Url->image($path, $options);
       
$options = array_diff_key($options, ['fullBase' => null, 'pathPrefix' => null]);

        if (!isset(
$options['alt'])) {
           
$options['alt'] = '';
        }

       
$url = false;
        if (!empty(
$options['url'])) {
           
$url = $options['url'];
            unset(
$options['url']);
        }

       
$templater = $this->templater();
       
$image = $templater->format('image', [
           
'url' => $path,
           
'attrs' => $templater->formatAttributes($options),
        ]);

        if (
$url) {
            return
$templater->format('link', [
               
'url' => $this->Url->build($url),
               
'attrs' => null,
               
'content' => $image,
            ]);
        }

        return
$image;
    }

   
/**
     * Returns a row of formatted and named TABLE headers.
     *
     * @param array $names Array of tablenames. Each tablename also can be a key that points to an array with a set
     *     of attributes to its specific tag
     * @param array|null $trOptions HTML options for TR elements.
     * @param array|null $thOptions HTML options for TH elements.
     * @return string Completed table headers
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-table-headings
     */
   
public function tableHeaders(array $names, array $trOptions = null, array $thOptions = null)
    {
       
$out = [];
        foreach (
$names as $arg) {
            if (!
is_array($arg)) {
               
$out[] = $this->formatTemplate('tableheader', [
                   
'attrs' => $this->templater()->formatAttributes($thOptions),
                   
'content' => $arg,
                ]);
            } else {
               
$out[] = $this->formatTemplate('tableheader', [
                   
'attrs' => $this->templater()->formatAttributes(current($arg)),
                   
'content' => key($arg),
                ]);
            }
        }

        return
$this->tableRow(implode(' ', $out), (array)$trOptions);
    }

   
/**
     * Returns a formatted string of table rows (TR's with TD's in them).
     *
     * @param array|string $data Array of table data
     * @param array|bool|null $oddTrOptions HTML options for odd TR elements if true useCount is used
     * @param array|bool|null $evenTrOptions HTML options for even TR elements
     * @param bool $useCount adds class "column-$i"
     * @param bool $continueOddEven If false, will use a non-static $count variable,
     *    so that the odd/even count is reset to zero just for that call.
     * @return string Formatted HTML
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-table-cells
     */
   
public function tableCells($data, $oddTrOptions = null, $evenTrOptions = null, $useCount = false, $continueOddEven = true)
    {
        if (empty(
$data[0]) || !is_array($data[0])) {
           
$data = [$data];
        }

        if (
$oddTrOptions === true) {
           
$useCount = true;
           
$oddTrOptions = null;
        }

        if (
$evenTrOptions === false) {
           
$continueOddEven = false;
           
$evenTrOptions = null;
        }

        if (
$continueOddEven) {
            static
$count = 0;
        } else {
           
$count = 0;
        }

       
$out = [];
        foreach (
$data as $line) {
           
$count++;
           
$cellsOut = $this->_renderCells($line, $useCount);
           
$opts = $count % 2 ? $oddTrOptions : $evenTrOptions;
           
$out[] = $this->tableRow(implode(' ', $cellsOut), (array)$opts);
        }

        return
implode("\n", $out);
    }

   
/**
     * Renders cells for a row of a table.
     *
     * This is a helper method for tableCells(). Overload this method as you
     * need to change the behavior of the cell rendering.
     *
     * @param array $line Line data to render.
     * @param bool $useCount Renders the count into the row. Default is false.
     * @return string[]
     */
   
protected function _renderCells($line, $useCount = false)
    {
       
$i = 0;
       
$cellsOut = [];
        foreach (
$line as $cell) {
           
$cellOptions = [];

            if (
is_array($cell)) {
               
$cellOptions = $cell[1];
               
$cell = $cell[0];
            }

            if (
$useCount) {
               
$i += 1;
                if (isset(
$cellOptions['class'])) {
                   
$cellOptions['class'] .= ' column-' . $i;
                } else {
                   
$cellOptions['class'] = 'column-' . $i;
                }
            }

           
$cellsOut[] = $this->tableCell($cell, $cellOptions);
        }

        return
$cellsOut;
    }

   
/**
     * Renders a single table row (A TR with attributes).
     *
     * @param string $content The content of the row.
     * @param array $options HTML attributes.
     * @return string
     */
   
public function tableRow($content, array $options = [])
    {
        return
$this->formatTemplate('tablerow', [
           
'attrs' => $this->templater()->formatAttributes($options),
           
'content' => $content,
        ]);
    }

   
/**
     * Renders a single table cell (A TD with attributes).
     *
     * @param string $content The content of the cell.
     * @param array $options HTML attributes.
     * @return string
     */
   
public function tableCell($content, array $options = [])
    {
        return
$this->formatTemplate('tablecell', [
           
'attrs' => $this->templater()->formatAttributes($options),
           
'content' => $content,
        ]);
    }

   
/**
     * Returns a formatted block tag, i.e DIV, SPAN, P.
     *
     * ### Options
     *
     * - `escape` Whether or not the contents should be html_entity escaped.
     *
     * @param string $name Tag name.
     * @param string|null $text String content that will appear inside the div element.
     *   If null, only a start tag will be printed
     * @param array $options Additional HTML attributes of the DIV tag, see above.
     * @return string The formatted tag element
     */
   
public function tag($name, $text = null, array $options = [])
    {
        if (empty(
$name)) {
            return
$text;
        }
        if (isset(
$options['escape']) && $options['escape']) {
           
$text = h($text);
            unset(
$options['escape']);
        }
        if (
$text === null) {
           
$tag = 'tagstart';
        } else {
           
$tag = 'tag';
        }

        return
$this->formatTemplate($tag, [
           
'attrs' => $this->templater()->formatAttributes($options),
           
'tag' => $name,
           
'content' => $text,
        ]);
    }

   
/**
     * Returns a formatted DIV tag for HTML FORMs.
     *
     * ### Options
     *
     * - `escape` Whether or not the contents should be html_entity escaped.
     *
     * @param string|null $class CSS class name of the div element.
     * @param string|null $text String content that will appear inside the div element.
     *   If null, only a start tag will be printed
     * @param array $options Additional HTML attributes of the DIV tag
     * @return string The formatted DIV element
     */
   
public function div($class = null, $text = null, array $options = [])
    {
        if (!empty(
$class)) {
           
$options['class'] = $class;
        }

        return
$this->tag('div', $text, $options);
    }

   
/**
     * Returns a formatted P tag.
     *
     * ### Options
     *
     * - `escape` Whether or not the contents should be html_entity escaped.
     *
     * @param string $class CSS class name of the p element.
     * @param string $text String content that will appear inside the p element.
     * @param array $options Additional HTML attributes of the P tag
     * @return string The formatted P element
     */
   
public function para($class, $text, array $options = [])
    {
        if (!empty(
$options['escape'])) {
           
$text = h($text);
        }
        if (
$class) {
           
$options['class'] = $class;
        }
       
$tag = 'para';
        if (
$text === null) {
           
$tag = 'parastart';
        }

        return
$this->formatTemplate($tag, [
           
'attrs' => $this->templater()->formatAttributes($options),
           
'content' => $text,
        ]);
    }

   
/**
     * Returns an audio/video element
     *
     * ### Usage
     *
     * Using an audio file:
     *
     * ```
     * echo $this->Html->media('audio.mp3', ['fullBase' => true]);
     * ```
     *
     * Outputs:
     *
     * ```
     * <video src="http://www.somehost.com/files/audio.mp3">Fallback text</video>
     * ```
     *
     * Using a video file:
     *
     * ```
     * echo $this->Html->media('video.mp4', ['text' => 'Fallback text']);
     * ```
     *
     * Outputs:
     *
     * ```
     * <video src="/files/video.mp4">Fallback text</video>
     * ```
     *
     * Using multiple video files:
     *
     * ```
     * echo $this->Html->media(
     *      ['video.mp4', ['src' => 'video.ogv', 'type' => "video/ogg; codecs='theora, vorbis'"]],
     *      ['tag' => 'video', 'autoplay']
     * );
     * ```
     *
     * Outputs:
     *
     * ```
     * <video autoplay="autoplay">
     *      <source src="/files/video.mp4" type="video/mp4"/>
     *      <source src="/files/video.ogv" type="video/ogv; codecs='theora, vorbis'"/>
     * </video>
     * ```
     *
     * ### Options
     *
     * - `tag` Type of media element to generate, either "audio" or "video".
     *  If tag is not provided it's guessed based on file's mime type.
     * - `text` Text to include inside the audio/video tag
     * - `pathPrefix` Path prefix to use for relative URLs, defaults to 'files/'
     * - `fullBase` If provided the src attribute will get a full address including domain name
     *
     * @param string|array $path Path to the video file, relative to the webroot/{$options['pathPrefix']} directory.
     *  Or an array where each item itself can be a path string or an associate array containing keys `src` and `type`
     * @param array $options Array of HTML attributes, and special options above.
     * @return string Generated media element
     */
   
public function media($path, array $options = [])
    {
       
$options += [
           
'tag' => null,
           
'pathPrefix' => 'files/',
           
'text' => '',
        ];

        if (!empty(
$options['tag'])) {
           
$tag = $options['tag'];
        } else {
           
$tag = null;
        }

        if (
is_array($path)) {
           
$sourceTags = '';
            foreach (
$path as &$source) {
                if (
is_string($source)) {
                   
$source = [
                       
'src' => $source,
                    ];
                }
                if (!isset(
$source['type'])) {
                   
$ext = pathinfo($source['src'], PATHINFO_EXTENSION);
                   
$source['type'] = $this->response->getMimeType($ext);
                }
               
$source['src'] = $this->Url->assetUrl($source['src'], $options);
               
$sourceTags .= $this->formatTemplate('tagselfclosing', [
                   
'tag' => 'source',
                   
'attrs' => $this->templater()->formatAttributes($source),
                ]);
            }
            unset(
$source);
           
$options['text'] = $sourceTags . $options['text'];
            unset(
$options['fullBase']);
        } else {
            if (empty(
$path) && !empty($options['src'])) {
               
$path = $options['src'];
            }
           
$options['src'] = $this->Url->assetUrl($path, $options);
        }

        if (
$tag === null) {
            if (
is_array($path)) {
               
$mimeType = $path[0]['type'];
            } else {
               
$mimeType = $this->response->getMimeType(pathinfo($path, PATHINFO_EXTENSION));
            }
            if (
preg_match('#^video/#', $mimeType)) {
               
$tag = 'video';
            } else {
               
$tag = 'audio';
            }
        }

        if (isset(
$options['poster'])) {
           
$options['poster'] = $this->Url->assetUrl($options['poster'], ['pathPrefix' => Configure::read('App.imageBaseUrl')] + $options);
        }
       
$text = $options['text'];

       
$options = array_diff_key($options, [
           
'tag' => null,
           
'fullBase' => null,
           
'pathPrefix' => null,
           
'text' => null,
        ]);

        return
$this->tag($tag, $text, $options);
    }

   
/**
     * Build a nested list (UL/OL) out of an associative array.
     *
     * Options for $options:
     *
     * - `tag` - Type of list tag to use (ol/ul)
     *
     * Options for $itemOptions:
     *
     * - `even` - Class to use for even rows.
     * - `odd` - Class to use for odd rows.
     *
     * @param array $list Set of elements to list
     * @param array $options Options and additional HTML attributes of the list (ol/ul) tag.
     * @param array $itemOptions Options and additional HTML attributes of the list item (LI) tag.
     * @return string The nested list
     * @link https://book.cakephp.org/3/en/views/helpers/html.html#creating-nested-lists
     */
   
public function nestedList(array $list, array $options = [], array $itemOptions = [])
    {
       
$options += ['tag' => 'ul'];
       
$items = $this->_nestedListItem($list, $options, $itemOptions);

        return
$this->formatTemplate($options['tag'], [
           
'attrs' => $this->templater()->formatAttributes($options, ['tag']),
           
'content' => $items,
        ]);
    }

   
/**
     * Internal function to build a nested list (UL/OL) out of an associative array.
     *
     * @param array $items Set of elements to list.
     * @param array $options Additional HTML attributes of the list (ol/ul) tag.
     * @param array $itemOptions Options and additional HTML attributes of the list item (LI) tag.
     * @return string The nested list element
     * @see \Cake\View\Helper\HtmlHelper::nestedList()
     */
   
protected function _nestedListItem($items, $options, $itemOptions)
    {
       
$out = '';

       
$index = 1;
        foreach (
$items as $key => $item) {
            if (
is_array($item)) {
               
$item = $key . $this->nestedList($item, $options, $itemOptions);
            }
            if (isset(
$itemOptions['even']) && $index % 2 === 0) {
               
$itemOptions['class'] = $itemOptions['even'];
            } elseif (isset(
$itemOptions['odd']) && $index % 2 !== 0) {
               
$itemOptions['class'] = $itemOptions['odd'];
            }
           
$out .= $this->formatTemplate('li', [
               
'attrs' => $this->templater()->formatAttributes($itemOptions, ['even', 'odd']),
               
'content' => $item,
            ]);
           
$index++;
        }

        return
$out;
    }

   
/**
     * Event listeners.
     *
     * @return array
     */
   
public function implementedEvents()
    {
        return [];
    }
}