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