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 179 2009-06-13 13:29:57Z 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 :
11 : /**
12 : * include the parant class (FluentDOM)
13 : */
14 : require_once(dirname(__FILE__).'/FluentDOM.php');
15 :
16 : /**
17 : * Function to create a new FluentDOMStyle instance
18 : *
19 : * This is a shortcut for "new FluentDOMStyle($source)"
20 : *
21 : * @param mixed $source
22 : * @access public
23 : * @return object FluentDOMStyle
24 : */
25 : function FluentDOMStyle($content) {
26 15 : return new FluentDOMStyle($content);
27 : }
28 :
29 : /**
30 : * FluentDOMStyle extends the FluentDOM class with a function to edit
31 : * the style attribute of html tags
32 : */
33 : class FluentDOMStyle extends FluentDOM {
34 :
35 : /**
36 : * Pattern to decode the stlye property string
37 : */
38 : const STYLE_PATTERN = '((?:^|;)\s*(?P<name>[-\w]+)\s*:\s*(?P<value>[^;]+))';
39 :
40 : /**
41 : * redefine the _spawn() method to get an new instance of FluentDOMStyle
42 : *
43 : * @access protected
44 : * @return object FluentDOMStyle
45 : */
46 : protected function _spawn() {
47 14 : return new FluentDOMStyle($this);
48 : }
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
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 : }
|