1<?php 2/** 3 * CSSTidy - CSS Parser and Optimiser 4 * 5 * CSS Parser class 6 * 7 * This file is part of CSSTidy. 8 * 9 * CSSTidy is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * CSSTidy is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with CSSTidy; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 * @license http://opensource.org/licenses/gpl-license.php GNU Public License 24 * @package csstidy 25 * @author Florian Schmitz (floele at gmail dot com) 2005-2006 26 */ 27 28/** 29 * Various CSS data needed for correct optimisations etc. 30 * 31 * @version 1.2 32 */ 33require(dirname(__FILE__).'/data.inc.php'); 34 35/** 36 * Contains a class for printing CSS code 37 * 38 * @version 1.0 39 */ 40require(dirname(__FILE__).'/class.csstidy_print.php'); 41 42/** 43 * Contains a class for optimising CSS code 44 * 45 * @version 1.0 46 */ 47require(dirname(__FILE__).'/class.csstidy_optimise.php'); 48 49/** 50 * CSS Parser class 51 * 52 * This class represents a CSS parser which reads CSS code and saves it in an array. 53 * In opposite to most other CSS parsers, it does not use regular expressions and 54 * thus has full CSS2 support and a higher reliability. 55 * Additional to that it applies some optimisations and fixes to the CSS code. 56 * An online version should be available here: http://cdburnerxp.se/cssparse/css_optimiser.php 57 * @package csstidy 58 * @author Florian Schmitz (floele at gmail dot com) 2005-2006 59 * @version 1.2 60 */ 61class csstidy { 62 63/** 64 * Saves the parsed CSS 65 * @var array 66 * @access public 67 */ 68var $css = array(); 69 70/** 71 * Saves the parsed CSS (raw) 72 * @var array 73 * @access private 74 */ 75var $tokens = array(); 76 77/** 78 * Printer class 79 * @see csstidy_print 80 * @var object 81 * @access public 82 */ 83var $print; 84 85/** 86 * Optimiser class 87 * @see csstidy_optimise 88 * @var object 89 * @access private 90 */ 91var $optimise; 92 93/** 94 * Saves the CSS charset (@charset) 95 * @var string 96 * @access private 97 */ 98var $charset = ''; 99 100/** 101 * Saves all @import URLs 102 * @var array 103 * @access private 104 */ 105var $import = array(); 106 107/** 108 * Saves the namespace 109 * @var string 110 * @access private 111 */ 112var $namespace = ''; 113 114/** 115 * Contains the version of csstidy 116 * @var string 117 * @access private 118 */ 119var $version = '1.2'; 120 121/** 122 * Stores the settings 123 * @var array 124 * @access private 125 */ 126var $settings = array(); 127 128/** 129 * Saves the parser-status. 130 * 131 * Possible values: 132 * - is = in selector 133 * - ip = in property 134 * - iv = in value 135 * - instr = in string (started at " or ' or ( ) 136 * - ic = in comment (ignore everything) 137 * - at = in @-block 138 * 139 * @var string 140 * @access private 141 */ 142var $status = 'is'; 143 144 145/** 146 * Saves the current at rule (@media) 147 * @var string 148 * @access private 149 */ 150var $at = ''; 151 152/** 153 * Saves the current selector 154 * @var string 155 * @access private 156 */ 157var $selector = ''; 158 159/** 160 * Saves the current property 161 * @var string 162 * @access private 163 */ 164var $property = ''; 165 166/** 167 * Saves the position of , in selectors 168 * @var array 169 * @access private 170 */ 171var $sel_separate = array(); 172 173/** 174 * Saves the current value 175 * @var string 176 * @access private 177 */ 178var $value = ''; 179 180/** 181 * Saves the current sub-value 182 * 183 * Example for a subvalue: 184 * background:url(foo.png) red no-repeat; 185 * "url(foo.png)", "red", and "no-repeat" are subvalues, 186 * seperated by whitespace 187 * @var string 188 * @access private 189 */ 190var $sub_value = ''; 191 192/** 193 * Array which saves all subvalues for a property. 194 * @var array 195 * @see sub_value 196 * @access private 197 */ 198var $sub_value_arr = array(); 199 200/** 201 * Saves the char which opened the last string 202 * @var string 203 * @access private 204 */ 205var $str_char = ''; 206 207/** 208 * Status from which the parser switched to ic or instr 209 * @var string 210 * @access private 211 */ 212var $from = ''; 213 214/** 215 * Variable needed to manage string-in-strings, for example url("foo.png") 216 * @var string 217 * @access private 218 */ 219var $str_in_str = false; 220 221/** 222 * =true if in invalid at-rule 223 * @var bool 224 * @access private 225 */ 226var $invalid_at = false; 227 228/** 229 * =true if something has been added to the current selector 230 * @var bool 231 * @access private 232 */ 233var $added = false; 234 235/** 236 * Array which saves the message log 237 * @var array 238 * @access private 239 */ 240var $log = array(); 241 242/** 243 * Saves the line number 244 * @var integer 245 * @access private 246 */ 247var $line = 1; 248 249/** 250 * Loads standard template and sets default settings 251 * @access private 252 * @version 1.2 253 */ 254function csstidy() 255{ 256 $this->settings['remove_bslash'] = true; 257 $this->settings['compress_colors'] = true; 258 $this->settings['compress_font-weight'] = true; 259 $this->settings['lowercase_s'] = false; 260 $this->settings['optimise_shorthands'] = 1; 261 $this->settings['remove_last_;'] = false; 262 $this->settings['case_properties'] = 1; 263 $this->settings['sort_properties'] = false; 264 $this->settings['sort_selectors'] = false; 265 $this->settings['merge_selectors'] = 2; 266 $this->settings['discard_invalid_properties'] = false; 267 $this->settings['css_level'] = 'CSS2.1'; 268 $this->settings['preserve_css'] = false; 269 $this->settings['timestamp'] = false; 270 271 $this->load_template('default'); 272 $this->print = new csstidy_print($this); 273 $this->optimise = new csstidy_optimise($this); 274} 275 276/** 277 * Get the value of a setting. 278 * @param string $setting 279 * @access public 280 * @return mixed 281 * @version 1.0 282 */ 283function get_cfg($setting) 284{ 285 if(isset($this->settings[$setting])) 286 { 287 return $this->settings[$setting]; 288 } 289 return false; 290} 291 292/** 293 * Set the value of a setting. 294 * @param string $setting 295 * @param mixed $value 296 * @access public 297 * @return bool 298 * @version 1.0 299 */ 300function set_cfg($setting,$value) 301{ 302 if(isset($this->settings[$setting]) && $value !== '') 303 { 304 $this->settings[$setting] = $value; 305 return true; 306 } 307 return false; 308} 309 310/** 311 * Adds a token to $this->tokens 312 * @param mixed $type 313 * @param string $data 314 * @param bool $do add a token even if preserve_css is off 315 * @access private 316 * @version 1.0 317 */ 318function _add_token($type, $data, $do = false) { 319 if($this->get_cfg('preserve_css') || $do) { 320 $this->tokens[] = array($type, ($type == COMMENT) ? $data : trim($data)); 321 } 322} 323 324/** 325 * Add a message to the message log 326 * @param string $message 327 * @param string $type 328 * @param integer $line 329 * @access private 330 * @version 1.0 331 */ 332function log($message,$type,$line = -1) 333{ 334 if($line === -1) 335 { 336 $line = $this->line; 337 } 338 $line = intval($line); 339 $add = array('m' => $message, 't' => $type); 340 if(!isset($this->log[$line]) || !in_array($add,$this->log[$line])) 341 { 342 $this->log[$line][] = $add; 343 } 344} 345 346/** 347 * Parse unicode notations and find a replacement character 348 * @param string $string 349 * @param integer $i 350 * @access private 351 * @return string 352 * @version 1.2 353 */ 354function _unicode(&$string, &$i) 355{ 356 ++$i; 357 $add = ''; 358 $tokens =& $GLOBALS['csstidy']['tokens']; 359 $replaced = false; 360 361 while($i < strlen($string) && (ctype_xdigit($string{$i}) || ctype_space($string{$i})) && strlen($add) < 6) 362 { 363 $add .= $string{$i}; 364 365 if(ctype_space($string{$i})) { 366 break; 367 } 368 $i++; 369 } 370 371 if(hexdec($add) > 47 && hexdec($add) < 58 || hexdec($add) > 64 && hexdec($add) < 91 || hexdec($add) > 96 && hexdec($add) < 123) 372 { 373 $this->log('Replaced unicode notation: Changed \\'. $add .' to ' . chr(hexdec($add)),'Information'); 374 $add = chr(hexdec($add)); 375 $replaced = true; 376 } 377 else { 378 $add = trim('\\'.$add); 379 } 380 381 if(@ctype_xdigit($string{$i+1}) && ctype_space($string{$i}) 382 && !$replaced || !ctype_space($string{$i})) { 383 $i--; 384 } 385 386 if($add != '\\' || !$this->get_cfg('remove_bslash') || strpos($tokens, $string{$i+1}) !== false) { 387 return $add; 388 } 389 390 if($add == '\\') { 391 $this->log('Removed unnecessary backslash','Information'); 392 } 393 return ''; 394} 395 396/** 397 * Loads a new template 398 * @param string $content either filename (if $from_file == true), content of a template file, "high_compression", "highest_compression", "low_compression", or "default" 399 * @param bool $from_file uses $content as filename if true 400 * @access public 401 * @version 1.1 402 * @see http://csstidy.sourceforge.net/templates.php 403 */ 404function load_template($content, $from_file=true) 405{ 406 $predefined_templates =& $GLOBALS['csstidy']['predefined_templates']; 407 if($content == 'high_compression' || $content == 'default' || $content == 'highest_compression' || $content == 'low_compression') 408 { 409 $this->template = $predefined_templates[$content]; 410 return; 411 } 412 413 if($from_file) 414 { 415 $content = strip_tags(file_get_contents($content),'<span>'); 416 } 417 $content = str_replace("\r\n","\n",$content); // Unify newlines (because the output also only uses \n) 418 $template = explode('|',$content); 419 420 for ($i = 0; $i < count($template); $i++ ) 421 { 422 $this->template[$i] = $template[$i]; 423 } 424} 425 426/** 427 * Starts parsing from URL 428 * @param string $url 429 * @access public 430 * @version 1.0 431 */ 432function parse_from_url($url) 433{ 434 return $this->parse(@file_get_contents($url)); 435} 436 437/** 438 * Checks if there is a token at the current position 439 * @param string $string 440 * @param integer $i 441 * @access public 442 * @version 1.11 443 */ 444function is_token(&$string, $i) 445{ 446 $tokens =& $GLOBALS['csstidy']['tokens']; 447 return (strpos($tokens, $string{$i}) !== false && !csstidy::escaped($string,$i)); 448} 449 450 451/** 452 * Parses CSS in $string. The code is saved as array in $this->css 453 * @param string $string the CSS code 454 * @access public 455 * @return bool 456 * @version 1.1 457 */ 458function parse($string) { 459 // PHP bug? Settings need to be refreshed in PHP4 460 $this->print = new csstidy_print($this); 461 $this->optimise = new csstidy_optimise($this); 462 463 $all_properties =& $GLOBALS['csstidy']['all_properties']; 464 $at_rules =& $GLOBALS['csstidy']['at_rules']; 465 466 $this->css = array(); 467 $this->print->input_css = $string; 468 $string = str_replace("\r\n","\n",$string) . ' '; 469 $cur_comment = ''; 470 471 for ($i = 0, $size = strlen($string); $i < $size; $i++ ) 472 { 473 if($string{$i} == "\n" || $string{$i} == "\r") 474 { 475 ++$this->line; 476 } 477 478 switch($this->status) 479 { 480 /* Case in at-block */ 481 case 'at': 482 if(csstidy::is_token($string,$i)) 483 { 484 if($string{$i} == '/' && @$string{$i+1} == '*') 485 { 486 $this->status = 'ic'; ++$i; 487 $this->from = 'at'; 488 } 489 elseif($string{$i} == '{') 490 { 491 $this->status = 'is'; 492 $this->_add_token(AT_START, $this->at); 493 } 494 elseif($string{$i} == ',') 495 { 496 $this->at = trim($this->at).','; 497 } 498 elseif($string{$i} == '\\') 499 { 500 $this->at .= $this->_unicode($string,$i); 501 } 502 } 503 else 504 { 505 $lastpos = strlen($this->at)-1; 506 if(!( (ctype_space($this->at{$lastpos}) || csstidy::is_token($this->at,$lastpos) && $this->at{$lastpos} == ',') && ctype_space($string{$i}))) 507 { 508 $this->at .= $string{$i}; 509 } 510 } 511 break; 512 513 /* Case in-selector */ 514 case 'is': 515 if(csstidy::is_token($string,$i)) 516 { 517 if($string{$i} == '/' && @$string{$i+1} == '*' && trim($this->selector) == '') 518 { 519 $this->status = 'ic'; ++$i; 520 $this->from = 'is'; 521 } 522 elseif($string{$i} == '@' && trim($this->selector) == '') 523 { 524 // Check for at-rule 525 $this->invalid_at = true; 526 foreach($at_rules as $name => $type) 527 { 528 if(!strcasecmp(substr($string,$i+1,strlen($name)),$name)) 529 { 530 ($type == 'at') ? $this->at = '@'.$name : $this->selector = '@'.$name; 531 $this->status = $type; 532 $i += strlen($name); 533 $this->invalid_at = false; 534 } 535 } 536 537 if($this->invalid_at) 538 { 539 $this->selector = '@'; 540 $invalid_at_name = ''; 541 for($j = $i+1; $j < $size; ++$j) 542 { 543 if(!ctype_alpha($string{$j})) 544 { 545 break; 546 } 547 $invalid_at_name .= $string{$j}; 548 } 549 $this->log('Invalid @-rule: '.$invalid_at_name.' (removed)','Warning'); 550 } 551 } 552 elseif(($string{$i} == '"' || $string{$i} == "'")) 553 { 554 $this->selector .= $string{$i}; 555 $this->status = 'instr'; 556 $this->str_char = $string{$i}; 557 $this->from = 'is'; 558 } 559 elseif($this->invalid_at && $string{$i} == ';') 560 { 561 $this->invalid_at = false; 562 $this->status = 'is'; 563 } 564 elseif($string{$i} == '{') 565 { 566 $this->status = 'ip'; 567 $this->_add_token(SEL_START, $this->selector); 568 $this->added = false; 569 } 570 elseif($string{$i} == '}') 571 { 572 $this->_add_token(AT_END, $this->at); 573 $this->at = ''; 574 $this->selector = ''; 575 $this->sel_separate = array(); 576 } 577 elseif($string{$i} == ',') 578 { 579 $this->selector = trim($this->selector).','; 580 $this->sel_separate[] = strlen($this->selector); 581 } 582 elseif($string{$i} == '\\') 583 { 584 $this->selector .= $this->_unicode($string,$i); 585 } 586 else $this->selector .= $string{$i}; 587 } 588 else 589 { 590 $lastpos = strlen($this->selector)-1; 591 if($lastpos == -1 || !( (ctype_space($this->selector{$lastpos}) || csstidy::is_token($this->selector,$lastpos) && $this->selector{$lastpos} == ',') && ctype_space($string{$i}))) 592 { 593 $this->selector .= $string{$i}; 594 } 595 } 596 break; 597 598 /* Case in-property */ 599 case 'ip': 600 if(csstidy::is_token($string,$i)) 601 { 602 if(($string{$i} == ':' || $string{$i} == '=') && $this->property != '') 603 { 604 $this->status = 'iv'; 605 if(csstidy::property_is_valid($this->property) || !$this->get_cfg('discard_invalid_properties')) { 606 $this->_add_token(PROPERTY, $this->property); 607 } 608 } 609 elseif($string{$i} == '/' && @$string{$i+1} == '*' && $this->property == '') 610 { 611 $this->status = 'ic'; ++$i; 612 $this->from = 'ip'; 613 } 614 elseif($string{$i} == '}') 615 { 616 $this->explode_selectors(); 617 $this->status = 'is'; 618 $this->invalid_at = false; 619 $this->_add_token(SEL_END, $this->selector); 620 $this->selector = ''; 621 $this->property = ''; 622 } 623 elseif($string{$i} == ';') 624 { 625 $this->property = ''; 626 } 627 elseif($string{$i} == '\\') 628 { 629 $this->property .= $this->_unicode($string,$i); 630 } 631 } 632 elseif(!ctype_space($string{$i})) 633 { 634 $this->property .= $string{$i}; 635 } 636 break; 637 638 /* Case in-value */ 639 case 'iv': 640 $pn = (($string{$i} == "\n" || $string{$i} == "\r") && $this->property_is_next($string,$i+1) || $i == strlen($string)-1); 641 if(csstidy::is_token($string,$i) || $pn) 642 { 643 if($string{$i} == '/' && @$string{$i+1} == '*') 644 { 645 $this->status = 'ic'; ++$i; 646 $this->from = 'iv'; 647 } 648 elseif(($string{$i} == '"' || $string{$i} == "'" || $string{$i} == '(')) 649 { 650 $this->sub_value .= $string{$i}; 651 $this->str_char = ($string{$i} == '(') ? ')' : $string{$i}; 652 $this->status = 'instr'; 653 $this->from = 'iv'; 654 } 655 elseif($string{$i} == ',') 656 { 657 $this->sub_value = trim($this->sub_value).','; 658 } 659 elseif($string{$i} == '\\') 660 { 661 $this->sub_value .= $this->_unicode($string,$i); 662 } 663 elseif($string{$i} == ';' || $pn) 664 { 665 if($this->selector{0} == '@' && isset($at_rules[substr($this->selector,1)]) && $at_rules[substr($this->selector,1)] == 'iv') 666 { 667 $this->sub_value_arr[] = trim($this->sub_value); 668 669 $this->status = 'is'; 670 671 switch($this->selector) 672 { 673 case '@charset': $this->charset = $this->sub_value_arr[0]; break; 674 case '@namespace': $this->namespace = implode(' ',$this->sub_value_arr); break; 675 case '@import': $this->import[] = implode(' ',$this->sub_value_arr); break; 676 } 677 678 $this->sub_value_arr = array(); 679 $this->sub_value = ''; 680 $this->selector = ''; 681 $this->sel_separate = array(); 682 } 683 else 684 { 685 $this->status = 'ip'; 686 } 687 } 688 elseif($string{$i} != '}') 689 { 690 $this->sub_value .= $string{$i}; 691 } 692 if(($string{$i} == '}' || $string{$i} == ';' || $pn) && !empty($this->selector)) 693 { 694 if($this->at == '') 695 { 696 $this->at = DEFAULT_AT; 697 } 698 699 // case settings 700 if($this->get_cfg('lowercase_s')) 701 { 702 $this->selector = strtolower($this->selector); 703 } 704 $this->property = strtolower($this->property); 705 706 $this->optimise->subvalue(); 707 if($this->sub_value != '') { 708 $this->sub_value_arr[] = $this->sub_value; 709 $this->sub_value = ''; 710 } 711 712 $this->value = implode(' ',$this->sub_value_arr); 713 714 $this->selector = trim($this->selector); 715 716 $this->optimise->value(); 717 718 $valid = csstidy::property_is_valid($this->property); 719 if((!$this->invalid_at || $this->get_cfg('preserve_css')) && (!$this->get_cfg('discard_invalid_properties') || $valid)) 720 { 721 $this->css_add_property($this->at,$this->selector,$this->property,$this->value); 722 $this->_add_token(VALUE, $this->value); 723 $this->optimise->shorthands(); 724 } 725 if(!$valid) 726 { 727 if($this->get_cfg('discard_invalid_properties')) 728 { 729 $this->log('Removed invalid property: '.$this->property,'Warning'); 730 } 731 else 732 { 733 $this->log('Invalid property in '.strtoupper($this->get_cfg('css_level')).': '.$this->property,'Warning'); 734 } 735 } 736 737 $this->property = ''; 738 $this->sub_value_arr = array(); 739 $this->value = ''; 740 } 741 if($string{$i} == '}') 742 { 743 $this->explode_selectors(); 744 $this->_add_token(SEL_END, $this->selector); 745 $this->status = 'is'; 746 $this->invalid_at = false; 747 $this->selector = ''; 748 } 749 } 750 elseif(!$pn) 751 { 752 $this->sub_value .= $string{$i}; 753 754 if(ctype_space($string{$i})) 755 { 756 $this->optimise->subvalue(); 757 if($this->sub_value != '') { 758 $this->sub_value_arr[] = $this->sub_value; 759 $this->sub_value = ''; 760 } 761 } 762 } 763 break; 764 765 /* Case in string */ 766 case 'instr': 767 if($this->str_char == ')' && $string{$i} == '"' && !$this->str_in_str && !csstidy::escaped($string,$i)) 768 { 769 $this->str_in_str = true; 770 } 771 elseif($this->str_char == ')' && $string{$i} == '"' && $this->str_in_str && !csstidy::escaped($string,$i)) 772 { 773 $this->str_in_str = false; 774 } 775 if($string{$i} == $this->str_char && !csstidy::escaped($string,$i) && !$this->str_in_str) 776 { 777 $this->status = $this->from; 778 } 779 $temp_add = $string{$i}; 780 // ...and no not-escaped backslash at the previous position 781 if( ($string{$i} == "\n" || $string{$i} == "\r") && !($string{$i-1} == '\\' && !csstidy::escaped($string,$i-1)) ) 782 { 783 $temp_add = "\\A "; 784 $this->log('Fixed incorrect newline in string','Warning'); 785 } 786 if($this->from == 'iv') 787 { 788 $this->sub_value .= $temp_add; 789 } 790 elseif($this->from == 'is') 791 { 792 $this->selector .= $temp_add; 793 } 794 break; 795 796 /* Case in-comment */ 797 case 'ic': 798 if($string{$i} == '*' && $string{$i+1} == '/') 799 { 800 $this->status = $this->from; 801 $i++; 802 $this->_add_token(COMMENT, $cur_comment); 803 $cur_comment = ''; 804 } 805 else 806 { 807 $cur_comment .= $string{$i}; 808 } 809 break; 810 } 811 } 812 813 $this->optimise->postparse(); 814 815 $this->print->_reset(); 816 817 return !(empty($this->css) && empty($this->import) && empty($this->charset) && empty($this->tokens) && empty($this->namespace)); 818} 819 820/** 821 * Explodes selectors 822 * @access private 823 * @version 1.0 824 */ 825function explode_selectors() 826{ 827 // Explode multiple selectors 828 if($this->get_cfg('merge_selectors') == 1) 829 { 830 $new_sels = array(); 831 $lastpos = 0; 832 $this->sel_separate[] = strlen($this->selector); 833 foreach($this->sel_separate as $num => $pos) 834 { 835 if($num == count($this->sel_separate)-1) { 836 $pos += 1; 837 } 838 839 $new_sels[] = substr($this->selector,$lastpos,$pos-$lastpos-1); 840 $lastpos = $pos; 841 } 842 843 if(count($new_sels) > 1) 844 { 845 foreach($new_sels as $selector) 846 { 847 $this->merge_css_blocks($this->at,$selector,$this->css[$this->at][$this->selector]); 848 } 849 unset($this->css[$this->at][$this->selector]); 850 } 851 } 852 $this->sel_separate = array(); 853} 854 855/** 856 * Checks if a character is escaped (and returns true if it is) 857 * @param string $string 858 * @param integer $pos 859 * @access public 860 * @return bool 861 * @version 1.02 862 */ 863function escaped(&$string,$pos) 864{ 865 return !(@($string{$pos-1} != '\\') || csstidy::escaped($string,$pos-1)); 866} 867 868/** 869 * Adds a property with value to the existing CSS code 870 * @param string $media 871 * @param string $selector 872 * @param string $property 873 * @param string $new_val 874 * @access private 875 * @version 1.2 876 */ 877function css_add_property($media,$selector,$property,$new_val) 878{ 879 if($this->get_cfg('preserve_css') || trim($new_val) == '') { 880 return; 881 } 882 883 $this->added = true; 884 if(isset($this->css[$media][$selector][$property])) 885 { 886 if((csstidy::is_important($this->css[$media][$selector][$property]) && csstidy::is_important($new_val)) || !csstidy::is_important($this->css[$media][$selector][$property])) 887 { 888 unset($this->css[$media][$selector][$property]); 889 $this->css[$media][$selector][$property] = trim($new_val); 890 } 891 } 892 else 893 { 894 $this->css[$media][$selector][$property] = trim($new_val); 895 } 896} 897 898/** 899 * Adds CSS to an existing media/selector 900 * @param string $media 901 * @param string $selector 902 * @param array $css_add 903 * @access private 904 * @version 1.1 905 */ 906function merge_css_blocks($media,$selector,$css_add) 907{ 908 foreach($css_add as $property => $value) 909 { 910 $this->css_add_property($media,$selector,$property,$value,false); 911 } 912} 913 914/** 915 * Checks if $value is !important. 916 * @param string $value 917 * @return bool 918 * @access public 919 * @version 1.0 920 */ 921function is_important(&$value) 922{ 923 return (!strcasecmp(substr(str_replace($GLOBALS['csstidy']['whitespace'],'',$value),-10,10),'!important')); 924} 925 926/** 927 * Returns a value without !important 928 * @param string $value 929 * @return string 930 * @access public 931 * @version 1.0 932 */ 933function gvw_important($value) 934{ 935 if(csstidy::is_important($value)) 936 { 937 $value = trim($value); 938 $value = substr($value,0,-9); 939 $value = trim($value); 940 $value = substr($value,0,-1); 941 $value = trim($value); 942 return $value; 943 } 944 return $value; 945} 946 947/** 948 * Checks if the next word in a string from pos is a CSS property 949 * @param string $istring 950 * @param integer $pos 951 * @return bool 952 * @access private 953 * @version 1.2 954 */ 955function property_is_next($istring, $pos) 956{ 957 $all_properties =& $GLOBALS['csstidy']['all_properties']; 958 $istring = substr($istring,$pos,strlen($istring)-$pos); 959 $pos = strpos($istring,':'); 960 if($pos === false) 961 { 962 return false; 963 } 964 $istring = strtolower(trim(substr($istring,0,$pos))); 965 if(isset($all_properties[$istring])) 966 { 967 $this->log('Added semicolon to the end of declaration','Warning'); 968 return true; 969 } 970 return false; 971} 972 973/** 974 * Checks if a property is valid 975 * @param string $property 976 * @return bool; 977 * @access public 978 * @version 1.0 979 */ 980function property_is_valid($property) { 981 $all_properties =& $GLOBALS['csstidy']['all_properties']; 982 return (isset($all_properties[$property]) && strpos($all_properties[$property],strtoupper($this->get_cfg('css_level'))) !== false ); 983} 984 985} 986?>