Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
class 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 &current_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
 
354
function 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
?>