<?php
require_once 'global.inc';
define('FEATURES_ENCODING',
mb_detect_encoding(file_get_contents($_SERVER['SCRIPT_FILENAME'])));
/**
* A list of language features with URNs definitions
* for reference links.
*/
class FeatureList
{
public $versions = array();
/**
* Versions of implementations that are considered safe.
* A feature is considered safe if it does not require
* an implementation version above these versions.
*
* @var Array[string=>string]
*/
public $safeVersions = array();
/**
* URNs that can be used for reference links.
*
* @var Array[string=>string]
*/
protected $urns = array();
/**
* The list of language features
*
* @var array[Features]
*/
protected $items = array();
/**
* Determines the number of printed items the table headings should be repeated
*
* @var int
*/
protected $headerRepeat = 25;
/**
* Initializes the FeatureList object
*
* @param array|Object $a
* @return FeatureList
*/
public function __construct
($a)
{
$aVars = get_class_vars(get_class($this));
while ((list($key, $value) = each($aVars)))
{
if (isset($a[$key]))
{
$this->$key = $a[$key];
}
}
/* Inform items of ourself so that URNs can be used for links */
if (is_array($this->items))
{
foreach ($this->items as &$item)
{
$item->setList($this);
}
}
/* resolve URN references that are URNs */
if (is_array($this->urns))
{
foreach ($this->urns as &$urn)
{
if (($url = $this->resolveURN($urn)))
{
$urn = $url;
}
}
}
}
public function printHeaders
()
{
foreach ($this->versions as $ver)
{
?>
<th>
<?php echo $ver; ?></th>
<?php
}
}
/**
* Prints the list of features.
*
* @see Feature::printMe()
*/
public function printItems
()
{
$counter = 0;
$headerRepeat = $this->headerRepeat;
$repeatHeaders = ($headerRepeat > 1);
foreach ($this->items as $feature)
{
if ($feature instanceof Feature
)
{
if ($repeatHeaders
&& $counter > 1
&& $counter % $headerRepeat === 0)
{
?>
<tr class="header">
<th>Feature</th>
<?php $this->printHeaders(); ?>
</tr>
<?php
}
$feature->printMe();
$counter++;
}
}
}
/**
* Resolves a URN according to the value of the
* object's <code>$urn</code> property.
*
* @param string $urn
* URN to be resolved
* @return string|boolean
* The resolved URN if successful,
* <code>false</code> otherwise.
*/
public function resolveURN
($urn)
{
if (is_array($this->urns))
{
$reURN = '|^(.+?):(?!//)|';
if (preg_match($reURN, $urn, $m) && isset($this->urns[$m[1]]))
{
return preg_replace($reURN, $this->urns[$m[1]], $urn);
}
}
return $urn;
}
}
/**
* A language feature.
*/
class Feature
{
/**
* Fragment identifiers to be defined for quickly accessing
* the feature description.
*
* @var Array[String]
*/
protected $anchors = array();
/**
* Value of the explanatory <code>title</code> attribute for the feature.
*
* @var string
*/
protected $title = '';
/**
* Name or example code of the feature
*
* @var string
*/
protected $content = '';
/**
* Description of the feature. Displayed directly if code is missing,
* otherwise used as `title' attribute value.
*
* @var string
*/
protected $descr = '';
/**
* Versions that support this feature
*
* @var Array
*/
protected $versions = array();
/**
* Reference to the FeatureList that this feature belongs to
*
* @var FeatureList
*/
protected $list = null;
public function setList
(&$oList)
{
$this->list =& $oList;
}
/**
* Creates a new Feature object, using values from the passed parameters
* array.
*
* @param array|Object $params
* @return Feature
*/
public function __construct
($params = array())
{
$aVars = get_class_vars(__CLASS__);
while ((list($key, $value) = each($aVars)))
{
if (isset($params[$key]))
{
$this->$key = $params[$key];
}
}
}
/**
* Returns <code>' class="safe"'</code> if the feature
* can be considered safe. The required information
* is stored in the <code>safeVersions</code> property
* of the associated <code>FeatureList</code> object.
*
* @return string
* @see FeatureList::defaultSafeVersions
*/
protected function getSafeStr
()
{
if (!is_null($this->list))
{
foreach ($this->list->safeVersions as $impl => &$safeVer)
{
$thisImplVer =& $this->versions[$impl];
if (is_array($thisImplVer))
{
if (isset($thisImplVer['tested']) && !is_bool($thisImplVer['tested']))
{
$thisImplVer =& $thisImplVer['tested'];
}
else
{
$thisImplVer =& $thisImplVer[0];
}
}
/* DEBUG */
// echo " $impl=$thisImplVer ";
if (preg_match('/^-?$/', $thisImplVer) || $thisImplVer > $safeVer)
{
return '';
}
}
return ' class="safe"';
}
else
{
return '';
}
}
protected function getTitleStr
()
{
if (!empty($this->title))
{
return " title=\"{$this->title}\"";
}
else
{
return '';
}
}
protected function getAnchors
()
{
$result = array();
foreach ($this->anchors as $anchor)
{
$result[] = "<a name=\"{$anchor}\"";
if (preg_match('/^[a-z][a-z0-9_:.-]*/i', $anchor))
{
$result[] = " id=\"{$anchor}\"";
}
$result[] = '></a>';
}
return join('', $result);
}
protected function getAssumed
($v)
{
if (is_array($v) && isset($v['assumed']) && $v['assumed'])
{
return ' class="assumed"';
}
return '';
}
protected function getTested
($v)
{
if (is_array($v) && isset($v['tested']) && $v['tested'])
{
return ' class="tested"';
}
return '';
}
/**
* Returns the version of a feature.
*
* @param string|VersionInfo $vInfo
* @return mixed
*/
protected function getVer
($vInfo)
{
if (is_array($vInfo))
{
/* TODO: Return all versions: documented, assumed, and tested */
$vNumber = (isset($vInfo['tested'])
&& gettype($vInfo['tested']) !== 'boolean')
?
$vInfo['tested']
: $vInfo[0];
$section = isset($vInfo['section'])
?
' <span class="section" title="Specification section">['
. $vInfo['section'] . ']</span>'
: '';
if (isset($vInfo['urn']))
{
if ($this->list instanceof FeatureList
)
{
$url = $this->list->resolveURN($vInfo['urn']);
$vNumber = '<a href="' . $url . '">' . $vNumber
. ($section ?
$section : '') . '</a>';
}
}
else if ($section)
{
$vNumber .= $section;
}
$vInfo = $vNumber;
}
return ($vInfo === '-')
?
'<span title="Not supported">−</span>'
: $vInfo;
}
protected static
function shl
($s)
{
/* stub */
}
public function printMe
()
{
?>
<tr
<?php echo $this->getSafeStr(); ?>>
<th
<?php echo $this->getTitleStr(); ?>>
<?php
echo $this->getAnchors();
echo /*preg_replace_callback(
'#(<code>)(.+?)(</code>)#',
array('self', 'shl'),*/
preg_replace('/…/', '…', $this->content)/*)*/;
?></th>
<?php
$versions = $this->versions;
if (!is_null($this->list))
{
$versions =& $this->list->versions;
}
static
$row = 0;
$row++;
$column = 0;
foreach ($versions as $key => $value)
{
$column++;
$id = "td$row-$column";
$ver = $this->versions[$key];
?>
<td id="
<?php echo $id; ?>"
<?php
echo $this->getAssumed($ver) . $this->getTested($ver);
if (!$key)
{
if (!empty($ver))
{
echo ' title="Test code: '
. htmlspecialchars(
stripslashes($ver),
ENT_COMPAT,
FEATURES_ENCODING
)
. '"';
}
else
{
echo ' title="Not applicable: No automated test case'
. ' is available for this feature. Please click'
. ' the feature code in the first column to run'
. ' a manual test."';
}
}
?>>
<?php
if ($key)
{
echo $this->getVer($ver);
}
else
{
if (!empty($ver))
{
?><script type="text/javascript">
// <![CDATA[
var s = test(
<?php echo $ver; ?>, '<span title="Supported">+</span>',
'<span title="Not supported">−</span>');
tryThis("document.write(s);",
"document.getElementById('
<?php echo $id; ?>').appendChild("
+ "document.createTextNode(s));");
// ]]>
</script>
<?php
}
else
{
echo '<abbr>N/A</abbr>';
}
}
?></td>
<?php
}
?>
</tr>
<?php
}
}
?>