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