Subversion Repositories PHPX

Rev

Rev 17 | Rev 20 | 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
  /**
19 PointedEar 33
   * <code>true</code> generates form controls for submitting test case
34
   * results
35
   *
36
   * @var bool
37
   */
38
  protected $testcase = false;
39
 
40
  /**
2 PointedEar 41
   * The list of language features
42
   *
43
   * @var array[Features]
44
   */
45
  protected $items = array();
46
 
47
  /**
48
   * Determines the number of printed items the table headings should be repeated
49
   *
50
   * @var int
51
   */
52
  protected $headerRepeat = 25;
53
 
54
  /**
55
   * Initializes the FeatureList object
56
   *
57
   * @param array|Object $a
58
   * @return FeatureList
59
   */
60
  public function __construct($a)
61
  {
9 PointedEar 62
    $aVars = get_class_vars(get_class($this));
63
 
19 PointedEar 64
 
65
    foreach ($aVars as $key => $value)
2 PointedEar 66
    {
67
      if (isset($a[$key]))
68
      {
69
        $this->$key = $a[$key];
70
      }
71
    }
19 PointedEar 72
 
2 PointedEar 73
    /* Inform items of ourself so that URNs can be used for links */
74
    if (is_array($this->items))
75
    {
76
      foreach ($this->items as &$item)
77
      {
4 PointedEar 78
        $item->setList($this);
2 PointedEar 79
      }
80
    }
81
 
82
    /* resolve URN references that are URNs */
83
    if (is_array($this->urns))
84
    {
85
      foreach ($this->urns as &$urn)
86
      {
87
        if (($url = $this->resolveURN($urn)))
88
        {
89
          $urn = $url;
90
        }
91
      }
92
    }
93
  }
94
 
19 PointedEar 95
  /*
96
   * Protected properties may be read, but not written
97
   */
98
  public function __get($property)
99
  {
100
    if (property_exists(get_class($this), $property))
101
    {
102
      return $this->$property;
103
    }
104
  }
105
 
2 PointedEar 106
  public function printHeaders()
107
  {
19 PointedEar 108
    foreach ($this->versions as $key => $ver)
2 PointedEar 109
    {
19 PointedEar 110
      if ($key || $this->testcase)
111
      {
2 PointedEar 112
?>
113
          <th><?php echo $ver; ?></th>
114
<?php
19 PointedEar 115
      }
2 PointedEar 116
    }
117
  }
118
 
119
  /**
120
   * Prints the list of features.
121
   *
122
   * @see Feature::printMe()
123
   */
124
  public function printItems()
125
  {
126
    $counter = 0;
127
    $headerRepeat = $this->headerRepeat;
128
    $repeatHeaders = ($headerRepeat > 1);
129
 
130
    foreach ($this->items as $feature)
131
    {
132
      if ($feature instanceof Feature)
133
      {
16 PointedEar 134
        /*
135
         * TODO: Disabled header repetition until footnote ref. name/ID
136
         * problem has been solved
137
         */
138
//        if ($repeatHeaders
139
//            && $counter > 1
140
//            && $counter % $headerRepeat === 0)
141
//        {
142
//          echo <<<HTML
143
//        <tr class="header">
144
//          <th>Feature</th>
145
//          {$this->printHeaders()}
146
//        </tr>
147
//HTML;
148
//        }
2 PointedEar 149
 
150
        $feature->printMe();
151
 
152
        $counter++;
153
      }
154
    }
155
  }
156
 
157
  /**
158
   * Resolves a URN according to the value of the
159
   * object's <code>$urn</code> property.
160
   *
161
   * @param string $urn
162
   *   URN to be resolved
163
   * @return string|boolean
164
   *   The resolved URN if successful,
165
   *   <code>false</code> otherwise.
166
   */
167
  public function resolveURN($urn)
168
  {
169
    if (is_array($this->urns))
170
    {
171
      $reURN = '|^(.+?):(?!//)|';
172
 
173
      if (preg_match($reURN, $urn, $m) && isset($this->urns[$m[1]]))
174
      {
175
        return preg_replace($reURN, $this->urns[$m[1]], $urn);
176
      }
177
    }
178
 
179
    return $urn;
180
  }
181
}
182
 
183
/**
184
 * A language feature.
185
 */
186
class Feature
187
{
188
  /**
189
   * Fragment identifiers to be defined for quickly accessing
190
   * the feature description.
191
   *
192
   * @var Array[String]
193
   */
194
  protected $anchors = array();
195
 
196
  /**
197
   * Value of the explanatory <code>title</code> attribute for the feature.
198
   *
199
   * @var string
200
   */
201
  protected $title = '';
202
 
203
  /**
204
   * Name or example code of the feature
205
   *
206
   * @var string
207
   */
208
  protected $content = '';
209
 
210
  /**
211
   * Description of the feature.  Displayed directly if code is missing,
212
   * otherwise used as `title' attribute value.
213
   *
214
   * @var string
215
   */
216
  protected $descr = '';
217
 
218
  /**
219
   * Versions that support this feature
220
   *
221
   * @var Array
222
   */
223
  protected $versions = array();
224
 
225
  /**
226
   * Reference to the FeatureList that this feature belongs to
227
   *
228
   * @var FeatureList
229
   */
230
  protected $list = null;
231
 
4 PointedEar 232
  public function setList(&$oList)
2 PointedEar 233
  {
234
    $this->list =& $oList;
235
  }
236
 
237
  /**
238
   * Creates a new Feature object, using values from the passed parameters
239
   * array.
240
   *
241
   * @param array|Object $params
242
   * @return Feature
243
   */
244
  public function __construct($params = array())
245
  {
14 PointedEar 246
    $aVars = get_class_vars(__CLASS__);
9 PointedEar 247
 
19 PointedEar 248
    foreach ($aVars as $key => $value)
2 PointedEar 249
    {
9 PointedEar 250
      if (isset($params[$key]))
251
      {
252
        $this->$key = $params[$key];
253
      }
2 PointedEar 254
    }
255
  }
256
 
257
  /**
17 PointedEar 258
   * Determines whether one version is greater than another.
259
   *
260
   * @param string $v1  Version string #1
261
   * @param string $v2  Version string #2
262
   * @return bool
263
   *   <code>true</code> if the version <var>$v1</var> is greater than
264
   *   the version <var>$v2</var>, <code>false</code> otherwise
265
   */
266
  protected static function _versionIsGreater($v1, $v2)
267
  {
268
    $v1 = explode('.', $v1);
269
    $v2 = explode('.', $v2);
270
 
271
    foreach ($v1 as $key => $value)
272
    {
273
      if ((int)$value <= (int)$v2[$key])
274
      {
275
        return false;
276
      }
277
    }
278
 
279
    return true;
280
  }
281
 
282
  /**
2 PointedEar 283
   * Returns <code>' class="safe"'</code> if the feature
284
   * can be considered safe.  The required information
285
   * is stored in the <code>safeVersions</code> property
286
   * of the associated <code>FeatureList</code> object.
287
   *
288
   * @return string
289
   * @see FeatureList::defaultSafeVersions
290
   */
291
  protected function getSafeStr()
292
  {
293
    if (!is_null($this->list))
294
    {
295
      foreach ($this->list->safeVersions as $impl => &$safeVer)
296
      {
297
        $thisImplVer =& $this->versions[$impl];
298
        if (is_array($thisImplVer))
299
        {
300
          if (isset($thisImplVer['tested']) && !is_bool($thisImplVer['tested']))
301
          {
302
            $thisImplVer =& $thisImplVer['tested'];
303
          }
304
          else
305
        {
306
            $thisImplVer =& $thisImplVer[0];
307
          }
308
        }
309
 
310
        /* DEBUG */
311
        // echo " $impl=$thisImplVer ";
312
 
17 PointedEar 313
        if (preg_match('/^-?$/', $thisImplVer) || self::_versionIsGreater($thisImplVer, $safeVer))
2 PointedEar 314
        {
315
          return '';
316
        }
317
      }
318
 
319
      return ' class="safe"';
320
    }
321
    else
322
    {
323
      return '';
324
    }
325
  }
326
 
327
  protected function getTitleStr()
328
  {
329
    if (!empty($this->title))
330
    {
331
      return " title=\"{$this->title}\"";
332
    }
333
    else
334
    {
335
      return '';
336
    }
337
  }
338
 
339
  protected function getAnchors()
340
  {
341
    $result = array();
342
 
343
    foreach ($this->anchors as $anchor)
344
    {
345
      $result[] = "<a name=\"{$anchor}\"";
346
 
347
      if (preg_match('/^[a-z][a-z0-9_:.-]*/i', $anchor))
348
      {
349
        $result[] = " id=\"{$anchor}\"";
350
      }
351
 
352
      $result[] = '></a>';
353
    }
354
 
355
    return join('', $result);
356
  }
357
 
358
  protected function getAssumed($v)
359
  {
360
    if (is_array($v) && isset($v['assumed']) && $v['assumed'])
361
    {
362
      return ' class="assumed"';
363
    }
364
 
365
    return '';
366
  }
367
 
368
  protected function getTested($v)
369
  {
370
    if (is_array($v) && isset($v['tested']) && $v['tested'])
371
    {
372
      return ' class="tested"';
373
    }
374
 
375
    return '';
376
  }
377
 
378
  /**
379
   * Returns the version of a feature.
380
   *
381
   * @param string|VersionInfo $vInfo
382
   * @return mixed
383
   */
384
  protected function getVer($vInfo)
385
  {
386
    if (is_array($vInfo))
387
    {
388
      /* TODO: Return all versions: documented, assumed, and tested */
389
      $vNumber = (isset($vInfo['tested'])
390
                  && gettype($vInfo['tested']) !== 'boolean')
391
                     ? $vInfo['tested']
392
                     : $vInfo[0];
393
      $section = isset($vInfo['section'])
394
                   ? ' <span class="section" title="Specification section">['
395
                      . $vInfo['section'] . ']</span>'
396
                   : '';
397
 
398
      if (isset($vInfo['urn']))
399
      {
400
        if ($this->list instanceof FeatureList)
401
        {
402
          $url = $this->list->resolveURN($vInfo['urn']);
403
          $vNumber = '<a href="' . $url . '">' . $vNumber
6 PointedEar 404
            . ($section ? $section : '') . '</a>';
2 PointedEar 405
        }
406
      }
407
      else if ($section)
408
      {
409
        $vNumber .= $section;
410
      }
411
 
6 PointedEar 412
      $vInfo = $vNumber;
2 PointedEar 413
    }
6 PointedEar 414
 
415
    return ($vInfo === '-')
416
      ? '<span title="Not supported">&#8722;</span>'
417
      : $vInfo;
2 PointedEar 418
  }
17 PointedEar 419
 
420
  /**
421
   * Returns a syntax-highlighted version of a string
422
   *
423
   * @param string $s
424
   * @return string
425
   */
2 PointedEar 426
  protected static function shl($s)
427
  {
428
    /* stub */
17 PointedEar 429
    return $s;
2 PointedEar 430
  }
431
 
432
  public function printMe()
433
  {
434
    ?>
435
<tr<?php echo $this->getSafeStr(); ?>>
436
          <th<?php echo $this->getTitleStr(); ?>><?php
437
            echo $this->getAnchors();
438
            echo /*preg_replace_callback(
439
              '#(<code>)(.+?)(</code>)#',
440
              array('self', 'shl'),*/
441
              preg_replace('/&hellip;/', '&#8230;', $this->content)/*)*/;
442
            ?></th>
443
<?php
444
    $versions = $this->versions;
19 PointedEar 445
    $testcase = false;
2 PointedEar 446
    if (!is_null($this->list))
447
    {
448
      $versions =& $this->list->versions;
19 PointedEar 449
      $testcase = $this->list->testcase;
2 PointedEar 450
    }
451
 
452
    static $row = 0;
453
    $row++;
454
 
455
    $column = 0;
17 PointedEar 456
    $thisVersions =& $this->versions;
2 PointedEar 457
 
458
    foreach ($versions as $key => $value)
459
    {
460
      $column++;
461
      $id = "td$row-$column";
17 PointedEar 462
      $ver = isset($thisVersions[$key]) ? $thisVersions[$key] : '';
19 PointedEar 463
      if ($key || $testcase)
464
      {
2 PointedEar 465
?>
17 PointedEar 466
          <td<?php
19 PointedEar 467
            if (!$key)
468
            {
469
              echo " id='$id'";
470
            }
471
 
2 PointedEar 472
            echo $this->getAssumed($ver) . $this->getTested($ver);
17 PointedEar 473
 
2 PointedEar 474
            if (!$key)
475
            {
476
              if (!empty($ver))
477
              {
13 PointedEar 478
                echo ' title="Test code: '
479
                  . htmlspecialchars(
17 PointedEar 480
                      preg_replace('/\\\(["\'])/', '\1',
481
                        reduceWhitespace($ver)
482
                      ),
13 PointedEar 483
                      ENT_COMPAT,
17 PointedEar 484
                      FEATURES_ENCODING
485
                    )
13 PointedEar 486
                  . '"';
2 PointedEar 487
              }
488
              else
19 PointedEar 489
            {
2 PointedEar 490
                echo ' title="Not applicable: No automated test case'
17 PointedEar 491
                  . ' is available for this feature.  If possible, please'
492
                  . ' click the feature code in the first column to run'
2 PointedEar 493
                  . ' a manual test."';
494
              }
495
            }
19 PointedEar 496
            else
497
          {
498
              echo ' title="'
499
                . htmlspecialchars(
500
                    preg_replace('/<.*?>/', '', $value),
501
                    ENT_COMPAT, FEATURES_ENCODING)
502
                . '"';
503
            }
2 PointedEar 504
            ?>><?php
505
            if ($key)
506
            {
507
              echo $this->getVer($ver);
17 PointedEar 508
 
509
              /* General footnotes support: include footnotes.class.php to enable */
510
              if (is_array($ver) && isset($ver['footnote']) && $ver['footnote'])
511
              {
512
                echo $ver['footnote'];
513
              }
2 PointedEar 514
            }
515
            else
19 PointedEar 516
          {
517
              if (!empty($ver) && $testcase)
2 PointedEar 518
              {
519
                ?><script type="text/javascript">
520
  // <![CDATA[
19 PointedEar 521
  var s = test(<?php echo $ver; ?>,
522
    '<input title="Supported" name="<?php echo htmlspecialchars($this->title, ENT_COMPAT, FEATURES_ENCODING); ?>" value="+" readonly>',
523
    '<input title="Not supported" name="<?php echo htmlspecialchars($this->title, ENT_COMPAT, FEATURES_ENCODING); ?>" value="&#8722;" readonly>');
524
  jsx.tryThis("document.write(s);",
2 PointedEar 525
          "document.getElementById('<?php echo $id; ?>').appendChild("
526
          + "document.createTextNode(s));");
527
  // ]]>
528
</script><?php
529
              }
530
              else
19 PointedEar 531
            {
2 PointedEar 532
                echo '<abbr>N/A</abbr>';
533
              }
534
            }
535
            ?></td>
536
<?php
19 PointedEar 537
      }
2 PointedEar 538
    }
539
?>
540
        </tr>
541
<?php
542
  }
543
}
544
 
545
?>