Clean the attributes of the html tags
@param DOMElement $elem
199222
private function cleanAttributes(DOMElement $elem)
    {
        $tagName = strtolower($elem->nodeName);
        $attributes = $elem->attributes;
        $attributesWhiteList = $this->config->WhiteListHtmlGlobalAttributes;
        $attributesFilterMap = array();
        $allowDataAttribute = in_array("data-*", $attributesWhiteList);
        $whiteListAttr = $this->config->getWhiteListAttr($tagName);
        foreach ($whiteListAttr as $key => $val) {
            if (is_string($val)) {
                $attributesWhiteList[] = $val;
            }
            if ($val instanceof Closure) {
                $attributesWhiteList[] = $key;
                $attributesFilterMap[$key] = $val;
            }
        }
        $index = $attributes->length;
        while (--$index >= 0) {
        /*
        foreach会有一个严重的的问题：
            href="JavaScript:alert("customAttr="xxx"");"
            这样嵌套的不合法的属性，会被解析出两个属性：
                1. href="JavaScript:alert("
                2. customAttr="xxx"
            但是foreach只能拿到一个。
        foreach ($elem->attributes->item() as $attrName => $domAttr) {
        */
            /* @var $domAttr DOMAttr */
            $domAttr = $attributes->item($index);
            $attrName = strtolower($domAttr->name);
            $attrValue = $domAttr->value;
            // 如果不在白名单attr中，而且允许data-*，且不是data-*，则删除
            if (!in_array($attrName, $attributesWhiteList) && $allowDataAttribute && (stripos($attrName, "data-") !== 0)) {
                $elem->removeAttribute($attrName);
            } else {
                if (isset($attributesFilterMap[$attrName])) {
                    $filteredAttrValue = $attributesFilterMap[$attrName]($attrValue);
                    if ($filteredAttrValue === false ) {
                        $elem->removeAttribute($attrName);
                    } else {
                        // https://stackoverflow.com/questions/25475936/updating-domattr-value-with-url-results-in-parameters-being-lost-unless-htmlenti
                        // Fix ampersands issue: https://github.com/lincanbin/White-HTML-Filter/issues/1
                        if ($filteredAttrValue !== $attrValue) {
                            $elem->setAttribute($attrName, $filteredAttrValue);
                        }
                    }
                } else {
                    $this->cleanAttrValue($domAttr);
                }
            }
        }
    }
100705
function (elem)
    {
        var i, el, j, tag, attribute, value, css, cssAttrs, attr, cssName, cssValue;
        if (elem.nodeType == Strophe.ElementType.NORMAL) {
            tag = elem.nodeName;
            if(Strophe.XHTML.validTag(tag)) {
                try {
                    el = Strophe.xmlElement(tag);
                    for(i = 0; i < Strophe.XHTML.attributes[tag].length; i++) {
                        attribute = Strophe.XHTML.attributes[tag][i];
                        value = elem.getAttribute(attribute);
                        if(typeof value == 'undefined' || value === null || value === '' || value === false || value === 0) {
                            continue;
                        }
                        if(attribute == 'style' && typeof value == 'object') {
                            if(typeof value.cssText != 'undefined') {
                                value = value.cssText; // we're dealing with IE, need to get CSS out
                            }
                        }
                        // filter out invalid css styles
                        if(attribute == 'style') {
                            css = [];
                            cssAttrs = value.split(';');
                            for(j = 0; j < cssAttrs.length; j++) {
                                attr = cssAttrs[j].split(':');
                                cssName = attr[0].replace(/^\s*/, "").replace(/\s*$/, "").toLowerCase();
                                if(Strophe.XHTML.validCSS(cssName)) {
                                    cssValue = attr[1].replace(/^\s*/, "").replace(/\s*$/, "");
                                    css.push(cssName + ': ' + cssValue);
                                }
                            }
                            if(css.length > 0) {
                                value = css.join('; ');
                                el.setAttribute(attribute, value);
                            }
                        } else {
                            el.setAttribute(attribute, value);
                        }
                    }

                    for (i = 0; i < elem.childNodes.length; i++) {
                        el.appendChild(Strophe.createHtml(elem.childNodes[i]));
                    }
                } catch(e) { // invalid elements
                  el = Strophe.xmlTextNode('');
                }
            } else {
                el = Strophe.xmlGenerator().createDocumentFragment();
                for (i = 0; i < elem.childNodes.length; i++) {
                    el.appendChild(Strophe.createHtml(elem.childNodes[i]));
                }
            }
        } else if (elem.nodeType == Strophe.ElementType.FRAGMENT) {
            el = Strophe.xmlGenerator().createDocumentFragment();
            for (i = 0; i < elem.childNodes.length; i++) {
                el.appendChild(Strophe.createHtml(elem.childNodes[i]));
            }
        } else if (elem.nodeType == Strophe.ElementType.TEXT) {
            el = Strophe.xmlTextNode(elem.nodeValue);
        }

        return el;
    }
385126
public function setDOMElementAttribute(DOMElement $element, $attribute, $value)
    {
        XmlUtils::setDOMElementAttribute($element, $this->getAttributeName($element, $attribute), $value);
    }
881669
function (elem) {
    var elems = [];
    // Array.prototype.slice.call doesn't work when given a NodeList in
    // IE8 ("JScript object expected").
    var nodeList = elem.getElementsByTagName('*');
    for (var i = 0; i < nodeList.length; i++) {
      elems.push(nodeList[i]);
    }
    elems.push(elem);
    $jq.cleanData(elems);
  }
245195
public function cleanConditionally(\DOMElement $e, $tag)
    {
        if (!$this->flagIsActive(self::FLAG_CLEAN_CONDITIONALLY)) {
            return;
        }

        $tagsList = $e->getElementsByTagName($tag);
        $curTagsLength = $tagsList->length;
        $node = null;

        /*
         * Gather counts for other typical elements embedded within.
         * Traverse backwards so we can remove nodes at the same time without effecting the traversal.
         *
         * TODO: Consider taking into account original contentScore here.
         */
        for ($i = $curTagsLength - 1; $i >= 0; --$i) {
            $node = $tagsList->item($i);
            $weight = $this->getWeight($node);
            $contentScore = ($node->hasAttribute('readability')) ? (int) $node->getAttribute('readability') : 0;
            $this->logger->debug('Start conditional cleaning of ' . $node->getNodePath() . ' (class=' . $node->getAttribute('class') . '; id=' . $node->getAttribute('id') . ')' . (($node->hasAttribute('readability')) ? (' with score ' . $node->getAttribute('readability')) : ''));

            if ($weight + $contentScore < 0) {
                $this->logger->debug('Removing...');
                $node->parentNode->removeChild($node);
            } elseif ($this->getCommaCount($this->getInnerText($node)) < self::MIN_COMMAS_IN_PARAGRAPH) {
                /*
                 * If there are not very many commas, and the number of
                 * non-paragraph elements is more than paragraphs or other ominous signs, remove the element.
                 */
                $p = $node->getElementsByTagName('p')->length;
                $img = $node->getElementsByTagName('img')->length;
                $li = $node->getElementsByTagName('li')->length - 100;
                $input = $node->getElementsByTagName('input')->length;
                $a = $node->getElementsByTagName('a')->length;
                $embedCount = 0;
                $embeds = $node->getElementsByTagName('embed');

                for ($ei = 0, $il = $embeds->length; $ei < $il; ++$ei) {
                    if (preg_match($this->regexps['media'], $embeds->item($ei)->getAttribute('src'))) {
                        ++$embedCount;
                    }
                }

                $embeds = $node->getElementsByTagName('iframe');
                for ($ei = 0, $il = $embeds->length; $ei < $il; ++$ei) {
                    if (preg_match($this->regexps['media'], $embeds->item($ei)->getAttribute('src'))) {
                        ++$embedCount;
                    }
                }

                $linkDensity = $this->getLinkDensity($node, true);
                $contentLength = mb_strlen($this->getInnerText($node));
                $toRemove = false;

                if ($this->lightClean) {
                    if ($li > $p && 'ul' !== $tag && 'ol' !== $tag) {
                        $this->logger->debug(' too many <li> elements, and parent is not <ul> or <ol>');
                        $toRemove = true;
                    } elseif ($input > floor($p / 3)) {
                        $this->logger->debug(' too many <input> elements');
                        $toRemove = true;
                    } elseif ($contentLength < 6 && (0 === $embedCount && (0 === $img || $img > 2))) {
                        $this->logger->debug(' content length less than 6 chars, 0 embeds and either 0 images or more than 2 images');
                        $toRemove = true;
                    } elseif ($weight < 25 && $linkDensity > 0.25) {
                        $this->logger->debug(' weight is ' . $weight . ' < 25 and link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.25');
                        $toRemove = true;
                    } elseif ($a > 2 && ($weight >= 25 && $linkDensity > 0.5)) {
                        $this->logger->debug('  more than 2 links and weight is ' . $weight . ' > 25 but link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.5');
                        $toRemove = true;
                    } elseif ($embedCount > 3) {
                        $this->logger->debug(' more than 3 embeds');
                        $toRemove = true;
                    }
                } else {
                    if ($img > $p) {
                        $this->logger->debug(' more image elements than paragraph elements');
                        $toRemove = true;
                    } elseif ($li > $p && 'ul' !== $tag && 'ol' !== $tag) {
                        $this->logger->debug('  too many <li> elements, and parent is not <ul> or <ol>');
                        $toRemove = true;
                    } elseif ($input > floor($p / 3)) {
                        $this->logger->debug('  too many <input> elements');
                        $toRemove = true;
                    } elseif ($contentLength < 10 && (0 === $img || $img > 2)) {
                        $this->logger->debug('  content length less than 10 chars and 0 images, or more than 2 images');
                        $toRemove = true;
                    } elseif ($weight < 25 && $linkDensity > 0.2) {
                        $this->logger->debug('  weight is ' . $weight . ' lower than 0 and link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.2');
                        $toRemove = true;
                    } elseif ($weight >= 25 && $linkDensity > 0.5) {
                        $this->logger->debug('  weight above 25 but link density is ' . sprintf('%.2f', $linkDensity) . ' > 0.5');
                        $toRemove = true;
                    } elseif ((1 === $embedCount && $contentLength < 75) || $embedCount > 1) {
                        $this->logger->debug('  1 embed and content length smaller than 75 chars, or more than one embed');
                        $toRemove = true;
                    }
                }

                if ($toRemove) {
                    $this->logger->debug('Removing...');
                    $node->parentNode->removeChild($node);
                }
            }
        }
    }
1082251
private function removeElements(\DOMNodeList $elems, $logMessage = null)
    {
        if (false === $this->hasElements($elems)) {
            return;
        }

        if (null !== $logMessage) {
            $this->logger->info($logMessage, ['length' => $elems->length]);
        }

        for ($i = $elems->length - 1; $i >= 0; --$i) {
            if ($elems->item($i)->parentNode) {
                $elems->item($i)->parentNode->removeChild($elems->item($i));
            }
        }
    }
471282
protected function reparentElement(DOMElement $element)
	{
		$parentNode = $element->parentNode;
		do
		{
			$lastChild = $parentNode->lastChild;
			$parentNode->parentNode->insertBefore($lastChild, $parentNode->nextSibling);
		}
		while (!$lastChild->isSameNode($element));
	}
1145630
public function retriveAttributeType(\DOMElement $element)
    {
        $name = $this->engine->query($element->getNodePath() . '/dia:attribute[@name="type"]/dia:string');
        return $this->cleanString($name->item(0)->nodeValue);
    }
234276
private function convertElement(DOMElement $element, $value)
    {
        $sequential = $this->isArrayAllKeySequential($value);

        if (! is_array($value)) {
            $element->nodeValue = htmlspecialchars($value);
            return;
        }

        foreach ($value as $key => $data) {
            if (! $sequential) {
                if (($key === '_attributes') || ($key === '@attributes')) {
                    $this->addAttributes($element, $data);
                } elseif ((($key === '_value') || ($key === '@value')) && is_string($data)) {
                    $element->nodeValue = htmlspecialchars($data);
                } elseif ($element->nodeName == $key) {
                    $this->addCollectionNode($element, $data);
                    continue;
                } else {
                    $this->addNode($element, $key, $data);
                }
            } elseif (is_array($data)) {
                $this->addCollectionNode($element, $data);
            } else {
                $this->addSequentialNode($element, $data);
            }
        }
    }
321952
private function prepareTextValue(DOMElement $xml, array $attributes = null)
    {
        $text = static::getElementText($xml, true);

        return (isset($attributes['type']) && 'text' === $attributes['type']) ?
            static::clearValue($text) :
            static::getPhpValue($text, null, $this);
    }
549451
public function attrs(DOMElement &$element, $value = [])
    {
        if (empty($value)) {
            // Get current attributes
            $attrs = [];
            foreach ($element->attributes as $attr) {
                $attrs[$attr->name] = $attr->value;
            }

            // Return the current attributes
            return $attrs;
        } elseif (is_array($value) && count($value) > 0) {
            // Set the given attributes
            foreach ($value as $key => $val) {
                $this->attr($element, $key, $val);
            }
        }

        return [];
    }
667852
public function elem($elemDef, ...$params) : self {
            if (is_string($elemDef)) {
                $arrayArgs = $params;
            } else if (is_array($elemDef)) {
                $elemDefArr = $elemDef;
                $elemDef = array_shift($elemDefArr);
                $arrayArgs = $elemDefArr;
            } else {
                throw new \InvalidArgumentException("Invalid string or array in input: elem(" . gettype($elemDef). ")");
            }


            $arr = explode("@", $elemDef);
            $tagName = trim(strtolower(array_shift($arr)));


            $attrs = [];
            $qmIndex = 0;
            foreach ($arr as $attdef) {
                if ($attdef === "")
                    continue;
                list ($key, $val) = explode("=", $attdef, 2);
                if ( ! isset ($val)) {
                    $attrs[trim ($key)] = null;
                    continue;
                }

                $val = trim ($val);
                if (isset ($arrayArgs)) {
                    if ($val == "?" && isset ($arrayArgs[$qmIndex])) {
                        $val = $arrayArgs[$qmIndex];
                        $qmIndex++;
                    }
                }
                $attrs[trim($key)] = $val;
            }




            $newNode = new GoElementNode();
            if (isset ($this->emptyTags[$tagName]))
                $newNode->isEmptyElement = true;
            $newNode->name = $tagName;
            $newNode->attributes = $attrs;
            $this->curNode->childs[] = $newNode;
            $newNode->parent = $this->curNode;

            if ($newNode->parent instanceof GoDocumentNode) {
                $newNode->preWhiteSpace = "\n";
            } else {
                $newNode->preWhiteSpace = $newNode->parent->preWhiteSpace . "    ";
            }

            return $this->cloneit($newNode);
        }
996333
protected static function removeInvalidAttributes(DOMDocument $dom)
	{
		$xpath = new DOMXPath($dom);
		foreach ($xpath->query('//@*') as $attribute)
		{
			if (!preg_match('(^(?:[-\\w]+:)?(?!\\d)[-\\w]+$)D', $attribute->nodeName))
			{
				$attribute->parentNode->removeAttributeNode($attribute);
			}
		}
	}
847792
protected function exportHtml(\DOMElement $element)
    {
        $body = $this->getDocument()->createElement('body');
        $body->appendChild($element);
        $html = $this->getDocument()->getElementsByTagName('html')->item(0);
        $html->appendChild($this->getHead());
        $html->appendChild($body);

        $html =  $this->getDocument()->saveHTML();
        $this->reset();

        return $html;
    }
444356
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('>');
        }
    }
1028466
private function removeAttributes(\DOMNodeList $elems, $logMessage = null)
    {
        if (null !== $logMessage) {
            $this->logger->info($logMessage, ['length' => $elems->length]);
        }

        foreach ($elems as $el) {
            $owner = $el->ownerElement;
            $owner->removeAttributeNode($el);
        }
    }
504972
protected function wrap(\DOMElement $element, $html)
    {
        $wrapper = $element->ownerDocument->createDocumentFragment();
        $wrapper->appendXML($html);

        $result = $wrapper->firstChild;
        $element->parentNode->appendChild($wrapper);

        $xpath = new \DOMXpath($element->ownerDocument);
        $targets = $xpath->query('//inky-content', $wrapper);

        if ($targets->length) {
            $parent = $targets->item(0)->parentNode;
            $parent->removeChild($targets->item(0));
            $parent->appendChild($element);
        }

        return $result;
    }