* * If you leave this notice intact, feel free to use this code however you'd like. * I wouldn't mind an email letting me know how you're using it or what you think. * * Send patches! */ /* Sometimes we don't want to return a wizDom object when we are looping over * our set and performing an action. Sometimes we want to return something else - * a string, a boolean, whatever. When we want to do that, we just throw this * exception, and we return. */ class wizDomReturnValueException extends Exception {} class pqDocument extends DOMDocument { public function __construct() { parent::__construct(); $this->registerNodeClass('DOMElement', 'pqElement'); } } class pqElement extends DOMElement { public function _empty() { $this->nodeValue = ""; foreach($this->childNodes as $child) { $kill[] = $child; } foreach($kill as $child) { if(method_exists($child, 'remove')) { $child->remove(); } } return $this; } public function remove() { if($this->parentNode instanceof self) { $this->parentNode->removeChild($this); } return $this; } public function wrap($html) { $elems = $this->htmlToElems($html); $pointer = false; foreach($elems as $child) { if(!$pointer) { $pointer = $child; } while($pointer->firstChild) { $pointer = $pointer->firstChild; } } if($pointer) { $pointer->appendChild($this); $this->parentNode->replaceChild($elems->item(0), $this); } return $this; } public function replaceChild($child, $parent) { return parent::replaceChild($this->normalizeElement($child), $parent); } private function normalizeElement($elem) { if(!$elem->ownerDocument->isSameNode($this->ownerDocument)) { return $this->ownerDocument->importNode($elem, true); } return $elem; } public function appendChild($elem) { parent::appendChild($this->normalizeElement($elem)); } private function getClasses() { $classes = array(); $oldClass = $this->getAttribute('class'); if($oldClass) $classes = explode(" ",$oldClass); return $classes; } private function saveClasses($classes) { $this->setAttribute('class', implode(" ",$classes)); } public function hasClass($class) { $classes = $this->getClasses(); return array_search($class, $classes) !== false; } public function addClass($class) { $classes = $this->getClasses(); $classes[] = $class; $classes = array_unique($classes); $this->saveClasses($classes); return $this; } public function toggleClass($class) { if($this->hasClass($class)) { $this->removeClass($class); } else { $this->addClass($class); } } public function removeClass($class) { $key = $this->hasClass($class); if($key !== false) { unset($classes[$key]); $this->saveClasses($classes); } return $this; } public function html($contents = false) { if(!$contents) { $doc = new DOMDocument(); foreach($this->childNodes as $child) { $doc->appendChild($doc->importNode($child, true)); } throw new wizDomReturnValueException($doc->saveHTML()); } $this->_empty(); foreach($this->htmlToElems($contents) as $child) { $this->appendChild($this->ownerDocument->importNode($child, true)); } return $this; } private function htmlToElems($html) { $doc = new pqDocument(); @$doc->loadHTML($html); return $doc->documentElement->firstChild->childNodes; } public function replaceWithChildren() { $frag = $this->ownerDocument->createDocumentFragment(); while($this->childNodes->length) { $frag->appendChild($this->childNodes->item(0)); } $this->parentNode->replaceChild($frag, $this); return $this; } public function id($contents = false) { if($contents) { $this->setAttribute('id', $contents); } throw new wizDomReturnValueException($this->getAttribute('id')); } public function text($contents = false) { if(!$contents) { throw new wizDomReturnValueException($this->nodeValue); } $this->_empty(); $this->appendChild($this->ownerDocument->createTextNode($contents)); return $this; } } class wizDom implements IteratorAggregate, Countable { public $doc; private $xpath; public $stack; static public function fetch($node = false) { return new self($node); } public function getIterator() { return $this->stack; } public function count() { return count($this->stack); } public function __construct($node = false) { if($node) { $this->doc = $node->ownerDocument; $this->stack = new ArrayObject($node); $this->xpath = new DOMXpath($this->doc); } else { $this->doc = new pqDocument(); $this->stack = new ArrayObject(); } } public function loadHTML($str) { $this->doc->loadHTML($str); $this->xpath = new DOMXpath($this->doc); $this->stack = array($this->doc->documentElement); } public function saveHTML() { return $this->doc->saveHTML(); } public function loadHTMLFile($filename) { $this->doc->loadHTMLFile($filename); $this->xpath = new DOMXpath($this->doc); } public function children($type = "*") { return $this->find("./child::$type"); } public function descendants($type = "*") { return $this->find("./descendant::$type"); } public function parent($type = "*") { return $this->find("./parent::$type"); } public function parents($type = "*") { return $this->find("./ancestor::$type"); } public function filterClass($class) { return $this->find("./self::*[contains(concat(\" \",@class,\" \"), \" $class \")]"); } public function find($query) { $newstack = new ArrayObject(); foreach($this->stack as $root) { $stack = $this->xpath->evaluate($query, $root); for($item = 0; $item < $stack->length; ++$item) { $duplicate = false; foreach($newstack as $alreadyGot) { if($alreadyGot->isSameNode($stack->item($item))) $duplicate = true; } if(!$duplicate) $newstack[] = $stack->item($item); } } $newobj = clone $this; $newobj->stack = $newstack; return $newobj; } public function __call($function, $args) { foreach($this->stack as $child) { if(method_exists($child, $function)) { try { call_user_func_array(array($child, $function), $args); } catch (wizDomReturnValueException $e) { return $e->getMessage(); } } } return $this; } } function WZ($val = false) { if($val instanceof pqElement) return wizDom::fetch($val); if(is_string($val)) { $wz = wizDom::fetch(); $wz->loadHTML($val); return $wz; } }