1<?php 2class FlowContext { 3 var $absolute_positioned; 4 var $fixed_positioned; 5 6 var $viewport; 7 var $_floats; 8 var $collapsed_margins; 9 var $container_uid; 10 11 function add_absolute_positioned(&$box) { 12 $this->absolute_positioned[] =& $box; 13 } 14 15 function add_fixed_positioned(&$box) { 16 $this->fixed_positioned[] =& $box; 17 } 18 19 function add_float(&$float) { 20 $this->_floats[0][] =& $float; 21 } 22 23 function container_uid() { 24 return $this->container_uid[0]; 25 } 26 27 function ¤t_floats() { 28 return $this->_floats[0]; 29 } 30 31 // Get the bottom edge coordinate of the bottommost float in 32 // current formatting context 33 // 34 // @return null in case of no floats exists in current context 35 // numeric coordinate value otherwise 36 // 37 function float_bottom() { 38 $floats =& $this->current_floats(); 39 40 if (count($floats) == 0) { return null; } 41 42 $bottom = $floats[0]->get_bottom_margin(); 43 $size = count($floats); 44 for ($i=1; $i<$size; $i++) { 45 $bottom = min($bottom, $floats[$i]->get_bottom_margin()); 46 }; 47 48 return $bottom; 49 } 50 51 // Calculates the leftmost x-coordinate not covered by floats in current context 52 // at the given level (y-coordinate) 53 // 54 // @param $x starting X coordinate (no point to the left of this allowed) 55 // @param $y Y coordinate we're searching at 56 // @return the leftmost X coordinate value 57 // 58 function float_left_x($x, $y) { 59 $floats =& $this->current_floats(); 60 61 $size = count($floats); 62 for ($i=0; $i<$size; $i++) { 63 $float =& $floats[$i]; 64 65 // Process only left-floating boxes 66 if ($float->get_css_property(CSS_FLOAT) == FLOAT_LEFT) { 67 // Check if this float contains given Y-coordinate 68 // 69 // Note that top margin coordinate is inclusive but 70 // bottom margin coordinate is exclusive! The cause is following: 71 // - if we have several floats in one line, their top margin edge Y coordinates will be equal, 72 // so we must use agreater or equal sign to avod placing all floats at one X coordinate 73 // - on the other side, if we place one float under the other, the top margin Y coordinate 74 // of bottom float will be equal to bottom margin Y coordinate of the top float and 75 // we should NOT offset tho bottom float in this case 76 // 77 78 if ($float->get_top_margin() + EPSILON >= $y && 79 $float->get_bottom_margin() < $y) { 80 $x = max($x, $float->get_right_margin()); 81 }; 82 }; 83 }; 84 85 return $x; 86 } 87 88 // Calculates position of left floating box (taking into account the possibility 89 // of "wrapping" float to next line in case we have not enough space at current level (Y coordinate) 90 // 91 // @param $parent reference to a parent box 92 // @param $width width of float being placed. Full width! so, extra horizontal space (padding, margins and borders) is added here too 93 // @param $x [out] X coordinate of float upper-left corner 94 // @param $y [in,out] Y coordinate of float upper-left corner 95 // 96 function float_left_xy(&$parent, $width, &$x, &$y) { 97 // Numbler of floats to clear; we need this because of the following example: 98 // <div style="width: 150px; background-color: red; padding: 5px;"> 99 // <div style="float: left; background-color: green; height: 40px; width: 100px;">T</div> 100 // <div style="float: left; background-color: yellow; height: 20px; width: 50px;">T</div> 101 // <div style="float: left; background-color: cyan; height: 20px; width: 50px;">T</div> 102 // in this case the third float will be rendered directly under the second, so only the 103 // second float should be cleared 104 105 $clear = 0; 106 107 $floats =& $this->current_floats(); 108 109 // Prepare information about the float bottom coordinates 110 $float_bottoms = array(); 111 $size = count($floats); 112 for ($i=0; $i<$size; $i++) { 113 $float_bottoms[] = $floats[$i]->get_bottom_margin(); 114 }; 115 116 // Note that the sort function SHOULD NOT maintain key-value assotiations! 117 rsort($float_bottoms); 118 119 do { 120 $x = $this->float_left_x($parent->get_left(), $y); 121 122 // Check if current float will fit into the parent box 123 // OR if there's no parent boxes with constrained width (it will expanded in this case anyway) 124 125 // small value to hide the rounding errors 126 $parent_wc = $parent->get_css_property(CSS_WIDTH); 127 if ($parent->get_right() + EPSILON >= $x + $width || 128 $parent->mayBeExpanded()) { 129 130 // Will fit; 131 // Check if current float will intersect the existing left-floating box 132 // 133 $x1 = $this->float_right_x($parent->get_right(), $y); 134 if ($x1 + EPSILON > $x + $width) { 135 return; 136 }; 137 return; 138 }; 139 140 // print("CLEAR<br/>"); 141 142 // No, float does not fit at current level, let's try to 'clear' some previous floats 143 $clear++; 144 145 // Check if we've cleared all existing floats; the loop will be terminated in this case, of course, 146 // but we can get a notice/warning message if we'll try to access the non-existing array element 147 if ($clear <= count($floats)) { $y = min( $y, $float_bottoms[$clear-1] ); }; 148 149 } while ($clear <= count($floats)); // We need to check if all floats have been cleared to avoid infinite loop 150 151 // All floats are cleared; fall back to the leftmost X coordinate 152 $x = $parent->get_left(); 153 } 154 155 // Get the right edge coordinate of the rightmost float in 156 // current formatting context 157 // 158 // @return null in case of no floats exists in current context 159 // numeric coordinate value otherwise 160 // 161 function float_right() { 162 $floats =& $this->current_floats(); 163 164 if (count($floats) == 0) { return null; } 165 166 $right = $floats[0]->get_right_margin(); 167 $size = count($floats); 168 for ($i=1; $i<$size; $i++) { 169 $right = max($right, $floats[$i]->get_right_margin()); 170 }; 171 172 return $right; 173 } 174 175 // Calculates the rightmost x-coordinate not covered by floats in current context 176 // at the given level (y-coordinate) 177 // 178 // @param $x starting X coordinate (no point to the right of this allowed) 179 // @param $y Y coordinate we're searching at 180 // @return the rightmost X coordinate value 181 // 182 function float_right_x($x, $y) { 183 $floats =& $this->current_floats(); 184 185 $size = count($floats); 186 for ($i=0; $i<$size; $i++) { 187 $float =& $floats[$i]; 188 189 // Process only right-floating boxes 190 if ($float->get_css_property(CSS_FLOAT) == FLOAT_RIGHT) { 191 // Check if this float contains given Y-coordinate 192 // 193 // Note that top margin coordinate is inclusive but 194 // bottom margin coordinate is exclusive! The cause is following: 195 // - if we have several floats in one line, their top margin edge Y coordinates will be equal, 196 // so we must use agreater or equal sign to avod placing all floats at one X coordinate 197 // - on the other side, if we place one float under the other, the top margin Y coordinate 198 // of bottom float will be equal to bottom margin Y coordinate of the top float and 199 // we should NOT offset tho bottom float in this case 200 // 201 202 if ($float->get_top_margin() + EPSILON >= $y && 203 $float->get_bottom_margin() < $y) { 204 $x = min($x, $float->get_left_margin()); 205 }; 206 }; 207 }; 208 209 return $x; 210 } 211 212 // Calculates position of right floating box (taking into account the possibility 213 // of "wrapping" float to next line in case we have not enough space at current level (Y coordinate) 214 // 215 // @param $parent reference to a parent box 216 // @param $width width of float being placed. Full width! so, extra horizontal space (padding, margins and borders) is added here too 217 // @param $x [out] X coordinate of float upper-right corner 218 // @param $y [in,out] Y coordinate of float upper-right corner 219 // 220 function float_right_xy(&$parent, $width, &$x, &$y) { 221 // Numbler of floats to clear; we need this because of the following example: 222 // <div style="width: 150px; background-color: red; padding: 5px;"> 223 // <div style="float: left; background-color: green; height: 40px; width: 100px;">T</div> 224 // <div style="float: left; background-color: yellow; height: 20px; width: 50px;">T</div> 225 // <div style="float: left; background-color: cyan; height: 20px; width: 50px;">T</div> 226 // in this case the third float will be rendered directly under the second, so only the 227 // second float should be cleared 228 229 $clear = 0; 230 231 $floats =& $this->current_floats(); 232 233 // Prepare information about the float bottom coordinates 234 $float_bottoms = array(); 235 $size = count($floats); 236 for ($i=0; $i<$size; $i++) { 237 $float_bottoms[] = $floats[$i]->get_bottom_margin(); 238 }; 239 240 // Note that the sort function SHOULD NOT maintain key-value assotiations! 241 rsort($float_bottoms); 242 243 do { 244 $x = $this->float_right_x($parent->get_right(), $y); 245 246 // Check if current float will fit into the parent box 247 // OR if the parent box have width: auto (it will expanded in this case anyway) 248 // 249 if ($parent->get_right() + EPSILON > $x || 250 $parent->width == WIDTH_AUTO) { 251 252 // Will fit; 253 // Check if current float will intersect the existing left-floating box 254 // 255 $x1 = $this->float_left_x($parent->get_left(), $y); 256 if ($x1 - EPSILON < $x - $width) { 257 return; 258 }; 259 }; 260 261 262 // No, float does not fit at current level, let's try to 'clear' some previous floats 263 $clear++; 264 265 // Check if we've cleared all existing floats; the loop will be terminated in this case, of course, 266 // but we can get a notice/warning message if we'll try to access the non-existing array element 267 if ($clear <= count($floats)) { $y = min( $y, $float_bottoms[$clear-1] ); }; 268 269 } while($clear <= count($floats)); // We need to check if all floats have been cleared to avoid infinite loop 270 271 // All floats are cleared; fall back to the rightmost X coordinate 272 $x = $parent->get_right(); 273 } 274 275 function FlowContext() { 276 $this->absolute_positioned = array(); 277 $this->fixed_positioned = array(); 278 279 $this->viewport = array(); 280 $this->_floats = array(array()); 281 $this->collapsed_margins = array(0); 282 $this->container_uid = array(1); 283 } 284 285 function get_collapsed_margin() { 286 return $this->collapsed_margins[0]; 287 } 288 289 function &get_viewport() { 290 return $this->viewport[0]; 291 } 292 293 function pop() { 294 $this->pop_collapsed_margin(); 295 $this->pop_floats(); 296 } 297 298 function pop_collapsed_margin() { 299 array_shift($this->collapsed_margins); 300 } 301 302 function pop_container_uid() { 303 array_shift($this->container_uid); 304 } 305 306 function pop_floats() { 307 array_shift($this->_floats); 308 } 309 310 function push() { 311 $this->push_collapsed_margin(0); 312 $this->push_floats(); 313 } 314 315 function push_collapsed_margin($margin) { 316 array_unshift($this->collapsed_margins, $margin); 317 } 318 319 function push_container_uid($uid) { 320 array_unshift($this->container_uid, $uid); 321 } 322 323 function push_floats() { 324 array_unshift($this->_floats, array()); 325 } 326 327 function push_viewport(&$box) { 328 array_unshift($this->viewport, $box); 329 } 330 331 function &point_in_floats($x, $y) { 332 // Scan the floating children list of the current container box 333 $floats =& $this->current_floats(); 334 $size = count($floats); 335 for ($i=0; $i<$size; $i++) { 336 if ($floats[$i]->contains_point_margin($x, $y)) { 337 return $floats[$i]; 338 } 339 } 340 341 $dummy = null; 342 return $dummy; 343 } 344 345 function pop_viewport() { 346 array_shift($this->viewport); 347 } 348 349 function sort_absolute_positioned_by_z_index() { 350 usort($this->absolute_positioned, "cmp_boxes_by_z_index"); 351 } 352} 353 354function cmp_boxes_by_z_index($a, $b) { 355 $a_z = $a->get_css_property(CSS_Z_INDEX); 356 $b_z = $b->get_css_property(CSS_Z_INDEX); 357 358 if ($a_z == $b_z) return 0; 359 return ($a_z < $b_z) ? -1 : 1; 360} 361?>