Method for filtering the media's 'width' and 'height' attributes so
that the theme can handle the dimensions how it sees fit.

@since  5.0.0
@access protected
@param  string  $html
@return string
504810
protected function filterDimensions( $html ) {

		$media_atts = [];
		$_html      = strip_tags( $html, '<object><embed><iframe><video>' );

		// Find the attributes of the media.
		$atts = wp_kses_hair( $_html, [ 'http', 'https' ] );

		// Loop through the media attributes and add them in key/value pairs.
		foreach ( $atts as $att ) {
			$media_atts[ $att['name'] ] = $att['value'];
		}

		// If no dimensions are found, just return the HTML.
		if ( empty( $media_atts ) || ! isset( $media_atts['width'] ) || ! isset( $media_atts['height'] ) ) {
			return $html;
		}

		// Set the max width.
		$max_width = $this->width;

		// Set the max height based on the max width and original width/height ratio.
		$max_height = round( $max_width / ( $media_atts['width'] / $media_atts['height'] ) );

		// Fix for Spotify embeds.
		if ( ! empty( $media_atts['src'] ) && preg_match( '#https?://(embed)\.spotify\.com/.*#i', $media_atts['src'], $matches ) ) {

			list( $max_width, $max_height ) = $this->spotifyDimensions( $media_atts );
		}

		// Calculate new media dimensions.
		$dimensions = wp_expand_dimensions(
			$media_atts['width'],
			$media_atts['height'],
			$max_width,
			$max_height
		);

		// Allow devs to filter the final width and height of the media.
		list( $width, $height ) = apply_filters(
			'hybrid/media/grabber/dimensions',
			$dimensions,                       // width/height array
			$media_atts,                       // media HTML attributes
			$this                              // media grabber object
		);

		// Set up the patterns for the 'width' and 'height' attributes.
		$patterns = [
			'/(width=[\'"]).+?([\'"])/i',
			'/(height=[\'"]).+?([\'"])/i',
			'/(<div.+?style=[\'"].*?width:.+?).+?(px;.+?[\'"].*?>)/i',
			'/(<div.+?style=[\'"].*?height:.+?).+?(px;.+?[\'"].*?>)/i'
		];

		// Set up the replacements for the 'width' and 'height' attributes.
		$replacements = [
			'${1}' . $width . '${2}',
			'${1}' . $height . '${2}',
			'${1}' . $width . '${2}',
			'${1}' . $height . '${2}',
		];

		// Filter the dimensions and return the media HTML.
		return preg_replace( $patterns, $replacements, $html );
	}
1070321
private function parseContents(string $html): string
    {
        if (\trim($html) === '') {
            return '';
        }

        // Determine value.
        $this->value = $html;
        if (\preg_match("/(.*)<\/\s*" . $this->name . "\s*>/iU", $html, $valueMatches) === 1) {
            $this->value = $valueMatches[1];
        }

        // Don't parse contents of "iframe" element.
        if ($this->name === 'iframe') {
            return $this->parseNoContents('iframe', $html);
        }

        // Only TEXT inside a "script" element.
        if ($this->name === 'script') {
            return $this->parseForeignContents('script', $html);
        }

        // Only TEXT inside a "style" element.
        if ($this->name === 'style') {
            return $this->parseForeignContents('style', $html);
        }

        // Parse contents one token at a time.
        $remainingHtml = $html;
        while ($this->isAnotherTokenPresent($remainingHtml)) {
            $token = Tokenizer::buildFromHtml($remainingHtml, $this);

            if (!$token instanceof TokenInterface || $token->isClosingElementImplied($remainingHtml)) {
                return $remainingHtml;
            }

            $remainingHtml = $token->parse($remainingHtml);
            $this->children[] = $token;
        }

        $this->removeLastTokenIfContainsOnlyWhitespace();

        // Remove remaining closing tag.
        $posOfClosingBracket = \mb_strpos($remainingHtml, '>');

        return \mb_substr($remainingHtml, $posOfClosingBracket + 1);
    }
1073460
protected function tidy( $html ) {

		// Venn diagram join between XTHML + HTML5 Deprecated Attributes
		//
		// Our $spec is artisanally hand crafted based on squinting very hard while reading the following docs:
		//
		//  + 2.3 - Extra HTML specifications using the $spec parameter
		//  + 3.4.6 -  Transformation of deprecated attributes
		//  + 3.3.2  - Tag-transformation for better compliance with standards
		//  + HTML5 - Deprecated Tags & Attributes
		//
		// That is we do not remove deprecated attributes that are already transformed by htmLawed
		//
		// More info:
		//  + http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/beta/htmLawed_README.htm
		//  + http://www.tutorialspoint.com/html5/html5_deprecated_tags.htm

		$config = [
			'valid_xhtml' => 1,
			'no_deprecated_attr' => 2,
			'unique_ids' => 'fixme-',
			'hook' => '\Pressbooks\Sanitize\html5_to_epub3',
			'tidy' => -1,
			'make_tag_strict' => 2,
			'comment' => 1,
		];

		$spec = '';
		$spec .= 'a=,-charset,-coords,-rev,-shape;';
		$spec .= 'area=-nohref;';
		$spec .= 'col=-align,-char,-charoff,-valign,-width;';
		$spec .= 'colgroup=-align,-char,-charoff,-valign,-width;';
		$spec .= 'div=-align;';
		$spec .= 'iframe=-align,-frameborder,-longdesc,-marginheight,-marginwidth,-scrolling;';
		$spec .= 'img=-longdesc,-srcset;';
		$spec .= 'link=-charset,-rev,-target;';
		$spec .= 'menu=-compact;';
		$spec .= 'object=-archive,-classid,-codebase,-codetype,-declare,-standby;';
		$spec .= 'param=-type,-valuetype;';
		$spec .= 't=-abbr,-axis;';
		$spec .= 'table=-border,-cellpadding,-frame,-rules;';
		$spec .= 'tbody=-align,-char,-charoff,-valign;';
		$spec .= 'td=-axis,-abbr,-align,-char,-charoff,-scope,-valign;';
		$spec .= 'tfoot=-align,-char,-charoff,-valign;';
		$spec .= 'th=-align,-char,-charoff,-valign;';
		$spec .= 'thead=-align,-char,-charoff,-valign;';
		$spec .= 'tr=-align,-char,-charoff,-valign;';
		$spec .= 'ul=-type;';

		// Reset on each htmLawed invocation
		unset( $GLOBALS['hl_Ids'] );
		if ( ! empty( $this->fixme ) ) {
			$GLOBALS['hl_Ids'] = $this->fixme;
		}

		$html = HtmLawed::filter( $html, $config, $spec );

		return $html;
	}
201682
public function readability_html( $html )
	{
		$data = $this->post_call('/readability', ['html' => $html ] );

		return ( !empty($data['readability']) ) ? $data['readability'] : null;
	}
1314865
private function minifyHTML($html)
    {
        $search = array('/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\'|\")\/\/.*))/', '/\n/', '/\>[^\S ]+/s', '/[^\S ]+\</s', '/(\s)+/s', '/<!--.*?-->/');
        $replace = array(' ', ' ', '>', '<', '\\1', '');

        $squeezedHTML = preg_replace($search, $replace, $html);

        return $squeezedHTML;

    }
1133613
public function addAttributesToHtml($html)
    {
        $html = trim((string) $html);

        $attributes = array_merge($this->getRequiredAttributes(), $this->attributes());
        $attributesString = Html::attributes($attributes);

        return preg_replace('|<(.*?)>|', "<$1$attributesString>", $html, 1);
    }
1343814
public function minify($html, $decodeUtf8Specials = false): string
    {
        $html = (string) $html;
        if (!isset($html[0])) {
            return '';
        }

        $html = \trim($html);
        if (!$html) {
            return '';
        }

        // init
        static $CACHE_SELF_CLOSING_TAGS = null;
        if ($CACHE_SELF_CLOSING_TAGS === null) {
            $CACHE_SELF_CLOSING_TAGS = \implode('|', self::$selfClosingTags);
        }

        // reset
        $this->protectedChildNodes = [];

        // save old content
        $origHtml = $html;
        $origHtmlLength = \strlen($html);

        // -------------------------------------------------------------------------
        // Minify the HTML via "HtmlDomParser"
        // -------------------------------------------------------------------------

        if ($this->doOptimizeViaHtmlDomParser) {
            $html = $this->minifyHtmlDom($html, $decodeUtf8Specials);
        }

        // -------------------------------------------------------------------------
        // Trim whitespace from html-string. [protected html is still protected]
        // -------------------------------------------------------------------------

        // Remove extra white-space(s) between HTML attribute(s)
        $html = (string) \preg_replace_callback(
            '#<([^/\s<>!]+)(?:\s+([^<>]*?)\s*|\s*)(/?)>#',
            function ($matches) {
                return '<' . $matches[1] . (string) \preg_replace('#([^\s=]+)(\=([\'"]?)(.*?)\3)?(\s+|$)#s', ' $1$2', $matches[2]) . $matches[3] . '>';
            },
            $html
        );

        if ($this->doRemoveSpacesBetweenTags) {
            // Remove spaces that are between > and <
            $html = (string) \preg_replace('/(>) (<)/', '>$2', $html);
        }

        // -------------------------------------------------------------------------
        // Restore protected HTML-code.
        // -------------------------------------------------------------------------

        $html = (string) \preg_replace_callback(
            '/<(?<element>' . $this->protectedChildNodesHelper . ')(?<attributes> [^>]*)?>(?<value>.*?)<\/' . $this->protectedChildNodesHelper . '>/',
            [$this, 'restoreProtectedHtml'],
            $html
        );

        // -------------------------------------------------------------------------
        // Restore protected HTML-entities.
        // -------------------------------------------------------------------------

        if ($this->doOptimizeViaHtmlDomParser) {
            $html = HtmlDomParser::putReplacedBackToPreserveHtmlEntities($html);
        }

        // ------------------------------------
        // Final clean-up
        // ------------------------------------

        $html = \str_replace(
            [
                'html>' . "\n",
                "\n" . '<html',
                'html/>' . "\n",
                "\n" . '</html',
                'head>' . "\n",
                "\n" . '<head',
                'head/>' . "\n",
                "\n" . '</head',
            ],
            [
                'html>',
                '<html',
                'html/>',
                '</html',
                'head>',
                '<head',
                'head/>',
                '</head',
            ],
            $html
        );

        // self closing tags, don't need a trailing slash ...
        $replace = [];
        $replacement = [];
        foreach (self::$selfClosingTags as $selfClosingTag) {
            $replace[] = '<' . $selfClosingTag . '/>';
            $replacement[] = '<' . $selfClosingTag . '>';
            $replace[] = '<' . $selfClosingTag . ' />';
            $replacement[] = '<' . $selfClosingTag . '>';
        }
        $html = \str_replace(
            $replace,
            $replacement,
            $html
        );

        $html = (string) \preg_replace('#<\b(' . $CACHE_SELF_CLOSING_TAGS . ')([^>]*+)><\/\b\1>#', '<\\1\\2>', $html);

        // ------------------------------------
        // check if compression worked
        // ------------------------------------

        if ($origHtmlLength < \strlen($html)) {
            $html = $origHtml;
        }

        return $html;
    }
233870
public function transform($html)
    {
        if (class_exists('HTMLPurifier')) {
            $config = HTMLPurifier_Config::createDefault();
            $purifier = new HTMLPurifier($config);
            $cleanHtml = $purifier->purify($html);
        } else {
            $cleanHtml = strip_tags($html, '<a><b><br><i><p><s><u>');
        }

        return $cleanHtml;
    }
1013534
protected function regexSearchReplace( $html ) {

		/* cherry pick likely content areas */
		// HTML5, ungreedy
		preg_match( '/(?:<main[^>]*>)(.*)<\/main>/isU', $html, $matches );
		$html = ( ! empty( $matches[1] ) ) ? $matches[1] : $html;

		// WP content area, greedy
		preg_match( '/(?:<div id="main"[^>]*>)(.*)<\/div>/is', $html, $matches );
		$html = ( ! empty( $matches[1] ) ) ? $matches[1] : $html;

		// general content area, greedy
		preg_match( '/(?:<div id="content"[^>]*>)(.*)<\/div>/is', $html, $matches );
		$html = ( ! empty( $matches[1] ) ) ? $matches[1] : $html;

		// specific PB content area, greedy
		preg_match( '/(?:<div class="entry-content"[^>]*>)(.*)<\/div>/is', $html, $matches );
		$html = ( ! empty( $matches[1] ) ) ? $matches[1] : $html;

		/* cull */
		// get rid of script tags, ungreedy
		$result = preg_replace( '/(?:<script[^>]*>)(.*)<\/script>/isU', '', $html );
		// get rid of forms, ungreedy
		$result = preg_replace( '/(?:<form[^>]*>)(.*)<\/form>/isU', '', $result );
		// get rid of html5 nav content, ungreedy
		$result = preg_replace( '/(?:<nav[^>]*>)(.*)<\/nav>/isU', '', $result );
		// get rid of PB nav, next/previous
		$result = preg_replace( '/(?:<div class="nav"[^>]*>)(.*)<\/div>/isU', '', $result );
		// get rid of PB share buttons
		$result = preg_replace( '/(?:<div class="share-wrap-single"[^>]*>)(.*)<\/div>/isU', '', $result );
		// get rid of html5 footer content, ungreedy
		$result = preg_replace( '/(?:<footer[^>]*>)(.*)<\/footer>/isU', '', $result );
		// get rid of sidebar content, greedy
		$result = preg_replace( '/(?:<div id="sidebar\d{0,}"[^>]*>)(.*)<\/div>/is', '', $result );
		// get rid of comments, greedy
		$result = preg_replace( '/(?:<div id="comments"[^>]*>)(.*)<\/div>/is', '', $result );

		return $result;
	}
1005359
public function convertToString($html)
    {
        $extractedStylesheets = $this->extractExternalStylesheets($html);
        $links = $this->createStylesheetPaths($extractedStylesheets['links']);

        $externalStylesheets =  array(
            'tags' => $extractedStylesheets[0],
            'links' => $links
        );

        $html = $this->replaceExternalCss($html, $externalStylesheets);

        return $html;
    }
502629
public function parse($html)
    {
        if (!is_array($html)) {
            if (file_exists($html)) {
                $html = file($html);
            } else {
                return "Error opening template file $html";
            }
        }
        $return = '';
        foreach ($html as $line) {
            $return .= preg_replace_callback('/\<dynamic\s+name\s*=\s*[\'"](\S+?)[\'"]\>/i', [&$this, 'replace'], $line);
        }

        return $return;
    }
185374
public function purify($html, $allowedTags = null, $allowedAttributes = null)
    {
        $html = (string)preg_replace('@<script.*>.*</script>@isU', '', $html);
        if ($html === '') {
            return '';
        }

        if (strpos($html, '<body>') === false) {
            $html = '<!doctype html><html><body>' . $html . '</body></html>';
        }

        $doc = new \DOMDocument();
        try {
            @$doc->loadHTML($html);
        } catch (\Exception $e) {
            return '';
        }

        $body = $doc->getElementsByTagName('body');
        if ($allowedTags !== null) {
            $tags = ',' . implode(',', $allowedTags) . ',';
        } else {
            $tags = $this->_allowedTags;
        }

        if ($allowedAttributes !== null) {
            $attributes = ',' . implode(',', $allowedAttributes) . ',';
        } else {
            $attributes = $this->_allowedAttributes;
        }

        $this->_purify(iterator_to_array($body->item(0)->childNodes), $tags, $attributes);
        $body = $doc->getElementsByTagName('body');
        return substr($doc->saveHTML($body->item(0)), 6, -7);
    }
632516
protected static function strip_images($html) {
        // Load HTML and suppress any parsing errors (DOMDocument->loadHTML() does not current support HTML5 tags).
        $dom = new DOMDocument();
        libxml_use_internal_errors(true);
        $dom->loadHTML('<?xml version="1.0" encoding="UTF-8" ?>' . $html);
        libxml_clear_errors();

        // Find all img tags.
        if ($imgnodes = $dom->getElementsByTagName('img')) {
            // Replace img nodes with the img alt text without overriding DOM elements.
            for ($i = ($imgnodes->length - 1); $i >= 0; $i--) {
                $imgnode = $imgnodes->item($i);
                $alt = ($imgnode->hasAttribute('alt')) ? ' [ ' . $imgnode->getAttribute('alt') . ' ] ' : ' ';
                $textnode = $dom->createTextNode($alt);

                $imgnode->parentNode->replaceChild($textnode, $imgnode);
            }
        }
        $count = 1;
        return str_replace("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>", "", $dom->saveHTML(), $count);
    }
218713
public function parseHtml($html)
    {
        return Parser::table($html, '#DBGrid', [
            'status', 'name', 'credit', 'examination', 'property', 'teacher', 'code', 'week',
            ['hour', function (Crawler $crawler) {
                return $crawler->attr("title") ?: $crawler->text();
            }],
            ['place', function (Crawler $crawler) {
                return $crawler->text() == ";" ? '' : ($crawler->attr("title") ?: $crawler->text());
            }],
            'campus',
            ['class', function (Crawler $crawler) {
                return $crawler->attr("title") ?: $crawler->text();
            }],
        ], 12, 1, 'text');
    }
989868
private String filterAttributes(String html) {
		String filteredHtml = html;
		for (String attribute : this.filterAttributes) {
			String regex = "\\s" + attribute + "=\"[^\"]*\"";
			Pattern p = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
			Matcher m = p.matcher(html);
			filteredHtml = m.replaceAll("");
		}
		return filteredHtml;
	}
204398
protected function convertMediaTypeAttributeToMediaQuery($html, &$content)
    {
        if (preg_match('/\bmedia=(["\'])([^"\']+)\1/i', $html, $m) && isset($m[2]) && $m[2] !== 'all') {
            $content = '@media ' . $m[2] . ' {' . $content . '}';
        }
    }
496516
public function info($html)
    {
        $tags = array('{total}', '{start}', '{end}', '{page}', '{pages}');
        $code = array($this->total, $this->start() + 1, $this->end(), $this->get, $this->pages());

        return str_replace($tags, $code, $html);
    }