1 | 1 | simandl | <?php |
2 | | | // Copyright Howard Jones, 2005 howie@thingy.com |
3 | | | // http://wotsit.thingy.com/haj/cacti/ |
4 | | | // Released under the GNU Public License |
5 | | | |
6 | | | // A simple port of the guts of Apache's mod_imap |
7 | | | // - if you have an image control in a form, it's not really defined what happens to USEMAP |
8 | | | // attributes. They are allowed in HTML 4.0 and XHTML, but some testing shows that they're |
9 | | | // basically ignored. So you need to use server-side imagemaps if you want to have a form |
10 | | | // where you are choosing a verb from (for example) a <SELECT> and also specifying part of |
11 | | | // an image with an IMAGE control. |
12 | | | // |
13 | | | // |
14 | | | class HTML_ImageMap_Area |
15 | | | { |
16 | | | var $href; |
17 | | | var $name; |
18 | | | var $id; |
19 | | | var $alt; |
20 | 85 | simandl | var $z; |
21 | 1 | simandl | var $extrahtml; |
22 | | | |
23 | | | function common_html() |
24 | | | { |
25 | | | $h = ""; |
26 | | | if($this->name != "") |
27 | | | { |
28 | 85 | simandl | // $h .= " alt=\"".$this->name."\" "; |
29 | 1 | simandl | $h .= " id=\"".$this->name."\" "; |
30 | | | } |
31 | | | if($this->href != "") |
32 | | | { |
33 | | | $h .= " href=\"".$this->href."\" "; |
34 | | | } |
35 | 85 | simandl | else { $h .= " nohref "; } |
36 | 1 | simandl | if($this->extrahtml != "") |
37 | | | { |
38 | | | $h .= " ".$this->extrahtml." "; |
39 | | | } |
40 | | | return $h; |
41 | | | } |
42 | | | |
43 | | | } |
44 | | | |
45 | | | class HTML_ImageMap_Area_Polygon extends HTML_ImageMap_Area |
46 | | | { |
47 | | | var $points = array(); |
48 | | | var $minx,$maxx,$miny,$maxy; // bounding box |
49 | | | var $npoints; |
50 | | | |
51 | | | function asHTML() |
52 | | | { |
53 | | | foreach ($this->points as $point) |
54 | | | { |
55 | | | $flatpoints[] = $point[0]; |
56 | | | $flatpoints[] = $point[1]; |
57 | | | } |
58 | | | $coordstring = join(",",$flatpoints); |
59 | | | |
60 | | | return '<area'.$this->common_html().' shape="poly" coords="'.$coordstring.'" />'; |
61 | | | } |
62 | | | |
63 | | | function asJSON() |
64 | | | { |
65 | 85 | simandl | $json = "{ \"shape\":'poly', \"npoints\":".$this->npoints.", \"name\":'".$this->name."',"; |
66 | 1 | simandl | |
67 | | | $xlist = ''; |
68 | | | $ylist = ''; |
69 | | | foreach ($this->points as $point) |
70 | | | { |
71 | | | $xlist .= $point[0].","; |
72 | | | $ylist .= $point[1].","; |
73 | | | } |
74 | | | $xlist = rtrim($xlist,", "); |
75 | | | $ylist = rtrim($ylist,", "); |
76 | 85 | simandl | $json .= " \"x\": [ $xlist ], \"y\":[ $ylist ], \"minx\": ".$this->minx.", \"miny\": ".$this->miny.", \"maxx\":".$this->maxx.", \"maxy\":".$this->maxy."}"; |
77 | 1 | simandl | |
78 | | | return($json); |
79 | | | } |
80 | | | |
81 | | | function hitTest($x,$y) |
82 | | | { |
83 | | | $c = 0; |
84 | | | // do the easy bounding-box test first. |
85 | | | if( ($x < $this->minx) || ($x>$this->maxx) || ($y<$this->miny) || ($y>$this->maxy)) |
86 | | | { |
87 | | | return false; |
88 | | | } |
89 | | | |
90 | | | // Algotithm from from |
91 | | | // http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html#The%20C%20Code |
92 | | | for ($i = 0, $j = $this->npoints-1; $i < $this->npoints; $j = $i++) |
93 | | | { |
94 | | | // print "Checking: $i, $j\n"; |
95 | | | $x1 = $this->points[$i][0]; |
96 | | | $y1 = $this->points[$i][1]; |
97 | | | $x2 = $this->points[$j][0]; |
98 | | | $y2 = $this->points[$j][1]; |
99 | | | |
100 | | | // print "($x,$y) vs ($x1,$y1)-($x2,$y2)\n"; |
101 | | | |
102 | | | if (((($y1<=$y) && ($y<$y2)) || (($y2<=$y) && ($y<$y1))) && |
103 | | | ($x < ($x2 - $x1) * ($y - $y1) / ($y2 - $y1) + $x1)) |
104 | | | { |
105 | | | $c = !$c; |
106 | | | } |
107 | | | } |
108 | | | |
109 | | | return ($c); |
110 | | | } |
111 | | | |
112 | | | function HTML_ImageMap_Area_Polygon ( $name="", $href="",$coords) |
113 | | | { |
114 | | | $c = $coords[0]; |
115 | | | |
116 | | | $this->name = $name; |
117 | | | $this->href= $href; |
118 | | | $this->npoints = count($c)/2; |
119 | | | |
120 | | | if( intval($this->npoints) != ($this->npoints)) |
121 | | | { |
122 | | | die("Odd number of points!"); |
123 | | | } |
124 | | | |
125 | | | for ($i=0; $i<count($c); $i+=2) |
126 | | | { |
127 | 85 | simandl | $x = round($c[$i]); |
128 | | | $y = round($c[$i+1]); |
129 | 1 | simandl | $point = array($x,$y); |
130 | | | $xlist[] = $x; // these two are used to get the bounding box in a moment |
131 | | | $ylist[] = $y; |
132 | | | $this->points[] = $point; |
133 | | | } |
134 | | | |
135 | | | $this->minx = min($xlist); |
136 | | | $this->maxx = max($xlist); |
137 | | | $this->miny = min($ylist); |
138 | | | $this->maxy = max($ylist); |
139 | | | |
140 | | | // print $this->asHTML()."\n"; |
141 | | | } |
142 | | | |
143 | | | } |
144 | | | |
145 | | | class HTML_ImageMap_Area_Rectangle extends HTML_ImageMap_Area |
146 | | | { |
147 | | | var $x1,$x2,$y1,$y2; |
148 | | | |
149 | | | function HTML_ImageMap_Area_Rectangle ( $name="", $href="",$coords) |
150 | | | { |
151 | | | |
152 | | | $c = $coords[0]; |
153 | | | |
154 | 85 | simandl | $x1 = round($c[0]); |
155 | | | $y1 = round($c[1]); |
156 | | | $x2 = round($c[2]); |
157 | | | $y2 = round($c[3]); |
158 | 1 | simandl | |
159 | | | // sort the points, so that the first is the top-left |
160 | | | if($x1>$x2) |
161 | | | { |
162 | | | $this->x1=$x2; |
163 | | | $this->x2=$x1; |
164 | | | } |
165 | | | else |
166 | | | { |
167 | | | $this->x1=$x1; |
168 | | | $this->x2=$x2; |
169 | | | } |
170 | | | |
171 | | | if($y1>$y2) |
172 | | | { |
173 | | | $this->y1=$y2; |
174 | | | $this->y2=$y1; |
175 | | | } |
176 | | | else |
177 | | | { |
178 | | | $this->y1=$y1; |
179 | | | $this->y2=$y2; |
180 | | | } |
181 | | | |
182 | | | $this->name = $name; |
183 | | | $this->href = $href; |
184 | | | } |
185 | | | |
186 | | | function hitTest($x,$y) |
187 | | | { |
188 | | | return ( ($x > $this->x1) && ($x < $this->x2) && ($y > $this->y1) && ($y < $this->y2) ); |
189 | | | } |
190 | | | |
191 | | | function asHTML() |
192 | | | { |
193 | | | $coordstring = join(",",array($this->x1,$this->y1,$this->x2,$this->y2)); |
194 | | | return '<area'.$this->common_html().' shape="rect" coords="'.$coordstring.'" />'; |
195 | | | |
196 | | | } |
197 | | | |
198 | | | function asJSON() |
199 | | | { |
200 | 85 | simandl | $json = "{ \"shape\":'rect', "; |
201 | 1 | simandl | |
202 | 85 | simandl | $json .= " \"x1\":".$this->x1.", \"y1\":".$this->y1.", \"x2\":".$this->x2.", \"y2\":".$this->y2.", \"name\":'".$this->name."'}"; |
203 | 1 | simandl | |
204 | | | return($json); |
205 | | | } |
206 | | | |
207 | | | } |
208 | | | |
209 | | | class HTML_ImageMap_Area_Circle extends HTML_ImageMap_Area |
210 | | | { |
211 | | | var $centx,$centy, $edgex, $edgey; |
212 | | | |
213 | | | function asHTML() |
214 | | | { |
215 | | | $coordstring = join(",",array($this->centx,$this->centy,$this->edgex,$this->edgey) ); |
216 | | | return '<area'.$this->common_html().' shape="circle" coords="'.$coordstring.'" />'; |
217 | | | } |
218 | | | |
219 | | | function hitTest($x,$y) |
220 | | | { |
221 | | | $radius1 = ($this->edgey - $this->centy) * ($this->edgey - $this->centy) |
222 | | | + ($this->edgex - $this->centx) * ($this->edgex - $this->centx); |
223 | | | |
224 | | | $radius2 = ($this->edgey - $y) * ($this->edgey - $y) |
225 | | | + ($this->edgex - $x) * ($this->edgex - $x); |
226 | | | |
227 | | | return ($radius2 <= $radius1); |
228 | | | } |
229 | | | |
230 | | | function HTML_ImageMap_Area_Circle($name="", $href="",$coords) |
231 | | | { |
232 | | | $c = $coords[0]; |
233 | | | |
234 | | | $this->name = $name; |
235 | | | $this->href = $href; |
236 | 85 | simandl | $this->centx = round($c[0]); |
237 | | | $this->centy = round($c[1]); |
238 | | | $this->edgex = round($c[2]); |
239 | | | $this->edgey = round($c[3]); |
240 | 1 | simandl | } |
241 | | | } |
242 | | | |
243 | | | class HTML_ImageMap |
244 | | | { |
245 | | | var $shapes; |
246 | | | var $nshapes; |
247 | | | var $name; |
248 | | | |
249 | | | function HTML_ImageMap($name="") |
250 | | | { |
251 | | | $this->Reset(); |
252 | | | $this->name = $name; |
253 | | | } |
254 | | | |
255 | | | function Reset() |
256 | | | { |
257 | | | $this->shapes = array(); |
258 | | | $this->nshapes = 0; |
259 | | | $this->name = ""; |
260 | | | } |
261 | | | |
262 | | | // add an element to the map - takes an array with the info, in a similar way to HTML_QuickForm |
263 | | | function addArea($element) |
264 | | | { |
265 | | | if (is_object($element) && is_subclass_of($element, 'html_imagemap_area')) { |
266 | | | $elementObject = &$element; |
267 | | | } else { |
268 | | | $args = func_get_args(); |
269 | | | $className = "HTML_ImageMap_Area_".$element; |
270 | 85 | simandl | $elementObject = new $className($args[1],$args[2],array_slice($args, 3)); |
271 | 1 | simandl | } |
272 | | | |
273 | | | $this->shapes[] =& $elementObject; |
274 | | | $this->nshapes++; |
275 | | | // print $this->nshapes." shapes\n"; |
276 | | | } |
277 | | | |
278 | | | // do a hit-test based on the current map |
279 | | | // - can be limited to only match elements whose names match the filter |
280 | | | // (e.g. pick a building, in a campus map) |
281 | | | function hitTest($x,$y,$namefilter="") |
282 | | | { |
283 | | | $preg = '/'.$namefilter.'/'; |
284 | | | foreach ($this->shapes as $shape) |
285 | | | { |
286 | | | if($shape->hitTest($x,$y)) |
287 | | | { |
288 | | | if( ($namefilter == "") || ( preg_match($preg,$shape->name) ) ) |
289 | | | { |
290 | | | return $shape->name; |
291 | | | } |
292 | | | } |
293 | | | } |
294 | | | return false; |
295 | | | } |
296 | | | |
297 | | | // update a property on all elements in the map that match a name |
298 | | | // (use it for retro-actively adding in link information to a pre-built geometry before generating HTML) |
299 | | | // returns the number of elements that were matched/changed |
300 | | | function setProp($which, $what, $where) |
301 | | | { |
302 | | | $count = 0; |
303 | | | for($i=0; $i<count($this->shapes); $i++) |
304 | | | { |
305 | | | // this USED to be a substring match, but that broke some things |
306 | | | // and wasn't actually used as one anywhere. |
307 | | | if( ($where == "") || ( $this->shapes[$i]->name==$where) ) |
308 | | | { |
309 | | | switch($which) |
310 | | | { |
311 | | | case 'href': |
312 | | | $this->shapes[$i]->href= $what; |
313 | | | break; |
314 | | | case 'extrahtml': |
315 | | | $this->shapes[$i]->extrahtml= $what; |
316 | 85 | simandl | #print "IMAGEMAP: Found $where and adding $which\n"; |
317 | 1 | simandl | break; |
318 | | | } |
319 | | | $count++; |
320 | | | } |
321 | | | } |
322 | | | return $count; |
323 | | | } |
324 | | | |
325 | 85 | simandl | // update a property on all elements in the map that match a name as a substring |
326 | | | // (use it for retro-actively adding in link information to a pre-built geometry before generating HTML) |
327 | | | // returns the number of elements that were matched/changed |
328 | | | function setPropSub($which, $what, $where) |
329 | | | { |
330 | | | |
331 | | | $count = 0; |
332 | | | for($i=0; $i<count($this->shapes); $i++) |
333 | | | { |
334 | | | if( ($where == "") || ( strstr($this->shapes[$i]->name,$where)!=FALSE ) ) |
335 | | | { |
336 | | | switch($which) |
337 | | | { |
338 | | | case 'href': |
339 | | | $this->shapes[$i]->href= $what; |
340 | | | break; |
341 | | | case 'extrahtml': |
342 | | | $this->shapes[$i]->extrahtml= $what; |
343 | | | break; |
344 | | | } |
345 | | | $count++; |
346 | | | } |
347 | | | } |
348 | | | return $count; |
349 | | | } |
350 | | | |
351 | 1 | simandl | // Return the imagemap as an HTML client-side imagemap for inclusion in a page |
352 | | | function asHTML() |
353 | | | { |
354 | | | $html = '<map'; |
355 | | | if($this->name != "") |
356 | | | { |
357 | | | $html .= ' name="'.$this->name.'"'; |
358 | | | } |
359 | | | $html .=">\n"; |
360 | | | foreach ($this->shapes as $shape) |
361 | | | { |
362 | 85 | simandl | $html .= $shape->asHTML()."\n"; |
363 | 1 | simandl | $html .= "\n"; |
364 | | | } |
365 | | | $html .= "</map>\n"; |
366 | | | |
367 | | | return $html; |
368 | | | } |
369 | | | |
370 | | | function subJSON($namefilter="",$reverseorder=false) |
371 | | | { |
372 | | | $json = ''; |
373 | | | |
374 | | | $preg = '/'.$namefilter.'/'; |
375 | | | foreach ($this->shapes as $shape) |
376 | | | { |
377 | | | if( ($namefilter == "") || ( preg_match($preg,$shape->name) )) |
378 | | | { |
379 | | | if($reverseorder) |
380 | | | { |
381 | | | $json = $shape->asJSON().",\n".$json; |
382 | | | } |
383 | | | else |
384 | | | { |
385 | | | $json .= $shape->asJSON().",\n"; |
386 | | | } |
387 | | | } |
388 | | | } |
389 | | | $json = rtrim($json,"\n, "); |
390 | | | $json .= "\n"; |
391 | | | |
392 | | | return $json; |
393 | | | } |
394 | | | |
395 | | | // return HTML for a subset of the map, specified by the filter string |
396 | | | // (suppose you want some partof your UI to have precedence over another part |
397 | | | // - the imagemap is checked from top-to-bottom in the HTML) |
398 | 85 | simandl | // - skipnolinks -> in normal HTML output, we don't need areas for things with no href |
399 | | | function subHTML($namefilter="",$reverseorder=false, $skipnolinks=false) |
400 | 1 | simandl | { |
401 | | | $html = ""; |
402 | | | $preg = '/'.$namefilter.'/'; |
403 | 85 | simandl | |
404 | 1 | simandl | foreach ($this->shapes as $shape) |
405 | | | { |
406 | 85 | simandl | # if( ($namefilter == "") || ( preg_match($preg,$shape->name) )) |
407 | | | if( ($namefilter == "") || ( strstr($shape->name, $namefilter) !== FALSE )) |
408 | 1 | simandl | { |
409 | 85 | simandl | if(!$skipnolinks || $shape->href != "" || $shape->extrahtml != "" ) |
410 | 1 | simandl | { |
411 | 85 | simandl | if($reverseorder) |
412 | | | { |
413 | | | $html = $shape->asHTML()."\n".$html; |
414 | | | } |
415 | | | else |
416 | | | { |
417 | | | $html .= $shape->asHTML()."\n"; |
418 | | | } |
419 | 1 | simandl | } |
420 | | | |
421 | | | } |
422 | | | } |
423 | | | return $html; |
424 | | | } |
425 | | | |
426 | | | } |
427 | | | // vim:ts=4:sw=4: |
428 | | | ?> |