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