1 : <?php
2 : /**
3 : * FluentDOMStyle extends the FluentDOM class with a function to edit
4 : * the style attribute of html tags
5 : *
6 : * @version $Id: Style.php 345 2009-10-19 19:51:37Z subjective $
7 : * @license http://www.opensource.org/licenses/mit-license.php The MIT License
8 : * @copyright Copyright (c) 2009 Bastian Feder, Thomas Weinert
9 : *
10 : * @package FluentDOM
11 : */
12 :
13 : /**
14 : * include the parent class (FluentDOM)
15 : */
16 : require_once(dirname(__FILE__).'/../FluentDOM.php');
17 :
18 : /**
19 : * Function to create a new FluentDOMStyleinstance and loads data into it if
20 : * a valid $source is provided.
21 : *
22 : * @param mixed $source
23 : * @param string $contentType optional, default value 'text/xml'
24 : * @access public
25 : * @return object FluentDOMStyle
26 : */
27 : function FluentDOMStyle($source = NULL, $contentType = 'text/xml') {
28 2 : $result = new FluentDOMStyle();
29 2 : if (isset($source)) {
30 1 : return $result->load($source, $contentType);
31 : } else {
32 1 : return $result;
33 : }
34 : }
35 :
36 : /**
37 : * FluentDOMStyle extends the FluentDOM class with a function to edit
38 : * the style attribute of html tags
39 : *
40 : * @package FluentDOM
41 : */
42 : class FluentDOMStyle extends FluentDOM {
43 :
44 : /**
45 : * Pattern to decode the stlye property string
46 : */
47 : const STYLE_PATTERN = '((?:^|;)\s*(?P<name>[-\w]+)\s*:\s*(?P<value>[^;]+))';
48 :
49 : /**
50 : * get or set CSS values in style attributes
51 : *
52 : * @param string|array $property
53 : * @param NULL|string|object Closure $value
54 : * @access public
55 : * @return string|object FluentDOMStyle
56 : */
57 : public function css($property, $value = NULL) {
58 14 : if (is_array($property)) {
59 : //set list of properties to all elements
60 6 : foreach ($this->_array as $node) {
61 6 : if ($node instanceof DOMElement) {
62 6 : $options = $this->_decodeStyleAttribute($node->getAttribute('style'));
63 6 : foreach ($property as $name => $value) {
64 6 : if ($this->_isCSSProperty($name)) {
65 5 : if (isset($options[$name]) && empty($value)) {
66 1 : unset($options[$name]);
67 5 : } elseif (!empty($value)) {
68 4 : $options[$name] = $value;
69 4 : }
70 5 : } else {
71 1 : throw new InvalidArgumentException('Invalid css property name: '.$property);
72 : }
73 5 : }
74 5 : $styleString = $this->_encodeStyleAttribute($options);
75 5 : if (empty($styleString) && $node->hasAttribute('style')) {
76 1 : $node->removeAttribute('style');
77 5 : } elseif (!empty($styleString)) {
78 4 : $node->setAttribute('style', $styleString);
79 4 : }
80 5 : }
81 5 : }
82 13 : } elseif (is_null($value)) {
83 : //get value from first DOMElement
84 4 : $firstNode = NULL;
85 4 : foreach ($this->_array as $node) {
86 3 : if ($node instanceof DOMElement) {
87 3 : $firstNode = $node;
88 3 : break;
89 : }
90 4 : }
91 4 : if (empty($firstNode)) {
92 1 : return NULL;
93 : } else {
94 3 : $options = $this->_decodeStyleAttribute($firstNode->getAttribute('style'));
95 3 : if (isset($options[$property])) {
96 2 : return $options[$property];
97 : }
98 : }
99 1 : return NULL;
100 : } else {
101 : //set value to all nodes
102 4 : if ($this->_isCSSProperty($property)) {
103 3 : foreach ($this->_array as $node) {
104 3 : if ($node instanceof DOMElement) {
105 3 : $options = $this->_decodeStyleAttribute($node->getAttribute('style'));
106 3 : if (empty($value)) {
107 1 : if (isset($options[$property])) {
108 1 : unset($options[$property]);
109 1 : }
110 3 : } elseif (is_string($value)) {
111 1 : $options[$property] = $value;
112 2 : } elseif ($this->_isCallback($value)) {
113 1 : $options[$property] = call_user_func(
114 1 : $value,
115 1 : $node,
116 1 : $property,
117 1 : empty($options[$property]) ? '' : $options[$property]
118 1 : );
119 1 : }
120 3 : $styleString = $this->_encodeStyleAttribute($options);
121 3 : if (empty($styleString) && $node->hasAttribute('style')) {
122 1 : $node->removeAttribute('style');
123 3 : } elseif (!empty($styleString)) {
124 2 : $node->setAttribute('style', $styleString);
125 2 : }
126 3 : }
127 3 : }
128 3 : } else {
129 1 : throw new InvalidArgumentException('Invalid css property name: '.$property);
130 : }
131 : }
132 8 : }
133 :
134 : /**
135 : * check if string is an valid css property name
136 : *
137 : * @param string $propertyName
138 : * @access private
139 : * @return boolean
140 : */
141 : private function _isCSSProperty($propertyName) {
142 13 : $pattern = '(^-?(?:[a-z]+-)*(?:[a-z]+)$)D';
143 13 : if (preg_match($pattern, $propertyName)) {
144 12 : return TRUE;
145 : }
146 2 : return FALSE;
147 : }
148 :
149 : /**
150 : * decode style attribute to css properties array
151 : *
152 : * @param string $styleString
153 : * @access private
154 : * @return array
155 : */
156 : private function _decodeStyleAttribute($styleString) {
157 12 : $result = array();
158 12 : if (!empty($styleString)) {
159 12 : $matches = array();
160 12 : if (preg_match_all(self::STYLE_PATTERN, $styleString, $matches, PREG_SET_ORDER)) {
161 12 : foreach ($matches as $match) {
162 12 : if (isset($match['name']) &&
163 12 : $this->_isCSSProperty($match['name']) &&
164 12 : !empty($match['value'])) {
165 12 : $result[$match['name']] = $match['value'];
166 12 : }
167 12 : }
168 12 : }
169 12 : }
170 12 : return $result;
171 : }
172 :
173 : /**
174 : * encode css options array for the style string
175 : *
176 : * @param array $properties
177 : * @access private
178 : * @return string
179 : */
180 : private function _encodeStyleAttribute($properties) {
181 8 : $result = '';
182 8 : if (is_array($properties) && count($properties) > 0) {
183 6 : uksort($properties, array($this, '_compareCSSProperties'));
184 6 : foreach ($properties as $name => $value) {
185 6 : $result .= ' '.$name.': '.$value.';';
186 6 : }
187 6 : }
188 8 : return substr($result, 1);
189 : }
190 :
191 : /**
192 : * compare to css property names
193 : *
194 : * by name, browser-prefix, level
195 : *
196 : * @param string $propertyNameOne
197 : * @param string $propertyNameTwo
198 : * @access private
199 : * @return integer
200 : */
201 : private function _compareCSSProperties($propertyNameOne, $propertyNameTwo) {
202 4 : $propertyOne = $this->_getCSSPropertyElements($propertyNameOne);
203 4 : $propertyTwo = $this->_getCSSPropertyElements($propertyNameTwo);
204 4 : $propertyOneLevels = count($propertyOne);
205 4 : $propertyTwoLevels = count($propertyTwo);
206 4 : $maxLevels = ($propertyOneLevels > $propertyTwoLevels)
207 4 : ? $propertyOneLevels : $propertyTwoLevels;
208 4 : for ($i = 0; $i < $maxLevels; ++$i) {
209 4 : if (isset($propertyOne[$i]) &&
210 4 : isset($propertyTwo[$i])) {
211 4 : $compare = strnatcasecmp(
212 4 : $propertyOne[$i],
213 4 : $propertyTwo[$i]
214 4 : );
215 4 : if ($compare != 0) {
216 4 : return $compare;
217 : }
218 2 : } else {
219 2 : break;
220 : }
221 2 : }
222 2 : if ($propertyOneLevels > $propertyTwoLevels) {
223 2 : return 1;
224 : } else {
225 2 : return -1;
226 : }
227 : }
228 :
229 : /**
230 : * decodes the css property name into an compareable array
231 : *
232 : * @access private
233 : * @return array
234 : */
235 : private function _getCSSPropertyElements($propertyName) {
236 4 : if (substr($propertyName, 0, 1) == '-') {
237 1 : $pos = strpos($propertyName, '-', 1);
238 1 : $items = explode('-', substr($propertyName, $pos + 1));
239 1 : $items[] = substr($propertyName, 1, $pos);
240 1 : return $items;
241 : } else {
242 4 : $items = explode('-', $propertyName);
243 4 : return $items;
244 : }
245 : }
|