FluentDOM
[ class tree: FluentDOM ] [ index: FluentDOM ] [ all elements ]

Source for file FluentDOM.php

Documentation is available at FluentDOM.php

  1. <?php
  2. /**
  3. * FluentDOM implements a jQuery like replacement for DOMNodeList
  4. *
  5. @version $Id: FluentDOM.php 440 2010-04-16 17:21:08Z subjective $
  6. @license http://www.opensource.org/licenses/mit-license.php The MIT License
  7. @copyright Copyright (c) 2009 Bastian Feder, Thomas Weinert
  8. *
  9. @tutorial FluentDOM.pkg
  10. @package FluentDOM
  11. */
  12.  
  13. /**
  14. * Include the core class
  15. */
  16. require_once(dirname(__FILE__).'/FluentDOM/Core.php');
  17. /**
  18. * Include the handler helper class
  19. */
  20. require_once(dirname(__FILE__).'/FluentDOM/Handler.php');
  21.  
  22. /**
  23. * Function to create a new FluentDOM instance and loads data into it if
  24. * a valid $source is provided.
  25. *
  26. @param mixed $source 
  27. @param string $contentType optional, default value 'text/xml'
  28. @return FluentDOM 
  29. */
  30. function FluentDOM($source NULL$contentType 'text/xml'{
  31.   $result new FluentDOM();
  32.   if (isset($source)) {
  33.     return $result->load($source$contentType);
  34.   else {
  35.     return $result;
  36.   }
  37. }
  38.  
  39. /**
  40. * FluentDOM implements a jQuery like replacement for DOMNodeList
  41. *
  42. @method bool empty() Remove all child nodes from the set of matched elements.
  43. @method DOMDocument clone() Clone matched DOM Elements and select the clones.
  44. *
  45. @package FluentDOM
  46. */
  47. class FluentDOM extends FluentDOMCore {
  48.  
  49.   /**
  50.   * declaring an empty() or clone() method will crash the parser so we use some magic
  51.   *
  52.   * @param string $name 
  53.   * @param array $arguments 
  54.   * @return mixed 
  55.   */
  56.   public function __call($name$arguments{
  57.     switch (strtolower($name)) {
  58.     case 'empty' :
  59.       return $this->_emptyNodes();
  60.     case 'clone' :
  61.       return $this->_cloneNodes();
  62.     default :
  63.       throw new BadMethodCallException('Unknown method '.get_class($this).'::'.$name);
  64.     }
  65.   }
  66.   /*
  67.   * Object Accessors
  68.   */
  69.  
  70.   /**
  71.   * Execute a function within the context of every matched element.
  72.   *
  73.   * @param callback $function 
  74.   * @return FluentDOM 
  75.   */
  76.   public function each($function{
  77.     if ($this->_isCallback($functionTRUEFALSE)) {
  78.       foreach ($this->_array as $index => $node{
  79.         call_user_func($function$node$index);
  80.       }
  81.     }
  82.     return $this;
  83.   }
  84.  
  85.   /*
  86.   * Miscellaneous
  87.   */
  88.  
  89.   /**
  90.   * Retrieve the matched DOM elements in an array.
  91.   *
  92.   * @return array 
  93.   */
  94.   public function toArray({
  95.     return $this->_array;
  96.   }
  97.  
  98.   /*
  99.   * Traversing - Filtering
  100.   */
  101.  
  102.   /**
  103.   * Reduce the set of matched elements to a single element.
  104.   *
  105.   * @example eq.php Usage Example: FluentDOM::eq()
  106.   * @param integer $position Element index (start with 0)
  107.   * @return FluentDOM 
  108.   */
  109.   public function eq($position{
  110.     $result $this->spawn();
  111.     if ($position 0{
  112.       $position count($this->_array$position;
  113.     }
  114.     if (isset($this->_array[$position])) {
  115.       $result->push($this->_array[$position]);
  116.     }
  117.     return $result;
  118.   }
  119.  
  120.   /**
  121.   * Removes all elements from the set of matched elements that do not match
  122.   * the specified expression(s).
  123.   *
  124.   * @example filter-expr.php Usage Example: FluentDOM::filter() with XPath expression
  125.   * @example filter-fn.php Usage Example: FluentDOM::filter() with Closure
  126.   * @param string|callback$expr XPath expression or callback function
  127.   * @return FluentDOM 
  128.   */
  129.   public function filter($expr{
  130.     $result $this->spawn();
  131.     foreach ($this->_array as $index => $node{
  132.       $check TRUE;
  133.       if (is_string($expr)) {
  134.         $check $this->_test($expr$node$index);
  135.       elseif ($this->_isCallback($exprTRUEFALSE)) {
  136.         $check call_user_func($expr$node$index);
  137.       }
  138.       if ($check{
  139.         $result->push($node);
  140.       }
  141.     }
  142.     return $result;
  143.   }
  144.  
  145.   /**
  146.   * Retrieve the matched DOM elements in an array. A negative position will be counted from the end.
  147.   *
  148.   * @param integer|NULLoptional offset of a single element to get.
  149.   * @return array() 
  150.   */
  151.   public function get($position NULL{
  152.     if (!isset($position)) {
  153.       return $this->_array;
  154.     }
  155.     if ($position 0{
  156.       $position count($this->_array$position;
  157.     }
  158.     if (isset($this->_array[$position])) {
  159.       return array($this->_array[$position]);
  160.     else {
  161.       return array();
  162.     }
  163.   }
  164.  
  165.   /**
  166.   * Search for a given element from among the matched elements.
  167.   *
  168.   * @param NULL|string|DOMNode|DOMNodelist|Iterator$expr 
  169.   * @return integer 
  170.   */
  171.   public function index($expr NULL{
  172.     if (count($this->_array0{
  173.       if (is_null($expr)) {
  174.         $counter = -1;
  175.         $targetNode $this->_array[0];
  176.         $nodeList $this->_match('preceding-sibling::node()'$targetNode);
  177.         foreach ($nodeList as $node{
  178.           if ($this->_isNode($node)) {
  179.             $counter++;
  180.           }
  181.         }
  182.         return $counter 1;
  183.       elseif (is_string($expr)) {
  184.         foreach ($this->_array as $index => $node{
  185.           if ($this->_test($expr$node)) {
  186.             return $index;
  187.           }
  188.         }
  189.       else {
  190.         $targetNode $this->_getContentElement($expr);
  191.         foreach ($this->_array as $index => $node{
  192.           if ($node->isSameNode($targetNode)) {
  193.             return $index;
  194.           }
  195.         }
  196.       }
  197.     }
  198.     return -1;
  199.   }
  200.  
  201.   /**
  202.   * Reduce the set of matched elements to those that have
  203.   * a descendant that matches the selector or DOM element.
  204.   *
  205.   * @param string|DOMNode$expr XPath expression or DOMNode
  206.   * @return boolean 
  207.   */
  208.   public function has($expr{
  209.     $result $this->spawn();
  210.     foreach ($this->_array as $node{
  211.       if ($node instanceof DOMElement &&
  212.           $node->hasChildNodes()) {
  213.         foreach ($node->childNodes as $childNode{
  214.           if ($expr instanceof DOMNode{
  215.             if ($expr === $childNode{
  216.               $result->push($node);
  217.               return $result;
  218.             }
  219.           elseif ($this->_test($expr$childNode)) {
  220.             $result->push($node);
  221.           }
  222.         }
  223.       }
  224.     }
  225.     return $result;
  226.   }
  227.  
  228.   /**
  229.   * Checks the current selection against an expression and returns true,
  230.   * if at least one element of the selection fits the given expression.
  231.   *
  232.   * @example is.php Usage Example: FluentDOM::is()
  233.   * @param string $expr XPath expression
  234.   * @return boolean 
  235.   */
  236.   public function is($expr{
  237.     foreach ($this->_array as $node{
  238.       return $this->_test($expr$node);
  239.     }
  240.     return FALSE;
  241.   }
  242.  
  243.   /**
  244.   * Translate a set of elements in the FluentDOM object into
  245.   * another set of values in an array (which may, or may not contain elements).
  246.   *
  247.   * If the callback function returns an array each element of the array will be added to the
  248.   * result array. All other variable types are put directly into the result array.
  249.   *
  250.   * @example map.php Usage Example: FluentDOM::map()
  251.   * @param callback $function 
  252.   * @return array 
  253.   */
  254.   public function map($function{
  255.     $result array();
  256.     foreach ($this->_array as $index => $node{
  257.       if ($this->_isCallback($functionTRUEFALSE)) {
  258.         $mapped call_user_func($function$node$index);
  259.       }
  260.       if ($mapped === NULL{
  261.         continue;
  262.       elseif ($mapped instanceof DOMNodeList ||
  263.                 $mapped instanceof Iterator ||
  264.                 $mapped instanceof IteratorAggregate ||
  265.                 is_array($mapped)) {
  266.         foreach ($mapped as $element{
  267.           if ($element !== NULL{
  268.             $result[$element;
  269.           }
  270.         }
  271.       else {
  272.         $result[$mapped;
  273.       }
  274.     }
  275.     return $result;
  276.   }
  277.  
  278.   /**
  279.   * Removes elements matching the specified expression from the set of matched elements.
  280.   *
  281.   * @example not.php Usage Example: FluentDOM::not()
  282.   * @param string|callback$expr XPath expression or callback function
  283.   * @return FluentDOM 
  284.   */
  285.   public function not($expr{
  286.     $result $this->spawn();
  287.     foreach ($this->_array as $index => $node{
  288.       $check FALSE;
  289.       if (is_string($expr)) {
  290.         $check $this->_test($expr$node$index);
  291.       elseif ($this->_isCallback($exprTRUEFALSE)) {
  292.         $check call_user_func($expr$node$index);
  293.       }
  294.       if (!$check{
  295.         $result->push($node);
  296.       }
  297.     }
  298.     return $result;
  299.   }
  300.  
  301.   /**
  302.   * Selects a subset of the matched elements.
  303.   *
  304.   * @example slice.php Usage Example: FluentDOM::slice()
  305.   * @param integer $start 
  306.   * @param integer $end 
  307.   * @return FluentDOM 
  308.   */
  309.   public function slice($start$end NULL{
  310.     $result $this->spawn();
  311.     if ($end === NULL{
  312.       $result->push(array_slice($this->_array$start));
  313.     elseif ($end 0{
  314.       $result->push(array_slice($this->_array$start$end));
  315.     elseif ($end $start{
  316.       $result->push(array_slice($this->_array$start$end $start));
  317.     else {
  318.       $result->push(array_slice($this->_array$end$start $end));
  319.     }
  320.     return $result;
  321.   }
  322.  
  323.   /*
  324.   * Traversing - Finding
  325.   */
  326.  
  327.   /**
  328.   * Adds more elements, matched by the given expression, to the set of matched elements.
  329.   *
  330.   * @example add.php Usage Examples: FluentDOM::add()
  331.   * @param string $expr XPath expression
  332.   * @return FluentDOM 
  333.   */
  334.   public function add($expr$context NULL{
  335.     $result $this->spawn();
  336.     $result->push($this->_array);
  337.     if (isset($context)) {
  338.       $targetNodes $this->_getTargetNodes($context);
  339.       if (!empty($targetNodes)) {
  340.         foreach ($targetNodes as $node{
  341.           $result->push($this->_match($expr$context));
  342.         }
  343.       }
  344.     elseif (is_object($expr||
  345.               (is_string($expr&& substr(ltrim($expr)01== '<')) {
  346.       $result->push($this->_getContentNodes($expr));
  347.     else {
  348.       $result->push($this->find($expr));
  349.     }
  350.     $result->_uniqueSort();
  351.     return $result;
  352.   }
  353.  
  354.   /**
  355.   * Get a set of elements containing of the unique immediate
  356.   * childnodes including only elements (not textnodes) of each
  357.   * of the matched set of elements.
  358.   *
  359.   * @example children.php Usage Examples: FluentDOM::children()
  360.   * @param string $expr XPath expression
  361.   * @return FluentDOM 
  362.   */
  363.   public function children($expr NULL{
  364.     $result $this->spawn();
  365.     foreach ($this->_array as $node{
  366.       if (empty($expr)) {
  367.         $result->push($node->childNodesTRUE);
  368.       else {
  369.         foreach ($node->childNodes as $childNode{
  370.           if ($this->_test($expr$childNode)) {
  371.             $result->push($childNodeTRUE);
  372.           }
  373.         }
  374.       }
  375.     }
  376.     $result->_uniqueSort();
  377.     return $result;
  378.   }
  379.  
  380.   /**
  381.   * Get a set of elements containing all of the unique immediate
  382.   * childnodes including elements and textnodes of each of the matched set of elements.
  383.   *
  384.   * @return FluentDOM 
  385.   */
  386.   public function contents({
  387.     $result $this->spawn();
  388.     foreach ($this->_array as $node{
  389.       $result->push($node->childNodesFALSE);
  390.     }
  391.     $result->_uniqueSort();
  392.     return $result;
  393.   }
  394.  
  395.   /**
  396.   * Searches for descendent elements that match the specified expression.
  397.   *
  398.   * @example find.php Usage Example: FluentDOM::find()
  399.   * @param string $expr XPath expression
  400.   * @param boolean $useDocumentContext ignore current node list
  401.   * @return FluentDOM 
  402.   */
  403.   public function find($expr$useDocumentContext FALSE{
  404.     $result $this->spawn();
  405.     if ($useDocumentContext ||
  406.         $this->_useDocumentContext{
  407.       $result->push($this->_match($expr));
  408.     else {
  409.       foreach ($this->_array as $contextNode{
  410.         $result->push($this->_match($expr$contextNode));
  411.       }
  412.     }
  413.     return $result;
  414.   }
  415.  
  416.   /**
  417.   * Get a set of elements containing the unique next siblings of each of the
  418.   * given set of elements.
  419.   *
  420.   * @example next.php Usage Example: FluentDOM::next()
  421.   * @param string $expr XPath expression
  422.   * @return FluentDOM 
  423.   */
  424.   public function next($expr NULL{
  425.     $result $this->spawn();
  426.     foreach ($this->_array as $node{
  427.       $next $node->nextSibling;
  428.       while ($next instanceof DOMNode && !$this->_isNode($next)) {
  429.         $next $next->nextSibling;
  430.       }
  431.       if (!empty($next)) {
  432.         if (empty($expr|| $this->_test($expr$next)) {
  433.           $result->push($next);
  434.         }
  435.       }
  436.     }
  437.     $result->_uniqueSort();
  438.     return $result;
  439.   }
  440.  
  441.   /**
  442.   * Find all sibling elements after the current element.
  443.   *
  444.   * @example nextAll.php Usage Example: FluentDOM::nextAll()
  445.   * @param string $expr XPath expression
  446.   * @return FluentDOM 
  447.   */
  448.   public function nextAll($expr NULL{
  449.     $result $this->spawn();
  450.     foreach ($this->_array as $node{
  451.       $next $node->nextSibling;
  452.       while ($next instanceof DOMNode{
  453.         if ($this->_isNode($next)) {
  454.           if (empty($expr|| $this->_test($expr$next)) {
  455.             $result->push($next);
  456.           }
  457.         }
  458.         $next $next->nextSibling;
  459.       }
  460.     }
  461.     return $result;
  462.   }
  463.  
  464.   /**
  465.   * Get all following siblings of each element up to but
  466.   * not including the element matched by the selector.
  467.   *
  468.   * @param string $expr XPath expression
  469.   * @return FluentDOM 
  470.   */
  471.   public function nextUntil($expr NULL{
  472.     $result $this->spawn();
  473.     foreach ($this->_array as $node{
  474.       $next $node->nextSibling;
  475.       while ($next instanceof DOMNode{
  476.         if ($this->_isNode($next)) {
  477.           if (isset($expr&& $this->_test($expr$next)) {
  478.             break;
  479.           else {
  480.             $result->push($next);
  481.           }
  482.         }
  483.         $next $next->nextSibling;
  484.       }
  485.     }
  486.     return $result;
  487.   }
  488.  
  489.   /**
  490.   * Get a set of elements containing the unique parents of the matched set of elements.
  491.   *
  492.   * @example parent.php Usage Example: FluentDOM::parent()
  493.   * @return FluentDOM 
  494.   */
  495.   public function parent({
  496.     $result $this->spawn();
  497.     foreach ($this->_array as $node{
  498.       if (isset($node->parentNode)) {
  499.         $result->push($node->parentNode);
  500.       }
  501.     }
  502.     $result->_uniqueSort();
  503.     return $result;
  504.   }
  505.  
  506.   /**
  507.   * Get the ancestors of each element in the current set of matched elements,
  508.   * optionally filtered by a selector.
  509.   *
  510.   * @example parents.php Usage Example: FluentDOM::parents()
  511.   * @param string $expr XPath expression
  512.   * @return FluentDOM 
  513.   */
  514.   public function parents($expr NULL{
  515.     $result $this->spawn();
  516.     foreach ($this->_array as $node{
  517.       $parents $this->_match('ancestor::*'$node);
  518.       for ($i $parents->length 1$i >= 0--$i{
  519.         $parentNode $parents->item($i);
  520.         if (empty($expr|| $this->_test($expr$parentNode)) {
  521.           $result->push($parentNode);
  522.         }
  523.       }
  524.     }
  525.     return $result;
  526.   }
  527.  
  528.   /**
  529.   * Get the ancestors of each element in the current set of matched elements,
  530.   * up to but not including the element matched by the selector.
  531.   *
  532.   * @param string $expr XPath expression
  533.   * @return FluentDOM 
  534.   */
  535.   public function parentsUntil($expr NULL{
  536.     $result $this->spawn();
  537.     foreach ($this->_array as $node{
  538.       $parents $this->_match('ancestor::*'$node);
  539.       for ($i $parents->length 1$i >= 0--$i{
  540.         $parentNode $parents->item($i);
  541.         if (!empty($expr&& $this->_test($expr$parentNode)) {
  542.           break;
  543.         }
  544.         $result->push($parentNode);
  545.       }
  546.     }
  547.     return $result;
  548.   }
  549.  
  550.   /**
  551.   * Get a set of elements containing the unique previous siblings of each of the
  552.   * matched set of elements.
  553.   *
  554.   * @example prev.php Usage Example: FluentDOM::prev()
  555.   * @param string $expr XPath expression
  556.   * @return FluentDOM 
  557.   */
  558.   public function prev($expr NULL{
  559.     $result $this->spawn();
  560.     foreach ($this->_array as $node{
  561.       $previous $node->previousSibling;
  562.       while ($previous instanceof DOMNode && !$this->_isNode($previous)) {
  563.         $previous $previous->previousSibling;
  564.       }
  565.       if (!empty($previous)) {
  566.         if (empty($expr|| $this->_test($expr$previous)) {
  567.           $result->push($previous);
  568.         }
  569.       }
  570.     }
  571.     $result->_uniqueSort();
  572.     return $result;
  573.   }
  574.  
  575.   /**
  576.   * Find all sibling elements in front of the current element.
  577.   *
  578.   * @example prevAll.php Usage Example: FluentDOM::prevAll()
  579.   * @param string $expr XPath expression
  580.   * @return FluentDOM 
  581.   */
  582.   public function prevAll($expr NULL{
  583.     $result $this->spawn();
  584.     foreach ($this->_array as $node{
  585.       $previous $node->previousSibling;
  586.       while ($previous instanceof DOMNode{
  587.         if ($this->_isNode($previous)) {
  588.           if (empty($expr|| $this->_test($expr$previous)) {
  589.             $result->push($previous);
  590.           }
  591.         }
  592.         $previous $previous->previousSibling;
  593.       }
  594.     }
  595.     return $result;
  596.   }
  597.  
  598.   /**
  599.   * Get all preceding siblings of each element up to but not including
  600.   * the element matched by the selector.
  601.   *
  602.   * @param string $expr XPath expression
  603.   * @return FluentDOM 
  604.   */
  605.   public function prevUntil($expr NULL{
  606.     $result $this->spawn();
  607.     foreach ($this->_array as $node{
  608.       $previous $node->previousSibling;
  609.       while ($previous instanceof DOMNode{
  610.         if (isset($expr&& $this->_isNode($previous)) {
  611.           if ($this->_test($expr$previous)) {
  612.             break;
  613.           else {
  614.             $result->push($previous);
  615.           }
  616.         }
  617.         $previous $previous->previousSibling;
  618.       }
  619.     }
  620.     return $result;
  621.   }
  622.  
  623.   /**
  624.   * Get a set of elements containing all of the unique siblings of each of the
  625.   * matched set of elements.
  626.   *
  627.   * @example siblings.php Usage Example: FluentDOM::siblings()
  628.   * @param string $expr XPath expression
  629.   * @return FluentDOM 
  630.   */
  631.   public function siblings($expr NULL{
  632.     $result $this->spawn();
  633.     foreach ($this->_array as $node{
  634.       if (isset($node->parentNode)) {
  635.         foreach ($node->parentNode->childNodes as $childNode{
  636.           if ($this->_isNode($childNode&&
  637.               $childNode !== $node{
  638.             if (empty($expr|| $this->_test($expr$childNode)) {
  639.               $result->push($childNode);
  640.             }
  641.           }
  642.         }
  643.       }
  644.     }
  645.     $result->_uniqueSort();
  646.     return $result;
  647.   }
  648.  
  649.   /**
  650.   * Get a set of elements containing the closest parent element that matches the specified
  651.   * selector, the starting element included.
  652.   *
  653.   * @example closest.php Usage Example: FluentDOM::closest()
  654.   * @param string $expr XPath expression
  655.   * @return FluentDOM 
  656.   */
  657.   public function closest($expr$context NULL{
  658.     $result $this->spawn();
  659.     $context $this->_getContextNodes($context);
  660.     foreach ($context as $node{
  661.       while (isset($node)) {
  662.         if ($this->_test($expr$node)) {
  663.           $result->push($node);
  664.           break;
  665.         }
  666.         $node $node->parentNode;
  667.       }
  668.     }
  669.     return $result;
  670.   }
  671.  
  672.   /**
  673.   * Get a set of elements containing only the first of the currently selected elements.
  674.   *
  675.   * @return FluentDOM 
  676.   */
  677.   public function first({
  678.     return $this->eq(0);
  679.   }
  680.  
  681.   /**
  682.   * Get a set of elements containing only the last of the currently selected elements.
  683.   *
  684.   * @return FluentDOM 
  685.   */
  686.   public function last({
  687.     return $this->eq(-1);
  688.   }
  689.  
  690.   /*
  691.   * Traversing - Chaining
  692.   */
  693.  
  694.   /**
  695.   * Add the previous selection to the current selection.
  696.   *
  697.   * @return FluentDOM 
  698.   */
  699.   public function andSelf({
  700.     $result $this->spawn();
  701.     $result->push($this->_array);
  702.     $result->push($this->_parent);
  703.     return $result;
  704.   }
  705.  
  706.   /**
  707.   * Revert the most recent traversing operation,
  708.   * changing the set of matched elements to its previous state.
  709.   *
  710.   * @return FluentDOM 
  711.   */
  712.   public function end({
  713.     if ($this->_parent instanceof FluentDOM{
  714.       return $this->_parent;
  715.     else {
  716.       return $this;
  717.     }
  718.   }
  719.  
  720.   /*
  721.   * Manipulation - Changing Contents
  722.   */
  723.  
  724.   /**
  725.   * Get or set the xml contents of the first matched element.
  726.   *
  727.   * @example xml.php Usage Example: FluentDOM::xml()
  728.   * @param string|Callback|Closure$xml XML fragment
  729.   * @return string|FluentDOM
  730.   */
  731.   public function xml($xml NULL{
  732.     if (isset($xml)) {
  733.       $isCallback $this->_isCallback($xmlFALSETRUE);
  734.       if ($isCallback{
  735.         foreach ($this->_array as $index => $node{
  736.           $xmlString call_user_func(
  737.             $xml,
  738.             $node,
  739.             $index,
  740.             $this->_getInnerXml($node)
  741.           );
  742.           $node->nodeValue '';
  743.           if (!empty($xmlString)) {
  744.             $fragment $this->_getContentFragment($xmlStringTRUE);
  745.             foreach ($fragment as $contentNode{
  746.               $node->appendChild($contentNode->cloneNode(TRUE));
  747.             }
  748.           }
  749.         }
  750.       else {
  751.         if (!empty($xml)) {
  752.           $fragment $this->_getContentFragment($xmlTRUE);
  753.         else {
  754.           $fragment array();
  755.         }
  756.         foreach ($this->_array as $node{
  757.           $node->nodeValue '';
  758.           foreach ($fragment as $contentNode{
  759.             $node->appendChild($contentNode->cloneNode(TRUE));
  760.           }
  761.         }
  762.       }
  763.       return $this;
  764.     else {
  765.       if (isset($this->_array[0])) {
  766.         return $this->_getInnerXml($this->_array[0]);
  767.       }
  768.       return '';
  769.     }
  770.   }
  771.  
  772.   /**
  773.   * Get the combined text contents of all matched elements or
  774.   * set the text contents of all matched elements.
  775.   *
  776.   * @example text.php Usage Example: FluentDOM::text()
  777.   * @param string|callback|Closure$text 
  778.   * @return string|FluentDOM
  779.   */
  780.   public function text($text NULL{
  781.     if (isset($text)) {
  782.       $isCallback $this->_isCallback($textFALSETRUE);
  783.       foreach ($this->_array as $index => $node{
  784.         if ($isCallback{
  785.           $node->nodeValue call_user_func($text$node$index$node->nodeValue);
  786.         else {
  787.           $node->nodeValue $text;
  788.         }
  789.       }
  790.       return $this;
  791.     else {
  792.       $result '';
  793.       foreach ($this->_array as $node{
  794.         $result .= $node->textContent;
  795.       }
  796.       return $result;
  797.     }
  798.   }
  799.  
  800.   /*
  801.   * Manipulation - Inserting Inside
  802.   */
  803.  
  804.   /**
  805.   * Append content to the inside of every matched element.
  806.   *
  807.   * @example append.php Usage Example: FluentDOM::append()
  808.   * @param string|array|DOMNode|Iterator$content DOMNode or DOMNodeList or xml fragment string
  809.   * @return FluentDOM 
  810.   */
  811.   public function append($content{
  812.     $result $this->spawn();
  813.     if (empty($this->_array&&
  814.         $this->_useDocumentContext &&
  815.         !isset($this->_document->documentElement)) {
  816.       if ($this->_isCallback($contentFALSETRUE)) {
  817.         $contentNode $this->_getContentElement(
  818.           $this->_executeEasySetter($contentNULL0'')
  819.         );
  820.       else {
  821.         $contentNode $this->_getContentElement($content);
  822.       }
  823.       $result->push($this->_document->appendChild($contentNode));
  824.     else {
  825.       $result->push(
  826.         $this->_applyContentToNodes(
  827.           $this->_array,
  828.           $content,
  829.           array($this->_getHandler()'appendChildren')
  830.         )
  831.       );
  832.     }
  833.     return $result;
  834.   }
  835.  
  836.   /**
  837.   * Append all of the matched elements to another, specified, set of elements.
  838.   * Returns all of the inserted elements.
  839.   *
  840.   * @example appendTo.php Usage Example: FluentDOM::appendTo()
  841.   * @param string|array|DOMNode|DOMNodeList|FluentDOM$selector 
  842.   * @return FluentDOM 
  843.   */
  844.   public function appendTo($selector{
  845.     $result $this->spawn();
  846.     $targetNodes $this->_getTargetNodes($selector);
  847.     if (!empty($targetNodes)) {
  848.       $result->push(
  849.         $this->_applyContentToNodes(
  850.           $targetNodes,
  851.           $this->_array,
  852.           array($this->_getHandler()'appendChildren')
  853.         )
  854.       );
  855.       $this->_removeNodes($this->_array);
  856.     }
  857.     return $result;
  858.   }
  859.  
  860.   /**
  861.   * Prepend content to the inside of every matched element.
  862.   *
  863.   * @example prepend.php Usage Example: FluentDOM::prepend()
  864.   * @param string|array|DOMNode|Iterator$content 
  865.   * @return FluentDOM 
  866.   */
  867.   public function prepend($content{
  868.     $result $this->spawn();
  869.     $result->push(
  870.       $this->_applyContentToNodes(
  871.         $this->_array,
  872.         $content,
  873.         array($this->_getHandler()'insertChildrenBefore')
  874.       )
  875.     );
  876.     return $result;
  877.   }
  878.  
  879.   /**
  880.   * Prepend all of the matched elements to another, specified, set of elements.
  881.   * Returns all of the inserted elements.
  882.   *
  883.   * @example prependTo.php Usage Example: FluentDOM::prependTo()
  884.   * @param string|array|DOMNode|DOMNodeList|FluentDOM$selector 
  885.   * @return FluentDOM list of all new elements
  886.   */
  887.   public function prependTo($selector{
  888.     $result $this->spawn();
  889.     $targetNodes $this->_getTargetNodes($selector);
  890.     if (!empty($targetNodes)) {
  891.       $result->push(
  892.         $this->_applyContentToNodes(
  893.           $targetNodes,
  894.           $this->_array,
  895.           array($this->_getHandler()'insertChildrenBefore')
  896.         )
  897.       );
  898.       $this->_removeNodes($this->_array);
  899.     }
  900.     return $result;
  901.   }
  902.  
  903.   /*
  904.   * Manipulation - Inserting Outside
  905.   */
  906.  
  907.   /**
  908.   * Insert content after each of the matched elements.
  909.   *
  910.   * @example after.php Usage Example: FluentDOM::after()
  911.   * @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content 
  912.   * @return FluentDOM 
  913.   */
  914.   public function after($content{
  915.     $result $this->spawn();
  916.     $result->push(
  917.       $this->_applyContentToNodes(
  918.         $this->_array$contentarray($this->_getHandler()'insertNodesAfter')
  919.       )
  920.     );
  921.     return $result;
  922.   }
  923.  
  924.   /**
  925.   * Insert content before each of the matched elements.
  926.   *
  927.   * @example before.php Usage Example: FluentDOM::before()
  928.   * @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content 
  929.   * @return FluentDOM 
  930.   */
  931.   public function before($content{
  932.     $result $this->spawn();
  933.     $result->push(
  934.       $this->_applyContentToNodes(
  935.         $this->_array$contentarray($this->_getHandler()'insertNodesBefore')
  936.       )
  937.     );
  938.     return $result;
  939.   }
  940.  
  941.   /**
  942.   * Insert all of the matched elements after another, specified, set of elements.
  943.   *
  944.   * @example insertAfter.php Usage Example: FluentDOM::insertAfter()
  945.   * @param string|array|DOMNode|DOMNodeList|Iterator$selector 
  946.   * @return FluentDOM 
  947.   */
  948.   public function insertAfter($selector{
  949.     $result $this->spawn();
  950.     $targetNodes $this->_getTargetNodes($selector);
  951.     if (!empty($targetNodes)) {
  952.       $result->push(
  953.         $this->_applyContentToNodes(
  954.           $targetNodes,
  955.           $this->_array,
  956.           array($this->_getHandler()'insertNodesAfter')
  957.         )
  958.       );
  959.       $this->_removeNodes($this->_array);
  960.     }
  961.     return $result;
  962.   }
  963.  
  964.   /**
  965.   * Insert all of the matched elements before another, specified, set of elements.
  966.   *
  967.   * @example insertBefore.php Usage Example: FluentDOM::insertBefore()
  968.   * @param string|array|DOMNode|DOMNodeList|Iterator$selector 
  969.   * @return FluentDOM 
  970.   */
  971.   public function insertBefore($selector{
  972.     $result $this->spawn();
  973.     $targetNodes $this->_getTargetNodes($selector);
  974.     if (!empty($targetNodes)) {
  975.       $result->push(
  976.         $this->_applyContentToNodes(
  977.           $targetNodes,
  978.           $this->_array,
  979.           array($this->_getHandler()'insertNodesBefore')
  980.         )
  981.       );
  982.       $this->_removeNodes($this->_array);
  983.     }
  984.     return $result;
  985.   }
  986.  
  987.   /*
  988.   * Manipulation - Inserting Around
  989.   */
  990.  
  991.   /**
  992.   * Wrap $content around a set of elements
  993.   *
  994.   * @param array $elements 
  995.   * @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content 
  996.   * @return FluentDOM 
  997.   */
  998.   protected function _wrap($elements$content{
  999.     $result array();
  1000.     $isCallback $this->_isCallback($contentFALSETRUE);
  1001.     if (!$isCallback{
  1002.       $wrapperTemplate $this->_getContentElement($content);
  1003.     }
  1004.     $simple FALSE;
  1005.     foreach ($elements as $index => $node{
  1006.       if ($isCallback{
  1007.         $wrapperTemplate NULL;
  1008.         $wrapContent call_user_func($content$node$index);
  1009.         if (!empty($wrapContent)) {
  1010.           $wrapperTemplate $this->_getContentElement($wrapContent);
  1011.         }
  1012.       }
  1013.       if ($wrapperTemplate instanceof DOMElement{
  1014.         $wrapper $wrapperTemplate->cloneNode(TRUE);
  1015.         if (!$simple{
  1016.           $targets $this->_match('.//*[count(*) = 0]'$wrapper);
  1017.         }
  1018.         if ($simple || $targets->length == 0{
  1019.           $target $wrapper;
  1020.           $simple TRUE;
  1021.         else {
  1022.           $target $targets->item(0);
  1023.         }
  1024.         if (isset($node->parentNode)) {
  1025.           $node->parentNode->insertBefore($wrapper$node);
  1026.         }
  1027.         $target->appendChild($node);
  1028.         $result[$node;
  1029.       }
  1030.     }
  1031.     return $result;
  1032.   }
  1033.  
  1034.   /**
  1035.   * Wrap each matched element with the specified content.
  1036.   *
  1037.   * If $content contains several elements the first one is used
  1038.   *
  1039.   * @example wrap.php Usage Example: FluentDOM::wrap()
  1040.   * @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content 
  1041.   * @return FluentDOM 
  1042.   */
  1043.   public function wrap($content{
  1044.     $result $this->spawn();
  1045.     $result->push($this->_wrap($this->_array$content));
  1046.     return $result;
  1047.   }
  1048.  
  1049.   /**
  1050.   * Wrap al matched elements with the specified content
  1051.   *
  1052.   * If the matched elemetns are not siblings, wrap each group of siblings.
  1053.   *
  1054.   * @example wrapAll.php Usage Example: FluentDOM::wrapAll()
  1055.   * @param string|array|DOMNode|Iterator$content 
  1056.   * @return FluentDOM 
  1057.   */
  1058.   public function wrapAll($content{
  1059.     $result $this->spawn();
  1060.     $current NULL;
  1061.     $counter 0;
  1062.     $groups array();
  1063.     //group elements by previous node - ignore whitespace text nodes
  1064.     foreach ($this->_array as $node{
  1065.       $previous $node->previousSibling;
  1066.       while ($previous instanceof DOMText && $previous->isWhitespaceInElementContent()) {
  1067.         $previous $previous->previousSibling;
  1068.       }
  1069.       if ($previous !== $current{
  1070.         $counter++;
  1071.       }
  1072.       $groups[$counter][$node;
  1073.       $current $node;
  1074.     }
  1075.     if (count($groups0{
  1076.       $wrapperTemplate $this->_getContentElement($content);
  1077.       $simple FALSE;
  1078.       foreach ($groups as $group{
  1079.         if (isset($group[0])) {
  1080.           $node $group[0];
  1081.           $wrapper $wrapperTemplate->cloneNode(TRUE);
  1082.           if (!$simple{
  1083.             $targets $this->_match('.//*[count(*) = 0]'$wrapper);
  1084.           }
  1085.           if ($simple || $targets->length == 0{
  1086.             $target $wrapper;
  1087.             $simple TRUE;
  1088.           else {
  1089.             $target $targets->item(0);
  1090.           }
  1091.           if (isset($node->parentNode)) {
  1092.             $node->parentNode->insertBefore($wrapper$node);
  1093.           }
  1094.           foreach ($group as $node{
  1095.             $target->appendChild($node);
  1096.           }
  1097.           $result->push($node);
  1098.         }
  1099.       }
  1100.     }
  1101.     return $result;
  1102.   }
  1103.  
  1104.   /**
  1105.   * Wrap the inner child contents of each matched element
  1106.   * (including text nodes) with an XML structure.
  1107.   *
  1108.   * @example wrapInner.php Usage Example: FluentDOM::wrapInner()
  1109.   * @param string|array|DOMNode|DOMNodeList|Iterator$content 
  1110.   * @return FluentDOM 
  1111.   */
  1112.   public function wrapInner($content{
  1113.     $result $this->spawn();
  1114.     $elements array();
  1115.     foreach ($this->_array as $node{
  1116.       foreach ($node->childNodes as $childNode{
  1117.         if ($this->_isNode($childNode)) {
  1118.           $elements[$childNode;
  1119.         }
  1120.       }
  1121.     }
  1122.     $result->push($this->_wrap($elements$content));
  1123.     return $result;
  1124.   }
  1125.  
  1126.   /*
  1127.   * Manipulation - Replacing
  1128.   */
  1129.  
  1130.   /**
  1131.   * Replaces all matched elements with the specified HTML or DOM elements.
  1132.   * This returns the JQuery element that was just replaced,
  1133.   * which has been removed from the DOM.
  1134.   *
  1135.   * @example replaceWith.php Usage Example: FluentDOM::replaceWith()
  1136.   * @param string|array|DOMNode|DOMNodeList|Iterator|callback|Closure$content 
  1137.   * @return FluentDOM 
  1138.   */
  1139.   public function replaceWith($content{
  1140.     $this->_applyContentToNodes(
  1141.       $this->_array$contentarray($this->_getHandler()'insertNodesBefore')
  1142.     );
  1143.     $this->_removeNodes($this->_array);
  1144.     return $this;
  1145.   }
  1146.  
  1147.  
  1148.   /**
  1149.   * Replaces the elements matched by the specified selector with the matched elements.
  1150.   *
  1151.   * @example replaceAll.php Usage Example: FluentDOM::replaceAll()
  1152.   * @param string|array|DOMNode|DOMNodeList|Iterator$selector 
  1153.   * @return FluentDOM 
  1154.   */
  1155.   public function replaceAll($selector{
  1156.     $result $this->spawn();
  1157.     $targetNodes $this->_getTargetNodes($selector);
  1158.     if (!empty($targetNodes)) {
  1159.       $this->_applyContentToNodes(
  1160.         $targetNodes,
  1161.         $this->_array,
  1162.         array($this->_getHandler()'insertNodesBefore')
  1163.       );
  1164.       $this->_removeNodes($targetNodes);
  1165.     }
  1166.     $this->_removeNodes($this->_array);
  1167.     return $result;
  1168.   }
  1169.  
  1170.   /*
  1171.   * Manipulation - Removing
  1172.   */
  1173.  
  1174.   /**
  1175.   * Remove all child nodes from the set of matched elements.
  1176.   *
  1177.   * This is the empty() method - but because empty
  1178.   * is a reserved word we can no declare it directly
  1179.   * @see __call
  1180.   *
  1181.   * @example empty.php Usage Example: FluentDOM:empty()
  1182.   * @return FluentDOM 
  1183.   */
  1184.   protected function _emptyNodes({
  1185.     foreach ($this->_array as $node{
  1186.       if ($node instanceof DOMElement ||
  1187.           $node instanceof DOMText{
  1188.         $node->nodeValue '';
  1189.       }
  1190.     }
  1191.     return $this;
  1192.   }
  1193.  
  1194.   /**
  1195.   * Removes all matched elements from the DOM.
  1196.   *
  1197.   * @example remove.php Usage Example: FluentDOM::remove()
  1198.   * @param string $expr XPath expression
  1199.   * @return FluentDOM removed elements
  1200.   */
  1201.   public function remove($expr NULL{
  1202.     $result $this->spawn();
  1203.     foreach ($this->_array as $node{
  1204.       if (isset($node->parentNode)) {
  1205.         if (empty($expr|| $this->_test($expr$node)) {
  1206.           $result->push($node->parentNode->removeChild($node));
  1207.         }
  1208.       }
  1209.     }
  1210.     return $result;
  1211.   }
  1212.  
  1213.   /*
  1214.   * Manipulation - Creation
  1215.   */
  1216.  
  1217.   /*
  1218.   * Manipulation - Copying
  1219.   */
  1220.  
  1221.   /**
  1222.   * Clone matched DOM Elements and select the clones.
  1223.   *
  1224.   * This is the clone() method - but because clone
  1225.   * is a reserved word we can no declare it directly
  1226.   * @see __call
  1227.   *
  1228.   * @example clone.php Usage Example: FluentDOM:clone()
  1229.   * @return FluentDOM 
  1230.   */
  1231.   protected function _cloneNodes({
  1232.     $result $this->spawn();
  1233.     foreach ($this->_array as $node{
  1234.       $result->push($node->cloneNode(TRUE));
  1235.     }
  1236.     return $result;
  1237.   }
  1238.  
  1239.   /*
  1240.   * Attributes - General
  1241.   */
  1242.  
  1243.   /**
  1244.   * Access a property on the first matched element or set the attribute(s) of all matched elements
  1245.   *
  1246.   * @example attr.php Usage Example: FluentDOM:attr() Read an attribute value.
  1247.   * @param string|array$attribute attribute name or attribute list
  1248.   * @param string|callback|Closure$value function callback($index, $value) or value
  1249.   * @return string|FluentDOMattribute value or $this
  1250.   */
  1251.   public function attr($attribute$value NULL{
  1252.     if (is_array($attribute&& count($attribute0{
  1253.       //expr is an array of attributes and values - set on each element
  1254.       foreach ($attribute as $key => $value{
  1255.         if ($this->_isQName($key)) {
  1256.           foreach ($this->_array as $node{
  1257.             if ($node instanceof DOMElement{
  1258.               $node->setAttribute($key$value);
  1259.             }
  1260.           }
  1261.         }
  1262.       }
  1263.     elseif (is_null($value)) {
  1264.       //empty value - read attribute from first element in list
  1265.       if ($this->_isQName($attribute&&
  1266.           count($this->_array0{
  1267.         $node $this->_array[0];
  1268.         if ($node instanceof DOMElement{
  1269.           return $node->getAttribute($attribute);
  1270.         }
  1271.       }
  1272.       return NULL;
  1273.     elseif (is_array($value||
  1274.               $value instanceof Closure{
  1275.       //value is function callback - execute it and set result on each element
  1276.       if ($this->_isQName($attribute)) {
  1277.         foreach ($this->_array as $index => $node{
  1278.           if ($node instanceof DOMElement{
  1279.             $newValue =
  1280.             $node->setAttribute(
  1281.               $attribute,
  1282.               call_user_func($value$node$index$node->getAttribute($attribute))
  1283.             );
  1284.           }
  1285.         }
  1286.       }
  1287.     else {
  1288.       // set attribute value of each element
  1289.       if ($this->_isQName($attribute)) {
  1290.         foreach ($this->_array as $node{
  1291.           if ($node instanceof DOMElement{
  1292.             $node->setAttribute($attribute(string)$value);
  1293.           }
  1294.         }
  1295.       }
  1296.     }
  1297.     return $this;
  1298.   }
  1299.  
  1300.   /**
  1301.   * Remove an attribute from each of the matched elements.
  1302.   *
  1303.   * @example removeAttr.php Usage Example: FluentDOM::removeAttr()
  1304.   * @param string $name 
  1305.   * @return FluentDOM 
  1306.   */
  1307.   public function removeAttr($name{
  1308.     if (!empty($name)) {
  1309.       if (is_string($name&& $name !== '*'{
  1310.         $attributes array($name);
  1311.       elseif (is_array($name)) {
  1312.         $attributes $name;
  1313.       elseif ($name !== '*'{
  1314.         throw new InvalidArgumentException();
  1315.       }
  1316.       foreach ($this->_array as $node{
  1317.         if ($node instanceof DOMElement{
  1318.           if ($name === '*'{
  1319.             for ($i $node->attributes->length 1$i >= 0$i--{
  1320.               $node->removeAttribute($node->attributes->item($i)->name);
  1321.             }
  1322.           else {
  1323.             foreach ($attributes as $attribute{
  1324.               if ($node->hasAttribute($attribute)) {
  1325.                 $node->removeAttribute($attribute);
  1326.               }
  1327.             }
  1328.           }
  1329.         }
  1330.       }
  1331.     }
  1332.     return $this;
  1333.   }
  1334.  
  1335.   /*
  1336.   * Attributes - Classes
  1337.   */
  1338.  
  1339.   /**
  1340.   * Adds the specified class(es) to each of the set of matched elements.
  1341.   *
  1342.   * @param string|callback|Closure$class 
  1343.   * @return FluentDOM 
  1344.   */
  1345.   public function addClass($class{
  1346.     return $this->toggleClass($classTRUE);
  1347.   }
  1348.  
  1349.   /**
  1350.   * Returns true if the specified class is present on at least one of the set of matched elements.
  1351.   *
  1352.   * @param string|callback|Closure$class 
  1353.   * @return boolean 
  1354.   */
  1355.   public function hasClass($class{
  1356.     foreach ($this->_array as $node{
  1357.       if ($node instanceof DOMElement &&
  1358.           $node->hasAttribute('class')) {
  1359.         $classes preg_split('(\s+)'trim($node->getAttribute('class')));
  1360.         if (in_array($class$classes)) {
  1361.           return TRUE;
  1362.         }
  1363.       }
  1364.     }
  1365.     return FALSE;
  1366.   }
  1367.  
  1368.   /**
  1369.   * Removes all or the specified class(es) from the set of matched elements.
  1370.   *
  1371.   * @param string|callback|Closure$class 
  1372.   * @return FluentDOM 
  1373.   */
  1374.   public function removeClass($class ''{
  1375.     return $this->toggleClass($classFALSE);
  1376.   }
  1377.  
  1378.   /**
  1379.   * Adds the specified class if the switch is TRUE,
  1380.   * removes the specified class if the switch is FALSE,
  1381.   * toggles the specified class if the switch is NULL.
  1382.   *
  1383.   * @example toggleClass.php Usage Example: FluentDOM::toggleClass()
  1384.   * @param string|callback|Closure$class 
  1385.   * @param NULL|boolean$switch toggle if NULL, add if TRUE, remove if FALSE
  1386.   * @return FluentDOM 
  1387.   */
  1388.   public function toggleClass($class$switch NULL{
  1389.     foreach ($this->_array as $index => $node{
  1390.       if ($node instanceof DOMElement{
  1391.         $isCallback $this->_isCallback($classFALSETRUE);
  1392.         if ($isCallback{
  1393.           $classString call_user_func(
  1394.             $class$node$index$node->getAttribute('class')
  1395.           );
  1396.         else {
  1397.           $classString $class;
  1398.         }
  1399.         if (empty($classString&& $switch == FALSE{
  1400.           if ($node->hasAttribute('class')) {
  1401.             $node->removeAttribute('class');
  1402.           }
  1403.         else {
  1404.           if ($node->hasAttribute('class')) {
  1405.             $currentClasses array_flip(
  1406.               preg_split('(\s+)'trim($node->getAttribute('class')))
  1407.             );
  1408.           else {
  1409.             $currentClasses array();
  1410.           }
  1411.           $toggledClasses array_unique(preg_split('(\s+)'trim($classString)));
  1412.           $modified FALSE;
  1413.           foreach ($toggledClasses as $toggledClass{
  1414.             if (isset($currentClasses[$toggledClass])) {
  1415.               if ($switch === FALSE || is_null($switch)) {
  1416.                 unset($currentClasses[$toggledClass]);
  1417.                 $modified TRUE;
  1418.               }
  1419.             else {
  1420.               if ($switch === TRUE || is_null($switch)) {
  1421.                 $currentClasses[$toggledClassTRUE;
  1422.                 $modified TRUE;
  1423.               }
  1424.             }
  1425.           }
  1426.           if ($modified{
  1427.             if (empty($currentClasses)) {
  1428.               $node->removeAttribute('class');
  1429.             else {
  1430.               $node->setAttribute('class'implode(' 'array_keys($currentClasses)));
  1431.             }
  1432.           }
  1433.         }
  1434.       }
  1435.     }
  1436.     return $this;
  1437.   }
  1438. }

Documentation generated on Sun, 26 Sep 2010 01:00:47 +0200 by phpDocumentor 1.4.3