/** * Clones a node for appending to raw-html containing Elements like Interactive. * * @param DOMNode $node The node to clone * @return DOMNode The cloned node. */ public static function cloneNode($node) { $clone = $node->cloneNode(true); if (Type::is($clone, 'DOMElement') && $clone->hasAttribute(self::INSTANT_ARTICLES_PARSED_FLAG)) { $clone->removeAttribute(self::INSTANT_ARTICLES_PARSED_FLAG); } return $clone; }
/** * @param \DOMNode $node * @return string */ public static function getOuterHtml(\DOMNode $node) { $domDocument = new \DOMDocument('1.0'); $b = $domDocument->importNode($node->cloneNode(true), true); $domDocument->appendChild($b); $html = $domDocument->saveHtml(); $html = StringHelper::safeEncodeStr($html); return $html; }
/** * Adds a node to parent node and all its child nodes * * @param DOMNode $node // parent node * @param DOMNode $appendChild // append node */ function appendChilds($node, $appendChild) { $cloneChild = $this->importNode($appendChild->cloneNode(false), true); $newNode = $node->appendChild($cloneChild); if ($appendChild->childNodes) { foreach ($appendChild->childNodes as $child) { $this->appendChilds($newNode, $child); } } }
/** * Duplicate an element in the main document. Mainly used to complete calls_list * * @param DOMNode $node A DOMNode element. * @param string $insert_duplicate_as Specifies how to place the duplicated element relative to its source. * @param bool $insert_before The numeric key location of an item in list before which to add this newly duplicated $node. * * @return void */ public function duplicateNode($node, $insert_duplicate_as = 'immediate_sibling', $insert_before = false) { $parent = $node->parentNode; if ($insert_duplicate_as == 'sub_child') { $parent = $node; } elseif ($insert_before === false && !empty($node->nextSibling)) { $insert_before = $node->nextSibling; } // Copy and insert the main node $node_copy = $node->cloneNode(true); if ($insert_before) { // Insert $node_copy $parent->insertBefore($node_copy, $insert_before); } else { // Insert $node_copy as last child $parent->appendChild($node_copy); } // See if we can first add a contextual line break or space. Not always the case anyways if ($this->formatOutput && is_object($parent->firstChild) && $parent->firstChild->nodeName == '#text' && is_null(trim($parent->firstChild->nodeValue))) { $white_space = $parent->firstChild->cloneNode(true); $parent->insertBefore($white_space, $node_copy); } return $node_copy; }
/** * @param \DOMNode $node * @return \DOMNode */ public function cloneAndPrependChild(\DOMNode $node) { return DOMStatic::prependTo($node->cloneNode(true), $this); }
/** * Cousin of the javascript attribute of the same name. * * Several methods of implementation are available, though the best is * likely not written yet, and would include node cloning and document * fragments. * * @param DOMElement $element * The element whos innerds we want * @param bool $deep * Sets whether or not we want to do a "deep copy" and recurse into child * elements (which is the de-facto for the standard javascript innerHTML * attribute). Set this to false in order to get a "shallow" innerHTML. * If $deep is set to false, then $method is ignored, and method 1 is * used since it is the only one that supports this option. * @param int $method * An integer selector for the method we wish to use (0, 1, or 2). There * are many ways of implementing this functionality, some better than * others. * Method 0 takes less memory and should be slightly faster than method 1. * Method 1 is the most versatile. * Method 2 only works with php v5.3.6 and above, and ignores the deep * setting, but is the most efficient method. * @param boo $preserve * If method 1 or 2 are used, then $preserve may be set to true in order to * preserve all characters from the original document; otherwise, they * will strip out all carriage-returns ("\n") in order to be a functional * equivalent to the default method 0. * @return String of the html within the submitted element * @author David Hazel **/ public static function innerHtml(DOMNode $node, $deep = true, $method = 0, $preserve = false) { // do type checking if (!is_bool($deep)) { throw new Exception(sprintf('The second parameter (for the "deep" setting) must be boolean. %s given.', gettype($deep))); } elseif (!is_int($method)) { throw new Exception(sprintf('The third parameter (for the "method" setting) must be an integer. %s given.', gettype($method))); } elseif (!is_bool($preserve)) { throw new Exception(sprintf('The fourth parameter (for the "preserve" setting) must be boolean. %s given.', gettype($preserve))); } // if deep is false, use the one method that can do shallow if ($deep === false) { $method = 1; } // execute the method if ($method == 0) { // taken from the Raxan PDI framework $d = new DOMDocument('1.0'); $b = $d->importNode($node->cloneNode(true), true); $d->appendChild($b); $h = $d->saveHTML(); // remove outer tags (comment out this line if tags are desired) $h = substr($h, strpos($h, '>') + 1, -(strlen($node->nodeName) + 4)); return $h; } else { if ($method == 1) { // my first method $innerHTML = ''; $children = $node->childNodes; foreach ($children as $child) { $tmp_dom = new DOMDocument(); $tmp_dom->appendChild($tmp_dom->importNode($child, $deep)); //$innerHTML .= trim($tmp_dom->saveHTML()); if ($preserve !== true) { $innerHTML .= preg_replace('/\\n/', '', $tmp_dom->saveHTML()); } else { $innerHTML .= $tmp_dom->saveHTML(); } } return $innerHTML; } else { if ($method == 2) { // my own creation, prior to the Raxan method // (only works with php v5.3.6 and above) $innerHTML = ''; $children = $node->childNodes; foreach ($children as $child) { $tmpString = $child->ownerDocument->saveHTML($child); if ($preserve !== true) { $innerHTML .= preg_replace('/\\n/', '', $tmpString); } else { $innerHTML .= $tmpString; } } return $innerHTML; } else { throw new Exception(sprintf('The requested innerHtml method ("%s") does not exist.', $method)); } } } }
private function parseNodeAttributes(DOMNode &$node) { if ($node->hasAttributes()) { foreach ($node->attributes as $attr) { if (strpos($attr->value, '${') !== false) { $expressions = array(); preg_match_all('/(\\$\\{)(.*)(\\})/imsxU', $attr->value, $expressions); for ($i = 0; $i < count($expressions[0]); $i++) { $toReplace = $expressions[0][$i]; $expression = $expressions[2][$i]; $expressionResult = ExpressionParser::evaluate($expression, $this->dataContext); switch (strtolower($attr->name)) { case 'repeat': // Can only loop if the result of the expression was an array if (!is_array($expressionResult)) { throw new ExpressionException("Can't repeat on a singular var"); } // Make sure the repeat variable doesn't show up in the cloned nodes (otherwise it would infinit recurse on this->parseNode()) $node->removeAttribute('repeat'); // Is a named var requested? $variableName = $node->getAttribute('var') ? trim($node->getAttribute('var')) : false; // Store the current 'Cur', index and count state, we might be in a nested repeat loop $previousCount = isset($this->dataContext['Context']['Count']) ? $this->dataContext['Context']['Count'] : null; $previousIndex = isset($this->dataContext['Context']['Index']) ? $this->dataContext['Context']['Index'] : null; $previousCur = $this->dataContext['Cur']; // For information on the loop context, see http://opensocial-resources.googlecode.com/svn/spec/0.9/OpenSocial-Templating.xml#rfc.section.10.1 $this->dataContext['Context']['Count'] = count($expressionResult); foreach ($expressionResult as $index => $entry) { if ($variableName) { // this is cheating a little since we're not putting it on the top level scope, the variable resolver will check 'Cur' first though so myVar.Something will still resolve correctly $this->dataContext['Cur'][$variableName] = $entry; } $this->dataContext['Cur'] = $entry; $this->dataContext['Context']['Index'] = $index; // Clone this node and it's children $newNode = $node->cloneNode(true); // Append the parsed & expanded node to the parent $newNode = $node->parentNode->insertBefore($newNode, $node); // And parse it (using the global + loop context) $this->parseNode($newNode, true); } // Restore our previous data context state $this->dataContext['Cur'] = $previousCur; if ($previousCount) { $this->dataContext['Context']['Count'] = $previousCount; } else { unset($this->dataContext['Context']['Count']); } if ($previousIndex) { $this->dataContext['Context']['Index'] = $previousIndex; } else { unset($this->dataContext['Context']['Index']); } return $node; break; case 'if': if (!$expressionResult) { return $node; } else { $node->removeAttribute('if'); } break; // These special cases that only apply for certain tag types // These special cases that only apply for certain tag types case 'selected': if ($node->tagName == 'option') { if ($expressionResult) { $node->setAttribute('selected', 'selected'); } else { $node->removeAttribute('selected'); } } else { throw new ExpressionException("Can only use selected on an option tag"); } break; case 'checked': if ($node->tagName == 'input') { if ($expressionResult) { $node->setAttribute('checked', 'checked'); } else { $node->removeAttribute('checked'); } } else { throw new ExpressionException("Can only use checked on an input tag"); } break; case 'disabled': $disabledTags = array('input', 'button', 'select', 'textarea'); if (in_array($node->tagName, $disabledTags)) { if ($expressionResult) { $node->setAttribute('disabled', 'disabled'); } else { $node->removeAttribute('disabled'); } } else { throw new ExpressionException("Can only use disabled on input, button, select and textarea tags"); } break; default: // On non os-template spec attributes, do a simple str_replace with the evaluated value $stringVal = htmlentities(ExpressionParser::stringValue($expressionResult), ENT_QUOTES, 'UTF-8'); $newAttrVal = str_replace($toReplace, $stringVal, $attr->value); $node->setAttribute($attr->name, $newAttrVal); break; } } } } } // if a repeat attribute was found, don't recurse on it's child nodes, the repeat handling already did that if (isset($node->childNodes) && $node->childNodes->length > 0) { $removeNodes = array(); // recursive loop to all this node's children foreach ($node->childNodes as $childNode) { if (($removeNode = $this->parseNode($childNode)) !== false) { $removeNodes[] = $removeNode; } } if (count($removeNodes)) { foreach ($removeNodes as $removeNode) { $removeNode->parentNode->removeChild($removeNode); } } } return false; }
public function reproduce() { return new self($this->element->cloneNode(true)); }
/** * Resolves a path and removes the PLACEHOLDER-Tags * * @param DOMNode $node // the result node on which $result will be appended * @param DOMNode $result // the extracted node from the target DOM */ private function resolvePath(DOMNode $root, DOMNode $result) { $curNode = $root->parentNode; $copy = $root->cloneNode(true); $appended = false; // bring result and root to the same level $sourceDOM = $this->currentDOM; $targetDOM = $this->currentTargetDOM; $subPath = null; while ($curNode) { // Search for a placeholder tag if ($curNode->nodeName != "PLACEHOLDER") { $curNode = $curNode->parentNode; $result = $result->parentNode; continue; } // get previous node $lastKnown = $curNode->parentNode; while ($result->parentNode && $result->parentNode->nodeName != $lastKnown->nodeName) { $result = $result->parentNode; if (!$subPath) { $subPath = $targetDOM->importNode($result, false); if (!$appended) { $subPath->appendChild($copy); } $appended = true; } else { $newPath = $targetDOM->importNode($result); $newPath->appendChild($subPath); $subPath = $newPath; } } if ($subPath) { $lastKnown->appendChild($subPath); $lastKnown->removeChild($curNode); } $curNode = $lastKnown; $curNode = $curNode->parentNode; $subPath = null; } if ($appended) { return $copy; } return $root; }
private function clonar(DOMNode $nd, $deep = false) { return $nd->cloneNode($deep); }