| Current Path : /home/digilove/www/41423/ |
| Current File : /home/digilove/www/41423/aimyspeedoptimization.tar |
fields/aimydownloadkey.php 0000644 00000002401 15234451223 0011721 0 ustar 00 <?php
/*
* Copyright (c) 2024 Aimy Extensions, Netzum Sorglos Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); require_once( __DIR__ . '/../helpers/DownloadKeyHelper.php' ); use Joomla\CMS\Form\FormField; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use AimySpeedOptimization\Helpers\DownloadKeyHelper; class JFormFieldAimyDownloadKey extends FormField { protected $type = 'aimydownloadkey'; protected $layout = 'joomla.form.field.text'; public function getInput() { if ( strpos( JVERSION, '3.' ) !== 0 ) { Factory::getDocument() ->addStyleDeclaration( '#fieldset-key .control-group { align-items: center; }' ); return '<div>' . Text::sprintf( 'AIMY_SO_DL_KEY_USE_DL_KEY_MANAGER', DownloadKeyHelper::getEditLink() ) . '</div>'; } Factory::getDocument() ->addStyleDeclaration( '#jform_params_dl_key,#jform_dl_key { min-width: 250px; }' ); return $this->getRenderer( $this->layout ) ->render( $this->getLayoutData() ); } protected function getLayoutData() { return array_merge( array( 'options' => array(), 'addonBefore' => '', 'addonAfter' => '', 'dirname' => '' ), parent::getLayoutData() ); } }
fields/index.html 0000644 00000000016 15234451223 0010005 0 ustar 00 <html></html>
helpers/DownloadKeyHelper.php 0000644 00000014660 15234451223 0012307 0 ustar 00 <?php
/*
* Copyright (c) 2024 Aimy Extensions, Netzum Sorglos Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* http://www.aimy-extensions.com/software-license.html
*/
namespace AimySpeedOptimization\Helpers; defined( '_JEXEC' ) or die(); use Joomla\CMS\Factory; use Joomla\CMS\Http\HttpFactory; use Joomla\CMS\Language\Text; use Joomla\CMS\HTML\HTMLHelper; use Joomla\Registry\Registry; abstract class DownloadKeyHelper { const V_KEY_MISSING = 0; const V_KEY_INVALID = 1; const V_KEY_DENIED = 2; const V_KEY_EXPIRED = 3; const V_KEY_GRANTED = 4; const V_KEY_FAILED = 5; static private $lastError = ''; static private $lastResponseData = array(); static private $serverBaseUrl = 'http://dl.aimy-extensions.com'; static public function getUpdatesiteId() { $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( 'update_site_id' ) ) ->from( $db->quoteName( '#__update_sites' ) ) ->where( $db->quoteName( 'name' ) . ' = ' . $db->quote( self::getUpdateServerName() ) ); $db->setQuery( $q ); return $db->loadResult(); } static public function getExtensionId() { $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( 'extension_id' ) ) ->from( $db->quoteName( '#__extensions' ) ) ->where( $db->quoteName( 'element' ) . ' = ' . $db->quote( self::getElementName() ) ); $db->setQuery( $q ); return $db->loadResult(); } static public function getEditLink() { if ( self::isJoomla3() ) { if ( 'plg' == 'com' ) { return 'index.php?option=com_config&view=component&' . 'component=com_aimyspeedoptimization'; } $link = 'index.php?option=com_plugins'; if ( ! empty( $id = self::getExtensionId() ) ) { return $link . '&task=plugin.edit&extension_id=' . $id; } return $link . '&view=plugins'; } if ( $id = self::getUpdatesiteId() ) { return 'index.php?option=com_installer&' . 'task=updatesite.edit&update_site_id=' . $id; } return 'index.php?option=com_installer&view=updatesite'; } static public function passKeyToUpdatesites( $key ) { $id = self::getUpdateSiteId(); if ( ! $id ) { throw new \RuntimeException( 'Failed to determine update site id of AimySpeedOptimization' ); } $eqVal = ''; if ( ! empty( $key ) ) { $key = trim( $key ); if ( ! empty( $key ) ) { $eqVal = 'dl_key=' . $key; } } $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->update( $db->quoteName( '#__update_sites' ) ) ->set( $db->quoteName( 'extra_query' ) . ' = ' . $db->quote( $eqVal ) ) ->where( $db->quoteName( 'update_site_id' ) . ' = ' . $db->quote( $id ) ); $db->setQuery( $q ); return $db->execute(); } static public function getKey() { $key = self::getKeyFromUpdatesites(); if ( self::isJoomla3() && empty( $key ) ) { $key = self::getKeyFromParams(); } return $key; } static public function getKeyFromUpdatesites() { $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( 'extra_query' ) ) ->from( $db->quoteName( '#__update_sites' ) ) ->where( $db->quoteName( 'name' ) . ' = ' . $db->quote( self::getUpdateServerName() ) ); $db->setQuery( $q ); $rv = $db->loadResult(); if ( ! empty( $rv ) && preg_match( '#dl_key=(\S+)#', $rv, $m ) ) { return $m[ 1 ]; } return false; } static public function getKeyFromParams() { $params = self::getParams(); if ( $params->exists( 'dl_key' ) ) { return trim( $params->get( 'dl_key' ) ); } return false; } static public function validateKey( $key = false ) { if ( empty( $key ) ) { $key = self::getKey(); } $rv = new \StdClass(); $rv->state = self::sendValidationRequest( $key ); $rv->msg = false; $rv->data = self::$lastResponseData; $rv->valid = false; self::loadLanguageStrings(); switch ( $rv->state ) { case self::V_KEY_GRANTED: $rv->valid = true; break; case self::V_KEY_MISSING: $rv->msg = Text::sprintf( 'AIMY_SO_DL_KEY_MISSING', htmlspecialchars( self::getEditLink() ) ); break; case self::V_KEY_INVALID: $rv->msg = Text::sprintf( 'AIMY_SO_DL_KEY_INVALID', htmlspecialchars( self::getEditLink() ) ); break; case self::V_KEY_EXPIRED: $rv->msg = Text::sprintf( 'AIMY_SO_DL_KEY_EXPIRED', HtmlHelper::date( isset( $rv->data[ 'since' ] ) ? $rv->data[ 'since' ] : 'now', Text::_( 'DATE_FORMAT_FILTER_DATE' ) ), htmlspecialchars( 'https://www.aimy-extensions.com/joomla/speed-optimization.html' ) ); if ( isset( $rv->data[ 'code' ] ) ) { $rv->msg .= ' ' . Text::sprintf( 'AIMY_SO_DL_KEY_CODE', $rv->data[ 'code'] ); } break; case self::V_KEY_DENIED: $rv->msg = Text::sprintf( 'AIMY_SO_DL_KEY_DENIED', htmlspecialchars( 'https://www.aimy-extensions.com/joomla/speed-optimization.html' ) ); break; case self::V_KEY_FAILED: $rv->msg = Text::sprintf( 'AIMY_SO_DL_KEY_VALIDATION_FAILED', self::$lastError ); break; } return $rv; } static private function sendValidationRequest( $key ) { if ( empty( $key ) ) { return self::V_KEY_MISSING; } try { self::$lastError = self::$lastResponseData = ''; $resp = HttpFactory::getHttp()->get( self::$serverBaseUrl . '/check/key/plg_AimySpeedOptimization/' . $key ); if ( is_object( $resp ) ) { if ( $resp->code != 200 ) { self::$lastError = 'HTTP ' . $resp->code; return self::V_KEY_FAILED; } $json = json_decode( $resp->body, true ); if ( ! is_array( $json ) or ! isset( $json[ 'rv' ] ) ) { throw new \RuntimeException( 'Failed to parse JSON data' ); } self::$lastResponseData = $json; switch( $json[ 'rv' ] ) { case 'invalid key': return self::V_KEY_INVALID; case 'expired': return self::V_KEY_EXPIRED; case 'denied': return self::V_KEY_DENIED; case 'granted': return self::V_KEY_GRANTED; } } } catch ( \Exception $e ) { self::$lastError = $e->getMessage(); return self::V_KEY_FAILED; } return self::V_KEY_FAILED; } static private function getUpdateServerName() { return 'Aimy Speed Optimization PRO Update Server'; } static private function getElementName() { if ( 'plg' == 'com' ) { return 'com_aimyspeedoptimization'; } return 'aimyspeedoptimization'; } static private function loadLanguageStrings() { $n = 'plg_'; if ( ! empty( $jp = 'system' ) ) { $n .= $jp . '_'; } $n .= 'aimyspeedoptimization'; Factory::getLanguage()->load( $n, JPATH_ADMINISTRATOR ); } static private function isJoomla3() { return ( strpos( JVERSION, '3.' ) === 0 ); } static public function getParams() { $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( 'params' ) ) ->from( $db->quoteName( '#__extensions' ) ) ->where( $db->quoteName( 'element' ) . ' = ' . $db->quote( self::getElementName() ) ); $db->setQuery( $q ); $rv = $db->loadResult(); if ( empty( $rv ) ) { return false; } $params = new Registry(); $params->loadString( $rv ); return $params; } }
helpers/index.html 0000644 00000000016 15234451223 0010201 0 ustar 00 <html></html>
HtaccessHelper.php 0000644 00000004152 15234451223 0010155 0 ustar 00 <?php
/*
* Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
* Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); jimport( 'joomla.filesystem.file' ); use Joomla\CMS\Filesystem\File; abstract class AimySpeedOptimizationHtaccessHelper { static private $mark_start = '# Aimy Speed Optimization START -- keep this line!'; static private $mark_end = '# Aimy Speed Optimization END -- keep this line!'; static public function cleanup() { $c = self::get_contents(); $re = '/\s*' . self::$mark_start . '.*?' . self::$mark_end . '\s*/s'; if ( preg_match( $re, $c, $m ) ) { $c = str_replace( $m[0], "\n", $c ); if ( @File::write( self::get_path(), $c ) === false ) { throw new RuntimeException( self::get_error() ); } } } static public function modify( $to_state ) { if ( empty( $to_state ) ) { return; } $to_states = explode( '+', $to_state ); $c = self::get_contents(); $c .= "\n\n" . self::$mark_start . "\n\n"; $dir = JPATH_ROOT . '/media/plg_aimyspeedoptimization'; foreach ( $to_states as $state ) { $path = $dir . '/' . 'htaccess-' . basename( $state ) . '.txt'; if ( ! File::exists( $path ) ) { throw new RuntimeException( 'No htaccess data for ' . $state ); } $s = @file_get_contents( $path ); if ( $s === false ) { throw new RuntimeException( self::get_error() ); } $c .= $s; } $c .= "\n" . self::$mark_end . "\n\n"; if ( @File::write( self::get_path(), $c ) === false ) { throw new RuntimeException( self::get_error() ); } } static public function get_contents() { $c = @file_get_contents( self::get_path() ); if ( $c === false ) { throw new RuntimeException( self::get_error() ); } return $c; } static public function get_path() { return JPATH_ROOT . '/' . '.htaccess'; } static public function htaccess_exists() { return file_exists( self::get_path() ); } static private function get_error() { $e = error_get_last(); if ( ! is_array( $e ) ) { return 'Unknown error'; } return $e[ 'message' ]; } }
JShrinkMinifier.php 0000644 00000021501 15234451223 0010310 0 ustar 00 <?php
/*
* == Aimy Extensions Comment Start ==
*
* JShrink is licensed under the Modified (3-clause) BSD License, which is
* compatible with the GNU GPL.
*
* See http://www.gnu.org/licenses/license-list.html#ModifiedBSD for details.
*
* == Aimy Extensions Comment End ==
*
*
* This file is part of the JShrink package.
*
* (c) Robert Hafner <tedivm@tedivm.com>
*
*
* Copyright (c) 2009, Robert Hafner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the Stash Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL Robert Hafner BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
namespace JShrink; defined( '_JEXEC' ) or die(); class Minifier { protected $input; protected $len = 0; protected $index = 0; protected $a = ''; protected $b = ''; protected $c; protected $last_char; protected $output; protected $options; protected $stringDelimiters = ['\'' => true, '"' => true, '`' => true]; protected static $defaultOptions = ['flaggedComments' => true]; protected static $keywords = ["delete", "do", "for", "in", "instanceof", "return", "typeof", "yield"]; protected $max_keyword_len; protected $locks = []; public static function minify($js, $options = []) { try { $jshrink = new Minifier(); $js = $jshrink->lock($js); $js = ltrim($jshrink->minifyToString($js, $options)); $js = $jshrink->unlock($js); unset($jshrink); return $js; } catch (\Exception $e) { if (isset($jshrink)) { $jshrink->clean(); unset($jshrink); } throw $e; } } protected function minifyToString($js, $options) { $this->initialize($js, $options); $this->loop(); $this->clean(); return $this->output; } protected function initialize($js, $options) { $this->options = array_merge(static::$defaultOptions, $options); $this->input = $js; $this->input .= PHP_EOL; $this->len = strlen($this->input); $this->a = "\n"; $this->b = "\n"; $this->last_char = "\n"; $this->output = ""; $this->max_keyword_len = max(array_map('strlen', static::$keywords)); } protected $noNewLineCharacters = [ '(' => true, '-' => true, '+' => true, '[' => true, '#' => true, '@' => true]; protected function echo($char) { $this->output .= $char; $this->last_char = $char[-1]; } protected function loop() { while ($this->a !== false && !is_null($this->a) && $this->a !== '') { switch ($this->a) { case "\r": case "\n": if ($this->b !== false && isset($this->noNewLineCharacters[$this->b])) { $this->echo($this->a); $this->saveString(); break; } if ($this->b === ' ') { break; } case ' ': if (static::isAlphaNumeric($this->b)) { $this->echo($this->a); } $this->saveString(); break; default: switch ($this->b) { case "\r": case "\n": if (strpos('}])+-"\'', $this->a) !== false) { $this->echo($this->a); $this->saveString(); break; } else { if (static::isAlphaNumeric($this->a)) { $this->echo($this->a); $this->saveString(); } } break; case ' ': if (!static::isAlphaNumeric($this->a)) { break; } default: if ($this->a === '/' && ($this->b === '\'' || $this->b === '"')) { $this->saveRegex(); continue 3; } $this->echo($this->a); $this->saveString(); break; } } $this->b = $this->getReal(); if ($this->b == '/') { $valid_tokens = "(,=:[!&|?\n"; $last_token = $this->a; if ($last_token == " ") { $last_token = $this->last_char; } if (strpos($valid_tokens, $last_token) !== false) { $this->saveRegex(); } else if ($this->endsInKeyword()) { $this->saveRegex(); } } } } protected function clean() { unset($this->input); $this->len = 0; $this->index = 0; $this->a = $this->b = ''; unset($this->c); unset($this->options); } protected function getChar() { if (isset($this->c)) { $char = $this->c; unset($this->c); } else { $char = $this->index < $this->len ? $this->input[$this->index] : false; if (isset($char) && $char === false) { return false; } $this->index++; } if ($char == "\r") { $char = "\n"; } if ($char !== "\n" && $char < "\x20") { return ' '; } return $char; } protected function peek() { if ($this->index >= $this->len) { return false; } $char = $this->input[$this->index]; if ($char == "\r") { $char = "\n"; } if ($char !== "\n" && $char < "\x20") { return ' '; } return $char; } protected function getReal() { $startIndex = $this->index; $char = $this->getChar(); if ($char !== '/') { return $char; } $this->c = $this->getChar(); if ($this->c === '/') { $this->processOneLineComments($startIndex); return $this->getReal(); } elseif ($this->c === '*') { $this->processMultiLineComments($startIndex); return $this->getReal(); } return $char; } protected function processOneLineComments($startIndex) { $thirdCommentString = $this->index < $this->len ? $this->input[$this->index] : false; $this->getNext("\n"); unset($this->c); if ($thirdCommentString == '@') { $endPoint = $this->index - $startIndex; $this->c = "\n" . substr($this->input, $startIndex, $endPoint); } } protected function processMultiLineComments($startIndex) { $this->getChar(); $thirdCommentString = $this->getChar(); if ($thirdCommentString == "*") { $peekChar = $this->peek(); if ($peekChar == "/") { $this->index++; return; } } if ($this->getNext('*/')) { $this->getChar(); $this->getChar(); $char = $this->getChar(); if (($this->options['flaggedComments'] && $thirdCommentString === '!') || ($thirdCommentString === '@')) { if ($startIndex > 0) { $this->echo($this->a); $this->a = " "; if ($this->input[($startIndex - 1)] === "\n") { $this->echo("\n"); } } $endPoint = ($this->index - 1) - $startIndex; $this->echo(substr($this->input, $startIndex, $endPoint)); $this->c = $char; return; } } else { $char = false; } if ($char === false) { throw new \RuntimeException('Unclosed multiline comment at position: ' . ($this->index - 2)); } $this->c = $char; } protected function getNext($string) { $pos = strpos($this->input, $string, $this->index); if ($pos === false) { return false; } $this->index = $pos; return $this->index < $this->len ? $this->input[$this->index] : false; } protected function saveString() { $startpos = $this->index; $this->a = $this->b; if (!isset($this->stringDelimiters[$this->a])) { return; } $stringType = $this->a; $this->echo($this->a); while (($this->a = $this->getChar()) !== false) { switch ($this->a) { case $stringType: break 2; case "\n": if ($stringType === '`') { $this->echo($this->a); } else { throw new \RuntimeException('Unclosed string at position: ' . $startpos); } break; case '\\': $this->b = $this->getChar(); if ($this->b === "\n") { break; } $this->echo($this->a . $this->b); break; default: $this->echo($this->a); } } } protected function saveRegex() { if ($this->a != " ") { $this->echo($this->a); } $this->echo($this->b); while (($this->a = $this->getChar()) !== false) { if ($this->a === '/') { break; } if ($this->a === '\\') { $this->echo($this->a); $this->a = $this->getChar(); } if ($this->a === "\n") { throw new \RuntimeException('Unclosed regex pattern at position: ' . $this->index); } $this->echo($this->a); } $this->b = $this->getReal(); } protected static function isAlphaNumeric($char) { return preg_match('/^[\w\$\pL]$/', $char) === 1 || $char == '/'; } protected function endsInKeyword() { $testOutput = substr($this->output . $this->a, -1 * ($this->max_keyword_len + 10)); foreach(static::$keywords as $keyword) { if (preg_match('/[^\w]'.$keyword.'[ ]?$/i', $testOutput) === 1) { return true; } } return false; } protected function lock($js) { $lock = '"LOCK---' . crc32(time()) . '"'; $matches = []; preg_match('/([+-])(\s+)([+-])/S', $js, $matches); if (empty($matches)) { return $js; } $this->locks[$lock] = $matches[2]; $js = preg_replace('/([+-])\s+([+-])/S', "$1{$lock}$2", $js); return $js; } protected function unlock($js) { if (empty($this->locks)) { return $js; } foreach ($this->locks as $lock => $replacement) { $js = str_replace($lock, $replacement, $js); } return $js; } }
MinifyCSS.php 0000644 00000010065 15234451223 0007064 0 ustar 00 <?php
/*
* Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
* Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); class AimySpeedOptimizationMinifyCSS { private $opt = 0; private $base_dir = null; private $root_dir = null; const KEEP_HEADER = 1; const SHORTEN_HEADER = 2; public function __construct( $base_dir = null, $root_dir = '/', $opt = 0 ) { if ( ! is_string( $base_dir ) && ! empty( $base_dir ) ) { throw new InvalidArgumentException( 'Invalid base_dir' ); } if ( ! is_string( $root_dir ) ) { throw new InvalidArgumentException( 'Invalid root_dir' ); } if ( ! is_numeric( $opt ) ) { throw new InvalidArgumentException( 'Invalid opt' ); } $this->opt = $opt; $this->base_dir = empty( $base_dir ) ? getcwd() : $base_dir; $this->root_dir = empty( $root_dir ) ? '/' : $root_dir; } public function minify( $code, $path = '/' ) { $header = ''; if ( $this->opt & self::KEEP_HEADER ) { if ( preg_match( '#/\*.*?\*/#s', $code, $m ) ) { $header = $m[0] . "\n"; if ( $this->opt & self::SHORTEN_HEADER ) { $header = preg_replace( '#\s+#', ' ', $header ); } } } if ( preg_match_all( '#@import\s+([^;\s]+)\s*([^;]+?)?;#', $code, $ms, PREG_SET_ORDER ) ) { foreach ( $ms as $ctx ) { $url = $ctx[1]; if ( stripos( $url, 'url(' ) === 0 ) { $url = substr( $url, 4, -1 ); } $url = trim( $url, " \n\r\t\"\';" ); if ( strpos ( $url, '//' ) === 0 or stripos( $url, 'http://' ) === 0 or stripos( $url, 'https://' ) === 0 ) { continue; } $file = $path . '/' . $url; $url_wo = preg_replace( '/[#\?].*$/', '', $url ); if ( preg_match( '#\.(php\d*)$#i', $url_wo ) ) { $file = preg_replace( '#^\./#', '', $file ); $p = ( strpos( $file, '/' ) === 0 ? $file : $this->root_dir . '/' . $file ); $code = str_replace( $ctx[0], str_replace( $ctx[1], 'url(' . $p . ')', $ctx[0] ), $code ); continue; } if ( strpos( $url, '/' ) === 0 ) { $file = $this->base_dir . preg_replace( '#^/?\Q' . $this->root_dir . '\E/?#', '/', $url ); } $icode = $this->minify_file( $file ); if ( isset( $ctx[2] ) ) { $icode = '@media ' . $ctx[2] . '{' . $icode . '}'; } $code = str_replace( $ctx[0], $icode, $code ); } } $code = preg_replace( '#/\*.*?\*/#s', '', $code ); $code = str_replace( array( "\r\n", "\n", "\r" ), ' ', $code ); $code = str_replace( "\t", ' ', $code ); $code = preg_replace( '#\s{2,}#s', ' ', $code ); $code = str_replace( ';}', '}', $code ); $code = str_replace( array( ', ', ' ,' ), ',', $code ); $code = str_replace( array( ': ', ' :' ), ':', $code ); $code = str_replace( array( ': ', ' :' ), ':', $code ); $code = str_replace( array( '; ', ' ;' ), ';', $code ); $code = str_replace( array( '> ', ' >' ), '>', $code ); $code = str_replace( array( '{ ', ' {' ), '{', $code ); $code = str_replace( array( '} ', ' }' ), '}', $code ); if (preg_match_all( '#url\(([^\)]+)\)#', $code, $ms, PREG_SET_ORDER )) { foreach ( $ms as $ctx ) { $url = trim( $ctx[1], " \t\n'\"" ); if ( stripos( $url, 'data:' ) === 0 or strpos( $url, '/' ) === 0 or stripos( $url, 'http://' ) === 0 or stripos( $url, 'https://' ) === 0 ) { continue; } $npath = str_replace( '//', '', preg_replace( '#^\Q' . $this->base_dir . '\E#', '/', $path ) . '/' . $url ); if ( strpos( $npath, './' ) === 0 ) { $npath = substr( $npath, 1 ); } if ( strpos( $npath, '/' ) !== 0 ) { $npath = str_replace( '//', '/', $this->root_dir . '/' . $npath ); } $nctx = str_replace( $ctx[1], $npath, $ctx[0] ); $code = str_replace( $ctx[0], $nctx, $code ); } } $code = str_replace( ';}', '}', $code ); $code = str_replace( '! ', '!', $code ); return $header . trim( $code ); } public function minify_file( $path ) { if ( ! is_string( $path ) or empty( $path ) ) { throw new InvalidArgumentException( 'Invalid path' ); } $code = @file_get_contents( ( strpos( $path, '/' ) === 0 ) ? $path : ( $this->base_dir . DIRECTORY_SEPARATOR . $path ) ); if ( $code === false ) { throw new RuntimeException( $path . ': failed to read' ); } return $this->minify( $code, dirname( $path ) ); } }
MinifyHTML.php 0000644 00000006335 15234451223 0007205 0 ustar 00 <?php
/*
* Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
* Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); abstract class AimySpeedOptimizationMinifyHTML { static public function minify_by_dom( &$dom ) { return self::get_minified_html( $dom ); } static private function get_minified_html( &$dom ) { $html = '<!DOCTYPE html>'; $e = & $dom->documentElement; $html .= '<' . $e->nodeName . self::format_element_attributes( $e ) . '>'; $html .= self::get_element_html( $e ); $html .= '</' . $e->nodeName . '>'; return $html; } static private function get_element_html( &$node ) { $html = ''; $es = $node->childNodes; foreach ( $es as $e ) { switch ( $e->nodeType ) { case XML_CDATA_SECTION_NODE: { $html .= trim( $e->nodeValue ); break; } case XML_TEXT_NODE: { if ( ! empty( $e->nodeValue ) ) { $par = $e->parentNode; if ( self::has_pre_parent( $par ) ) { $html .= htmlspecialchars( $e->nodeValue, ENT_NOQUOTES ); } else if ( is_object( $par ) && ! empty( $par->nodeName ) && ( $par->nodeName == 'script' || $par->nodeName == 'style' ) ) { $html .= $e->nodeValue; } else { $v = htmlspecialchars( self::normalize_space( $e->nodeValue, false ), ENT_NOQUOTES ); if ( is_object( $par ) && $par->nodeName == 'head' ) { } else { $html .= $v; } } } break; } case XML_COMMENT_NODE: { if ( strpos( $e->nodeValue, '[' ) === 0 ) { $html .= '<!--' . self::normalize_space( $e->nodeValue ) . '-->'; } break; } case XML_ELEMENT_NODE: { $html .= '<' . $e->nodeName . self::format_element_attributes( $e ); if ( $e->hasChildNodes() ) { $html .= '>'; $html .= self::get_element_html( $e ); $html .= '</' . $e->nodeName . '>'; } else { if ( self::is_void_element( $e->nodeName ) ) { $html .= ( $e->hasAttributes() ? ' ' : '' ) . '/>'; } else { $html .= '></' . $e->nodeName . '>'; } } break; } } } return $html; } static private function is_void_element( $t ) { static $ts = array( 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr' ); return in_array( $t, $ts ); } static private function has_pre_parent( &$e ) { if ( ! $e || ! $e->nodeName ) { return false; } if ( $e->nodeName == 'pre' ) { return true; } if ( $e->parentNode ) { return self::has_pre_parent( $e->parentNode ); } return false; } static private function normalize_space( $s, $do_trim = true ) { return preg_replace( '#\s+#', ' ', ( $do_trim ? trim( $s ) : $s ) ); } static private function format_element_attributes( &$e ) { if ( ! $e->hasAttributes() ) { return ''; } $html = ''; foreach ( $e->attributes as $attr ) { $v = htmlspecialchars( self::normalize_space( $attr->nodeValue ) ); $html .= ' ' . $attr->nodeName; if ( $v === '' ) { continue; } $html .= '='; if ( strpos( $v, ' ' ) === false && strpos( $v, "\t" ) === false && strpos( $v, "\n" ) === false && strpos( $v, "\f" ) === false && strpos( $v, "\r" ) === false && strpos( $v, '?' ) === false && strpos( $v, '&' ) === false && strpos( $v, '=' ) === false && strpos( $v, "'" ) === false ) { $html .= $v; } else { $html .= '"' . $v . '"'; } } return $html; } }
aimyspeedoptimization.php 0000644 00000060056 15234451223 0011714 0 ustar 00 <?php
/*
* Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
* Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); jimport( 'joomla.plugin.plugin' ); use Joomla\CMS\Plugin\CMSPlugin; use Joomla\CMS\Factory; use Joomla\CMS\Language\Text; use Joomla\CMS\Table\Table; use Joomla\CMS\Uri\Uri; class plgSystemAimySpeedOptimization extends CMSPlugin { protected $autoloadLanguage = true; private $html5 = null; private $dom = null; private $app = null; private $queue = null; private $ctx = null; public function __construct( &$subject, $config ) { parent::__construct( $subject, $config ); $this->app = Factory::getApplication(); $this->queue = array( 'js' => array( 'links' => array(), 'code' => array() ), 'css' => array() ); $this->ctx = $this->app->input->getCmd( 'option', 'unknown' ) . '.' . $this->app->input->getCmd( 'view', 'unknown' ); } public function onBeforeRender() { if ( ! $this->in_frontend() ) { $this->update_plugin_ordering(); return $this->handle_htaccess_config_change(); } } public function onAfterRender() { if ( ! $this->in_frontend() or $this->is_unsupported_ctx() or $this->is_frontend_editing() or ( $this->params->get( 'exclude_pages', false ) && $this->is_excluded_page() ) ) { return; } $doc = Factory::getDocument(); if ( $doc->getType() !== 'html' ) { return; } $html = $this->app->getBody(); if ( ! is_string( $html ) or empty( $html ) ) { return false; } require_once( dirname( __FILE__ ) . '/html5-bundle.php' ); try { $this->html5 = new AimySpeedOptimization\Masterminds\HTML5(); $this->dom = @$this->html5->loadHTML( $html ); } catch ( Exception $e ) { error_log( "Aimy Speed Optimization: $e" ); return; } if ( $this->params->get( 'eliminate_render_blocking', false ) && ! $this->page_should_not_be_unblocked() ) { $this->handle_js(); $this->handle_css(); } if ( $this->params->get( 'image_action', false ) ) { $this->handle_images(); } $this->add_js_loader(); $html = ''; if ( $this->params->get( 'minify_html', false ) ) { $html = $this->get_minified_html(); } else { $html = $this->html5->saveHTML( $this->dom ); $html = preg_replace( '#(<!xml:)(lang="\w{2}(?:-\w{2})?")\s+lang="\w{2}(?:-\w{2})?"#', '$1', $html ); } $this->app->setBody( $html ); } private function update_htaccess( $state ) { require_once( dirname( __FILE__ ) . '/HtaccessHelper.php' ); if ( ! AimySpeedOptimizationHtaccessHelper::htaccess_exists() ) { $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_ERR_NO_HTACCESS_FILE' ), 'error' ); return; } try { AimySpeedOptimizationHtaccessHelper::cleanup(); AimySpeedOptimizationHtaccessHelper::modify( $state ); } catch ( Exception $e ) { $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . $e->getMessage(), 'error' ); return false; } $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_MSG_HTACCESS_UPDATED' ), 'notice' ); return true; } private function handle_htaccess_config_change() { if ( $this->ctx != 'com_plugins.plugin' && $this->ctx != 'com_plugins.plugins' ) { return; } $old_state = $this->app->getUserState( 'htaccess_state', false, 'aimyspeedoptimization' ); if ( $old_state === false ) { $old_state = $this->params->get( 'htaccess_state', '' ); } $on = array(); if ( $this->params->get( 'enable_browser_caching', 0 ) ) { $on[] = 'browsercache'; } if ( $this->params->get( 'enable_compression', 0 ) ) { $on[] = 'compress'; } $new_state = implode( '+', $on ); if ( $old_state == $new_state ) { return; } if ( ! $this->update_htaccess( $new_state ) ) { return false; } $this->app->setUserState( 'htaccess_state', $new_state, 'aimyspeedoptimization' ); $this->params->set( 'htaccess_state', $new_state ); $tbl = Table::getInstance( 'extension' ); $tbl->load( array( 'element' => 'aimyspeedoptimization' ) ); $tbl->set( 'params', $this->params->toString() ); $tbl->store(); return; } private function update_plugin_ordering() { if ( ! $this->params->get( 'order_as_last', true ) ) { return; } if ( $this->ctx != 'com_plugins.plugin' && $this->ctx != 'com_plugins.plugins' ) { return; } $exts = Table::getInstance( 'extension' ); $exts->find( array( 'type' => 'plugin', 'folder' => 'system' ) ); $next = $exts->getNextOrder(); $plg = Table::getInstance( 'extension' ); $plg->load( array( 'element' => 'aimyspeedoptimization' ) ); if ( $plg->get( 'ordering', 0 ) + 1 < $next ) { $plg->set( 'ordering', $next ); $plg->store(); $this->app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_PLG_ORDERED_AS_LAST' ) ); } } private function handle_images() { $els = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'img' ) ); $action = $this->params->get( 'image_action', 'defer' ); $select = $this->params->get( 'image_selection', 'uploaded' ); $wrap = $this->params->get( 'wrap_in_link', false ); $images_base_dir = Uri::root( true ) . '/images/'; $cache_base_dir = Uri::root( true ) . '/cache/'; $embed_images = $this->params->get( 'embed_images', false ); $embed_image_maxsize = intVal( $this->params->get( 'embed_image_max_size', 5 ) ) * 1024; foreach ( $this->dom->getElementsByTagName( 'picture' ) as $pic ) { foreach ( $pic->childNodes as $el ) { if ( isset( $el->nodeName ) && $el->nodeName == 'source' ) { $els[] = $el; } } } foreach ( $els as $i => $el ) { $isA = strtolower( $el->nodeName ); if ( ( $isA == 'img' && ( ! $el->hasAttribute( 'src' ) && ! $el->hasAttribute( 'srcset' ) ) ) || ( $isA == 'source' && ! $el->hasAttribute( 'srcset' ) ) ) { unset( $els[ $i ] ); continue; } $src = false; if ( $isA == 'source' ) { $v = $el->getAttribute( 'srcset' ); if ( ! empty( $v ) ) { $src = self::get_first_srcset_path( $v ); } } else { $src = $el->getAttribute( 'src' ); } if ( empty( $src ) or strpos( $src, 'data:' ) === 0 ) { unset( $els[ $i ] ); continue; } switch ( $select ) { case 'class_defer': if ( ! $el->hasAttribute( 'class' ) ) { unset( $els[ $i ] ); break; } $class = $el->getAttribute( 'class' ); if ( ! preg_match( '#\bdefer\b#', $class ) ) { unset( $els[ $i ] ); break; } break; case 'uploaded': default: if ( ! self::is_local_link( $src ) || ( strpos( $src, $images_base_dir ) === false && strpos( $src, 'images/' ) !== 0 && strpos( $src, $cache_base_dir ) === false && strpos( $src, 'cache/' ) !== 0 ) ) { unset( $els[ $i ] ); break; } break; } } $els = array_values( $els ); if ( ( $n = intVal( $this->params->get( 'skip_first_n', 0 ) ) ) > 0 ) { while ( $n > 0 && count( $els ) > 0 ) { array_shift( $els ); $n--; } } if ( count( $els ) === 0 ) { return; } foreach ( $els as $i => $el ) { $isA = strtolower( $el->nodeName ); $src = false; if ( $isA == 'img' ) { $src = trim( $el->getAttribute( 'src' ) ); } elseif ( $isA == 'source' ) { $src = self::get_first_srcset_path( $el->getAttribute( 'srcset' ) ); } if ( ! $src ) { continue; } $path = JPATH_ROOT . '/' . preg_replace( '/\s*[#\?].*$/', '', preg_replace( '#^' . Uri::root( true ) . '/?#', '/', preg_replace( '#https?://[^/]+/#i', '/', $src ) ) ); if ( preg_match( '#/$#', $path ) ) { continue; } if ( self::is_local_link( $src ) && preg_match( '#\.(jpe?g|png|gif|tiff?|webp)$#i', $path ) && is_file( $path ) ) { if ( $isA == 'img' && ( ! $el->hasAttribute( 'width' ) or ! $el->hasAttribute( 'height' ) ) ) { $ii = getimagesize( $path ); if ( is_array( $ii ) ) { $el->setAttribute( 'width', $ii[ 0 ] ); $el->setAttribute( 'height', $ii[ 1 ] ); } } if ( $embed_images ) { $fsize = filesize( $path ); if ( $fsize && $fsize <= $embed_image_maxsize ) { $data = self::get_image_data_uri( $path ); if ( $data ) { $attr = ( $isA == 'source' ) ? 'srcset' : 'src'; $el->setAttribute( $attr, $data ); continue; } } } } if ( $action == 'dimensions' ) { continue; } if ( $el->hasAttribute( 'loading' ) && $el->getAttribute( 'loading' ) == 'lazy' ) { continue; } if ( $action == 'lazy-native' ) { if ( $isA == 'img' ) { $el->setAttribute( 'loading', 'lazy' ); } continue; } if ( $el->hasAttribute( 'srcset' ) ) { $el->setAttribute( 'data-srcset', $el->getAttribute( 'srcset' ) ); $el->removeAttribute( 'srcset' ); } if ( $isA == 'img' && $el->hasAttribute( 'src' ) ) { $el->setAttribute( 'data-src', $src ); $el->setAttribute( 'src', self::get_inline_image() ); } $class = $el->hasAttribute( 'class' ) ? $el->getAttribute( 'class' ) : ''; if ( ! preg_match( '#\bdefer\b#', $class ) ) { $class .= ' defer'; $el->setAttribute( 'class', trim( $class ) ); } if ( $isA == 'img' ) { if ( $wrap && ! self::is_wrapped_in_link( $el ) && ! self::is_descendant_of_class( $el, 'AimyVideoEmbedderVideoPlaceholder' ) ) { $a = $this->dom->createElement( 'a' ); $href = $this->dom->createAttribute( 'href' ); $cls = $this->dom->createAttribute( 'class' ); $href->nodeValue = $src; $cls->nodeValue = 'defer-wrap'; $a->appendChild( $href ); $a->appendChild( $cls ); $el->parentNode->replaceChild( $a, $el ); $a->appendChild( $el ); } } } if ( $action == 'defer' or $action == 'lazy' ) { array_unshift( $this->queue[ 'js' ][ 'links' ], sprintf( '%s/media/plg_aimyspeedoptimization/%s', Uri::root( true ), ( $action == 'defer' ? 'loadDeferredImages.js' : 'blazy.js' ) ) ); if ( $wrap ) { $h = $this->dom->getElementsByTagName( 'head' )->item( 0 ); if ( $h ) { $s = $this->dom->createElement( 'style' ); $s->nodeValue = '.defer-wrap{cursor:default;}'; $h->appendChild( $s ); } } } } private function handle_js() { $scripts = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'script' ) ); if ( empty( $scripts ) ) { return; } $auto_preload = $this->params->get( 'auto_preload', false ); if ( ! self::is_joomla3() ) { foreach ( $scripts as $script ) { if ( ! self::is_js_script_element( $script ) || ! $script->hasAttribute( 'src' ) ) { continue; } $src = $script->getAttribute( 'src' ); if ( strpos( $src, 'media/system/js/' ) === false && strpos( $src, 'media/com_finder/js' ) === false ) { continue; } if ( preg_match( '#^(.*)-es5\.min\.js#', $src, $m ) ) { $es6_name = $m[ 1 ] . '.min.js'; foreach ( $scripts as $_s ) { if ( ! self::is_js_script_element( $_s ) || ! $_s->hasAttribute( 'src' ) ) { continue; } $_src = preg_replace( '#\?.*$#', '', $_s->getAttribute( 'src' ) ); if ( $_src == $es6_name ) { $script->parentNode->removeChild( $script ); } } } } } foreach ( $scripts as $script ) { if ( ! $script->parentNode ) { continue; } if ( ! self::is_js_script_element( $script ) || $script->hasAttribute( 'async' ) ) { continue; } if ( $script->hasAttribute( 'src' ) ) { $link = $script->getAttribute( 'src' ); if ( ! empty( $link ) && ! self::is_unsupported_js( $link ) ) { $script->parentNode->removeChild( $script ); $type = strtolower( $script->getAttribute( 'type' ) ?: '' ); if ( $type == 'module' ) { $this->queue[ 'js' ][ 'links' ][] = 'module:' . $link; if ( $auto_preload ) { $this->add_js_module_preload_link( $link ); } } else { $this->queue[ 'js' ][ 'links' ][] = $link; if ( $auto_preload ) { $this->add_preload_link( $link, 'script' ); } } } } else { require_once( dirname( __FILE__ ) . '/JShrinkMinifier.php' ); $code = trim( $script->nodeValue ); try { $code = JShrink\Minifier::minify( $code ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $e->getMessage() ); } if ( ! empty( $code ) && ! self::is_unsupported_js_code( $code ) ) { $this->queue[ 'js' ][ 'code' ][] = $code; $script->parentNode->removeChild( $script ); } } } } private function handle_css() { $head = $this->dom->getElementsByTagName( 'head' )->item( 0 ); $els = self::DomNodeList_to_array( $head->childNodes ); $num = 0; $auto_preload = $this->params->get( 'auto_preload', false ); foreach ( $els as $el ) { if ( ! isset( $el->nodeName ) ) { continue; } if ( $el->nodeName == 'link' && $el->hasAttribute( 'rel' ) && $el->getAttribute( 'rel' ) == 'stylesheet' && $el->hasAttribute( 'href' ) ) { $href = $el->getAttribute( 'href' ); $media = $el->hasAttribute( 'media' ) ? $el->getAttribute( 'media' ) : ''; if ( self::is_local_link( $href ) && ! self::requires_http_access( $href ) ) { require_once( dirname( __FILE__ ) . '/MinifyCSS.php' ); $path = self::get_link_path( $href ); $code = null; try { $cssmin = new AimySpeedOptimizationMinifyCSS( JPATH_ROOT, Uri::root( true ), AimySpeedOptimizationMinifyCSS::KEEP_HEADER + AimySpeedOptimizationMinifyCSS::SHORTEN_HEADER ); $fs_path = preg_replace( '#^' . Uri::root( true ) . '/?#', '', $path ); $code = $cssmin->minify_file( $fs_path ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $fs_path . ': ' . $e->getMessage() ); } if ( empty( $code ) ) { continue; } if ( ! empty( $media ) ) { $code = '@media ' . $media . '{' . $code . '};'; } $nel = $this->dom->createElement( 'style' ); $nid = $this->dom->createAttribute( 'data-asoid' ); $nid->nodeValue = ++$num; $nel->appendChild( $nid ); $nel->appendChild( $this->dom->createTextNode( $code ) ); $head->replaceChild( $nel, $el ); } else { $this->queue[ 'css' ][] = array( 'value' => $href, 'media' => $media, 'id' => ++$num ); $noscript = $this->dom->createElement( 'noscript' ); $noscript->appendChild( $this->dom->createTextNode( $this->dom->saveXML( $el ) ) ); $head->replaceChild( $noscript, $el ); if ( $auto_preload ) { $this->add_preload_link( $href, 'style' ); } } } else if ( $el->nodeName == 'style' ) { $media = $el->hasAttribute( 'media' ) ? $el->getAttribute( 'media' ) : ''; $code = null; require_once( dirname( __FILE__ ) . '/MinifyCSS.php' ); try { $cssmin = new AimySpeedOptimizationMinifyCSS( JPATH_ROOT, Uri::root( true ), AimySpeedOptimizationMinifyCSS::KEEP_HEADER + AimySpeedOptimizationMinifyCSS::SHORTEN_HEADER ); $code = $cssmin->minify( $el->nodeValue, Uri::root( true ) ); } catch ( Exception $e ) { error_log( 'Aimy Speed Optimization: ' . $e->getMessage() ); } if ( empty( $code ) ) { continue; } if ( ! empty( $media ) ) { $code = '@media ' . $media . '{' . $code . '};'; } $el->nodeValue = trim( $code ); $nid = $this->dom->createAttribute( 'data-asoid' ); $nid->nodeValue = ++$num; $el->appendChild( $nid ); } } } private function get_minified_html() { require_once( dirname( __FILE__ ) . '/MinifyHTML.php' ); return AimySpeedOptimizationMinifyHTML::minify_by_dom( $this->dom ); } private function add_js_loader() { if ( empty( $this->queue[ 'js' ][ 'links' ] ) && empty( $this->queue[ 'js' ][ 'code' ] ) && empty( $this->queue[ 'css' ] ) ) { return; } $loader = ''; foreach ( $this->queue[ 'css' ] as $css ) { $loader .= 'AimySpeedOptimization.loadCssLink("' . $css[ 'value' ] . '","' . $css[ 'media' ] . '","' . $css[ 'id' ] . '");'; } $held_dom_ready = 0; foreach ( $this->queue[ 'js' ][ 'links' ] as $src ) { $loader .= '$script("' . $src . '",' . 'function(){'; if ( strpos( $src, 'jquery.js' ) !== false || strpos( $src, 'jquery.min.js' ) !== false || strpos( $src, 'jquery.min' ) !== false || strpos( $src, 'jquery.noconflict.js' ) !== false || strpos( $src, 'jquery-noconflict.js' ) !== false ) { $loader .= 'AimySpeedOptimization.holdDomReadyEvent();'; $held_dom_ready++; } } if ( ! empty( $this->queue[ 'js' ][ 'code' ] ) ) { $loader .= 'AimySpeedOptimization.addInlineScript("' . rawurlencode( implode( ';', $this->queue[ 'js' ][ 'code' ] ) ) . '");'; } while ( $held_dom_ready-- > 0 ) { $loader .= 'AimySpeedOptimization.releaseDomReadyEvent();'; } if ( ! self::is_joomla3() ) { $loader .= 'AimySpeedOptimization.triggerDOMContentLoaded();'; } $loader .= 'AimySpeedOptimization.triggerWindowLoad();'; for ( $i = 0; $i < count( $this->queue[ 'js' ][ 'links' ] ); $i++ ) { $loader .= ';})'; } $loader .= ';'; $frameworks = trim( @file_get_contents( JPATH_ROOT . '/' . 'media' . '/' . 'plg_aimyspeedoptimization' . '/' . 'scriptjs.js' ) ) . ';' . trim( @file_get_contents( JPATH_ROOT . '/' . 'media' . '/' . 'plg_aimyspeedoptimization' . '/' . 'aimyspeedoptimization.js' ) ); $js = $this->dom->createElement( 'script' ); $js->appendChild( $this->dom->createTextNode( $frameworks . $loader ) ); return $this->add_js_element( $js ); } private function add_js_element( &$js ) { $to = null; try { $nodes = self::DomNodeList_to_array( $this->dom->documentElement->childNodes ); foreach ( $nodes as $n ) { if ( $n->nodeType === XML_ELEMENT_NODE && isset( $n->nodeName ) && ( $n->nodeName != 'style' && $n->nodeName != 'script' && $n->nodeName != 'link' && $n->nodeName != 'meta' ) ) { $to = $n; } } } catch ( Exception $e ){ } if ( empty( $to ) ) { return $this->add_head_element( $js ); } return $to->appendChild( $js ); } private function add_head_element( &$el ) { return $this->dom->getElementsByTagName( 'head' ) ->item( 0 ) ->appendChild( $el ); } private function add_preload_link( $link, $as ) { $link = self::encode_ampersand( $link ); $nel = $this->dom->createElement( 'link' ); $relAttr = $this->dom->createAttribute( 'rel' ); $relAttr->nodeValue = 'preload'; $nel->appendChild( $relAttr ); $hrefAttr = $this->dom->createAttribute( 'href' ); $hrefAttr->nodeValue = $link; $nel->appendChild( $hrefAttr ); $asAttr = $this->dom->createAttribute( 'as' ); $asAttr->nodeValue = $as; $nel->appendChild( $asAttr ); if ( ! self::is_local_link( $link ) ) { $xoAttr = $this->dom->createAttribute( 'crossorigin' ); $xoAttr->nodeValue = ''; $nel->appendChild( $xoAttr ); } return $this->add_head_element( $nel ); } private function add_js_module_preload_link( $link ) { $link = self::encode_ampersand( $link ); $nel = $this->dom->createElement( 'link' ); $relAttr = $this->dom->createAttribute( 'rel' ); $relAttr->nodeValue = 'modulepreload'; $nel->appendChild( $relAttr ); $hrefAttr = $this->dom->createAttribute( 'href' ); $hrefAttr->nodeValue = $link; $nel->appendChild( $hrefAttr ); if ( ! self::is_local_link( $link ) ) { $xoAttr = $this->dom->createAttribute( 'crossorigin' ); $xoAttr->nodeValue = ''; $nel->appendChild( $xoAttr ); } return $this->add_head_element( $nel ); } private function is_excluded_page( $url = null ) { $pep_res = self::extract_patterns( $this->params->get( 'page_exclude_patterns', '' ) ); if ( empty( $pep_res ) ) { return false; } if ( is_null( $url ) ) { $url = strVal( Uri::getInstance() ); } $juri = new Uri( $url ); $cur = $juri->getPath(); if ( ( $q = $juri->getQuery() ) != '' ) { $cur .= '?' . $q; } foreach ( $pep_res as $pep_re ) { if ( preg_match( '/' . $pep_re . '/', $cur ) ) { return true; } } return false; } private function in_frontend() { if ( self::is_joomla3() ) { return $this->app->isSite(); } return $this->app->isClient( 'site' ); } private function is_unsupported_ctx() { return ( $this->ctx == 'com_sppagebuilder.dashboard' or $this->ctx == 'com_sppagebuilder.form' ); } private function is_frontend_editing() { if ( $this->ctx == 'unknown.unknown' ) { return ( Factory::getUser()->id > 0 or $this->app->input->get( 'jooa11y', 0, 'int' ) > 0 ); } return false; } static private function DomNodeList_to_array( $dnl ) { $a = array(); foreach ( $dnl as $n ) { $a[] = $n; } return $a; } static private function is_wrapped_in_link( &$e ) { if ( ! $e || ! $e->nodeName ) { return false; } if ( $e->nodeName == 'a' ) { return true; } if ( $e->parentNode ) { return self::is_wrapped_in_link( $e->parentNode ); } return false; } static private function is_descendant_of_class( &$el, $cls ) { if ( ! $el || ! $el->nodeName || $el->nodeName == '#document' ) { return false; } if ( $el->hasAttribute( 'class' ) ) { $class = $el->getAttribute( 'class' ); if ( preg_match( '#\b\Q' . $cls . '\E\b#', $class ) ) { return true; } } return self::is_descendant_of_class( $el->parentNode, $cls ); } static private function get_link_path( $l ) { $u = new Uri( $l ); return $u->getPath(); } static private function is_local_link( $l ) { if ( stripos( $l, 'http://' ) === 0 || stripos( $l, 'https://' ) === 0 ) { $site = new Uri( Uri::root() ); $link = new Uri( $l ); return ( $site->getHost() == $link->getHost() && strpos( $link->getPath(), $site->getPath() ) === 0 ); } if ( strpos( '/', $l ) !== 0 ) { return true; } $root = Uri::root( true ); $root = empty( $root ) ? '/' : $root; return ( strpos( $l, '//' ) !== 0 && strpos( $l, $root ) === 0 ); } static private function is_unsupported_js( $l ) { return ( strpos( $l, 'require.js' ) !== false || strpos( $l, 'mod_horizontal_scrolling_slideshow.js' ) !== false ); } static private function is_unsupported_js_code( &$c ) { if ( strpos( $c, 'function utmx_section()' ) === 0 || strpos( $c, 'utmx(' ) === 0 ) { return true; } if ( strpos( $c, '/gtm.js?id=' ) !== false && strpos( $c, 'gtm.start' ) !== false ) { return true; } if ( strpos( $c, '/fbevents.js' ) !== false || strpos( $c, 'fbq(' ) === 0 ) { return true; } if ( strpos( $c, 'window.ezb=window.eb=' ) === 0 ) { return true; } if ( strpos( $c, 'var IHRSS_WIDTH' ) !== false ) { return true; } if ( strpos( $c, '/reDimCookieHint/' ) !== false ) { return true; } if ( strpos( $c, 'var gdprConfigurationOptions' ) === 0 ) { return true; } return false; } private function page_should_not_be_unblocked() { $scripts = self::DomNodeList_to_array( $this->dom->getElementsByTagName( 'script' ) ); if ( empty( $scripts ) ) { return false; } foreach ( $scripts as $script ) { if ( ! self::is_js_script_element( $script ) || ! $script->hasAttribute( 'src' ) ) { continue; } $src = $script->getAttribute( 'src' ); if ( strpos( $src, 'cdn.consentmanager.net/' ) !== false ) { return true; } if ( strpos( $src, 'jooa11y.' ) !== false ) { return true; } } return false; } static private function requires_http_access( $l ) { return preg_match( '#\.php\d*$#i', preg_replace( '#[\?\#].*$#', '', $l ) ); } static private function get_inline_image() { return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAA' . 'fFcSJAAAABmJLR0QAmQAAAAAlVf4eAAAACXBIWXMAAAsTAAALEwEAmpwYAAAA' . 'B3RJTUUH3wwJBwcE2ifiigAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR' . '0lNUFeBDhcAAAANSURBVAjXY2BgYGAAAAAFAAFe8yo6AAAAAElFTkSuQmCC'; } static private function extract_patterns( $str ) { $str = trim( $str ); if ( empty( $str ) ) { return array(); } $r = array(); $in = explode( "\r\n", $str ); foreach ( $in as $i => $s ) { $s = trim( $s ); if ( empty( $s ) ) { continue; } $r[] = '^' . str_replace( '*', '.*?', preg_replace( '#([./?\[\]{}()$\^])#', '\\\\${1}', $s ) ) . '$'; } return $r; } static private function get_first_srcset_path( $s ) { $ps = preg_split( '#,?\s+#', trim( $s ) ); if ( is_array( $ps ) && count( $ps ) ) { return $ps[ 0 ]; } return false; } static private function is_js_script_element( & $el ) { if ( $el->hasAttribute( 'type' ) && ( $el->getAttribute( 'type' ) != 'text/javascript' && $el->getAttribute( 'type' ) != 'module' ) ) { return false; } return true; } static private function get_image_data_uri( $path, $ext = false ) { $data = @file_get_contents( $path ); if ( $data === false ) { return false; } if ( ! $ext ) { $ext = pathinfo( $path, PATHINFO_EXTENSION ); } if ( ! $ext ) { return false; } $mime = false; switch ( strtolower( $ext ) ) { case 'jpg': case 'jpeg': $mime = 'image/jpeg'; break; case 'png': $mime = 'image/png'; break; case 'gif': $mime = 'image/gif'; break; case 'tif': case 'tiff': $mime = 'image/tiff'; break; case 'webp': $mime = 'image/webp'; break; } if ( ! $mime ) { return false; } return sprintf( 'data:%s;base64,%s', $mime, base64_encode( $data ) ); } static private function is_joomla3() { return ( strpos( strVal( JVERSION ), '3.' ) === 0 ); } static private function encode_ampersand( $s ) { return preg_replace( '#&(?!amp;)#', '&', $s ); } public function onExtensionAfterSave( $context, $table, $isNew, $data = array() ) { if ( strpos( JVERSION, '3.' ) !== 0 ) { return; } if ( $context != 'com_plugins.plugin' or ! is_object( $table ) or ! property_exists( $table, 'element' ) or $table->element != 'aimyspeedoptimization' ) { return; } if ( ! is_array( $data ) or empty( $data ) or ! isset( $data[ 'params' ] ) or ! is_array( $data[ 'params' ] ) or ! isset( $data[ 'params' ][ 'dl_key' ] ) ) { return; } $key = trim( $data[ 'params' ][ 'dl_key' ] ); require_once( __DIR__ . '/helpers/DownloadKeyHelper.php' ); try { AimySpeedOptimization\Helpers\DownloadKeyHelper::passKeyToUpdatesites( $key ); } catch ( Exception $e ) { Factory::getApplication( 'Failed to pass download key to update sites: ' . $e->getMessage(), 'error' ); } } public function onInstallerBeforePackageDownload( &$url, &$headers ) { if ( ! strpos( $url, 'aimy-extensions.com' ) or ! strpos( $url, '/plg_AimySpeedOptimization/' ) ) { return; } require_once( __DIR__ . '/helpers/DownloadKeyHelper.php' ); $rv = AimySpeedOptimization\Helpers\DownloadKeyHelper::validateKey(); if ( is_object( $rv ) && ! $rv->valid && isset( $rv->msg ) ) { Factory::getApplication()->enqueueMessage( '<b>Aimy Speed Optimization: ' . $rv->msg . '</b>', 'error' ); } } }
aimyspeedoptimization.xml 0000644 00000025402 15234451223 0011721 0 ustar 00 <?xml version="1.0" encoding="UTF-8"?>
<extension version="3.0" type="plugin" group="system" method="upgrade">
<name>System - Aimy Speed Optimization (PRO)</name>
<creationDate>2024-10-15</creationDate>
<author>Aimy Extensions (Netzum Sorglos Software GmbH)</author>
<authorEmail>info@aimy-extensions.com</authorEmail>
<authorUrl>https://www.aimy-extensions.com/</authorUrl>
<copyright>2015-2024 Aimy Extensions, Netzum Sorglos Software GmbH</copyright>
<license>GNU General Public License (GPL) v2</license>
<version>21.0</version>
<description>Optimize your website's speed in various ways (PRO)</description>
<dlid prefix="dl_key=" suffix="" />
<scriptfile>install-hints.php</scriptfile>
<files>
<filename plugin="aimyspeedoptimization">aimyspeedoptimization.php</filename>
<filename>html5-bundle.php</filename>
<filename>index.html</filename>
<filename>HtaccessHelper.php</filename>
<filename>MinifyHTML.php</filename>
<filename>MinifyCSS.php</filename>
<filename>JShrinkMinifier.php</filename>
<folder>helpers</folder>
<folder>fields</folder>
</files>
<media destination="plg_aimyspeedoptimization" folder="media">
<filename>aimy-logo_340x327.png</filename>
<filename>aimyspeedoptimization.js</filename>
<filename>scriptjs.js</filename>
<filename>loadDeferredImages.js</filename>
<filename>blazy.js</filename>
<filename>index.html</filename>
<filename>htaccess-browsercache.txt</filename>
<filename>htaccess-compress.txt</filename>
</media>
<languages>
<language tag="de-DE">i18n/de-DE.plg_system_aimyspeedoptimization.ini</language>
<language tag="en-GB">i18n/en-GB.plg_system_aimyspeedoptimization.ini</language>
<language tag="es-ES">i18n/es-ES.plg_system_aimyspeedoptimization.ini</language>
<language tag="fa-IR">i18n/fa-IR.plg_system_aimyspeedoptimization.ini</language>
<language tag="fr-FR">i18n/fr-FR.plg_system_aimyspeedoptimization.ini</language>
<language tag="hu-HU">i18n/hu-HU.plg_system_aimyspeedoptimization.ini</language>
<language tag="nl-NL">i18n/nl-NL.plg_system_aimyspeedoptimization.ini</language>
<language tag="pl-PL">i18n/pl-PL.plg_system_aimyspeedoptimization.ini</language>
<language tag="sk-SK">i18n/sk-SK.plg_system_aimyspeedoptimization.ini</language>
<language tag="sl-SI">i18n/sl-SI.plg_system_aimyspeedoptimization.ini</language>
<language tag="prs-AF">i18n/prs-AF.plg_system_aimyspeedoptimization.ini</language>
<language tag="cs-CZ">i18n/cs-CZ.plg_system_aimyspeedoptimization.ini</language>
<language tag="bg-BG">i18n/bg-BG.plg_system_aimyspeedoptimization.ini</language>
</languages>
<config>
<fields name="params"
addfieldpath="/plugins/system/aimyspeedoptimization/fields/">
<fieldset name="basic">
<field name="eliminate_render_blocking"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_ELIMINATE_RENDER_BLOCKING_LBL"
description="AIMY_SO_ELIMINATE_RENDER_BLOCKING_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="auto_preload"
type="radio" default="1"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="no"
showon="eliminate_render_blocking:1"
label="AIMY_SO_AUTO_PRELOAD_LBL"
description="AIMY_SO_AUTO_PRELOAD_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field type="spacer" showon="eliminate_render_blocking:1" name="s1" hr="true" />
<field name="minify_html"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_MINIFY_HTML_LBL"
description="AIMY_SO_MINIFY_HTML_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field type="spacer" showon="exclude_pages:1" name="s2" hr="true" />
<field name="exclude_pages"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_EXCLUDE_PAGES_LBL"
description="AIMY_SO_EXCLUDE_PAGES_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="page_exclude_patterns"
type="textarea"
class="inputbox"
rows="5"
cols="30"
required="false"
default=""
showon="exclude_pages:1"
label="AIMY_SO_PAGE_EXCLUDE_PATTERNS_LBL"
description="AIMY_SO_PAGE_EXCLUDE_PATTERNS_DSC" />
<field type="spacer" showon="exclude_pages:1" name="s2" hr="true" />
</fieldset>
<fieldset name="image"
label="AIMY_SO_IMAGE_SETTINGS_TAB_LBL">
<field name="image_action"
type="radio" default="0"
class="btn-group"
labelclass="control-group"
required="true"
label="AIMY_SO_IMAGE_ACTION_LBL"
description="AIMY_SO_IMAGE_ACTION_DSC">
<option value="0">AIMY_SO_IMAGE_ACTION_NONE</option>
<option value="dimensions">AIMY_SO_IMAGE_ACTION_DIMENSIONS</option>
<option value="defer">AIMY_SO_IMAGE_ACTION_DEFER</option>
<option value="lazy">AIMY_SO_IMAGE_ACTION_LAZY</option>
<option value="lazy-native">AIMY_SO_IMAGE_ACTION_LAZY_NATIVE</option>
</field>
<field name="image_selection"
type="radio" default="uploaded"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_IMAGE_SELECTION_LBL"
description="AIMY_SO_IMAGE_SELECTION_DSC">
<option value="uploaded">AIMY_SO_IMAGE_SELECTION_UPLOADED</option>
<option value="class_defer">AIMY_SO_IMAGE_SELECTION_CLASS_DEFER</option>
</field>
<field name="skip_first_n"
type="integer"
default="0"
class="btn-group"
labelclass="control-group"
label="AIMY_SO_IMAGE_SKIP_FIRST_N_LBL"
description="AIMY_SO_IMAGE_SKIP_FIRST_N_DSC"
required="true"
showon="image_action:defer,lazy,lazy-native"
first="0"
last="20"
step="1" />
<field name="wrap_in_link"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
showon="image_action:defer,lazy"
label="AIMY_SO_IMAGE_WRAP_IN_LINK_LBL"
description="AIMY_SO_IMAGE_WRAP_IN_LINK_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field type="spacer" hr="true" />
<field name="embed_images"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="no"
label="AIMY_SO_EMBED_IMAGES_LBL"
description="AIMY_SO_EMBED_IMAGES_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="embed_image_max_size"
type="integer"
default="5"
class="btn-group"
labelclass="control-group"
label="AIMY_SO_EMBED_IMAGE_MAX_SIZE_LBL"
description="AIMY_SO_EMBED_IMAGE_MAX_SIZE_DSC"
required="true"
showon="embed_images:1"
first="1"
last="30"
step="1" />
</fieldset>
<fieldset name="expert"
label="AIMY_SO_EXPERT_SETTINGS_TAB_LBL">
<field type="spacer"
label="AIMY_SO_EXPERT_SETTINGS_TAB_DSC"
class="btn btn-lg btn-danger" />
<field name="enable_browser_caching"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_ENABLE_BROWSER_CACHING_LBL"
description="AIMY_SO_ENABLE_BROWSER_CACHING_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="enable_compression"
type="radio" default="0"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_ENABLE_COMPRESSION_LBL"
description="AIMY_SO_ENABLE_COMPRESSION_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field type="spacer"
label="AIMY_SO_ORDER_AS_LAST_WARNING"
class="btn btn-lg btn-danger" />
<field name="order_as_last"
type="radio" default="1"
class="btn-group btn-group-yesno"
labelclass="control-group"
required="true"
label="AIMY_SO_ORDER_AS_LAST_LBL"
description="AIMY_SO_ORDER_AS_LAST_DSC">
<option value="1">JYES</option>
<option value="0">JNO</option>
</field>
<field name="htaccess_state" type="hidden" default="" />
</fieldset>
<fieldset name="key" label="AIMY_SO_KEY_TAB_LBL">
<field name="dl_key" type="aimydownloadkey" default=""
class="inputbox"
required="false"
filter="cmd"
label="AIMY_SO_DL_KEY_LBL" />
</fieldset>
</fields>
</config>
<updateservers>
<server
type="extension"
priority="1"
name="Aimy Speed Optimization PRO Update Server">http://updates.aimy-extensions.com/joomla/plg_aimyspeedoptimization-pro.xml</server>
</updateservers>
</extension>
html5-bundle.php 0000644 00000421263 15234451223 0007566 0 ustar 00 <?php
/*
* == Aimy Extensions Comment Start ==
*
* HTML5-PHP is released under the MIT license. The original html5lib library
* was also released under the MIT license.
*
* The X11 License (aka. MIT License) is compatible with the GNU GPL.
*
* See http://www.gnu.org/licenses/license-list.html#X11License for details.
*
* == Aimy Extensions Comment End ==
## HTML5-PHP License
Copyright (c) 2013 The Authors of HTML5-PHP
Matt Butcher - mattbutcher@google.com
Matt Farina - matt@mattfarina.com
Asmir Mustafic - goetas@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
## HTML5Lib License
Portions of this are based on html5lib's PHP version, which was a
sub-project of html5lib. The following is the list of contributors from
html5lib:
html5lib:
Copyright (c) 2006-2009 The Authors
Contributors:
James Graham - jg307@cam.ac.uk
Anne van Kesteren - annevankesteren@gmail.com
Lachlan Hunt - lachlan.hunt@lachy.id.au
Matt McDonald - kanashii@kanashii.ca
Sam Ruby - rubys@intertwingly.net
Ian Hickson (Google) - ian@hixie.ch
Thomas Broyer - t.broyer@ltgt.net
Jacques Distler - distler@golem.ph.utexas.edu
Henri Sivonen - hsivonen@iki.fi
Adam Barth - abarth@webkit.org
Eric Seidel - eric@webkit.org
The Mozilla Foundation (contributions from Henri Sivonen since 2008)
David Flanagan (Mozilla) - dflanagan@mozilla.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
namespace AimySpeedOptimization\Masterminds; defined('_JEXEC') or die(); use AimySpeedOptimization\Masterminds\HTML5\Parser\DOMTreeBuilder; use AimySpeedOptimization\Masterminds\HTML5\Parser\Scanner; use AimySpeedOptimization\Masterminds\HTML5\Parser\Tokenizer; use AimySpeedOptimization\Masterminds\HTML5\Serializer\OutputRules; use AimySpeedOptimization\Masterminds\HTML5\Serializer\Traverser; class HTML5 { private $defaultOptions = array( 'encode_entities' => false, 'disable_html_ns' => false, ); protected $errors = array(); public function __construct(array $defaultOptions = array()) { $this->defaultOptions = array_merge($this->defaultOptions, $defaultOptions); } public function getOptions() { return $this->defaultOptions; } public function load($file, array $options = array()) { if (is_resource($file)) { return $this->parse(stream_get_contents($file), $options); } return $this->parse(file_get_contents($file), $options); } public function loadHTML($string, array $options = array()) { return $this->parse($string, $options); } public function loadHTMLFile($file, array $options = array()) { return $this->load($file, $options); } public function loadHTMLFragment($string, array $options = array()) { return $this->parseFragment($string, $options); } public function getErrors() { return $this->errors; } public function hasErrors() { return count($this->errors) > 0; } public function parse($input, array $options = array()) { $this->errors = array(); $options = array_merge($this->defaultOptions, $options); $events = new DOMTreeBuilder(false, $options); $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'); $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML); $parser->parse(); $this->errors = $events->getErrors(); return $events->document(); } public function parseFragment($input, array $options = array()) { $options = array_merge($this->defaultOptions, $options); $events = new DOMTreeBuilder(true, $options); $scanner = new Scanner($input, !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'); $parser = new Tokenizer($scanner, $events, !empty($options['xmlNamespaces']) ? Tokenizer::CONFORMANT_XML : Tokenizer::CONFORMANT_HTML); $parser->parse(); $this->errors = $events->getErrors(); return $events->fragment(); } public function save($dom, $file, $options = array()) { $close = true; if (is_resource($file)) { $stream = $file; $close = false; } else { $stream = fopen($file, 'wb'); } $options = array_merge($this->defaultOptions, $options); $rules = new OutputRules($stream, $options); $trav = new Traverser($dom, $stream, $rules, $options); $trav->walk(); $rules->unsetTraverser(); if ($close) { fclose($stream); } } public function saveHTML($dom, $options = array()) { $stream = fopen('php://temp', 'wb'); $this->save($dom, $stream, array_merge($this->defaultOptions, $options)); $html = stream_get_contents($stream, -1, 0); fclose($stream); return $html; } } namespace AimySpeedOptimization\Masterminds\HTML5; class Elements { const KNOWN_ELEMENT = 1; const TEXT_RAW = 2; const TEXT_RCDATA = 4; const VOID_TAG = 8; const AUTOCLOSE_P = 16; const TEXT_PLAINTEXT = 32; const BLOCK_TAG = 64; const BLOCK_ONLY_INLINE = 128; public static $optionalEndElementsParentsToClose = array( 'tr' => array('td', 'tr'), 'td' => array('td', 'th'), 'th' => array('td', 'th'), 'tfoot' => array('td', 'th', 'tr', 'tbody', 'thead'), 'tbody' => array('td', 'th', 'tr', 'thead'), ); public static $html5 = array( 'a' => 1, 'abbr' => 1, 'address' => 65, 'area' => 9, 'article' => 81, 'aside' => 81, 'audio' => 1, 'b' => 1, 'base' => 9, 'bdi' => 1, 'bdo' => 1, 'blockquote' => 81, 'body' => 1, 'br' => 9, 'button' => 1, 'canvas' => 65, 'caption' => 1, 'cite' => 1, 'code' => 1, 'col' => 9, 'colgroup' => 1, 'command' => 9, 'datalist' => 1, 'dd' => 65, 'del' => 1, 'details' => 17, 'dfn' => 1, 'dialog' => 17, 'div' => 81, 'dl' => 81, 'dt' => 1, 'em' => 1, 'embed' => 9, 'fieldset' => 81, 'figcaption' => 81, 'figure' => 81, 'footer' => 81, 'form' => 81, 'h1' => 81, 'h2' => 81, 'h3' => 81, 'h4' => 81, 'h5' => 81, 'h6' => 81, 'head' => 1, 'header' => 81, 'hgroup' => 81, 'hr' => 73, 'html' => 1, 'i' => 1, 'iframe' => 3, 'img' => 9, 'input' => 9, 'kbd' => 1, 'ins' => 1, 'keygen' => 9, 'label' => 1, 'legend' => 1, 'li' => 1, 'link' => 9, 'map' => 1, 'mark' => 1, 'menu' => 17, 'meta' => 9, 'meter' => 1, 'nav' => 17, 'noscript' => 65, 'object' => 1, 'ol' => 81, 'optgroup' => 1, 'option' => 1, 'output' => 65, 'p' => 209, 'param' => 9, 'pre' => 81, 'progress' => 1, 'q' => 1, 'rp' => 1, 'rt' => 1, 'ruby' => 1, 's' => 1, 'samp' => 1, 'script' => 3, 'section' => 81, 'select' => 1, 'small' => 1, 'source' => 9, 'span' => 1, 'strong' => 1, 'style' => 3, 'sub' => 1, 'summary' => 17, 'sup' => 1, 'table' => 65, 'tbody' => 1, 'td' => 1, 'textarea' => 5, 'tfoot' => 65, 'th' => 1, 'thead' => 1, 'time' => 1, 'title' => 5, 'tr' => 1, 'track' => 9, 'u' => 1, 'ul' => 81, 'var' => 1, 'video' => 1, 'wbr' => 9, 'basefont' => 8, 'bgsound' => 8, 'noframes' => 2, 'frame' => 9, 'frameset' => 1, 'center' => 16, 'dir' => 16, 'listing' => 16, 'plaintext' => 48, 'applet' => 0, 'marquee' => 0, 'isindex' => 8, 'xmp' => 20, 'noembed' => 2, ); public static $mathml = array( 'maction' => 1, 'maligngroup' => 1, 'malignmark' => 1, 'math' => 1, 'menclose' => 1, 'merror' => 1, 'mfenced' => 1, 'mfrac' => 1, 'mglyph' => 1, 'mi' => 1, 'mlabeledtr' => 1, 'mlongdiv' => 1, 'mmultiscripts' => 1, 'mn' => 1, 'mo' => 1, 'mover' => 1, 'mpadded' => 1, 'mphantom' => 1, 'mroot' => 1, 'mrow' => 1, 'ms' => 1, 'mscarries' => 1, 'mscarry' => 1, 'msgroup' => 1, 'msline' => 1, 'mspace' => 1, 'msqrt' => 1, 'msrow' => 1, 'mstack' => 1, 'mstyle' => 1, 'msub' => 1, 'msup' => 1, 'msubsup' => 1, 'mtable' => 1, 'mtd' => 1, 'mtext' => 1, 'mtr' => 1, 'munder' => 1, 'munderover' => 1, ); public static $svg = array( 'a' => 1, 'altGlyph' => 1, 'altGlyphDef' => 1, 'altGlyphItem' => 1, 'animate' => 1, 'animateColor' => 1, 'animateMotion' => 1, 'animateTransform' => 1, 'circle' => 1, 'clipPath' => 1, 'color-profile' => 1, 'cursor' => 1, 'defs' => 1, 'desc' => 1, 'ellipse' => 1, 'feBlend' => 1, 'feColorMatrix' => 1, 'feComponentTransfer' => 1, 'feComposite' => 1, 'feConvolveMatrix' => 1, 'feDiffuseLighting' => 1, 'feDisplacementMap' => 1, 'feDistantLight' => 1, 'feFlood' => 1, 'feFuncA' => 1, 'feFuncB' => 1, 'feFuncG' => 1, 'feFuncR' => 1, 'feGaussianBlur' => 1, 'feImage' => 1, 'feMerge' => 1, 'feMergeNode' => 1, 'feMorphology' => 1, 'feOffset' => 1, 'fePointLight' => 1, 'feSpecularLighting' => 1, 'feSpotLight' => 1, 'feTile' => 1, 'feTurbulence' => 1, 'filter' => 1, 'font' => 1, 'font-face' => 1, 'font-face-format' => 1, 'font-face-name' => 1, 'font-face-src' => 1, 'font-face-uri' => 1, 'foreignObject' => 1, 'g' => 1, 'glyph' => 1, 'glyphRef' => 1, 'hkern' => 1, 'image' => 1, 'line' => 1, 'linearGradient' => 1, 'marker' => 1, 'mask' => 1, 'metadata' => 1, 'missing-glyph' => 1, 'mpath' => 1, 'path' => 1, 'pattern' => 1, 'polygon' => 1, 'polyline' => 1, 'radialGradient' => 1, 'rect' => 1, 'script' => 3, 'set' => 1, 'stop' => 1, 'style' => 3, 'svg' => 1, 'switch' => 1, 'symbol' => 1, 'text' => 1, 'textPath' => 1, 'title' => 1, 'tref' => 1, 'tspan' => 1, 'use' => 1, 'view' => 1, 'vkern' => 1, ); public static $svgCaseSensitiveAttributeMap = array( 'attributename' => 'attributeName', 'attributetype' => 'attributeType', 'basefrequency' => 'baseFrequency', 'baseprofile' => 'baseProfile', 'calcmode' => 'calcMode', 'clippathunits' => 'clipPathUnits', 'contentscripttype' => 'contentScriptType', 'contentstyletype' => 'contentStyleType', 'diffuseconstant' => 'diffuseConstant', 'edgemode' => 'edgeMode', 'externalresourcesrequired' => 'externalResourcesRequired', 'filterres' => 'filterRes', 'filterunits' => 'filterUnits', 'glyphref' => 'glyphRef', 'gradienttransform' => 'gradientTransform', 'gradientunits' => 'gradientUnits', 'kernelmatrix' => 'kernelMatrix', 'kernelunitlength' => 'kernelUnitLength', 'keypoints' => 'keyPoints', 'keysplines' => 'keySplines', 'keytimes' => 'keyTimes', 'lengthadjust' => 'lengthAdjust', 'limitingconeangle' => 'limitingConeAngle', 'markerheight' => 'markerHeight', 'markerunits' => 'markerUnits', 'markerwidth' => 'markerWidth', 'maskcontentunits' => 'maskContentUnits', 'maskunits' => 'maskUnits', 'numoctaves' => 'numOctaves', 'pathlength' => 'pathLength', 'patterncontentunits' => 'patternContentUnits', 'patterntransform' => 'patternTransform', 'patternunits' => 'patternUnits', 'pointsatx' => 'pointsAtX', 'pointsaty' => 'pointsAtY', 'pointsatz' => 'pointsAtZ', 'preservealpha' => 'preserveAlpha', 'preserveaspectratio' => 'preserveAspectRatio', 'primitiveunits' => 'primitiveUnits', 'refx' => 'refX', 'refy' => 'refY', 'repeatcount' => 'repeatCount', 'repeatdur' => 'repeatDur', 'requiredextensions' => 'requiredExtensions', 'requiredfeatures' => 'requiredFeatures', 'specularconstant' => 'specularConstant', 'specularexponent' => 'specularExponent', 'spreadmethod' => 'spreadMethod', 'startoffset' => 'startOffset', 'stddeviation' => 'stdDeviation', 'stitchtiles' => 'stitchTiles', 'surfacescale' => 'surfaceScale', 'systemlanguage' => 'systemLanguage', 'tablevalues' => 'tableValues', 'targetx' => 'targetX', 'targety' => 'targetY', 'textlength' => 'textLength', 'viewbox' => 'viewBox', 'viewtarget' => 'viewTarget', 'xchannelselector' => 'xChannelSelector', 'ychannelselector' => 'yChannelSelector', 'zoomandpan' => 'zoomAndPan', ); public static $svgCaseSensitiveElementMap = array( 'altglyph' => 'altGlyph', 'altglyphdef' => 'altGlyphDef', 'altglyphitem' => 'altGlyphItem', 'animatecolor' => 'animateColor', 'animatemotion' => 'animateMotion', 'animatetransform' => 'animateTransform', 'clippath' => 'clipPath', 'feblend' => 'feBlend', 'fecolormatrix' => 'feColorMatrix', 'fecomponenttransfer' => 'feComponentTransfer', 'fecomposite' => 'feComposite', 'feconvolvematrix' => 'feConvolveMatrix', 'fediffuselighting' => 'feDiffuseLighting', 'fedisplacementmap' => 'feDisplacementMap', 'fedistantlight' => 'feDistantLight', 'feflood' => 'feFlood', 'fefunca' => 'feFuncA', 'fefuncb' => 'feFuncB', 'fefuncg' => 'feFuncG', 'fefuncr' => 'feFuncR', 'fegaussianblur' => 'feGaussianBlur', 'feimage' => 'feImage', 'femerge' => 'feMerge', 'femergenode' => 'feMergeNode', 'femorphology' => 'feMorphology', 'feoffset' => 'feOffset', 'fepointlight' => 'fePointLight', 'fespecularlighting' => 'feSpecularLighting', 'fespotlight' => 'feSpotLight', 'fetile' => 'feTile', 'feturbulence' => 'feTurbulence', 'foreignobject' => 'foreignObject', 'glyphref' => 'glyphRef', 'lineargradient' => 'linearGradient', 'radialgradient' => 'radialGradient', 'textpath' => 'textPath', ); public static function isA($name, $mask) { return (static::element($name) & $mask) === $mask; } public static function isHtml5Element($name) { return isset(static::$html5[strtolower($name)]); } public static function isMathMLElement($name) { return isset(static::$mathml[$name]); } public static function isSvgElement($name) { return isset(static::$svg[$name]); } public static function isElement($name) { return static::isHtml5Element($name) || static::isMathMLElement($name) || static::isSvgElement($name); } public static function element($name) { if (isset(static::$html5[$name])) { return static::$html5[$name]; } if (isset(static::$svg[$name])) { return static::$svg[$name]; } if (isset(static::$mathml[$name])) { return static::$mathml[$name]; } return 0; } public static function normalizeSvgElement($name) { $name = strtolower($name); if (isset(static::$svgCaseSensitiveElementMap[$name])) { $name = static::$svgCaseSensitiveElementMap[$name]; } return $name; } public static function normalizeSvgAttribute($name) { $name = strtolower($name); if (isset(static::$svgCaseSensitiveAttributeMap[$name])) { $name = static::$svgCaseSensitiveAttributeMap[$name]; } return $name; } public static function normalizeMathMlAttribute($name) { $name = strtolower($name); if ('definitionurl' === $name) { $name = 'definitionURL'; } return $name; } } namespace AimySpeedOptimization\Masterminds\HTML5; class Entities { public static $byName = array( 'Aacute' => 'Á', 'Aacut' => 'Á', 'aacute' => 'á', 'aacut' => 'á', 'Abreve' => 'Ă', 'abreve' => 'ă', 'ac' => '∾', 'acd' => '∿', 'acE' => '∾̳', 'Acirc' => 'Â', 'Acir' => 'Â', 'acirc' => 'â', 'acir' => 'â', 'acute' => '´', 'acut' => '´', 'Acy' => 'А', 'acy' => 'а', 'AElig' => 'Æ', 'AEli' => 'Æ', 'aelig' => 'æ', 'aeli' => 'æ', 'af' => '', 'Afr' => '𝔄', 'afr' => '𝔞', 'Agrave' => 'À', 'Agrav' => 'À', 'agrave' => 'à', 'agrav' => 'à', 'alefsym' => 'ℵ', 'aleph' => 'ℵ', 'Alpha' => 'Α', 'alpha' => 'α', 'Amacr' => 'Ā', 'amacr' => 'ā', 'amalg' => '⨿', 'AMP' => '&', 'AM' => '&', 'amp' => '&', 'am' => '&', 'And' => '⩓', 'and' => '∧', 'andand' => '⩕', 'andd' => '⩜', 'andslope' => '⩘', 'andv' => '⩚', 'ang' => '∠', 'ange' => '⦤', 'angle' => '∠', 'angmsd' => '∡', 'angmsdaa' => '⦨', 'angmsdab' => '⦩', 'angmsdac' => '⦪', 'angmsdad' => '⦫', 'angmsdae' => '⦬', 'angmsdaf' => '⦭', 'angmsdag' => '⦮', 'angmsdah' => '⦯', 'angrt' => '∟', 'angrtvb' => '⊾', 'angrtvbd' => '⦝', 'angsph' => '∢', 'angst' => 'Å', 'angzarr' => '⍼', 'Aogon' => 'Ą', 'aogon' => 'ą', 'Aopf' => '𝔸', 'aopf' => '𝕒', 'ap' => '≈', 'apacir' => '⩯', 'apE' => '⩰', 'ape' => '≊', 'apid' => '≋', 'apos' => '\'', 'ApplyFunction' => '', 'approx' => '≈', 'approxeq' => '≊', 'Aring' => 'Å', 'Arin' => 'Å', 'aring' => 'å', 'arin' => 'å', 'Ascr' => '𝒜', 'ascr' => '𝒶', 'Assign' => '≔', 'ast' => '*', 'asymp' => '≈', 'asympeq' => '≍', 'Atilde' => 'Ã', 'Atild' => 'Ã', 'atilde' => 'ã', 'atild' => 'ã', 'Auml' => 'Ä', 'Aum' => 'Ä', 'auml' => 'ä', 'aum' => 'ä', 'awconint' => '∳', 'awint' => '⨑', 'backcong' => '≌', 'backepsilon' => '϶', 'backprime' => '‵', 'backsim' => '∽', 'backsimeq' => '⋍', 'Backslash' => '∖', 'Barv' => '⫧', 'barvee' => '⊽', 'Barwed' => '⌆', 'barwed' => '⌅', 'barwedge' => '⌅', 'bbrk' => '⎵', 'bbrktbrk' => '⎶', 'bcong' => '≌', 'Bcy' => 'Б', 'bcy' => 'б', 'bdquo' => '„', 'becaus' => '∵', 'Because' => '∵', 'because' => '∵', 'bemptyv' => '⦰', 'bepsi' => '϶', 'bernou' => 'ℬ', 'Bernoullis' => 'ℬ', 'Beta' => 'Β', 'beta' => 'β', 'beth' => 'ℶ', 'between' => '≬', 'Bfr' => '𝔅', 'bfr' => '𝔟', 'bigcap' => '⋂', 'bigcirc' => '◯', 'bigcup' => '⋃', 'bigodot' => '⨀', 'bigoplus' => '⨁', 'bigotimes' => '⨂', 'bigsqcup' => '⨆', 'bigstar' => '★', 'bigtriangledown' => '▽', 'bigtriangleup' => '△', 'biguplus' => '⨄', 'bigvee' => '⋁', 'bigwedge' => '⋀', 'bkarow' => '⤍', 'blacklozenge' => '⧫', 'blacksquare' => '▪', 'blacktriangle' => '▴', 'blacktriangledown' => '▾', 'blacktriangleleft' => '◂', 'blacktriangleright' => '▸', 'blank' => '␣', 'blk12' => '▒', 'blk14' => '░', 'blk34' => '▓', 'block' => '█', 'bne' => '=⃥', 'bnequiv' => '≡⃥', 'bNot' => '⫭', 'bnot' => '⌐', 'Bopf' => '𝔹', 'bopf' => '𝕓', 'bot' => '⊥', 'bottom' => '⊥', 'bowtie' => '⋈', 'boxbox' => '⧉', 'boxDL' => '╗', 'boxDl' => '╖', 'boxdL' => '╕', 'boxdl' => '┐', 'boxDR' => '╔', 'boxDr' => '╓', 'boxdR' => '╒', 'boxdr' => '┌', 'boxH' => '═', 'boxh' => '─', 'boxHD' => '╦', 'boxHd' => '╤', 'boxhD' => '╥', 'boxhd' => '┬', 'boxHU' => '╩', 'boxHu' => '╧', 'boxhU' => '╨', 'boxhu' => '┴', 'boxminus' => '⊟', 'boxplus' => '⊞', 'boxtimes' => '⊠', 'boxUL' => '╝', 'boxUl' => '╜', 'boxuL' => '╛', 'boxul' => '┘', 'boxUR' => '╚', 'boxUr' => '╙', 'boxuR' => '╘', 'boxur' => '└', 'boxV' => '║', 'boxv' => '│', 'boxVH' => '╬', 'boxVh' => '╫', 'boxvH' => '╪', 'boxvh' => '┼', 'boxVL' => '╣', 'boxVl' => '╢', 'boxvL' => '╡', 'boxvl' => '┤', 'boxVR' => '╠', 'boxVr' => '╟', 'boxvR' => '╞', 'boxvr' => '├', 'bprime' => '‵', 'Breve' => '˘', 'breve' => '˘', 'brvbar' => '¦', 'brvba' => '¦', 'Bscr' => 'ℬ', 'bscr' => '𝒷', 'bsemi' => '⁏', 'bsim' => '∽', 'bsime' => '⋍', 'bsol' => '\\', 'bsolb' => '⧅', 'bsolhsub' => '⟈', 'bull' => '•', 'bullet' => '•', 'bump' => '≎', 'bumpE' => '⪮', 'bumpe' => '≏', 'Bumpeq' => '≎', 'bumpeq' => '≏', 'Cacute' => 'Ć', 'cacute' => 'ć', 'Cap' => '⋒', 'cap' => '∩', 'capand' => '⩄', 'capbrcup' => '⩉', 'capcap' => '⩋', 'capcup' => '⩇', 'capdot' => '⩀', 'CapitalDifferentialD' => 'ⅅ', 'caps' => '∩︀', 'caret' => '⁁', 'caron' => 'ˇ', 'Cayleys' => 'ℭ', 'ccaps' => '⩍', 'Ccaron' => 'Č', 'ccaron' => 'č', 'Ccedil' => 'Ç', 'Ccedi' => 'Ç', 'ccedil' => 'ç', 'ccedi' => 'ç', 'Ccirc' => 'Ĉ', 'ccirc' => 'ĉ', 'Cconint' => '∰', 'ccups' => '⩌', 'ccupssm' => '⩐', 'Cdot' => 'Ċ', 'cdot' => 'ċ', 'cedil' => '¸', 'cedi' => '¸', 'Cedilla' => '¸', 'cemptyv' => '⦲', 'cent' => '¢', 'cen' => '¢', 'CenterDot' => '·', 'centerdot' => '·', 'Cfr' => 'ℭ', 'cfr' => '𝔠', 'CHcy' => 'Ч', 'chcy' => 'ч', 'check' => '✓', 'checkmark' => '✓', 'Chi' => 'Χ', 'chi' => 'χ', 'cir' => '○', 'circ' => 'ˆ', 'circeq' => '≗', 'circlearrowleft' => '↺', 'circlearrowright' => '↻', 'circledast' => '⊛', 'circledcirc' => '⊚', 'circleddash' => '⊝', 'CircleDot' => '⊙', 'circledR' => '®', 'circledS' => 'Ⓢ', 'CircleMinus' => '⊖', 'CirclePlus' => '⊕', 'CircleTimes' => '⊗', 'cirE' => '⧃', 'cire' => '≗', 'cirfnint' => '⨐', 'cirmid' => '⫯', 'cirscir' => '⧂', 'ClockwiseContourIntegral' => '∲', 'CloseCurlyDoubleQuote' => '”', 'CloseCurlyQuote' => '’', 'clubs' => '♣', 'clubsuit' => '♣', 'Colon' => '∷', 'colon' => ':', 'Colone' => '⩴', 'colone' => '≔', 'coloneq' => '≔', 'comma' => ',', 'commat' => '@', 'comp' => '∁', 'compfn' => '∘', 'complement' => '∁', 'complexes' => 'ℂ', 'cong' => '≅', 'congdot' => '⩭', 'Congruent' => '≡', 'Conint' => '∯', 'conint' => '∮', 'ContourIntegral' => '∮', 'Copf' => 'ℂ', 'copf' => '𝕔', 'coprod' => '∐', 'Coproduct' => '∐', 'COPY' => '©', 'COP' => '©', 'copy' => '©', 'cop' => '©', 'copysr' => '℗', 'CounterClockwiseContourIntegral' => '∳', 'crarr' => '↵', 'Cross' => '⨯', 'cross' => '✗', 'Cscr' => '𝒞', 'cscr' => '𝒸', 'csub' => '⫏', 'csube' => '⫑', 'csup' => '⫐', 'csupe' => '⫒', 'ctdot' => '⋯', 'cudarrl' => '⤸', 'cudarrr' => '⤵', 'cuepr' => '⋞', 'cuesc' => '⋟', 'cularr' => '↶', 'cularrp' => '⤽', 'Cup' => '⋓', 'cup' => '∪', 'cupbrcap' => '⩈', 'CupCap' => '≍', 'cupcap' => '⩆', 'cupcup' => '⩊', 'cupdot' => '⊍', 'cupor' => '⩅', 'cups' => '∪︀', 'curarr' => '↷', 'curarrm' => '⤼', 'curlyeqprec' => '⋞', 'curlyeqsucc' => '⋟', 'curlyvee' => '⋎', 'curlywedge' => '⋏', 'curren' => '¤', 'curre' => '¤', 'curvearrowleft' => '↶', 'curvearrowright' => '↷', 'cuvee' => '⋎', 'cuwed' => '⋏', 'cwconint' => '∲', 'cwint' => '∱', 'cylcty' => '⌭', 'Dagger' => '‡', 'dagger' => '†', 'daleth' => 'ℸ', 'Darr' => '↡', 'dArr' => '⇓', 'darr' => '↓', 'dash' => '‐', 'Dashv' => '⫤', 'dashv' => '⊣', 'dbkarow' => '⤏', 'dblac' => '˝', 'Dcaron' => 'Ď', 'dcaron' => 'ď', 'Dcy' => 'Д', 'dcy' => 'д', 'DD' => 'ⅅ', 'dd' => 'ⅆ', 'ddagger' => '‡', 'ddarr' => '⇊', 'DDotrahd' => '⤑', 'ddotseq' => '⩷', 'deg' => '°', 'de' => '°', 'Del' => '∇', 'Delta' => 'Δ', 'delta' => 'δ', 'demptyv' => '⦱', 'dfisht' => '⥿', 'Dfr' => '𝔇', 'dfr' => '𝔡', 'dHar' => '⥥', 'dharl' => '⇃', 'dharr' => '⇂', 'DiacriticalAcute' => '´', 'DiacriticalDot' => '˙', 'DiacriticalDoubleAcute' => '˝', 'DiacriticalGrave' => '`', 'DiacriticalTilde' => '˜', 'diam' => '⋄', 'Diamond' => '⋄', 'diamond' => '⋄', 'diamondsuit' => '♦', 'diams' => '♦', 'die' => '¨', 'DifferentialD' => 'ⅆ', 'digamma' => 'ϝ', 'disin' => '⋲', 'div' => '÷', 'divide' => '÷', 'divid' => '÷', 'divideontimes' => '⋇', 'divonx' => '⋇', 'DJcy' => 'Ђ', 'djcy' => 'ђ', 'dlcorn' => '⌞', 'dlcrop' => '⌍', 'dollar' => '$', 'Dopf' => '𝔻', 'dopf' => '𝕕', 'Dot' => '¨', 'dot' => '˙', 'DotDot' => '⃜', 'doteq' => '≐', 'doteqdot' => '≑', 'DotEqual' => '≐', 'dotminus' => '∸', 'dotplus' => '∔', 'dotsquare' => '⊡', 'doublebarwedge' => '⌆', 'DoubleContourIntegral' => '∯', 'DoubleDot' => '¨', 'DoubleDownArrow' => '⇓', 'DoubleLeftArrow' => '⇐', 'DoubleLeftRightArrow' => '⇔', 'DoubleLeftTee' => '⫤', 'DoubleLongLeftArrow' => '⟸', 'DoubleLongLeftRightArrow' => '⟺', 'DoubleLongRightArrow' => '⟹', 'DoubleRightArrow' => '⇒', 'DoubleRightTee' => '⊨', 'DoubleUpArrow' => '⇑', 'DoubleUpDownArrow' => '⇕', 'DoubleVerticalBar' => '∥', 'DownArrow' => '↓', 'Downarrow' => '⇓', 'downarrow' => '↓', 'DownArrowBar' => '⤓', 'DownArrowUpArrow' => '⇵', 'DownBreve' => '̑', 'downdownarrows' => '⇊', 'downharpoonleft' => '⇃', 'downharpoonright' => '⇂', 'DownLeftRightVector' => '⥐', 'DownLeftTeeVector' => '⥞', 'DownLeftVector' => '↽', 'DownLeftVectorBar' => '⥖', 'DownRightTeeVector' => '⥟', 'DownRightVector' => '⇁', 'DownRightVectorBar' => '⥗', 'DownTee' => '⊤', 'DownTeeArrow' => '↧', 'drbkarow' => '⤐', 'drcorn' => '⌟', 'drcrop' => '⌌', 'Dscr' => '𝒟', 'dscr' => '𝒹', 'DScy' => 'Ѕ', 'dscy' => 'ѕ', 'dsol' => '⧶', 'Dstrok' => 'Đ', 'dstrok' => 'đ', 'dtdot' => '⋱', 'dtri' => '▿', 'dtrif' => '▾', 'duarr' => '⇵', 'duhar' => '⥯', 'dwangle' => '⦦', 'DZcy' => 'Џ', 'dzcy' => 'џ', 'dzigrarr' => '⟿', 'Eacute' => 'É', 'Eacut' => 'É', 'eacute' => 'é', 'eacut' => 'é', 'easter' => '⩮', 'Ecaron' => 'Ě', 'ecaron' => 'ě', 'ecir' => 'ê', 'Ecirc' => 'Ê', 'Ecir' => 'Ê', 'ecirc' => 'ê', 'ecolon' => '≕', 'Ecy' => 'Э', 'ecy' => 'э', 'eDDot' => '⩷', 'Edot' => 'Ė', 'eDot' => '≑', 'edot' => 'ė', 'ee' => 'ⅇ', 'efDot' => '≒', 'Efr' => '𝔈', 'efr' => '𝔢', 'eg' => '⪚', 'Egrave' => 'È', 'Egrav' => 'È', 'egrave' => 'è', 'egrav' => 'è', 'egs' => '⪖', 'egsdot' => '⪘', 'el' => '⪙', 'Element' => '∈', 'elinters' => '⏧', 'ell' => 'ℓ', 'els' => '⪕', 'elsdot' => '⪗', 'Emacr' => 'Ē', 'emacr' => 'ē', 'empty' => '∅', 'emptyset' => '∅', 'EmptySmallSquare' => '◻', 'emptyv' => '∅', 'EmptyVerySmallSquare' => '▫', 'emsp' => ' ', 'emsp13' => ' ', 'emsp14' => ' ', 'ENG' => 'Ŋ', 'eng' => 'ŋ', 'ensp' => ' ', 'Eogon' => 'Ę', 'eogon' => 'ę', 'Eopf' => '𝔼', 'eopf' => '𝕖', 'epar' => '⋕', 'eparsl' => '⧣', 'eplus' => '⩱', 'epsi' => 'ε', 'Epsilon' => 'Ε', 'epsilon' => 'ε', 'epsiv' => 'ϵ', 'eqcirc' => '≖', 'eqcolon' => '≕', 'eqsim' => '≂', 'eqslantgtr' => '⪖', 'eqslantless' => '⪕', 'Equal' => '⩵', 'equals' => '=', 'EqualTilde' => '≂', 'equest' => '≟', 'Equilibrium' => '⇌', 'equiv' => '≡', 'equivDD' => '⩸', 'eqvparsl' => '⧥', 'erarr' => '⥱', 'erDot' => '≓', 'Escr' => 'ℰ', 'escr' => 'ℯ', 'esdot' => '≐', 'Esim' => '⩳', 'esim' => '≂', 'Eta' => 'Η', 'eta' => 'η', 'ETH' => 'Ð', 'ET' => 'Ð', 'eth' => 'ð', 'et' => 'ð', 'Euml' => 'Ë', 'Eum' => 'Ë', 'euml' => 'ë', 'eum' => 'ë', 'euro' => '€', 'excl' => '!', 'exist' => '∃', 'Exists' => '∃', 'expectation' => 'ℰ', 'ExponentialE' => 'ⅇ', 'exponentiale' => 'ⅇ', 'fallingdotseq' => '≒', 'Fcy' => 'Ф', 'fcy' => 'ф', 'female' => '♀', 'ffilig' => 'ffi', 'fflig' => 'ff', 'ffllig' => 'ffl', 'Ffr' => '𝔉', 'ffr' => '𝔣', 'filig' => 'fi', 'FilledSmallSquare' => '◼', 'FilledVerySmallSquare' => '▪', 'fjlig' => 'fj', 'flat' => '♭', 'fllig' => 'fl', 'fltns' => '▱', 'fnof' => 'ƒ', 'Fopf' => '𝔽', 'fopf' => '𝕗', 'ForAll' => '∀', 'forall' => '∀', 'fork' => '⋔', 'forkv' => '⫙', 'Fouriertrf' => 'ℱ', 'fpartint' => '⨍', 'frac12' => '½', 'frac1' => '¼', 'frac13' => '⅓', 'frac14' => '¼', 'frac15' => '⅕', 'frac16' => '⅙', 'frac18' => '⅛', 'frac23' => '⅔', 'frac25' => '⅖', 'frac34' => '¾', 'frac3' => '¾', 'frac35' => '⅗', 'frac38' => '⅜', 'frac45' => '⅘', 'frac56' => '⅚', 'frac58' => '⅝', 'frac78' => '⅞', 'frasl' => '⁄', 'frown' => '⌢', 'Fscr' => 'ℱ', 'fscr' => '𝒻', 'gacute' => 'ǵ', 'Gamma' => 'Γ', 'gamma' => 'γ', 'Gammad' => 'Ϝ', 'gammad' => 'ϝ', 'gap' => '⪆', 'Gbreve' => 'Ğ', 'gbreve' => 'ğ', 'Gcedil' => 'Ģ', 'Gcirc' => 'Ĝ', 'gcirc' => 'ĝ', 'Gcy' => 'Г', 'gcy' => 'г', 'Gdot' => 'Ġ', 'gdot' => 'ġ', 'gE' => '≧', 'ge' => '≥', 'gEl' => '⪌', 'gel' => '⋛', 'geq' => '≥', 'geqq' => '≧', 'geqslant' => '⩾', 'ges' => '⩾', 'gescc' => '⪩', 'gesdot' => '⪀', 'gesdoto' => '⪂', 'gesdotol' => '⪄', 'gesl' => '⋛︀', 'gesles' => '⪔', 'Gfr' => '𝔊', 'gfr' => '𝔤', 'Gg' => '⋙', 'gg' => '≫', 'ggg' => '⋙', 'gimel' => 'ℷ', 'GJcy' => 'Ѓ', 'gjcy' => 'ѓ', 'gl' => '≷', 'gla' => '⪥', 'glE' => '⪒', 'glj' => '⪤', 'gnap' => '⪊', 'gnapprox' => '⪊', 'gnE' => '≩', 'gne' => '⪈', 'gneq' => '⪈', 'gneqq' => '≩', 'gnsim' => '⋧', 'Gopf' => '𝔾', 'gopf' => '𝕘', 'grave' => '`', 'GreaterEqual' => '≥', 'GreaterEqualLess' => '⋛', 'GreaterFullEqual' => '≧', 'GreaterGreater' => '⪢', 'GreaterLess' => '≷', 'GreaterSlantEqual' => '⩾', 'GreaterTilde' => '≳', 'Gscr' => '𝒢', 'gscr' => 'ℊ', 'gsim' => '≳', 'gsime' => '⪎', 'gsiml' => '⪐', 'GT' => '>', 'G' => '>', 'Gt' => '≫', 'gt' => '>', 'g' => '>', 'gtcc' => '⪧', 'gtcir' => '⩺', 'gtdot' => '⋗', 'gtlPar' => '⦕', 'gtquest' => '⩼', 'gtrapprox' => '⪆', 'gtrarr' => '⥸', 'gtrdot' => '⋗', 'gtreqless' => '⋛', 'gtreqqless' => '⪌', 'gtrless' => '≷', 'gtrsim' => '≳', 'gvertneqq' => '≩︀', 'gvnE' => '≩︀', 'Hacek' => 'ˇ', 'hairsp' => ' ', 'half' => '½', 'hamilt' => 'ℋ', 'HARDcy' => 'Ъ', 'hardcy' => 'ъ', 'hArr' => '⇔', 'harr' => '↔', 'harrcir' => '⥈', 'harrw' => '↭', 'Hat' => '^', 'hbar' => 'ℏ', 'Hcirc' => 'Ĥ', 'hcirc' => 'ĥ', 'hearts' => '♥', 'heartsuit' => '♥', 'hellip' => '…', 'hercon' => '⊹', 'Hfr' => 'ℌ', 'hfr' => '𝔥', 'HilbertSpace' => 'ℋ', 'hksearow' => '⤥', 'hkswarow' => '⤦', 'hoarr' => '⇿', 'homtht' => '∻', 'hookleftarrow' => '↩', 'hookrightarrow' => '↪', 'Hopf' => 'ℍ', 'hopf' => '𝕙', 'horbar' => '―', 'HorizontalLine' => '─', 'Hscr' => 'ℋ', 'hscr' => '𝒽', 'hslash' => 'ℏ', 'Hstrok' => 'Ħ', 'hstrok' => 'ħ', 'HumpDownHump' => '≎', 'HumpEqual' => '≏', 'hybull' => '⁃', 'hyphen' => '‐', 'Iacute' => 'Í', 'Iacut' => 'Í', 'iacute' => 'í', 'iacut' => 'í', 'ic' => '', 'Icirc' => 'Î', 'Icir' => 'Î', 'icirc' => 'î', 'icir' => 'î', 'Icy' => 'И', 'icy' => 'и', 'Idot' => 'İ', 'IEcy' => 'Е', 'iecy' => 'е', 'iexcl' => '¡', 'iexc' => '¡', 'iff' => '⇔', 'Ifr' => 'ℑ', 'ifr' => '𝔦', 'Igrave' => 'Ì', 'Igrav' => 'Ì', 'igrave' => 'ì', 'igrav' => 'ì', 'ii' => 'ⅈ', 'iiiint' => '⨌', 'iiint' => '∭', 'iinfin' => '⧜', 'iiota' => '℩', 'IJlig' => 'IJ', 'ijlig' => 'ij', 'Im' => 'ℑ', 'Imacr' => 'Ī', 'imacr' => 'ī', 'image' => 'ℑ', 'ImaginaryI' => 'ⅈ', 'imagline' => 'ℐ', 'imagpart' => 'ℑ', 'imath' => 'ı', 'imof' => '⊷', 'imped' => 'Ƶ', 'Implies' => '⇒', 'in' => '∈', 'incare' => '℅', 'infin' => '∞', 'infintie' => '⧝', 'inodot' => 'ı', 'Int' => '∬', 'int' => '∫', 'intcal' => '⊺', 'integers' => 'ℤ', 'Integral' => '∫', 'intercal' => '⊺', 'Intersection' => '⋂', 'intlarhk' => '⨗', 'intprod' => '⨼', 'InvisibleComma' => '', 'InvisibleTimes' => '', 'IOcy' => 'Ё', 'iocy' => 'ё', 'Iogon' => 'Į', 'iogon' => 'į', 'Iopf' => '𝕀', 'iopf' => '𝕚', 'Iota' => 'Ι', 'iota' => 'ι', 'iprod' => '⨼', 'iquest' => '¿', 'iques' => '¿', 'Iscr' => 'ℐ', 'iscr' => '𝒾', 'isin' => '∈', 'isindot' => '⋵', 'isinE' => '⋹', 'isins' => '⋴', 'isinsv' => '⋳', 'isinv' => '∈', 'it' => '', 'Itilde' => 'Ĩ', 'itilde' => 'ĩ', 'Iukcy' => 'І', 'iukcy' => 'і', 'Iuml' => 'Ï', 'Ium' => 'Ï', 'iuml' => 'ï', 'ium' => 'ï', 'Jcirc' => 'Ĵ', 'jcirc' => 'ĵ', 'Jcy' => 'Й', 'jcy' => 'й', 'Jfr' => '𝔍', 'jfr' => '𝔧', 'jmath' => 'ȷ', 'Jopf' => '𝕁', 'jopf' => '𝕛', 'Jscr' => '𝒥', 'jscr' => '𝒿', 'Jsercy' => 'Ј', 'jsercy' => 'ј', 'Jukcy' => 'Є', 'jukcy' => 'є', 'Kappa' => 'Κ', 'kappa' => 'κ', 'kappav' => 'ϰ', 'Kcedil' => 'Ķ', 'kcedil' => 'ķ', 'Kcy' => 'К', 'kcy' => 'к', 'Kfr' => '𝔎', 'kfr' => '𝔨', 'kgreen' => 'ĸ', 'KHcy' => 'Х', 'khcy' => 'х', 'KJcy' => 'Ќ', 'kjcy' => 'ќ', 'Kopf' => '𝕂', 'kopf' => '𝕜', 'Kscr' => '𝒦', 'kscr' => '𝓀', 'lAarr' => '⇚', 'Lacute' => 'Ĺ', 'lacute' => 'ĺ', 'laemptyv' => '⦴', 'lagran' => 'ℒ', 'Lambda' => 'Λ', 'lambda' => 'λ', 'Lang' => '⟪', 'lang' => '⟨', 'langd' => '⦑', 'langle' => '⟨', 'lap' => '⪅', 'Laplacetrf' => 'ℒ', 'laquo' => '«', 'laqu' => '«', 'Larr' => '↞', 'lArr' => '⇐', 'larr' => '←', 'larrb' => '⇤', 'larrbfs' => '⤟', 'larrfs' => '⤝', 'larrhk' => '↩', 'larrlp' => '↫', 'larrpl' => '⤹', 'larrsim' => '⥳', 'larrtl' => '↢', 'lat' => '⪫', 'lAtail' => '⤛', 'latail' => '⤙', 'late' => '⪭', 'lates' => '⪭︀', 'lBarr' => '⤎', 'lbarr' => '⤌', 'lbbrk' => '❲', 'lbrace' => '{', 'lbrack' => '[', 'lbrke' => '⦋', 'lbrksld' => '⦏', 'lbrkslu' => '⦍', 'Lcaron' => 'Ľ', 'lcaron' => 'ľ', 'Lcedil' => 'Ļ', 'lcedil' => 'ļ', 'lceil' => '⌈', 'lcub' => '{', 'Lcy' => 'Л', 'lcy' => 'л', 'ldca' => '⤶', 'ldquo' => '“', 'ldquor' => '„', 'ldrdhar' => '⥧', 'ldrushar' => '⥋', 'ldsh' => '↲', 'lE' => '≦', 'le' => '≤', 'LeftAngleBracket' => '⟨', 'LeftArrow' => '←', 'Leftarrow' => '⇐', 'leftarrow' => '←', 'LeftArrowBar' => '⇤', 'LeftArrowRightArrow' => '⇆', 'leftarrowtail' => '↢', 'LeftCeiling' => '⌈', 'LeftDoubleBracket' => '⟦', 'LeftDownTeeVector' => '⥡', 'LeftDownVector' => '⇃', 'LeftDownVectorBar' => '⥙', 'LeftFloor' => '⌊', 'leftharpoondown' => '↽', 'leftharpoonup' => '↼', 'leftleftarrows' => '⇇', 'LeftRightArrow' => '↔', 'Leftrightarrow' => '⇔', 'leftrightarrow' => '↔', 'leftrightarrows' => '⇆', 'leftrightharpoons' => '⇋', 'leftrightsquigarrow' => '↭', 'LeftRightVector' => '⥎', 'LeftTee' => '⊣', 'LeftTeeArrow' => '↤', 'LeftTeeVector' => '⥚', 'leftthreetimes' => '⋋', 'LeftTriangle' => '⊲', 'LeftTriangleBar' => '⧏', 'LeftTriangleEqual' => '⊴', 'LeftUpDownVector' => '⥑', 'LeftUpTeeVector' => '⥠', 'LeftUpVector' => '↿', 'LeftUpVectorBar' => '⥘', 'LeftVector' => '↼', 'LeftVectorBar' => '⥒', 'lEg' => '⪋', 'leg' => '⋚', 'leq' => '≤', 'leqq' => '≦', 'leqslant' => '⩽', 'les' => '⩽', 'lescc' => '⪨', 'lesdot' => '⩿', 'lesdoto' => '⪁', 'lesdotor' => '⪃', 'lesg' => '⋚︀', 'lesges' => '⪓', 'lessapprox' => '⪅', 'lessdot' => '⋖', 'lesseqgtr' => '⋚', 'lesseqqgtr' => '⪋', 'LessEqualGreater' => '⋚', 'LessFullEqual' => '≦', 'LessGreater' => '≶', 'lessgtr' => '≶', 'LessLess' => '⪡', 'lesssim' => '≲', 'LessSlantEqual' => '⩽', 'LessTilde' => '≲', 'lfisht' => '⥼', 'lfloor' => '⌊', 'Lfr' => '𝔏', 'lfr' => '𝔩', 'lg' => '≶', 'lgE' => '⪑', 'lHar' => '⥢', 'lhard' => '↽', 'lharu' => '↼', 'lharul' => '⥪', 'lhblk' => '▄', 'LJcy' => 'Љ', 'ljcy' => 'љ', 'Ll' => '⋘', 'll' => '≪', 'llarr' => '⇇', 'llcorner' => '⌞', 'Lleftarrow' => '⇚', 'llhard' => '⥫', 'lltri' => '◺', 'Lmidot' => 'Ŀ', 'lmidot' => 'ŀ', 'lmoust' => '⎰', 'lmoustache' => '⎰', 'lnap' => '⪉', 'lnapprox' => '⪉', 'lnE' => '≨', 'lne' => '⪇', 'lneq' => '⪇', 'lneqq' => '≨', 'lnsim' => '⋦', 'loang' => '⟬', 'loarr' => '⇽', 'lobrk' => '⟦', 'LongLeftArrow' => '⟵', 'Longleftarrow' => '⟸', 'longleftarrow' => '⟵', 'LongLeftRightArrow' => '⟷', 'Longleftrightarrow' => '⟺', 'longleftrightarrow' => '⟷', 'longmapsto' => '⟼', 'LongRightArrow' => '⟶', 'Longrightarrow' => '⟹', 'longrightarrow' => '⟶', 'looparrowleft' => '↫', 'looparrowright' => '↬', 'lopar' => '⦅', 'Lopf' => '𝕃', 'lopf' => '𝕝', 'loplus' => '⨭', 'lotimes' => '⨴', 'lowast' => '∗', 'lowbar' => '_', 'LowerLeftArrow' => '↙', 'LowerRightArrow' => '↘', 'loz' => '◊', 'lozenge' => '◊', 'lozf' => '⧫', 'lpar' => '(', 'lparlt' => '⦓', 'lrarr' => '⇆', 'lrcorner' => '⌟', 'lrhar' => '⇋', 'lrhard' => '⥭', 'lrm' => '', 'lrtri' => '⊿', 'lsaquo' => '‹', 'Lscr' => 'ℒ', 'lscr' => '𝓁', 'Lsh' => '↰', 'lsh' => '↰', 'lsim' => '≲', 'lsime' => '⪍', 'lsimg' => '⪏', 'lsqb' => '[', 'lsquo' => '‘', 'lsquor' => '‚', 'Lstrok' => 'Ł', 'lstrok' => 'ł', 'LT' => '<', 'L' => '<', 'Lt' => '≪', 'lt' => '<', 'l' => '<', 'ltcc' => '⪦', 'ltcir' => '⩹', 'ltdot' => '⋖', 'lthree' => '⋋', 'ltimes' => '⋉', 'ltlarr' => '⥶', 'ltquest' => '⩻', 'ltri' => '◃', 'ltrie' => '⊴', 'ltrif' => '◂', 'ltrPar' => '⦖', 'lurdshar' => '⥊', 'luruhar' => '⥦', 'lvertneqq' => '≨︀', 'lvnE' => '≨︀', 'macr' => '¯', 'mac' => '¯', 'male' => '♂', 'malt' => '✠', 'maltese' => '✠', 'Map' => '⤅', 'map' => '↦', 'mapsto' => '↦', 'mapstodown' => '↧', 'mapstoleft' => '↤', 'mapstoup' => '↥', 'marker' => '▮', 'mcomma' => '⨩', 'Mcy' => 'М', 'mcy' => 'м', 'mdash' => '—', 'mDDot' => '∺', 'measuredangle' => '∡', 'MediumSpace' => ' ', 'Mellintrf' => 'ℳ', 'Mfr' => '𝔐', 'mfr' => '𝔪', 'mho' => '℧', 'micro' => 'µ', 'micr' => 'µ', 'mid' => '∣', 'midast' => '*', 'midcir' => '⫰', 'middot' => '·', 'middo' => '·', 'minus' => '−', 'minusb' => '⊟', 'minusd' => '∸', 'minusdu' => '⨪', 'MinusPlus' => '∓', 'mlcp' => '⫛', 'mldr' => '…', 'mnplus' => '∓', 'models' => '⊧', 'Mopf' => '𝕄', 'mopf' => '𝕞', 'mp' => '∓', 'Mscr' => 'ℳ', 'mscr' => '𝓂', 'mstpos' => '∾', 'Mu' => 'Μ', 'mu' => 'μ', 'multimap' => '⊸', 'mumap' => '⊸', 'nabla' => '∇', 'Nacute' => 'Ń', 'nacute' => 'ń', 'nang' => '∠⃒', 'nap' => '≉', 'napE' => '⩰̸', 'napid' => '≋̸', 'napos' => 'ʼn', 'napprox' => '≉', 'natur' => '♮', 'natural' => '♮', 'naturals' => 'ℕ', 'nbsp' => ' ', 'nbs' => ' ', 'nbump' => '≎̸', 'nbumpe' => '≏̸', 'ncap' => '⩃', 'Ncaron' => 'Ň', 'ncaron' => 'ň', 'Ncedil' => 'Ņ', 'ncedil' => 'ņ', 'ncong' => '≇', 'ncongdot' => '⩭̸', 'ncup' => '⩂', 'Ncy' => 'Н', 'ncy' => 'н', 'ndash' => '–', 'ne' => '≠', 'nearhk' => '⤤', 'neArr' => '⇗', 'nearr' => '↗', 'nearrow' => '↗', 'nedot' => '≐̸', 'NegativeMediumSpace' => '', 'NegativeThickSpace' => '', 'NegativeThinSpace' => '', 'NegativeVeryThinSpace' => '', 'nequiv' => '≢', 'nesear' => '⤨', 'nesim' => '≂̸', 'NestedGreaterGreater' => '≫', 'NestedLessLess' => '≪', 'NewLine' => '
', 'nexist' => '∄', 'nexists' => '∄', 'Nfr' => '𝔑', 'nfr' => '𝔫', 'ngE' => '≧̸', 'nge' => '≱', 'ngeq' => '≱', 'ngeqq' => '≧̸', 'ngeqslant' => '⩾̸', 'nges' => '⩾̸', 'nGg' => '⋙̸', 'ngsim' => '≵', 'nGt' => '≫⃒', 'ngt' => '≯', 'ngtr' => '≯', 'nGtv' => '≫̸', 'nhArr' => '⇎', 'nharr' => '↮', 'nhpar' => '⫲', 'ni' => '∋', 'nis' => '⋼', 'nisd' => '⋺', 'niv' => '∋', 'NJcy' => 'Њ', 'njcy' => 'њ', 'nlArr' => '⇍', 'nlarr' => '↚', 'nldr' => '‥', 'nlE' => '≦̸', 'nle' => '≰', 'nLeftarrow' => '⇍', 'nleftarrow' => '↚', 'nLeftrightarrow' => '⇎', 'nleftrightarrow' => '↮', 'nleq' => '≰', 'nleqq' => '≦̸', 'nleqslant' => '⩽̸', 'nles' => '⩽̸', 'nless' => '≮', 'nLl' => '⋘̸', 'nlsim' => '≴', 'nLt' => '≪⃒', 'nlt' => '≮', 'nltri' => '⋪', 'nltrie' => '⋬', 'nLtv' => '≪̸', 'nmid' => '∤', 'NoBreak' => '', 'NonBreakingSpace' => ' ', 'Nopf' => 'ℕ', 'nopf' => '𝕟', 'Not' => '⫬', 'not' => '¬', 'no' => '¬', 'NotCongruent' => '≢', 'NotCupCap' => '≭', 'NotDoubleVerticalBar' => '∦', 'NotElement' => '∉', 'NotEqual' => '≠', 'NotEqualTilde' => '≂̸', 'NotExists' => '∄', 'NotGreater' => '≯', 'NotGreaterEqual' => '≱', 'NotGreaterFullEqual' => '≧̸', 'NotGreaterGreater' => '≫̸', 'NotGreaterLess' => '≹', 'NotGreaterSlantEqual' => '⩾̸', 'NotGreaterTilde' => '≵', 'NotHumpDownHump' => '≎̸', 'NotHumpEqual' => '≏̸', 'notin' => '∉', 'notindot' => '⋵̸', 'notinE' => '⋹̸', 'notinva' => '∉', 'notinvb' => '⋷', 'notinvc' => '⋶', 'NotLeftTriangle' => '⋪', 'NotLeftTriangleBar' => '⧏̸', 'NotLeftTriangleEqual' => '⋬', 'NotLess' => '≮', 'NotLessEqual' => '≰', 'NotLessGreater' => '≸', 'NotLessLess' => '≪̸', 'NotLessSlantEqual' => '⩽̸', 'NotLessTilde' => '≴', 'NotNestedGreaterGreater' => '⪢̸', 'NotNestedLessLess' => '⪡̸', 'notni' => '∌', 'notniva' => '∌', 'notnivb' => '⋾', 'notnivc' => '⋽', 'NotPrecedes' => '⊀', 'NotPrecedesEqual' => '⪯̸', 'NotPrecedesSlantEqual' => '⋠', 'NotReverseElement' => '∌', 'NotRightTriangle' => '⋫', 'NotRightTriangleBar' => '⧐̸', 'NotRightTriangleEqual' => '⋭', 'NotSquareSubset' => '⊏̸', 'NotSquareSubsetEqual' => '⋢', 'NotSquareSuperset' => '⊐̸', 'NotSquareSupersetEqual' => '⋣', 'NotSubset' => '⊂⃒', 'NotSubsetEqual' => '⊈', 'NotSucceeds' => '⊁', 'NotSucceedsEqual' => '⪰̸', 'NotSucceedsSlantEqual' => '⋡', 'NotSucceedsTilde' => '≿̸', 'NotSuperset' => '⊃⃒', 'NotSupersetEqual' => '⊉', 'NotTilde' => '≁', 'NotTildeEqual' => '≄', 'NotTildeFullEqual' => '≇', 'NotTildeTilde' => '≉', 'NotVerticalBar' => '∤', 'npar' => '∦', 'nparallel' => '∦', 'nparsl' => '⫽⃥', 'npart' => '∂̸', 'npolint' => '⨔', 'npr' => '⊀', 'nprcue' => '⋠', 'npre' => '⪯̸', 'nprec' => '⊀', 'npreceq' => '⪯̸', 'nrArr' => '⇏', 'nrarr' => '↛', 'nrarrc' => '⤳̸', 'nrarrw' => '↝̸', 'nRightarrow' => '⇏', 'nrightarrow' => '↛', 'nrtri' => '⋫', 'nrtrie' => '⋭', 'nsc' => '⊁', 'nsccue' => '⋡', 'nsce' => '⪰̸', 'Nscr' => '𝒩', 'nscr' => '𝓃', 'nshortmid' => '∤', 'nshortparallel' => '∦', 'nsim' => '≁', 'nsime' => '≄', 'nsimeq' => '≄', 'nsmid' => '∤', 'nspar' => '∦', 'nsqsube' => '⋢', 'nsqsupe' => '⋣', 'nsub' => '⊄', 'nsubE' => '⫅̸', 'nsube' => '⊈', 'nsubset' => '⊂⃒', 'nsubseteq' => '⊈', 'nsubseteqq' => '⫅̸', 'nsucc' => '⊁', 'nsucceq' => '⪰̸', 'nsup' => '⊅', 'nsupE' => '⫆̸', 'nsupe' => '⊉', 'nsupset' => '⊃⃒', 'nsupseteq' => '⊉', 'nsupseteqq' => '⫆̸', 'ntgl' => '≹', 'Ntilde' => 'Ñ', 'Ntild' => 'Ñ', 'ntilde' => 'ñ', 'ntild' => 'ñ', 'ntlg' => '≸', 'ntriangleleft' => '⋪', 'ntrianglelefteq' => '⋬', 'ntriangleright' => '⋫', 'ntrianglerighteq' => '⋭', 'Nu' => 'Ν', 'nu' => 'ν', 'num' => '#', 'numero' => '№', 'numsp' => ' ', 'nvap' => '≍⃒', 'nVDash' => '⊯', 'nVdash' => '⊮', 'nvDash' => '⊭', 'nvdash' => '⊬', 'nvge' => '≥⃒', 'nvgt' => '>⃒', 'nvHarr' => '⤄', 'nvinfin' => '⧞', 'nvlArr' => '⤂', 'nvle' => '≤⃒', 'nvlt' => '<⃒', 'nvltrie' => '⊴⃒', 'nvrArr' => '⤃', 'nvrtrie' => '⊵⃒', 'nvsim' => '∼⃒', 'nwarhk' => '⤣', 'nwArr' => '⇖', 'nwarr' => '↖', 'nwarrow' => '↖', 'nwnear' => '⤧', 'Oacute' => 'Ó', 'Oacut' => 'Ó', 'oacute' => 'ó', 'oacut' => 'ó', 'oast' => '⊛', 'ocir' => 'ô', 'Ocirc' => 'Ô', 'Ocir' => 'Ô', 'ocirc' => 'ô', 'Ocy' => 'О', 'ocy' => 'о', 'odash' => '⊝', 'Odblac' => 'Ő', 'odblac' => 'ő', 'odiv' => '⨸', 'odot' => '⊙', 'odsold' => '⦼', 'OElig' => 'Œ', 'oelig' => 'œ', 'ofcir' => '⦿', 'Ofr' => '𝔒', 'ofr' => '𝔬', 'ogon' => '˛', 'Ograve' => 'Ò', 'Ograv' => 'Ò', 'ograve' => 'ò', 'ograv' => 'ò', 'ogt' => '⧁', 'ohbar' => '⦵', 'ohm' => 'Ω', 'oint' => '∮', 'olarr' => '↺', 'olcir' => '⦾', 'olcross' => '⦻', 'oline' => '‾', 'olt' => '⧀', 'Omacr' => 'Ō', 'omacr' => 'ō', 'Omega' => 'Ω', 'omega' => 'ω', 'Omicron' => 'Ο', 'omicron' => 'ο', 'omid' => '⦶', 'ominus' => '⊖', 'Oopf' => '𝕆', 'oopf' => '𝕠', 'opar' => '⦷', 'OpenCurlyDoubleQuote' => '“', 'OpenCurlyQuote' => '‘', 'operp' => '⦹', 'oplus' => '⊕', 'Or' => '⩔', 'or' => '∨', 'orarr' => '↻', 'ord' => 'º', 'order' => 'ℴ', 'orderof' => 'ℴ', 'ordf' => 'ª', 'ordm' => 'º', 'origof' => '⊶', 'oror' => '⩖', 'orslope' => '⩗', 'orv' => '⩛', 'oS' => 'Ⓢ', 'Oscr' => '𝒪', 'oscr' => 'ℴ', 'Oslash' => 'Ø', 'Oslas' => 'Ø', 'oslash' => 'ø', 'oslas' => 'ø', 'osol' => '⊘', 'Otilde' => 'Õ', 'Otild' => 'Õ', 'otilde' => 'õ', 'otild' => 'õ', 'Otimes' => '⨷', 'otimes' => '⊗', 'otimesas' => '⨶', 'Ouml' => 'Ö', 'Oum' => 'Ö', 'ouml' => 'ö', 'oum' => 'ö', 'ovbar' => '⌽', 'OverBar' => '‾', 'OverBrace' => '⏞', 'OverBracket' => '⎴', 'OverParenthesis' => '⏜', 'par' => '¶', 'para' => '¶', 'parallel' => '∥', 'parsim' => '⫳', 'parsl' => '⫽', 'part' => '∂', 'PartialD' => '∂', 'Pcy' => 'П', 'pcy' => 'п', 'percnt' => '%', 'period' => '.', 'permil' => '‰', 'perp' => '⊥', 'pertenk' => '‱', 'Pfr' => '𝔓', 'pfr' => '𝔭', 'Phi' => 'Φ', 'phi' => 'φ', 'phiv' => 'ϕ', 'phmmat' => 'ℳ', 'phone' => '☎', 'Pi' => 'Π', 'pi' => 'π', 'pitchfork' => '⋔', 'piv' => 'ϖ', 'planck' => 'ℏ', 'planckh' => 'ℎ', 'plankv' => 'ℏ', 'plus' => '+', 'plusacir' => '⨣', 'plusb' => '⊞', 'pluscir' => '⨢', 'plusdo' => '∔', 'plusdu' => '⨥', 'pluse' => '⩲', 'PlusMinus' => '±', 'plusmn' => '±', 'plusm' => '±', 'plussim' => '⨦', 'plustwo' => '⨧', 'pm' => '±', 'Poincareplane' => 'ℌ', 'pointint' => '⨕', 'Popf' => 'ℙ', 'popf' => '𝕡', 'pound' => '£', 'poun' => '£', 'Pr' => '⪻', 'pr' => '≺', 'prap' => '⪷', 'prcue' => '≼', 'prE' => '⪳', 'pre' => '⪯', 'prec' => '≺', 'precapprox' => '⪷', 'preccurlyeq' => '≼', 'Precedes' => '≺', 'PrecedesEqual' => '⪯', 'PrecedesSlantEqual' => '≼', 'PrecedesTilde' => '≾', 'preceq' => '⪯', 'precnapprox' => '⪹', 'precneqq' => '⪵', 'precnsim' => '⋨', 'precsim' => '≾', 'Prime' => '″', 'prime' => '′', 'primes' => 'ℙ', 'prnap' => '⪹', 'prnE' => '⪵', 'prnsim' => '⋨', 'prod' => '∏', 'Product' => '∏', 'profalar' => '⌮', 'profline' => '⌒', 'profsurf' => '⌓', 'prop' => '∝', 'Proportion' => '∷', 'Proportional' => '∝', 'propto' => '∝', 'prsim' => '≾', 'prurel' => '⊰', 'Pscr' => '𝒫', 'pscr' => '𝓅', 'Psi' => 'Ψ', 'psi' => 'ψ', 'puncsp' => ' ', 'Qfr' => '𝔔', 'qfr' => '𝔮', 'qint' => '⨌', 'Qopf' => 'ℚ', 'qopf' => '𝕢', 'qprime' => '⁗', 'Qscr' => '𝒬', 'qscr' => '𝓆', 'quaternions' => 'ℍ', 'quatint' => '⨖', 'quest' => '?', 'questeq' => '≟', 'QUOT' => '"', 'QUO' => '"', 'quot' => '"', 'quo' => '"', 'rAarr' => '⇛', 'race' => '∽̱', 'Racute' => 'Ŕ', 'racute' => 'ŕ', 'radic' => '√', 'raemptyv' => '⦳', 'Rang' => '⟫', 'rang' => '⟩', 'rangd' => '⦒', 'range' => '⦥', 'rangle' => '⟩', 'raquo' => '»', 'raqu' => '»', 'Rarr' => '↠', 'rArr' => '⇒', 'rarr' => '→', 'rarrap' => '⥵', 'rarrb' => '⇥', 'rarrbfs' => '⤠', 'rarrc' => '⤳', 'rarrfs' => '⤞', 'rarrhk' => '↪', 'rarrlp' => '↬', 'rarrpl' => '⥅', 'rarrsim' => '⥴', 'Rarrtl' => '⤖', 'rarrtl' => '↣', 'rarrw' => '↝', 'rAtail' => '⤜', 'ratail' => '⤚', 'ratio' => '∶', 'rationals' => 'ℚ', 'RBarr' => '⤐', 'rBarr' => '⤏', 'rbarr' => '⤍', 'rbbrk' => '❳', 'rbrace' => '}', 'rbrack' => ']', 'rbrke' => '⦌', 'rbrksld' => '⦎', 'rbrkslu' => '⦐', 'Rcaron' => 'Ř', 'rcaron' => 'ř', 'Rcedil' => 'Ŗ', 'rcedil' => 'ŗ', 'rceil' => '⌉', 'rcub' => '}', 'Rcy' => 'Р', 'rcy' => 'р', 'rdca' => '⤷', 'rdldhar' => '⥩', 'rdquo' => '”', 'rdquor' => '”', 'rdsh' => '↳', 'Re' => 'ℜ', 'real' => 'ℜ', 'realine' => 'ℛ', 'realpart' => 'ℜ', 'reals' => 'ℝ', 'rect' => '▭', 'REG' => '®', 'RE' => '®', 'reg' => '®', 're' => '®', 'ReverseElement' => '∋', 'ReverseEquilibrium' => '⇋', 'ReverseUpEquilibrium' => '⥯', 'rfisht' => '⥽', 'rfloor' => '⌋', 'Rfr' => 'ℜ', 'rfr' => '𝔯', 'rHar' => '⥤', 'rhard' => '⇁', 'rharu' => '⇀', 'rharul' => '⥬', 'Rho' => 'Ρ', 'rho' => 'ρ', 'rhov' => 'ϱ', 'RightAngleBracket' => '⟩', 'RightArrow' => '→', 'Rightarrow' => '⇒', 'rightarrow' => '→', 'RightArrowBar' => '⇥', 'RightArrowLeftArrow' => '⇄', 'rightarrowtail' => '↣', 'RightCeiling' => '⌉', 'RightDoubleBracket' => '⟧', 'RightDownTeeVector' => '⥝', 'RightDownVector' => '⇂', 'RightDownVectorBar' => '⥕', 'RightFloor' => '⌋', 'rightharpoondown' => '⇁', 'rightharpoonup' => '⇀', 'rightleftarrows' => '⇄', 'rightleftharpoons' => '⇌', 'rightrightarrows' => '⇉', 'rightsquigarrow' => '↝', 'RightTee' => '⊢', 'RightTeeArrow' => '↦', 'RightTeeVector' => '⥛', 'rightthreetimes' => '⋌', 'RightTriangle' => '⊳', 'RightTriangleBar' => '⧐', 'RightTriangleEqual' => '⊵', 'RightUpDownVector' => '⥏', 'RightUpTeeVector' => '⥜', 'RightUpVector' => '↾', 'RightUpVectorBar' => '⥔', 'RightVector' => '⇀', 'RightVectorBar' => '⥓', 'ring' => '˚', 'risingdotseq' => '≓', 'rlarr' => '⇄', 'rlhar' => '⇌', 'rlm' => '', 'rmoust' => '⎱', 'rmoustache' => '⎱', 'rnmid' => '⫮', 'roang' => '⟭', 'roarr' => '⇾', 'robrk' => '⟧', 'ropar' => '⦆', 'Ropf' => 'ℝ', 'ropf' => '𝕣', 'roplus' => '⨮', 'rotimes' => '⨵', 'RoundImplies' => '⥰', 'rpar' => ')', 'rpargt' => '⦔', 'rppolint' => '⨒', 'rrarr' => '⇉', 'Rrightarrow' => '⇛', 'rsaquo' => '›', 'Rscr' => 'ℛ', 'rscr' => '𝓇', 'Rsh' => '↱', 'rsh' => '↱', 'rsqb' => ']', 'rsquo' => '’', 'rsquor' => '’', 'rthree' => '⋌', 'rtimes' => '⋊', 'rtri' => '▹', 'rtrie' => '⊵', 'rtrif' => '▸', 'rtriltri' => '⧎', 'RuleDelayed' => '⧴', 'ruluhar' => '⥨', 'rx' => '℞', 'Sacute' => 'Ś', 'sacute' => 'ś', 'sbquo' => '‚', 'Sc' => '⪼', 'sc' => '≻', 'scap' => '⪸', 'Scaron' => 'Š', 'scaron' => 'š', 'sccue' => '≽', 'scE' => '⪴', 'sce' => '⪰', 'Scedil' => 'Ş', 'scedil' => 'ş', 'Scirc' => 'Ŝ', 'scirc' => 'ŝ', 'scnap' => '⪺', 'scnE' => '⪶', 'scnsim' => '⋩', 'scpolint' => '⨓', 'scsim' => '≿', 'Scy' => 'С', 'scy' => 'с', 'sdot' => '⋅', 'sdotb' => '⊡', 'sdote' => '⩦', 'searhk' => '⤥', 'seArr' => '⇘', 'searr' => '↘', 'searrow' => '↘', 'sect' => '§', 'sec' => '§', 'semi' => ';', 'seswar' => '⤩', 'setminus' => '∖', 'setmn' => '∖', 'sext' => '✶', 'Sfr' => '𝔖', 'sfr' => '𝔰', 'sfrown' => '⌢', 'sharp' => '♯', 'SHCHcy' => 'Щ', 'shchcy' => 'щ', 'SHcy' => 'Ш', 'shcy' => 'ш', 'ShortDownArrow' => '↓', 'ShortLeftArrow' => '←', 'shortmid' => '∣', 'shortparallel' => '∥', 'ShortRightArrow' => '→', 'ShortUpArrow' => '↑', 'shy' => '', 'sh' => '', 'Sigma' => 'Σ', 'sigma' => 'σ', 'sigmaf' => 'ς', 'sigmav' => 'ς', 'sim' => '∼', 'simdot' => '⩪', 'sime' => '≃', 'simeq' => '≃', 'simg' => '⪞', 'simgE' => '⪠', 'siml' => '⪝', 'simlE' => '⪟', 'simne' => '≆', 'simplus' => '⨤', 'simrarr' => '⥲', 'slarr' => '←', 'SmallCircle' => '∘', 'smallsetminus' => '∖', 'smashp' => '⨳', 'smeparsl' => '⧤', 'smid' => '∣', 'smile' => '⌣', 'smt' => '⪪', 'smte' => '⪬', 'smtes' => '⪬︀', 'SOFTcy' => 'Ь', 'softcy' => 'ь', 'sol' => '/', 'solb' => '⧄', 'solbar' => '⌿', 'Sopf' => '𝕊', 'sopf' => '𝕤', 'spades' => '♠', 'spadesuit' => '♠', 'spar' => '∥', 'sqcap' => '⊓', 'sqcaps' => '⊓︀', 'sqcup' => '⊔', 'sqcups' => '⊔︀', 'Sqrt' => '√', 'sqsub' => '⊏', 'sqsube' => '⊑', 'sqsubset' => '⊏', 'sqsubseteq' => '⊑', 'sqsup' => '⊐', 'sqsupe' => '⊒', 'sqsupset' => '⊐', 'sqsupseteq' => '⊒', 'squ' => '□', 'Square' => '□', 'square' => '□', 'SquareIntersection' => '⊓', 'SquareSubset' => '⊏', 'SquareSubsetEqual' => '⊑', 'SquareSuperset' => '⊐', 'SquareSupersetEqual' => '⊒', 'SquareUnion' => '⊔', 'squarf' => '▪', 'squf' => '▪', 'srarr' => '→', 'Sscr' => '𝒮', 'sscr' => '𝓈', 'ssetmn' => '∖', 'ssmile' => '⌣', 'sstarf' => '⋆', 'Star' => '⋆', 'star' => '☆', 'starf' => '★', 'straightepsilon' => 'ϵ', 'straightphi' => 'ϕ', 'strns' => '¯', 'Sub' => '⋐', 'sub' => '⊂', 'subdot' => '⪽', 'subE' => '⫅', 'sube' => '⊆', 'subedot' => '⫃', 'submult' => '⫁', 'subnE' => '⫋', 'subne' => '⊊', 'subplus' => '⪿', 'subrarr' => '⥹', 'Subset' => '⋐', 'subset' => '⊂', 'subseteq' => '⊆', 'subseteqq' => '⫅', 'SubsetEqual' => '⊆', 'subsetneq' => '⊊', 'subsetneqq' => '⫋', 'subsim' => '⫇', 'subsub' => '⫕', 'subsup' => '⫓', 'succ' => '≻', 'succapprox' => '⪸', 'succcurlyeq' => '≽', 'Succeeds' => '≻', 'SucceedsEqual' => '⪰', 'SucceedsSlantEqual' => '≽', 'SucceedsTilde' => '≿', 'succeq' => '⪰', 'succnapprox' => '⪺', 'succneqq' => '⪶', 'succnsim' => '⋩', 'succsim' => '≿', 'SuchThat' => '∋', 'Sum' => '∑', 'sum' => '∑', 'sung' => '♪', 'Sup' => '⋑', 'sup' => '³', 'sup1' => '¹', 'sup2' => '²', 'sup3' => '³', 'supdot' => '⪾', 'supdsub' => '⫘', 'supE' => '⫆', 'supe' => '⊇', 'supedot' => '⫄', 'Superset' => '⊃', 'SupersetEqual' => '⊇', 'suphsol' => '⟉', 'suphsub' => '⫗', 'suplarr' => '⥻', 'supmult' => '⫂', 'supnE' => '⫌', 'supne' => '⊋', 'supplus' => '⫀', 'Supset' => '⋑', 'supset' => '⊃', 'supseteq' => '⊇', 'supseteqq' => '⫆', 'supsetneq' => '⊋', 'supsetneqq' => '⫌', 'supsim' => '⫈', 'supsub' => '⫔', 'supsup' => '⫖', 'swarhk' => '⤦', 'swArr' => '⇙', 'swarr' => '↙', 'swarrow' => '↙', 'swnwar' => '⤪', 'szlig' => 'ß', 'szli' => 'ß', 'Tab' => ' ', 'target' => '⌖', 'Tau' => 'Τ', 'tau' => 'τ', 'tbrk' => '⎴', 'Tcaron' => 'Ť', 'tcaron' => 'ť', 'Tcedil' => 'Ţ', 'tcedil' => 'ţ', 'Tcy' => 'Т', 'tcy' => 'т', 'tdot' => '⃛', 'telrec' => '⌕', 'Tfr' => '𝔗', 'tfr' => '𝔱', 'there4' => '∴', 'Therefore' => '∴', 'therefore' => '∴', 'Theta' => 'Θ', 'theta' => 'θ', 'thetasym' => 'ϑ', 'thetav' => 'ϑ', 'thickapprox' => '≈', 'thicksim' => '∼', 'ThickSpace' => ' ', 'thinsp' => ' ', 'ThinSpace' => ' ', 'thkap' => '≈', 'thksim' => '∼', 'THORN' => 'Þ', 'THOR' => 'Þ', 'thorn' => 'þ', 'thor' => 'þ', 'Tilde' => '∼', 'tilde' => '˜', 'TildeEqual' => '≃', 'TildeFullEqual' => '≅', 'TildeTilde' => '≈', 'times' => '×', 'time' => '×', 'timesb' => '⊠', 'timesbar' => '⨱', 'timesd' => '⨰', 'tint' => '∭', 'toea' => '⤨', 'top' => '⊤', 'topbot' => '⌶', 'topcir' => '⫱', 'Topf' => '𝕋', 'topf' => '𝕥', 'topfork' => '⫚', 'tosa' => '⤩', 'tprime' => '‴', 'TRADE' => '™', 'trade' => '™', 'triangle' => '▵', 'triangledown' => '▿', 'triangleleft' => '◃', 'trianglelefteq' => '⊴', 'triangleq' => '≜', 'triangleright' => '▹', 'trianglerighteq' => '⊵', 'tridot' => '◬', 'trie' => '≜', 'triminus' => '⨺', 'TripleDot' => '⃛', 'triplus' => '⨹', 'trisb' => '⧍', 'tritime' => '⨻', 'trpezium' => '⏢', 'Tscr' => '𝒯', 'tscr' => '𝓉', 'TScy' => 'Ц', 'tscy' => 'ц', 'TSHcy' => 'Ћ', 'tshcy' => 'ћ', 'Tstrok' => 'Ŧ', 'tstrok' => 'ŧ', 'twixt' => '≬', 'twoheadleftarrow' => '↞', 'twoheadrightarrow' => '↠', 'Uacute' => 'Ú', 'Uacut' => 'Ú', 'uacute' => 'ú', 'uacut' => 'ú', 'Uarr' => '↟', 'uArr' => '⇑', 'uarr' => '↑', 'Uarrocir' => '⥉', 'Ubrcy' => 'Ў', 'ubrcy' => 'ў', 'Ubreve' => 'Ŭ', 'ubreve' => 'ŭ', 'Ucirc' => 'Û', 'Ucir' => 'Û', 'ucirc' => 'û', 'ucir' => 'û', 'Ucy' => 'У', 'ucy' => 'у', 'udarr' => '⇅', 'Udblac' => 'Ű', 'udblac' => 'ű', 'udhar' => '⥮', 'ufisht' => '⥾', 'Ufr' => '𝔘', 'ufr' => '𝔲', 'Ugrave' => 'Ù', 'Ugrav' => 'Ù', 'ugrave' => 'ù', 'ugrav' => 'ù', 'uHar' => '⥣', 'uharl' => '↿', 'uharr' => '↾', 'uhblk' => '▀', 'ulcorn' => '⌜', 'ulcorner' => '⌜', 'ulcrop' => '⌏', 'ultri' => '◸', 'Umacr' => 'Ū', 'umacr' => 'ū', 'uml' => '¨', 'um' => '¨', 'UnderBar' => '_', 'UnderBrace' => '⏟', 'UnderBracket' => '⎵', 'UnderParenthesis' => '⏝', 'Union' => '⋃', 'UnionPlus' => '⊎', 'Uogon' => 'Ų', 'uogon' => 'ų', 'Uopf' => '𝕌', 'uopf' => '𝕦', 'UpArrow' => '↑', 'Uparrow' => '⇑', 'uparrow' => '↑', 'UpArrowBar' => '⤒', 'UpArrowDownArrow' => '⇅', 'UpDownArrow' => '↕', 'Updownarrow' => '⇕', 'updownarrow' => '↕', 'UpEquilibrium' => '⥮', 'upharpoonleft' => '↿', 'upharpoonright' => '↾', 'uplus' => '⊎', 'UpperLeftArrow' => '↖', 'UpperRightArrow' => '↗', 'Upsi' => 'ϒ', 'upsi' => 'υ', 'upsih' => 'ϒ', 'Upsilon' => 'Υ', 'upsilon' => 'υ', 'UpTee' => '⊥', 'UpTeeArrow' => '↥', 'upuparrows' => '⇈', 'urcorn' => '⌝', 'urcorner' => '⌝', 'urcrop' => '⌎', 'Uring' => 'Ů', 'uring' => 'ů', 'urtri' => '◹', 'Uscr' => '𝒰', 'uscr' => '𝓊', 'utdot' => '⋰', 'Utilde' => 'Ũ', 'utilde' => 'ũ', 'utri' => '▵', 'utrif' => '▴', 'uuarr' => '⇈', 'Uuml' => 'Ü', 'Uum' => 'Ü', 'uuml' => 'ü', 'uum' => 'ü', 'uwangle' => '⦧', 'vangrt' => '⦜', 'varepsilon' => 'ϵ', 'varkappa' => 'ϰ', 'varnothing' => '∅', 'varphi' => 'ϕ', 'varpi' => 'ϖ', 'varpropto' => '∝', 'vArr' => '⇕', 'varr' => '↕', 'varrho' => 'ϱ', 'varsigma' => 'ς', 'varsubsetneq' => '⊊︀', 'varsubsetneqq' => '⫋︀', 'varsupsetneq' => '⊋︀', 'varsupsetneqq' => '⫌︀', 'vartheta' => 'ϑ', 'vartriangleleft' => '⊲', 'vartriangleright' => '⊳', 'Vbar' => '⫫', 'vBar' => '⫨', 'vBarv' => '⫩', 'Vcy' => 'В', 'vcy' => 'в', 'VDash' => '⊫', 'Vdash' => '⊩', 'vDash' => '⊨', 'vdash' => '⊢', 'Vdashl' => '⫦', 'Vee' => '⋁', 'vee' => '∨', 'veebar' => '⊻', 'veeeq' => '≚', 'vellip' => '⋮', 'Verbar' => '‖', 'verbar' => '|', 'Vert' => '‖', 'vert' => '|', 'VerticalBar' => '∣', 'VerticalLine' => '|', 'VerticalSeparator' => '❘', 'VerticalTilde' => '≀', 'VeryThinSpace' => ' ', 'Vfr' => '𝔙', 'vfr' => '𝔳', 'vltri' => '⊲', 'vnsub' => '⊂⃒', 'vnsup' => '⊃⃒', 'Vopf' => '𝕍', 'vopf' => '𝕧', 'vprop' => '∝', 'vrtri' => '⊳', 'Vscr' => '𝒱', 'vscr' => '𝓋', 'vsubnE' => '⫋︀', 'vsubne' => '⊊︀', 'vsupnE' => '⫌︀', 'vsupne' => '⊋︀', 'Vvdash' => '⊪', 'vzigzag' => '⦚', 'Wcirc' => 'Ŵ', 'wcirc' => 'ŵ', 'wedbar' => '⩟', 'Wedge' => '⋀', 'wedge' => '∧', 'wedgeq' => '≙', 'weierp' => '℘', 'Wfr' => '𝔚', 'wfr' => '𝔴', 'Wopf' => '𝕎', 'wopf' => '𝕨', 'wp' => '℘', 'wr' => '≀', 'wreath' => '≀', 'Wscr' => '𝒲', 'wscr' => '𝓌', 'xcap' => '⋂', 'xcirc' => '◯', 'xcup' => '⋃', 'xdtri' => '▽', 'Xfr' => '𝔛', 'xfr' => '𝔵', 'xhArr' => '⟺', 'xharr' => '⟷', 'Xi' => 'Ξ', 'xi' => 'ξ', 'xlArr' => '⟸', 'xlarr' => '⟵', 'xmap' => '⟼', 'xnis' => '⋻', 'xodot' => '⨀', 'Xopf' => '𝕏', 'xopf' => '𝕩', 'xoplus' => '⨁', 'xotime' => '⨂', 'xrArr' => '⟹', 'xrarr' => '⟶', 'Xscr' => '𝒳', 'xscr' => '𝓍', 'xsqcup' => '⨆', 'xuplus' => '⨄', 'xutri' => '△', 'xvee' => '⋁', 'xwedge' => '⋀', 'Yacute' => 'Ý', 'Yacut' => 'Ý', 'yacute' => 'ý', 'yacut' => 'ý', 'YAcy' => 'Я', 'yacy' => 'я', 'Ycirc' => 'Ŷ', 'ycirc' => 'ŷ', 'Ycy' => 'Ы', 'ycy' => 'ы', 'yen' => '¥', 'ye' => '¥', 'Yfr' => '𝔜', 'yfr' => '𝔶', 'YIcy' => 'Ї', 'yicy' => 'ї', 'Yopf' => '𝕐', 'yopf' => '𝕪', 'Yscr' => '𝒴', 'yscr' => '𝓎', 'YUcy' => 'Ю', 'yucy' => 'ю', 'Yuml' => 'Ÿ', 'yuml' => 'ÿ', 'yum' => 'ÿ', 'Zacute' => 'Ź', 'zacute' => 'ź', 'Zcaron' => 'Ž', 'zcaron' => 'ž', 'Zcy' => 'З', 'zcy' => 'з', 'Zdot' => 'Ż', 'zdot' => 'ż', 'zeetrf' => 'ℨ', 'ZeroWidthSpace' => '', 'Zeta' => 'Ζ', 'zeta' => 'ζ', 'Zfr' => 'ℨ', 'zfr' => '𝔷', 'ZHcy' => 'Ж', 'zhcy' => 'ж', 'zigrarr' => '⇝', 'Zopf' => 'ℤ', 'zopf' => '𝕫', 'Zscr' => '𝒵', 'zscr' => '𝓏', 'zwj' => '', 'zwnj' => '', ); } namespace AimySpeedOptimization\Masterminds\HTML5; class Exception extends \Exception { } namespace AimySpeedOptimization\Masterminds\HTML5; interface InstructionProcessor { public function process(\DOMElement $element, $name, $data); } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; use AimySpeedOptimization\Masterminds\HTML5\Elements; class Tokenizer { protected $scanner; protected $events; protected $tok; protected $text = ''; protected $carryOn = true; protected $textMode = 0; protected $untilTag = null; const CONFORMANT_XML = 'xml'; const CONFORMANT_HTML = 'html'; protected $mode = self::CONFORMANT_HTML; public function __construct($scanner, $eventHandler, $mode = self::CONFORMANT_HTML) { $this->scanner = $scanner; $this->events = $eventHandler; $this->mode = $mode; } public function parse() { do { $this->consumeData(); } while ($this->carryOn); } public function setTextMode($textmode, $untilTag = null) { $this->textMode = $textmode & (Elements::TEXT_RAW | Elements::TEXT_RCDATA); $this->untilTag = $untilTag; } protected function consumeData() { $tok = $this->scanner->current(); if ('&' === $tok) { $ref = $this->decodeCharacterReference(); $this->buffer($ref); $tok = $this->scanner->current(); } if ('<' === $tok) { $this->flushBuffer(); $tok = $this->scanner->next(); if (false === $tok) { $this->parseError('Illegal tag opening'); } elseif ('!' === $tok) { $this->markupDeclaration(); } elseif ('/' === $tok) { $this->endTag(); } elseif ('?' === $tok) { $this->processingInstruction(); } elseif ($this->is_alpha($tok)) { $this->tagName(); } else { $this->parseError('Illegal tag opening'); $this->characterData(); } $tok = $this->scanner->current(); } if (false === $tok) { $this->eof(); } else { switch ($this->textMode) { case Elements::TEXT_RAW: $this->rawText($tok); break; case Elements::TEXT_RCDATA: $this->rcdata($tok); break; default: if ('<' === $tok || '&' === $tok) { break; } if ("\00" === $tok) { $this->parseError('Received null character.'); $this->text .= $tok; $this->scanner->consume(); break; } $this->text .= $this->scanner->charsUntil("<&\0"); } } return $this->carryOn; } protected function characterData() { $tok = $this->scanner->current(); if (false === $tok) { return false; } switch ($this->textMode) { case Elements::TEXT_RAW: return $this->rawText($tok); case Elements::TEXT_RCDATA: return $this->rcdata($tok); default: if ('<' === $tok || '&' === $tok) { return false; } return $this->text($tok); } } protected function text($tok) { if (false === $tok) { return false; } if ("\00" === $tok) { $this->parseError('Received null character.'); } $this->buffer($tok); $this->scanner->consume(); return true; } protected function rawText($tok) { if (is_null($this->untilTag)) { return $this->text($tok); } $sequence = '</' . $this->untilTag . '>'; $txt = $this->readUntilSequence($sequence); $this->events->text($txt); $this->setTextMode(0); return $this->endTag(); } protected function rcdata($tok) { if (is_null($this->untilTag)) { return $this->text($tok); } $sequence = '</' . $this->untilTag; $txt = ''; $caseSensitive = !Elements::isHtml5Element($this->untilTag); while (false !== $tok && !('<' == $tok && ($this->scanner->sequenceMatches($sequence, $caseSensitive)))) { if ('&' == $tok) { $txt .= $this->decodeCharacterReference(); $tok = $this->scanner->current(); } else { $txt .= $tok; $tok = $this->scanner->next(); } } $len = strlen($sequence); $this->scanner->consume($len); $len += $this->scanner->whitespace(); if ('>' !== $this->scanner->current()) { $this->parseError('Unclosed RCDATA end tag'); } $this->scanner->unconsume($len); $this->events->text($txt); $this->setTextMode(0); return $this->endTag(); } protected function eof() { $this->flushBuffer(); $this->events->eof(); $this->carryOn = false; } protected function markupDeclaration() { $tok = $this->scanner->next(); if ('-' == $tok && '-' == $this->scanner->peek()) { $this->scanner->consume(2); return $this->comment(); } elseif ('D' == $tok || 'd' == $tok) { return $this->doctype(); } elseif ('[' == $tok) { return $this->cdataSection(); } $this->parseError('Expected <!--, <![CDATA[, or <!DOCTYPE. Got <!%s', $tok); $this->bogusComment('<!'); return true; } protected function endTag() { if ('/' != $this->scanner->current()) { return false; } $tok = $this->scanner->next(); if (!$this->is_alpha($tok)) { $this->parseError("Expected tag name, got '%s'", $tok); if ("\0" == $tok || false === $tok) { return false; } return $this->bogusComment('</'); } $name = $this->scanner->charsUntil("\n\f \t>"); $name = self::CONFORMANT_XML === $this->mode ? $name : strtolower($name); $this->scanner->whitespace(); $tok = $this->scanner->current(); if ('>' != $tok) { $this->parseError("Expected >, got '%s'", $tok); $this->scanner->charsUntil('>'); } $this->events->endTag($name); $this->scanner->consume(); return true; } protected function tagName() { $name = $this->scanner->charsWhile(':_-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'); $name = self::CONFORMANT_XML === $this->mode ? $name : strtolower($name); $attributes = array(); $selfClose = false; try { do { $this->scanner->whitespace(); $this->attribute($attributes); } while (!$this->isTagEnd($selfClose)); } catch (ParseError $e) { $selfClose = false; } $mode = $this->events->startTag($name, $attributes, $selfClose); if (is_int($mode)) { $this->setTextMode($mode, $name); } $this->scanner->consume(); return true; } protected function isTagEnd(&$selfClose) { $tok = $this->scanner->current(); if ('/' == $tok) { $this->scanner->consume(); $this->scanner->whitespace(); $tok = $this->scanner->current(); if ('>' == $tok) { $selfClose = true; return true; } if (false === $tok) { $this->parseError('Unexpected EOF inside of tag.'); return true; } $this->parseError("Unexpected '%s' inside of a tag.", $tok); return false; } if ('>' == $tok) { return true; } if (false === $tok) { $this->parseError('Unexpected EOF inside of tag.'); return true; } return false; } protected function attribute(&$attributes) { $tok = $this->scanner->current(); if ('/' == $tok || '>' == $tok || false === $tok) { return false; } if ('<' == $tok) { $this->parseError("Unexpected '<' inside of attributes list."); $this->scanner->unconsume(); throw new ParseError('Start tag inside of attribute.'); } $name = strtolower($this->scanner->charsUntil("/>=\n\f\t ")); if (0 == strlen($name)) { $tok = $this->scanner->current(); $this->parseError('Expected an attribute name, got %s.', $tok); $name = $tok; $this->scanner->consume(); } $isValidAttribute = true; if (preg_match("/[\x1-\x2C\\/\x3B-\x40\x5B-\x5E\x60\x7B-\x7F]/u", $name)) { $this->parseError('Unexpected characters in attribute name: %s', $name); $isValidAttribute = false; } elseif (preg_match('/^[0-9.-]/u', $name)) { $this->parseError('Unexpected character at the begining of attribute name: %s', $name); $isValidAttribute = false; } $this->scanner->whitespace(); $val = $this->attributeValue(); if ($isValidAttribute) { $attributes[$name] = $val; } return true; } protected function attributeValue() { if ('=' != $this->scanner->current()) { return null; } $this->scanner->consume(); $this->scanner->whitespace(); $tok = $this->scanner->current(); switch ($tok) { case "\n": case "\f": case ' ': case "\t": return null; case '"': case "'": $this->scanner->consume(); return $this->quotedAttributeValue($tok); case '>': $this->parseError('Expected attribute value, got tag end.'); return null; case '=': case '`': $this->parseError('Expecting quotes, got %s.', $tok); return $this->unquotedAttributeValue(); default: return $this->unquotedAttributeValue(); } } protected function quotedAttributeValue($quote) { $stoplist = "\f" . $quote; $val = ''; while (true) { $tokens = $this->scanner->charsUntil($stoplist . '&'); if (false !== $tokens) { $val .= $tokens; } else { break; } $tok = $this->scanner->current(); if ('&' == $tok) { $val .= $this->decodeCharacterReference(true); continue; } break; } $this->scanner->consume(); return $val; } protected function unquotedAttributeValue() { $val = ''; $tok = $this->scanner->current(); while (false !== $tok) { switch ($tok) { case "\n": case "\f": case ' ': case "\t": case '>': break 2; case '&': $val .= $this->decodeCharacterReference(true); $tok = $this->scanner->current(); break; case "'": case '"': case '<': case '=': case '`': $this->parseError('Unexpected chars in unquoted attribute value %s', $tok); $val .= $tok; $tok = $this->scanner->next(); break; default: $val .= $this->scanner->charsUntil("\t\n\f >&\"'<=`"); $tok = $this->scanner->current(); } } return $val; } protected function bogusComment($leading = '') { $comment = $leading; $tokens = $this->scanner->charsUntil('>'); if (false !== $tokens) { $comment .= $tokens; } $tok = $this->scanner->current(); if (false !== $tok) { $comment .= $tok; } $this->flushBuffer(); $this->events->comment($comment); $this->scanner->consume(); return true; } protected function comment() { $tok = $this->scanner->current(); $comment = ''; if ('>' == $tok) { $this->parseError("Expected comment data, got '>'"); $this->events->comment(''); $this->scanner->consume(); return true; } if ("\0" == $tok) { $tok = UTF8Utils::FFFD; } while (!$this->isCommentEnd()) { $comment .= $tok; $tok = $this->scanner->next(); } $this->events->comment($comment); $this->scanner->consume(); return true; } protected function isCommentEnd() { $tok = $this->scanner->current(); if (false === $tok) { $this->parseError('Unexpected EOF in a comment.'); return true; } if ('-' != $tok || '-' != $this->scanner->peek()) { return false; } $this->scanner->consume(2); if ('>' == $this->scanner->current()) { return true; } if ('!' == $this->scanner->current() && '>' == $this->scanner->peek()) { $this->scanner->consume(); return true; } $this->scanner->unconsume(2); return false; } protected function doctype() { if ($this->scanner->sequenceMatches('DOCTYPE', false)) { $this->scanner->consume(7); } else { $chars = $this->scanner->charsWhile('DOCTYPEdoctype'); $this->parseError('Expected DOCTYPE, got %s', $chars); return $this->bogusComment('<!' . $chars); } $this->scanner->whitespace(); $tok = $this->scanner->current(); if (false === $tok) { $this->events->doctype('html5', EventHandler::DOCTYPE_NONE, '', true); $this->eof(); return true; } if ("\0" === $tok) { $this->parseError('Unexpected null character in DOCTYPE.'); } $stop = " \n\f>"; $doctypeName = $this->scanner->charsUntil($stop); $doctypeName = strtolower(strtr($doctypeName, "\0", UTF8Utils::FFFD)); $tok = $this->scanner->current(); if (false === $tok) { $this->parseError('Unexpected EOF in DOCTYPE declaration.'); $this->events->doctype($doctypeName, EventHandler::DOCTYPE_NONE, null, true); return true; } if ('>' == $tok) { if (0 == strlen($doctypeName)) { $this->parseError('Expected a DOCTYPE name. Got nothing.'); $this->events->doctype($doctypeName, 0, null, true); $this->scanner->consume(); return true; } $this->events->doctype($doctypeName); $this->scanner->consume(); return true; } $this->scanner->whitespace(); $pub = strtoupper($this->scanner->getAsciiAlpha()); $white = $this->scanner->whitespace(); if (('PUBLIC' == $pub || 'SYSTEM' == $pub) && $white > 0) { $type = 'PUBLIC' == $pub ? EventHandler::DOCTYPE_PUBLIC : EventHandler::DOCTYPE_SYSTEM; $id = $this->quotedString("\0>"); if (false === $id) { $this->events->doctype($doctypeName, $type, $pub, false); return true; } if (false === $this->scanner->current()) { $this->parseError('Unexpected EOF in DOCTYPE'); $this->events->doctype($doctypeName, $type, $id, true); return true; } $this->scanner->whitespace(); if ('>' == $this->scanner->current()) { $this->events->doctype($doctypeName, $type, $id, false); $this->scanner->consume(); return true; } $this->scanner->charsUntil('>'); $this->parseError('Malformed DOCTYPE.'); $this->events->doctype($doctypeName, $type, $id, true); $this->scanner->consume(); return true; } $this->scanner->charsUntil('>'); $this->parseError('Expected PUBLIC or SYSTEM. Got %s.', $pub); $this->events->doctype($doctypeName, 0, null, true); $this->scanner->consume(); return true; } protected function quotedString($stopchars) { $tok = $this->scanner->current(); if ('"' == $tok || "'" == $tok) { $this->scanner->consume(); $ret = $this->scanner->charsUntil($tok . $stopchars); if ($this->scanner->current() == $tok) { $this->scanner->consume(); } else { $this->parseError('Expected %s, got %s', $tok, $this->scanner->current()); } return $ret; } return false; } protected function cdataSection() { $cdata = ''; $this->scanner->consume(); $chars = $this->scanner->charsWhile('CDAT'); if ('CDATA' != $chars || '[' != $this->scanner->current()) { $this->parseError('Expected [CDATA[, got %s', $chars); return $this->bogusComment('<![' . $chars); } $tok = $this->scanner->next(); do { if (false === $tok) { $this->parseError('Unexpected EOF inside CDATA.'); $this->bogusComment('<![CDATA[' . $cdata); return true; } $cdata .= $tok; $tok = $this->scanner->next(); } while (!$this->scanner->sequenceMatches(']]>')); $this->scanner->consume(3); $this->events->cdata($cdata); return true; } protected function processingInstruction() { if ('?' != $this->scanner->current()) { return false; } $tok = $this->scanner->next(); $procName = $this->scanner->getAsciiAlpha(); $white = $this->scanner->whitespace(); if (0 == strlen($procName) || 0 == $white || false == $this->scanner->current()) { $this->parseError("Expected processing instruction name, got $tok"); $this->bogusComment('<?' . $tok . $procName); return true; } $data = ''; while (!('?' == $this->scanner->current() && '>' == $this->scanner->peek())) { $data .= $this->scanner->current(); $tok = $this->scanner->next(); if (false === $tok) { $this->parseError('Unexpected EOF in processing instruction.'); $this->events->processingInstruction($procName, $data); return true; } } $this->scanner->consume(2); $this->events->processingInstruction($procName, $data); return true; } protected function readUntilSequence($sequence) { $buffer = ''; $first = substr($sequence, 0, 1); while (false !== $this->scanner->current()) { $buffer .= $this->scanner->charsUntil($first); if ($this->scanner->sequenceMatches($sequence, false)) { return $buffer; } $buffer .= $this->scanner->current(); $this->scanner->consume(); } $this->parseError('Unexpected EOF during text read.'); return $buffer; } protected function sequenceMatches($sequence, $caseSensitive = true) { @trigger_error(__METHOD__ . ' method is deprecated since version 2.4 and will be removed in 3.0. Use Scanner::sequenceMatches() instead.', E_USER_DEPRECATED); return $this->scanner->sequenceMatches($sequence, $caseSensitive); } protected function flushBuffer() { if ('' === $this->text) { return; } $this->events->text($this->text); $this->text = ''; } protected function buffer($str) { $this->text .= $str; } protected function parseError($msg) { $args = func_get_args(); if (count($args) > 1) { array_shift($args); $msg = vsprintf($msg, $args); } $line = $this->scanner->currentLine(); $col = $this->scanner->columnOffset(); $this->events->parseError($msg, $line, $col); return false; } protected function decodeCharacterReference($inAttribute = false) { $tok = $this->scanner->next(); $start = $this->scanner->position(); if (false === $tok) { return '&'; } if ("\t" === $tok || "\n" === $tok || "\f" === $tok || ' ' === $tok || '&' === $tok || '<' === $tok) { return '&'; } if ('#' === $tok) { $tok = $this->scanner->next(); if (false === $tok) { $this->parseError('Expected &#DEC; &#HEX;, got EOF'); $this->scanner->unconsume(1); return '&'; } if ('x' === $tok || 'X' === $tok) { $tok = $this->scanner->next(); $hex = $this->scanner->getHex(); if (empty($hex)) { $this->parseError('Expected &#xHEX;, got &#x%s', $tok); $this->scanner->unconsume(2); return '&'; } $entity = CharacterReference::lookupHex($hex); } else { $numeric = $this->scanner->getNumeric(); if (false === $numeric) { $this->parseError('Expected &#DIGITS;, got &#%s', $tok); $this->scanner->unconsume(2); return '&'; } $entity = CharacterReference::lookupDecimal($numeric); } } elseif ('=' === $tok && $inAttribute) { return '&'; } else { $cname = $this->scanner->getAsciiAlphaNum(); $entity = CharacterReference::lookupName($cname); if (null === $entity) { if (!$inAttribute || '' === $cname) { $this->parseError("No match in entity table for '%s'", $cname); } $this->scanner->unconsume($this->scanner->position() - $start); return '&'; } } $tok = $this->scanner->current(); if (';' === $tok) { $this->scanner->consume(); return $entity; } $this->scanner->unconsume($this->scanner->position() - $start); $this->parseError('Expected &ENTITY;, got &ENTITY%s (no trailing ;) ', $tok); return '&'; } protected function is_alpha($input) { $code = ord($input); return ($code >= 97 && $code <= 122) || ($code >= 65 && $code <= 90); } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; class TreeBuildingRules { protected static $tags = array( 'li' => 1, 'dd' => 1, 'dt' => 1, 'rt' => 1, 'rp' => 1, 'tr' => 1, 'th' => 1, 'td' => 1, 'thead' => 1, 'tfoot' => 1, 'tbody' => 1, 'table' => 1, 'optgroup' => 1, 'option' => 1, ); public function hasRules($tagname) { return isset(static::$tags[$tagname]); } public function evaluate($new, $current) { switch ($new->tagName) { case 'li': return $this->handleLI($new, $current); case 'dt': case 'dd': return $this->handleDT($new, $current); case 'rt': case 'rp': return $this->handleRT($new, $current); case 'optgroup': return $this->closeIfCurrentMatches($new, $current, array( 'optgroup', )); case 'option': return $this->closeIfCurrentMatches($new, $current, array( 'option', )); case 'tr': return $this->closeIfCurrentMatches($new, $current, array( 'tr', )); case 'td': case 'th': return $this->closeIfCurrentMatches($new, $current, array( 'th', 'td', )); case 'tbody': case 'thead': case 'tfoot': case 'table': return $this->closeIfCurrentMatches($new, $current, array( 'thead', 'tfoot', 'tbody', )); } return $current; } protected function handleLI($ele, $current) { return $this->closeIfCurrentMatches($ele, $current, array( 'li', )); } protected function handleDT($ele, $current) { return $this->closeIfCurrentMatches($ele, $current, array( 'dt', 'dd', )); } protected function handleRT($ele, $current) { return $this->closeIfCurrentMatches($ele, $current, array( 'rt', 'rp', )); } protected function closeIfCurrentMatches($ele, $current, $match) { if (in_array($current->tagName, $match, true)) { $current->parentNode->appendChild($ele); } else { $current->appendChild($ele); } return $ele; } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; interface InputStream extends \Iterator { public function currentLine(); public function columnOffset(); public function remainingChars(); public function charsUntil($bytes, $max = null); public function charsWhile($bytes, $max = null); public function unconsume($howMany = 1); public function peek(); } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; class StringInputStream implements InputStream { private $data; private $char; private $EOF; public $errors = array(); public function __construct($data, $encoding = 'UTF-8', $debug = '') { $data = UTF8Utils::convertToUTF8($data, $encoding); if ($debug) { fprintf(STDOUT, $debug, $data, strlen($data)); } $this->errors = UTF8Utils::checkForIllegalCodepoints($data); $data = $this->replaceLinefeeds($data); $this->data = $data; $this->char = 0; $this->EOF = strlen($data); } public function __toString() { return $this->data; } protected function replaceLinefeeds($data) { $crlfTable = array( "\0" => "\xEF\xBF\xBD", "\r\n" => "\n", "\r" => "\n", ); return strtr($data, $crlfTable); } public function currentLine() { if (empty($this->EOF) || 0 === $this->char) { return 1; } return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1; } public function getCurrentLine() { return $this->currentLine(); } public function columnOffset() { if (0 === $this->char) { return 0; } $backwardFrom = $this->char - 1 - strlen($this->data); $lastLine = strrpos($this->data, "\n", $backwardFrom); if (false !== $lastLine) { $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); } else { $findLengthOf = substr($this->data, 0, $this->char); } return UTF8Utils::countChars($findLengthOf); } public function getColumnOffset() { return $this->columnOffset(); } public function current() : mixed { return $this->data[$this->char]; } public function next() : void { ++$this->char; } public function rewind() : void { $this->char = 0; } public function valid() : bool { return $this->char < $this->EOF; } public function remainingChars() { if ($this->char < $this->EOF) { $data = substr($this->data, $this->char); $this->char = $this->EOF; return $data; } return ''; } public function charsUntil($bytes, $max = null) { if ($this->char >= $this->EOF) { return false; } if (0 === $max || $max) { $len = strcspn($this->data, $bytes, $this->char, $max); } else { $len = strcspn($this->data, $bytes, $this->char); } $string = (string) substr($this->data, $this->char, $len); $this->char += $len; return $string; } public function charsWhile($bytes, $max = null) { if ($this->char >= $this->EOF) { return false; } if (0 === $max || $max) { $len = strspn($this->data, $bytes, $this->char, $max); } else { $len = strspn($this->data, $bytes, $this->char); } $string = (string) substr($this->data, $this->char, $len); $this->char += $len; return $string; } public function unconsume($howMany = 1) { if (($this->char - $howMany) >= 0) { $this->char -= $howMany; } } public function peek() { if (($this->char + 1) <= $this->EOF) { return $this->data[$this->char + 1]; } return false; } public function key() : mixed { return $this->char; } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; class FileInputStream extends StringInputStream implements InputStream { public function __construct($data, $encoding = 'UTF-8', $debug = '') { $content = file_get_contents($data); parent::__construct($content, $encoding, $debug); } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; class ParseError extends \Exception { } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; use AimySpeedOptimization\Masterminds\HTML5\Exception; class UTF8Utils { const FFFD = "\xEF\xBF\xBD"; public static function countChars($string) { if (function_exists('mb_strlen')) { return mb_strlen($string, 'utf-8'); } if (function_exists('iconv_strlen')) { return iconv_strlen($string, 'utf-8'); } $count = count_chars($string); return array_sum(array_slice($count, 0, 0x80)) + array_sum(array_slice($count, 0xC2, 0x33)); } public static function convertToUTF8($data, $encoding = 'UTF-8') { if (function_exists('mb_convert_encoding')) { $save = mb_substitute_character(); mb_substitute_character('none'); $data = mb_convert_encoding($data, 'UTF-8', $encoding); mb_substitute_character($save); } elseif (function_exists('iconv') && 'auto' !== $encoding) { $data = @iconv($encoding, 'UTF-8//IGNORE', $data); } else { throw new Exception('Not implemented, please install mbstring or iconv'); } if ("\xEF\xBB\xBF" === substr($data, 0, 3)) { $data = substr($data, 3); } return $data; } public static function checkForIllegalCodepoints($data) { $errors = array(); for ($i = 0, $count = substr_count($data, "\0"); $i < $count; ++$i) { $errors[] = 'null-character'; } $count = preg_match_all( '/(?:
[\x01-\x08\x0B\x0E-\x1F\x7F] # U+0001 to U+0008, U+000B, U+000E to U+001F and U+007F
|
\xC2[\x80-\x9F] # U+0080 to U+009F
|
\xED(?:\xA0[\x80-\xFF]|[\xA1-\xBE][\x00-\xFF]|\xBF[\x00-\xBF]) # U+D800 to U+DFFFF
|
\xEF\xB7[\x90-\xAF] # U+FDD0 to U+FDEF
|
\xEF\xBF[\xBE\xBF] # U+FFFE and U+FFFF
|
[\xF0-\xF4][\x8F-\xBF]\xBF[\xBE\xBF] # U+nFFFE and U+nFFFF (1 <= n <= 10_{16})
)/x', $data, $matches); for ($i = 0; $i < $count; ++$i) { $errors[] = 'invalid-codepoint'; } return $errors; } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; interface EventHandler { const DOCTYPE_NONE = 0; const DOCTYPE_PUBLIC = 1; const DOCTYPE_SYSTEM = 2; public function doctype($name, $idType = 0, $id = null, $quirks = false); public function startTag($name, $attributes = array(), $selfClosing = false); public function endTag($name); public function comment($cdata); public function text($cdata); public function eof(); public function parseError($msg, $line, $col); public function cdata($data); public function processingInstruction($name, $data = null); } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; use AimySpeedOptimization\Masterminds\HTML5\Entities; class CharacterReference { protected static $numeric_mask = array( 0x0, 0x2FFFF, 0, 0xFFFF, ); public static function lookupName($name) { return isset(Entities::$byName[$name]) ? Entities::$byName[$name] : null; } public static function lookupDecimal($int) { $entity = '&#' . $int . ';'; return mb_decode_numericentity($entity, static::$numeric_mask, 'utf-8'); } public static function lookupHex($hexdec) { return static::lookupDecimal(hexdec($hexdec)); } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; use AimySpeedOptimization\Masterminds\HTML5\Elements; use AimySpeedOptimization\Masterminds\HTML5\InstructionProcessor; class DOMTreeBuilder implements EventHandler { const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml'; const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML'; const NAMESPACE_SVG = 'http://www.w3.org/2000/svg'; const NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink'; const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace'; const NAMESPACE_XMLNS = 'http://www.w3.org/2000/xmlns/'; const OPT_DISABLE_HTML_NS = 'disable_html_ns'; const OPT_TARGET_DOC = 'target_document'; const OPT_IMPLICIT_NS = 'implicit_namespaces'; protected $nsRoots = array( 'html' => self::NAMESPACE_HTML, 'svg' => self::NAMESPACE_SVG, 'math' => self::NAMESPACE_MATHML, ); protected $implicitNamespaces = array( 'xml' => self::NAMESPACE_XML, 'xmlns' => self::NAMESPACE_XMLNS, 'xlink' => self::NAMESPACE_XLINK, ); protected $nsStack = array(); protected $pushes = array(); const IM_INITIAL = 0; const IM_BEFORE_HTML = 1; const IM_BEFORE_HEAD = 2; const IM_IN_HEAD = 3; const IM_IN_HEAD_NOSCRIPT = 4; const IM_AFTER_HEAD = 5; const IM_IN_BODY = 6; const IM_TEXT = 7; const IM_IN_TABLE = 8; const IM_IN_TABLE_TEXT = 9; const IM_IN_CAPTION = 10; const IM_IN_COLUMN_GROUP = 11; const IM_IN_TABLE_BODY = 12; const IM_IN_ROW = 13; const IM_IN_CELL = 14; const IM_IN_SELECT = 15; const IM_IN_SELECT_IN_TABLE = 16; const IM_AFTER_BODY = 17; const IM_IN_FRAMESET = 18; const IM_AFTER_FRAMESET = 19; const IM_AFTER_AFTER_BODY = 20; const IM_AFTER_AFTER_FRAMESET = 21; const IM_IN_SVG = 22; const IM_IN_MATHML = 23; protected $options = array(); protected $stack = array(); protected $current; protected $rules; protected $doc; protected $frag; protected $processor; protected $insertMode = 0; protected $onlyInline; protected $quirks = true; protected $errors = array(); public function __construct($isFragment = false, array $options = array()) { $this->options = $options; if (isset($options[self::OPT_TARGET_DOC])) { $this->doc = $options[self::OPT_TARGET_DOC]; } else { $impl = new \DOMImplementation(); $dt = $impl->createDocumentType('html'); $this->doc = $impl->createDocument(null, '', $dt); $this->doc->encoding = !empty($options['encoding']) ? $options['encoding'] : 'UTF-8'; } $this->errors = array(); $this->current = $this->doc; $this->rules = new TreeBuildingRules(); $implicitNS = array(); if (isset($this->options[self::OPT_IMPLICIT_NS])) { $implicitNS = $this->options[self::OPT_IMPLICIT_NS]; } elseif (isset($this->options['implicitNamespaces'])) { $implicitNS = $this->options['implicitNamespaces']; } array_unshift($this->nsStack, $implicitNS + array('' => self::NAMESPACE_HTML) + $this->implicitNamespaces); if ($isFragment) { $this->insertMode = static::IM_IN_BODY; $this->frag = $this->doc->createDocumentFragment(); $this->current = $this->frag; } } public function document() { return $this->doc; } public function fragment() { return $this->frag; } public function setInstructionProcessor(InstructionProcessor $proc) { $this->processor = $proc; } public function doctype($name, $idType = 0, $id = null, $quirks = false) { $this->quirks = $quirks; if ($this->insertMode > static::IM_INITIAL) { $this->parseError('Illegal placement of DOCTYPE tag. Ignoring: ' . $name); return; } $this->insertMode = static::IM_BEFORE_HTML; } public function startTag($name, $attributes = array(), $selfClosing = false) { $lname = $this->normalizeTagName($name); if (!$this->doc->documentElement && 'html' !== $name && !$this->frag) { $this->startTag('html'); } if ($this->insertMode === static::IM_INITIAL) { $this->quirks = true; $this->parseError('No DOCTYPE specified.'); } if ('image' === $name && !($this->insertMode === static::IM_IN_SVG || $this->insertMode === static::IM_IN_MATHML)) { $name = 'img'; } if ($this->insertMode >= static::IM_IN_BODY && Elements::isA($name, Elements::AUTOCLOSE_P)) { $this->autoclose('p'); } switch ($name) { case 'html': $this->insertMode = static::IM_BEFORE_HEAD; break; case 'head': if ($this->insertMode > static::IM_BEFORE_HEAD) { $this->parseError('Unexpected head tag outside of head context.'); } else { $this->insertMode = static::IM_IN_HEAD; } break; case 'body': $this->insertMode = static::IM_IN_BODY; break; case 'svg': $this->insertMode = static::IM_IN_SVG; break; case 'math': $this->insertMode = static::IM_IN_MATHML; break; case 'noscript': if ($this->insertMode === static::IM_IN_HEAD) { $this->insertMode = static::IM_IN_HEAD_NOSCRIPT; } break; } if ($this->insertMode === static::IM_IN_SVG) { $lname = Elements::normalizeSvgElement($lname); } $pushes = 0; if (isset($this->nsRoots[$lname]) && $this->nsStack[0][''] !== $this->nsRoots[$lname]) { array_unshift($this->nsStack, array( '' => $this->nsRoots[$lname], ) + $this->nsStack[0]); ++$pushes; } $needsWorkaround = false; if (isset($this->options['xmlNamespaces']) && $this->options['xmlNamespaces']) { foreach ($attributes as $aName => $aVal) { if ('xmlns' === $aName) { $needsWorkaround = $aVal; array_unshift($this->nsStack, array( '' => $aVal, ) + $this->nsStack[0]); ++$pushes; } elseif ('xmlns' === (($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : '')) { array_unshift($this->nsStack, array( substr($aName, $pos + 1) => $aVal, ) + $this->nsStack[0]); ++$pushes; } } } if ($this->onlyInline && Elements::isA($lname, Elements::BLOCK_TAG)) { $this->autoclose($this->onlyInline); $this->onlyInline = null; } if ($this->current instanceof \DOMElement && isset(Elements::$optionalEndElementsParentsToClose[$lname])) { foreach (Elements::$optionalEndElementsParentsToClose[$lname] as $parentElName) { if ($this->current instanceof \DOMElement && $this->current->tagName === $parentElName) { $this->autoclose($parentElName); } } } try { $prefix = ($pos = strpos($lname, ':')) ? substr($lname, 0, $pos) : ''; if (false !== $needsWorkaround) { $xml = "<$lname xmlns=\"$needsWorkaround\" " . (strlen($prefix) && isset($this->nsStack[0][$prefix]) ? ("xmlns:$prefix=\"" . $this->nsStack[0][$prefix] . '"') : '') . '/>'; $frag = new \DOMDocument('1.0', 'UTF-8'); $frag->loadXML($xml); $ele = $this->doc->importNode($frag->documentElement, true); } else { if (!isset($this->nsStack[0][$prefix]) || ('' === $prefix && isset($this->options[self::OPT_DISABLE_HTML_NS]) && $this->options[self::OPT_DISABLE_HTML_NS])) { $ele = $this->doc->createElement($lname); } else { $ele = $this->doc->createElementNS($this->nsStack[0][$prefix], $lname); } } } catch (\DOMException $e) { $this->parseError("Illegal tag name: <$lname>. Replaced with <invalid>."); $ele = $this->doc->createElement('invalid'); } if (Elements::isA($lname, Elements::BLOCK_ONLY_INLINE)) { $this->onlyInline = $lname; } if ($pushes > 0 && !Elements::isA($name, Elements::VOID_TAG)) { $this->pushes[spl_object_hash($ele)] = array($pushes, $ele); } foreach ($attributes as $aName => $aVal) { if ('xmlns' === $aName) { continue; } if ($this->insertMode === static::IM_IN_SVG) { $aName = Elements::normalizeSvgAttribute($aName); } elseif ($this->insertMode === static::IM_IN_MATHML) { $aName = Elements::normalizeMathMlAttribute($aName); } $aVal = (string) $aVal; try { $prefix = ($pos = strpos($aName, ':')) ? substr($aName, 0, $pos) : false; if ('xmlns' === $prefix) { $ele->setAttributeNS(self::NAMESPACE_XMLNS, $aName, $aVal); } elseif (false !== $prefix && isset($this->nsStack[0][$prefix])) { $ele->setAttributeNS($this->nsStack[0][$prefix], $aName, $aVal); } else { $ele->setAttribute($aName, $aVal); } } catch (\DOMException $e) { $this->parseError("Illegal attribute name for tag $name. Ignoring: $aName"); continue; } if ('id' === $aName) { $ele->setIdAttribute('id', true); } } if ($this->frag !== $this->current && $this->rules->hasRules($name)) { $this->current = $this->rules->evaluate($ele, $this->current); } else { $this->current->appendChild($ele); if (!Elements::isA($name, Elements::VOID_TAG)) { $this->current = $ele; } if (Elements::isHtml5Element($name)) { $selfClosing = false; } } if ($this->insertMode <= static::IM_BEFORE_HEAD && 'head' !== $name && 'html' !== $name) { $this->insertMode = static::IM_IN_BODY; } if ($pushes > 0 && Elements::isA($name, Elements::VOID_TAG)) { for ($i = 0; $i < $pushes; ++$i) { array_shift($this->nsStack); } } if ($selfClosing) { $this->endTag($name); } return Elements::element($name); } public function endTag($name) { $lname = $this->normalizeTagName($name); if ('br' === $name) { $this->parseError('Closing tag encountered for void element br.'); $this->startTag('br'); } elseif (Elements::isA($name, Elements::VOID_TAG)) { return; } if ($this->insertMode <= static::IM_BEFORE_HTML) { if (in_array($name, array( 'html', 'br', 'head', 'title', ))) { $this->startTag('html'); $this->endTag($name); $this->insertMode = static::IM_BEFORE_HEAD; return; } $this->parseError('Illegal closing tag at global scope.'); return; } if ($this->insertMode === static::IM_IN_SVG) { $lname = Elements::normalizeSvgElement($lname); } $cid = spl_object_hash($this->current); if ('html' === $lname) { return; } if (isset($this->pushes[$cid])) { for ($i = 0; $i < $this->pushes[$cid][0]; ++$i) { array_shift($this->nsStack); } unset($this->pushes[$cid]); } if (!$this->autoclose($lname)) { $this->parseError('Could not find closing tag for ' . $lname); } switch ($lname) { case 'head': $this->insertMode = static::IM_AFTER_HEAD; break; case 'body': $this->insertMode = static::IM_AFTER_BODY; break; case 'svg': case 'mathml': $this->insertMode = static::IM_IN_BODY; break; } } public function comment($cdata) { $node = $this->doc->createComment($cdata); $this->current->appendChild($node); } public function text($data) { if ($this->insertMode < static::IM_IN_HEAD) { $dataTmp = trim($data, " \t\n\r\f"); if (!empty($dataTmp)) { $this->parseError('Unexpected text. Ignoring: ' . $dataTmp); } return; } $node = $this->doc->createTextNode($data); $this->current->appendChild($node); } public function eof() { } public function parseError($msg, $line = 0, $col = 0) { $this->errors[] = sprintf('Line %d, Col %d: %s', $line, $col, $msg); } public function getErrors() { return $this->errors; } public function cdata($data) { $node = $this->doc->createCDATASection($data); $this->current->appendChild($node); } public function processingInstruction($name, $data = null) { if ($this->insertMode === static::IM_INITIAL && 'xml' === strtolower($name)) { return; } if ($this->processor instanceof InstructionProcessor) { $res = $this->processor->process($this->current, $name, $data); if (!empty($res)) { $this->current = $res; } return; } $node = $this->doc->createProcessingInstruction($name, $data); $this->current->appendChild($node); } protected function normalizeTagName($tagName) { return $tagName; } protected function quirksTreeResolver($name) { throw new \Exception('Not implemented.'); } protected function autoclose($tagName) { $working = $this->current; do { if (XML_ELEMENT_NODE !== $working->nodeType) { return false; } if ($working->tagName === $tagName) { $this->current = $working->parentNode; return true; } } while ($working = $working->parentNode); return false; } protected function isAncestor($tagName) { $candidate = $this->current; while (XML_ELEMENT_NODE === $candidate->nodeType) { if ($candidate->tagName === $tagName) { return true; } $candidate = $candidate->parentNode; } return false; } protected function isParent($tagName) { return $this->current->tagName === $tagName; } } namespace AimySpeedOptimization\Masterminds\HTML5\Parser; use AimySpeedOptimization\Masterminds\HTML5\Exception; class Scanner { const CHARS_HEX = 'abcdefABCDEF01234567890'; const CHARS_ALNUM = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890'; const CHARS_ALPHA = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; private $data; private $char; private $EOF; public $errors = array(); public function __construct($data, $encoding = 'UTF-8') { if ($data instanceof InputStream) { @trigger_error('InputStream objects are deprecated since version 2.4 and will be removed in 3.0. Use strings instead.', E_USER_DEPRECATED); $data = (string) $data; } $data = UTF8Utils::convertToUTF8($data, $encoding); $this->errors = UTF8Utils::checkForIllegalCodepoints($data); $data = $this->replaceLinefeeds($data); $this->data = $data; $this->char = 0; $this->EOF = strlen($data); } public function sequenceMatches($sequence, $caseSensitive = true) { $portion = substr($this->data, $this->char, strlen($sequence)); return $caseSensitive ? $portion === $sequence : 0 === strcasecmp($portion, $sequence); } public function position() { return $this->char; } public function peek() { if (($this->char + 1) < $this->EOF) { return $this->data[$this->char + 1]; } return false; } public function next() { ++$this->char; if ($this->char < $this->EOF) { return $this->data[$this->char]; } return false; } public function current() { if ($this->char < $this->EOF) { return $this->data[$this->char]; } return false; } public function consume($count = 1) { $this->char += $count; } public function unconsume($howMany = 1) { if (($this->char - $howMany) >= 0) { $this->char -= $howMany; } } public function getHex() { return $this->doCharsWhile(static::CHARS_HEX); } public function getAsciiAlpha() { return $this->doCharsWhile(static::CHARS_ALPHA); } public function getAsciiAlphaNum() { return $this->doCharsWhile(static::CHARS_ALNUM); } public function getNumeric() { return $this->doCharsWhile('0123456789'); } public function whitespace() { if ($this->char >= $this->EOF) { return false; } $len = strspn($this->data, "\n\t\f ", $this->char); $this->char += $len; return $len; } public function currentLine() { if (empty($this->EOF) || 0 === $this->char) { return 1; } return substr_count($this->data, "\n", 0, min($this->char, $this->EOF)) + 1; } public function charsUntil($mask) { return $this->doCharsUntil($mask); } public function charsWhile($mask) { return $this->doCharsWhile($mask); } public function columnOffset() { if (0 === $this->char) { return 0; } $backwardFrom = $this->char - 1 - strlen($this->data); $lastLine = strrpos($this->data, "\n", $backwardFrom); if (false !== $lastLine) { $findLengthOf = substr($this->data, $lastLine + 1, $this->char - 1 - $lastLine); } else { $findLengthOf = substr($this->data, 0, $this->char); } return UTF8Utils::countChars($findLengthOf); } public function remainingChars() { if ($this->char < $this->EOF) { $data = substr($this->data, $this->char); $this->char = $this->EOF; return $data; } return ''; } private function replaceLinefeeds($data) { $crlfTable = array( "\0" => "\xEF\xBF\xBD", "\r\n" => "\n", "\r" => "\n", ); return strtr($data, $crlfTable); } private function doCharsUntil($bytes, $max = null) { if ($this->char >= $this->EOF) { return false; } if (0 === $max || $max) { $len = strcspn($this->data, $bytes, $this->char, $max); } else { $len = strcspn($this->data, $bytes, $this->char); } $string = (string) substr($this->data, $this->char, $len); $this->char += $len; return $string; } private function doCharsWhile($bytes, $max = null) { if ($this->char >= $this->EOF) { return false; } if (0 === $max || $max) { $len = strspn($this->data, $bytes, $this->char, $max); } else { $len = strspn($this->data, $bytes, $this->char); } $string = (string) substr($this->data, $this->char, $len); $this->char += $len; return $string; } } namespace AimySpeedOptimization\Masterminds\HTML5\Serializer; class HTML5Entities { public static $map = array( ' ' => '	', "\n" => '
', '!' => '!', '"' => '"', '#' => '#', '$' => '$', '%' => '%', '&' => '&', '\'' => ''', '(' => '(', ')' => ')', '*' => '*', '+' => '+', ',' => ',', '.' => '.', '/' => '/', ':' => ':', ';' => ';', '<' => '<', '<⃒' => '&nvlt', '=' => '=', '=⃥' => '&bne', '>' => '>', '>⃒' => '&nvgt', '?' => '?', '@' => '@', '[' => '[', '\\' => '\', ']' => ']', '^' => '^', '_' => '_', '`' => '`', 'fj' => '&fjlig', '{' => '{', '|' => '|', '}' => '}', ' ' => ' ', '¡' => '¡', '¢' => '¢', '£' => '£', '¤' => '¤', '¥' => '¥', '¦' => '¦', '§' => '§', '¨' => '¨', '©' => '©', 'ª' => 'ª', '«' => '«', '¬' => '¬', '' => '­', '®' => '®', '¯' => '¯', '°' => '°', '±' => '±', '²' => '²', '³' => '³', '´' => '´', 'µ' => 'µ', '¶' => '¶', '·' => '·', '¸' => '¸', '¹' => '¹', 'º' => 'º', '»' => '»', '¼' => '¼', '½' => '½', '¾' => '¾', '¿' => '¿', 'À' => 'À', 'Á' => 'Á', 'Â' => 'Â', 'Ã' => 'Ã', 'Ä' => 'Ä', 'Å' => 'Å', 'Æ' => 'Æ', 'Ç' => 'Ç', 'È' => 'È', 'É' => 'É', 'Ê' => 'Ê', 'Ë' => 'Ë', 'Ì' => 'Ì', 'Í' => 'Í', 'Î' => 'Î', 'Ï' => 'Ï', 'Ð' => 'Ð', 'Ñ' => 'Ñ', 'Ò' => 'Ò', 'Ó' => 'Ó', 'Ô' => 'Ô', 'Õ' => 'Õ', 'Ö' => 'Ö', '×' => '×', 'Ø' => 'Ø', 'Ù' => 'Ù', 'Ú' => 'Ú', 'Û' => 'Û', 'Ü' => 'Ü', 'Ý' => 'Ý', 'Þ' => 'Þ', 'ß' => 'ß', 'à' => 'à', 'á' => 'á', 'â' => 'â', 'ã' => 'ã', 'ä' => 'ä', 'å' => 'å', 'æ' => 'æ', 'ç' => 'ç', 'è' => 'è', 'é' => 'é', 'ê' => 'ê', 'ë' => 'ë', 'ì' => 'ì', 'í' => 'í', 'î' => 'î', 'ï' => 'ï', 'ð' => 'ð', 'ñ' => 'ñ', 'ò' => 'ò', 'ó' => 'ó', 'ô' => 'ô', 'õ' => 'õ', 'ö' => 'ö', '÷' => '÷', 'ø' => 'ø', 'ù' => 'ù', 'ú' => 'ú', 'û' => 'û', 'ü' => 'ü', 'ý' => 'ý', 'þ' => 'þ', 'ÿ' => 'ÿ', 'Ā' => 'Ā', 'ā' => 'ā', 'Ă' => 'Ă', 'ă' => 'ă', 'Ą' => 'Ą', 'ą' => 'ą', 'Ć' => 'Ć', 'ć' => 'ć', 'Ĉ' => 'Ĉ', 'ĉ' => 'ĉ', 'Ċ' => 'Ċ', 'ċ' => 'ċ', 'Č' => 'Č', 'č' => 'č', 'Ď' => 'Ď', 'ď' => 'ď', 'Đ' => 'Đ', 'đ' => 'đ', 'Ē' => 'Ē', 'ē' => 'ē', 'Ė' => 'Ė', 'ė' => 'ė', 'Ę' => 'Ę', 'ę' => 'ę', 'Ě' => 'Ě', 'ě' => 'ě', 'Ĝ' => 'Ĝ', 'ĝ' => 'ĝ', 'Ğ' => 'Ğ', 'ğ' => 'ğ', 'Ġ' => 'Ġ', 'ġ' => 'ġ', 'Ģ' => 'Ģ', 'Ĥ' => 'Ĥ', 'ĥ' => 'ĥ', 'Ħ' => 'Ħ', 'ħ' => 'ħ', 'Ĩ' => 'Ĩ', 'ĩ' => 'ĩ', 'Ī' => 'Ī', 'ī' => 'ī', 'Į' => 'Į', 'į' => 'į', 'İ' => 'İ', 'ı' => 'ı', 'IJ' => 'IJ', 'ij' => 'ij', 'Ĵ' => 'Ĵ', 'ĵ' => 'ĵ', 'Ķ' => 'Ķ', 'ķ' => 'ķ', 'ĸ' => 'ĸ', 'Ĺ' => 'Ĺ', 'ĺ' => 'ĺ', 'Ļ' => 'Ļ', 'ļ' => 'ļ', 'Ľ' => 'Ľ', 'ľ' => 'ľ', 'Ŀ' => 'Ŀ', 'ŀ' => 'ŀ', 'Ł' => 'Ł', 'ł' => 'ł', 'Ń' => 'Ń', 'ń' => 'ń', 'Ņ' => 'Ņ', 'ņ' => 'ņ', 'Ň' => 'Ň', 'ň' => 'ň', 'ʼn' => 'ʼn', 'Ŋ' => 'Ŋ', 'ŋ' => 'ŋ', 'Ō' => 'Ō', 'ō' => 'ō', 'Ő' => 'Ő', 'ő' => 'ő', 'Œ' => 'Œ', 'œ' => 'œ', 'Ŕ' => 'Ŕ', 'ŕ' => 'ŕ', 'Ŗ' => 'Ŗ', 'ŗ' => 'ŗ', 'Ř' => 'Ř', 'ř' => 'ř', 'Ś' => 'Ś', 'ś' => 'ś', 'Ŝ' => 'Ŝ', 'ŝ' => 'ŝ', 'Ş' => 'Ş', 'ş' => 'ş', 'Š' => 'Š', 'š' => 'š', 'Ţ' => 'Ţ', 'ţ' => 'ţ', 'Ť' => 'Ť', 'ť' => 'ť', 'Ŧ' => 'Ŧ', 'ŧ' => 'ŧ', 'Ũ' => 'Ũ', 'ũ' => 'ũ', 'Ū' => 'Ū', 'ū' => 'ū', 'Ŭ' => 'Ŭ', 'ŭ' => 'ŭ', 'Ů' => 'Ů', 'ů' => 'ů', 'Ű' => 'Ű', 'ű' => 'ű', 'Ų' => 'Ų', 'ų' => 'ų', 'Ŵ' => 'Ŵ', 'ŵ' => 'ŵ', 'Ŷ' => 'Ŷ', 'ŷ' => 'ŷ', 'Ÿ' => 'Ÿ', 'Ź' => 'Ź', 'ź' => 'ź', 'Ż' => 'Ż', 'ż' => 'ż', 'Ž' => 'Ž', 'ž' => 'ž', 'ƒ' => 'ƒ', 'Ƶ' => 'Ƶ', 'ǵ' => 'ǵ', 'ȷ' => 'ȷ', 'ˆ' => 'ˆ', 'ˇ' => 'ˇ', '˘' => '˘', '˙' => '˙', '˚' => '˚', '˛' => '˛', '˜' => '˜', '˝' => '˝', '̑' => '̑', 'Α' => 'Α', 'Β' => 'Β', 'Γ' => 'Γ', 'Δ' => 'Δ', 'Ε' => 'Ε', 'Ζ' => 'Ζ', 'Η' => 'Η', 'Θ' => 'Θ', 'Ι' => 'Ι', 'Κ' => 'Κ', 'Λ' => 'Λ', 'Μ' => 'Μ', 'Ν' => 'Ν', 'Ξ' => 'Ξ', 'Ο' => 'Ο', 'Π' => 'Π', 'Ρ' => 'Ρ', 'Σ' => 'Σ', 'Τ' => 'Τ', 'Υ' => 'Υ', 'Φ' => 'Φ', 'Χ' => 'Χ', 'Ψ' => 'Ψ', 'Ω' => 'Ω', 'α' => 'α', 'β' => 'β', 'γ' => 'γ', 'δ' => 'δ', 'ε' => 'ε', 'ζ' => 'ζ', 'η' => 'η', 'θ' => 'θ', 'ι' => 'ι', 'κ' => 'κ', 'λ' => 'λ', 'μ' => 'μ', 'ν' => 'ν', 'ξ' => 'ξ', 'ο' => 'ο', 'π' => 'π', 'ρ' => 'ρ', 'ς' => 'ς', 'σ' => 'σ', 'τ' => 'τ', 'υ' => 'υ', 'φ' => 'φ', 'χ' => 'χ', 'ψ' => 'ψ', 'ω' => 'ω', 'ϑ' => 'ϑ', 'ϒ' => 'ϒ', 'ϕ' => 'ϕ', 'ϖ' => 'ϖ', 'Ϝ' => 'Ϝ', 'ϝ' => 'ϝ', 'ϰ' => 'ϰ', 'ϱ' => 'ϱ', 'ϵ' => 'ϵ', '϶' => '϶', 'Ё' => 'Ё', 'Ђ' => 'Ђ', 'Ѓ' => 'Ѓ', 'Є' => 'Є', 'Ѕ' => 'Ѕ', 'І' => 'І', 'Ї' => 'Ї', 'Ј' => 'Ј', 'Љ' => 'Љ', 'Њ' => 'Њ', 'Ћ' => 'Ћ', 'Ќ' => 'Ќ', 'Ў' => 'Ў', 'Џ' => 'Џ', 'А' => 'А', 'Б' => 'Б', 'В' => 'В', 'Г' => 'Г', 'Д' => 'Д', 'Е' => 'Е', 'Ж' => 'Ж', 'З' => 'З', 'И' => 'И', 'Й' => 'Й', 'К' => 'К', 'Л' => 'Л', 'М' => 'М', 'Н' => 'Н', 'О' => 'О', 'П' => 'П', 'Р' => 'Р', 'С' => 'С', 'Т' => 'Т', 'У' => 'У', 'Ф' => 'Ф', 'Х' => 'Х', 'Ц' => 'Ц', 'Ч' => 'Ч', 'Ш' => 'Ш', 'Щ' => 'Щ', 'Ъ' => 'Ъ', 'Ы' => 'Ы', 'Ь' => 'Ь', 'Э' => 'Э', 'Ю' => 'Ю', 'Я' => 'Я', 'а' => 'а', 'б' => 'б', 'в' => 'в', 'г' => 'г', 'д' => 'д', 'е' => 'е', 'ж' => 'ж', 'з' => 'з', 'и' => 'и', 'й' => 'й', 'к' => 'к', 'л' => 'л', 'м' => 'м', 'н' => 'н', 'о' => 'о', 'п' => 'п', 'р' => 'р', 'с' => 'с', 'т' => 'т', 'у' => 'у', 'ф' => 'ф', 'х' => 'х', 'ц' => 'ц', 'ч' => 'ч', 'ш' => 'ш', 'щ' => 'щ', 'ъ' => 'ъ', 'ы' => 'ы', 'ь' => 'ь', 'э' => 'э', 'ю' => 'ю', 'я' => 'я', 'ё' => 'ё', 'ђ' => 'ђ', 'ѓ' => 'ѓ', 'є' => 'є', 'ѕ' => 'ѕ', 'і' => 'і', 'ї' => 'ї', 'ј' => 'ј', 'љ' => 'љ', 'њ' => 'њ', 'ћ' => 'ћ', 'ќ' => 'ќ', 'ў' => 'ў', 'џ' => 'џ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', ' ' => ' ', '' => '​', '' => '‌', '' => '‍', '' => '‎', '' => '‏', '‐' => '‐', '–' => '–', '—' => '—', '―' => '―', '‖' => '‖', '‘' => '‘', '’' => '’', '‚' => '‚', '“' => '“', '”' => '”', '„' => '„', '†' => '†', '‡' => '‡', '•' => '•', '‥' => '‥', '…' => '…', '‰' => '‰', '‱' => '‱', '′' => '′', '″' => '″', '‴' => '‴', '‵' => '‵', '‹' => '‹', '›' => '›', '‾' => '‾', '⁁' => '⁁', '⁃' => '⁃', '⁄' => '⁄', '⁏' => '⁏', '⁗' => '⁗', ' ' => ' ', ' ' => '&ThickSpace', '' => '⁠', '' => '⁡', '' => '⁢', '' => '⁣', '€' => '€', '⃛' => '⃛', '⃜' => '⃜', 'ℂ' => 'ℂ', '℅' => '℅', 'ℊ' => 'ℊ', 'ℋ' => 'ℋ', 'ℌ' => 'ℌ', 'ℍ' => 'ℍ', 'ℎ' => 'ℎ', 'ℏ' => 'ℏ', 'ℐ' => 'ℐ', 'ℑ' => 'ℑ', 'ℒ' => 'ℒ', 'ℓ' => 'ℓ', 'ℕ' => 'ℕ', '№' => '№', '℗' => '℗', '℘' => '℘', 'ℙ' => 'ℙ', 'ℚ' => 'ℚ', 'ℛ' => 'ℛ', 'ℜ' => 'ℜ', 'ℝ' => 'ℝ', '℞' => '℞', '™' => '™', 'ℤ' => 'ℤ', '℧' => '℧', 'ℨ' => 'ℨ', '℩' => '℩', 'ℬ' => 'ℬ', 'ℭ' => 'ℭ', 'ℯ' => 'ℯ', 'ℰ' => 'ℰ', 'ℱ' => 'ℱ', 'ℳ' => 'ℳ', 'ℴ' => 'ℴ', 'ℵ' => 'ℵ', 'ℶ' => 'ℶ', 'ℷ' => 'ℷ', 'ℸ' => 'ℸ', 'ⅅ' => 'ⅅ', 'ⅆ' => 'ⅆ', 'ⅇ' => 'ⅇ', 'ⅈ' => 'ⅈ', '⅓' => '⅓', '⅔' => '⅔', '⅕' => '⅕', '⅖' => '⅖', '⅗' => '⅗', '⅘' => '⅘', '⅙' => '⅙', '⅚' => '⅚', '⅛' => '⅛', '⅜' => '⅜', '⅝' => '⅝', '⅞' => '⅞', '←' => '←', '↑' => '↑', '→' => '→', '↓' => '↓', '↔' => '↔', '↕' => '↕', '↖' => '↖', '↗' => '↗', '↘' => '↘', '↙' => '↙', '↚' => '↚', '↛' => '↛', '↝' => '↝', '↝̸' => '&nrarrw', '↞' => '↞', '↟' => '↟', '↠' => '↠', '↡' => '↡', '↢' => '↢', '↣' => '↣', '↤' => '↤', '↥' => '↥', '↦' => '↦', '↧' => '↧', '↩' => '↩', '↪' => '↪', '↫' => '↫', '↬' => '↬', '↭' => '↭', '↮' => '↮', '↰' => '↰', '↱' => '↱', '↲' => '↲', '↳' => '↳', '↵' => '↵', '↶' => '↶', '↷' => '↷', '↺' => '↺', '↻' => '↻', '↼' => '↼', '↽' => '↽', '↾' => '↾', '↿' => '↿', '⇀' => '⇀', '⇁' => '⇁', '⇂' => '⇂', '⇃' => '⇃', '⇄' => '⇄', '⇅' => '⇅', '⇆' => '⇆', '⇇' => '⇇', '⇈' => '⇈', '⇉' => '⇉', '⇊' => '⇊', '⇋' => '⇋', '⇌' => '⇌', '⇍' => '⇍', '⇎' => '⇎', '⇏' => '⇏', '⇐' => '⇐', '⇑' => '⇑', '⇒' => '⇒', '⇓' => '⇓', '⇔' => '⇔', '⇕' => '⇕', '⇖' => '⇖', '⇗' => '⇗', '⇘' => '⇘', '⇙' => '⇙', '⇚' => '⇚', '⇛' => '⇛', '⇝' => '⇝', '⇤' => '⇤', '⇥' => '⇥', '⇵' => '⇵', '⇽' => '⇽', '⇾' => '⇾', '⇿' => '⇿', '∀' => '∀', '∁' => '∁', '∂' => '∂', '∂̸' => '&npart', '∃' => '∃', '∄' => '∄', '∅' => '∅', '∇' => '∇', '∈' => '∈', '∉' => '∉', '∋' => '∋', '∌' => '∌', '∏' => '∏', '∐' => '∐', '∑' => '∑', '−' => '−', '∓' => '∓', '∔' => '∔', '∖' => '∖', '∗' => '∗', '∘' => '∘', '√' => '√', '∝' => '∝', '∞' => '∞', '∟' => '∟', '∠' => '∠', '∠⃒' => '&nang', '∡' => '∡', '∢' => '∢', '∣' => '∣', '∤' => '∤', '∥' => '∥', '∦' => '∦', '∧' => '∧', '∨' => '∨', '∩' => '∩', '∩︀' => '&caps', '∪' => '∪', '∪︀' => '&cups', '∫' => '∫', '∬' => '∬', '∭' => '∭', '∮' => '∮', '∯' => '∯', '∰' => '∰', '∱' => '∱', '∲' => '∲', '∳' => '∳', '∴' => '∴', '∵' => '∵', '∶' => '∶', '∷' => '∷', '∸' => '∸', '∺' => '∺', '∻' => '∻', '∼' => '∼', '∼⃒' => '&nvsim', '∽' => '∽', '∽̱' => '&race', '∾' => '∾', '∾̳' => '&acE', '∿' => '∿', '≀' => '≀', '≁' => '≁', '≂' => '≂', '≂̸' => '&nesim', '≃' => '≃', '≄' => '≄', '≅' => '≅', '≆' => '≆', '≇' => '≇', '≈' => '≈', '≉' => '≉', '≊' => '≊', '≋' => '≋', '≋̸' => '&napid', '≌' => '≌', '≍' => '≍', '≍⃒' => '&nvap', '≎' => '≎', '≎̸' => '&nbump', '≏' => '≏', '≏̸' => '&nbumpe', '≐' => '≐', '≐̸' => '&nedot', '≑' => '≑', '≒' => '≒', '≓' => '≓', '≔' => '≔', '≕' => '≕', '≖' => '≖', '≗' => '≗', '≙' => '≙', '≚' => '≚', '≜' => '≜', '≟' => '≟', '≠' => '≠', '≡' => '≡', '≡⃥' => '&bnequiv', '≢' => '≢', '≤' => '≤', '≤⃒' => '&nvle', '≥' => '≥', '≥⃒' => '&nvge', '≦' => '≦', '≦̸' => '&nlE', '≧' => '≧', '≧̸' => '&NotGreaterFullEqual', '≨' => '≨', '≨︀' => '&lvertneqq', '≩' => '≩', '≩︀' => '&gvertneqq', '≪' => '≪', '≪̸' => '&nLtv', '≪⃒' => '&nLt', '≫' => '≫', '≫̸' => '&NotGreaterGreater', '≫⃒' => '&nGt', '≬' => '≬', '≭' => '≭', '≮' => '≮', '≯' => '≯', '≰' => '≰', '≱' => '≱', '≲' => '≲', '≳' => '≳', '≴' => '≴', '≵' => '≵', '≶' => '≶', '≷' => '≷', '≸' => '≸', '≹' => '≹', '≺' => '≺', '≻' => '≻', '≼' => '≼', '≽' => '≽', '≾' => '≾', '≿' => '≿', '≿̸' => '&NotSucceedsTilde', '⊀' => '⊀', '⊁' => '⊁', '⊂' => '⊂', '⊂⃒' => '&vnsub', '⊃' => '⊃', '⊃⃒' => '&nsupset', '⊄' => '⊄', '⊅' => '⊅', '⊆' => '⊆', '⊇' => '⊇', '⊈' => '⊈', '⊉' => '⊉', '⊊' => '⊊', '⊊︀' => '&vsubne', '⊋' => '⊋', '⊋︀' => '&vsupne', '⊍' => '⊍', '⊎' => '⊎', '⊏' => '⊏', '⊏̸' => '&NotSquareSubset', '⊐' => '⊐', '⊐̸' => '&NotSquareSuperset', '⊑' => '⊑', '⊒' => '⊒', '⊓' => '⊓', '⊓︀' => '&sqcaps', '⊔' => '⊔', '⊔︀' => '&sqcups', '⊕' => '⊕', '⊖' => '⊖', '⊗' => '⊗', '⊘' => '⊘', '⊙' => '⊙', '⊚' => '⊚', '⊛' => '⊛', '⊝' => '⊝', '⊞' => '⊞', '⊟' => '⊟', '⊠' => '⊠', '⊡' => '⊡', '⊢' => '⊢', '⊣' => '⊣', '⊤' => '⊤', '⊥' => '⊥', '⊧' => '⊧', '⊨' => '⊨', '⊩' => '⊩', '⊪' => '⊪', '⊫' => '⊫', '⊬' => '⊬', '⊭' => '⊭', '⊮' => '⊮', '⊯' => '⊯', '⊰' => '⊰', '⊲' => '⊲', '⊳' => '⊳', '⊴' => '⊴', '⊴⃒' => '&nvltrie', '⊵' => '⊵', '⊵⃒' => '&nvrtrie', '⊶' => '⊶', '⊷' => '⊷', '⊸' => '⊸', '⊹' => '⊹', '⊺' => '⊺', '⊻' => '⊻', '⊽' => '⊽', '⊾' => '⊾', '⊿' => '⊿', '⋀' => '⋀', '⋁' => '⋁', '⋂' => '⋂', '⋃' => '⋃', '⋄' => '⋄', '⋅' => '⋅', '⋆' => '⋆', '⋇' => '⋇', '⋈' => '⋈', '⋉' => '⋉', '⋊' => '⋊', '⋋' => '⋋', '⋌' => '⋌', '⋍' => '⋍', '⋎' => '⋎', '⋏' => '⋏', '⋐' => '⋐', '⋑' => '⋑', '⋒' => '⋒', '⋓' => '⋓', '⋔' => '⋔', '⋕' => '⋕', '⋖' => '⋖', '⋗' => '⋗', '⋘' => '⋘', '⋘̸' => '&nLl', '⋙' => '⋙', '⋙̸' => '&nGg', '⋚' => '⋚', '⋚︀' => '&lesg', '⋛' => '⋛', '⋛︀' => '&gesl', '⋞' => '⋞', '⋟' => '⋟', '⋠' => '⋠', '⋡' => '⋡', '⋢' => '⋢', '⋣' => '⋣', '⋦' => '⋦', '⋧' => '⋧', '⋨' => '⋨', '⋩' => '⋩', '⋪' => '⋪', '⋫' => '⋫', '⋬' => '⋬', '⋭' => '⋭', '⋮' => '⋮', '⋯' => '⋯', '⋰' => '⋰', '⋱' => '⋱', '⋲' => '⋲', '⋳' => '⋳', '⋴' => '⋴', '⋵' => '⋵', '⋵̸' => '¬indot', '⋶' => '⋶', '⋷' => '⋷', '⋹' => '⋹', '⋹̸' => '¬inE', '⋺' => '⋺', '⋻' => '⋻', '⋼' => '⋼', '⋽' => '⋽', '⋾' => '⋾', '⌅' => '⌅', '⌆' => '⌆', '⌈' => '⌈', '⌉' => '⌉', '⌊' => '⌊', '⌋' => '⌋', '⌌' => '⌌', '⌍' => '⌍', '⌎' => '⌎', '⌏' => '⌏', '⌐' => '⌐', '⌒' => '⌒', '⌓' => '⌓', '⌕' => '⌕', '⌖' => '⌖', '⌜' => '⌜', '⌝' => '⌝', '⌞' => '⌞', '⌟' => '⌟', '⌢' => '⌢', '⌣' => '⌣', '⌭' => '⌭', '⌮' => '⌮', '⌶' => '⌶', '⌽' => '⌽', '⌿' => '⌿', '⍼' => '⍼', '⎰' => '⎰', '⎱' => '⎱', '⎴' => '⎴', '⎵' => '⎵', '⎶' => '⎶', '⏜' => '⏜', '⏝' => '⏝', '⏞' => '⏞', '⏟' => '⏟', '⏢' => '⏢', '⏧' => '⏧', '␣' => '␣', 'Ⓢ' => 'Ⓢ', '─' => '─', '│' => '│', '┌' => '┌', '┐' => '┐', '└' => '└', '┘' => '┘', '├' => '├', '┤' => '┤', '┬' => '┬', '┴' => '┴', '┼' => '┼', '═' => '═', '║' => '║', '╒' => '╒', '╓' => '╓', '╔' => '╔', '╕' => '╕', '╖' => '╖', '╗' => '╗', '╘' => '╘', '╙' => '╙', '╚' => '╚', '╛' => '╛', '╜' => '╜', '╝' => '╝', '╞' => '╞', '╟' => '╟', '╠' => '╠', '╡' => '╡', '╢' => '╢', '╣' => '╣', '╤' => '╤', '╥' => '╥', '╦' => '╦', '╧' => '╧', '╨' => '╨', '╩' => '╩', '╪' => '╪', '╫' => '╫', '╬' => '╬', '▀' => '▀', '▄' => '▄', '█' => '█', '░' => '░', '▒' => '▒', '▓' => '▓', '□' => '□', '▪' => '▪', '▫' => '▫', '▭' => '▭', '▮' => '▮', '▱' => '▱', '△' => '△', '▴' => '▴', '▵' => '▵', '▸' => '▸', '▹' => '▹', '▽' => '▽', '▾' => '▾', '▿' => '▿', '◂' => '◂', '◃' => '◃', '◊' => '◊', '○' => '○', '◬' => '◬', '◯' => '◯', '◸' => '◸', '◹' => '◹', '◺' => '◺', '◻' => '◻', '◼' => '◼', '★' => '★', '☆' => '☆', '☎' => '☎', '♀' => '♀', '♂' => '♂', '♠' => '♠', '♣' => '♣', '♥' => '♥', '♦' => '♦', '♪' => '♪', '♭' => '♭', '♮' => '♮', '♯' => '♯', '✓' => '✓', '✗' => '✗', '✠' => '✠', '✶' => '✶', '❘' => '❘', '❲' => '❲', '❳' => '❳', '⟈' => '⟈', '⟉' => '⟉', '⟦' => '⟦', '⟧' => '⟧', '⟨' => '⟨', '⟩' => '⟩', '⟪' => '⟪', '⟫' => '⟫', '⟬' => '⟬', '⟭' => '⟭', '⟵' => '⟵', '⟶' => '⟶', '⟷' => '⟷', '⟸' => '⟸', '⟹' => '⟹', '⟺' => '⟺', '⟼' => '⟼', '⟿' => '⟿', '⤂' => '⤂', '⤃' => '⤃', '⤄' => '⤄', '⤅' => '⤅', '⤌' => '⤌', '⤍' => '⤍', '⤎' => '⤎', '⤏' => '⤏', '⤐' => '⤐', '⤑' => '⤑', '⤒' => '⤒', '⤓' => '⤓', '⤖' => '⤖', '⤙' => '⤙', '⤚' => '⤚', '⤛' => '⤛', '⤜' => '⤜', '⤝' => '⤝', '⤞' => '⤞', '⤟' => '⤟', '⤠' => '⤠', '⤣' => '⤣', '⤤' => '⤤', '⤥' => '⤥', '⤦' => '⤦', '⤧' => '⤧', '⤨' => '⤨', '⤩' => '⤩', '⤪' => '⤪', '⤳' => '⤳', '⤳̸' => '&nrarrc', '⤵' => '⤵', '⤶' => '⤶', '⤷' => '⤷', '⤸' => '⤸', '⤹' => '⤹', '⤼' => '⤼', '⤽' => '⤽', '⥅' => '⥅', '⥈' => '⥈', '⥉' => '⥉', '⥊' => '⥊', '⥋' => '⥋', '⥎' => '⥎', '⥏' => '⥏', '⥐' => '⥐', '⥑' => '⥑', '⥒' => '⥒', '⥓' => '⥓', '⥔' => '⥔', '⥕' => '⥕', '⥖' => '⥖', '⥗' => '⥗', '⥘' => '⥘', '⥙' => '⥙', '⥚' => '⥚', '⥛' => '⥛', '⥜' => '⥜', '⥝' => '⥝', '⥞' => '⥞', '⥟' => '⥟', '⥠' => '⥠', '⥡' => '⥡', '⥢' => '⥢', '⥣' => '⥣', '⥤' => '⥤', '⥥' => '⥥', '⥦' => '⥦', '⥧' => '⥧', '⥨' => '⥨', '⥩' => '⥩', '⥪' => '⥪', '⥫' => '⥫', '⥬' => '⥬', '⥭' => '⥭', '⥮' => '⥮', '⥯' => '⥯', '⥰' => '⥰', '⥱' => '⥱', '⥲' => '⥲', '⥳' => '⥳', '⥴' => '⥴', '⥵' => '⥵', '⥶' => '⥶', '⥸' => '⥸', '⥹' => '⥹', '⥻' => '⥻', '⥼' => '⥼', '⥽' => '⥽', '⥾' => '⥾', '⥿' => '⥿', '⦅' => '⦅', '⦆' => '⦆', '⦋' => '⦋', '⦌' => '⦌', '⦍' => '⦍', '⦎' => '⦎', '⦏' => '⦏', '⦐' => '⦐', '⦑' => '⦑', '⦒' => '⦒', '⦓' => '⦓', '⦔' => '⦔', '⦕' => '⦕', '⦖' => '⦖', '⦚' => '⦚', '⦜' => '⦜', '⦝' => '⦝', '⦤' => '⦤', '⦥' => '⦥', '⦦' => '⦦', '⦧' => '⦧', '⦨' => '⦨', '⦩' => '⦩', '⦪' => '⦪', '⦫' => '⦫', '⦬' => '⦬', '⦭' => '⦭', '⦮' => '⦮', '⦯' => '⦯', '⦰' => '⦰', '⦱' => '⦱', '⦲' => '⦲', '⦳' => '⦳', '⦴' => '⦴', '⦵' => '⦵', '⦶' => '⦶', '⦷' => '⦷', '⦹' => '⦹', '⦻' => '⦻', '⦼' => '⦼', '⦾' => '⦾', '⦿' => '⦿', '⧀' => '⧀', '⧁' => '⧁', '⧂' => '⧂', '⧃' => '⧃', '⧄' => '⧄', '⧅' => '⧅', '⧉' => '⧉', '⧍' => '⧍', '⧎' => '⧎', '⧏' => '⧏', '⧏̸' => '&NotLeftTriangleBar', '⧐' => '⧐', '⧐̸' => '&NotRightTriangleBar', '⧜' => '⧜', '⧝' => '⧝', '⧞' => '⧞', '⧣' => '⧣', '⧤' => '⧤', '⧥' => '⧥', '⧫' => '⧫', '⧴' => '⧴', '⧶' => '⧶', '⨀' => '⨀', '⨁' => '⨁', '⨂' => '⨂', '⨄' => '⨄', '⨆' => '⨆', '⨌' => '⨌', '⨍' => '⨍', '⨐' => '⨐', '⨑' => '⨑', '⨒' => '⨒', '⨓' => '⨓', '⨔' => '⨔', '⨕' => '⨕', '⨖' => '⨖', '⨗' => '⨗', '⨢' => '⨢', '⨣' => '⨣', '⨤' => '⨤', '⨥' => '⨥', '⨦' => '⨦', '⨧' => '⨧', '⨩' => '⨩', '⨪' => '⨪', '⨭' => '⨭', '⨮' => '⨮', '⨯' => '⨯', '⨰' => '⨰', '⨱' => '⨱', '⨳' => '⨳', '⨴' => '⨴', '⨵' => '⨵', '⨶' => '⨶', '⨷' => '⨷', '⨸' => '⨸', '⨹' => '⨹', '⨺' => '⨺', '⨻' => '⨻', '⨼' => '⨼', '⨿' => '⨿', '⩀' => '⩀', '⩂' => '⩂', '⩃' => '⩃', '⩄' => '⩄', '⩅' => '⩅', '⩆' => '⩆', '⩇' => '⩇', '⩈' => '⩈', '⩉' => '⩉', '⩊' => '⩊', '⩋' => '⩋', '⩌' => '⩌', '⩍' => '⩍', '⩐' => '⩐', '⩓' => '⩓', '⩔' => '⩔', '⩕' => '⩕', '⩖' => '⩖', '⩗' => '⩗', '⩘' => '⩘', '⩚' => '⩚', '⩛' => '⩛', '⩜' => '⩜', '⩝' => '⩝', '⩟' => '⩟', '⩦' => '⩦', '⩪' => '⩪', '⩭' => '⩭', '⩭̸' => '&ncongdot', '⩮' => '⩮', '⩯' => '⩯', '⩰' => '⩰', '⩰̸' => '&napE', '⩱' => '⩱', '⩲' => '⩲', '⩳' => '⩳', '⩴' => '⩴', '⩵' => '⩵', '⩷' => '⩷', '⩸' => '⩸', '⩹' => '⩹', '⩺' => '⩺', '⩻' => '⩻', '⩼' => '⩼', '⩽' => '⩽', '⩽̸' => '&nles', '⩾' => '⩾', '⩾̸' => '&nges', '⩿' => '⩿', '⪀' => '⪀', '⪁' => '⪁', '⪂' => '⪂', '⪃' => '⪃', '⪄' => '⪄', '⪅' => '⪅', '⪆' => '⪆', '⪇' => '⪇', '⪈' => '⪈', '⪉' => '⪉', '⪊' => '⪊', '⪋' => '⪋', '⪌' => '⪌', '⪍' => '⪍', '⪎' => '⪎', '⪏' => '⪏', '⪐' => '⪐', '⪑' => '⪑', '⪒' => '⪒', '⪓' => '⪓', '⪔' => '⪔', '⪕' => '⪕', '⪖' => '⪖', '⪗' => '⪗', '⪘' => '⪘', '⪙' => '⪙', '⪚' => '⪚', '⪝' => '⪝', '⪞' => '⪞', '⪟' => '⪟', '⪠' => '⪠', '⪡' => '⪡', '⪡̸' => '&NotNestedLessLess', '⪢' => '⪢', '⪢̸' => '&NotNestedGreaterGreater', '⪤' => '⪤', '⪥' => '⪥', '⪦' => '⪦', '⪧' => '⪧', '⪨' => '⪨', '⪩' => '⪩', '⪪' => '⪪', '⪫' => '⪫', '⪬' => '⪬', '⪬︀' => '&smtes', '⪭' => '⪭', '⪭︀' => '&lates', '⪮' => '⪮', '⪯' => '⪯', '⪯̸' => '&NotPrecedesEqual', '⪰' => '⪰', '⪰̸' => '&NotSucceedsEqual', '⪳' => '⪳', '⪴' => '⪴', '⪵' => '⪵', '⪶' => '⪶', '⪷' => '⪷', '⪸' => '⪸', '⪹' => '⪹', '⪺' => '⪺', '⪻' => '⪻', '⪼' => '⪼', '⪽' => '⪽', '⪾' => '⪾', '⪿' => '⪿', '⫀' => '⫀', '⫁' => '⫁', '⫂' => '⫂', '⫃' => '⫃', '⫄' => '⫄', '⫅' => '⫅', '⫅̸' => '&nsubE', '⫆' => '⫆', '⫆̸' => '&nsupseteqq', '⫇' => '⫇', '⫈' => '⫈', '⫋' => '⫋', '⫋︀' => '&vsubnE', '⫌' => '⫌', '⫌︀' => '&varsupsetneqq', '⫏' => '⫏', '⫐' => '⫐', '⫑' => '⫑', '⫒' => '⫒', '⫓' => '⫓', '⫔' => '⫔', '⫕' => '⫕', '⫖' => '⫖', '⫗' => '⫗', '⫘' => '⫘', '⫙' => '⫙', '⫚' => '⫚', '⫛' => '⫛', '⫤' => '⫤', '⫦' => '⫦', '⫧' => '⫧', '⫨' => '⫨', '⫩' => '⫩', '⫫' => '⫫', '⫬' => '⫬', '⫭' => '⫭', '⫮' => '⫮', '⫯' => '⫯', '⫰' => '⫰', '⫱' => '⫱', '⫲' => '⫲', '⫳' => '⫳', '⫽︀' => '&varsupsetneqq', 'ff' => 'ff', 'fi' => 'fi', 'fl' => 'fl', 'ffi' => 'ffi', 'ffl' => 'ffl', '𝒜' => '𝒜', '𝒞' => '𝒞', '𝒟' => '𝒟', '𝒢' => '𝒢', '𝒥' => '𝒥', '𝒦' => '𝒦', '𝒩' => '𝒩', '𝒪' => '𝒪', '𝒫' => '𝒫', '𝒬' => '𝒬', '𝒮' => '𝒮', '𝒯' => '𝒯', '𝒰' => '𝒰', '𝒱' => '𝒱', '𝒲' => '𝒲', '𝒳' => '𝒳', '𝒴' => '𝒴', '𝒵' => '𝒵', '𝒶' => '𝒶', '𝒷' => '𝒷', '𝒸' => '𝒸', '𝒹' => '𝒹', '𝒻' => '𝒻', '𝒽' => '𝒽', '𝒾' => '𝒾', '𝒿' => '𝒿', '𝓀' => '𝓀', '𝓁' => '𝓁', '𝓂' => '𝓂', '𝓃' => '𝓃', '𝓅' => '𝓅', '𝓆' => '𝓆', '𝓇' => '𝓇', '𝓈' => '𝓈', '𝓉' => '𝓉', '𝓊' => '𝓊', '𝓋' => '𝓋', '𝓌' => '𝓌', '𝓍' => '𝓍', '𝓎' => '𝓎', '𝓏' => '𝓏', '𝔄' => '𝔄', '𝔅' => '𝔅', '𝔇' => '𝔇', '𝔈' => '𝔈', '𝔉' => '𝔉', '𝔊' => '𝔊', '𝔍' => '𝔍', '𝔎' => '𝔎', '𝔏' => '𝔏', '𝔐' => '𝔐', '𝔑' => '𝔑', '𝔒' => '𝔒', '𝔓' => '𝔓', '𝔔' => '𝔔', '𝔖' => '𝔖', '𝔗' => '𝔗', '𝔘' => '𝔘', '𝔙' => '𝔙', '𝔚' => '𝔚', '𝔛' => '𝔛', '𝔜' => '𝔜', '𝔞' => '𝔞', '𝔟' => '𝔟', '𝔠' => '𝔠', '𝔡' => '𝔡', '𝔢' => '𝔢', '𝔣' => '𝔣', '𝔤' => '𝔤', '𝔥' => '𝔥', '𝔦' => '𝔦', '𝔧' => '𝔧', '𝔨' => '𝔨', '𝔩' => '𝔩', '𝔪' => '𝔪', '𝔫' => '𝔫', '𝔬' => '𝔬', '𝔭' => '𝔭', '𝔮' => '𝔮', '𝔯' => '𝔯', '𝔰' => '𝔰', '𝔱' => '𝔱', '𝔲' => '𝔲', '𝔳' => '𝔳', '𝔴' => '𝔴', '𝔵' => '𝔵', '𝔶' => '𝔶', '𝔷' => '𝔷', '𝔸' => '𝔸', '𝔹' => '𝔹', '𝔻' => '𝔻', '𝔼' => '𝔼', '𝔽' => '𝔽', '𝔾' => '𝔾', '𝕀' => '𝕀', '𝕁' => '𝕁', '𝕂' => '𝕂', '𝕃' => '𝕃', '𝕄' => '𝕄', '𝕆' => '𝕆', '𝕊' => '𝕊', '𝕋' => '𝕋', '𝕌' => '𝕌', '𝕍' => '𝕍', '𝕎' => '𝕎', '𝕏' => '𝕏', '𝕐' => '𝕐', '𝕒' => '𝕒', '𝕓' => '𝕓', '𝕔' => '𝕔', '𝕕' => '𝕕', '𝕖' => '𝕖', '𝕗' => '𝕗', '𝕘' => '𝕘', '𝕙' => '𝕙', '𝕚' => '𝕚', '𝕛' => '𝕛', '𝕜' => '𝕜', '𝕝' => '𝕝', '𝕞' => '𝕞', '𝕟' => '𝕟', '𝕠' => '𝕠', '𝕡' => '𝕡', '𝕢' => '𝕢', '𝕣' => '𝕣', '𝕤' => '𝕤', '𝕥' => '𝕥', '𝕦' => '𝕦', '𝕧' => '𝕧', '𝕨' => '𝕨', '𝕩' => '𝕩', '𝕪' => '𝕪', '𝕫' => '𝕫', ); } namespace AimySpeedOptimization\Masterminds\HTML5\Serializer; class Traverser { protected static $local_ns = array( 'http://www.w3.org/1999/xhtml' => 'html', 'http://www.w3.org/1998/Math/MathML' => 'math', 'http://www.w3.org/2000/svg' => 'svg', ); protected $dom; protected $options; protected $encode = false; protected $rules; protected $out; public function __construct($dom, $out, RulesInterface $rules, $options = array()) { $this->dom = $dom; $this->out = $out; $this->rules = $rules; $this->options = $options; $this->rules->setTraverser($this); } public function walk() { if ($this->dom instanceof \DOMDocument) { $this->rules->document($this->dom); } elseif ($this->dom instanceof \DOMDocumentFragment) { if ($this->dom->hasChildNodes()) { $this->children($this->dom->childNodes); } } elseif ($this->dom instanceof \DOMNodeList) { $this->children($this->dom); } else { $this->node($this->dom); } return $this->out; } public function node($node) { switch ($node->nodeType) { case XML_ELEMENT_NODE: $this->rules->element($node); break; case XML_TEXT_NODE: $this->rules->text($node); break; case XML_CDATA_SECTION_NODE: $this->rules->cdata($node); break; case XML_PI_NODE: $this->rules->processorInstruction($node); break; case XML_COMMENT_NODE: $this->rules->comment($node); break; default: break; } } public function children($nl) { foreach ($nl as $node) { $this->node($node); } } public function isLocalElement($ele) { $uri = $ele->namespaceURI; if (empty($uri)) { return false; } return isset(static::$local_ns[$uri]); } } namespace AimySpeedOptimization\Masterminds\HTML5\Serializer; interface RulesInterface { public function __construct($output, $options = array()); public function setTraverser(Traverser $traverser); public function document($dom); public function element($ele); public function text($ele); public function cdata($ele); public function comment($ele); public function processorInstruction($ele); } namespace AimySpeedOptimization\Masterminds\HTML5\Serializer; use AimySpeedOptimization\Masterminds\HTML5\Elements; class OutputRules implements RulesInterface { const NAMESPACE_HTML = 'http://www.w3.org/1999/xhtml'; const NAMESPACE_MATHML = 'http://www.w3.org/1998/Math/MathML'; const NAMESPACE_SVG = 'http://www.w3.org/2000/svg'; const NAMESPACE_XLINK = 'http://www.w3.org/1999/xlink'; const NAMESPACE_XML = 'http://www.w3.org/XML/1998/namespace'; const NAMESPACE_XMLNS = 'http://www.w3.org/2000/xmlns/'; protected $implicitNamespaces = array( self::NAMESPACE_HTML, self::NAMESPACE_SVG, self::NAMESPACE_MATHML, self::NAMESPACE_XML, self::NAMESPACE_XMLNS, ); const IM_IN_HTML = 1; const IM_IN_SVG = 2; const IM_IN_MATHML = 3; private $hasHTML5 = false; protected $traverser; protected $encode = false; protected $out; protected $outputMode; private $xpath; protected $nonBooleanAttributes = array( array( 'nodeNamespace' => 'http://www.w3.org/1999/xhtml', 'attrName' => array('href', 'hreflang', 'http-equiv', 'icon', 'id', 'keytype', 'kind', 'label', 'lang', 'language', 'list', 'maxlength', 'media', 'method', 'name', 'placeholder', 'rel', 'rows', 'rowspan', 'sandbox', 'spellcheck', 'scope', 'seamless', 'shape', 'size', 'sizes', 'span', 'src', 'srcdoc', 'srclang', 'srcset', 'start', 'step', 'style', 'summary', 'tabindex', 'target', 'title', 'type', 'value', 'width', 'border', 'charset', 'cite', 'class', 'code', 'codebase', 'color', 'cols', 'colspan', 'content', 'coords', 'data', 'datetime', 'default', 'dir', 'dirname', 'enctype', 'for', 'form', 'formaction', 'headers', 'height', 'accept', 'accept-charset', 'accesskey', 'action', 'align', 'alt', 'bgcolor', ), ), array( 'nodeNamespace' => 'http://www.w3.org/1999/xhtml', 'xpath' => 'starts-with(local-name(), \'data-\')', ), ); const DOCTYPE = '<!DOCTYPE html>'; public function __construct($output, $options = array()) { if (isset($options['encode_entities'])) { $this->encode = $options['encode_entities']; } $this->outputMode = static::IM_IN_HTML; $this->out = $output; $this->hasHTML5 = defined('ENT_HTML5'); } public function addRule(array $rule) { $this->nonBooleanAttributes[] = $rule; } public function setTraverser(Traverser $traverser) { $this->traverser = $traverser; return $this; } public function unsetTraverser() { $this->traverser = null; return $this; } public function document($dom) { $this->doctype(); if ($dom->documentElement) { foreach ($dom->childNodes as $node) { $this->traverser->node($node); } $this->nl(); } } protected function doctype() { $this->wr(static::DOCTYPE); $this->nl(); } public function element($ele) { $name = $ele->tagName; if ($this->traverser->isLocalElement($ele)) { $name = $ele->localName; } if ('svg' == $name) { $this->outputMode = static::IM_IN_SVG; $name = Elements::normalizeSvgElement($name); } elseif ('math' == $name) { $this->outputMode = static::IM_IN_MATHML; } $this->openTag($ele); if (Elements::isA($name, Elements::TEXT_RAW)) { foreach ($ele->childNodes as $child) { if ($child instanceof \DOMCharacterData) { $this->wr($child->data); } elseif ($child instanceof \DOMElement) { $this->element($child); } } } else { if ($ele->hasChildNodes()) { $this->traverser->children($ele->childNodes); } if ('svg' == $name || 'math' == $name) { $this->outputMode = static::IM_IN_HTML; } } if (!Elements::isA($name, Elements::VOID_TAG)) { $this->closeTag($ele); } } public function text($ele) { if (isset($ele->parentNode) && isset($ele->parentNode->tagName) && Elements::isA($ele->parentNode->localName, Elements::TEXT_RAW)) { $this->wr($ele->data); return; } $this->wr($this->enc($ele->data)); } public function cdata($ele) { $this->wr($ele->ownerDocument->saveXML($ele)); } public function comment($ele) { $this->wr($ele->ownerDocument->saveXML($ele)); } public function processorInstruction($ele) { $this->wr('<?') ->wr($ele->target) ->wr(' ') ->wr($ele->data) ->wr('?>'); } protected function namespaceAttrs($ele) { if (!$this->xpath || $this->xpath->document !== $ele->ownerDocument) { $this->xpath = new \DOMXPath($ele->ownerDocument); } foreach ($this->xpath->query('namespace::*[not(.=../../namespace::*)]', $ele) as $nsNode) { if (!in_array($nsNode->nodeValue, $this->implicitNamespaces)) { $this->wr(' ')->wr($nsNode->nodeName)->wr('="')->wr($nsNode->nodeValue)->wr('"'); } } } protected function openTag($ele) { $this->wr('<')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName); $this->attrs($ele); $this->namespaceAttrs($ele); if ($this->outputMode == static::IM_IN_HTML) { $this->wr('>'); } else { if ($ele->hasChildNodes()) { $this->wr('>'); } else { $this->wr(' />'); } } } protected function attrs($ele) { if (!$ele->hasAttributes()) { return $this; } $map = $ele->attributes; $len = $map->length; for ($i = 0; $i < $len; ++$i) { $node = $map->item($i); $val = $this->enc($node->value, true); $name = $node->nodeName; if ($this->outputMode == static::IM_IN_SVG) { $name = Elements::normalizeSvgAttribute($name); } elseif ($this->outputMode == static::IM_IN_MATHML) { $name = Elements::normalizeMathMlAttribute($name); } $this->wr(' ')->wr($name); if ((isset($val) && '' !== $val) || $this->nonBooleanAttribute($node)) { $this->wr('="')->wr($val)->wr('"'); } } } protected function nonBooleanAttribute(\DOMAttr $attr) { $ele = $attr->ownerElement; foreach ($this->nonBooleanAttributes as $rule) { if (isset($rule['nodeNamespace']) && $rule['nodeNamespace'] !== $ele->namespaceURI) { continue; } if (isset($rule['attNamespace']) && $rule['attNamespace'] !== $attr->namespaceURI) { continue; } if (isset($rule['nodeName']) && !is_array($rule['nodeName']) && $rule['nodeName'] !== $ele->localName) { continue; } if (isset($rule['nodeName']) && is_array($rule['nodeName']) && !in_array($ele->localName, $rule['nodeName'], true)) { continue; } if (isset($rule['attrName']) && !is_array($rule['attrName']) && $rule['attrName'] !== $attr->localName) { continue; } if (isset($rule['attrName']) && is_array($rule['attrName']) && !in_array($attr->localName, $rule['attrName'], true)) { continue; } if (isset($rule['xpath'])) { $xp = $this->getXPath($attr); if (isset($rule['prefixes'])) { foreach ($rule['prefixes'] as $nsPrefix => $ns) { $xp->registerNamespace($nsPrefix, $ns); } } if (!$xp->evaluate($rule['xpath'], $attr)) { continue; } } return true; } return false; } private function getXPath(\DOMNode $node) { if (!$this->xpath) { $this->xpath = new \DOMXPath($node->ownerDocument); } return $this->xpath; } protected function closeTag($ele) { if ($this->outputMode == static::IM_IN_HTML || $ele->hasChildNodes()) { $this->wr('</')->wr($this->traverser->isLocalElement($ele) ? $ele->localName : $ele->tagName)->wr('>'); } } protected function wr($text) { fwrite($this->out, $text); return $this; } protected function nl() { fwrite($this->out, PHP_EOL); return $this; } protected function enc($text, $attribute = false) { if (!$this->encode) { return $this->escape($text, $attribute); } if ($this->hasHTML5) { return htmlentities($text, ENT_HTML5 | ENT_SUBSTITUTE | ENT_QUOTES, 'UTF-8', false); } else { return strtr($text, HTML5Entities::$map); } } protected function escape($text, $attribute = false) { if ($attribute) { $replace = array( '"' => '"', '&' => '&', "\xc2\xa0" => ' ', ); } else { $replace = array( '<' => '<', '>' => '>', '&' => '&', "\xc2\xa0" => ' ', ); } return strtr($text, $replace); } }
index.html 0000644 00000000016 15234451223 0006537 0 ustar 00 <html></html>
install-hints.php 0000644 00000013143 15234451223 0010051 0 ustar 00 <?php
/*
* Copyright (c) 2017-2024 Aimy Extensions, Netzum Sorglos Software GmbH
* Copyright (c) 2015-2017 Aimy Extensions, Lingua-Systems Software GmbH
*
* https://www.aimy-extensions.com/
*
* License: GNU GPLv2, see LICENSE.txt within distribution and/or
* https://www.aimy-extensions.com/software-license.html
*/
defined( '_JEXEC' ) or die(); use Joomla\CMS\Factory; use Joomla\CMS\Uri\Uri; use Joomla\CMS\Language\Text; use Joomla\CMS\Router\Route; use Joomla\Registry\Registry; class plgSystemAimySpeedOptimizationInstallerScript { const V_JOOMLA_MIN = '3.9.0'; const V_PHP_MIN = '7.0.0'; public function preflight( $type, $adapter ) { $requiredPhpExts = array( 'dom', 'mbstring' ); foreach ( $requiredPhpExts as $ext ) { if ( ! extension_loaded( $ext ) ) { Factory::getApplication()->enqueueMessage( 'Aimy Speed Optimization requires the ' . '<b>' . ucfirst( $ext ) . ' PHP extension</b> to ' . 'be installed which is not the case on your system. ' . '<b>Please install the extension first!</b>', 'error' ); return false; } } return ( self::check_php_version( self::V_PHP_MIN ) && self::check_joomla_version( self::V_JOOMLA_MIN ) ); } public function postflight( $route, $adapter ) { $task = strtolower( $route ); if ( $task != 'install' && $task != 'update' ) { return; } Factory::getApplication()->setUserState( 'htaccess_state', false, 'aimyspeedoptimization' ); if ( $task == 'update' ) { if ( strpos( JVERSION, '3.' ) !== 0 ) { try { self::deleteExtensionParam( 'dl_key' ); } catch ( Exception $e ) { error_log( 'AimySpeedOptimization: ' . $e->getMessage() ); } } } if ( $task == 'install' ) { Factory::getLanguage()->load( 'plg_system_aimyspeedoptimization', JPATH_ADMINISTRATOR ); echo '<div style="padding:32px;text-align:center;">', '<h1>', '<img src="', Uri::base(), '../media/plg_aimyspeedoptimization/aimy-logo_340x327.png" ', 'width="340" height="327" alt="Aimy" />', '<br/>', 'Aimy Speed Optimization (PRO) v21.0', '</h1>', '<p class="lead">', Text::_( $task == 'update' ? 'AIMY_SO_HINT_UPDATED' : 'AIMY_SO_HINT_INSTALLED' ), '!', '</p>'; $cfg_url = false; $enabled = false; try { $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( array( 'extension_id', 'enabled' ) ) ) ->from( $db->quoteName( '#__extensions' ) ) ->where( $db->quoteName( 'element' ) . ' = ' . $db->quote( 'aimyspeedoptimization' ) ) ->where( $db->quoteName( 'type' ) . ' = ' . $db->quote( 'plugin' ) ); $db->setQuery( $q ); $row = $db->loadObject(); if ( $row && $row->extension_id ) { $cfg_url = Route::_( 'index.php?' . 'option=com_plugins&task=plugin.edit&' . 'extension_id=' . $row->extension_id ); $enabled = $row->enabled; } } catch ( Exception $e ) {} echo '<p>'; if ( $cfg_url ) { echo self::_btn( $cfg_url, Text::_( ! $enabled ? 'AIMY_SO_HINT_ENABLE_CONFIGURE' : 'AIMY_SO_HINT_CONFIGURE' ) ), ' '; } echo self::_btn( 'https://www.aimy-extensions.com/joomla/speed-optimization.html#user-manual', Text::_( 'AIMY_SO_HINT_READ_MANUAL' ), true ), ' ', self::_btn( 'https://aimy-extensions.com/images/products/speed-optimization/plg-aimy-speed-optimization.pdf?v=21.0', Text::_( 'AIMY_SO_HINT_DL_MANUAL' ), false ), '</p>'; echo '<p style="padding:12px 0;" />', '<a href="https://www.aimy-extensions.com/joomla/speed-optimization.html" target="_blank">https://www.aimy-extensions.com/joomla/speed-optimization.html</a>', '</p>', '</div>', "\n"; } } public function uninstall( $parent ) { require_once( JPATH_ROOT . '/plugins/system/aimyspeedoptimization/HtaccessHelper.php' ); $app = Factory::getApplication(); if ( AimySpeedOptimizationHtaccessHelper::htaccess_exists() ) { try { AimySpeedOptimizationHtaccessHelper::cleanup(); Factory::getLanguage()->load( 'plg_system_aimyspeedoptimization', JPATH_ADMINISTRATOR ); $app->enqueueMessage( 'Aimy Speed Optimization: ' . Text::_( 'AIMY_SO_MSG_HTACCESS_UPDATED' ) ); } catch ( Exception $e ) { $app->enqueueMessage( 'Aimy Speed Optimization: ' . $e->getMessage(), 'error' ); } } return true; } static private function _btn( $url, $text, $new_tab = false ) { return '<a class="btn btn-lg btn-success" href="' . $url . '" ' . ( $new_tab ? 'target="_blank" ' : '' ) . 'role="button" style="color:#FFF">' . $text . '!' . '</a>'; } static private function check_php_version( $min ) { if ( version_compare( PHP_VERSION, $min, '<' ) ) { Factory::getApplication()->enqueueMessage( 'You are currently using PHP ' . PHP_VERSION . ', ' . 'but Aimy Speed Optimization requires at least PHP ' . $min . '.', 'error' ); return false; } return true; } static private function check_joomla_version( $min ) { $jv = defined( 'JVERSION' ) ? JVERSION : 0; if ( version_compare( $jv, $min, '<' ) ) { Factory::getApplication()->enqueueMessage( 'You are currently using Joomla! ' . $jv . ', ' . 'but Aimy Speed Optimization requires at least Joomla! ' . $min . '.', 'error' ); return false; } return true; } static private function deleteExtensionParam( $name ) { $element = ( 'plg' == 'com' ? 'com_' : '' ) . 'aimyspeedoptimization'; $db = Factory::getDbo(); $q = $db->getQuery( true ); $q->select( $db->quoteName( 'params' ) ) ->from( $db->quoteName( '#__extensions' ) ) ->where( $db->quoteName( 'element' ) . ' = ' . $db->quote( $element ) ); $db->setQuery( $q ); $rv = $db->loadResult(); if ( empty( $rv ) ) { return false; } $params = new Registry(); $params->loadString( $rv ); if ( ! $params or ! $params->exists( $name ) ) { return false; } $params->remove( $name ); $q = $db->getQuery( true ); $q->update( $db->quoteName( '#__extensions' ) ) ->set( $db->quoteName( 'params' ) . ' = ' . $db->quote( $params->toString() ) ) ->where( $db->quoteName( 'element' ) . ' = ' . $db->quote( $element ) ); $db->setQuery( $q ); return $db->execute(); } }