Rev 6 | Rev 13 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 2 | PointedEar | 1 | <?php | 
        
| 2 | |||
| 3 | require_once 'global.inc';  | 
        ||
| 4 | |||
| 5 | /** | 
        ||
| 6 |  * A list of language features with URNs definitions | 
        ||
| 7 |  * for reference links. | 
        ||
| 8 |  */ | 
        ||
| 9 | class FeatureList | 
        ||
| 10 | { | 
        ||
| 11 | public $versions = array();  | 
        ||
| 12 | |||
| 13 |   /** | 
        ||
| 14 |    * Versions of implementations that are considered safe. | 
        ||
| 15 |    * A feature is considered safe if it does not require | 
        ||
| 16 |    * an implementation version above these versions. | 
        ||
| 17 |    * | 
        ||
| 18 |    * @var Array[string=>string] | 
        ||
| 19 |    */ | 
        ||
| 20 | public $safeVersions = array();  | 
        ||
| 21 | |||
| 22 |   /** | 
        ||
| 23 |    * URNs that can be used for reference links. | 
        ||
| 24 |    * | 
        ||
| 25 |    * @var Array[string=>string] | 
        ||
| 26 |    */ | 
        ||
| 27 | protected $urns = array();  | 
        ||
| 28 | |||
| 29 |   /** | 
        ||
| 30 |    * The list of language features | 
        ||
| 31 |    * | 
        ||
| 32 |    * @var array[Features] | 
        ||
| 33 |    */ | 
        ||
| 34 | protected $items = array();  | 
        ||
| 35 | |||
| 36 |   /** | 
        ||
| 37 |    * Determines the number of printed items the table headings should be repeated | 
        ||
| 38 |    * | 
        ||
| 39 |    * @var int | 
        ||
| 40 |    */ | 
        ||
| 41 | protected $headerRepeat = 25;  | 
        ||
| 42 | |||
| 43 |   /** | 
        ||
| 44 |    * Initializes the FeatureList object | 
        ||
| 45 |    * | 
        ||
| 46 |    * @param array|Object $a | 
        ||
| 47 |    * @return FeatureList | 
        ||
| 48 |    */ | 
        ||
| 49 | public function __construct($a)  | 
        ||
| 50 |   { | 
        ||
| 9 | PointedEar | 51 | $aVars = get_class_vars(get_class($this));  | 
        
| 52 | |||
| 2 | PointedEar | 53 | while ((list($key, $value) = each($aVars)))  | 
        
| 54 |     { | 
        ||
| 55 | if (isset($a[$key]))  | 
        ||
| 56 |       { | 
        ||
| 57 | $this->$key = $a[$key];  | 
        ||
| 58 |       } | 
        ||
| 59 |     } | 
        ||
| 60 | |||
| 61 |     /* Inform items of ourself so that URNs can be used for links */ | 
        ||
| 62 | if (is_array($this->items))  | 
        ||
| 63 |     { | 
        ||
| 64 | foreach ($this->items as &$item)  | 
        ||
| 65 |       { | 
        ||
| 4 | PointedEar | 66 | $item->setList($this);  | 
        
| 2 | PointedEar | 67 |       } | 
        
| 68 |     } | 
        ||
| 69 | |||
| 70 |     /* resolve URN references that are URNs */ | 
        ||
| 71 | if (is_array($this->urns))  | 
        ||
| 72 |     { | 
        ||
| 73 | foreach ($this->urns as &$urn)  | 
        ||
| 74 |       { | 
        ||
| 75 | if (($url = $this->resolveURN($urn)))  | 
        ||
| 76 |         { | 
        ||
| 77 | $urn = $url;  | 
        ||
| 78 |         } | 
        ||
| 79 |       } | 
        ||
| 80 |     } | 
        ||
| 81 |   } | 
        ||
| 82 | |||
| 83 | public function printHeaders()  | 
        ||
| 84 |   { | 
        ||
| 85 | foreach ($this->versions as $ver)  | 
        ||
| 86 |     { | 
        ||
| 87 | ?> | 
        ||
| 88 | <th><?php echo $ver; ?></th>  | 
        ||
| 89 | <?php | 
        ||
| 90 |     } | 
        ||
| 91 |   } | 
        ||
| 92 | |||
| 93 |   /** | 
        ||
| 94 |    * Prints the list of features. | 
        ||
| 95 |    * | 
        ||
| 96 |    * @see Feature::printMe() | 
        ||
| 97 |    */ | 
        ||
| 98 | public function printItems()  | 
        ||
| 99 |   { | 
        ||
| 100 | $counter = 0;  | 
        ||
| 101 | $headerRepeat = $this->headerRepeat;  | 
        ||
| 102 | $repeatHeaders = ($headerRepeat > 1);  | 
        ||
| 103 | |||
| 104 | foreach ($this->items as $feature)  | 
        ||
| 105 |     { | 
        ||
| 106 | if ($feature instanceof Feature)  | 
        ||
| 107 |       { | 
        ||
| 108 | if ($repeatHeaders  | 
        ||
| 109 | && $counter > 1  | 
        ||
| 110 | && $counter % $headerRepeat === 0)  | 
        ||
| 111 |         { | 
        ||
| 112 | ?> | 
        ||
| 113 | <tr class="header">  | 
        ||
| 114 | <th>Feature</th>  | 
        ||
| 115 | <?php $this->printHeaders(); ?>  | 
        ||
| 116 | </tr>  | 
        ||
| 117 | <?php | 
        ||
| 118 |         } | 
        ||
| 119 | |||
| 120 | $feature->printMe();  | 
        ||
| 121 | |||
| 122 | $counter++;  | 
        ||
| 123 |       } | 
        ||
| 124 |     } | 
        ||
| 125 |   } | 
        ||
| 126 | |||
| 127 |   /** | 
        ||
| 128 |    * Resolves a URN according to the value of the | 
        ||
| 129 |    * object's <code>$urn</code> property. | 
        ||
| 130 |    * | 
        ||
| 131 |    * @param string $urn | 
        ||
| 132 |    *   URN to be resolved | 
        ||
| 133 |    * @return string|boolean | 
        ||
| 134 |    *   The resolved URN if successful, | 
        ||
| 135 |    *   <code>false</code> otherwise. | 
        ||
| 136 |    */ | 
        ||
| 137 | public function resolveURN($urn)  | 
        ||
| 138 |   { | 
        ||
| 139 | if (is_array($this->urns))  | 
        ||
| 140 |     { | 
        ||
| 141 | $reURN = '|^(.+?):(?!//)|';  | 
        ||
| 142 | |||
| 143 | if (preg_match($reURN, $urn, $m) && isset($this->urns[$m[1]]))  | 
        ||
| 144 |       { | 
        ||
| 145 | return preg_replace($reURN, $this->urns[$m[1]], $urn);  | 
        ||
| 146 |       } | 
        ||
| 147 |     } | 
        ||
| 148 | |||
| 149 | return $urn;  | 
        ||
| 150 |   } | 
        ||
| 151 | } | 
        ||
| 152 | |||
| 153 | /** | 
        ||
| 154 |  * A language feature. | 
        ||
| 155 |  */ | 
        ||
| 156 | class Feature | 
        ||
| 157 | { | 
        ||
| 158 |   /** | 
        ||
| 159 |    * Fragment identifiers to be defined for quickly accessing | 
        ||
| 160 |    * the feature description. | 
        ||
| 161 |    * | 
        ||
| 162 |    * @var Array[String] | 
        ||
| 163 |    */ | 
        ||
| 164 | protected $anchors = array();  | 
        ||
| 165 | |||
| 166 |   /** | 
        ||
| 167 |    * Value of the explanatory <code>title</code> attribute for the feature. | 
        ||
| 168 |    * | 
        ||
| 169 |    * @var string | 
        ||
| 170 |    */ | 
        ||
| 171 | protected $title = '';  | 
        ||
| 172 | |||
| 173 |   /** | 
        ||
| 174 |    * Name or example code of the feature | 
        ||
| 175 |    * | 
        ||
| 176 |    * @var string | 
        ||
| 177 |    */ | 
        ||
| 178 | protected $content = '';  | 
        ||
| 179 | |||
| 180 |   /** | 
        ||
| 181 |    * Description of the feature.  Displayed directly if code is missing, | 
        ||
| 182 |    * otherwise used as `title' attribute value. | 
        ||
| 183 |    * | 
        ||
| 184 |    * @var string | 
        ||
| 185 |    */ | 
        ||
| 186 | protected $descr = '';  | 
        ||
| 187 | |||
| 188 |   /** | 
        ||
| 189 |    * Versions that support this feature | 
        ||
| 190 |    * | 
        ||
| 191 |    * @var Array | 
        ||
| 192 |    */ | 
        ||
| 193 | protected $versions = array();  | 
        ||
| 194 | |||
| 195 |   /** | 
        ||
| 196 |    * Reference to the FeatureList that this feature belongs to | 
        ||
| 197 |    * | 
        ||
| 198 |    * @var FeatureList | 
        ||
| 199 |    */ | 
        ||
| 200 | protected $list = null;  | 
        ||
| 201 | |||
| 4 | PointedEar | 202 | public function setList(&$oList)  | 
        
| 2 | PointedEar | 203 |   { | 
        
| 204 | $this->list =& $oList;  | 
        ||
| 205 |   } | 
        ||
| 206 | |||
| 207 |   /** | 
        ||
| 208 |    * Creates a new Feature object, using values from the passed parameters | 
        ||
| 209 |    * array. | 
        ||
| 210 |    * | 
        ||
| 211 |    * @param array|Object $params | 
        ||
| 212 |    * @return Feature | 
        ||
| 213 |    */ | 
        ||
| 214 | public function __construct($params = array())  | 
        ||
| 215 |   { | 
        ||
| 9 | PointedEar | 216 | $aVars = get_class_vars(get_class($this));  | 
        
| 217 | |||
| 218 | while ((list($key, $value) = each($aVars)))  | 
        ||
| 2 | PointedEar | 219 |     { | 
        
| 9 | PointedEar | 220 | if (isset($params[$key]))  | 
        
| 221 |       { | 
        ||
| 222 | $this->$key = $params[$key];  | 
        ||
| 223 |       } | 
        ||
| 2 | PointedEar | 224 |     } | 
        
| 225 |   } | 
        ||
| 226 | |||
| 227 |   /** | 
        ||
| 228 |    * Returns <code>' class="safe"'</code> if the feature | 
        ||
| 229 |    * can be considered safe.  The required information | 
        ||
| 230 |    * is stored in the <code>safeVersions</code> property | 
        ||
| 231 |    * of the associated <code>FeatureList</code> object. | 
        ||
| 232 |    * | 
        ||
| 233 |    * @return string | 
        ||
| 234 |    * @see FeatureList::defaultSafeVersions | 
        ||
| 235 |    */ | 
        ||
| 236 | protected function getSafeStr()  | 
        ||
| 237 |   { | 
        ||
| 238 | if (!is_null($this->list))  | 
        ||
| 239 |     { | 
        ||
| 240 | foreach ($this->list->safeVersions as $impl => &$safeVer)  | 
        ||
| 241 |       { | 
        ||
| 242 | $thisImplVer =& $this->versions[$impl];  | 
        ||
| 243 | if (is_array($thisImplVer))  | 
        ||
| 244 |         { | 
        ||
| 245 | if (isset($thisImplVer['tested']) && !is_bool($thisImplVer['tested']))  | 
        ||
| 246 |           { | 
        ||
| 247 | $thisImplVer =& $thisImplVer['tested'];  | 
        ||
| 248 |           } | 
        ||
| 249 |           else | 
        ||
| 250 |         { | 
        ||
| 251 | $thisImplVer =& $thisImplVer[0];  | 
        ||
| 252 |           } | 
        ||
| 253 |         } | 
        ||
| 254 | |||
| 255 |         /* DEBUG */ | 
        ||
| 256 |         // echo " $impl=$thisImplVer "; | 
        ||
| 257 | |||
| 3 | PointedEar | 258 | if (preg_match('/^-?$/', $thisImplVer) || $thisImplVer > $safeVer)  | 
        
| 2 | PointedEar | 259 |         { | 
        
| 260 | return '';  | 
        ||
| 261 |         } | 
        ||
| 262 |       } | 
        ||
| 263 | |||
| 264 | return ' class="safe"';  | 
        ||
| 265 |     } | 
        ||
| 266 |     else | 
        ||
| 267 |     { | 
        ||
| 268 | return '';  | 
        ||
| 269 |     } | 
        ||
| 270 |   } | 
        ||
| 271 | |||
| 272 | protected function getTitleStr()  | 
        ||
| 273 |   { | 
        ||
| 274 | if (!empty($this->title))  | 
        ||
| 275 |     { | 
        ||
| 276 | return " title=\"{$this->title}\"";  | 
        ||
| 277 |     } | 
        ||
| 278 |     else | 
        ||
| 279 |     { | 
        ||
| 280 | return '';  | 
        ||
| 281 |     } | 
        ||
| 282 |   } | 
        ||
| 283 | |||
| 284 | protected function getAnchors()  | 
        ||
| 285 |   { | 
        ||
| 286 | $result = array();  | 
        ||
| 287 | |||
| 288 | foreach ($this->anchors as $anchor)  | 
        ||
| 289 |     { | 
        ||
| 290 | $result[] = "<a name=\"{$anchor}\"";  | 
        ||
| 291 | |||
| 292 | if (preg_match('/^[a-z][a-z0-9_:.-]*/i', $anchor))  | 
        ||
| 293 |       { | 
        ||
| 294 | $result[] = " id=\"{$anchor}\"";  | 
        ||
| 295 |       } | 
        ||
| 296 | |||
| 297 | $result[] = '></a>';  | 
        ||
| 298 |     } | 
        ||
| 299 | |||
| 300 | return join('', $result);  | 
        ||
| 301 |   } | 
        ||
| 302 | |||
| 303 | protected function getAssumed($v)  | 
        ||
| 304 |   { | 
        ||
| 305 | if (is_array($v) && isset($v['assumed']) && $v['assumed'])  | 
        ||
| 306 |     { | 
        ||
| 307 | return ' class="assumed"';  | 
        ||
| 308 |     } | 
        ||
| 309 | |||
| 310 | return '';  | 
        ||
| 311 |   } | 
        ||
| 312 | |||
| 313 | protected function getTested($v)  | 
        ||
| 314 |   { | 
        ||
| 315 | if (is_array($v) && isset($v['tested']) && $v['tested'])  | 
        ||
| 316 |     { | 
        ||
| 317 | return ' class="tested"';  | 
        ||
| 318 |     } | 
        ||
| 319 | |||
| 320 | return '';  | 
        ||
| 321 |   } | 
        ||
| 322 | |||
| 323 |   /** | 
        ||
| 324 |    * Returns the version of a feature. | 
        ||
| 325 |    * | 
        ||
| 326 |    * @param string|VersionInfo $vInfo | 
        ||
| 327 |    * @return mixed | 
        ||
| 328 |    */ | 
        ||
| 329 | protected function getVer($vInfo)  | 
        ||
| 330 |   { | 
        ||
| 331 | if (is_array($vInfo))  | 
        ||
| 332 |     { | 
        ||
| 333 |       /* TODO: Return all versions: documented, assumed, and tested */ | 
        ||
| 334 | $vNumber = (isset($vInfo['tested'])  | 
        ||
| 335 | && gettype($vInfo['tested']) !== 'boolean')  | 
        ||
| 336 | ? $vInfo['tested']  | 
        ||
| 337 | : $vInfo[0];  | 
        ||
| 338 | $section = isset($vInfo['section'])  | 
        ||
| 339 |                    ? ' <span class="section" title="Specification section">[' | 
        ||
| 340 | . $vInfo['section'] . ']</span>'  | 
        ||
| 341 | : '';  | 
        ||
| 342 | |||
| 343 | if (isset($vInfo['urn']))  | 
        ||
| 344 |       { | 
        ||
| 345 | if ($this->list instanceof FeatureList)  | 
        ||
| 346 |         { | 
        ||
| 347 | $url = $this->list->resolveURN($vInfo['urn']);  | 
        ||
| 348 | $vNumber = '<a href="' . $url . '">' . $vNumber  | 
        ||
| 6 | PointedEar | 349 | . ($section ? $section : '') . '</a>';  | 
        
| 2 | PointedEar | 350 |         } | 
        
| 351 |       } | 
        ||
| 352 | else if ($section)  | 
        ||
| 353 |       { | 
        ||
| 354 | $vNumber .= $section;  | 
        ||
| 355 |       } | 
        ||
| 356 | |||
| 6 | PointedEar | 357 | $vInfo = $vNumber;  | 
        
| 2 | PointedEar | 358 |     } | 
        
| 6 | PointedEar | 359 | |
| 360 | return ($vInfo === '-')  | 
        ||
| 361 |       ? '<span title="Not supported">−</span>' | 
        ||
| 362 | : $vInfo;  | 
        ||
| 2 | PointedEar | 363 |   } | 
        
| 364 | |||
| 365 | protected static function shl($s)  | 
        ||
| 366 |   { | 
        ||
| 367 |     /* stub */ | 
        ||
| 368 |   } | 
        ||
| 369 | |||
| 370 | public function printMe()  | 
        ||
| 371 |   { | 
        ||
| 372 |     ?> | 
        ||
| 373 | <tr<?php echo $this->getSafeStr(); ?>>  | 
        ||
| 374 | <th<?php echo $this->getTitleStr(); ?>><?php  | 
        ||
| 375 | echo $this->getAnchors();  | 
        ||
| 376 | echo /*preg_replace_callback(  | 
        ||
| 377 |               '#(<code>)(.+?)(</code>)#', | 
        ||
| 378 |               array('self', 'shl'),*/ | 
        ||
| 379 | preg_replace('/…/', '…', $this->content)/*)*/;  | 
        ||
| 380 |             ?></th> | 
        ||
| 381 | <?php | 
        ||
| 382 | $versions = $this->versions;  | 
        ||
| 383 | if (!is_null($this->list))  | 
        ||
| 384 |     { | 
        ||
| 385 | $versions =& $this->list->versions;  | 
        ||
| 386 |     } | 
        ||
| 387 | |||
| 388 | static $row = 0;  | 
        ||
| 389 | $row++;  | 
        ||
| 390 | |||
| 391 | $column = 0;  | 
        ||
| 392 | |||
| 393 | foreach ($versions as $key => $value)  | 
        ||
| 394 |     { | 
        ||
| 395 | $column++;  | 
        ||
| 396 | $id = "td$row-$column";  | 
        ||
| 397 | $ver = $this->versions[$key];  | 
        ||
| 398 | ?> | 
        ||
| 399 | <td id="<?php echo $id; ?>"<?php  | 
        ||
| 400 | echo $this->getAssumed($ver) . $this->getTested($ver);  | 
        ||
| 401 | if (!$key)  | 
        ||
| 402 |             { | 
        ||
| 403 | if (!empty($ver))  | 
        ||
| 404 |               { | 
        ||
| 405 | echo ' title="Test code: ' . htmlspecialchars(stripslashes($ver)) . '"';  | 
        ||
| 406 |               } | 
        ||
| 407 |               else | 
        ||
| 408 |               { | 
        ||
| 409 | echo ' title="Not applicable: No automated test case'  | 
        ||
| 410 | . ' is available for this feature. Please click'  | 
        ||
| 411 | . ' the feature code in the first column to run'  | 
        ||
| 412 | . ' a manual test."';  | 
        ||
| 413 |               } | 
        ||
| 414 |             } | 
        ||
| 415 | ?>><?php  | 
        ||
| 416 | if ($key)  | 
        ||
| 417 |             { | 
        ||
| 418 | echo $this->getVer($ver);  | 
        ||
| 419 |             } | 
        ||
| 420 |             else | 
        ||
| 421 |             { | 
        ||
| 422 | if (!empty($ver))  | 
        ||
| 423 |               { | 
        ||
| 424 |                 ?><script type="text/javascript"> | 
        ||
| 425 | // <![CDATA[  | 
        ||
| 5 | PointedEar | 426 | var s = test(<?php echo $ver; ?>, '<span title="Supported">+</span>',  | 
        
| 427 | '<span title="Not supported">−</span>');  | 
        ||
| 2 | PointedEar | 428 |   tryThis("document.write(s);", | 
        
| 429 |           "document.getElementById('<?php echo $id; ?>').appendChild(" | 
        ||
| 430 | + "document.createTextNode(s));");  | 
        ||
| 431 | // ]]>  | 
        ||
| 432 | </script><?php | 
        ||
| 433 |               } | 
        ||
| 434 |               else | 
        ||
| 435 |               { | 
        ||
| 436 | echo '<abbr>N/A</abbr>';  | 
        ||
| 437 |               } | 
        ||
| 438 |             } | 
        ||
| 439 |             ?></td> | 
        ||
| 440 | <?php | 
        ||
| 441 |     } | 
        ||
| 442 | ?> | 
        ||
| 443 | </tr>  | 
        ||
| 444 | <?php | 
        ||
| 445 |   } | 
        ||
| 446 | } | 
        ||
| 447 | |||
| 448 | ?> |