List:Commits« Previous MessageNext Message »
From:Hakan Kuecuekyilmaz Date:September 10 2008 10:38pm
Subject:bzr commit into mysql-falcon branch (hakan:254)
View as plain text  
#At bzr+ssh://bk-internal.mysql.com/bzrroot/mysql-falcon/

  254 Hakan Kuecuekyilmaz	2008-09-11
      Added table.js for sortable tables in
      Falcon Bug Overview and adjusted tables
      accordingly.
      
      table.js can be found at:
          http://www.javascripttoolbox.com/lib/table/
added:
  falcon_bug_statistic/images/
  falcon_bug_statistic/images/ascending.gif
  falcon_bug_statistic/images/descending.gif
  falcon_bug_statistic/images/unsorted.gif
  falcon_bug_statistic/include/table.js
modified:
  falcon_bug_statistic/falcon_bug_statistic.php
  falcon_bug_statistic/fbs.css

=== modified file 'falcon_bug_statistic/falcon_bug_statistic.php'
--- a/falcon_bug_statistic/falcon_bug_statistic.php	2008-09-06 19:55:10 +0000
+++ b/falcon_bug_statistic/falcon_bug_statistic.php	2008-09-10 22:37:53 +0000
@@ -8,9 +8,16 @@
 
 require_once './include/config.inc.php';
 
-/**
- * HTML output.
- */
+if (isset($_POST['days'])) {
+    $date = date('Y-m-d', time() - 60 * 60 * 24 * $_POST['days']);
+    $days = $_POST['days'];
+} else {
+    $days = 7;
+}
+require_once './include/fbs_query.php';
+
+$spacer8  = '        ';
+$spacer16 = '                ';
 ?>
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
@@ -19,6 +26,7 @@ require_once './include/config.inc.php';
         <meta http-equiv="content-type" content="text/html;charset=utf-8" />
         <title>Falcon Bug Statstic</title>
         <link href="./fbs.css" rel="stylesheet" type="text/css" />
+        <script type="text/javascript" src="./include/table.js"></script>
     </head>
 <body>
 
@@ -29,307 +37,322 @@ require_once './include/config.inc.php';
     </div>
 </div>
 
-<?php
-$values= array(7, 14, 21, 30);
-
-echo '<div class="fbs" style="text-align: center;">' . "\n";
-echo '    <form action="'. $_SERVER["PHP_SELF"] . '" method="Post">' . "\n";
-echo '        <select name="days">' . "\n";
-foreach ($values as $data) {
-    if (isset($_POST['days']) && $_POST['days'] == $data) {
-        echo '            <option value="' . $data . '" selected="selected">Last ' . $data .' days</option>' . "\n";
-    } else {
-        echo '            <option value="' . $data . '">Last ' . $data .' days</option>' . "\n";
-    }
-}
-echo '        </select>' . "\n";
-echo '        <input type="submit" name="submit" value="Submit" />' . "\n";
-echo '        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;' . "\n";
-echo '        <strong>' . THIS_DATE . '</strong>' . "\n";
-echo '    </form>' . "\n";
-echo '</div>' . "\n";
-
-if (isset($_POST['days'])) {
-    $date = date('Y-m-d', time() - 60 * 60 * 24 * $_POST['days']);
-    $days = $_POST['days'];
-} else {
-    $days = 7;
-}
-
-require_once './include/fbs_query.php';
-?>
+<div class="fbs">
+    <?php echo '<strong>' . THIS_DATE . '</strong>' . "\n"; ?>
+</div>
 
 <div class="fbs">
-    <table style="width: 90%;">
+    <table class="container">
         <tr>
             <td style="border: none; vertical-align: top;">
-            <table>
-                <tr>
-                    <td colspan="2">Active Falcon Bugs</td>
-                </tr>
-                <tr>
-                    <td><h2>Priority</h2></td>
-                    <td><h2>Count</h2></td>
-                </tr>
+            <table class="table-autosort:0 table-stripeclass:alternate">
+                <thead>
+                    <tr>
+                        <th colspan="2">Active Falcon Bugs</th>
+                    </tr>
+                    <tr class="alternate">
+                        <th class="table-sortable:numeric"><h3>Priority</h3></th>
+                        <th class="table-sortable:numeric"><h3>Count</h3></th>
+                    </tr>
+                </thead>
                 <?php
                 $i = 0;
                 $sum = 0;
+
+                echo '<tbody>' . "\n";
                 while ($row = mysql_fetch_assoc($res_prio)) {
                     $i++;
                     $sum += $row['count'];
                     $url = sprintf(URL, '-' . $row['priority'], $status_active_url, '');
 
-                    if ($i % 2) {
-                        echo '<tr class="even">' . "\n";
-                    } else {
-                        echo '<tr class="odd">' . "\n";
-                    }
-                    echo '    <td><a href="' . $url . '">' . $row['priority'] . '</a></td>' . "\n";
-                    echo '    <td>' . $row['count'] . '</td>' . "\n";
-                    echo '</tr>' . "\n";
+                    echo $spacer16 . '    <tr class="sorted">' . "\n";
+                    echo $spacer16 . '        <td><a href="' . $url . '">' . $row['priority'] . '</a></td>' . "\n";
+                    echo $spacer16 . '        <td>' . $row['count'] . '</td>' . "\n";
+                    echo $spacer16 . '    </tr>' . "\n";
                 }
+                echo $spacer16 . '</tbody>' . "\n";
 
                 $url = sprintf(URL, 'all', $status_active_url, '');
-                echo '<tr>' . "\n";
-                echo '    <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
-                echo '    <td><b>' . $sum . '</b></td>' . "\n";
-                echo '</tr>' . "\n";
+                echo $spacer16 . '<tfoot>' . "\n";
+                echo $spacer16 . '    <tr>' . "\n";
+                echo $spacer16 . '        <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
+                echo $spacer16 . '        <td><strong>' . $sum . '</strong></td>' . "\n";
+                echo $spacer16 . '    </tr>' . "\n";
+                echo $spacer16 . '</tfoot>' . "\n";
                 ?>
             </table></td>
 
             <td rowspan="2" style="border: none; vertical-align: top;">
-            <table>
-                <tr>
-                    <td colspan="2">Active Falcon Bugs</td>
-                </tr>
-                <tr>
-                    <td><h2>Status</h2></td>
-                    <td><h2>Count</h2></td>
-                </tr>
+            <table class="table-autosort:0 table-stripeclass:alternate">
+                <thead>
+                    <tr>
+                        <th colspan="2">Active Falcon Bugs</th>
+                    </tr>
+                    <tr class="alternate">
+                        <th class="table-sortable:default"><h3>Status</h3></th>
+                        <th class="table-sortable:numeric"><h3>Count</h3></th>
+                    </tr>
+                </thead>
                 <?php
                 $i = 0;
                 $sum = 0;
+
+                echo '<tbody>' . "\n";
                 while ($row = mysql_fetch_assoc($res_stat)) {
                     $i++;
                     $sum += $row['count'];
                     $url = sprintf(URL, 'all', $row['status'], '');
 
-                    if ($i % 2) {
-                        echo '<tr class="even">' . "\n";
-                    } else {
-                        echo '<tr class="odd">' . "\n";
-                    }
-                    echo '    <td><a href="' . $url . '">' . $row['status'] . '</a></td>' . "\n";
-                    echo '    <td>' . $row['count'] . '</td>' . "\n";
-                    echo '</tr>' . "\n";
+                    echo $spacer16 . '    <tr class="sorted">' . "\n";
+                    echo $spacer16 . '        <td><a href="' . $url . '">' . $row['status'] . '</a></td>' . "\n";
+                    echo $spacer16 . '        <td>' . $row['count'] . '</td>' . "\n";
+                    echo $spacer16 . '    </tr>' . "\n";
                 }
+                echo $spacer16 . '</tbody>' . "\n";
 
                 $url = sprintf(URL, 'all', $status_active_url, '');
-                echo '<tr>' . "\n";
-                echo '    <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
-                echo '    <td><b>' . $sum . '</b></td>' . "\n";
-                echo '</tr>' . "\n";
-
+                echo $spacer16 . '<tfoot>' . "\n";
+                echo $spacer16 . '    <tr>' . "\n";
+                echo $spacer16 . '        <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
+                echo $spacer16 . '        <td><b>' . $sum . '</b></td>' . "\n";
+                echo $spacer16 . '    </tr>' . "\n";
+                echo $spacer16 . '</tfoot>' . "\n";
                 ?>
             </table></td>
 
             <td rowspan="2" style="border: none; vertical-align: top;">
-            <table>
-                <tr>
-                    <td colspan="3">Active Falcon Bugs</td>
-                </tr>
-                <tr>
-                    <td><h2>Priority</h2></td>
-                    <td><h2>Status</h2></td>
-                    <td><h2>Count</h2></td>
-                </tr>
+            <table class="table-autosort:0 table-stripeclass:alternate">
+                <thead>
+                    <tr>
+                        <th colspan="3">Active Falcon Bugs</th>
+                    </tr>
+                    <tr class="alternate">
+                        <th class="table-sortable:numeric"><h3>Priority</h3></th>
+                        <th class="table-sortable:default"><h3>Status</h3></th>
+                        <th class="table-sortable:numeric"><h3>Count</h3></th>
+                    </tr>
+                </thead>
                 <?php
                 $i = 0;
                 $sum = 0;
+
+                echo '<tbody>' . "\n";
                 while ($row = mysql_fetch_assoc($res_prio_stat)) {
                     $i++;
                     $sum += $row['count'];
                     $url = sprintf(URL, '-' . $row['priority'], $row['status'], '');
 
-                    if ($i % 2) {
-                        echo '<tr class="even">' . "\n";
-                    } else {
-                        echo '<tr class="odd">' . "\n";
-                    }
-                    echo '    <td><a href="' . $url . '">' . $row['priority'] . '</a></td>' . "\n";
-                    echo '    <td>' . $row['status'] . '</td>' . "\n";
-                    echo '    <td>' . $row['count'] . '</td>' . "\n";
-                    echo '</tr>' . "\n";
+                    echo $spacer16 . '    <tr class="sorted">' . "\n";
+                    echo $spacer16 . '        <td><a href="' . $url . '">' . $row['priority'] . '</a></td>' . "\n";
+                    echo $spacer16 . '        <td>' . $row['status'] . '</td>' . "\n";
+                    echo $spacer16 . '        <td>' . $row['count'] . '</td>' . "\n";
+                    echo $spacer16 . '    </tr>' . "\n";
                 }
+                echo $spacer16 . '</tbody>' . "\n";
 
                 $url = sprintf(URL, 'all', $status_active_url, '');
-                echo '<tr>' . "\n";
-                echo '    <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
-                echo '    <td>&nbsp;</td>' . "\n";
-                echo '    <td><b>' . $sum . '</b></td>' . "\n";
-                echo '</tr>' . "\n";
-
+                echo $spacer16 . '<tfoot>' . "\n";
+                echo $spacer16 . '    <tr>' . "\n";
+                echo $spacer16 . '        <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
+                echo $spacer16 . '        <td>&nbsp;</td>' . "\n";
+                echo $spacer16 . '        <td><b>' . $sum . '</b></td>' . "\n";
+                echo $spacer16 . '    </tr>' . "\n";
+                echo $spacer16 . '</tfoot>' . "\n";
                 ?>
             </table></td>
         </tr>
 
         </tr>
             <td style="border: none; vertical-align: top;">
-            <table>
-                <tr>
-                    <td colspan="2">Inactive Falcon Bugs</td>
-                </tr>
-                <tr>
-                    <td><h2>Status</h2></td>
-                    <td><h2>Count</h2></td>
-                </tr>
+            <table class="table-autosort:0 table-stripeclass:alternate">
+                <thead>
+                    <tr>
+                        <th colspan="2">Inactive Falcon Bugs</th>
+                    </tr>
+                    <tr class="alternate">
+                        <th class="table-sortable:default"><h3>Status</h3></th>
+                        <th class="table-sortable:numeric"><h3>Count</h3></th>
+                    </tr>
+                </thead>
                 <?php
                 $i = 0;
                 $sum = 0;
+
+                echo '<tbody>' . "\n";
                 while ($row = mysql_fetch_assoc($res_stat_inactive)) {
                     $i++;
                     $sum += $row['count'];
                     $url = sprintf(URL, 'all', $row['status'], '');
 
-                    if ($i % 2) {
-                        echo '<tr class="even">' . "\n";
-                    } else {
-                        echo '<tr class="odd">' . "\n";
-                    }
-                    echo '    <td><a href="' . $url . '">' . $row['status'] . '</a></td>' . "\n";
-                    echo '    <td>' . $row['count'] . '</td>' . "\n";
-                    echo '</tr>' . "\n";
+
+                    echo $spacer16 . '    <tr class="sorted">' . "\n";
+                    echo $spacer16 . '        <td><a href="' . $url . '">' . $row['status'] . '</a></td>' . "\n";
+                    echo $spacer16 . '        <td>' . $row['count'] . '</td>' . "\n";
+                    echo $spacer16 . '    </tr>' . "\n";
                 }
+                echo $spacer16 . '</tbody>' . "\n";
 
                 $url = sprintf(URL, 'all', $status_inactive_url, '');
-                echo '<tr>' . "\n";
-                echo '    <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
-                echo '    <td><b>' . $sum . '</b></td>' . "\n";
-                echo '</tr>' . "\n";
+                echo $spacer16 . '<tfoot>' . "\n";
+                echo $spacer16 . '    <tr>' . "\n";
+                echo $spacer16 . '        <td><a href="' . $url . '"><b>All</b></a></td>' . "\n";
+                echo $spacer16 . '        <td><b>' . $sum . '</b></td>' . "\n";
+                echo $spacer16 . '    </tr>' . "\n";
+                echo $spacer16 . '</tfoot>' . "\n";
                 ?>
             </table></td>
         </tr>
     </table>
-
     <br />
     <br />
+</div>
 
-    <table style="width: 90%;">
-        <tr>
-            <td colspan="4"><h2>Bugs reported in the last <?php echo $days; ?> days</h2></td>
-        </tr>
-        <tr>
-            <td><h2>Id</h2></td>
-            <td><h2>Priority</h2></td>
-            <td><h2>Status</h2></td>
-            <td><h2>Short description</h2></td>
-        </tr>
+<?php
+$values= array(7, 14, 21, 30);
+
+echo '<a name="select"></a>' . "\n";
+echo '<div class="fbs" style="text-align: center;">' . "\n";
+echo '    <form action="'. $_SERVER["PHP_SELF"] . '#select" method="Post">' . "\n";
+echo '        Get latest reported bugs and bugs with their status change of ' . "\n";
+echo '        <select name="days">' . "\n";
+foreach ($values as $data) {
+    if (isset($_POST['days']) && $_POST['days'] == $data) {
+        echo '            <option value="' . $data . '" selected="selected">last ' . $data .' days</option>' . "\n";
+    } else {
+        echo '            <option value="' . $data . '">last ' . $data .' days</option>' . "\n";
+    }
+}
+echo '        </select>' . "\n";
+echo '        <input type="submit" name="submit" value="Submit" />' . "\n";
+echo '    </form>' . "\n";
+echo '</div>' . "\n";
+?>
+<div class="fbs">
+    <table class="table-autosort:0 table-stripeclass:alternate">
+        <thead>
+            <tr>
+                <th colspan="4"><h2>Bugs reported in the last <?php echo $days; ?> days</h2></th>
+            </tr>
+            <tr class="alternate">
+                <th class="table-sortable:numeric"><h3>Id</h3></th>
+                <th class="table-sortable:numeric"><h3>Priority</h3></th>
+                <th class="table-sortable:default"><h3>Status</h3></th>
+                <th class="table-sortable:default"><h3>Short description</h3></th>
+            </tr>
+        </thead>
         <?php
         $i = 0;
         $sum = 0;
+
+        echo '<tbody>' . "\n";
         while ($row = mysql_fetch_assoc($res_last_bugs)) {
             $i++;
             $url = sprintf('http://bugs.mysql.com/%d', $row[id]);
 
-            if ($i % 2) {
-                echo '<tr class="even">' . "\n";
-            } else {
-                echo '<tr class="odd">' . "\n";
-            }
-            echo '    <td><a href="' . $url . '">' . $row['id'] . '</a></td>' . "\n";
-            echo '    <td>' . $row['priority'] . '</td>' . "\n";
-            echo '    <td>' . $row['status'] . '</td>' . "\n";
-            echo '    <td>' . $row['sdesc'] . '</td>' . "\n";
-            echo '</tr>' . "\n";
+            echo $spacer8 . '    <tr class="sorted">' . "\n";
+            echo $spacer8 . '        <td><a href="' . $url . '">' . $row['id'] . '</a></td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['priority'] . '</td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['status'] . '</td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['sdesc'] . '</td>' . "\n";
+            echo $spacer8 . '    </tr>' . "\n";
         }
+        echo $spacer8 . '</tbody>' . "\n";
 
-        echo '<tr>' . "\n";
-        echo '    <td><b>All</b></td>' . "\n";
-        echo '    <td>&nbsp;</td>' . "\n";
-        echo '    <td>&nbsp;</td>' . "\n";
-        echo '    <td><b>' . $i . '</b></td>' . "\n";
-        echo '</tr>' . "\n";
-
+        echo $spacer8 . '<tfoot>' . "\n";
+        echo $spacer8 . '    <tr>' . "\n";
+        echo $spacer8 . '        <td><b>All</b></td>' . "\n";
+        echo $spacer8 . '        <td>&nbsp;</td>' . "\n";
+        echo $spacer8 . '        <td>&nbsp;</td>' . "\n";
+        echo $spacer8 . '        <td><b>' . $i . '</b></td>' . "\n";
+        echo $spacer8 . '    </tr>' . "\n";
+        echo $spacer8 . '</tfoot>' . "\n";
         ?>
     </table>
 
     <br />
     <br />
 
-    <table style="width: 90%;">
-        <tr>
-            <td colspan="4"><h2>Bugs changed their status in the last <?php echo $days; ?> days</h2></td>
-        </tr>
-        <tr>
-            <td><h2>Id</h2></td>
-            <td><h2>Priority</h2></td>
-            <td><h2>Status</h2></td>
-            <td><h2>Synopsis and change</h2></td>
-        </tr>
+    <table class="table-autosort:0 table-stripeclass:alternate">
+        <thead>
+            <tr>
+                <th colspan="4"><h2>Bugs changed their status in the last <?php echo $days; ?> days</h2></th>
+            </tr>
+            <tr class="alternate">
+                <th class="table-sortable:numeric"><h3>Id</h3></th>
+                <th class="table-sortable:numeric"><h3>Priority</h3></th>
+                <th class="table-sortable:default"><h3>Status</h3></th>
+                <th class="table-sortable:default"><h3>Synopsis and change</h3></th>
+            </tr>
+        </thead>
         <?php
         $i = 0;
         $sum = 0;
+
+        echo '<tbody>' . "\n";
         while ($row = mysql_fetch_assoc($res_last_status_changes)) {
             $i++;
             $url = sprintf('http://bugs.mysql.com/%d', $row[id]);
 
-            if ($i % 2) {
-                echo '<tr class="even">' . "\n";
-            } else {
-                echo '<tr class="odd">' . "\n";
-            }
-            echo '    <td><a href="' . $url . '">' . $row['id'] . '</a></td>' . "\n";
-            echo '    <td>' . $row['priority'] . '</td>' . "\n";
-            echo '    <td>' . $row['status'] . '</td>' . "\n";
-            echo '    <td>' . $row['synopsis'] . '<br />' . nl2br($row['entry']) . '</td>' . "\n";
-            echo '</tr>' . "\n";
+            echo $spacer8 . '    <tr class="sorted">' . "\n";
+            echo $spacer8 . '        <td><a href="' . $url . '">' . $row['id'] . '</a></td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['priority'] . '</td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['status'] . '</td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['synopsis'] . '<br />' . nl2br($row['entry']) . '</td>' . "\n";
+            echo $spacer8 . '    </tr>' . "\n";
         }
+        echo $spacer8 . '</tbody>' . "\n";
 
-        echo '<tr>' . "\n";
-        echo '    <td><b>All</b></td>' . "\n";
-        echo '    <td>&nbsp;</td>' . "\n";
-        echo '    <td>&nbsp;</td>' . "\n";
-        echo '    <td><b>' . $i . '</b></td>' . "\n";
-        echo '</tr>' . "\n";
-
+        echo $spacer8 . '<tfoot>' . "\n";
+        echo $spacer8 . '    <tr>' . "\n";
+        echo $spacer8 . '        <td><b>All</b></td>' . "\n";
+        echo $spacer8 . '        <td>&nbsp;</td>' . "\n";
+        echo $spacer8 . '        <td>&nbsp;</td>' . "\n";
+        echo $spacer8 . '        <td><b>' . $i . '</b></td>' . "\n";
+        echo $spacer8 . '    </tr>' . "\n";
+        echo $spacer8 . '</tfoot>' . "\n";
         ?>
     </table>
 
     <br />
     <br />
+</div>
 
-    <table style="width: 90%;">
-        <tr>
-            <td colspan="4"><h2>Falcon related bugs by developer and status</h2></td>
-        </tr>
-        <tr>
-            <td><h2>Developer</h2></td>
-            <td><h2>Status</h2></td>
-            <td><h2>Count</h2></td>
-        </tr>
+<div class="fbs">
+    <table class="table-autosort:0 table-stripeclass:alternate">
+        <thead>
+            <tr>
+                <th colspan="4"><h2>Falcon related bugs by developer and status</h2></th>
+            </tr>
+            <tr>
+                <th class="table-sortable:default"><h3>Developer</h3></th>
+                <th class="table-sortable:default"><h3>Status</h3></th>
+                <th class="table-sortable:numeric"><h3>Count</h3></th>
+            </tr>
+        </thead>
         <?php
         $i = 0;
         $sum = 0;
+
+        echo '<tbody>' . "\n";
         while ($row = mysql_fetch_assoc($res_developer_status)) {
             $sum += $row['count'];
             $url = sprintf(URL, 'all', $row['status'], $row['assign']);
 
-            if ($i % 2) {
-                echo '<tr class="even">' . "\n";
-            } else {
-                echo '<tr class="odd">' . "\n";
-            }
-            echo '    <td><a href="' . $url . '">' . $developers[$row['assign']] . '</a></td>' . "\n";
-            echo '    <td>' . $row['status'] . '</td>' . "\n";
-            echo '    <td>' . $row['count'] . '</td>' . "\n";
-            echo '</tr>' . "\n";
+            echo $spacer8 . '    <tr class="sorted">' . "\n";
+            echo $spacer8 . '        <td><a href="' . $url . '">' . $developers[$row['assign']] . '</a></td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['status'] . '</td>' . "\n";
+            echo $spacer8 . '        <td>' . $row['count'] . '</td>' . "\n";
+            echo $spacer8 . '    </tr>' . "\n";
         }
+        echo $spacer8 . '</tbody>' . "\n";
 
-        echo '<tr>' . "\n";
-        echo '    <td><b>All</b></td>' . "\n";
-        echo '    <td>&nbsp;</td>' . "\n";
-        echo '    <td><b>' . $sum . '</b></td>' . "\n";
-        echo '</tr>' . "\n";
+        echo $spacer8 . '<tfoot>' . "\n";
+        echo $spacer8 . '    <tr>' . "\n";
+        echo $spacer8 . '        <td><b>All</b></td>' . "\n";
+        echo $spacer8 . '        <td>&nbsp;</td>' . "\n";
+        echo $spacer8 . '        <td><b>' . $sum . '</b></td>' . "\n";
+        echo $spacer8 . '    </tr>' . "\n";
+        echo $spacer8 . '</tfoot>' . "\n";
 
         ?>
     </table>

=== modified file 'falcon_bug_statistic/fbs.css'
--- a/falcon_bug_statistic/fbs.css	2008-09-06 19:55:10 +0000
+++ b/falcon_bug_statistic/fbs.css	2008-09-10 22:37:53 +0000
@@ -1,6 +1,6 @@
 /**
  * Style sheet for Falcon Bug statistics.
- * 
+ *
  * Hakan Kuecuekyilmaz <hakan at mysql dot com>, 2008-06-24.
  * $Id$
  */
@@ -22,6 +22,7 @@ h1 {
 
 h2 {
     color: black;
+    display: inline;
     font-size: 1.4em;
     font-style: italic;
     font-weight: bold;
@@ -33,6 +34,7 @@ h3 {
     display: inline;
     font-size: 1.2em;
     font-weight: bold;
+    text-align: center;
 }
 
 h4 {
@@ -46,59 +48,69 @@ img {
     border: none;
 }
 
-div.fbs_head {
-    background: #FEFEFE;
-    border: 1px dashed black;
-    color: black;
-    font-family: Verdana, Arial, Helvetica, Sans-Serif;
-    margin: 23px 11% 10px 11%;
-    padding: 10px 20px 10px 20px;
-    position: relative;
-    width: auto;
+table {
+    border: 1px solid #000000;
+    border-collapse: collapse;
+    margin: auto;
 }
 
-div.fbs_head_img {
-    text-align: center;
+table.container {
+    border: none;
+    width: 90%;
 }
 
-div.fbs {
-    background: #C6C6CF;
-    border: 1px dashed black;
-    color: black;
-    font-family: Verdana, Arial, Helvetica, Sans-Serif;
-    margin: 23px 11% 10px 11%;
-    padding: 20px 20px 20px 20px;
-    position: relative;
-    width: auto;
+th, td  {
+    border: 1px solid #000000;
+    padding: 2px 15px 2px 15px;
+    vertical-align: bottom;
 }
 
-div.fbs table {
-    border-collapse: collapse;
-    margin: auto;
+tr.even {
+    background-color: #E1E1E1;
 }
 
-div.fbs td {
-    border: 1px solid #000000;
-    padding: 0.1em 0.2em;
-    vertical-align: bottom;
+tr.odd {
+    background-color: #EFFFFA;
 }
 
-div.fbs tr.even {
-    background-color:#E1E1E1;
+/* Striping */
+tr.sorted {
+    background-color: #E1E1E1;
 }
 
-div.fbs tr.odd {
-    background-color:#EFFFFA
+tr.alternate {
+    background-color: #EFFFFA;
 }
 
-div.fbs a {
+/* Sorting */
+th.table-sortable {
+    background-image: url("images/unsorted.gif");
+    background-position: center left;
+    background-repeat: no-repeat;
+    cursor: pointer;
+    padding-left: 0.7em;
+}
+
+th.table-sorted-asc {
+    background-image: url("images/ascending.gif");
+    background-position: center left;
+    background-repeat: no-repeat;
+}
+
+th.table-sorted-desc {
+    background-image: url("images/descending.gif");
+    background-position: center left;
+    background-repeat: no-repeat;
+}
+
+a {
     display: block;
     padding: 0px 0px 0px 5px;
     text-decoration: none;
     color: #5A5A5A;
 }
 
-div.fbs a:hover {
+a:hover {
     color: #EFFFFA;
     background-color: #C3B1E1;
 }
@@ -115,4 +127,30 @@ input:focus, select:focus, textarea:focu
 
 input:hover {
     background-color: #FFE0D4;
-}
\ No newline at end of file
+}
+
+div.fbs_head {
+    background: #FEFEFE;
+    border: 1px dashed black;
+    color: black;
+    font-family: Verdana, Arial, Helvetica, Sans-Serif;
+    margin: 23px 5% 10px 5%;
+    padding: 5px 20px 5px 20px;
+    position: relative;
+    width: auto;
+}
+
+div.fbs_head_img {
+    text-align: center;
+}
+
+div.fbs {
+    background: #C6C6CF;
+    border: 1px dashed black;
+    color: black;
+    font-family: Verdana, Arial, Helvetica, Sans-Serif;
+    margin: 23px 5% 10px 5%;
+    padding: 20px 20px 20px 20px;
+    position: relative;
+    width: auto;
+}

=== added directory 'falcon_bug_statistic/images'
=== added file 'falcon_bug_statistic/images/ascending.gif'
Files a/falcon_bug_statistic/images/ascending.gif	1970-01-01 00:00:00 +0000 and b/falcon_bug_statistic/images/ascending.gif	2008-09-10 22:37:53 +0000 differ

=== added file 'falcon_bug_statistic/images/descending.gif'
Files a/falcon_bug_statistic/images/descending.gif	1970-01-01 00:00:00 +0000 and b/falcon_bug_statistic/images/descending.gif	2008-09-10 22:37:53 +0000 differ

=== added file 'falcon_bug_statistic/images/unsorted.gif'
Files a/falcon_bug_statistic/images/unsorted.gif	1970-01-01 00:00:00 +0000 and b/falcon_bug_statistic/images/unsorted.gif	2008-09-10 22:37:53 +0000 differ

=== added file 'falcon_bug_statistic/include/table.js'
--- a/falcon_bug_statistic/include/table.js	1970-01-01 00:00:00 +0000
+++ b/falcon_bug_statistic/include/table.js	2008-09-10 22:37:53 +0000
@@ -0,0 +1,1002 @@
+/**
+ * Copyright (c)2005-2007 Matt Kruse (javascripttoolbox.com)
+ * 
+ * Dual licensed under the MIT and GPL licenses. 
+ * This basically means you can use this code however you want for
+ * free, but don't claim to have written it yourself!
+ * Donations always accepted: http://www.JavascriptToolbox.com/donate/
+ * 
+ * Please do not link to the .js files on javascripttoolbox.com from
+ * your site. Copy the files locally to your server instead.
+ * 
+ */
+/**
+ * Table.js
+ * Functions for interactive Tables
+ *
+ * Copyright (c) 2007 Matt Kruse (javascripttoolbox.com)
+ * Dual licensed under the MIT and GPL licenses. 
+ *
+ * @version 0.981
+ *
+ * @history 0.981 2007-03-19 Added Sort.numeric_comma, additional date parsing formats
+ * @history 0.980 2007-03-18 Release new BETA release pending some testing. Todo: Additional docs, examples, plus jQuery plugin.
+ * @history 0.959 2007-03-05 Added more "auto" functionality, couple bug fixes
+ * @history 0.958 2007-02-28 Added auto functionality based on class names
+ * @history 0.957 2007-02-21 Speed increases, more code cleanup, added Auto Sort functionality
+ * @history 0.956 2007-02-16 Cleaned up the code and added Auto Filter functionality.
+ * @history 0.950 2006-11-15 First BETA release.
+ *
+ * @todo Add more date format parsers
+ * @todo Add style classes to colgroup tags after sorting/filtering in case the user wants to highlight the whole column
+ * @todo Correct for colspans in data rows (this may slow it down)
+ * @todo Fix for IE losing form control values after sort?
+ */
+
+/**
+ * Sort Functions
+ */
+var Sort = (function(){
+	var sort = {};
+	// Default alpha-numeric sort
+	// --------------------------
+	sort.alphanumeric = function(a,b) {
+		return (a==b)?0:(a<b)?-1:1;
+	};
+	sort['default'] = sort.alphanumeric; // IE chokes on sort.default
+
+	// This conversion is generalized to work for either a decimal separator of , or .
+	sort.numeric_converter = function(separator) {
+		return function(val) {
+			if (typeof(val)=="string") {
+				val = parseFloat(val.replace(/^[^\d\.]*([\d., ]+).*/g,"$1").replace(new RegExp("[^\\\d"+separator+"]","g"),'').replace(/,/,'.')) || 0;
+			}
+			return val || 0;
+		};
+	};
+
+	// Numeric Sort	
+	// ------------
+	sort.numeric = function(a,b) {
+		return sort.numeric.convert(a)-sort.numeric.convert(b);
+	};
+	sort.numeric.convert = sort.numeric_converter(".");
+
+	// Numeric Sort	- comma decimal separator
+	// --------------------------------------
+	sort.numeric_comma = function(a,b) {
+		return sort.numeric_comma.convert(a)-sort.numeric_comma.convert(b);
+	};
+	sort.numeric_comma.convert = sort.numeric_converter(",");
+
+	// Case-insensitive Sort
+	// ---------------------
+	sort.ignorecase = function(a,b) {
+		return sort.alphanumeric(sort.ignorecase.convert(a),sort.ignorecase.convert(b));
+	};
+	sort.ignorecase.convert = function(val) {
+		if (val==null) { return ""; }
+		return (""+val).toLowerCase();
+	};
+
+	// Currency Sort
+	// -------------
+	sort.currency = sort.numeric; // Just treat it as numeric!
+	sort.currency_comma = sort.numeric_comma;
+
+	// Date sort
+	// ---------
+	sort.date = function(a,b) {
+		return sort.numeric(sort.date.convert(a),sort.date.convert(b));
+	};
+	// Convert 2-digit years to 4
+	sort.date.fixYear=function(yr) {
+		yr = +yr;
+		if (yr<50) { yr += 2000; }
+		else if (yr<100) { yr += 1900; }
+		return yr;
+	};
+	sort.date.formats = [
+		// YY[YY]-MM-DD
+		{ re:/(\d{2,4})-(\d{1,2})-(\d{1,2})/ , f:function(x){ return (new Date(sort.date.fixYear(x[1]),+x[2],+x[3])).getTime(); } }
+		// MM/DD/YY[YY] or MM-DD-YY[YY]
+		,{ re:/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})/ , f:function(x){ return (new Date(sort.date.fixYear(x[3]),+x[1],+x[2])).getTime(); } }
+		// Any catch-all format that new Date() can handle. This is not reliable except for long formats, for example: 31 Jan 2000 01:23:45 GMT
+		,{ re:/(.*\d{4}.*\d+:\d+\d+.*)/, f:function(x){ var d=new Date(x[1]); if(d){return d.getTime();} } }
+	];
+	sort.date.convert = function(val) {
+		var m,v, f = sort.date.formats;
+		for (var i=0,L=f.length; i<L; i++) {
+			if (m=val.match(f[i].re)) {
+				v=f[i].f(m);
+				if (typeof(v)!="undefined") { return v; }
+			}
+		}
+		return 9999999999999; // So non-parsed dates will be last, not first
+	};
+
+	return sort;
+})();
+
+/**
+ * The main Table namespace
+ */
+var Table = (function(){
+
+	/**
+	 * Determine if a reference is defined
+	 */
+	function def(o) {return (typeof o!="undefined");};
+
+	/**
+	 * Determine if an object or class string contains a given class.
+	 */
+	function hasClass(o,name) {
+		return new RegExp("(^|\\s)"+name+"(\\s|$)").test(o.className);
+	};
+
+	/**
+	 * Add a class to an object
+	 */
+	function addClass(o,name) {
+		var c = o.className || "";
+		if (def(c) && !hasClass(o,name)) {
+			o.className += (c?" ":"") + name;
+		}
+	};
+
+	/**
+	 * Remove a class from an object
+	 */
+	function removeClass(o,name) {
+		var c = o.className || "";
+		o.className = c.replace(new RegExp("(^|\\s)"+name+"(\\s|$)"),"$1");
+	};
+
+	/**
+	 * For classes that match a given substring, return the rest
+	 */
+	function classValue(o,prefix) {
+		var c = o.className;
+		if (c.match(new RegExp("(^|\\s)"+prefix+"([^ ]+)"))) {
+			return RegExp.$2;
+		}
+		return null;
+	};
+
+	/**
+	 * Return true if an object is hidden.
+	 * This uses the "russian doll" technique to unwrap itself to the most efficient
+	 * function after the first pass. This avoids repeated feature detection that 
+	 * would always fall into the same block of code.
+	 */
+	 function isHidden(o) {
+		if (window.getComputedStyle) {
+			var cs = window.getComputedStyle;
+			return (isHidden = function(o) {
+				return 'none'==cs(o,null).getPropertyValue('display');
+			})(o);
+		}
+		else if (window.currentStyle) {
+			return(isHidden = function(o) {
+				return 'none'==o.currentStyle['display'];
+			})(o);
+		}
+		return (isHidden = function(o) {
+			return 'none'==o.style['display'];
+		})(o);
+	};
+
+	/**
+	 * Get a parent element by tag name, or the original element if it is of the tag type
+	 */
+	function getParent(o,a,b) {
+		if (o!=null && o.nodeName) {
+			if (o.nodeName==a || (b && o.nodeName==b)) {
+				return o;
+			}
+			while (o=o.parentNode) {
+				if (o.nodeName && (o.nodeName==a || (b && o.nodeName==b))) {
+					return o;
+				}
+			}
+		}
+		return null;
+	};
+
+	/**
+	 * Utility function to copy properties from one object to another
+	 */
+	function copy(o1,o2) {
+		for (var i=2;i<arguments.length; i++) {
+			var a = arguments[i];
+			if (def(o1[a])) {
+				o2[a] = o1[a];
+			}
+		}
+	}
+
+	// The table object itself
+	var table = {
+		//Class names used in the code
+		AutoStripeClassName:"table-autostripe",
+		StripeClassNamePrefix:"table-stripeclass:",
+
+		AutoSortClassName:"table-autosort",
+		AutoSortColumnPrefix:"table-autosort:",
+		AutoSortTitle:"Click to sort",
+		SortedAscendingClassName:"table-sorted-asc",
+		SortedDescendingClassName:"table-sorted-desc",
+		SortableClassName:"table-sortable",
+		SortableColumnPrefix:"table-sortable:",
+		NoSortClassName:"table-nosort",
+
+		AutoFilterClassName:"table-autofilter",
+		FilteredClassName:"table-filtered",
+		FilterableClassName:"table-filterable",
+		FilteredRowcountPrefix:"table-filtered-rowcount:",
+		RowcountPrefix:"table-rowcount:",
+		FilterAllLabel:"Filter: All",
+
+		AutoPageSizePrefix:"table-autopage:",
+		AutoPageJumpPrefix:"table-page:",
+		PageNumberPrefix:"table-page-number:",
+		PageCountPrefix:"table-page-count:"
+	};
+
+	/**
+	 * A place to store misc table information, rather than in the table objects themselves
+	 */
+	table.tabledata = {};
+
+	/**
+	 * Resolve a table given an element reference, and make sure it has a unique ID
+	 */
+	table.uniqueId=1;
+	table.resolve = function(o,args) {
+		if (o!=null && o.nodeName && o.nodeName!="TABLE") {
+			o = getParent(o,"TABLE");
+		}
+		if (o==null) { return null; }
+		if (!o.id) {
+			var id = null;
+			do { var id = "TABLE_"+(table.uniqueId++); } 
+				while (document.getElementById(id)!=null);
+			o.id = id;
+		}
+		this.tabledata[o.id] = this.tabledata[o.id] || {};
+		if (args) {
+			copy(args,this.tabledata[o.id],"stripeclass","ignorehiddenrows","useinnertext","sorttype","col","desc","page","pagesize");
+		}
+		return o;
+	};
+
+
+	/**
+	 * Run a function against each cell in a table header or footer, usually 
+	 * to add or remove css classes based on sorting, filtering, etc.
+	 */
+	table.processTableCells = function(t, type, func, arg) {
+		t = this.resolve(t);
+		if (t==null) { return; }
+		if (type!="TFOOT") {
+			this.processCells(t.tHead, func, arg);
+		}
+		if (type!="THEAD") {
+			this.processCells(t.tFoot, func, arg);
+		}
+	};
+
+	/**
+	 * Internal method used to process an arbitrary collection of cells.
+	 * Referenced by processTableCells.
+	 * It's done this way to avoid getElementsByTagName() which would also return nested table cells.
+	 */
+	table.processCells = function(section,func,arg) {
+		if (section!=null) {
+			if (section.rows && section.rows.length && section.rows.length>0) { 
+				var rows = section.rows;
+				for (var j=0,L2=rows.length; j<L2; j++) { 
+					var row = rows[j];
+					if (row.cells && row.cells.length && row.cells.length>0) {
+						var cells = row.cells;
+						for (var k=0,L3=cells.length; k<L3; k++) {
+							var cellsK = cells[k];
+							func.call(this,cellsK,arg);
+						}
+					}
+				}
+			}
+		}
+	};
+
+	/**
+	 * Get the cellIndex value for a cell. This is only needed because of a Safari
+	 * bug that causes cellIndex to exist but always be 0.
+	 * Rather than feature-detecting each time it is called, the function will
+	 * re-write itself the first time it is called.
+	 */
+	table.getCellIndex = function(td) {
+		var tr = td.parentNode;
+		var cells = tr.cells;
+		if (cells && cells.length) {
+			if (cells.length>1 && cells[cells.length-1].cellIndex>0) {
+				// Define the new function, overwrite the one we're running now, and then run the new one
+				(this.getCellIndex = function(td) {
+					return td.cellIndex;
+				})(td);
+			}
+			// Safari will always go through this slower block every time. Oh well.
+			for (var i=0,L=cells.length; i<L; i++) {
+				if (tr.cells[i]==td) {
+					return i;
+				}
+			}
+		}
+		return 0;
+	};
+
+	/**
+	 * A map of node names and how to convert them into their "value" for sorting, filtering, etc.
+	 * These are put here so it is extensible.
+	 */
+	table.nodeValue = {
+		'INPUT':function(node) { 
+			if (def(node.value) && node.type && ((node.type!="checkbox" && node.type!="radio") || node.checked)) {
+				return node.value;
+			}
+			return "";
+		},
+		'SELECT':function(node) {
+			if (node.selectedIndex>=0 && node.options) {
+				// Sort select elements by the visible text
+				return node.options[node.selectedIndex].text;
+			}
+			return "";
+		},
+		'IMG':function(node) {
+			return node.name || "";
+		}
+	};
+
+	/**
+	 * Get the text value of a cell. Only use innerText if explicitly told to, because 
+	 * otherwise we want to be able to handle sorting on inputs and other types
+	 */
+	table.getCellValue = function(td,useInnerText) {
+		if (useInnerText && def(td.innerText)) {
+			return td.innerText;
+		}
+		if (!td.childNodes) { 
+			return ""; 
+		}
+		var childNodes=td.childNodes;
+		var ret = "";
+		for (var i=0,L=childNodes.length; i<L; i++) {
+			var node = childNodes[i];
+			var type = node.nodeType;
+			// In order to get realistic sort results, we need to treat some elements in a special way.
+			// These behaviors are defined in the nodeValue() object, keyed by node name
+			if (type==1) {
+				var nname = node.nodeName;
+				if (this.nodeValue[nname]) {
+					ret += this.nodeValue[nname](node);
+				}
+				else {
+					ret += this.getCellValue(node);
+				}
+			}
+			else if (type==3) {
+				if (def(node.innerText)) {
+					ret += node.innerText;
+				}
+				else if (def(node.nodeValue)) {
+					ret += node.nodeValue;
+				}
+			}
+		}
+		return ret;
+	};
+
+	/**
+	 * Consider colspan and rowspan values in table header cells to calculate the actual cellIndex
+	 * of a given cell. This is necessary because if the first cell in row 0 has a rowspan of 2, 
+	 * then the first cell in row 1 will have a cellIndex of 0 rather than 1, even though it really
+	 * starts in the second column rather than the first.
+	 * See: http://www.javascripttoolbox.com/temp/table_cellindex.html
+	 */
+	table.tableHeaderIndexes = {};
+	table.getActualCellIndex = function(tableCellObj) {
+		if (!def(tableCellObj.cellIndex)) { return null; }
+		var tableObj = getParent(tableCellObj,"TABLE");
+		var cellCoordinates = tableCellObj.parentNode.rowIndex+"-"+this.getCellIndex(tableCellObj);
+
+		// If it has already been computed, return the answer from the lookup table
+		if (def(this.tableHeaderIndexes[tableObj.id])) {
+			return this.tableHeaderIndexes[tableObj.id][cellCoordinates];      
+		} 
+
+		var matrix = [];
+		this.tableHeaderIndexes[tableObj.id] = {};
+		var thead = getParent(tableCellObj,"THEAD");
+		var trs = thead.getElementsByTagName('TR');
+
+		// Loop thru every tr and every cell in the tr, building up a 2-d array "grid" that gets
+		// populated with an "x" for each space that a cell takes up. If the first cell is colspan
+		// 2, it will fill in values [0] and [1] in the first array, so that the second cell will
+		// find the first empty cell in the first row (which will be [2]) and know that this is
+		// where it sits, rather than its internal .cellIndex value of [1].
+		for (var i=0; i<trs.length; i++) {
+			var cells = trs[i].cells;
+			for (var j=0; j<cells.length; j++) {
+				var c = cells[j];
+				var rowIndex = c.parentNode.rowIndex;
+				var cellId = rowIndex+"-"+this.getCellIndex(c);
+				var rowSpan = c.rowSpan || 1;
+				var colSpan = c.colSpan || 1;
+				var firstAvailCol;
+				if(!def(matrix[rowIndex])) { 
+					matrix[rowIndex] = []; 
+				}
+				var m = matrix[rowIndex];
+				// Find first available column in the first row
+				for (var k=0; k<m.length+1; k++) {
+					if (!def(m[k])) {
+						firstAvailCol = k;
+						break;
+					}
+				}
+				this.tableHeaderIndexes[tableObj.id][cellId] = firstAvailCol;
+				for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
+					if(!def(matrix[k])) { 
+						matrix[k] = []; 
+					}
+					var matrixrow = matrix[k];
+					for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
+						matrixrow[l] = "x";
+					}
+				}
+			}
+		}
+		// Store the map so future lookups are fast.
+		return this.tableHeaderIndexes[tableObj.id][cellCoordinates];
+	};
+
+	/**
+	 * Sort all rows in each TBODY (tbodies are sorted independent of each other)
+	 */
+	table.sort = function(o,args) {
+		var t, tdata, sortconvert=null;
+		// Allow for a simple passing of sort type as second parameter
+		if (typeof(args)=="function") {
+			args={sorttype:args};
+		}
+		args = args || {};
+
+		// If no col is specified, deduce it from the object sent in
+		if (!def(args.col)) { 
+			args.col = this.getActualCellIndex(o) || 0; 
+		}
+		// If no sort type is specified, default to the default sort
+		args.sorttype = args.sorttype || Sort['default'];
+
+		// Resolve the table
+		t = this.resolve(o,args);
+		tdata = this.tabledata[t.id];
+
+		// If we are sorting on the same column as last time, flip the sort direction
+		if (def(tdata.lastcol) && tdata.lastcol==tdata.col && def(tdata.lastdesc)) {
+			tdata.desc = !tdata.lastdesc;
+		}
+		else {
+			tdata.desc = !!args.desc;
+		}
+
+		// Store the last sorted column so clicking again will reverse the sort order
+		tdata.lastcol=tdata.col;
+		tdata.lastdesc=!!tdata.desc;
+
+		// If a sort conversion function exists, pre-convert cell values and then use a plain alphanumeric sort
+		var sorttype = tdata.sorttype;
+		if (typeof(sorttype.convert)=="function") {
+			sortconvert=tdata.sorttype.convert;
+			sorttype=Sort.alphanumeric;
+		}
+
+		// Loop through all THEADs and remove sorted class names, then re-add them for the col
+		// that is being sorted
+		this.processTableCells(t,"THEAD",
+			function(cell) {
+				if (hasClass(cell,this.SortableClassName)) {
+					removeClass(cell,this.SortedAscendingClassName);
+					removeClass(cell,this.SortedDescendingClassName);
+					// If the computed colIndex of the cell equals the sorted colIndex, flag it as sorted
+					if (tdata.col==table.getActualCellIndex(cell) && (classValue(cell,table.SortableClassName))) {
+						addClass(cell,tdata.desc?this.SortedAscendingClassName:this.SortedDescendingClassName);
+					}
+				}
+			}
+		);
+
+		// Sort each tbody independently
+		var bodies = t.tBodies;
+		if (bodies==null || bodies.length==0) { return; }
+
+		// Define a new sort function to be called to consider descending or not
+		var newSortFunc = (tdata.desc)?
+			function(a,b){return sorttype(b[0],a[0]);}
+			:function(a,b){return sorttype(a[0],b[0]);};
+
+		var useinnertext=!!tdata.useinnertext;
+		var col = tdata.col;
+
+		for (var i=0,L=bodies.length; i<L; i++) {
+			var tb = bodies[i], tbrows = tb.rows, rows = [];
+
+			// Allow tbodies to request that they not be sorted
+			if(!hasClass(tb,table.NoSortClassName)) {
+				// Create a separate array which will store the converted values and refs to the
+				// actual rows. This is the array that will be sorted.
+				var cRow, cRowIndex=0;
+				if (cRow=tbrows[cRowIndex]){
+					// Funky loop style because it's considerably faster in IE
+					do {
+						if (rowCells = cRow.cells) {
+							var cellValue = (col<rowCells.length)?this.getCellValue(rowCells[col],useinnertext):null;
+							if (sortconvert) cellValue = sortconvert(cellValue);
+							rows[cRowIndex] = [cellValue,tbrows[cRowIndex]];
+						}
+					} while (cRow=tbrows[++cRowIndex])
+				}
+
+				// Do the actual sorting
+				rows.sort(newSortFunc);
+
+				// Move the rows to the correctly sorted order. Appending an existing DOM object just moves it!
+				cRowIndex=0;
+				var displayedCount=0;
+				var f=[removeClass,addClass];
+				if (cRow=rows[cRowIndex]){
+					do { 
+						tb.appendChild(cRow[1]); 
+					} while (cRow=rows[++cRowIndex])
+				}
+			}
+		}
+
+		// If paging is enabled on the table, then we need to re-page because the order of rows has changed!
+		if (tdata.pagesize) {
+			this.page(t); // This will internally do the striping
+		}
+		else {
+			// Re-stripe if a class name was supplied
+			if (tdata.stripeclass) {
+				this.stripe(t,tdata.stripeclass,!!tdata.ignorehiddenrows);
+			}
+		}
+	};
+
+	/**
+	* Apply a filter to rows in a table and hide those that do not match.
+	*/
+	table.filter = function(o,filters,args) {
+		var cell;
+		args = args || {};
+
+		var t = this.resolve(o,args);
+		var tdata = this.tabledata[t.id];
+
+		// If new filters were passed in, apply them to the table's list of filters
+		if (!filters) {
+			// If a null or blank value was sent in for 'filters' then that means reset the table to no filters
+			tdata.filters = null;
+		}
+		else {
+			// Allow for passing a select list in as the filter, since this is common design
+			if (filters.nodeName=="SELECT" && filters.type=="select-one" && filters.selectedIndex>-1) {
+				filters={ 'filter':filters.options[filters.selectedIndex].value };
+			}
+			// Also allow for a regular input
+			if (filters.nodeName=="INPUT" && filters.type=="text") {
+				filters={ 'filter':"/^"+filters.value+"/" };
+			}
+			// Force filters to be an array
+			if (typeof(filters)=="object" && !filters.length) {
+				filters = [filters];
+			}
+
+			// Convert regular expression strings to RegExp objects and function strings to function objects
+			for (var i=0,L=filters.length; i<L; i++) {
+				var filter = filters[i];
+				if (typeof(filter.filter)=="string") {
+					// If a filter string is like "/expr/" then turn it into a Regex
+					if (filter.filter.match(/^\/(.*)\/$/)) {
+						filter.filter = new RegExp(RegExp.$1);
+						filter.filter.regex=true;
+					}
+					// If filter string is like "function (x) { ... }" then turn it into a function
+					else if (filter.filter.match(/^function\s*\(([^\)]*)\)\s*\{(.*)}\s*$/)) {
+						filter.filter = Function(RegExp.$1,RegExp.$2);
+					}
+				}
+				// If some non-table object was passed in rather than a 'col' value, resolve it 
+				// and assign it's column index to the filter if it doesn't have one. This way, 
+				// passing in a cell reference or a select object etc instead of a table object 
+				// will automatically set the correct column to filter.
+				if (filter && !def(filter.col) && (cell=getParent(o,"TD","TH"))) {
+					filter.col = this.getCellIndex(cell);
+				}
+
+				// Apply the passed-in filters to the existing list of filters for the table, removing those that have a filter of null or ""
+				if ((!filter || !filter.filter) && tdata.filters) {
+					delete tdata.filters[filter.col];
+				}
+				else {
+					tdata.filters = tdata.filters || {};
+					tdata.filters[filter.col] = filter.filter;
+				}
+			}
+			// If no more filters are left, then make sure to empty out the filters object
+			for (var j in tdata.filters) { var keep = true; }
+			if (!keep) {
+				tdata.filters = null;
+			}
+		}		
+		// Everything's been setup, so now scrape the table rows
+		return table.scrape(o);
+	};
+
+	/**
+	 * "Page" a table by showing only a subset of the rows
+	 */
+	table.page = function(t,page,args) {
+		args = args || {};
+		if (def(page)) { args.page = page; }
+		return table.scrape(t,args);
+	};
+
+	/**
+	 * Jump forward or back any number of pages
+	 */
+	table.pageJump = function(t,count,args) {
+		t = this.resolve(t,args);
+		return this.page(t,(table.tabledata[t.id].page||0)+count,args);
+	};
+
+	/**
+	 * Go to the next page of a paged table
+	 */	
+	table.pageNext = function(t,args) {
+		return this.pageJump(t,1,args);
+	};
+
+	/**
+	 * Go to the previous page of a paged table
+	 */	
+	table.pagePrevious = function(t,args) {
+		return this.pageJump(t,-1,args);
+	};
+
+	/**
+	* Scrape a table to either hide or show each row based on filters and paging
+	*/
+	table.scrape = function(o,args) {
+		var col,cell,filterList,filterReset=false,filter;
+		var page,pagesize,pagestart,pageend;
+		var unfilteredrows=[],unfilteredrowcount=0,totalrows=0;
+		var t,tdata,row,hideRow;
+		args = args || {};
+
+		// Resolve the table object
+		t = this.resolve(o,args);
+		tdata = this.tabledata[t.id];
+
+		// Setup for Paging
+		var page = tdata.page;
+		if (def(page)) {
+			// Don't let the page go before the beginning
+			if (page<0) { tdata.page=page=0; }
+			pagesize = tdata.pagesize || 25; // 25=arbitrary default
+			pagestart = page*pagesize+1;
+			pageend = pagestart + pagesize - 1;
+		}
+
+		// Scrape each row of each tbody
+		var bodies = t.tBodies;
+		if (bodies==null || bodies.length==0) { return; }
+		for (var i=0,L=bodies.length; i<L; i++) {
+			var tb = bodies[i];
+			for (var j=0,L2=tb.rows.length; j<L2; j++) {
+				row = tb.rows[j];
+				hideRow = false;
+
+				// Test if filters will hide the row
+				if (tdata.filters && row.cells) {
+					var cells = row.cells;
+					var cellsLength = cells.length;
+					// Test each filter
+					for (col in tdata.filters) {
+						if (!hideRow) {
+							filter = tdata.filters[col];
+							if (filter && col<cellsLength) {
+								var val = this.getCellValue(cells[col]);
+								if (filter.regex && val.search) {
+									hideRow=(val.search(filter)<0);
+								}
+								else if (typeof(filter)=="function") {
+									hideRow=!filter(val,cells[col]);
+								}
+								else {
+									hideRow = (val!=filter);
+								}
+							}
+						}
+					}
+				}
+
+				// Keep track of the total rows scanned and the total runs _not_ filtered out
+				totalrows++;
+				if (!hideRow) {
+					unfilteredrowcount++;
+					if (def(page)) {
+						// Temporarily keep an array of unfiltered rows in case the page we're on goes past
+						// the last page and we need to back up. Don't want to filter again!
+						unfilteredrows.push(row);
+						if (unfilteredrowcount<pagestart || unfilteredrowcount>pageend) {
+							hideRow = true;
+						}
+					}
+				}
+
+				row.style.display = hideRow?"none":"";
+			}
+		}
+
+		if (def(page)) {
+			// Check to see if filtering has put us past the requested page index. If it has, 
+			// then go back to the last page and show it.
+			if (pagestart>=unfilteredrowcount) {
+				pagestart = unfilteredrowcount-(unfilteredrowcount%pagesize);
+				tdata.page = page = pagestart/pagesize;
+				for (var i=pagestart,L=unfilteredrows.length; i<L; i++) {
+					unfilteredrows[i].style.display="";
+				}
+			}
+		}
+
+		// Loop through all THEADs and add/remove filtered class names
+		this.processTableCells(t,"THEAD",
+			function(c) {
+				((tdata.filters && def(tdata.filters[table.getCellIndex(c)]) && hasClass(c,table.FilterableClassName))?addClass:removeClass)(c,table.FilteredClassName);
+			}
+		);
+
+		// Stripe the table if necessary
+		if (tdata.stripeclass) {
+			this.stripe(t);
+		}
+
+		// Calculate some values to be returned for info and updating purposes
+		var pagecount = Math.floor(unfilteredrowcount/pagesize)+1;
+		if (def(page)) {
+			// Update the page number/total containers if they exist
+			if (tdata.container_number) {
+				tdata.container_number.innerHTML = page+1;
+			}
+			if (tdata.container_count) {
+				tdata.container_count.innerHTML = pagecount;
+			}
+		}
+
+		// Update the row count containers if they exist
+		if (tdata.container_filtered_count) {
+			tdata.container_filtered_count.innerHTML = unfilteredrowcount;
+		}
+		if (tdata.container_all_count) {
+			tdata.container_all_count.innerHTML = totalrows;
+		}
+		return { 'data':tdata, 'unfilteredcount':unfilteredrowcount, 'total':totalrows, 'pagecount':pagecount, 'page':page, 'pagesize':pagesize };
+	};
+
+	/**
+	 * Shade alternate rows, aka Stripe the table.
+	 */
+	table.stripe = function(t,className,args) { 
+		args = args || {};
+		args.stripeclass = className;
+
+		t = this.resolve(t,args);
+		var tdata = this.tabledata[t.id];
+
+		var bodies = t.tBodies;
+		if (bodies==null || bodies.length==0) { 
+			return; 
+		}
+
+		className = tdata.stripeclass;
+		// Cache a shorter, quicker reference to either the remove or add class methods
+		var f=[removeClass,addClass];
+		for (var i=0,L=bodies.length; i<L; i++) {
+			var tb = bodies[i], tbrows = tb.rows, cRowIndex=0, cRow, displayedCount=0;
+			if (cRow=tbrows[cRowIndex]){
+				// The ignorehiddenrows test is pulled out of the loop for a slight speed increase.
+				// Makes a bigger difference in FF than in IE.
+				// In this case, speed always wins over brevity!
+				if (tdata.ignoreHiddenRows) {
+					do {
+						f[displayedCount++%2](cRow,className);
+					} while (cRow=tbrows[++cRowIndex])
+				}
+				else {
+					do {
+						if (!isHidden(cRow)) {
+							f[displayedCount++%2](cRow,className);
+						}
+					} while (cRow=tbrows[++cRowIndex])
+				}
+			}
+		}
+	};
+
+	/**
+	 * Build up a list of unique values in a table column
+	 */
+	table.getUniqueColValues = function(t,col) {
+		var values={}, bodies = this.resolve(t).tBodies;
+		for (var i=0,L=bodies.length; i<L; i++) {
+			var tbody = bodies[i];
+			for (var r=0,L2=tbody.rows.length; r<L2; r++) {
+				values[this.getCellValue(tbody.rows[r].cells[col])] = true;
+			}
+		}
+		var valArray = [];
+		for (var val in values) {
+			valArray.push(val);
+		}
+		return valArray.sort();
+	};
+
+	/**
+	 * Scan the document on load and add sorting, filtering, paging etc ability automatically
+	 * based on existence of class names on the table and cells.
+	 */
+	table.auto = function(args) {
+		var cells = [], tables = document.getElementsByTagName("TABLE");
+		var val,tdata;
+		if (tables!=null) {
+			for (var i=0,L=tables.length; i<L; i++) {
+				var t = table.resolve(tables[i]);
+				tdata = table.tabledata[t.id];
+				if (val=classValue(t,table.StripeClassNamePrefix)) {
+					tdata.stripeclass=val;
+				}
+				// Do auto-filter if necessary
+				if (hasClass(t,table.AutoFilterClassName)) {
+					table.autofilter(t);
+				}
+				// Do auto-page if necessary
+				if (val = classValue(t,table.AutoPageSizePrefix)) {
+					table.autopage(t,{'pagesize':+val});
+				}
+				// Do auto-sort if necessary
+				if ((val = classValue(t,table.AutoSortColumnPrefix)) || (hasClass(t,table.AutoSortClassName))) {
+					table.autosort(t,{'col':(val==null)?null:+val});
+				}
+				// Do auto-stripe if necessary
+				if (tdata.stripeclass && hasClass(t,table.AutoStripeClassName)) {
+					table.stripe(t);
+				}
+			}
+		}
+	};
+
+	/**
+	 * Add sorting functionality to a table header cell
+	 */
+	table.autosort = function(t,args) {
+		t = this.resolve(t,args);
+		var tdata = this.tabledata[t.id];
+		this.processTableCells(t, "THEAD", function(c) {
+			var type = classValue(c,table.SortableColumnPrefix);
+			if (type!=null) {
+				type = type || "default";
+				c.title =c.title || table.AutoSortTitle;
+				addClass(c,table.SortableClassName);
+				c.onclick = Function("","Table.sort(this,{'sorttype':Sort['"+type+"']})");
+				// If we are going to auto sort on a column, we need to keep track of what kind of sort it will be
+				if (args.col!=null) {
+					if (args.col==table.getActualCellIndex(c)) {
+						tdata.sorttype=Sort['"+type+"'];
+					}
+				}
+			}
+		} );
+		if (args.col!=null) {
+			table.sort(t,args);
+		}
+	};
+
+	/**
+	 * Add paging functionality to a table 
+	 */
+	table.autopage = function(t,args) {
+		t = this.resolve(t,args);
+		var tdata = this.tabledata[t.id];
+		if (tdata.pagesize) {
+			this.processTableCells(t, "THEAD,TFOOT", function(c) {
+				var type = classValue(c,table.AutoPageJumpPrefix);
+				if (type=="next") { type = 1; }
+				else if (type=="previous") { type = -1; }
+				if (type!=null) {
+					c.onclick = Function("","Table.pageJump(this,"+type+")");
+				}
+			} );
+			if (val = classValue(t,table.PageNumberPrefix)) {
+				tdata.container_number = document.getElementById(val);
+			}
+			if (val = classValue(t,table.PageCountPrefix)) {
+				tdata.container_count = document.getElementById(val);
+			}
+			return table.page(t,0,args);
+		}
+	};
+
+	/**
+	 * A util function to cancel bubbling of clicks on filter dropdowns
+	 */
+	table.cancelBubble = function(e) {
+		e = e || window.event;
+		if (typeof(e.stopPropagation)=="function") { e.stopPropagation(); } 
+		if (def(e.cancelBubble)) { e.cancelBubble = true; }
+	};
+
+	/**
+	 * Auto-filter a table
+	 */
+	table.autofilter = function(t,args) {
+		args = args || {};
+		t = this.resolve(t,args);
+		var tdata = this.tabledata[t.id],val;
+		table.processTableCells(t, "THEAD", function(cell) {
+			if (hasClass(cell,table.FilterableClassName)) {
+				var cellIndex = table.getCellIndex(cell);
+				var colValues = table.getUniqueColValues(t,cellIndex);
+				if (colValues.length>0) {
+					if (typeof(args.insert)=="function") {
+						func.insert(cell,colValues);
+					}
+					else {
+						var sel = '<select onchange="Table.filter(this,this)" onclick="Table.cancelBubble(event)" class="'+table.AutoFilterClassName+'"><option value="">'+table.FilterAllLabel+'</option>';
+						for (var i=0; i<colValues.length; i++) {
+							sel += '<option value="'+colValues[i]+'">'+colValues[i]+'</option>';
+						}
+						sel += '</select>';
+						cell.innerHTML += "<br>"+sel;
+					}
+				}
+			}
+		});
+		if (val = classValue(t,table.FilteredRowcountPrefix)) {
+			tdata.container_filtered_count = document.getElementById(val);
+		}
+		if (val = classValue(t,table.RowcountPrefix)) {
+			tdata.container_all_count = document.getElementById(val);
+		}
+	};
+
+	/**
+	 * Attach the auto event so it happens on load.
+	 * use jQuery's ready() function if available
+	 */
+	if (typeof(jQuery)!="undefined") {
+		jQuery(table.auto);
+	}
+	else if (window.addEventListener) {
+		window.addEventListener( "load", table.auto, false );
+	}
+	else if (window.attachEvent) {
+		window.attachEvent( "onload", table.auto );
+	}
+
+	return table;
+})();

Thread
bzr commit into mysql-falcon branch (hakan:254) Hakan Kuecuekyilmaz11 Sep