home *** CD-ROM | disk | FTP | other *** search
/ Cricao de Sites - 650 Layouts Prontos / WebMasters.iso / Servidores / xampp-win32-1.6.7-installer.exe / php / PEAR / HTML / CSS.php < prev    next >
Encoding:
PHP Script  |  2008-07-02  |  88.2 KB  |  2,465 lines

  1. <?php
  2. /**
  3.  * Copyright (c) 2003-2008, Klaus Guenther <klaus@capitalfocus.org>
  4.  *                          Laurent Laville <pear@laurent-laville.org>
  5.  *
  6.  * All rights reserved.
  7.  *
  8.  * Redistribution and use in source and binary forms, with or without
  9.  * modification, are permitted provided that the following conditions
  10.  * are met:
  11.  *
  12.  *     * Redistributions of source code must retain the above copyright
  13.  *       notice, this list of conditions and the following disclaimer.
  14.  *     * Redistributions in binary form must reproduce the above copyright
  15.  *       notice, this list of conditions and the following disclaimer in the
  16.  *       documentation and/or other materials provided with the distribution.
  17.  *     * Neither the name of the authors nor the names of its contributors
  18.  *       may be used to endorse or promote products derived from this software
  19.  *       without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  22.  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
  25.  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  26.  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27.  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28.  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29.  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31.  * POSSIBILITY OF SUCH DAMAGE.
  32.  *
  33.  * PHP versions 4 and 5
  34.  *
  35.  * @category  HTML
  36.  * @package   HTML_CSS
  37.  * @author    Klaus Guenther <klaus@capitalfocus.org>
  38.  * @author    Laurent Laville <pear@laurent-laville.org>
  39.  * @copyright 2003-2008 Klaus Guenther, Laurent Laville
  40.  * @license   http://www.opensource.org/licenses/bsd-license.php  New BSD License
  41.  * @version   CVS: $Id: CSS.php,v 1.78 2008/03/07 08:46:56 farell Exp $
  42.  * @link      http://pear.php.net/package/HTML_CSS
  43.  * @since     File available since Release 0.2.0
  44.  */
  45.  
  46. require_once 'HTML/Common.php';
  47.  
  48. /**#@+
  49.  * Basic error codes
  50.  *
  51.  * @var        integer
  52.  * @since      0.3.3
  53.  */
  54. define('HTML_CSS_ERROR_UNKNOWN', -1);
  55. define('HTML_CSS_ERROR_INVALID_INPUT', -100);
  56. define('HTML_CSS_ERROR_INVALID_GROUP', -101);
  57. define('HTML_CSS_ERROR_NO_GROUP', -102);
  58. define('HTML_CSS_ERROR_NO_ELEMENT', -103);
  59. define('HTML_CSS_ERROR_NO_ELEMENT_PROPERTY', -104);
  60. define('HTML_CSS_ERROR_NO_FILE', -105);
  61. define('HTML_CSS_ERROR_WRITE_FILE', -106);
  62. define('HTML_CSS_ERROR_INVALID_SOURCE', -107);
  63. define('HTML_CSS_ERROR_INVALID_DEPS', -108);
  64. define('HTML_CSS_ERROR_NO_ATRULE', -109);
  65. /**#@-*/
  66.  
  67. /**
  68.  * Base class for CSS definitions
  69.  *
  70.  * This class handles the details for creating properly
  71.  * constructed CSS declarations.
  72.  *
  73.  * @category  HTML
  74.  * @package   HTML_CSS
  75.  * @author    Klaus Guenther <klaus@capitalfocus.org>
  76.  * @author    Laurent Laville <pear@laurent-laville.org>
  77.  * @copyright 2003-2008 Klaus Guenther, Laurent Laville
  78.  * @license   http://www.opensource.org/licenses/bsd-license.php  BSD
  79.  * @version   Release: 1.5.1
  80.  * @link      http://pear.php.net/package/HTML_CSS
  81.  * @since     Class available since Release 0.2.0
  82.  */
  83.  
  84. class HTML_CSS extends HTML_Common
  85. {
  86.     /**
  87.      * Options configuration list
  88.      *
  89.      * - xhtml :
  90.      *    Defines whether element selectors should be automatically lowercased.
  91.      *    Determines how parseSelectors treats the data.
  92.      *    @see setXhtmlCompliance()
  93.      * - tab :
  94.      *    Sets indent string.
  95.      *    @see setTab(), HTML_Common::setTab()
  96.      * - filename :
  97.      *    Name of file to be parsed.
  98.      *    @see parseFile()
  99.      * - cache :
  100.      *    Determines whether the nocache headers are sent.
  101.      *    Controls caching of the page.
  102.      *    @see setCache()
  103.      * - oneline :
  104.      *    Defines whether to output all properties on one line.
  105.      *    @see setSingleLineOutput()
  106.      * - charset :
  107.      *    Contains the character encoding string.
  108.      *    @see setCharset()
  109.      * - contentDisposition :
  110.      *    Contains the Content-Disposition filename.
  111.      *    @see setContentDisposition()
  112.      * - lineEnd :
  113.      *    Sets the line end style to Windows, Mac, Unix or a custom string.
  114.      *    @see setLineEnd(), HTML_Common::setLineEnd()
  115.      * - groupsfirst :
  116.      *    Determines whether to output groups before elements.
  117.      *    @see setOutputGroupsFirst()
  118.      * - allowduplicates :
  119.      *    Allow to have duplicate rules in selector. Useful for IE hack.
  120.      *
  121.      * @var        array
  122.      * @since      1.4.0
  123.      * @access     private
  124.      * @see        __set(), __get()
  125.      */
  126.     var $options;
  127.  
  128.     /**
  129.      * Contains the CSS definitions.
  130.      *
  131.      * @var        array
  132.      * @since      0.2.0
  133.      * @access     private
  134.      */
  135.     var $_css = array();
  136.  
  137.     /**
  138.      * Contains "alibis" (other elements that share a definition) of an element
  139.      * defined in CSS
  140.      *
  141.      * @var        array
  142.      * @since      0.2.0
  143.      * @access     private
  144.      */
  145.     var $_alibis = array();
  146.  
  147.     /**
  148.      * Contains last assigned index for duplicate styles
  149.      *
  150.      * @var        array
  151.      * @since      0.3.0
  152.      * @access     private
  153.      */
  154.     var $_duplicateCounter = 0;
  155.  
  156.     /**
  157.      * Contains grouped styles
  158.      *
  159.      * @var        array
  160.      * @since      0.3.0
  161.      * @access     private
  162.      */
  163.     var $_groups = array();
  164.  
  165.     /**
  166.      * Number of CSS definition groups
  167.      *
  168.      * @var        int
  169.      * @since      0.3.0
  170.      * @access     private
  171.      */
  172.     var $_groupCount = 0;
  173.  
  174.     /**
  175.      * Error message callback.
  176.      * This will be used to generate the error message
  177.      * from the error code.
  178.      *
  179.      * @var        false|string|array
  180.      * @since      1.0.0
  181.      * @access     private
  182.      * @see        _initErrorStack()
  183.      */
  184.     var $_callback_message = false;
  185.  
  186.     /**
  187.      * Error context callback.
  188.      * This will be used to generate the error context for an error.
  189.      *
  190.      * @var        false|string|array
  191.      * @since      1.0.0
  192.      * @access     private
  193.      * @see        _initErrorStack()
  194.      */
  195.     var $_callback_context = false;
  196.  
  197.     /**
  198.      * Error push callback.
  199.      * The return value will be used to determine whether to allow
  200.      * an error to be pushed or logged.
  201.      *
  202.      * @var        false|string|array
  203.      * @since      1.0.0
  204.      * @access     private
  205.      * @see        _initErrorStack()
  206.      */
  207.     var $_callback_push = false;
  208.  
  209.     /**
  210.      * Error callback.
  211.      * User function that decides what to do with error (display, log, ...)
  212.      *
  213.      * @var        false|string|array
  214.      * @since      1.4.0
  215.      * @access     private
  216.      * @see        _initErrorStack()
  217.      */
  218.     var $_callback_error = false;
  219.  
  220.     /**
  221.      * Error handler callback.
  222.      * This will handle any errors raised by this package.
  223.      *
  224.      * @var        false|string|array
  225.      * @since      1.0.0
  226.      * @access     private
  227.      * @see        _initErrorStack()
  228.      */
  229.     var $_callback_errorhandler = false;
  230.  
  231.     /**
  232.      * Associative array of key-value pairs
  233.      * that are used to specify any handler-specific settings.
  234.      *
  235.      * @var        array
  236.      * @since      1.0.0
  237.      * @access     private
  238.      * @see        _initErrorStack()
  239.      */
  240.     var $_errorhandler_options = array();
  241.  
  242.     /**
  243.      * Last error that might occured
  244.      *
  245.      * @var        false|mixed
  246.      * @since      1.0.0RC2
  247.      * @access     private
  248.      * @see        isError(), raiseError()
  249.      */
  250.     var $_lastError = false;
  251.  
  252.  
  253.     /**
  254.      * Class constructor
  255.      *
  256.      * Class constructors :
  257.      * Zend Engine 1 uses HTML_CSS, while Zend Engine 2 uses __construct
  258.      *
  259.      * @param array $attributes (optional) Pass options to the constructor.
  260.      *                          Valid options are :
  261.      *                           - xhtml (sets xhtml compliance),
  262.      *                           - tab (sets indent string),
  263.      *                           - filename (name of file to be parsed),
  264.      *                           - cache (determines whether the nocache headers
  265.      *                             are sent),
  266.      *                           - oneline (whether to output each definition
  267.      *                             on one line),
  268.      *                           - groupsfirst (determines whether to output groups
  269.      *                             before elements)
  270.      *                           - allowduplicates (allow to have duplicate rules
  271.      *                             in selector)
  272.      * @param array $errorPrefs (optional) has to configure error handler
  273.      *
  274.      * @since      version 0.2.0 (2003-07-31)
  275.      * @access     public
  276.      */
  277.     function HTML_CSS($attributes = array(), $errorPrefs = array())
  278.     {
  279.         $this->__construct($attributes, $errorPrefs);
  280.     }
  281.  
  282.     /**
  283.      * Class constructor
  284.      *
  285.      * Class constructors :
  286.      * Zend Engine 1 uses HTML_CSS, while Zend Engine 2 uses __construct
  287.      *
  288.      * @param array $attributes (optional) Pass options to the constructor.
  289.      *                          Valid options are :
  290.      *                           - xhtml (sets xhtml compliance),
  291.      *                           - tab (sets indent string),
  292.      *                           - filename (name of file to be parsed),
  293.      *                           - cache (determines whether the nocache headers
  294.      *                             are sent),
  295.      *                           - oneline (whether to output each definition
  296.      *                             on one line),
  297.      *                           - groupsfirst (determines whether to output groups
  298.      *                             before elements)
  299.      *                           - allowduplicates (allow to have duplicate rules
  300.      *                             in selector)
  301.      * @param array $errorPrefs (optional) has to configure error handler
  302.      *
  303.      * @since      version 1.4.0 (2007-12-13)
  304.      * @access     protected
  305.      */
  306.     function __construct($attributes = array(), $errorPrefs = array())
  307.     {
  308.         $this->_initErrorStack($errorPrefs);
  309.  
  310.         if (!is_array($attributes)) {
  311.             $attributes = array($attributes);
  312.         }
  313.         if ($attributes) {
  314.             $attributes = $this->_parseAttributes($attributes);
  315.         }
  316.  
  317.         $tab = '  ';
  318.         $eol = strtolower(substr(PHP_OS, 0, 3)) == 'win' ? "\r\n" : "\n";
  319.  
  320.         // default options
  321.         $this->options = array('xhtml' => true, 'tab' => $tab, 'cache' => true,
  322.             'oneline' => false, 'charset' => 'iso-8859-1',
  323.             'contentDisposition' => false, 'lineEnd' => $eol,
  324.             'groupsfirst' => true, 'allowduplicates' => false);
  325.         // and options that come directly from HTML_Common
  326.         $this->setTab($tab);
  327.         $this->setLineEnd($eol);
  328.  
  329.         // apply user options
  330.         foreach ($attributes as $opt => $val) {
  331.             $this->__set($opt, $val);
  332.         }
  333.     }
  334.  
  335.     /**
  336.      * Return the current API version
  337.      *
  338.      * Since 1.0.0 a string is returned rather than a float (for previous versions).
  339.      *
  340.      * @return     string                   compatible with php.version_compare()
  341.      * @since      version 0.2.0 (2003-07-31)
  342.      * @access     public
  343.      */
  344.     function apiVersion()
  345.     {
  346.         return '1.5.0';
  347.     }
  348.  
  349.     /**
  350.      * Set option for the class
  351.      *
  352.      * Set an individual option value. Option must exist.
  353.      *
  354.      * @param string $option Name of option to set
  355.      * @param string $val    Value of option to set
  356.      *
  357.      * @return void
  358.      * @since  version 1.4.0 (2007-12-13)
  359.      * @access public
  360.      */
  361.     function __set($option, $val)
  362.     {
  363.         if (isset($this->options[$option])) {
  364.             $this->options[$option] = $val;
  365.         }
  366.     }
  367.  
  368.     /**
  369.      * Get option for the class
  370.      *
  371.      * Return current value of an individual option. If option does not exist,
  372.      * returns value is NULL.
  373.      *
  374.      * @param string $option Name of option to set
  375.      *
  376.      * @return mixed
  377.      * @since  version 1.4.0 (2007-12-13)
  378.      * @access public
  379.      */
  380.     function __get($option)
  381.     {
  382.         if (isset($this->options[$option])) {
  383.             $r = $this->options[$option];
  384.         } else {
  385.             $r = null;
  386.         }
  387.         return $r;
  388.     }
  389.  
  390.     /**
  391.      * Return all options for the class
  392.      *
  393.      * Return all configuration options at once
  394.      *
  395.      * @return array
  396.      * @since  version 1.5.0 (2008-01-15)
  397.      * @access public
  398.      */
  399.     function getOptions()
  400.     {
  401.         return $this->options;
  402.     }
  403.  
  404.     /**
  405.      * Set tab value
  406.      *
  407.      * Sets the string used to indent HTML
  408.      *
  409.      * @param string $string String used to indent ("\11", "\t", '  ', etc.).
  410.      *
  411.      * @since     version 1.4.0 (2007-12-13)
  412.      * @access    public
  413.      * @return    void
  414.      */
  415.     function setTab($string)
  416.     {
  417.         $this->__set('tab', $string);
  418.         parent::setTab($string);
  419.     }
  420.  
  421.     /**
  422.      * Set lineend value
  423.      *
  424.      * Set the line end style to Windows, Mac, Unix or a custom string
  425.      *
  426.      * @param string $style "win", "mac", "unix" or custom string.
  427.      *
  428.      * @since   version 1.4.0 (2007-12-13)
  429.      * @access  public
  430.      * @return  void
  431.      */
  432.     function setLineEnd($style)
  433.     {
  434.         $this->__set('lineEnd', $style);
  435.         parent::setLineEnd($style);
  436.     }
  437.  
  438.     /**
  439.      * Set oneline flag
  440.      *
  441.      * Determine whether definitions are output on a single line or multi lines
  442.      *
  443.      * @param bool $value flag to true if single line, false for multi lines
  444.      *
  445.      * @return     void|PEAR_Error
  446.      * @since      version 0.3.3 (2004-05-20)
  447.      * @access     public
  448.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  449.      */
  450.     function setSingleLineOutput($value)
  451.     {
  452.         if (!is_bool($value)) {
  453.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  454.                 array('var' => '$value',
  455.                       'was' => gettype($value),
  456.                       'expected' => 'boolean',
  457.                       'paramnum' => 1));
  458.         }
  459.         $this->options['oneline'] = $value;
  460.     }
  461.  
  462.     /**
  463.      * Set groupsfirst flag
  464.      *
  465.      * Determine whether groups are output before elements or not
  466.      *
  467.      * @param bool $value flag to true if groups are output before elements,
  468.      *                    false otherwise
  469.      *
  470.      * @return     void|PEAR_Error
  471.      * @since      version 0.3.3 (2004-05-20)
  472.      * @access     public
  473.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  474.      */
  475.     function setOutputGroupsFirst($value)
  476.     {
  477.         if (!is_bool($value)) {
  478.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  479.                 array('var' => '$value',
  480.                       'was' => gettype($value),
  481.                       'expected' => 'boolean',
  482.                       'paramnum' => 1));
  483.         }
  484.         $this->options['groupsfirst'] = $value;
  485.     }
  486.  
  487.     /**
  488.      * Parse a string containing selector(s)
  489.      *
  490.      * It processes it and returns an array or string containing
  491.      * modified selectors (depends on XHTML compliance setting;
  492.      * defaults to ensure lowercase element names)
  493.      *
  494.      * @param string $selectors  Selector string
  495.      * @param int    $outputMode (optional) 0 = string; 1 = array; 2 = deep array
  496.      *
  497.      * @return     mixed|PEAR_Error
  498.      * @since      version 0.3.2 (2004-03-24)
  499.      * @access     protected
  500.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  501.      */
  502.     function parseSelectors($selectors, $outputMode = 0)
  503.     {
  504.         if (!is_string($selectors)) {
  505.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  506.                 array('var' => '$selectors',
  507.                       'was' => gettype($selectors),
  508.                       'expected' => 'string',
  509.                       'paramnum' => 1));
  510.  
  511.         } elseif (!is_int($outputMode)) {
  512.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  513.                 array('var' => '$outputMode',
  514.                       'was' => gettype($outputMode),
  515.                       'expected' => 'integer',
  516.                       'paramnum' => 2));
  517.  
  518.         } elseif ($outputMode < 0 || $outputMode > 3) {
  519.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  520.                 array('var' => '$outputMode',
  521.                       'was' => $outputMode,
  522.                       'expected' => '0 | 1 | 2 | 3',
  523.                       'paramnum' => 2));
  524.         }
  525.  
  526.         $selectors_array =  explode(',', $selectors);
  527.         $i               = 0;
  528.         foreach ($selectors_array as $selector) {
  529.             // trim to remove possible whitespace
  530.             $selector = trim($this->collapseInternalSpaces($selector));
  531.             if (strpos($selector, ' ')) {
  532.                 $sel_a = array();
  533.                 foreach (explode(' ', $selector) as $sub_selector) {
  534.                     $sel_a[] = $this->parseSelectors($sub_selector, $outputMode);
  535.                 }
  536.                 if ($outputMode === 0) {
  537.                         $array[$i] = implode(' ', $sel_a);
  538.                 } else {
  539.                     $sel_a2 = array();
  540.                     foreach ($sel_a as $sel_a_temp) {
  541.                         $sel_a2 = array_merge($sel_a2, $sel_a_temp);
  542.                     }
  543.                     if ($outputMode == 2) {
  544.                         $array[$i]['inheritance'] = $sel_a2;
  545.                     } else {
  546.                         $array[$i] = implode(' ', $sel_a2);
  547.                     }
  548.                 }
  549.                 $i++;
  550.             } else {
  551.                 // initialize variables
  552.                 $element = '';
  553.                 $id      = '';
  554.                 $class   = '';
  555.                 $pseudo  = '';
  556.  
  557.                 if (strpos($selector, ':') !== false) {
  558.                     $pseudo   = strstr($selector, ':');
  559.                     $selector = substr($selector, 0, strpos($selector, ':'));
  560.                 }
  561.                 if (strpos($selector, '.') !== false) {
  562.                     $class    = strstr($selector, '.');
  563.                     $selector = substr($selector, 0, strpos($selector, '.'));
  564.                 }
  565.                 if (strpos($selector, '#') !== false) {
  566.                     $id       = strstr($selector, '#');
  567.                     $selector = substr($selector, 0, strpos($selector, '#'));
  568.                 }
  569.                 if ($selector != '') {
  570.                     $element = $selector;
  571.                 }
  572.                 if ($this->options['xhtml']) {
  573.                     $element = strtolower($element);
  574.                     $pseudo  = strtolower($pseudo);
  575.                 }
  576.                 if ($outputMode == 2) {
  577.                     $array[$i]['element'] = $element;
  578.                     $array[$i]['id']      = $id;
  579.                     $array[$i]['class']   = $class;
  580.                     $array[$i]['pseudo']  = $pseudo;
  581.                 } else {
  582.                     $array[$i] = $element.$id.$class.$pseudo;
  583.                 }
  584.                 $i++;
  585.             }
  586.         }
  587.         if ($outputMode == 0) {
  588.             $output = implode(', ', $array);
  589.             return $output;
  590.         } else {
  591.             return $array;
  592.         }
  593.     }
  594.  
  595.     /**
  596.      * Strips excess spaces in string.
  597.      *
  598.      * @param string $subject string to format
  599.      *
  600.      * @return     string
  601.      * @since      version 0.3.2 (2004-03-24)
  602.      * @access     protected
  603.      */
  604.     function collapseInternalSpaces($subject)
  605.     {
  606.         $string = preg_replace('/\s+/', ' ', $subject);
  607.         return $string;
  608.     }
  609.  
  610.     /**
  611.      * sort and move simple declarative At-Rules to the top
  612.      *
  613.      * @return     void
  614.      * @access     protected
  615.      * @since      version 1.5.0 (2008-01-15)
  616.      */
  617.     function sortAtRules()
  618.     {
  619.         // split simple declarative At-Rules from the other
  620.         $return = array('atrules' => array(), 'newcss' => array());
  621.  
  622.         foreach ($this->_css as $key => $value) {
  623.             if ((0 === strpos($key, "@")) && (1 !== strpos($key, "-"))) {
  624.                 $return["atrules"][$key] = $value;
  625.             } else {
  626.                 $return["newcss"][$key] = $value;
  627.             }
  628.         }
  629.  
  630.         // bring sprecial rules to the top
  631.         foreach (array('@namespace', '@import', '@charset') as $name) {
  632.             if (isset($return['atrules'][$name])) {
  633.                 $rule = array($name => $return['atrules'][$name]);
  634.                 unset($return['atrules'][$name]);
  635.                 $return['atrules'] = $rule + $return['atrules'];
  636.             }
  637.         }
  638.  
  639.         $this->_css = $return['atrules'] + $return['newcss'];
  640.     }
  641.  
  642.     /**
  643.      * Set xhtml flag
  644.      *
  645.      * Active or not the XHTML mode compliant
  646.      *
  647.      * @param bool $value flag to true if XHTML compliance needed,
  648.      *                    false otherwise
  649.      *
  650.      * @return     void|PEAR_Error
  651.      * @since      version 0.3.2 (2004-03-24)
  652.      * @access     public
  653.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  654.      */
  655.     function setXhtmlCompliance($value)
  656.     {
  657.         if (!is_bool($value)) {
  658.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  659.                 array('var' => '$value',
  660.                       'was' => gettype($value),
  661.                       'expected' => 'boolean',
  662.                       'paramnum' => 1));
  663.         }
  664.         $this->options['xhtml'] = $value;
  665.     }
  666.  
  667.     /**
  668.      * Return list of supported At-Rules
  669.      *
  670.      * Return the list of At-Rules supported by API 1.5.0 of HTML_CSS
  671.      *
  672.      * @return void
  673.      * @since  version 1.5.0 (2008-01-15)
  674.      * @access public
  675.      */
  676.     function getAtRulesList()
  677.     {
  678.         $atRules = array('@charset', '@font-face',
  679.                          '@import', '@media', '@page', '@namespace');
  680.         return $atRules;
  681.     }
  682.  
  683.     /**
  684.      * Create a new simple declarative At-Rule
  685.      *
  686.      * Create a simple at-rule without declaration style blocks.
  687.      * That include @charset, @import and @namespace
  688.      *
  689.      * @param string $atKeyword at-rule keyword
  690.      * @param string $arguments argument list for @charset, @import or @namespace
  691.      *
  692.      * @return void|PEAR_Error
  693.      * @since  version 1.5.0 (2008-01-15)
  694.      * @access public
  695.      * @throws HTML_CSS_ERROR_INVALID_INPUT
  696.      * @see    unsetAtRule()
  697.      */
  698.     function createAtRule($atKeyword, $arguments = '')
  699.     {
  700.         $allowed_atrules = array('@charset', '@import', '@namespace');
  701.  
  702.         if (!is_string($atKeyword)) {
  703.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  704.                 array('var' => '$atKeyword',
  705.                       'was' => gettype($atKeyword),
  706.                       'expected' => 'string',
  707.                       'paramnum' => 1));
  708.  
  709.         } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
  710.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  711.                 array('var' => '$atKeyword',
  712.                       'was' => $atKeyword,
  713.                       'expected' => implode('|', $allowed_atrules),
  714.                       'paramnum' => 1));
  715.  
  716.         } elseif (!is_string($arguments)) {
  717.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  718.                 array('var' => '$arguments',
  719.                       'was' => gettype($arguments),
  720.                       'expected' => 'string',
  721.                       'paramnum' => 2));
  722.         }
  723.  
  724.         if (empty($arguments)) {
  725.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  726.                 array('var' => '$arguments',
  727.                       'was' => $arguments,
  728.                       'expected' => 'not empty value',
  729.                       'paramnum' => 2));
  730.         } else {
  731.             $this->_css[strtolower($atKeyword)] = array($arguments => '');
  732.         }
  733.     }
  734.  
  735.     /**
  736.      * Remove an existing At-Rule
  737.      *
  738.      * Remove an existing and supported at-rule. See HTML_CSS::getAtRulesList()
  739.      * for a full list of supported At-Rules.
  740.      *
  741.      * @param string $atKeyword at-rule keyword
  742.      *
  743.      * @return void|PEAR_Error
  744.      * @since  version 1.5.0 (2008-01-15)
  745.      * @access public
  746.      * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ATRULE
  747.      */
  748.     function unsetAtRule($atKeyword)
  749.     {
  750.         $allowed_atrules = $this->getAtRulesList();
  751.  
  752.         if (!is_string($atKeyword)) {
  753.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  754.                 array('var' => '$atKeyword',
  755.                       'was' => gettype($atKeyword),
  756.                       'expected' => 'string',
  757.                       'paramnum' => 1));
  758.  
  759.         } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
  760.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  761.                 array('var' => '$atKeyword',
  762.                       'was' => $atKeyword,
  763.                       'expected' => implode('|', $allowed_atrules),
  764.                       'paramnum' => 1));
  765.  
  766.         } elseif (!isset($this->_css[strtolower($atKeyword)])) {
  767.             return $this->raiseError(HTML_CSS_ERROR_NO_ATRULE, 'error',
  768.                 array('identifier' => $atKeyword));
  769.         }
  770.  
  771.         unset($this->_css[strtolower($atKeyword)]);
  772.     }
  773.  
  774.     /**
  775.      * Define a conditional/informative At-Rule
  776.      *
  777.      * Set arguments and declaration style block for at-rules that follow :
  778.      * "@media, @page, @font-face"
  779.      *
  780.      * @param string $atKeyword  at-rule keyword
  781.      * @param string $arguments  argument list
  782.      *                           (optional for @font-face)
  783.      * @param string $selectors  selectors of declaration style block
  784.      *                           (optional for @media, @page, @font-face)
  785.      * @param string $property   property of a single declaration style block
  786.      * @param string $value      value of a single declaration style block
  787.      * @param bool   $duplicates (optional) Allow or disallow duplicates
  788.      *
  789.      * @return void|PEAR_Error
  790.      * @since  version 1.5.0 (2008-01-15)
  791.      * @access public
  792.      * @throws HTML_CSS_ERROR_INVALID_INPUT
  793.      * @see    getAtRuleStyle()
  794.      */
  795.     function setAtRuleStyle($atKeyword, $arguments, $selectors, $property, $value,
  796.         $duplicates = null)
  797.     {
  798.         $allowed_atrules = array('@media', '@page', '@font-face');
  799.  
  800.         if (!is_string($atKeyword)) {
  801.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  802.                 array('var' => '$atKeyword',
  803.                       'was' => gettype($atKeyword),
  804.                       'expected' => 'string',
  805.                       'paramnum' => 1));
  806.  
  807.         } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
  808.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  809.                 array('var' => '$atKeyword',
  810.                       'was' => $atKeyword,
  811.                       'expected' => implode('|', $allowed_atrules),
  812.                       'paramnum' => 1));
  813.  
  814.         } elseif (empty($arguments) && strtolower($atKeyword) != '@font-face') {
  815.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  816.                 array('var' => '$arguments',
  817.                       'was' => $arguments,
  818.                       'expected' => 'not empty value for '. $atKeyword,
  819.                       'paramnum' => 2));
  820.  
  821.         } elseif (!is_string($selectors)) {
  822.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  823.                 array('var' => '$selectors',
  824.                       'was' => gettype($selectors),
  825.                       'expected' => 'string',
  826.                       'paramnum' => 3));
  827.  
  828.         } elseif (!is_string($property)) {
  829.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  830.                 array('var' => '$property',
  831.                       'was' => gettype($property),
  832.                       'expected' => 'string',
  833.                       'paramnum' => 4));
  834.  
  835.         } elseif (!is_string($value)) {
  836.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  837.                 array('var' => '$value',
  838.                       'was' => gettype($value),
  839.                       'expected' => 'string',
  840.                       'paramnum' => 5));
  841.  
  842.         } elseif (empty($property)) {
  843.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  844.                 array('var' => '$property',
  845.                       'was' => $property,
  846.                       'expected' => 'no empty string',
  847.                       'paramnum' => 4));
  848.  
  849.         } elseif (empty($value)) {
  850.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  851.                 array('var' => '$value',
  852.                       'was' => gettype($value),
  853.                       'expected' => 'no empty string',
  854.                       'paramnum' => 5));
  855.         }
  856.  
  857.         if (!isset($duplicates)) {
  858.             $duplicates = $this->__get('allowduplicates');
  859.         }
  860.  
  861.         $atKeyword = strtolower($atKeyword);
  862.  
  863.         if ($selectors == '') {
  864.             $this->_css[$atKeyword][$arguments][$selectors][$property] = $value;
  865.         } else {
  866.             $selectors = $this->parseSelectors($selectors, 1);
  867.             foreach ($selectors as $selector) {
  868.                 $this->_css[$atKeyword][$arguments][$selector][$property] = $value;
  869.             }
  870.         }
  871.     }
  872.  
  873.     /**
  874.      * Get style value of an existing At-Rule
  875.      *
  876.      * Retrieve arguments or style value of an existing At-Rule.
  877.      * See HTML_CSS::getAtRulesList() for a full list of supported At-Rules.
  878.      *
  879.      * @param string $atKeyword at-rule keyword
  880.      * @param string $arguments argument list
  881.      *                          (optional for @font-face)
  882.      * @param string $selectors selectors of declaration style block
  883.      *                          (optional for @media, @page, @font-face)
  884.      * @param string $property  property of a single declaration style block
  885.      *
  886.      * @return void|PEAR_Error
  887.      * @since  version 1.5.0 (2008-01-15)
  888.      * @access public
  889.      * @throws HTML_CSS_ERROR_INVALID_INPUT
  890.      * @see    setAtRuleStyle()
  891.      */
  892.     function getAtRuleStyle($atKeyword, $arguments, $selectors, $property)
  893.     {
  894.         $allowed_atrules = $this->getAtRulesList();
  895.  
  896.         if (!is_string($atKeyword)) {
  897.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  898.                 array('var' => '$atKeyword',
  899.                       'was' => gettype($atKeyword),
  900.                       'expected' => 'string',
  901.                       'paramnum' => 1));
  902.  
  903.         } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
  904.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  905.                 array('var' => '$atKeyword',
  906.                       'was' => $atKeyword,
  907.                       'expected' => implode('|', $allowed_atrules),
  908.                       'paramnum' => 1));
  909.  
  910.         } elseif (!is_string($arguments)) {
  911.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  912.                 array('var' => '$arguments',
  913.                       'was' => gettype($arguments),
  914.                       'expected' => 'string',
  915.                       'paramnum' => 2));
  916.  
  917.         } elseif (!is_string($selectors)) {
  918.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  919.                 array('var' => '$selectors',
  920.                       'was' => gettype($selectors),
  921.                       'expected' => 'string',
  922.                       'paramnum' => 3));
  923.  
  924.         } elseif (!is_string($property)) {
  925.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  926.                 array('var' => '$property',
  927.                       'was' => gettype($property),
  928.                       'expected' => 'string',
  929.                       'paramnum' => 4));
  930.         }
  931.  
  932.         if (isset($this->_css[$atKeyword][$arguments][$selectors][$property])) {
  933.             $val = $this->_css[$atKeyword][$arguments][$selectors][$property];
  934.         } else {
  935.             $val = null;
  936.         }
  937.         return $val;
  938.     }
  939.  
  940.     /**
  941.      * Create a new CSS definition group
  942.      *
  943.      * Create a new CSS definition group. Return an integer identifying the group.
  944.      *
  945.      * @param string $selectors Selector(s) to be defined, comma delimited.
  946.      * @param mixed  $group     (optional) Group identifier. If not passed,
  947.      *                          will return an automatically assigned integer.
  948.      *
  949.      * @return     mixed|PEAR_Error
  950.      * @since      version 0.3.0 (2003-11-03)
  951.      * @access     public
  952.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_INVALID_GROUP
  953.      * @see        unsetGroup()
  954.      */
  955.     function createGroup($selectors, $group = null)
  956.     {
  957.         if (!is_string($selectors)) {
  958.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  959.                 array('var' => '$selectors',
  960.                       'was' => gettype($selectors),
  961.                       'expected' => 'string',
  962.                       'paramnum' => 1));
  963.         }
  964.  
  965.         if (!isset($group)) {
  966.             $this->_groupCount++;
  967.             $group = $this->_groupCount;
  968.         } else {
  969.             if (isset($this->_groups['@-'.$group])) {
  970.                 return $this->raiseError(HTML_CSS_ERROR_INVALID_GROUP, 'error',
  971.                     array('identifier' => $group));
  972.             }
  973.         }
  974.  
  975.         $groupIdent = '@-'.$group;
  976.  
  977.         $selectors = $this->parseSelectors($selectors, 1);
  978.         foreach ($selectors as $selector) {
  979.             $this->_alibis[$selector][] = $groupIdent;
  980.         }
  981.  
  982.         $this->_groups[$groupIdent] = $selectors;
  983.  
  984.         return $group;
  985.     }
  986.  
  987.     /**
  988.      * Remove a CSS definition group
  989.      *
  990.      * Remove a CSS definition group. Use the same identifier as for group creation.
  991.      *
  992.      * @param mixed $group CSS definition group identifier
  993.      *
  994.      * @return     void|PEAR_Error
  995.      * @since      version 0.3.0 (2003-11-03)
  996.      * @access     public
  997.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
  998.      * @see        createGroup()
  999.      */
  1000.     function unsetGroup($group)
  1001.     {
  1002.         if (!is_int($group) && !is_string($group)) {
  1003.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1004.                 array('var' => '$group',
  1005.                       'was' => gettype($group),
  1006.                       'expected' => 'integer | string',
  1007.                       'paramnum' => 1));
  1008.         }
  1009.         $groupIdent = '@-'.$group;
  1010.         if ($group < 0 || $group > $this->_groupCount ||
  1011.             !isset($this->_groups[$groupIdent])) {
  1012.             return $this->raiseError(HTML_CSS_ERROR_NO_GROUP, 'error',
  1013.                 array('identifier' => $group));
  1014.         }
  1015.  
  1016.         $alibis = $this->_alibis;
  1017.         foreach ($alibis as $selector => $data) {
  1018.             foreach ($data as $key => $value) {
  1019.                 if ($value == $groupIdent) {
  1020.                     unset($this->_alibis[$selector][$key]);
  1021.                     break;
  1022.                 }
  1023.             }
  1024.             if (count($this->_alibis[$selector]) == 0) {
  1025.                 unset($this->_alibis[$selector]);
  1026.             }
  1027.         }
  1028.         unset($this->_groups[$groupIdent]);
  1029.         unset($this->_css[$groupIdent]);
  1030.     }
  1031.  
  1032.     /**
  1033.      * Set or add a CSS definition for a CSS group
  1034.      *
  1035.      * Define the new value of a property for a CSS group. The group should exist.
  1036.      * If not, use HTML_CSS::createGroup first
  1037.      *
  1038.      * @param mixed  $group      CSS definition group identifier
  1039.      * @param string $property   Property defined
  1040.      * @param string $value      Value assigned
  1041.      * @param bool   $duplicates (optional) Allow or disallow duplicates.
  1042.      *
  1043.      * @return     void|int|PEAR_Error     Returns an integer if duplicates
  1044.      *                                     are allowed.
  1045.      * @since      version 0.3.0 (2003-11-03)
  1046.      * @access     public
  1047.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
  1048.      * @see        getGroupStyle()
  1049.      */
  1050.     function setGroupStyle($group, $property, $value, $duplicates = null)
  1051.     {
  1052.         if (!is_int($group) && !is_string($group)) {
  1053.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1054.                 array('var' => '$group',
  1055.                       'was' => gettype($group),
  1056.                       'expected' => 'integer | string',
  1057.                       'paramnum' => 1));
  1058.  
  1059.         } elseif (!is_string($property)) {
  1060.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1061.                 array('var' => '$property',
  1062.                       'was' => gettype($property),
  1063.                       'expected' => 'string',
  1064.                       'paramnum' => 2));
  1065.  
  1066.         } elseif (!is_string($value)) {
  1067.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1068.                 array('var' => '$value',
  1069.                       'was' => gettype($value),
  1070.                       'expected' => 'string',
  1071.                       'paramnum' => 3));
  1072.  
  1073.         } elseif (isset($duplicates) && !is_bool($duplicates)) {
  1074.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1075.                 array('var' => '$duplicates',
  1076.                       'was' => gettype($duplicates),
  1077.                       'expected' => 'bool',
  1078.                       'paramnum' => 4));
  1079.         }
  1080.  
  1081.         if (!isset($duplicates)) {
  1082.             $duplicates = $this->__get('allowduplicates');
  1083.         }
  1084.  
  1085.         $groupIdent = '@-'.$group;
  1086.         if ($group < 0 || $group > $this->_groupCount ||
  1087.             !isset($this->_groups[$groupIdent])) {
  1088.             return $this->raiseError(HTML_CSS_ERROR_NO_GROUP, 'error',
  1089.                 array('identifier' => $group));
  1090.         }
  1091.  
  1092.         if ($duplicates === true) {
  1093.             $this->_duplicateCounter++;
  1094.             $this->_css[$groupIdent][$this->_duplicateCounter][$property] = $value;
  1095.             return $this->_duplicateCounter;
  1096.         } else {
  1097.             $this->_css[$groupIdent][$property] = $value;
  1098.         }
  1099.     }
  1100.  
  1101.     /**
  1102.      * Return CSS definition for a CSS group
  1103.      *
  1104.      * Get the CSS definition for group created by setGroupStyle()
  1105.      *
  1106.      * @param mixed  $group    CSS definition group identifier
  1107.      * @param string $property Property defined
  1108.      *
  1109.      * @return     mixed|PEAR_Error
  1110.      * @since      version 0.3.0 (2003-11-03)
  1111.      * @access     public
  1112.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP,
  1113.      *             HTML_CSS_ERROR_NO_ELEMENT
  1114.      * @see        setGroupStyle()
  1115.      */
  1116.     function getGroupStyle($group, $property)
  1117.     {
  1118.         if (!is_int($group) && !is_string($group)) {
  1119.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1120.                 array('var' => '$group',
  1121.                       'was' => gettype($group),
  1122.                       'expected' => 'integer | string',
  1123.                       'paramnum' => 1));
  1124.  
  1125.         } elseif (!is_string($property)) {
  1126.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1127.                 array('var' => '$property',
  1128.                       'was' => gettype($property),
  1129.                       'expected' => 'string',
  1130.                       'paramnum' => 2));
  1131.         }
  1132.         $groupIdent = '@-'.$group;
  1133.         if ($group < 0 || $group > $this->_groupCount ||
  1134.             !isset($this->_groups[$groupIdent])) {
  1135.             return $this->raiseError(HTML_CSS_ERROR_NO_GROUP, 'error',
  1136.                 array('identifier' => $group));
  1137.         }
  1138.  
  1139.         $styles = array();
  1140.  
  1141.         foreach ($this->_css[$groupIdent] as $rank => $prop) {
  1142.             // if the style is not duplicate
  1143.             if (!is_numeric($rank)) {
  1144.                 $prop = array($rank => $prop);
  1145.             }
  1146.             foreach ($prop as $key => $value) {
  1147.                 if ($key == $property) {
  1148.                     $styles[] = $value;
  1149.                 }
  1150.             }
  1151.         }
  1152.  
  1153.         if (count($styles) < 2) {
  1154.             $styles = array_shift($styles);
  1155.         }
  1156.         return $styles;
  1157.     }
  1158.  
  1159.     /**
  1160.      * Add a selector to a CSS definition group.
  1161.      *
  1162.      * Add a selector to a CSS definition group
  1163.      *
  1164.      * @param mixed  $group     CSS definition group identifier
  1165.      * @param string $selectors Selector(s) to be defined, comma delimited.
  1166.      *
  1167.      * @return   void|PEAR_Error
  1168.      * @since    version 0.3.0 (2003-11-03)
  1169.      * @access   public
  1170.      * @throws   HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
  1171.      */
  1172.     function addGroupSelector($group, $selectors)
  1173.     {
  1174.         $groupIdent = '@-'.$group;
  1175.         if ($group < 0 || $group > $this->_groupCount ||
  1176.             !isset($this->_groups[$groupIdent])) {
  1177.             return $this->raiseError(HTML_CSS_ERROR_NO_GROUP, 'error',
  1178.                 array('identifier' => $group));
  1179.  
  1180.         } elseif (!is_string($selectors)) {
  1181.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1182.                 array('var' => '$selectors',
  1183.                       'was' => gettype($selectors),
  1184.                       'expected' => 'string',
  1185.                       'paramnum' => 2));
  1186.         }
  1187.  
  1188.         $newSelectors = $this->parseSelectors($selectors, 1);
  1189.         foreach ($newSelectors as $selector) {
  1190.             $this->_alibis[$selector][] = $groupIdent;
  1191.         }
  1192.         $oldSelectors = $this->_groups[$groupIdent];
  1193.  
  1194.         $this->_groups[$groupIdent] = array_merge($oldSelectors, $newSelectors);
  1195.     }
  1196.  
  1197.     /**
  1198.      * Remove a selector from a group
  1199.      *
  1200.      * Definitively remove a selector from a CSS group
  1201.      *
  1202.      * @param mixed  $group     CSS definition group identifier
  1203.      * @param string $selectors Selector(s) to be removed, comma delimited.
  1204.      *
  1205.      * @return   void|PEAR_Error
  1206.      * @since    version 0.3.0 (2003-11-03)
  1207.      * @access   public
  1208.      * @throws   HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
  1209.      */
  1210.     function removeGroupSelector($group, $selectors)
  1211.     {
  1212.         $groupIdent = '@-'.$group;
  1213.         if ($group < 0 || $group > $this->_groupCount ||
  1214.             !isset($this->_groups[$groupIdent])) {
  1215.             return $this->raiseError(HTML_CSS_ERROR_NO_GROUP, 'error',
  1216.                 array('identifier' => $group));
  1217.  
  1218.         } elseif (!is_string($selectors)) {
  1219.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1220.                 array('var' => '$selectors',
  1221.                       'was' => gettype($selectors),
  1222.                       'expected' => 'string',
  1223.                       'paramnum' => 2));
  1224.         }
  1225.  
  1226.         $oldSelectors = $this->_groups[$groupIdent];
  1227.         $selectors    = $this->parseSelectors($selectors, 1);
  1228.         foreach ($selectors as $selector) {
  1229.             foreach ($oldSelectors as $key => $value) {
  1230.                 if ($value == $selector) {
  1231.                     unset($this->_groups[$groupIdent][$key]);
  1232.                 }
  1233.             }
  1234.             foreach ($this->_alibis[$selector] as $key => $value) {
  1235.                 if ($value == $groupIdent) {
  1236.                     unset($this->_alibis[$selector][$key]);
  1237.                 }
  1238.             }
  1239.         }
  1240.     }
  1241.  
  1242.     /**
  1243.      * Set or add a CSS definition
  1244.      *
  1245.      * Add or change a single value for an element property
  1246.      *
  1247.      * @param string $element    Element (or class) to be defined
  1248.      * @param string $property   Property defined
  1249.      * @param string $value      Value assigned
  1250.      * @param bool   $duplicates (optional) Allow or disallow duplicates.
  1251.      *
  1252.      * @return     void|PEAR_Error
  1253.      * @since      version 0.2.0 (2003-07-31)
  1254.      * @access     public
  1255.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1256.      * @see        getStyle()
  1257.      */
  1258.     function setStyle($element, $property, $value, $duplicates = null)
  1259.     {
  1260.         if (!is_string($element)) {
  1261.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1262.                 array('var' => '$element',
  1263.                       'was' => gettype($element),
  1264.                       'expected' => 'string',
  1265.                       'paramnum' => 1));
  1266.  
  1267.         } elseif (!is_string($property)) {
  1268.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1269.                 array('var' => '$property',
  1270.                       'was' => gettype($property),
  1271.                       'expected' => 'string',
  1272.                       'paramnum' => 2));
  1273.  
  1274.         } elseif (!is_string($value)) {
  1275.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1276.                 array('var' => '$value',
  1277.                       'was' => gettype($value),
  1278.                       'expected' => 'string',
  1279.                       'paramnum' => 3));
  1280.  
  1281.         } elseif (strpos($element, ',')) {
  1282.             // Check if there are any groups.
  1283.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  1284.                 array('var' => '$element',
  1285.                       'was' => $element,
  1286.                       'expected' => 'string without comma',
  1287.                       'paramnum' => 1));
  1288.  
  1289.         } elseif (isset($duplicates) && !is_bool($duplicates)) {
  1290.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1291.                 array('var' => '$duplicates',
  1292.                       'was' => gettype($duplicates),
  1293.                       'expected' => 'bool',
  1294.                       'paramnum' => 4));
  1295.         }
  1296.  
  1297.         if (!isset($duplicates)) {
  1298.              $duplicates = $this->__get('allowduplicates');
  1299.         }
  1300.  
  1301.         $element = $this->parseSelectors($element);
  1302.  
  1303.         if ($duplicates === true) {
  1304.             $this->_duplicateCounter++;
  1305.             $this->_css[$element][$this->_duplicateCounter][$property] = $value;
  1306.             return $this->_duplicateCounter;
  1307.         } else {
  1308.             $this->_css[$element][$property] = $value;
  1309.         }
  1310.     }
  1311.  
  1312.     /**
  1313.      * Return the value of a CSS property
  1314.      *
  1315.      * Get the value of a property to an identifed simple CSS element
  1316.      *
  1317.      * @param string $element  Element (or class) to be defined
  1318.      * @param string $property Property defined
  1319.      *
  1320.      * @return     mixed|PEAR_Error
  1321.      * @since      version 0.3.0 (2003-11-03)
  1322.      * @access     public
  1323.      * @throws     HTML_CSS_ERROR_INVALID_INPUT,
  1324.      *             HTML_CSS_ERROR_NO_ELEMENT, HTML_CSS_ERROR_NO_ELEMENT_PROPERTY
  1325.      * @see        setStyle()
  1326.      */
  1327.     function getStyle($element, $property)
  1328.     {
  1329.         if (!is_string($element)) {
  1330.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1331.                 array('var' => '$element',
  1332.                       'was' => gettype($element),
  1333.                       'expected' => 'string',
  1334.                       'paramnum' => 1));
  1335.  
  1336.         } elseif (!is_string($property)) {
  1337.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1338.                 array('var' => '$property',
  1339.                       'was' => gettype($property),
  1340.                       'expected' => 'string',
  1341.                       'paramnum' => 2));
  1342.         }
  1343.         if (!isset($this->_css[$element]) && !isset($this->_alibis[$element])) {
  1344.             return $this->raiseError(HTML_CSS_ERROR_NO_ELEMENT, 'error',
  1345.                 array('identifier' => $element));
  1346.         }
  1347.  
  1348.         if (isset($this->_alibis[$element])) {
  1349.             $lastImplementation = array_keys($this->_alibis[$element]);
  1350.             $lastImplementation = array_pop($lastImplementation);
  1351.  
  1352.             $group = substr($this->_alibis[$element][$lastImplementation], 2);
  1353.  
  1354.             $property_value = $this->getGroupStyle($group, $property);
  1355.         }
  1356.         if (isset($this->_css[$element]) && !isset($property_value)) {
  1357.             $property_value = array();
  1358.             foreach ($this->_css[$element] as $rank => $prop) {
  1359.                 if (!is_numeric($rank)) {
  1360.                     $prop = array($rank => $prop);
  1361.                 }
  1362.                 foreach ($prop as $key => $value) {
  1363.                     if ($key == $property) {
  1364.                         $property_value[] = $value;
  1365.                     }
  1366.                 }
  1367.             }
  1368.             if (count($property_value) == 1) {
  1369.                 $property_value = $property_value[0];
  1370.             } elseif (count($property_value) == 0) {
  1371.                 unset($property_value);
  1372.             }
  1373.         }
  1374.  
  1375.         if (!isset($property_value)) {
  1376.             return $this->raiseError(HTML_CSS_ERROR_NO_ELEMENT_PROPERTY, 'error',
  1377.                 array('identifier' => $element,
  1378.                       'property'   => $property));
  1379.         }
  1380.         return $property_value;
  1381.     }
  1382.  
  1383.     /**
  1384.      * Retrieve styles corresponding to an element filter
  1385.      *
  1386.      * Return array entries of styles that match patterns (Perl compatible)
  1387.      *
  1388.      * @param string $elmPattern Element or class pattern to retrieve
  1389.      * @param string $proPattern (optional) Property pattern to retrieve
  1390.      *
  1391.      * @return     array|PEAR_Error
  1392.      * @since      version 1.1.0 (2007-01-01)
  1393.      * @access     public
  1394.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1395.      * @link       http://www.php.net/en/ref.pcre.php
  1396.      *             Regular Expression Functions (Perl-Compatible)
  1397.      */
  1398.     function grepStyle($elmPattern, $proPattern = null)
  1399.     {
  1400.         if (!is_string($elmPattern)) {
  1401.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1402.                 array('var' => '$elmPattern',
  1403.                       'was' => gettype($elmPattern),
  1404.                       'expected' => 'string',
  1405.                       'paramnum' => 1));
  1406.  
  1407.         } elseif (isset($proPattern) && !is_string($proPattern)) {
  1408.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1409.                 array('var' => '$proPattern',
  1410.                       'was' => gettype($proPattern),
  1411.                       'expected' => 'string',
  1412.                       'paramnum' => 2));
  1413.         }
  1414.  
  1415.         $styles = array();
  1416.  
  1417.         // first, search inside alibis
  1418.         $alibis = array_keys($this->_alibis);
  1419.         $alibis = preg_grep($elmPattern, $alibis);
  1420.         foreach ($alibis as $a) {
  1421.             foreach ($this->_alibis[$a] as $g) {
  1422.                 if (isset($proPattern)) {
  1423.                     $properties = array_keys($this->_css[$g]);
  1424.                     $properties = preg_grep($proPattern, $properties);
  1425.                     if (count($properties) == 0) {
  1426.                         // this group does not have a such property pattern
  1427.                         continue;
  1428.                     }
  1429.                 }
  1430.                 if (isset($styles[$a])) {
  1431.                     $styles[$a] = array_merge($styles[$a], $this->_css[$g]);
  1432.                 } else {
  1433.                     $styles[$a] = $this->_css[$g];
  1434.                 }
  1435.             }
  1436.         }
  1437.  
  1438.         // second, search inside elements
  1439.         $elements = array_keys($this->_css);
  1440.         $elements = preg_grep($elmPattern, $elements);
  1441.         foreach ($elements as $e) {
  1442.             if (substr($e, 0, 1) == '@' ) {
  1443.                 // excludes groups (already found with alibis)
  1444.                 continue;
  1445.             }
  1446.             if (isset($proPattern)) {
  1447.                 $properties = array_keys($this->_css[$e]);
  1448.                 $properties = preg_grep($proPattern, $properties);
  1449.                 if (count($properties) == 0) {
  1450.                     // this element does not have a such property pattern
  1451.                     continue;
  1452.                 }
  1453.             }
  1454.             if (isset($styles[$e])) {
  1455.                 $styles[$e] = array_merge($styles[$e], $this->_css[$e]);
  1456.             } else {
  1457.                 $styles[$e] = $this->_css[$e];
  1458.             }
  1459.         }
  1460.         return $styles;
  1461.     }
  1462.  
  1463.     /**
  1464.      * Apply same styles on two selectors
  1465.      *
  1466.      * Set or change the properties of new selectors
  1467.      * to the values of an existing selector
  1468.      *
  1469.      * @param string $new New selector(s) that should share the same
  1470.      *                    definitions, separated by commas
  1471.      * @param string $old Selector that is already defined
  1472.      *
  1473.      * @return     void|PEAR_Error
  1474.      * @since      version 0.2.0 (2003-07-31)
  1475.      * @access     public
  1476.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ELEMENT
  1477.      */
  1478.     function setSameStyle($new, $old)
  1479.     {
  1480.         if (!is_string($new)) {
  1481.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1482.                 array('var' => '$new',
  1483.                       'was' => gettype($new),
  1484.                       'expected' => 'string',
  1485.                       'paramnum' => 1));
  1486.  
  1487.         } elseif (!is_string($old)) {
  1488.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1489.                 array('var' => '$old',
  1490.                       'was' => gettype($old),
  1491.                       'expected' => 'string',
  1492.                       'paramnum' => 2));
  1493.         }
  1494.  
  1495.         $old = $this->parseSelectors($old);
  1496.         if (!isset($this->_css[$old])) {
  1497.             return $this->raiseError(HTML_CSS_ERROR_NO_ELEMENT, 'error',
  1498.                 array('identifier' => $old));
  1499.         }
  1500.  
  1501.         $selector = implode(', ', array($old, $new));
  1502.         $grp      = $this->createGroup($selector, 'samestyleas_'.$old);
  1503.  
  1504.         $others = $this->parseSelectors($new, 1);
  1505.         foreach ($others as $other) {
  1506.             $other = trim($other);
  1507.             foreach ($this->_css[$old] as $rank => $property) {
  1508.                 if (!is_numeric($rank)) {
  1509.                     $property = array($rank => $property);
  1510.                 }
  1511.                 foreach ($property as $key => $value) {
  1512.                     $this->setGroupStyle($grp, $key, $value);
  1513.                 }
  1514.             }
  1515.             unset($this->_css[$old]);
  1516.         }
  1517.     }
  1518.  
  1519.     /**
  1520.      * Set cache flag
  1521.      *
  1522.      * Define if the document should be cached by the browser. Default to false.
  1523.      *
  1524.      * @param bool $cache (optional) flag to true to cache result, false otherwise
  1525.      *
  1526.      * @return     void|PEAR_Error
  1527.      * @since      version 0.2.0 (2003-07-31)
  1528.      * @access     public
  1529.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1530.      */
  1531.     function setCache($cache = true)
  1532.     {
  1533.         if (!is_bool($cache)) {
  1534.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1535.                 array('var' => '$cache',
  1536.                       'was' => gettype($cache),
  1537.                       'expected' => 'boolean',
  1538.                       'paramnum' => 1));
  1539.         }
  1540.         $this->options['cache'] = $cache;
  1541.     }
  1542.  
  1543.     /**
  1544.      * Returns the cache option value
  1545.      *
  1546.      * @return     boolean
  1547.      * @since      version 1.4.0 (2007-12-13)
  1548.      * @access     public
  1549.      * @see        setCache()
  1550.      */
  1551.     function getCache()
  1552.     {
  1553.         return $this->__get('cache');
  1554.     }
  1555.  
  1556.     /**
  1557.      * Set Content-Disposition header
  1558.      *
  1559.      * Define the Content-Disposition header to supply a recommended filename
  1560.      * and force the browser to display the save dialog.
  1561.      * Default to basename($_SERVER['PHP_SELF']).'.css'
  1562.      *
  1563.      * @param bool   $enable   (optional)
  1564.      * @param string $filename (optional)
  1565.      *
  1566.      * @return     void|PEAR_Error
  1567.      * @since      version 1.3.0 (2007-10-22)
  1568.      * @access     public
  1569.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1570.      * @see        getContentDisposition()
  1571.      * @link       http://pear.php.net/bugs/bug.php?id=12195
  1572.      *             Patch by Carsten Wiedmann
  1573.      */
  1574.     function setContentDisposition($enable = true, $filename = '')
  1575.     {
  1576.         if (!is_bool($enable)) {
  1577.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1578.                 array('var' => '$enable',
  1579.                       'was' => gettype($enable),
  1580.                       'expected' => 'bool',
  1581.                       'paramnum' => 1));
  1582.         } elseif (!is_string($filename)) {
  1583.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1584.                 array('var' => '$filename',
  1585.                       'was' => gettype($filename),
  1586.                       'expected' => 'string',
  1587.                       'paramnum' => 2));
  1588.         }
  1589.  
  1590.         if ($enable == false) {
  1591.             $filename = false;
  1592.         } elseif ($filename == '') {
  1593.             $filename = basename($_SERVER['PHP_SELF']) . '.css';
  1594.         }
  1595.  
  1596.         $this->options['contentDisposition'] = $filename;
  1597.     }
  1598.  
  1599.     /**
  1600.      * Return the Content-Disposition header
  1601.      *
  1602.      * Get value of Content-Disposition header (inline filename) used
  1603.      * to display results
  1604.      *
  1605.      * @return     mixed     boolean FALSE if no content disposition, otherwise
  1606.      *                       string for inline filename
  1607.      * @since      version 1.3.0 (2007-10-22)
  1608.      * @access     public
  1609.      * @see        setContentDisposition()
  1610.      * @link       http://pear.php.net/bugs/bug.php?id=12195
  1611.      *             Patch by Carsten Wiedmann
  1612.      */
  1613.     function getContentDisposition()
  1614.     {
  1615.         return $this->__get('contentDisposition');
  1616.     }
  1617.  
  1618.     /**
  1619.      * Set charset value
  1620.      *
  1621.      * Define the charset for the file. Default to ISO-8859-1 because of CSS1
  1622.      * compatability issue for older browsers.
  1623.      *
  1624.      * @param string $type (optional) Charset encoding; defaults to ISO-8859-1.
  1625.      *
  1626.      * @return     void|PEAR_Error
  1627.      * @since      version 0.2.0 (2003-07-31)
  1628.      * @access     public
  1629.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1630.      * @see        getCharset()
  1631.      */
  1632.     function setCharset($type = 'iso-8859-1')
  1633.     {
  1634.         if (!is_string($type)) {
  1635.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1636.                 array('var' => '$type',
  1637.                       'was' => gettype($type),
  1638.                       'expected' => 'string',
  1639.                       'paramnum' => 1));
  1640.         }
  1641.         $this->options['charset'] = $type;
  1642.     }
  1643.  
  1644.     /**
  1645.      * Return the charset encoding string
  1646.      *
  1647.      * By default, HTML_CSS uses iso-8859-1 encoding.
  1648.      *
  1649.      * @return     string
  1650.      * @since      version 0.2.0 (2003-07-31)
  1651.      * @access     public
  1652.      * @see        setCharset()
  1653.      */
  1654.     function getCharset()
  1655.     {
  1656.         return $this->__get('charset');
  1657.     }
  1658.  
  1659.     /**
  1660.      * Parse a string
  1661.      *
  1662.      * Parse a string that contains CSS information
  1663.      *
  1664.      * @param string $str        text string to parse
  1665.      * @param bool   $duplicates (optional) Allows or disallows
  1666.      *                           duplicate style definitions
  1667.      *
  1668.      * @return     void|PEAR_Error
  1669.      * @since      version 0.3.0 (2003-11-03)
  1670.      * @access     public
  1671.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1672.      * @see        createGroup(), setGroupStyle(), setStyle()
  1673.      */
  1674.     function parseString($str, $duplicates = null)
  1675.     {
  1676.         if (!is_string($str)) {
  1677.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1678.                 array('var' => '$str',
  1679.                       'was' => gettype($str),
  1680.                       'expected' => 'string',
  1681.                       'paramnum' => 1));
  1682.  
  1683.         } elseif (isset($duplicates) && !is_bool($duplicates)) {
  1684.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1685.                 array('var' => '$duplicates',
  1686.                       'was' => gettype($duplicates),
  1687.                       'expected' => 'bool',
  1688.                       'paramnum' => 2));
  1689.         }
  1690.  
  1691.         if (!isset($duplicates)) {
  1692.             $duplicates = $this->__get('allowduplicates');
  1693.         }
  1694.  
  1695.         // Remove comments
  1696.         $str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
  1697.  
  1698.         // Protect parser vs IE hack
  1699.         $str = str_replace('"\"}\""', '#34#125#34', $str);
  1700.  
  1701.         // Parse simple declarative At-Rules
  1702.         $atRules = array();
  1703.         if (preg_match_all('/^\s*(@[a-z\-]+)\s+(.+);\s*$/m', $str, $atRules,
  1704.             PREG_SET_ORDER)) {
  1705.             foreach ($atRules as $value) {
  1706.                 $this->createAtRule(trim($value[1]), trim($value[2]));
  1707.             }
  1708.             $str = preg_replace('/^\s*@[a-z\-]+\s+.+;\s*$/m', '', $str);
  1709.         }
  1710.  
  1711.         $elements   = array();
  1712.         $properties = array();
  1713.  
  1714.         // Parse each element of csscode
  1715.         $parts = explode("}", $str);
  1716.         foreach ($parts as $part) {
  1717.             $part = trim($part);
  1718.             if (strlen($part) == 0) {
  1719.                 continue;
  1720.             }
  1721.             // prevent invalide css data structure
  1722.             $pos = strpos($part, '{');
  1723.             if (strpos($part, '{', $pos+1) !== false && $part{0} !== '@') {
  1724.  
  1725.                 $context  = debug_backtrace();
  1726.                 $context  = @array_pop($context);
  1727.                 $function = strtolower($context['function']);
  1728.                 if ($function === 'parsestring') {
  1729.                     $var = 'str';
  1730.                 } elseif ($function === 'parsefile') {
  1731.                     $var = 'filename';
  1732.                 } else {
  1733.                     $var = 'styles';
  1734.                 }
  1735.  
  1736.                 return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'error',
  1737.                     array('var' => '$'.$var,
  1738.                           'was' => 'invalid data source',
  1739.                           'expected' => 'valid CSS structure',
  1740.                           'paramnum' => 1));
  1741.             }
  1742.             $parse = preg_split('/\{(.*)\}/', "$part }", -1,
  1743.                          PREG_SPLIT_DELIM_CAPTURE);
  1744.  
  1745.             if (count($parse) == 1) {
  1746.                 list($keystr, $codestr) = explode("{", $part);
  1747.                 $elements[]             = trim($keystr);
  1748.                 $properties[]           = trim($codestr);
  1749.             } else {
  1750.                 for ($i = 0; $i < 2; $i++) {
  1751.                     if ($i == 0) {
  1752.                         $part = ltrim($parse[$i], "\r\n}");
  1753.                         $pos  = strpos($part, '{');
  1754.                         if ($pos === false) {
  1755.                             $elements[] = trim($part);
  1756.                         } else {
  1757.                             // Remove eol
  1758.                             $part = preg_replace("/\r?\n?/", '', $part);
  1759.  
  1760.                             if (strpos($part, '}', $pos+1) === false) {
  1761.                                 // complex declaration block style (nested)
  1762.                                 list($keystr, $codestr) = explode("{", $part);
  1763.                                 $elements[]             = trim($part);
  1764.                             } else {
  1765.                                 // simple declaration block style
  1766.                                 $parse        = preg_split('/\{(.*)\}/', "$part }",
  1767.                                                     -1, PREG_SPLIT_DELIM_CAPTURE);
  1768.                                 $elements[]   = trim($parse[0]);
  1769.                                 $properties[] = trim($parse[1]);
  1770.                             }
  1771.                         }
  1772.                     } else {
  1773.                         $properties[] = trim($parse[$i]);
  1774.                     }
  1775.                 }
  1776.             }
  1777.         }
  1778.  
  1779.         foreach ($elements as $i => $keystr) {
  1780.             if (strpos($keystr, '{') === false) {
  1781.                 $nested_bloc = false;
  1782.             } else {
  1783.                 $nested_bloc = true;
  1784.  
  1785.                 list($keystr, $nestedsel) = explode("{", $keystr);
  1786.             }
  1787.  
  1788.             $key_a   = $this->parseSelectors($keystr, 1);
  1789.             $keystr  = implode(', ', $key_a);
  1790.             $codestr = $properties[$i];
  1791.             // Check if there are any groups; in standard selectors exclude at-rules
  1792.             if (strpos($keystr, ',') && $keystr{0} !== '@') {
  1793.                 $group = $this->createGroup($keystr);
  1794.  
  1795.                 // Parse each property of an element
  1796.                 $codes = explode(";", trim($codestr));
  1797.                 foreach ($codes as $code) {
  1798.                     if (strlen(trim($code)) > 0) {
  1799.                         // find the property and the value
  1800.                         $property
  1801.                             = trim(substr($code, 0, strpos($code, ':', 0)));
  1802.                         $value
  1803.                             = trim(substr($code, strpos($code, ':', 0) + 1));
  1804.                         // IE hack only
  1805.                         if (strcasecmp($property, 'voice-family') == 0) {
  1806.                             $value
  1807.                                 = str_replace('#34#125#34', '"\"}\""', $value);
  1808.                         }
  1809.                         $this->setGroupStyle($group, $property, $value,
  1810.                             $duplicates);
  1811.                     }
  1812.                 }
  1813.             } else {
  1814.                 // let's get on with regular definitions
  1815.                 $key = trim($keystr);
  1816.  
  1817.                 // Parse each property of an element
  1818.                 $codes = explode(";", trim($codestr));
  1819.                 foreach ($codes as $code) {
  1820.                     if (strlen(trim($code)) == 0) {
  1821.                         continue;
  1822.                     }
  1823.                     $code = ltrim($code, "\r\n}");
  1824.  
  1825.                     $p = trim(substr($code, 0, strpos($code, ':')));
  1826.                     $v = trim(substr($code, strpos($code, ':') + 1));
  1827.                     // IE hack only
  1828.                     if (strcasecmp($p, 'voice-family') == 0) {
  1829.                         $v = str_replace('#34#125#34', '"\"}\""',
  1830.                                      $v);
  1831.                     }
  1832.  
  1833.                     if ($key{0} == '@') {
  1834.                         // at-rules
  1835.                         list($atKeyword, $arguments) = explode(' ', "$key ");
  1836.                         if ($nested_bloc) {
  1837.                             $this->setAtRuleStyle($atKeyword, $arguments, $nestedsel,
  1838.                                 $p, $v, $duplicates);
  1839.                         } else {
  1840.                             $this->setAtRuleStyle($atKeyword, $arguments, '',
  1841.                                 $p, $v, $duplicates);
  1842.                         }
  1843.  
  1844.                     } else {
  1845.                         // simple declarative style
  1846.                         $this->setStyle($key, $p, $v, $duplicates);
  1847.                     }
  1848.                 }
  1849.             }
  1850.         }
  1851.     }
  1852.  
  1853.     /**
  1854.      * Parse file content
  1855.      *
  1856.      * Parse a file that contains CSS information
  1857.      *
  1858.      * @param string $filename   file to parse
  1859.      * @param bool   $duplicates (optional) Allow or disallow duplicates.
  1860.      *
  1861.      * @return     void|PEAR_Error
  1862.      * @since      version 0.3.0 (2003-11-03)
  1863.      * @access     public
  1864.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_FILE
  1865.      * @see        parseString()
  1866.      */
  1867.     function parseFile($filename, $duplicates = null)
  1868.     {
  1869.         if (!is_string($filename)) {
  1870.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1871.                 array('var' => '$filename',
  1872.                       'was' => gettype($filename),
  1873.                       'expected' => 'string',
  1874.                       'paramnum' => 1));
  1875.  
  1876.         } elseif (!file_exists($filename)) {
  1877.             return $this->raiseError(HTML_CSS_ERROR_NO_FILE, 'error',
  1878.                     array('identifier' => $filename));
  1879.  
  1880.         } elseif (isset($duplicates) && !is_bool($duplicates)) {
  1881.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1882.                 array('var' => '$duplicates',
  1883.                       'was' => gettype($duplicates),
  1884.                       'expected' => 'bool',
  1885.                       'paramnum' => 2));
  1886.         }
  1887.  
  1888.         if (!isset($duplicates)) {
  1889.             $duplicates = $this->__get('allowduplicates');
  1890.         }
  1891.  
  1892.         $ret = $this->parseString(file_get_contents($filename), $duplicates);
  1893.         return $ret;
  1894.     }
  1895.  
  1896.     /**
  1897.      * Parse multiple data sources
  1898.      *
  1899.      * Parse data sources, file(s) or string(s), that contains CSS information
  1900.      *
  1901.      * @param array $styles     data sources to parse
  1902.      * @param bool  $duplicates (optional) Allow or disallow duplicates.
  1903.      *
  1904.      * @return     void|PEAR_Error
  1905.      * @since      version 1.0.0RC2 (2005-12-15)
  1906.      * @access     public
  1907.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  1908.      * @see        parseString(), parseFile()
  1909.      */
  1910.     function parseData($styles, $duplicates = null)
  1911.     {
  1912.         if (!is_array($styles)) {
  1913.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1914.                 array('var' => '$styles',
  1915.                       'was' => gettype($styles),
  1916.                       'expected' => 'array',
  1917.                       'paramnum' => 1));
  1918.  
  1919.         } elseif (isset($duplicates) && !is_bool($duplicates)) {
  1920.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1921.                 array('var' => '$duplicates',
  1922.                       'was' => gettype($duplicates),
  1923.                       'expected' => 'bool',
  1924.                       'paramnum' => 2));
  1925.         }
  1926.  
  1927.         if (!isset($duplicates)) {
  1928.             $duplicates = $this->__get('allowduplicates');
  1929.         }
  1930.  
  1931.         foreach ($styles as $i => $style) {
  1932.             if (!is_string($style)) {
  1933.                 return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1934.                     array('var' => '$styles[' . $i . ']',
  1935.                           'was' => gettype($styles[$i]),
  1936.                           'expected' => 'string',
  1937.                           'paramnum' => 1));
  1938.             }
  1939.             if (strcasecmp(substr($style, -4, 4), '.css') == 0) {
  1940.                 $res = $this->parseFile($style, $duplicates);
  1941.             } else {
  1942.                 $res = $this->parseString($style, $duplicates);
  1943.             }
  1944.             if (!is_bool($this->_lastError)) {
  1945.                 return $res;
  1946.             }
  1947.         }
  1948.     }
  1949.  
  1950.     /**
  1951.      * Validate a CSS data source
  1952.      *
  1953.      * Execute the W3C CSS validator service on each data source (filename
  1954.      * or string) given by parameter $styles.
  1955.      *
  1956.      * @param array $styles    Data sources to check validity
  1957.      * @param array &$messages Error and Warning messages
  1958.      *                         issue from W3C CSS validator service
  1959.      *
  1960.      * @return     boolean|PEAR_Error
  1961.      * @since      version 1.5.0 (2008-01-15)
  1962.      * @access     public
  1963.      * @throws     HTML_CSS_ERROR_INVALID_INPUT,
  1964.      *             HTML_CSS_ERROR_INVALID_DEPS, HTML_CSS_ERROR_INVALID_SOURCE
  1965.      */
  1966.     function validate($styles, &$messages)
  1967.     {
  1968.         $php = phpversion();
  1969.         if (version_compare($php, '5.0.0', '<')) {
  1970.             return $this->raiseError(HTML_CSS_ERROR_INVALID_DEPS, 'exception',
  1971.                 array('funcname' => __FUNCTION__,
  1972.                       'dependency' => 'PHP 5',
  1973.                       'currentdep' => "PHP $php"));
  1974.         }
  1975.         @include_once 'Services/W3C/CSSValidator.php';
  1976.         if (class_exists('Services_W3C_CSSValidator', false) === false) {
  1977.             return $this->raiseError(HTML_CSS_ERROR_INVALID_DEPS, 'exception',
  1978.                 array('funcname' => __FUNCTION__,
  1979.                       'dependency' => 'PEAR::Services_W3C_CSSValidator',
  1980.                       'currentdep' => 'nothing'));
  1981.         }
  1982.         if (!is_array($styles)) {
  1983.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1984.                 array('var' => '$styles',
  1985.                       'was' => gettype($styles),
  1986.                       'expected' => 'array',
  1987.                       'paramnum' => 1));
  1988.  
  1989.         } elseif (!is_array($messages)) {
  1990.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  1991.                 array('var' => '$messages',
  1992.                       'was' => gettype($messages),
  1993.                       'expected' => 'array',
  1994.                       'paramnum' => 2));
  1995.         }
  1996.  
  1997.         // prepare to call the W3C CSS validator service
  1998.         $v        = new Services_W3C_CSSValidator();
  1999.         $validity = true;
  2000.         $messages = array('errors' => array(), 'warnings' => array());
  2001.  
  2002.         foreach ($styles as $i => $source) {
  2003.             if (!is_string($source)) {
  2004.                 return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  2005.                     array('var' => '$styles[' . $i . ']',
  2006.                           'was' => gettype($styles[$i]),
  2007.                           'expected' => 'string',
  2008.                           'paramnum' => 1));
  2009.             }
  2010.             if (strcasecmp(substr($source, -4, 4), '.css') == 0) {
  2011.                 // validate a file as CSS content
  2012.                 $r = $v->validateFile($source);
  2013.             } else {
  2014.                 // validate a string as CSS content
  2015.                 $r = $v->validateFragment($source);
  2016.             }
  2017.             if ($r === false) {
  2018.                 $validity = false;
  2019.             }
  2020.             if ($r->isValid() === false) {
  2021.                 $validity = false;
  2022.                 foreach ($r->errors as $error) {
  2023.                     $properties           = get_object_vars($error);
  2024.                     $messages['errors'][] = $properties;
  2025.                 }
  2026.                 foreach ($r->warnings as $warning) {
  2027.                     $properties             = get_object_vars($warning);
  2028.                     $messages['warnings'][] = $properties;
  2029.                 }
  2030.                 $this->raiseError(HTML_CSS_ERROR_INVALID_SOURCE,
  2031.                     ((count($r->errors) == 0) ? 'warning' : 'error'),
  2032.                     array('sourcenum' => $i,
  2033.                           'errcount' => count($r->errors),
  2034.                           'warncount' => count($r->warnings)));
  2035.             }
  2036.         }
  2037.         return $validity;
  2038.     }
  2039.  
  2040.     /**
  2041.      * Return the CSS contents in an array
  2042.      *
  2043.      * Return the full contents of CSS data sources (parsed) in an array
  2044.      *
  2045.      * @return     array
  2046.      * @since      version 0.2.0 (2003-07-31)
  2047.      * @access     public
  2048.      */
  2049.     function toArray()
  2050.     {
  2051.         $css = array();
  2052.  
  2053.         // bring AtRules in correct order
  2054.         $this->sortAtRules();
  2055.  
  2056.         foreach ($this->_css as $key => $value) {
  2057.             if (strpos($key, '@-') === 0) {
  2058.                 $key = implode(', ', $this->_groups[$key]);
  2059.             }
  2060.             $css[$key] = $value;
  2061.         }
  2062.         return $css;
  2063.     }
  2064.  
  2065.     /**
  2066.      * Return a string-properties for style attribute of an HTML element
  2067.      *
  2068.      * Generate and return the CSS properties of an element or class
  2069.      * as a string for inline use.
  2070.      *
  2071.      * @param string $element Element or class
  2072.      *                        for which inline CSS should be generated
  2073.      *
  2074.      * @return     string|PEAR_Error
  2075.      * @since      version 0.2.0 (2003-07-31)
  2076.      * @access     public
  2077.      * @throws     HTML_CSS_ERROR_INVALID_INPUT
  2078.      */
  2079.     function toInline($element)
  2080.     {
  2081.         if (!is_string($element)) {
  2082.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  2083.                 array('var' => '$element',
  2084.                       'was' => gettype($element),
  2085.                       'expected' => 'string',
  2086.                       'paramnum' => 1));
  2087.         }
  2088.  
  2089.         $strCss      = '';
  2090.         $newCssArray = array();
  2091.  
  2092.         // This allows for grouped elements definitions to work
  2093.         if (isset($this->_alibis[$element])) {
  2094.             $alibis = $this->_alibis[$element];
  2095.  
  2096.             // All the groups must be run through to be able to
  2097.             // properly assign the value to the inline.
  2098.             foreach ($alibis as $alibi) {
  2099.                 foreach ($this->_css[$alibi] as $key => $value) {
  2100.                     $newCssArray[$key] = $value;
  2101.                 }
  2102.             }
  2103.         }
  2104.  
  2105.         // This allows for single elements definitions to work
  2106.         if (isset($this->_css[$element])) {
  2107.             foreach ($this->_css[$element] as $rank => $property) {
  2108.                 if (!is_numeric($rank)) {
  2109.                     $property = array($rank => $property);
  2110.                 }
  2111.                 foreach ($property as $key => $value) {
  2112.                     if ($key != 'other-elements') {
  2113.                         $newCssArray[$key] = $value;
  2114.                     }
  2115.                 }
  2116.             }
  2117.         }
  2118.  
  2119.         foreach ($newCssArray as $key => $value) {
  2120.             if ((0 === strpos($element, '@')) && ('' == $value)) {
  2121.                 // simple declarative At-Rule definition
  2122.                 $strCss .= $key . ';';
  2123.             } else {
  2124.                 // other CSS definition
  2125.                 $strCss .= $key . ':' . $value . ";";
  2126.             }
  2127.         }
  2128.  
  2129.         return $strCss;
  2130.     }
  2131.  
  2132.     /**
  2133.      * Generate CSS and stores it in a file
  2134.      *
  2135.      * Generate current parsed CSS data sources and write result in a user file
  2136.      *
  2137.      * @param string $filename Name of file that content the stylesheet
  2138.      *
  2139.      * @return     void|PEAR_Error
  2140.      * @since      version 0.3.0 (2003-11-03)
  2141.      * @access     public
  2142.      * @throws     HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_WRITE_FILE
  2143.      * @see        toString()
  2144.      */
  2145.     function toFile($filename)
  2146.     {
  2147.         if (!is_string($filename)) {
  2148.             return $this->raiseError(HTML_CSS_ERROR_INVALID_INPUT, 'exception',
  2149.                 array('var' => '$filename',
  2150.                       'was' => gettype($filename),
  2151.                       'expected' => 'string',
  2152.                       'paramnum' => 1));
  2153.         }
  2154.  
  2155.         if (function_exists('file_put_contents')) {
  2156.             file_put_contents($filename, $this->toString());
  2157.         } else {
  2158.             $file = fopen($filename, 'wb');
  2159.             fwrite($file, $this->toString());
  2160.             fclose($file);
  2161.         }
  2162.         if (!file_exists($filename)) {
  2163.             return $this->raiseError(HTML_CSS_ERROR_WRITE_FILE, 'error',
  2164.                     array('filename' => $filename));
  2165.         }
  2166.     }
  2167.  
  2168.     /**
  2169.      * Return current CSS parsed data as a string
  2170.      *
  2171.      * Generate current parsed CSS data sources and return result as a string
  2172.      *
  2173.      * @return     string
  2174.      * @since      version 0.2.0 (2003-07-31)
  2175.      * @access     public
  2176.      */
  2177.     function toString()
  2178.     {
  2179.         // get line endings
  2180.         $lnEnd = $this->_getLineEnd();
  2181.         $tabs  = $this->_getTabs();
  2182.         $tab   = $this->_getTab();
  2183.  
  2184.         // initialize $alibis
  2185.         $alibis = array();
  2186.  
  2187.         $strCss     = '';
  2188.         $strAtRules = '';
  2189.  
  2190.         // Allow a CSS comment
  2191.         if ($this->_comment) {
  2192.             $strCss = $tabs . '/* ' . $this->getComment() . ' */' . $lnEnd;
  2193.         }
  2194.  
  2195.         // If groups are to be output first, initialize a special variable
  2196.         if ($this->__get('groupsfirst')) {
  2197.             $strCssElements = '';
  2198.         }
  2199.  
  2200.         // bring AtRules in correct order
  2201.         $this->sortAtRules();
  2202.  
  2203.         // Iterate through the array and process each element
  2204.         foreach ($this->_css as $identifier => $rank) {
  2205.  
  2206.             // Groups are handled separately
  2207.             if (strpos($identifier, '@-') !== false) {
  2208.                 // its a group
  2209.                 $element = implode(', ', $this->_groups[$identifier]);
  2210.             } else {
  2211.                 $element = $identifier;
  2212.             }
  2213.  
  2214.             if ((0 === strpos($element, '@')) && (1 !== strpos($element, '-'))) {
  2215.                 // simple declarative At-Rule definition
  2216.                 foreach ($rank as $arg => $decla) {
  2217.                     if (is_array($decla)) {
  2218.                         $strAtRules .= $element . ' ' . $arg;
  2219.                         foreach ($decla as $s => $d) {
  2220.                             $t = $tabs . $tab;
  2221.                             if (empty($s)) {
  2222.                                 $strAtRules .= ' {' . $lnEnd;
  2223.                             } else {
  2224.                                 $t          .= $tab;
  2225.                                 $strAtRules .= ' {' . $lnEnd .
  2226.                                     $tab . $s . ' {' . $lnEnd;
  2227.                             }
  2228.                             foreach ($d as $p => $v) {
  2229.                                 $strAtRules .= $t . $p . ': ' . $v . ';' . $lnEnd;
  2230.                             }
  2231.                             if (empty($s)) {
  2232.                                 $strAtRules .= $tabs . '}';
  2233.                             } else {
  2234.                                 $strAtRules .=  $tabs . $tab . '}' . $lnEnd . '}';
  2235.                             }
  2236.                         }
  2237.                         $strAtRules .=  $lnEnd . $lnEnd;;
  2238.                     } else {
  2239.                         $strAtRules .= $element . ' ' . $arg . ';' . $lnEnd . $lnEnd;
  2240.                     }
  2241.                 }
  2242.             } else {
  2243.                 // Start CSS element definition
  2244.                 $definition = $element . ' {' . $lnEnd;
  2245.  
  2246.                 // Iterate through the array of properties
  2247.                 foreach ($rank as $pos => $property) {
  2248.                     // check to see if it is a duplicate
  2249.                     if (!is_numeric($pos)) {
  2250.                         $property = array($pos => $property);
  2251.                         unset($pos);
  2252.                     }
  2253.                     foreach ($property as $key => $value) {
  2254.                         $definition .= $tabs . $tab
  2255.                                     . $key . ': ' . $value . ';' . $lnEnd;
  2256.                     }
  2257.                 }
  2258.  
  2259.                 // end CSS element definition
  2260.                 $definition .= $tabs . '}';
  2261.             }
  2262.  
  2263.             // if this is to be on a single line, collapse
  2264.             if ($this->options['oneline']) {
  2265.                 $definition = $this->collapseInternalSpaces($definition);
  2266.                 $strAtRules = $this->collapseInternalSpaces($strAtRules);
  2267.             }
  2268.  
  2269.             // if groups are to be output first, elements must be placed in a
  2270.             // different string which will be appended in the end
  2271.             if (isset($definition)) {
  2272.                 if ($this->__get('groupsfirst') === true
  2273.                     && strpos($identifier, '@-') === false) {
  2274.                     // add to elements
  2275.                     $strCssElements .= $lnEnd . $tabs . $definition . $lnEnd;
  2276.                 } else {
  2277.                     // add to strCss
  2278.                     $strCss .= $lnEnd . $tabs . $definition . $lnEnd;
  2279.                 }
  2280.             }
  2281.         }
  2282.  
  2283.         if ($this->__get('groupsfirst')) {
  2284.             $strCss .= $strCssElements;
  2285.         }
  2286.  
  2287.         $strAtRules = rtrim($strAtRules);
  2288.         if (!empty($strAtRules)) {
  2289.             $strAtRules .= $lnEnd;
  2290.         }
  2291.         $strCss = $strAtRules . $strCss;
  2292.  
  2293.         if ($this->options['oneline']) {
  2294.             $strCss = preg_replace('/(\n|\r\n|\r)/', '', $strCss);
  2295.         }
  2296.  
  2297.         return $strCss;
  2298.     }
  2299.  
  2300.     /**
  2301.      * Output CSS Code.
  2302.      *
  2303.      * Send the stylesheet content to standard output, handling cacheControl
  2304.      * and contentDisposition headers
  2305.      *
  2306.      * @return     void
  2307.      * @since      version 0.2.0 (2003-07-31)
  2308.      * @access     public
  2309.      * @see        toString()
  2310.      */
  2311.     function display()
  2312.     {
  2313.         if ($this->__get('cache') !== true) {
  2314.             header("Expires: Tue, 1 Jan 1980 12:00:00 GMT");
  2315.             header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
  2316.             header("Cache-Control: no-cache");
  2317.             header("Pragma: no-cache");
  2318.         }
  2319.  
  2320.         // set character encoding
  2321.         header("Content-Type: text/css; charset=" . $this->__get('charset'));
  2322.  
  2323.         // set Content-Disposition
  2324.         if ($this->__get('contentDisposition') !== false) {
  2325.             header('Content-Disposition: inline; filename="' .
  2326.                 $this->__get('contentDisposition') . '"');
  2327.         }
  2328.  
  2329.         $strCss = $this->toString();
  2330.         print $strCss;
  2331.     }
  2332.  
  2333.     /**
  2334.      * Initialize Error engine preferences
  2335.      *
  2336.      * @param array $prefs (optional) hash of params to customize error generation
  2337.      *
  2338.      * @return     void
  2339.      * @since      version 0.3.3 (2004-05-20)
  2340.      * @access     private
  2341.      */
  2342.     function _initErrorStack($prefs = array())
  2343.     {
  2344.         // error message mapping callback
  2345.         if (isset($prefs['message_callback'])
  2346.             && is_callable($prefs['message_callback'])) {
  2347.             $this->_callback_message = $prefs['message_callback'];
  2348.         } else {
  2349.             $this->_callback_message = array('HTML_CSS_Error', '_msgCallback');
  2350.         }
  2351.  
  2352.         // error context mapping callback
  2353.         if (isset($prefs['context_callback'])
  2354.             && is_callable($prefs['context_callback'])) {
  2355.             $this->_callback_context = $prefs['context_callback'];
  2356.         } else {
  2357.             $this->_callback_context = array('HTML_CSS_Error', 'getBacktrace');
  2358.         }
  2359.  
  2360.         // determine whether to allow an error to be pushed or logged
  2361.         if (isset($prefs['push_callback'])
  2362.             && is_callable($prefs['push_callback'])) {
  2363.             $this->_callback_push = $prefs['push_callback'];
  2364.         } else {
  2365.             $this->_callback_push = array('HTML_CSS_Error', '_handleError');
  2366.         }
  2367.  
  2368.         // determine whether to display or log an error by a free user function
  2369.         if (isset($prefs['error_callback'])
  2370.             && is_callable($prefs['error_callback'])) {
  2371.             $this->_callback_error = $prefs['error_callback'];
  2372.         } else {
  2373.             $this->_callback_error = null;
  2374.         }
  2375.  
  2376.         // default error handler will use PEAR_Error
  2377.         if (isset($prefs['error_handler'])
  2378.             && is_callable($prefs['error_handler'])) {
  2379.             $this->_callback_errorhandler = $prefs['error_handler'];
  2380.         } else {
  2381.             $this->_callback_errorhandler = array(&$this, '_errorHandler');
  2382.         }
  2383.  
  2384.         // any handler-specific settings
  2385.         if (isset($prefs['handler'])) {
  2386.             $this->_errorhandler_options = $prefs['handler'];
  2387.         }
  2388.     }
  2389.  
  2390.     /**
  2391.      * Standard error handler that will use PEAR_Error object
  2392.      *
  2393.      * To improve performances, the PEAR.php file is included dynamically.
  2394.      * The file is so included only when an error is triggered. So, in most
  2395.      * cases, the file isn't included and perfs are much better.
  2396.      *
  2397.      * @param integer $code   Error code.
  2398.      * @param string  $level  The error level of the message.
  2399.      * @param array   $params Associative array of error parameters
  2400.      *
  2401.      * @return     PEAR_Error
  2402.      * @since      version 1.0.0 (2006-06-24)
  2403.      * @access     private
  2404.      */
  2405.     function _errorHandler($code, $level, $params)
  2406.     {
  2407.         include_once 'HTML/CSS/Error.php';
  2408.  
  2409.         $mode    = call_user_func($this->_callback_push, $code, $level);
  2410.         $message = call_user_func($this->_callback_message, $code, $params);
  2411.         $options = $this->_callback_error;
  2412.  
  2413.         $userinfo['level'] = $level;
  2414.  
  2415.         if (isset($this->_errorhandler_options['display'])) {
  2416.             $userinfo['display'] = $this->_errorhandler_options['display'];
  2417.         } else {
  2418.             $userinfo['display'] = array();
  2419.         }
  2420.         if (isset($this->_errorhandler_options['log'])) {
  2421.             $userinfo['log'] = $this->_errorhandler_options['log'];
  2422.         } else {
  2423.             $userinfo['log'] = array();
  2424.         }
  2425.  
  2426.         return PEAR::raiseError($message, $code, $mode, $options, $userinfo,
  2427.                    'HTML_CSS_Error');
  2428.     }
  2429.  
  2430.     /**
  2431.      * A basic wrapper around the default PEAR_Error object
  2432.      *
  2433.      * This method is a wrapper that returns an instance of the configured
  2434.      * error class with this object's default error handling applied.
  2435.      *
  2436.      * @return     object    PEAR_Error when default error handler is used
  2437.      * @since      version 0.3.3 (2004-05-20)
  2438.      * @access     public
  2439.      * @see        _errorHandler()
  2440.      */
  2441.     function raiseError()
  2442.     {
  2443.         $args = func_get_args();
  2444.         $this->_lastError
  2445.             = call_user_func_array($this->_callback_errorhandler, $args);
  2446.         return $this->_lastError;
  2447.     }
  2448.  
  2449.     /**
  2450.      * Determine whether there is an error
  2451.      *
  2452.      * Determine whether last action raised an error or not
  2453.      *
  2454.      * @return     boolean               TRUE if error raised, FALSE otherwise
  2455.      * @since      version 1.0.0RC2 (2005-12-15)
  2456.      * @access     public
  2457.      */
  2458.     function isError()
  2459.     {
  2460.          $res              = (!is_bool($this->_lastError));
  2461.          $this->_lastError = false;
  2462.          return $res;
  2463.     }
  2464. }
  2465. ?>