1 : <?php
2 : /**
3 : * Copyright (c) 2003-2009, 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-2009 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.89 2009/07/03 15:52:22 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-2009 Klaus Guenther, Laurent Laville
78 : * @license http://www.opensource.org/licenses/bsd-license.php BSD
79 : * @version Release: 1.5.4
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 0 : $this->__construct($attributes, $errorPrefs);
280 0 : }
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 56 : $this->_initErrorStack($errorPrefs);
309 :
310 56 : if (!is_array($attributes)) {
311 0 : $attributes = array($attributes);
312 0 : }
313 56 : if ($attributes) {
314 15 : $attributes = $this->_parseAttributes($attributes);
315 15 : }
316 :
317 56 : $tab = ' ';
318 56 : $eol = strtolower(substr(PHP_OS, 0, 3)) == 'win' ? "\r\n" : "\n";
319 :
320 : // default options
321 56 : $this->options = array('xhtml' => true, 'tab' => $tab, 'cache' => true,
322 56 : 'oneline' => false, 'charset' => 'iso-8859-1',
323 56 : 'contentDisposition' => false, 'lineEnd' => $eol,
324 56 : 'groupsfirst' => true, 'allowduplicates' => false);
325 : // and options that come directly from HTML_Common
326 56 : $this->setTab($tab);
327 56 : $this->setLineEnd($eol);
328 :
329 : // apply user options
330 56 : foreach ($attributes as $opt => $val) {
331 15 : $this->__set($opt, $val);
332 56 : }
333 56 : }
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 1 : 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 56 : if (isset($this->options[$option])) {
364 56 : $this->options[$option] = $val;
365 56 : }
366 56 : }
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 41 : if (isset($this->options[$option])) {
383 40 : $r = $this->options[$option];
384 40 : } else {
385 1 : $r = null;
386 : }
387 41 : 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 4 : 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 56 : $this->__set('tab', $string);
418 56 : parent::setTab($string);
419 56 : }
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 56 : $this->__set('lineEnd', $style);
435 56 : parent::setLineEnd($style);
436 56 : }
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 2 : if (!is_bool($value)) {
453 1 : return $this->raiseError(
454 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
455 1 : array('var' => '$value',
456 1 : 'was' => gettype($value),
457 1 : 'expected' => 'boolean',
458 1 : 'paramnum' => 1)
459 1 : );
460 : }
461 1 : $this->options['oneline'] = $value;
462 1 : }
463 :
464 : /**
465 : * Set groupsfirst flag
466 : *
467 : * Determine whether groups are output before elements or not
468 : *
469 : * @param bool $value flag to true if groups are output before elements,
470 : * false otherwise
471 : *
472 : * @return void|PEAR_Error
473 : * @since version 0.3.3 (2004-05-20)
474 : * @access public
475 : * @throws HTML_CSS_ERROR_INVALID_INPUT
476 : */
477 : function setOutputGroupsFirst($value)
478 : {
479 2 : if (!is_bool($value)) {
480 1 : return $this->raiseError(
481 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
482 1 : array('var' => '$value',
483 1 : 'was' => gettype($value),
484 1 : 'expected' => 'boolean',
485 1 : 'paramnum' => 1)
486 1 : );
487 : }
488 1 : $this->options['groupsfirst'] = $value;
489 1 : }
490 :
491 : /**
492 : * Parse a string containing selector(s)
493 : *
494 : * It processes it and returns an array or string containing
495 : * modified selectors (depends on XHTML compliance setting;
496 : * defaults to ensure lowercase element names)
497 : *
498 : * @param string $selectors Selector string
499 : * @param int $outputMode (optional) 0 = string; 1 = array; 2 = deep array
500 : *
501 : * @return mixed|PEAR_Error
502 : * @since version 0.3.2 (2004-03-24)
503 : * @access protected
504 : * @throws HTML_CSS_ERROR_INVALID_INPUT
505 : */
506 : function parseSelectors($selectors, $outputMode = 0)
507 : {
508 31 : if (!is_string($selectors)) {
509 1 : return $this->raiseError(
510 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
511 1 : array('var' => '$selectors',
512 1 : 'was' => gettype($selectors),
513 1 : 'expected' => 'string',
514 1 : 'paramnum' => 1)
515 1 : );
516 :
517 31 : } elseif (!is_int($outputMode)) {
518 1 : return $this->raiseError(
519 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
520 1 : array('var' => '$outputMode',
521 1 : 'was' => gettype($outputMode),
522 1 : 'expected' => 'integer',
523 1 : 'paramnum' => 2)
524 1 : );
525 :
526 31 : } elseif ($outputMode < 0 || $outputMode > 3) {
527 1 : return $this->raiseError(
528 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
529 1 : array('var' => '$outputMode',
530 1 : 'was' => $outputMode,
531 1 : 'expected' => '0 | 1 | 2 | 3',
532 1 : 'paramnum' => 2)
533 1 : );
534 : }
535 :
536 31 : $selectors_array = explode(',', $selectors);
537 31 : $i = 0;
538 31 : foreach ($selectors_array as $selector) {
539 : // trim to remove possible whitespace
540 31 : $selector = trim($this->collapseInternalSpaces($selector));
541 31 : if (strpos($selector, ' ')) {
542 10 : $sel_a = array();
543 10 : foreach (explode(' ', $selector) as $sub_selector) {
544 10 : $sel_a[] = $this->parseSelectors($sub_selector, $outputMode);
545 10 : }
546 10 : if ($outputMode === 0) {
547 6 : $array[$i] = implode(' ', $sel_a);
548 6 : } else {
549 9 : $sel_a2 = array();
550 9 : foreach ($sel_a as $sel_a_temp) {
551 9 : $sel_a2 = array_merge($sel_a2, $sel_a_temp);
552 9 : }
553 9 : if ($outputMode == 2) {
554 1 : $array[$i]['inheritance'] = $sel_a2;
555 1 : } else {
556 8 : $array[$i] = implode(' ', $sel_a2);
557 : }
558 : }
559 10 : $i++;
560 10 : } else {
561 : // initialize variables
562 31 : $element = '';
563 31 : $id = '';
564 31 : $class = '';
565 31 : $pseudo = '';
566 :
567 31 : if (strpos($selector, ':') !== false) {
568 3 : $pseudo = strstr($selector, ':');
569 3 : $selector = substr($selector, 0, strpos($selector, ':'));
570 3 : }
571 31 : if (strpos($selector, '.') !== false) {
572 9 : $class = strstr($selector, '.');
573 9 : $selector = substr($selector, 0, strpos($selector, '.'));
574 9 : }
575 31 : if (strpos($selector, '#') !== false) {
576 8 : $id = strstr($selector, '#');
577 8 : $selector = substr($selector, 0, strpos($selector, '#'));
578 8 : }
579 31 : if ($selector != '') {
580 29 : $element = $selector;
581 29 : }
582 31 : if ($this->options['xhtml']) {
583 27 : $element = strtolower($element);
584 27 : $pseudo = strtolower($pseudo);
585 27 : }
586 31 : if ($outputMode == 2) {
587 1 : $array[$i]['element'] = $element;
588 1 : $array[$i]['id'] = $id;
589 1 : $array[$i]['class'] = $class;
590 1 : $array[$i]['pseudo'] = $pseudo;
591 1 : } else {
592 30 : $array[$i] = $element.$id.$class.$pseudo;
593 : }
594 31 : $i++;
595 : }
596 31 : }
597 31 : if ($outputMode == 0) {
598 23 : $output = implode(', ', $array);
599 23 : return $output;
600 : } else {
601 27 : return $array;
602 : }
603 : }
604 :
605 : /**
606 : * Strips excess spaces in string.
607 : *
608 : * @param string $subject string to format
609 : *
610 : * @return string
611 : * @since version 0.3.2 (2004-03-24)
612 : * @access protected
613 : */
614 : function collapseInternalSpaces($subject)
615 : {
616 34 : $string = preg_replace('/\s+/', ' ', $subject);
617 34 : return $string;
618 : }
619 :
620 : /**
621 : * sort and move simple declarative At-Rules to the top
622 : *
623 : * @return void
624 : * @access protected
625 : * @since version 1.5.0 (2008-01-15)
626 : */
627 : function sortAtRules()
628 : {
629 : // split simple declarative At-Rules from the other
630 28 : $return = array('atrules' => array(), 'newcss' => array());
631 :
632 28 : foreach ($this->_css as $key => $value) {
633 27 : if ((0 === strpos($key, "@")) && (1 !== strpos($key, "-"))) {
634 12 : $return["atrules"][$key] = $value;
635 12 : } else {
636 20 : $return["newcss"][$key] = $value;
637 : }
638 28 : }
639 :
640 : // bring sprecial rules to the top
641 28 : foreach (array('@namespace', '@import', '@charset') as $name) {
642 28 : if (isset($return['atrules'][$name])) {
643 4 : $rule = array($name => $return['atrules'][$name]);
644 4 : unset($return['atrules'][$name]);
645 4 : $return['atrules'] = $rule + $return['atrules'];
646 4 : }
647 28 : }
648 :
649 28 : $this->_css = $return['atrules'] + $return['newcss'];
650 28 : }
651 :
652 : /**
653 : * Set xhtml flag
654 : *
655 : * Active or not the XHTML mode compliant
656 : *
657 : * @param bool $value flag to true if XHTML compliance needed,
658 : * false otherwise
659 : *
660 : * @return void|PEAR_Error
661 : * @since version 0.3.2 (2004-03-24)
662 : * @access public
663 : * @throws HTML_CSS_ERROR_INVALID_INPUT
664 : */
665 : function setXhtmlCompliance($value)
666 : {
667 6 : if (!is_bool($value)) {
668 1 : return $this->raiseError(
669 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
670 1 : array('var' => '$value',
671 1 : 'was' => gettype($value),
672 1 : 'expected' => 'boolean',
673 1 : 'paramnum' => 1)
674 1 : );
675 : }
676 5 : $this->options['xhtml'] = $value;
677 5 : }
678 :
679 : /**
680 : * Return list of supported At-Rules
681 : *
682 : * Return the list of At-Rules supported by API 1.5.0 of HTML_CSS
683 : *
684 : * @return void
685 : * @since version 1.5.0 (2008-01-15)
686 : * @access public
687 : */
688 : function getAtRulesList()
689 : {
690 5 : $atRules = array('@charset', '@font-face',
691 5 : '@import', '@media', '@page', '@namespace');
692 5 : return $atRules;
693 : }
694 :
695 : /**
696 : * Create a new simple declarative At-Rule
697 : *
698 : * Create a simple at-rule without declaration style blocks.
699 : * That include @charset, @import and @namespace
700 : *
701 : * @param string $atKeyword at-rule keyword
702 : * @param string $arguments argument list for @charset, @import or @namespace
703 : * @param bool $duplicates (optional) Allow or disallow duplicates
704 : *
705 : * @return void|PEAR_Error
706 : * @since version 1.5.0 (2008-01-15)
707 : * @access public
708 : * @throws HTML_CSS_ERROR_INVALID_INPUT
709 : * @see unsetAtRule()
710 : */
711 : function createAtRule($atKeyword, $arguments = '', $duplicates = null)
712 : {
713 5 : $allowed_atrules = array('@charset', '@import', '@namespace');
714 :
715 5 : if (!is_string($atKeyword)) {
716 1 : return $this->raiseError(
717 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
718 1 : array('var' => '$atKeyword',
719 1 : 'was' => gettype($atKeyword),
720 1 : 'expected' => 'string',
721 1 : 'paramnum' => 1)
722 1 : );
723 :
724 5 : } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
725 1 : return $this->raiseError(
726 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
727 1 : array('var' => '$atKeyword',
728 1 : 'was' => $atKeyword,
729 1 : 'expected' => implode('|', $allowed_atrules),
730 1 : 'paramnum' => 1)
731 1 : );
732 :
733 5 : } elseif (!is_string($arguments)) {
734 1 : return $this->raiseError(
735 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
736 1 : array('var' => '$arguments',
737 1 : 'was' => gettype($arguments),
738 1 : 'expected' => 'string',
739 1 : 'paramnum' => 2)
740 1 : );
741 : }
742 :
743 5 : if (empty($arguments)) {
744 1 : return $this->raiseError(
745 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
746 1 : array('var' => '$arguments',
747 1 : 'was' => $arguments,
748 1 : 'expected' => 'not empty value',
749 1 : 'paramnum' => 2)
750 1 : );
751 : }
752 :
753 4 : if (!isset($duplicates)) {
754 1 : $duplicates = $this->__get('allowduplicates');
755 1 : }
756 :
757 4 : if ($duplicates) {
758 2 : $this->_duplicateCounter++;
759 2 : $this->_css[strtolower($atKeyword)][$this->_duplicateCounter]
760 2 : = array($arguments => '');
761 2 : } else {
762 2 : $this->_css[strtolower($atKeyword)] = array($arguments => '');
763 : }
764 4 : }
765 :
766 : /**
767 : * Remove an existing At-Rule
768 : *
769 : * Remove an existing and supported at-rule. See HTML_CSS::getAtRulesList()
770 : * for a full list of supported At-Rules.
771 : *
772 : * @param string $atKeyword at-rule keyword
773 : *
774 : * @return void|PEAR_Error
775 : * @since version 1.5.0 (2008-01-15)
776 : * @access public
777 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ATRULE
778 : */
779 : function unsetAtRule($atKeyword)
780 : {
781 2 : $allowed_atrules = $this->getAtRulesList();
782 :
783 2 : if (!is_string($atKeyword)) {
784 1 : return $this->raiseError(
785 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
786 1 : array('var' => '$atKeyword',
787 1 : 'was' => gettype($atKeyword),
788 1 : 'expected' => 'string',
789 1 : 'paramnum' => 1)
790 1 : );
791 :
792 2 : } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
793 1 : return $this->raiseError(
794 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
795 1 : array('var' => '$atKeyword',
796 1 : 'was' => $atKeyword,
797 1 : 'expected' => implode('|', $allowed_atrules),
798 1 : 'paramnum' => 1)
799 1 : );
800 :
801 2 : } elseif (!isset($this->_css[strtolower($atKeyword)])) {
802 1 : return $this->raiseError(
803 1 : HTML_CSS_ERROR_NO_ATRULE, 'error',
804 1 : array('identifier' => $atKeyword)
805 1 : );
806 : }
807 :
808 1 : unset($this->_css[strtolower($atKeyword)]);
809 1 : }
810 :
811 : /**
812 : * Define a conditional/informative At-Rule
813 : *
814 : * Set arguments and declaration style block for at-rules that follow :
815 : * "@media, @page, @font-face"
816 : *
817 : * @param string $atKeyword at-rule keyword
818 : * @param string $arguments argument list
819 : * (optional for @font-face)
820 : * @param string $selectors selectors of declaration style block
821 : * (optional for @media, @page, @font-face)
822 : * @param string $property property of a single declaration style block
823 : * @param string $value value of a single declaration style block
824 : * @param bool $duplicates (optional) Allow or disallow duplicates
825 : *
826 : * @return void|PEAR_Error
827 : * @since version 1.5.0 (2008-01-15)
828 : * @access public
829 : * @throws HTML_CSS_ERROR_INVALID_INPUT
830 : * @see getAtRuleStyle()
831 : */
832 : function setAtRuleStyle($atKeyword, $arguments, $selectors, $property, $value,
833 : $duplicates = null
834 : ) {
835 10 : $allowed_atrules = array('@media', '@page', '@font-face');
836 :
837 10 : if (!is_string($atKeyword)) {
838 1 : return $this->raiseError(
839 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
840 1 : array('var' => '$atKeyword',
841 1 : 'was' => gettype($atKeyword),
842 1 : 'expected' => 'string',
843 1 : 'paramnum' => 1)
844 1 : );
845 :
846 10 : } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
847 1 : return $this->raiseError(
848 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
849 1 : array('var' => '$atKeyword',
850 1 : 'was' => $atKeyword,
851 1 : 'expected' => implode('|', $allowed_atrules),
852 1 : 'paramnum' => 1)
853 1 : );
854 :
855 10 : } elseif (empty($arguments) && strtolower($atKeyword) != '@font-face') {
856 1 : return $this->raiseError(
857 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
858 1 : array('var' => '$arguments',
859 1 : 'was' => $arguments,
860 1 : 'expected' => 'not empty value for '. $atKeyword,
861 1 : 'paramnum' => 2)
862 1 : );
863 :
864 10 : } elseif (!is_string($selectors)) {
865 1 : return $this->raiseError(
866 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
867 1 : array('var' => '$selectors',
868 1 : 'was' => gettype($selectors),
869 1 : 'expected' => 'string',
870 1 : 'paramnum' => 3)
871 1 : );
872 :
873 10 : } elseif (!is_string($property)) {
874 1 : return $this->raiseError(
875 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
876 1 : array('var' => '$property',
877 1 : 'was' => gettype($property),
878 1 : 'expected' => 'string',
879 1 : 'paramnum' => 4)
880 1 : );
881 :
882 10 : } elseif (!is_string($value)) {
883 1 : return $this->raiseError(
884 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
885 1 : array('var' => '$value',
886 1 : 'was' => gettype($value),
887 1 : 'expected' => 'string',
888 1 : 'paramnum' => 5)
889 1 : );
890 :
891 10 : } elseif (empty($property)) {
892 1 : return $this->raiseError(
893 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
894 1 : array('var' => '$property',
895 1 : 'was' => $property,
896 1 : 'expected' => 'no empty string',
897 1 : 'paramnum' => 4)
898 1 : );
899 :
900 10 : } elseif (empty($value)) {
901 1 : return $this->raiseError(
902 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
903 1 : array('var' => '$value',
904 1 : 'was' => gettype($value),
905 1 : 'expected' => 'no empty string',
906 1 : 'paramnum' => 5)
907 1 : );
908 : }
909 :
910 9 : if (!isset($duplicates)) {
911 2 : $duplicates = $this->__get('allowduplicates');
912 2 : }
913 :
914 9 : $atKeyword = strtolower($atKeyword);
915 :
916 9 : if (!empty($selectors)) {
917 7 : $selectors = $this->parseSelectors($selectors);
918 7 : }
919 9 : $this->_css[$atKeyword][$arguments][$selectors][$property] = $value;
920 9 : }
921 :
922 : /**
923 : * Get style value of an existing At-Rule
924 : *
925 : * Retrieve arguments or style value of an existing At-Rule.
926 : * See HTML_CSS::getAtRulesList() for a full list of supported At-Rules.
927 : *
928 : * @param string $atKeyword at-rule keyword
929 : * @param string $arguments argument list
930 : * (optional for @font-face)
931 : * @param string $selectors selectors of declaration style block
932 : * (optional for @media, @page, @font-face)
933 : * @param string $property property of a single declaration style block
934 : *
935 : * @return void|PEAR_Error
936 : * @since version 1.5.0 (2008-01-15)
937 : * @access public
938 : * @throws HTML_CSS_ERROR_INVALID_INPUT
939 : * @see setAtRuleStyle()
940 : */
941 : function getAtRuleStyle($atKeyword, $arguments, $selectors, $property)
942 : {
943 3 : $allowed_atrules = $this->getAtRulesList();
944 :
945 3 : if (!is_string($atKeyword)) {
946 1 : return $this->raiseError(
947 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
948 1 : array('var' => '$atKeyword',
949 1 : 'was' => gettype($atKeyword),
950 1 : 'expected' => 'string',
951 1 : 'paramnum' => 1)
952 1 : );
953 :
954 3 : } elseif (!in_array(strtolower($atKeyword), $allowed_atrules)) {
955 1 : return $this->raiseError(
956 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
957 1 : array('var' => '$atKeyword',
958 1 : 'was' => $atKeyword,
959 1 : 'expected' => implode('|', $allowed_atrules),
960 1 : 'paramnum' => 1)
961 1 : );
962 :
963 3 : } elseif (!is_string($arguments)) {
964 1 : return $this->raiseError(
965 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
966 1 : array('var' => '$arguments',
967 1 : 'was' => gettype($arguments),
968 1 : 'expected' => 'string',
969 1 : 'paramnum' => 2)
970 1 : );
971 :
972 3 : } elseif (!is_string($selectors)) {
973 1 : return $this->raiseError(
974 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
975 1 : array('var' => '$selectors',
976 1 : 'was' => gettype($selectors),
977 1 : 'expected' => 'string',
978 1 : 'paramnum' => 3)
979 1 : );
980 :
981 3 : } elseif (!is_string($property)) {
982 1 : return $this->raiseError(
983 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
984 1 : array('var' => '$property',
985 1 : 'was' => gettype($property),
986 1 : 'expected' => 'string',
987 1 : 'paramnum' => 4)
988 1 : );
989 : }
990 :
991 2 : if (isset($this->_css[$atKeyword][$arguments][$selectors][$property])) {
992 1 : $val = $this->_css[$atKeyword][$arguments][$selectors][$property];
993 1 : } else {
994 1 : $val = null;
995 : }
996 2 : return $val;
997 : }
998 :
999 : /**
1000 : * Create a new CSS definition group
1001 : *
1002 : * Create a new CSS definition group. Return an integer identifying the group.
1003 : *
1004 : * @param string $selectors Selector(s) to be defined, comma delimited.
1005 : * @param mixed $group (optional) Group identifier. If not passed,
1006 : * will return an automatically assigned integer.
1007 : *
1008 : * @return mixed|PEAR_Error
1009 : * @since version 0.3.0 (2003-11-03)
1010 : * @access public
1011 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_INVALID_GROUP
1012 : * @see unsetGroup()
1013 : */
1014 : function createGroup($selectors, $group = null)
1015 : {
1016 14 : if (!is_string($selectors)) {
1017 1 : return $this->raiseError(
1018 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1019 1 : array('var' => '$selectors',
1020 1 : 'was' => gettype($selectors),
1021 1 : 'expected' => 'string',
1022 1 : 'paramnum' => 1)
1023 1 : );
1024 : }
1025 :
1026 14 : if (!isset($group)) {
1027 12 : $this->_groupCount++;
1028 12 : $group = $this->_groupCount;
1029 12 : } else {
1030 3 : if (isset($this->_groups['@-'.$group])) {
1031 1 : return $this->raiseError(
1032 1 : HTML_CSS_ERROR_INVALID_GROUP, 'error',
1033 1 : array('identifier' => $group)
1034 1 : );
1035 : }
1036 : }
1037 :
1038 14 : $groupIdent = '@-'.$group;
1039 :
1040 14 : $selectors = $this->parseSelectors($selectors, 1);
1041 14 : foreach ($selectors as $selector) {
1042 14 : $this->_alibis[$selector][] = $groupIdent;
1043 14 : }
1044 :
1045 14 : $this->_groups[$groupIdent] = $selectors;
1046 :
1047 14 : return $group;
1048 : }
1049 :
1050 : /**
1051 : * Remove a CSS definition group
1052 : *
1053 : * Remove a CSS definition group. Use the same identifier as for group creation.
1054 : *
1055 : * @param mixed $group CSS definition group identifier
1056 : *
1057 : * @return void|PEAR_Error
1058 : * @since version 0.3.0 (2003-11-03)
1059 : * @access public
1060 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
1061 : * @see createGroup()
1062 : */
1063 : function unsetGroup($group)
1064 : {
1065 2 : if (!is_int($group) && !is_string($group)) {
1066 1 : return $this->raiseError(
1067 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1068 1 : array('var' => '$group',
1069 1 : 'was' => gettype($group),
1070 1 : 'expected' => 'integer | string',
1071 1 : 'paramnum' => 1)
1072 1 : );
1073 : }
1074 2 : $groupIdent = '@-'.$group;
1075 2 : if ($group < 0 || $group > $this->_groupCount
1076 2 : || !isset($this->_groups[$groupIdent])
1077 2 : ) {
1078 1 : return $this->raiseError(
1079 1 : HTML_CSS_ERROR_NO_GROUP, 'error',
1080 1 : array('identifier' => $group)
1081 1 : );
1082 : }
1083 :
1084 1 : $alibis = $this->_alibis;
1085 1 : foreach ($alibis as $selector => $data) {
1086 1 : foreach ($data as $key => $value) {
1087 1 : if ($value == $groupIdent) {
1088 1 : unset($this->_alibis[$selector][$key]);
1089 1 : break;
1090 : }
1091 1 : }
1092 1 : if (count($this->_alibis[$selector]) == 0) {
1093 1 : unset($this->_alibis[$selector]);
1094 1 : }
1095 1 : }
1096 1 : unset($this->_groups[$groupIdent]);
1097 1 : unset($this->_css[$groupIdent]);
1098 1 : }
1099 :
1100 : /**
1101 : * Set or add a CSS definition for a CSS group
1102 : *
1103 : * Define the new value of a property for a CSS group. The group should exist.
1104 : * If not, use HTML_CSS::createGroup first
1105 : *
1106 : * @param mixed $group CSS definition group identifier
1107 : * @param string $property Property defined
1108 : * @param string $value Value assigned
1109 : * @param bool $duplicates (optional) Allow or disallow duplicates.
1110 : *
1111 : * @return void|int|PEAR_Error Returns an integer if duplicates
1112 : * are allowed.
1113 : * @since version 0.3.0 (2003-11-03)
1114 : * @access public
1115 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP
1116 : * @see getGroupStyle()
1117 : */
1118 : function setGroupStyle($group, $property, $value, $duplicates = null)
1119 : {
1120 13 : if (!is_int($group) && !is_string($group)) {
1121 1 : return $this->raiseError(
1122 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1123 1 : array('var' => '$group',
1124 1 : 'was' => gettype($group),
1125 1 : 'expected' => 'integer | string',
1126 1 : 'paramnum' => 1)
1127 1 : );
1128 :
1129 13 : } elseif (!is_string($property)) {
1130 1 : return $this->raiseError(
1131 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1132 1 : array('var' => '$property',
1133 1 : 'was' => gettype($property),
1134 1 : 'expected' => 'string',
1135 1 : 'paramnum' => 2)
1136 1 : );
1137 :
1138 13 : } elseif (empty($property)) {
1139 1 : return $this->raiseError(
1140 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
1141 1 : array('var' => '$property',
1142 1 : 'was' => gettype($property),
1143 1 : 'expected' => 'no empty string',
1144 1 : 'paramnum' => 2)
1145 1 : );
1146 :
1147 13 : } elseif (!is_string($value)) {
1148 1 : return $this->raiseError(
1149 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1150 1 : array('var' => '$value',
1151 1 : 'was' => gettype($value),
1152 1 : 'expected' => 'string',
1153 1 : 'paramnum' => 3)
1154 1 : );
1155 :
1156 13 : } elseif (isset($duplicates) && !is_bool($duplicates)) {
1157 1 : return $this->raiseError(
1158 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1159 1 : array('var' => '$duplicates',
1160 1 : 'was' => gettype($duplicates),
1161 1 : 'expected' => 'bool',
1162 1 : 'paramnum' => 4)
1163 1 : );
1164 : }
1165 :
1166 13 : if (!isset($duplicates)) {
1167 6 : $duplicates = $this->__get('allowduplicates');
1168 6 : }
1169 :
1170 13 : $groupIdent = '@-'.$group;
1171 13 : if ($group < 0 || $group > $this->_groupCount
1172 13 : || !isset($this->_groups[$groupIdent])
1173 13 : ) {
1174 1 : return $this->raiseError(
1175 1 : HTML_CSS_ERROR_NO_GROUP, 'error',
1176 1 : array('identifier' => $group)
1177 1 : );
1178 : }
1179 :
1180 12 : if ($duplicates === true) {
1181 1 : $this->_duplicateCounter++;
1182 1 : $this->_css[$groupIdent][$this->_duplicateCounter][$property] = $value;
1183 1 : return $this->_duplicateCounter;
1184 : } else {
1185 11 : $this->_css[$groupIdent][$property] = $value;
1186 : }
1187 11 : }
1188 :
1189 : /**
1190 : * Return CSS definition for a CSS group
1191 : *
1192 : * Get the CSS definition for group created by setGroupStyle()
1193 : *
1194 : * @param mixed $group CSS definition group identifier
1195 : * @param string $property Property defined
1196 : *
1197 : * @return mixed|PEAR_Error
1198 : * @since version 0.3.0 (2003-11-03)
1199 : * @access public
1200 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_GROUP,
1201 : * HTML_CSS_ERROR_NO_ELEMENT
1202 : * @see setGroupStyle()
1203 : */
1204 : function getGroupStyle($group, $property)
1205 : {
1206 3 : if (!is_int($group) && !is_string($group)) {
1207 1 : return $this->raiseError(
1208 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1209 1 : array('var' => '$group',
1210 1 : 'was' => gettype($group),
1211 1 : 'expected' => 'integer | string',
1212 1 : 'paramnum' => 1)
1213 1 : );
1214 :
1215 3 : } elseif (!is_string($property)) {
1216 1 : return $this->raiseError(
1217 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1218 1 : array('var' => '$property',
1219 1 : 'was' => gettype($property),
1220 1 : 'expected' => 'string',
1221 1 : 'paramnum' => 2)
1222 1 : );
1223 : }
1224 3 : $groupIdent = '@-'.$group;
1225 3 : if ($group < 0 || $group > $this->_groupCount
1226 3 : || !isset($this->_groups[$groupIdent])
1227 3 : ) {
1228 1 : return $this->raiseError(
1229 1 : HTML_CSS_ERROR_NO_GROUP, 'error',
1230 1 : array('identifier' => $group)
1231 1 : );
1232 : }
1233 :
1234 3 : $styles = array();
1235 :
1236 3 : if (!isset($this->_css[$groupIdent])) {
1237 1 : return $styles;
1238 : }
1239 :
1240 2 : foreach ($this->_css[$groupIdent] as $rank => $prop) {
1241 : // if the style is not duplicate
1242 2 : if (!is_numeric($rank)) {
1243 2 : $prop = array($rank => $prop);
1244 2 : }
1245 2 : foreach ($prop as $key => $value) {
1246 2 : if ($key == $property) {
1247 2 : $styles[] = $value;
1248 2 : }
1249 2 : }
1250 2 : }
1251 :
1252 2 : if (count($styles) < 2) {
1253 2 : $styles = array_shift($styles);
1254 2 : }
1255 2 : return $styles;
1256 : }
1257 :
1258 : /**
1259 : * Add a selector to a CSS definition group.
1260 : *
1261 : * Add a selector to a CSS definition group
1262 : *
1263 : * @param mixed $group CSS definition group identifier
1264 : * @param string $selectors Selector(s) to be defined, comma delimited.
1265 : *
1266 : * @return void|PEAR_Error
1267 : * @since version 0.3.0 (2003-11-03)
1268 : * @access public
1269 : * @throws HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
1270 : */
1271 : function addGroupSelector($group, $selectors)
1272 : {
1273 2 : if (!is_int($group) && !is_string($group)) {
1274 1 : return $this->raiseError(
1275 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1276 1 : array('var' => '$group',
1277 1 : 'was' => gettype($group),
1278 1 : 'expected' => 'integer | string',
1279 1 : 'paramnum' => 1)
1280 1 : );
1281 : }
1282 2 : $groupIdent = '@-'.$group;
1283 2 : if ($group < 0 || $group > $this->_groupCount
1284 2 : || !isset($this->_groups[$groupIdent])
1285 2 : ) {
1286 1 : return $this->raiseError(
1287 1 : HTML_CSS_ERROR_NO_GROUP, 'error',
1288 1 : array('identifier' => $group)
1289 1 : );
1290 :
1291 2 : } elseif (!is_string($selectors)) {
1292 1 : return $this->raiseError(
1293 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1294 1 : array('var' => '$selectors',
1295 1 : 'was' => gettype($selectors),
1296 1 : 'expected' => 'string',
1297 1 : 'paramnum' => 2)
1298 1 : );
1299 : }
1300 :
1301 1 : $newSelectors = $this->parseSelectors($selectors, 1);
1302 1 : foreach ($newSelectors as $selector) {
1303 1 : $this->_alibis[$selector][] = $groupIdent;
1304 1 : }
1305 1 : $oldSelectors = $this->_groups[$groupIdent];
1306 :
1307 1 : $this->_groups[$groupIdent] = array_merge($oldSelectors, $newSelectors);
1308 1 : }
1309 :
1310 : /**
1311 : * Remove a selector from a group
1312 : *
1313 : * Definitively remove a selector from a CSS group
1314 : *
1315 : * @param mixed $group CSS definition group identifier
1316 : * @param string $selectors Selector(s) to be removed, comma delimited.
1317 : *
1318 : * @return void|PEAR_Error
1319 : * @since version 0.3.0 (2003-11-03)
1320 : * @access public
1321 : * @throws HTML_CSS_ERROR_NO_GROUP, HTML_CSS_ERROR_INVALID_INPUT
1322 : */
1323 : function removeGroupSelector($group, $selectors)
1324 : {
1325 2 : if (!is_int($group) && !is_string($group)) {
1326 1 : return $this->raiseError(
1327 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1328 1 : array('var' => '$group',
1329 1 : 'was' => gettype($group),
1330 1 : 'expected' => 'integer | string',
1331 1 : 'paramnum' => 1)
1332 1 : );
1333 : }
1334 2 : $groupIdent = '@-'.$group;
1335 2 : if ($group < 0 || $group > $this->_groupCount
1336 2 : || !isset($this->_groups[$groupIdent])
1337 2 : ) {
1338 1 : return $this->raiseError(
1339 1 : HTML_CSS_ERROR_NO_GROUP, 'error',
1340 1 : array('identifier' => $group)
1341 1 : );
1342 :
1343 2 : } elseif (!is_string($selectors)) {
1344 1 : return $this->raiseError(
1345 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1346 1 : array('var' => '$selectors',
1347 1 : 'was' => gettype($selectors),
1348 1 : 'expected' => 'string',
1349 1 : 'paramnum' => 2)
1350 1 : );
1351 : }
1352 :
1353 1 : $oldSelectors = $this->_groups[$groupIdent];
1354 1 : $selectors = $this->parseSelectors($selectors, 1);
1355 1 : foreach ($selectors as $selector) {
1356 1 : foreach ($oldSelectors as $key => $value) {
1357 1 : if ($value == $selector) {
1358 1 : unset($this->_groups[$groupIdent][$key]);
1359 1 : }
1360 1 : }
1361 1 : foreach ($this->_alibis[$selector] as $key => $value) {
1362 1 : if ($value == $groupIdent) {
1363 1 : unset($this->_alibis[$selector][$key]);
1364 1 : }
1365 1 : }
1366 1 : }
1367 1 : }
1368 :
1369 : /**
1370 : * Set or add a CSS definition
1371 : *
1372 : * Add or change a single value for an element property
1373 : *
1374 : * @param string $element Element (or class) to be defined
1375 : * @param string $property Property defined
1376 : * @param string $value Value assigned
1377 : * @param bool $duplicates (optional) Allow or disallow duplicates.
1378 : *
1379 : * @return void|PEAR_Error
1380 : * @since version 0.2.0 (2003-07-31)
1381 : * @access public
1382 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1383 : * @see getStyle()
1384 : */
1385 : function setStyle($element, $property, $value, $duplicates = null)
1386 : {
1387 18 : if (!is_string($element)) {
1388 1 : return $this->raiseError(
1389 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1390 1 : array('var' => '$element',
1391 1 : 'was' => gettype($element),
1392 1 : 'expected' => 'string',
1393 1 : 'paramnum' => 1)
1394 1 : );
1395 :
1396 18 : } elseif (!is_string($property)) {
1397 1 : return $this->raiseError(
1398 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1399 1 : array('var' => '$property',
1400 1 : 'was' => gettype($property),
1401 1 : 'expected' => 'string',
1402 1 : 'paramnum' => 2)
1403 1 : );
1404 :
1405 18 : } elseif (!is_string($value)) {
1406 1 : return $this->raiseError(
1407 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1408 1 : array('var' => '$value',
1409 1 : 'was' => gettype($value),
1410 1 : 'expected' => 'string',
1411 1 : 'paramnum' => 3)
1412 1 : );
1413 :
1414 18 : } elseif (strpos($element, ',')) {
1415 : // Check if there are any groups.
1416 1 : return $this->raiseError(
1417 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
1418 1 : array('var' => '$element',
1419 1 : 'was' => $element,
1420 1 : 'expected' => 'string without comma',
1421 1 : 'paramnum' => 1)
1422 1 : );
1423 :
1424 18 : } elseif (isset($duplicates) && !is_bool($duplicates)) {
1425 1 : return $this->raiseError(
1426 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1427 1 : array('var' => '$duplicates',
1428 1 : 'was' => gettype($duplicates),
1429 1 : 'expected' => 'bool',
1430 1 : 'paramnum' => 4)
1431 1 : );
1432 : }
1433 :
1434 18 : if (!isset($duplicates)) {
1435 8 : $duplicates = $this->__get('allowduplicates');
1436 8 : }
1437 :
1438 18 : $element = $this->parseSelectors($element);
1439 :
1440 18 : if ($duplicates === true) {
1441 2 : $this->_duplicateCounter++;
1442 2 : $this->_css[$element][$this->_duplicateCounter][$property] = $value;
1443 2 : return $this->_duplicateCounter;
1444 : } else {
1445 17 : $this->_css[$element][$property] = $value;
1446 : }
1447 17 : }
1448 :
1449 : /**
1450 : * Return the value of a CSS property
1451 : *
1452 : * Get the value of a property to an identifed simple CSS element
1453 : *
1454 : * @param string $element Element (or class) to be defined
1455 : * @param string $property Property defined
1456 : *
1457 : * @return mixed|PEAR_Error
1458 : * @since version 0.3.0 (2003-11-03)
1459 : * @access public
1460 : * @throws HTML_CSS_ERROR_INVALID_INPUT,
1461 : * HTML_CSS_ERROR_NO_ELEMENT, HTML_CSS_ERROR_NO_ELEMENT_PROPERTY
1462 : * @see setStyle()
1463 : */
1464 : function getStyle($element, $property)
1465 : {
1466 3 : if (!is_string($element)) {
1467 1 : return $this->raiseError(
1468 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1469 1 : array('var' => '$element',
1470 1 : 'was' => gettype($element),
1471 1 : 'expected' => 'string',
1472 1 : 'paramnum' => 1)
1473 1 : );
1474 :
1475 3 : } elseif (!is_string($property)) {
1476 1 : return $this->raiseError(
1477 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1478 1 : array('var' => '$property',
1479 1 : 'was' => gettype($property),
1480 1 : 'expected' => 'string',
1481 1 : 'paramnum' => 2)
1482 1 : );
1483 : }
1484 3 : if (!isset($this->_css[$element]) && !isset($this->_alibis[$element])) {
1485 1 : return $this->raiseError(
1486 1 : HTML_CSS_ERROR_NO_ELEMENT, 'error',
1487 1 : array('identifier' => $element)
1488 1 : );
1489 : }
1490 :
1491 3 : if (isset($this->_css[$element]) && isset($this->_alibis[$element])) {
1492 2 : $lastImplementation = array_keys($this->_alibis[$element]);
1493 2 : $lastImplementation = array_pop($lastImplementation);
1494 :
1495 2 : $group = substr($this->_alibis[$element][$lastImplementation], 2);
1496 :
1497 2 : $property_value = $this->getGroupStyle($group, $property);
1498 2 : if (count($property_value) == 0) {
1499 1 : unset($property_value);
1500 1 : }
1501 2 : }
1502 3 : if (isset($this->_css[$element]) && !isset($property_value)) {
1503 2 : $property_value = array();
1504 2 : foreach ($this->_css[$element] as $rank => $prop) {
1505 2 : if (!is_numeric($rank)) {
1506 2 : $prop = array($rank => $prop);
1507 2 : }
1508 2 : foreach ($prop as $key => $value) {
1509 2 : if ($key == $property) {
1510 1 : $property_value[] = $value;
1511 1 : }
1512 2 : }
1513 2 : }
1514 2 : if (count($property_value) == 1) {
1515 1 : $property_value = $property_value[0];
1516 2 : } elseif (count($property_value) == 0) {
1517 1 : unset($property_value);
1518 1 : }
1519 2 : }
1520 :
1521 3 : if (!isset($property_value)) {
1522 1 : return $this->raiseError(
1523 1 : HTML_CSS_ERROR_NO_ELEMENT_PROPERTY, 'error',
1524 1 : array('identifier' => $element,
1525 1 : 'property' => $property)
1526 1 : );
1527 : }
1528 2 : return $property_value;
1529 : }
1530 :
1531 : /**
1532 : * Retrieve styles corresponding to an element filter
1533 : *
1534 : * Return array entries of styles that match patterns (Perl compatible)
1535 : *
1536 : * @param string $elmPattern Element or class pattern to retrieve
1537 : * @param string $proPattern (optional) Property pattern to retrieve
1538 : *
1539 : * @return array|PEAR_Error
1540 : * @since version 1.1.0 (2007-01-01)
1541 : * @access public
1542 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1543 : * @link http://www.php.net/en/ref.pcre.php
1544 : * Regular Expression Functions (Perl-Compatible)
1545 : */
1546 : function grepStyle($elmPattern, $proPattern = null)
1547 : {
1548 2 : if (!is_string($elmPattern)) {
1549 1 : return $this->raiseError(
1550 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1551 1 : array('var' => '$elmPattern',
1552 1 : 'was' => gettype($elmPattern),
1553 1 : 'expected' => 'string',
1554 1 : 'paramnum' => 1)
1555 1 : );
1556 :
1557 2 : } elseif (isset($proPattern) && !is_string($proPattern)) {
1558 1 : return $this->raiseError(
1559 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1560 1 : array('var' => '$proPattern',
1561 1 : 'was' => gettype($proPattern),
1562 1 : 'expected' => 'string',
1563 1 : 'paramnum' => 2)
1564 1 : );
1565 : }
1566 :
1567 1 : $styles = array();
1568 :
1569 : // first, search inside alibis
1570 1 : $alibis = array_keys($this->_alibis);
1571 1 : $alibis = preg_grep($elmPattern, $alibis);
1572 1 : foreach ($alibis as $a) {
1573 1 : foreach ($this->_alibis[$a] as $g) {
1574 1 : if (isset($proPattern)) {
1575 1 : $properties = array_keys($this->_css[$g]);
1576 1 : $properties = preg_grep($proPattern, $properties);
1577 1 : if (count($properties) == 0) {
1578 : // this group does not have a such property pattern
1579 1 : continue;
1580 : }
1581 0 : }
1582 1 : if (isset($styles[$a])) {
1583 0 : $styles[$a] = array_merge($styles[$a], $this->_css[$g]);
1584 0 : } else {
1585 1 : $styles[$a] = $this->_css[$g];
1586 : }
1587 1 : }
1588 1 : }
1589 :
1590 : // second, search inside elements
1591 1 : $elements = array_keys($this->_css);
1592 1 : $elements = preg_grep($elmPattern, $elements);
1593 1 : foreach ($elements as $e) {
1594 1 : if (substr($e, 0, 1) == '@' ) {
1595 : // excludes groups (already found with alibis)
1596 1 : continue;
1597 : }
1598 1 : if (isset($proPattern)) {
1599 1 : $properties = array_keys($this->_css[$e]);
1600 1 : $properties = preg_grep($proPattern, $properties);
1601 1 : if (count($properties) == 0) {
1602 : // this element does not have a such property pattern
1603 1 : continue;
1604 : }
1605 1 : }
1606 1 : if (isset($styles[$e])) {
1607 1 : $styles[$e] = array_merge($styles[$e], $this->_css[$e]);
1608 1 : } else {
1609 1 : $styles[$e] = $this->_css[$e];
1610 : }
1611 1 : }
1612 1 : return $styles;
1613 : }
1614 :
1615 : /**
1616 : * Apply same styles on two selectors
1617 : *
1618 : * Set or change the properties of new selectors
1619 : * to the values of an existing selector
1620 : *
1621 : * @param string $new New selector(s) that should share the same
1622 : * definitions, separated by commas
1623 : * @param string $old Selector that is already defined
1624 : *
1625 : * @return void|PEAR_Error
1626 : * @since version 0.2.0 (2003-07-31)
1627 : * @access public
1628 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_ELEMENT
1629 : */
1630 : function setSameStyle($new, $old)
1631 : {
1632 4 : if (!is_string($new)) {
1633 1 : return $this->raiseError(
1634 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1635 1 : array('var' => '$new',
1636 1 : 'was' => gettype($new),
1637 1 : 'expected' => 'string',
1638 1 : 'paramnum' => 1)
1639 1 : );
1640 :
1641 4 : } elseif (!is_string($old)) {
1642 1 : return $this->raiseError(
1643 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1644 1 : array('var' => '$old',
1645 1 : 'was' => gettype($old),
1646 1 : 'expected' => 'string',
1647 1 : 'paramnum' => 2)
1648 1 : );
1649 :
1650 4 : } elseif (strpos($new, ',')) {
1651 : // Check if there are any groups.
1652 1 : return $this->raiseError(
1653 1 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
1654 1 : array('var' => '$new',
1655 1 : 'was' => $new,
1656 1 : 'expected' => 'string without comma',
1657 1 : 'paramnum' => 1)
1658 1 : );
1659 :
1660 3 : } elseif (strpos($old, ',')) {
1661 : // Check if there are any groups.
1662 0 : return $this->raiseError(
1663 0 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
1664 0 : array('var' => '$old',
1665 0 : 'was' => $old,
1666 0 : 'expected' => 'string without comma',
1667 0 : 'paramnum' => 2)
1668 0 : );
1669 : }
1670 :
1671 3 : $old = $this->parseSelectors($old);
1672 3 : if (!isset($this->_css[$old])) {
1673 1 : return $this->raiseError(
1674 1 : HTML_CSS_ERROR_NO_ELEMENT, 'error',
1675 1 : array('identifier' => $old)
1676 1 : );
1677 : }
1678 :
1679 2 : $selector = implode(', ', array($old, $new));
1680 2 : $grp = $this->createGroup($selector, 'samestyleas_'.$old);
1681 :
1682 2 : $others = $this->parseSelectors($new, 1);
1683 2 : foreach ($others as $other) {
1684 2 : $other = trim($other);
1685 2 : foreach ($this->_css[$old] as $rank => $property) {
1686 2 : if (!is_numeric($rank)) {
1687 2 : $property = array($rank => $property);
1688 2 : }
1689 2 : foreach ($property as $key => $value) {
1690 2 : $this->setGroupStyle($grp, $key, $value);
1691 2 : }
1692 2 : }
1693 2 : unset($this->_css[$old]);
1694 2 : }
1695 2 : }
1696 :
1697 : /**
1698 : * Set cache flag
1699 : *
1700 : * Define if the document should be cached by the browser. Default to false.
1701 : *
1702 : * @param bool $cache (optional) flag to true to cache result, false otherwise
1703 : *
1704 : * @return void|PEAR_Error
1705 : * @since version 0.2.0 (2003-07-31)
1706 : * @access public
1707 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1708 : */
1709 : function setCache($cache = true)
1710 : {
1711 2 : if (!is_bool($cache)) {
1712 1 : return $this->raiseError(
1713 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1714 1 : array('var' => '$cache',
1715 1 : 'was' => gettype($cache),
1716 1 : 'expected' => 'boolean',
1717 1 : 'paramnum' => 1)
1718 1 : );
1719 : }
1720 1 : $this->options['cache'] = $cache;
1721 1 : }
1722 :
1723 : /**
1724 : * Returns the cache option value
1725 : *
1726 : * @return boolean
1727 : * @since version 1.4.0 (2007-12-13)
1728 : * @access public
1729 : * @see setCache()
1730 : */
1731 : function getCache()
1732 : {
1733 1 : return $this->__get('cache');
1734 : }
1735 :
1736 : /**
1737 : * Set Content-Disposition header
1738 : *
1739 : * Define the Content-Disposition header to supply a recommended filename
1740 : * and force the browser to display the save dialog.
1741 : * Default to basename($_SERVER['PHP_SELF']).'.css'
1742 : *
1743 : * @param bool $enable (optional)
1744 : * @param string $filename (optional)
1745 : *
1746 : * @return void|PEAR_Error
1747 : * @since version 1.3.0 (2007-10-22)
1748 : * @access public
1749 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1750 : * @see getContentDisposition()
1751 : * @link http://pear.php.net/bugs/bug.php?id=12195
1752 : * Patch by Carsten Wiedmann
1753 : */
1754 : function setContentDisposition($enable = true, $filename = '')
1755 : {
1756 2 : if (!is_bool($enable)) {
1757 1 : return $this->raiseError(
1758 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1759 1 : array('var' => '$enable',
1760 1 : 'was' => gettype($enable),
1761 1 : 'expected' => 'bool',
1762 1 : 'paramnum' => 1)
1763 1 : );
1764 2 : } elseif (!is_string($filename)) {
1765 1 : return $this->raiseError(
1766 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1767 1 : array('var' => '$filename',
1768 1 : 'was' => gettype($filename),
1769 1 : 'expected' => 'string',
1770 1 : 'paramnum' => 2)
1771 1 : );
1772 : }
1773 :
1774 1 : if ($enable == false) {
1775 0 : $filename = false;
1776 1 : } elseif ($filename == '') {
1777 0 : $filename = basename($_SERVER['PHP_SELF']) . '.css';
1778 0 : }
1779 :
1780 1 : $this->options['contentDisposition'] = $filename;
1781 1 : }
1782 :
1783 : /**
1784 : * Return the Content-Disposition header
1785 : *
1786 : * Get value of Content-Disposition header (inline filename) used
1787 : * to display results
1788 : *
1789 : * @return mixed boolean FALSE if no content disposition, otherwise
1790 : * string for inline filename
1791 : * @since version 1.3.0 (2007-10-22)
1792 : * @access public
1793 : * @see setContentDisposition()
1794 : * @link http://pear.php.net/bugs/bug.php?id=12195
1795 : * Patch by Carsten Wiedmann
1796 : */
1797 : function getContentDisposition()
1798 : {
1799 1 : return $this->__get('contentDisposition');
1800 : }
1801 :
1802 : /**
1803 : * Set charset value
1804 : *
1805 : * Define the charset for the file. Default to ISO-8859-1 because of CSS1
1806 : * compatability issue for older browsers.
1807 : *
1808 : * @param string $type (optional) Charset encoding; defaults to ISO-8859-1.
1809 : *
1810 : * @return void|PEAR_Error
1811 : * @since version 0.2.0 (2003-07-31)
1812 : * @access public
1813 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1814 : * @see getCharset()
1815 : */
1816 : function setCharset($type = 'iso-8859-1')
1817 : {
1818 2 : if (!is_string($type)) {
1819 1 : return $this->raiseError(
1820 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1821 1 : array('var' => '$type',
1822 1 : 'was' => gettype($type),
1823 1 : 'expected' => 'string',
1824 1 : 'paramnum' => 1)
1825 1 : );
1826 : }
1827 1 : $this->options['charset'] = $type;
1828 1 : }
1829 :
1830 : /**
1831 : * Return the charset encoding string
1832 : *
1833 : * By default, HTML_CSS uses iso-8859-1 encoding.
1834 : *
1835 : * @return string
1836 : * @since version 0.2.0 (2003-07-31)
1837 : * @access public
1838 : * @see setCharset()
1839 : */
1840 : function getCharset()
1841 : {
1842 1 : return $this->__get('charset');
1843 : }
1844 :
1845 : /**
1846 : * Parse a string
1847 : *
1848 : * Parse a string that contains CSS information
1849 : *
1850 : * @param string $str text string to parse
1851 : * @param bool $duplicates (optional) Allows or disallows
1852 : * duplicate style definitions
1853 : *
1854 : * @return void|PEAR_Error
1855 : * @since version 0.3.0 (2003-11-03)
1856 : * @access public
1857 : * @throws HTML_CSS_ERROR_INVALID_INPUT
1858 : * @see createGroup(), setGroupStyle(), setStyle()
1859 : */
1860 : function parseString($str, $duplicates = null)
1861 : {
1862 23 : if (!is_string($str)) {
1863 1 : return $this->raiseError(
1864 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1865 1 : array('var' => '$str',
1866 1 : 'was' => gettype($str),
1867 1 : 'expected' => 'string',
1868 1 : 'paramnum' => 1)
1869 1 : );
1870 :
1871 23 : } elseif (isset($duplicates) && !is_bool($duplicates)) {
1872 1 : return $this->raiseError(
1873 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
1874 1 : array('var' => '$duplicates',
1875 1 : 'was' => gettype($duplicates),
1876 1 : 'expected' => 'bool',
1877 1 : 'paramnum' => 2)
1878 1 : );
1879 : }
1880 :
1881 23 : if (!isset($duplicates)) {
1882 14 : $duplicates = $this->__get('allowduplicates');
1883 14 : }
1884 :
1885 : // Remove comments
1886 23 : $str = preg_replace("/\/\*(.*)?\*\//Usi", '', $str);
1887 :
1888 : // Protect parser vs IE hack
1889 23 : $str = str_replace('"\"}\""', '#34#125#34', $str);
1890 :
1891 : // Parse simple declarative At-Rules
1892 23 : $atRules = array();
1893 23 : $elements = array();
1894 23 : $properties = array();
1895 :
1896 : // core of major 1.5.4 parser
1897 23 : preg_match_all(
1898 23 : '/(?ims)([a-z0-9\s\.\:#_\-@,]+)\{([^\{|^\}]*)\}/',
1899 23 : $str, $rules, PREG_SET_ORDER
1900 23 : );
1901 :
1902 : // structure simplified
1903 23 : $structure = preg_replace(
1904 23 : '/(?ims)([a-zA-Z0-9\s\.\:#_\-@,]+)\{([^\{|^\}]*)\}/',
1905 23 : '\1{}', $str
1906 23 : );
1907 : // structure map
1908 23 : $structure = preg_split(
1909 23 : '/([a-zA-Z0-9\s\.\:#_\-@,]+)\{(.*)\}/', $structure, -1,
1910 23 : PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
1911 23 : );
1912 :
1913 23 : $atRulesMap = array();
1914 23 : $atRule = '';
1915 23 : foreach ($structure as $struct) {
1916 23 : $struct = trim($struct);
1917 23 : if (empty($struct)) {
1918 9 : continue;
1919 : }
1920 23 : if ($struct{0} == '}') {
1921 6 : $atRule = '';
1922 6 : $struct = substr($struct, 1);
1923 6 : $struct = ltrim($struct);
1924 6 : }
1925 :
1926 23 : if ($this->options['xhtml']) {
1927 19 : $struct = strtolower($struct);
1928 19 : }
1929 :
1930 :
1931 23 : $has_AtRules = preg_match_all(
1932 23 : '/^(@[a-zA-Z\-]+)\s+(.+);\s*$/m', $struct, $atRules,
1933 : PREG_SET_ORDER
1934 23 : );
1935 23 : if ($has_AtRules) {
1936 2 : foreach ($atRules as $value) {
1937 2 : $this->createAtRule(
1938 2 : trim($value[1]), trim($value[2]), $duplicates
1939 2 : );
1940 2 : }
1941 2 : continue;
1942 : }
1943 22 : $struct = $this->collapseInternalSpaces($struct);
1944 :
1945 22 : $pos = strpos($struct, '{');
1946 : if ($pos
1947 22 : || (strpos($struct, ':') && !empty($atRule))
1948 22 : ) {
1949 9 : $cc = count_chars($struct, 1);
1950 9 : if ((isset($cc[64]) && $cc[64] > 1)
1951 8 : || (isset($cc[58]) && $cc[58] == 1)
1952 9 : ) {
1953 3 : $context = debug_backtrace();
1954 3 : $context = @array_pop($context);
1955 3 : $function = strtolower($context['function']);
1956 3 : if ($function === 'parsestring') {
1957 0 : $var = 'str';
1958 3 : } elseif ($function === 'parsefile') {
1959 0 : $var = 'filename';
1960 0 : } else {
1961 3 : $var = 'styles';
1962 : }
1963 :
1964 3 : return $this->raiseError(
1965 3 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
1966 3 : array('var' => '$'.$var,
1967 3 : 'was' => 'invalid data source',
1968 3 : 'expected' => 'valid CSS structure',
1969 3 : 'paramnum' => 1)
1970 3 : );
1971 : }
1972 8 : $atRule = rtrim(substr($struct, 0, $pos));
1973 :
1974 8 : } else {
1975 20 : $atRulesMap[$struct][] = $atRule;
1976 : }
1977 22 : }
1978 :
1979 20 : foreach ($rules as $rule) {
1980 :
1981 : // prevent invalid css data structure
1982 19 : $pos = strpos($rule[0], '{');
1983 19 : $sel = trim($rule[1]);
1984 19 : if ((strpos($rule[0], '{', $pos+1) !== false)
1985 19 : || (substr($sel, -1, 1) == ':')
1986 19 : ) {
1987 0 : $context = debug_backtrace();
1988 0 : $context = @array_pop($context);
1989 0 : $function = strtolower($context['function']);
1990 0 : if ($function === 'parsestring') {
1991 0 : $var = 'str';
1992 0 : } elseif ($function === 'parsefile') {
1993 0 : $var = 'filename';
1994 0 : } else {
1995 0 : $var = 'styles';
1996 : }
1997 :
1998 0 : return $this->raiseError(
1999 0 : HTML_CSS_ERROR_INVALID_INPUT, 'error',
2000 0 : array('var' => '$'.$var,
2001 0 : 'was' => 'invalid data source',
2002 0 : 'expected' => 'valid CSS structure',
2003 0 : 'paramnum' => 1)
2004 0 : );
2005 : }
2006 :
2007 19 : if ($this->options['xhtml']) {
2008 15 : $rule[1] = strtolower($rule[1]);
2009 15 : }
2010 :
2011 19 : $elements[] = trim($rule[1]);
2012 19 : $properties[] = trim($rule[2]);
2013 20 : }
2014 :
2015 20 : foreach ($elements as $i => $keystr) {
2016 :
2017 19 : $key_a = $this->parseSelectors($keystr, 1);
2018 19 : $keystr = implode(', ', $key_a);
2019 19 : $codestr = $properties[$i];
2020 :
2021 19 : $key = trim($keystr);
2022 19 : $parentAtRule = isset($atRulesMap[$key][$i])
2023 19 : ? $atRulesMap[$key][$i] : $atRulesMap[$key][0];
2024 :
2025 : // Check if there are any groups; in standard selectors exclude at-rules
2026 19 : if (strpos($keystr, ',') && (empty($parentAtRule))) {
2027 6 : $group = $this->createGroup($keystr);
2028 :
2029 : // Parse each property of an element
2030 6 : $codes = explode(";", trim($codestr));
2031 6 : foreach ($codes as $code) {
2032 6 : if (strlen(trim($code)) > 0) {
2033 : // find the property and the value
2034 : $property
2035 6 : = trim(substr($code, 0, strpos($code, ':', 0)));
2036 : $value
2037 6 : = trim(substr($code, strpos($code, ':', 0) + 1));
2038 : // IE hack only
2039 6 : if (strcasecmp($property, 'voice-family') == 0) {
2040 : $value
2041 0 : = str_replace('#34#125#34', '"\"}\""', $value);
2042 0 : }
2043 6 : $this->setGroupStyle(
2044 6 : $group, $property, $value, $duplicates
2045 6 : );
2046 6 : }
2047 6 : }
2048 6 : } else {
2049 : // Parse each property of an element
2050 17 : $codes = explode(";", trim($codestr));
2051 17 : foreach ($codes as $code) {
2052 17 : if (strlen(trim($code)) == 0) {
2053 13 : continue;
2054 : }
2055 17 : $code = ltrim($code, "\r\n}");
2056 :
2057 17 : $p = trim(substr($code, 0, strpos($code, ':')));
2058 17 : $v = trim(substr($code, strpos($code, ':') + 1));
2059 : // IE hack only
2060 17 : if (strcasecmp($p, 'voice-family') == 0) {
2061 1 : $v = str_replace('#34#125#34', '"\"}\""', $v);
2062 1 : }
2063 :
2064 17 : if (!empty($parentAtRule)) {
2065 : // at-rules
2066 6 : $atkw_args = preg_split(
2067 6 : '/(@[a-zA-Z\-]+)\s+(.+)/', $parentAtRule, -1,
2068 6 : PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
2069 6 : );
2070 6 : if (count($atkw_args) == 1) {
2071 : // special case of @font-face (without argument)
2072 0 : $atkw_args[] = '';
2073 0 : }
2074 6 : list($atKeyword, $arguments) = $atkw_args;
2075 6 : $this->setAtRuleStyle(
2076 6 : $atKeyword, $arguments, $keystr, $p, $v, $duplicates
2077 6 : );
2078 :
2079 17 : } elseif ($key{0} == '@') {
2080 2 : $atkw_args = preg_split(
2081 2 : '/(@[a-zA-Z\-]+)\s+(.+)/', $key, -1,
2082 2 : PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY
2083 2 : );
2084 2 : if (count($atkw_args) == 1) {
2085 1 : $atkw_args[] = '';
2086 1 : }
2087 2 : list($atKeyword, $arguments) = $atkw_args;
2088 2 : $this->setAtRuleStyle(
2089 2 : $atKeyword, $arguments, '', $p, $v, $duplicates
2090 2 : );
2091 2 : } else {
2092 : // simple declarative style
2093 12 : $this->setStyle($key, $p, $v, $duplicates);
2094 : }
2095 17 : }
2096 : }
2097 20 : }
2098 20 : }
2099 :
2100 : /**
2101 : * Parse file content
2102 : *
2103 : * Parse a file that contains CSS information
2104 : *
2105 : * @param string $filename file to parse
2106 : * @param bool $duplicates (optional) Allow or disallow duplicates.
2107 : *
2108 : * @return void|PEAR_Error
2109 : * @since version 0.3.0 (2003-11-03)
2110 : * @access public
2111 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_NO_FILE
2112 : * @see parseString()
2113 : */
2114 : function parseFile($filename, $duplicates = null)
2115 : {
2116 3 : if (!is_string($filename)) {
2117 1 : return $this->raiseError(
2118 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2119 1 : array('var' => '$filename',
2120 1 : 'was' => gettype($filename),
2121 1 : 'expected' => 'string',
2122 1 : 'paramnum' => 1)
2123 1 : );
2124 :
2125 3 : } elseif (!file_exists($filename)) {
2126 1 : return $this->raiseError(
2127 1 : HTML_CSS_ERROR_NO_FILE, 'error',
2128 1 : array('identifier' => $filename)
2129 1 : );
2130 :
2131 3 : } elseif (isset($duplicates) && !is_bool($duplicates)) {
2132 1 : return $this->raiseError(
2133 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2134 1 : array('var' => '$duplicates',
2135 1 : 'was' => gettype($duplicates),
2136 1 : 'expected' => 'bool',
2137 1 : 'paramnum' => 2)
2138 1 : );
2139 : }
2140 :
2141 2 : if (!isset($duplicates)) {
2142 1 : $duplicates = $this->__get('allowduplicates');
2143 1 : }
2144 :
2145 2 : $ret = $this->parseString(file_get_contents($filename), $duplicates);
2146 2 : return $ret;
2147 : }
2148 :
2149 : /**
2150 : * Parse multiple data sources
2151 : *
2152 : * Parse data sources, file(s) or string(s), that contains CSS information
2153 : *
2154 : * @param array $styles data sources to parse
2155 : * @param bool $duplicates (optional) Allow or disallow duplicates.
2156 : *
2157 : * @return void|PEAR_Error
2158 : * @since version 1.0.0RC2 (2005-12-15)
2159 : * @access public
2160 : * @throws HTML_CSS_ERROR_INVALID_INPUT
2161 : * @see parseString(), parseFile()
2162 : */
2163 : function parseData($styles, $duplicates = null)
2164 : {
2165 2 : if (!is_array($styles)) {
2166 1 : return $this->raiseError(
2167 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2168 1 : array('var' => '$styles',
2169 1 : 'was' => gettype($styles),
2170 1 : 'expected' => 'array',
2171 1 : 'paramnum' => 1)
2172 1 : );
2173 :
2174 2 : } elseif (isset($duplicates) && !is_bool($duplicates)) {
2175 1 : return $this->raiseError(
2176 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2177 1 : array('var' => '$duplicates',
2178 1 : 'was' => gettype($duplicates),
2179 1 : 'expected' => 'bool',
2180 1 : 'paramnum' => 2)
2181 1 : );
2182 : }
2183 :
2184 2 : if (!isset($duplicates)) {
2185 2 : $duplicates = $this->__get('allowduplicates');
2186 2 : }
2187 :
2188 2 : foreach ($styles as $i => $style) {
2189 2 : if (!is_string($style)) {
2190 1 : return $this->raiseError(
2191 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2192 1 : array('var' => '$styles[' . $i . ']',
2193 1 : 'was' => gettype($styles[$i]),
2194 1 : 'expected' => 'string',
2195 1 : 'paramnum' => 1)
2196 1 : );
2197 : }
2198 2 : if (strcasecmp(substr($style, -4, 4), '.css') == 0) {
2199 1 : $this->parseFile($style, $duplicates);
2200 1 : } else {
2201 2 : $this->parseString($style, $duplicates);
2202 : }
2203 2 : }
2204 1 : }
2205 :
2206 : /**
2207 : * Validate a CSS data source
2208 : *
2209 : * Execute the W3C CSS validator service on each data source (filename
2210 : * or string) given by parameter $styles.
2211 : *
2212 : * @param array $styles Data sources to check validity
2213 : * @param array &$messages Error and Warning messages
2214 : * issue from W3C CSS validator service
2215 : *
2216 : * @return boolean|PEAR_Error
2217 : * @since version 1.5.0 (2008-01-15)
2218 : * @access public
2219 : * @throws HTML_CSS_ERROR_INVALID_INPUT,
2220 : * HTML_CSS_ERROR_INVALID_DEPS, HTML_CSS_ERROR_INVALID_SOURCE
2221 : */
2222 : function validate($styles, &$messages)
2223 : {
2224 0 : $php = phpversion();
2225 0 : if (version_compare($php, '5.0.0', '<')) {
2226 0 : return $this->raiseError(
2227 0 : HTML_CSS_ERROR_INVALID_DEPS, 'exception',
2228 0 : array('funcname' => __FUNCTION__,
2229 0 : 'dependency' => 'PHP 5',
2230 0 : 'currentdep' => "PHP $php")
2231 0 : );
2232 : }
2233 0 : @include_once 'Services/W3C/CSSValidator.php';
2234 0 : if (class_exists('Services_W3C_CSSValidator', false) === false) {
2235 0 : return $this->raiseError(
2236 0 : HTML_CSS_ERROR_INVALID_DEPS, 'exception',
2237 0 : array('funcname' => __FUNCTION__,
2238 0 : 'dependency' => 'PEAR::Services_W3C_CSSValidator',
2239 0 : 'currentdep' => 'nothing')
2240 0 : );
2241 : }
2242 0 : if (!is_array($styles)) {
2243 0 : return $this->raiseError(
2244 0 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2245 0 : array('var' => '$styles',
2246 0 : 'was' => gettype($styles),
2247 0 : 'expected' => 'array',
2248 0 : 'paramnum' => 1)
2249 0 : );
2250 :
2251 0 : } elseif (!is_array($messages)) {
2252 0 : return $this->raiseError(
2253 0 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2254 0 : array('var' => '$messages',
2255 0 : 'was' => gettype($messages),
2256 0 : 'expected' => 'array',
2257 0 : 'paramnum' => 2)
2258 0 : );
2259 : }
2260 :
2261 : // prepare to call the W3C CSS validator service
2262 0 : $v = new Services_W3C_CSSValidator();
2263 0 : $validity = true;
2264 0 : $messages = array('errors' => array(), 'warnings' => array());
2265 :
2266 0 : foreach ($styles as $i => $source) {
2267 0 : if (!is_string($source)) {
2268 0 : return $this->raiseError(
2269 0 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2270 0 : array('var' => '$styles[' . $i . ']',
2271 0 : 'was' => gettype($styles[$i]),
2272 0 : 'expected' => 'string',
2273 0 : 'paramnum' => 1)
2274 0 : );
2275 : }
2276 0 : if (strcasecmp(substr($source, -4, 4), '.css') == 0) {
2277 : // validate a file as CSS content
2278 0 : $r = $v->validateFile($source);
2279 0 : } else {
2280 : // validate a string as CSS content
2281 0 : $r = $v->validateFragment($source);
2282 : }
2283 0 : if ($r === false) {
2284 0 : $validity = false;
2285 0 : }
2286 0 : if ($r->isValid() === false) {
2287 0 : $validity = false;
2288 0 : foreach ($r->errors as $error) {
2289 0 : $properties = get_object_vars($error);
2290 0 : $messages['errors'][] = $properties;
2291 0 : }
2292 0 : foreach ($r->warnings as $warning) {
2293 0 : $properties = get_object_vars($warning);
2294 0 : $messages['warnings'][] = $properties;
2295 0 : }
2296 0 : $this->raiseError(
2297 0 : HTML_CSS_ERROR_INVALID_SOURCE,
2298 0 : ((count($r->errors) == 0) ? 'warning' : 'error'),
2299 0 : array('sourcenum' => $i,
2300 0 : 'errcount' => count($r->errors),
2301 0 : 'warncount' => count($r->warnings))
2302 0 : );
2303 0 : }
2304 0 : }
2305 0 : return $validity;
2306 : }
2307 :
2308 : /**
2309 : * Return the CSS contents in an array
2310 : *
2311 : * Return the full contents of CSS data sources (parsed) in an array
2312 : *
2313 : * @return array
2314 : * @since version 0.2.0 (2003-07-31)
2315 : * @access public
2316 : */
2317 : function toArray()
2318 : {
2319 27 : $css = array();
2320 :
2321 : // bring AtRules in correct order
2322 27 : $this->sortAtRules();
2323 :
2324 27 : foreach ($this->_css as $key => $value) {
2325 26 : if (strpos($key, '@-') === 0) {
2326 10 : $key = implode(', ', $this->_groups[$key]);
2327 10 : }
2328 26 : $css[$key] = $value;
2329 27 : }
2330 27 : return $css;
2331 : }
2332 :
2333 : /**
2334 : * Return a string-properties for style attribute of an HTML element
2335 : *
2336 : * Generate and return the CSS properties of an element or class
2337 : * as a string for inline use.
2338 : *
2339 : * @param string $element Element or class
2340 : * for which inline CSS should be generated
2341 : *
2342 : * @return string|PEAR_Error
2343 : * @since version 0.2.0 (2003-07-31)
2344 : * @access public
2345 : * @throws HTML_CSS_ERROR_INVALID_INPUT
2346 : */
2347 : function toInline($element)
2348 : {
2349 2 : if (!is_string($element)) {
2350 1 : return $this->raiseError(
2351 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2352 1 : array('var' => '$element',
2353 1 : 'was' => gettype($element),
2354 1 : 'expected' => 'string',
2355 1 : 'paramnum' => 1)
2356 1 : );
2357 : }
2358 :
2359 1 : $strCss = '';
2360 1 : $newCssArray = array();
2361 :
2362 : // This allows for grouped elements definitions to work
2363 1 : if (isset($this->_alibis[$element])) {
2364 1 : $alibis = $this->_alibis[$element];
2365 :
2366 : // All the groups must be run through to be able to
2367 : // properly assign the value to the inline.
2368 1 : foreach ($alibis as $alibi) {
2369 1 : foreach ($this->_css[$alibi] as $key => $value) {
2370 1 : $newCssArray[$key] = $value;
2371 1 : }
2372 1 : }
2373 1 : }
2374 :
2375 : // This allows for single elements definitions to work
2376 1 : if (isset($this->_css[$element])) {
2377 0 : foreach ($this->_css[$element] as $rank => $property) {
2378 0 : if (!is_numeric($rank)) {
2379 0 : $property = array($rank => $property);
2380 0 : }
2381 0 : foreach ($property as $key => $value) {
2382 0 : if ($key != 'other-elements') {
2383 0 : $newCssArray[$key] = $value;
2384 0 : }
2385 0 : }
2386 0 : }
2387 0 : }
2388 :
2389 1 : foreach ($newCssArray as $key => $value) {
2390 1 : if ((0 === strpos($element, '@')) && ('' == $value)) {
2391 : // simple declarative At-Rule definition
2392 0 : $strCss .= $key . ';';
2393 0 : } else {
2394 : // other CSS definition
2395 1 : $strCss .= $key . ':' . $value . ";";
2396 : }
2397 1 : }
2398 :
2399 1 : return $strCss;
2400 : }
2401 :
2402 : /**
2403 : * Generate CSS and stores it in a file
2404 : *
2405 : * Generate current parsed CSS data sources and write result in a user file
2406 : *
2407 : * @param string $filename Name of file that content the stylesheet
2408 : *
2409 : * @return void|PEAR_Error
2410 : * @since version 0.3.0 (2003-11-03)
2411 : * @access public
2412 : * @throws HTML_CSS_ERROR_INVALID_INPUT, HTML_CSS_ERROR_WRITE_FILE
2413 : * @see toString()
2414 : */
2415 : function toFile($filename)
2416 : {
2417 2 : if (!is_string($filename)) {
2418 1 : return $this->raiseError(
2419 1 : HTML_CSS_ERROR_INVALID_INPUT, 'exception',
2420 1 : array('var' => '$filename',
2421 1 : 'was' => gettype($filename),
2422 1 : 'expected' => 'string',
2423 1 : 'paramnum' => 1)
2424 1 : );
2425 : }
2426 :
2427 1 : if (function_exists('file_put_contents')) {
2428 1 : file_put_contents($filename, $this->toString());
2429 1 : } else {
2430 0 : $file = fopen($filename, 'wb');
2431 0 : fwrite($file, $this->toString());
2432 0 : fclose($file);
2433 : }
2434 1 : if (!file_exists($filename)) {
2435 0 : return $this->raiseError(
2436 0 : HTML_CSS_ERROR_WRITE_FILE, 'error',
2437 0 : array('filename' => $filename)
2438 0 : );
2439 : }
2440 1 : }
2441 :
2442 : /**
2443 : * Return current CSS parsed data as a string
2444 : *
2445 : * Generate current parsed CSS data sources and return result as a string
2446 : *
2447 : * @return string
2448 : * @since version 0.2.0 (2003-07-31)
2449 : * @access public
2450 : */
2451 : function toString()
2452 : {
2453 : // get line endings
2454 2 : $lnEnd = $this->_getLineEnd();
2455 2 : $tabs = $this->_getTabs();
2456 2 : $tab = $this->_getTab();
2457 :
2458 : // initialize $alibis
2459 2 : $alibis = array();
2460 :
2461 2 : $strCss = '';
2462 2 : $strAtRules = '';
2463 :
2464 : // Allow a CSS comment
2465 2 : if ($this->_comment) {
2466 0 : $strCss = $tabs . '/* ' . $this->getComment() . ' */' . $lnEnd;
2467 0 : }
2468 :
2469 : // If groups are to be output first, initialize a special variable
2470 2 : if ($this->__get('groupsfirst')) {
2471 2 : $strCssElements = '';
2472 2 : }
2473 :
2474 : // bring AtRules in correct order
2475 2 : $this->sortAtRules();
2476 :
2477 : // Iterate through the array and process each element
2478 2 : foreach ($this->_css as $identifier => $rank) {
2479 :
2480 : // Groups are handled separately
2481 2 : if (strpos($identifier, '@-') !== false) {
2482 : // its a group
2483 2 : $element = implode(', ', $this->_groups[$identifier]);
2484 2 : } else {
2485 1 : $element = $identifier;
2486 : }
2487 :
2488 2 : if ((0 === strpos($element, '@')) && (1 !== strpos($element, '-'))) {
2489 : // simple declarative At-Rule definition
2490 0 : foreach ($rank as $arg => $decla) {
2491 : // check to see if it is a duplicate
2492 0 : if (is_numeric($arg)) {
2493 0 : $arg = array_keys($decla);
2494 0 : $arg = array_shift($arg);
2495 0 : $decla = array_values($decla);
2496 0 : $decla = array_shift($decla);
2497 0 : }
2498 0 : if (is_array($decla)) {
2499 0 : $strAtRules .= $element . ' ' . $arg;
2500 0 : foreach ($decla as $s => $d) {
2501 0 : $t = $tabs . $tab;
2502 0 : if (empty($s)) {
2503 0 : $strAtRules .= ' {' . $lnEnd;
2504 0 : } else {
2505 0 : $t .= $tab;
2506 0 : $strAtRules .= ' {' . $lnEnd .
2507 0 : $tab . $s . ' {' . $lnEnd;
2508 : }
2509 0 : foreach ($d as $p => $v) {
2510 0 : $strAtRules .= $t . $p . ': ' . $v . ';' . $lnEnd;
2511 0 : }
2512 0 : if (empty($s)) {
2513 0 : $strAtRules .= $tabs . '}';
2514 0 : } else {
2515 0 : $strAtRules .= $tabs . $tab . '}' . $lnEnd . '}';
2516 : }
2517 0 : }
2518 0 : $strAtRules .= $lnEnd . $lnEnd;;
2519 0 : } else {
2520 0 : $strAtRules .= $element . ' ' . $arg . ';' . $lnEnd . $lnEnd;
2521 : }
2522 0 : }
2523 0 : } else {
2524 : // Start CSS element definition
2525 2 : $definition = $element . ' {' . $lnEnd;
2526 :
2527 : // Iterate through the array of properties
2528 2 : foreach ($rank as $pos => $property) {
2529 : // check to see if it is a duplicate
2530 2 : if (!is_numeric($pos)) {
2531 2 : $property = array($pos => $property);
2532 2 : unset($pos);
2533 2 : }
2534 2 : foreach ($property as $key => $value) {
2535 : $definition .= $tabs . $tab
2536 2 : . $key . ': ' . $value . ';' . $lnEnd;
2537 2 : }
2538 2 : }
2539 :
2540 : // end CSS element definition
2541 2 : $definition .= $tabs . '}';
2542 : }
2543 :
2544 : // if this is to be on a single line, collapse
2545 2 : if ($this->options['oneline']) {
2546 1 : $definition = $this->collapseInternalSpaces($definition);
2547 1 : $strAtRules = $this->collapseInternalSpaces($strAtRules);
2548 1 : }
2549 :
2550 : // if groups are to be output first, elements must be placed in a
2551 : // different string which will be appended in the end
2552 2 : if (isset($definition)) {
2553 2 : if ($this->__get('groupsfirst') === true
2554 2 : && strpos($identifier, '@-') === false
2555 2 : ) {
2556 : // add to elements
2557 1 : $strCssElements .= $lnEnd . $tabs . $definition . $lnEnd;
2558 1 : } else {
2559 : // add to strCss
2560 2 : $strCss .= $lnEnd . $tabs . $definition . $lnEnd;
2561 : }
2562 2 : }
2563 2 : }
2564 :
2565 2 : if ($this->__get('groupsfirst')) {
2566 2 : $strCss .= $strCssElements;
2567 2 : }
2568 :
2569 2 : $strAtRules = rtrim($strAtRules);
2570 2 : if (!empty($strAtRules)) {
2571 0 : $strAtRules .= $lnEnd;
2572 0 : }
2573 2 : $strCss = $strAtRules . $strCss;
2574 :
2575 2 : if ($this->options['oneline']) {
2576 1 : $strCss = preg_replace('/(\n|\r\n|\r)/', '', $strCss);
2577 1 : }
2578 :
2579 2 : return $strCss;
2580 : }
2581 :
2582 : /**
2583 : * Output CSS Code.
2584 : *
2585 : * Send the stylesheet content to standard output, handling cacheControl
2586 : * and contentDisposition headers
2587 : *
2588 : * @return void
2589 : * @since version 0.2.0 (2003-07-31)
2590 : * @access public
2591 : * @see toString()
2592 : */
2593 : function display()
2594 : {
2595 1 : if (!headers_sent()) {
2596 0 : if ($this->__get('cache') !== true) {
2597 0 : header("Expires: Tue, 1 Jan 1980 12:00:00 GMT");
2598 0 : header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
2599 0 : header("Cache-Control: no-cache");
2600 0 : header("Pragma: no-cache");
2601 0 : }
2602 :
2603 : // set character encoding
2604 0 : header("Content-Type: text/css; charset=" . $this->__get('charset'));
2605 :
2606 : // set Content-Disposition
2607 0 : if ($this->__get('contentDisposition') !== false) {
2608 0 : header(
2609 : 'Content-Disposition: inline; filename="' .
2610 0 : $this->__get('contentDisposition') . '"'
2611 0 : );
2612 0 : }
2613 0 : }
2614 :
2615 1 : $strCss = $this->toString();
2616 1 : print $strCss;
2617 1 : }
2618 :
2619 : /**
2620 : * Initialize Error engine preferences
2621 : *
2622 : * @param array $prefs (optional) hash of params to customize error generation
2623 : *
2624 : * @return void
2625 : * @since version 0.3.3 (2004-05-20)
2626 : * @access private
2627 : */
2628 : function _initErrorStack($prefs = array())
2629 : {
2630 : // error message mapping callback
2631 56 : if (isset($prefs['message_callback'])
2632 0 : && is_callable($prefs['message_callback'])
2633 56 : ) {
2634 0 : $this->_callback_message = $prefs['message_callback'];
2635 0 : } else {
2636 56 : $this->_callback_message = array('HTML_CSS_Error', '_msgCallback');
2637 : }
2638 :
2639 : // error context mapping callback
2640 56 : if (isset($prefs['context_callback'])
2641 0 : && is_callable($prefs['context_callback'])
2642 56 : ) {
2643 0 : $this->_callback_context = $prefs['context_callback'];
2644 0 : } else {
2645 56 : $this->_callback_context = array('HTML_CSS_Error', 'getBacktrace');
2646 : }
2647 :
2648 : // determine whether to allow an error to be pushed or logged
2649 56 : if (isset($prefs['push_callback'])
2650 56 : && is_callable($prefs['push_callback'])
2651 56 : ) {
2652 56 : $this->_callback_push = $prefs['push_callback'];
2653 56 : } else {
2654 2 : $this->_callback_push = array('HTML_CSS_Error', '_handleError');
2655 : }
2656 :
2657 : // determine whether to display or log an error by a free user function
2658 56 : if (isset($prefs['error_callback'])
2659 56 : && is_callable($prefs['error_callback'])
2660 56 : ) {
2661 56 : $this->_callback_error = $prefs['error_callback'];
2662 56 : } else {
2663 2 : $this->_callback_error = null;
2664 : }
2665 :
2666 : // default error handler will use PEAR_Error
2667 56 : if (isset($prefs['error_handler'])
2668 0 : && is_callable($prefs['error_handler'])
2669 56 : ) {
2670 0 : $this->_callback_errorhandler = $prefs['error_handler'];
2671 0 : } else {
2672 56 : $this->_callback_errorhandler = array(&$this, '_errorHandler');
2673 : }
2674 :
2675 : // any handler-specific settings
2676 56 : if (isset($prefs['handler'])) {
2677 0 : $this->_errorhandler_options = $prefs['handler'];
2678 0 : }
2679 56 : }
2680 :
2681 : /**
2682 : * Standard error handler that will use PEAR_Error object
2683 : *
2684 : * To improve performances, the PEAR.php file is included dynamically.
2685 : * The file is so included only when an error is triggered. So, in most
2686 : * cases, the file isn't included and perfs are much better.
2687 : *
2688 : * @param integer $code Error code.
2689 : * @param string $level The error level of the message.
2690 : * @param array $params Associative array of error parameters
2691 : *
2692 : * @return PEAR_Error
2693 : * @since version 1.0.0 (2006-06-24)
2694 : * @access private
2695 : */
2696 : function _errorHandler($code, $level, $params)
2697 : {
2698 5 : include_once 'HTML/CSS/Error.php';
2699 :
2700 5 : $mode = call_user_func($this->_callback_push, $code, $level);
2701 5 : $message = call_user_func($this->_callback_message, $code, $params);
2702 5 : $options = $this->_callback_error;
2703 :
2704 5 : $userinfo['level'] = $level;
2705 :
2706 5 : if (isset($this->_errorhandler_options['display'])) {
2707 0 : $userinfo['display'] = $this->_errorhandler_options['display'];
2708 0 : } else {
2709 5 : $userinfo['display'] = array();
2710 : }
2711 5 : if (isset($this->_errorhandler_options['log'])) {
2712 0 : $userinfo['log'] = $this->_errorhandler_options['log'];
2713 0 : } else {
2714 5 : $userinfo['log'] = array();
2715 : }
2716 :
2717 5 : return PEAR::raiseError(
2718 5 : $message, $code, $mode, $options, $userinfo, 'HTML_CSS_Error'
2719 5 : );
2720 : }
2721 :
2722 : /**
2723 : * A basic wrapper around the default PEAR_Error object
2724 : *
2725 : * This method is a wrapper that returns an instance of the configured
2726 : * error class with this object's default error handling applied.
2727 : *
2728 : * @return object PEAR_Error when default error handler is used
2729 : * @since version 0.3.3 (2004-05-20)
2730 : * @access public
2731 : * @see _errorHandler()
2732 : */
2733 : function raiseError()
2734 : {
2735 5 : $args = func_get_args();
2736 5 : $this->_lastError
2737 5 : = call_user_func_array($this->_callback_errorhandler, $args);
2738 5 : return $this->_lastError;
2739 : }
2740 :
2741 : /**
2742 : * Determine whether there is an error
2743 : *
2744 : * Determine whether last action raised an error or not
2745 : *
2746 : * @return boolean TRUE if error raised, FALSE otherwise
2747 : * @since version 1.0.0RC2 (2005-12-15)
2748 : * @access public
2749 : */
2750 : function isError()
2751 : {
2752 0 : $res = (!is_bool($this->_lastError));
2753 0 : $this->_lastError = false;
2754 0 : return $res;
2755 : }
2756 : }
|