summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobert Buchholz <rbu@goodpoint.de>2008-04-08 09:41:35 +0200
committerRobert Buchholz <rbu@goodpoint.de>2008-04-08 09:41:35 +0200
commit943fdafe78d19e2319e9b4310449ef2cab59cc14 (patch)
treeede64d44ff617f9fcec6783c41a8c38337626e00
downloadglsamaker-943fdafe78d19e2319e9b4310449ef2cab59cc14.tar.gz
glsamaker-943fdafe78d19e2319e9b4310449ef2cab59cc14.tar.bz2
glsamaker-943fdafe78d19e2319e9b4310449ef2cab59cc14.zip
GLSAmaker, state from 2007-10-11
Author: Tim Yamin <plasm@roo.me.uk>
-rw-r--r--.gitignore4
-rw-r--r--css/body.css22
-rw-r--r--css/frames.css55
-rw-r--r--css/pinstripe.css91
-rw-r--r--email.alias5
-rw-r--r--frame-archive.php199
-rw-r--r--frame-body.php170
-rw-r--r--frame-edit.php10
-rw-r--r--frame-fetch.php40
-rw-r--r--frame-left-collapsed.php21
-rw-r--r--frame-left.php67
-rw-r--r--frame-new.php667
-rw-r--r--frame-recent.php210
-rw-r--r--frame-request.php122
-rw-r--r--frame-search.php80
-rw-r--r--frame-stats.graph.php252
-rw-r--r--frame-stats.php88
-rw-r--r--frame-view.php563
-rw-r--r--framed.php10
-rw-r--r--images/bg-grid-dark.gifbin0 -> 60 bytes
-rw-r--r--images/bg-grid.gifbin0 -> 60 bytes
-rw-r--r--images/gentooLogo.gifbin0 -> 7108 bytes
-rw-r--r--images/gentooLogo.pngbin0 -> 17302 bytes
-rw-r--r--images/glsaLogo.gifbin0 -> 5734 bytes
-rw-r--r--images/glsaLogo.pngbin0 -> 10575 bytes
-rw-r--r--images/icons/back.gifbin0 -> 1067 bytes
-rw-r--r--images/icons/back.pngbin0 -> 868 bytes
-rw-r--r--images/icons/bugs.gifbin0 -> 999 bytes
-rw-r--r--images/icons/bugs.pngbin0 -> 1024 bytes
-rw-r--r--images/icons/cross.gifbin0 -> 586 bytes
-rw-r--r--images/icons/cross.pngbin0 -> 792 bytes
-rw-r--r--images/icons/directory.gifbin0 -> 1036 bytes
-rw-r--r--images/icons/directory.pngbin0 -> 1111 bytes
-rw-r--r--images/icons/flag.gifbin0 -> 564 bytes
-rw-r--r--images/icons/flag.pngbin0 -> 745 bytes
-rw-r--r--images/icons/flag_disabled.gifbin0 -> 962 bytes
-rw-r--r--images/icons/flag_disabled.pngbin0 -> 401 bytes
-rw-r--r--images/icons/flag_green.gifbin0 -> 561 bytes
-rw-r--r--images/icons/flag_green.pngbin0 -> 701 bytes
-rw-r--r--images/icons/flag_star.gifbin0 -> 569 bytes
-rw-r--r--images/icons/flag_star.pngbin0 -> 704 bytes
-rw-r--r--images/icons/flag_yellow.gifbin0 -> 558 bytes
-rw-r--r--images/icons/flag_yellow.pngbin0 -> 718 bytes
-rw-r--r--images/icons/glsa.gifbin0 -> 9093 bytes
-rw-r--r--images/icons/glsa.pngbin0 -> 16908 bytes
-rw-r--r--images/icons/help.gifbin0 -> 1036 bytes
-rw-r--r--images/icons/help.pngbin0 -> 1169 bytes
-rw-r--r--images/icons/login.gifbin0 -> 558 bytes
-rw-r--r--images/icons/login.pngbin0 -> 683 bytes
-rw-r--r--images/icons/new.gifbin0 -> 1012 bytes
-rw-r--r--images/icons/new.lg.gifbin0 -> 1542 bytes
-rw-r--r--images/icons/new.lg.pngbin0 -> 2219 bytes
-rw-r--r--images/icons/new.pngbin0 -> 948 bytes
-rw-r--r--images/icons/newbug.gifbin0 -> 601 bytes
-rw-r--r--images/icons/newbug.pngbin0 -> 879 bytes
-rw-r--r--images/icons/query.gifbin0 -> 1009 bytes
-rw-r--r--images/icons/query.pngbin0 -> 988 bytes
-rw-r--r--images/icons/queue.gifbin0 -> 996 bytes
-rw-r--r--images/icons/queue.lg.pngbin0 -> 2030 bytes
-rw-r--r--images/icons/queue.pngbin0 -> 946 bytes
-rw-r--r--images/icons/reporting.gifbin0 -> 1010 bytes
-rw-r--r--images/icons/reporting.lg.pngbin0 -> 2073 bytes
-rw-r--r--images/icons/reporting.pngbin0 -> 962 bytes
-rw-r--r--images/icons/resolved.gifbin0 -> 574 bytes
-rw-r--r--images/icons/resolved.pngbin0 -> 594 bytes
-rw-r--r--images/icons/tick.gifbin0 -> 313 bytes
-rw-r--r--images/icons/tick.pngbin0 -> 661 bytes
-rw-r--r--images/icons/tick_p.gifbin0 -> 207 bytes
-rw-r--r--images/icons/tick_p.pngbin0 -> 549 bytes
-rw-r--r--images/icons/view.lg.pngbin0 -> 2174 bytes
-rw-r--r--images/icons/view_text.gifbin0 -> 577 bytes
-rw-r--r--images/icons/view_text.pngbin0 -> 828 bytes
-rw-r--r--images/icons/viewmag.lg.pngbin0 -> 1928 bytes
-rw-r--r--images/icons/viewmag.pngbin0 -> 704 bytes
-rw-r--r--images/icons/warning.gifbin0 -> 576 bytes
-rw-r--r--images/icons/warning.pngbin0 -> 601 bytes
-rw-r--r--includes/common.auth62
-rw-r--r--includes/common.diff78
-rw-r--r--includes/common.diff.php51
-rw-r--r--includes/common.spell86
-rw-r--r--includes/common.version8
-rw-r--r--includes/io.filegrepper391
-rw-r--r--includes/io.mailer106
-rw-r--r--includes/jpgraph/imgdata_balls.inc1065
-rw-r--r--includes/jpgraph/imgdata_bevels.inc106
-rw-r--r--includes/jpgraph/imgdata_diamonds.inc179
-rw-r--r--includes/jpgraph/imgdata_pushpins.inc519
-rw-r--r--includes/jpgraph/imgdata_squares.inc152
-rw-r--r--includes/jpgraph/imgdata_stars.inc146
-rw-r--r--includes/jpgraph/jpgraph.php8736
-rw-r--r--includes/jpgraph/jpgraph_bar.php794
-rw-r--r--includes/jpgraph/jpgraph_canvas.php73
-rw-r--r--includes/jpgraph/jpgraph_canvtools.php517
-rw-r--r--includes/jpgraph/jpgraph_error.php154
-rw-r--r--includes/jpgraph/jpgraph_flags.php360
-rw-r--r--includes/jpgraph/jpgraph_gantt.php3566
-rw-r--r--includes/jpgraph/jpgraph_gb2312.php1554
-rw-r--r--includes/jpgraph/jpgraph_gradient.php322
-rw-r--r--includes/jpgraph/jpgraph_imgtrans.php230
-rw-r--r--includes/jpgraph/jpgraph_line.php444
-rw-r--r--includes/jpgraph/jpgraph_log.php248
-rw-r--r--includes/jpgraph/jpgraph_pie.php971
-rw-r--r--includes/jpgraph/jpgraph_pie3d.php895
-rw-r--r--includes/jpgraph/jpgraph_plotmark.inc480
-rw-r--r--includes/jpgraph/jpgraph_polar.php834
-rw-r--r--includes/jpgraph/jpgraph_radar.php600
-rw-r--r--includes/jpgraph/jpgraph_regstat.php105
-rw-r--r--includes/jpgraph/jpgraph_scatter.php222
-rw-r--r--includes/jpgraph/jpgraph_stock.php183
-rw-r--r--includes/ui.body148
-rw-r--r--includes/ui.css60
-rw-r--r--includes/ui.css.js66
-rw-r--r--includes/ui.init49
-rw-r--r--includes/ui.newitems66
-rw-r--r--includes/xml.glsaparser1228
l---------index.php1
-rw-r--r--spell.data8
117 files changed, 28539 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5b6cb50
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+data
+pool
+.htaccess
+.htpasswd
diff --git a/css/body.css b/css/body.css
new file mode 100644
index 0000000..5d4f352
--- /dev/null
+++ b/css/body.css
@@ -0,0 +1,22 @@
+.center { text-align: center; }
+
+.grayinput_halfWidth { width: 50%; }
+.grayinput_fullWidth { width: 100%; }
+
+acronym, .help { cursor: help; }
+.monospace { font-family: monospace; }
+
+.highlightAddedLine { color: #DD0000; }
+.highlightComment { color: #007700; }
+.highlightRemovedLine { color: #0000BB; }
+
+OPTION.dtop { border-top: 2px dotted black; }
+OPTION.eq { border-top: 2px dotted black; }
+OPTION.ge { background-color: #D1EFB0; }
+OPTION.gt { background-color: #C6E3A7; }
+OPTION.rge { background-color: #BAEFB0; }
+OPTION.rgt { background-color: #B1E3A7; }
+OPTION.le { background-color: #BEB0EF; }
+OPTION.lt { background-color: #B4A7E3; }
+OPTION.rle { background-color: #D4B0EF; }
+OPTION.rlt { background-color: #C9A7E3; }
diff --git a/css/frames.css b/css/frames.css
new file mode 100644
index 0000000..a64b9fa
--- /dev/null
+++ b/css/frames.css
@@ -0,0 +1,55 @@
+/* This software is open source; however; you may only use
+/* and distribute this source code as specified by the GNU
+/* General Public License <http://www.gnu.org/copyleft/gpl.html>
+
+/* < (C) Copyright 2003 SquirrelSoft Technologies...> */
+
+body {
+
+ font: small Verdana, Sans-serif;
+
+}
+
+.frame_nav_item {
+
+ text-align: left;
+ font-family: Verdana, Geneva, Arial, helvetica, sans-serif;
+ font-size: x-small;
+ text-decoration:none;
+
+ margin-left: 3px;
+ margin-right: 3px;
+
+}
+
+.frame_nav_item a {
+
+ border-top: 1px solid #999;
+ border-right: 1px solid #444;
+ border-bottom: 1px solid #444;
+ border-left: 1px solid #999;
+
+ padding-left: 6px;
+ padding-right: 6px;
+ padding-top: 3px;
+ padding-bottom: 3px;
+
+ margin-top: 1px;
+ margin-right: 1px;
+
+ display: block;
+
+ color: #888;
+ background: #fff;
+ font-weight : normal;
+
+}
+
+.frame_nav_item a:hover {
+
+ text-decoration:none;
+ color: #666;
+ font-weight: normal;
+ background: #eee;
+
+}
diff --git a/css/pinstripe.css b/css/pinstripe.css
new file mode 100644
index 0000000..f5f82d5
--- /dev/null
+++ b/css/pinstripe.css
@@ -0,0 +1,91 @@
+body {
+ font: small Verdana, sans-serif;
+}
+
+.body_rootTable {
+ -moz-border-radius: 7px 0px 6px 0px;
+ background: url('../images/bg-grid-dark.gif');
+ border: 1px solid gray;
+ text-align: justify;
+}
+
+.body_rootTable_center {
+ font-style: italic;
+ text-align: center;
+}
+
+.body_rootTable_light {
+ background: url('../images/bg-grid.gif');
+ border: 1px solid gray;
+ padding: 4px;
+ text-align: justify;
+}
+
+a, a:visited
+{
+ color: blue;
+ text-decoration: underline; /* Opera 7 doesn't do this automatically for some reason */
+}
+a:hover
+{
+ font-weight: bold;
+}
+
+a.black, a.black:visited
+{
+ color: black;
+}
+a.black:hover
+{
+ font-weight: bold;
+}
+
+a.gray,a.gray:visited {
+ color: #888888;
+ font-weight: normal;
+ font-style: normal;
+ text-decoration: underline;
+ background-color: transparent;
+}
+
+a.gray:hover {
+ font-weight: bold;
+}
+
+a.nav, a.nav:visited {
+ color: black;
+ font-weight: normal;
+ font-style: normal;
+ text-decoration: none;
+}
+
+a.nav:hover {
+ font-weight: bold;
+}
+
+.grayinput {
+ background-image: url('../images/bg-grid.gif');
+ border: 1px solid gray;
+ padding-bottom: 1px;
+ text-align: center;
+}
+
+.grayinput_fullWidth, .grayinput_halfWidth {
+ background: url('../images/bg-grid.gif');
+ border: 1px solid gray;
+ font-family: Verdana, sans-serif;
+ font-size: small;
+ padding: 2px;
+}
+
+.datestamp {
+ color: #444444;
+ float: right;
+ font-weight: bold;
+ font-size: x-small;
+ font-style: italic;
+ padding-top: 2px;
+}
+
+pre { border-right: 2px solid #DDDDDD; }
+
diff --git a/email.alias b/email.alias
new file mode 100644
index 0000000..d504e92
--- /dev/null
+++ b/email.alias
@@ -0,0 +1,5 @@
+# Lines beginning with '#' are ignored.
+#
+# Add one alias per one line, for example:
+# glsaman::glsaman@glsa.org
+
diff --git a/frame-archive.php b/frame-archive.php
new file mode 100644
index 0000000..1874bff
--- /dev/null
+++ b/frame-archive.php
@@ -0,0 +1,199 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/xml.glsaparser';
+
+initBegin('Archive');
+bodyFooter_invoke();
+
+$rangeYears = array();
+
+function shiftWeek($day)
+{
+ $day--;
+ if($day < 0)
+ return 6;
+ return $day;
+}
+
+function stepDay($timeStamp, $direction = 1)
+{
+ $stampData = getdate($timeStamp);
+ $day = &$stampData['mday'];
+ $month = &$stampData['mon'];
+ $year = &$stampData['year'];
+ $day += $direction;
+ if($day > date('t', $timeStamp))
+ {
+ $day = 1;
+ $month++;
+ }
+ if($day < 1)
+ {
+ $month--;
+ if($month < 0)
+ {
+ $month = 12;
+ $year--;
+ }
+ $day = date('t', mktime(0, 0, 0, $month, 1, $year));
+ }
+ if($month > 12)
+ {
+ $month = 1;
+ $year++;
+ }
+ if($month < 0)
+ {
+ $month = 12;
+ $year--;
+ }
+ return mktime(0, 0, 0, $month, $day, $year);
+}
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Gentoo GLSA Announcement Calendar</i></b></font></td><td align="right"><img src="images/icons/new.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</td></tr><tr><td colspan="2"><hr></td></tr>
+ <tr><td colspan="2"><table width="100%" border="0" cellspacing="2" cellpadding="2">
+ <?
+ $GLSAItems = fileGrepper_parseTree(true, true);
+ foreach ($GLSAItems as $GLSAItem)
+ {
+ sscanf($GLSAItem[0], '%04d%02d-%d', $ID1, $ID2, $ID3);
+ $output = fileGrepper_getGLSAText($ID1, $ID2, $ID3);
+
+ if($output != '<nodata>')
+ {
+ $Parser = new GLSAParser();
+ $Parser->GLSAparse($output, true);
+
+ $GLSAs[strtotime($Parser->GLSADate)][] = $GLSAItem[0];
+ $GLSAShortSummaries[$GLSAItem[0]] = $Parser->GLSAShortSummary;
+ if(!isset($rangeYears[$ID1]))
+ $rangeYears[$ID1] = array();
+ if(!in_array($ID2, $rangeYears[$ID1]))
+ $rangeYears[$ID1][] = $ID2;
+ }
+ }
+
+ $HTTP_GET_VARS["GLSAMonth"] > 0 && $HTTP_GET_VARS["GLSAMonth"] <= 13 ? $month = $HTTP_GET_VARS["GLSAMonth"] : $month = date('m');
+ isset($rangeYears[$HTTP_GET_VARS["GLSAYear"]]) ? $year = $HTTP_GET_VARS["GLSAYear"] : $year = date('Y');
+ end($rangeYears[$year]);
+ in_array($month, $rangeYears[$year]) ? false : $month = current($rangeYears[$year]);
+
+ $monthStart = mktime(0, 0, 0, $month, 1, $year);
+
+ $sDay = 2; // Set to '1' for StartOnSunday
+ $tStamp = mktime(0, 0, 0, date('w', $monthStart) > 0 ? $month-1 : $month, date('w', $monthStart) > 0 ? date('t', mktime(0, 0, 0, $month-1, 1, $year))-date('w', $monthStart)+$sDay : 1, $year);
+ $iStamp = $tStamp;
+
+ echo '<tr bgcolor="#CCCCCC"><td colspan="9"><table width="100%"><tr><td><i><b>', date('F Y', $monthStart), '</b></i></td><td align="right">';
+ foreach($rangeYears as $rangeYear => $rangeMonths)
+ {
+ if($rangeYear == $year)
+ echo, $rangeYear, ' ';
+ else
+ echo '<a href="frame-archive.php?GLSAYear=', $rangeYear, '&GLSAMonth=', $month, '">', $rangeYear, '</a> ';
+ }
+ echo '</td></tr></table></td></tr>';
+ for ($i = -1; $i < 5; $i++)
+ {
+ if($i == -1)
+ {
+ $nStamp = stepDay($monthStart, -1);
+ echo '<tr bgcolor="#CCCCCC"><td rowspan="6">';
+ if(@in_array(date('m', $nStamp), $rangeYears[date('Y', $nStamp)]))
+ echo '<a href="frame-archive.php?GLSAYear=', date('Y', $nStamp), '&GLSAMonth=', date('m', $nStamp), '">&lt;&lt;</a>';
+ echo '</td>';
+ }
+ else
+ echo '<tr>';
+
+ for ($ii = 0; $ii < 7; $ii++)
+ {
+ if($i == -1)
+ {
+ echo '<td align="center" width="14%">';
+ echo '<font color="#222222"><b>', date('D', $iStamp), '</b></font>';
+ }
+ else
+ {
+ echo '<td width="14%" valign="top" style="border: 1px solid gray;"><table height="55px" width="100%" cellspacing="0" cellpadding="1"><tr><td valign="top"><font size="+1">';
+ $stampData = getdate($iStamp);
+ if($stampData['mon'] != $month)
+ echo '<font color="#666666"><i>', date('d', $iStamp), '</i></font>';
+ else
+ echo date('d', $iStamp);
+ echo '</font></td><td align="right" valign="top">';
+ if(count($GLSAs[$iStamp]) > 0)
+ {
+ foreach($GLSAs[$iStamp] as $GLSAID)
+ {
+ echo '<a href="frame-view.php?id=', $GLSAID, '" title="', $GLSAShortSummaries[$GLSAID], '">', $GLSAID, '</a><br>';
+ }
+ }
+ echo '</td></tr></table>';
+ }
+ $iStamp = stepDay($iStamp);
+ echo '</td>';
+ }
+ if($i == -1)
+ {
+ $iStamp = $tStamp;
+ $nStamp = stepDay(mktime(0, 0, 0, $month, date('t', $monthStart), $year));
+ echo '<td rowspan="6">';
+ if(@in_array(date('m', $nStamp), $rangeYears[date('Y', $nStamp)]))
+ echo '<a href="frame-archive.php?GLSAYear=', date('Y', $nStamp), '&GLSAMonth=', date('m', $nStamp), '">&gt;&gt;</a>';
+ echo '</td>';
+ }
+ }
+?>
+ </tr>
+ <tr><td bgcolor="#CCCCCC" colspan="9" align="right"><i>
+<?
+ if(@count($rangeYears[$year]) > 0)
+ {
+ foreach($rangeYears[$year] as $rangeMonth)
+ {
+ if($rangeMonth == $month)
+ echo date('F', mktime(0, 0, 0, $rangeMonth, 1, $year)), ' ';
+ else
+ echo '<a href="frame-archive.php?GLSAYear=', $year, '&GLSAMonth=', $rangeMonth, '">', date('F', mktime(0, 0, 0, $rangeMonth, 1, $year)), '</a> ';
+ }
+
+ }
+?>
+ </i></td></tr>
+ </table></td></tr>
+ </table>
+ <br>
+ <? bodyHeader_invoke(); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-body.php b/frame-body.php
new file mode 100644
index 0000000..4adbadc
--- /dev/null
+++ b/frame-body.php
@@ -0,0 +1,170 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/xml.glsaparser';
+
+initBegin('Welcome');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Welcome to Gentoo GLSAMaker</i></b></font><hr><img src="images/gentooLogo.<? generateImageExt(); ?>" align="right" style="padding-bottom: 6px; padding-left: 6px;" alt="Gentoo Logo"><div><dl><dt></dt><dd><? generateKonqBreak(); ?> Security is a primary focus of Gentoo Linux and ensuring the confidentiality and security of our users' machines is of utmost importance to us.<br><br>The Gentoo Linux Security Response team is tasked with providing timely information about security vulnerabilities in Gentoo Linux, along with patches to secure those vulnerabilities. We work directly with vendors, end users and other OSS programs to ensure all security incidents are responded to quickly and professionally.<? generateKonqBreak();generateKonqBreak(''); ?> </dd></dl></div></td></tr>
+ </table>
+ <br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Pooled Gentoo GLSA Announcements</i></b></font></td><td align="right"><img src="images/icons/queue.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</tr><tr><td colspan="2"><hr></td></tr>
+ <?
+ $recentItems = fileGrepper_parsePool();
+ for ($i = 0; $i < count($recentItems); $i++) {
+ $GLSABReady = false;
+ $GLSAMSubmitter = '???';
+ $GLSALReviewCounter = '-';
+ $GLSATReviewCounter = 0;
+ $GLSAUReviewed = 0;
+
+ echo '<tr><td colspan="2"><table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr>';
+ echo '<td width="250" align="center" class="monospace"><a href="frame-view.php?id=', $recentItems[$i][0], '">', $recentItems[$i][0], '</a></td>';
+ echo '<td width="150">', date('Y-m-d H:i:s', $recentItems[$i][1]), '</td><td width="32">';
+
+ $output = fileGrepper_getGLSAText(NULL, NULL, NULL, $recentItems[$i][0]);
+ $Parser = new GLSAParser();
+
+ if($output != '<nodata>')
+ {
+ $Parser->GLSAparse($output, true, true);
+ $header = $Parser->GLSAShortSummary;
+
+ // Set submitter
+ if(is_array($GLSAMAuthor =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'submitter')))
+ $GLSAMSubmitter = trim($GLSAMAuthor['cdata']);
+
+ // Check for reviews
+ if(is_array($GLSAReviews =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'reviews')))
+ {
+ $GLSALReviewCounter = 0;
+ foreach($GLSAReviews['data'] as $GLSAMetadataItem)
+ {
+ $GLSACRevision = -1;
+ $GLSALReviewBuffer = false;
+ if(is_array($GLSAMetadataItem['data']) && count($GLSAMetadataItem['data']) > 0)
+ {
+ foreach($GLSAMetadataItem['data'] as $GLSAReview)
+ {
+ if($GLSAReview['tag'] == 'reviewApproval' || $GLSAReview['tag'] == 'reviewRejection' || $GLSAReview['tag'] == 'reviewComment'
+ && $GLSAMSubmitter != $GLSAMetadataItem['author'])
+ {
+ $GLSATReviewCounter++;
+
+ if($GLSAMetadataItem['author'] == authGetStatus())
+ $GLSAUReviewed = 1;
+
+ if($GLSAReview['tag'] == 'reviewApproval')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = true;
+ }
+ else if($GLSAReview['tag'] == 'reviewRejection')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = false;
+ }
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSACRevision = $GLSAReview['revision'];
+ }
+ }
+ if($GLSALReviewBuffer == true && $GLSAUReviewed > 0)
+ $GLSAUReviewed = 2;
+ $GLSALReviewCounter += $GLSALReviewBuffer;
+ }
+ }
+ }
+
+ // Check for a bugReady flag
+ if(is_array($Parser->searchMetadata($Parser->GLSAMetadata, 0, 'bugReady')))
+ $GLSABReady = true;
+ }
+ else { $header = '<i>Not available!</i>'; }
+ echo '<img align="left" src="images/icons/flag';
+ if(preg_match('/Fight[ -]?Club/i', $header) || substr($header, 0, 9) == 'Request: ')
+ echo '_star';
+ else if($GLSALReviewCounter == '-' && $GLSATReviewCounter == 0)
+ {
+ $GLSATReviewCounter = 'No';
+ echo '_disabled';
+ }
+ else if($GLSALReviewCounter >= $GLSAVNeededReviews)
+ echo '_green';
+ else if($GLSALReviewCounter > 0)
+ echo '_yellow';
+ echo '.';
+ generateImageExt();
+ echo '" title="', $GLSATReviewCounter, ' comment';
+ if($GLSATReviewCounter != 1)
+ echo 's';
+ if($GLSALReviewCounter > 0)
+ echo ' - ', $GLSALReviewCounter, ' review';
+ if($GLSALReviewCounter > 1)
+ echo 's';
+ if($GLSABReady)
+ echo ', marked as bug-ready';
+ echo '..."';
+ if($GLSABReady)
+ echo ' style="background: #dddddd; border: 1px solid #aaaaaa;"';
+ echo '></td><td width="32">';
+ if($GLSAMSubmitter != authGetStatus() && substr($header, 0, 9) != 'Request: ')
+ {
+ echo '<img src="images/icons/';
+ if($GLSAUReviewed == 0)
+ echo 'cross';
+ else if($GLSAUReviewed == 1)
+ echo 'tick_p';
+ else
+ echo 'tick';
+ echo '.';
+ generateImageExt();
+ echo '" title="';
+ if($GLSAUReviewed == 0)
+ echo 'Not reviewed';
+ else if($GLSAUReviewed == 1)
+ echo 'Reviewed as not positive';
+ else
+ echo 'Reviewed';
+ echo ' by you...">';
+ }
+ echo '</td><td><b>', $GLSAMSubmitter, '</b>: ', $header, '.</td></tr></table>';
+ }
+
+ ?>
+ </td></tr>
+ </table>
+ <br>
+ <? bodyHeader_invoke(true); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-edit.php b/frame-edit.php
new file mode 100644
index 0000000..f986e86
--- /dev/null
+++ b/frame-edit.php
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
+<html>
+<head>
+ <title>Gentoo Security - GLSA Edit</title>
+</head>
+<frameset rows="75%, *" name="frameSet">
+ <frame name="topFrame" src="frame-new.php<? if($HTTP_GET_VARS['editGLSA'] != '') { echo '?editGLSA=', $HTTP_GET_VARS['editGLSA']; } ?>">
+ <frame name="bottomFrame" src="frame-view.php<? if($HTTP_GET_VARS['editGLSA'] != '') { echo '?reviewsOnly&id=', $HTTP_GET_VARS['editGLSA']; } ?>">
+</frameset>
+</html>
diff --git a/frame-fetch.php b/frame-fetch.php
new file mode 100644
index 0000000..2ce8ecc
--- /dev/null
+++ b/frame-fetch.php
@@ -0,0 +1,40 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/io.filegrepper';
+
+$ID='<novalue>';
+foreach($HTTP_GET_VARS as $key => $value)
+ { if($key == 'id' || $key == 'glsa') $ID = $value; }
+
+if(sscanf($ID, '%04d%02d-%d', $ID1, $ID2, $ID3) == 3){ $valid = true; }
+if(preg_match('/^[A-F0-9]{32}$/i', $ID)){ $valid = true; $pool = true; }
+
+if($ID == '<novalue>'){ echo 'No GLSA ID has been specified.'; die(); }
+else if ( $valid == false ) { echo 'Invalid GLSA ID [` ', $ID, ' `] specified!'; die(); }
+
+header('Content-Disposition: attachment; filename="glsa-'.$ID.'.xml"');
+echo fileGrepper_getGLSAText( $pool ? NULL: $ID1, $pool ? NULL: $ID2, $pool ? NULL: $ID3, $pool ? $ID : NULL);
+
+?>
diff --git a/frame-left-collapsed.php b/frame-left-collapsed.php
new file mode 100644
index 0000000..47d4760
--- /dev/null
+++ b/frame-left-collapsed.php
@@ -0,0 +1,21 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>Gentoo Security</title>
+ <link rel="stylesheet" type="text/css" href="css/frames.css">
+</head>
+
+<? require_once './includes/ui.body'; ?>
+<body bgcolor="#BBBBBB" style="margin: 2px;" background="images/bg-grid-dark.gif" onClick="revertFrame();">
+<script>
+function revertFrame()
+{
+ document.location.href = "frame-left.php";
+ parent.document.body.cols = "230, *";
+}
+</script>
+<span style="position: absolute; bottom: 4px; left: 6px; line-height: 14px; font-size: 12px; text-align: center; font-family: monospace;">
+ <b>&lt;<br>&lt;<br>&lt;<br>&nbsp;<br><i>R<br>e<br>s<br>t<br>o<br>r<br>e</i></b>
+</span>
+</body>
+</html>
diff --git a/frame-left.php b/frame-left.php
new file mode 100644
index 0000000..1210c1f
--- /dev/null
+++ b/frame-left.php
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<html>
+<head>
+ <title>Gentoo Security</title>
+ <link rel="stylesheet" type="text/css" href="css/frames.css">
+</head>
+
+<body bgcolor="#739DD1" onLoad="revertFrame();">
+<? require_once './includes/ui.body'; ?>
+<script>
+function collapseFrame()
+{
+ parent.document.body.cols = "20, *";
+}
+function revertFrame()
+{
+ /* If somebody hits "Back" the normal width is restored */
+ parent.document.body.cols = "230, *";
+}
+</script>
+<table width="100%" border="0" cellspacing="0" cellpadding="0">
+ <tr>
+ <td align="center"><img src="images/glsaLogo.<? generateImageExt(); ?>" alt=""><br><font color="#FFFFFF"><br>Welcome to Gentoo Security<br><i>http://glsa.gentoo.org</i><br><br></font></td>
+ </tr>
+ <tr>
+ <td valign="middle" align="right">
+ <p class="frame_nav_item">
+ <a target="rightFrame" href="frame-body.php" class="frame_nav_item">
+ <img src="images/icons/view_text.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;News
+ </a>
+ </p>
+ <p class="frame_nav_item">
+ <a target="rightFrame" href="frame-recent.php" class="frame_nav_item">
+ <img src="images/icons/queue.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Announcement pool
+ </a>
+ </p>
+ <p class="frame_nav_item">
+ <a href="frame-new.php" target="rightFrame" class="frame_nav_item">
+ <img src="images/icons/new.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;File an announcement
+ </a>
+ </p>
+<!-- <p class="frame_nav_item">
+ <a href="" class="frame_nav_item">
+ <img src="images/icons/query.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Query announcements
+ </a>
+ </p> -->
+ <p class="frame_nav_item">
+ <a target="rightFrame" href="frame-stats.php" class="frame_nav_item">
+ <img src="images/icons/reporting.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Announcement stats
+ </a>
+ </p>
+
+ <p class="frame_nav_item">
+ <a href="frame-left-collapsed.php" class="frame_nav_item" onClick="collapseFrame()">
+ <img src="images/icons/back.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Collapse this frame
+ </a>
+ </p>
+ <p class="frame_nav_item">
+ <a href="" class="frame_nav_item">
+ <img src="images/icons/help.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Help
+ </a>
+ </p>
+ </td>
+ </tr>
+</table>&nbsp;
+</body>
+</html>
diff --git a/frame-new.php b/frame-new.php
new file mode 100644
index 0000000..cf9a63f
--- /dev/null
+++ b/frame-new.php
@@ -0,0 +1,667 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require './includes/common.auth';
+require_once './includes/common.diff';
+require_once './includes/common.spell';
+require_once './includes/io.mailer';
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/ui.newitems';
+require_once './includes/xml.glsaparser';
+
+$GLSAProcessed = array();
+$GLSAReferences = array();
+$GLSAVersions = array();
+
+if($HTTP_GET_VARS['editGLSA'] != '')
+{
+ $ID = $HTTP_GET_VARS['editGLSA'];
+ $startEdit = true;
+}
+else if($HTTP_GET_VARS['reGLSA'] != '')
+{
+ $ID = $HTTP_GET_VARS['reGLSA'];
+ $reEdit = true;
+}
+else if($HTTP_POST_VARS['GLSA_ID'] != '')
+{
+ $ID = $HTTP_POST_VARS['GLSA_ID'];
+ $continueEdit = true;
+}
+
+if($ID)
+{
+ if(sscanf($ID, '%04d%02d-%d', $GID1, $GID2, $GID3) == 3){ $validEdit = true; }
+ else if(preg_match('/^[A-F0-9]{32}$/i', $ID)){ $validEdit = true; $pool = true; }
+}
+
+$Parser = new GLSAParser();
+if($validEdit)
+{
+ $GLSA = fileGrepper_getGLSAText($pool ? NULL: $GID1, $pool ? NULL: $GID2, $pool ? NULL: $GID3, $pool ? $ID : NULL);
+ $Parser->GLSAparse($GLSA);
+
+ if($startEdit || $reEdit)
+ {
+ $HTTP_POST_VARS['GLSA_Title'] = $Parser->GLSAShortSummary;
+ $HTTP_POST_VARS['GLSA_Access'] = ucfirst($Parser->GLSAAccess);
+ $HTTP_POST_VARS['GLSA_Priority'] = $Parser->GLSASeverity;
+ $HTTP_POST_VARS['GLSA_Synopsis'] = $Parser->GLSASynopsis;
+ $HTTP_POST_VARS['GLSA_Bugs'] = implode(' ', $Parser->GLSABugs);
+ $HTTP_POST_VARS['GLSA_Product'] = $Parser->GLSAProduct;
+ $HTTP_POST_VARS['GLSA_ProductType'] = $Parser->GLSAProductType;
+ $GLSAVersions = $Parser->GLSAPackages;
+ $HTTP_POST_VARS['GLSA_Background'] = reformatString($Parser->GLSABackground, 2, 0);
+ $HTTP_POST_VARS['GLSA_Description'] = reformatString($Parser->GLSADescription, 2, 0);
+ $HTTP_POST_VARS['GLSA_Impact'] = reformatString($Parser->GLSAImpact, 2, 0);
+ $HTTP_POST_VARS['GLSA_Workaround'] = reformatString($Parser->GLSAWorkaround, 2, 0);
+ $HTTP_POST_VARS['GLSA_Resolution'] = reformatString($Parser->GLSAResolution, 2, 0);
+ foreach( $Parser->GLSAReferences as $GLSAReferenceURL => $GLSAReference)
+ {
+ $GLSAReferences[] = array($GLSAReference => $GLSAReferenceURL);
+ }
+ }
+}
+
+if($reEdit)
+ $validEdit = false;
+
+initBegin($validEdit ? 'Edit a GLSA' : 'File an announcement');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i><? if($validEdit){ echo 'Edit a GLSA Announcement'; } else { echo 'File a new GLSA Announcement'; } ?></i></b></font></td><td align="right"><img src="images/icons/new.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</td></tr><tr><td colspan="2"><hr></td></tr>
+ <tr><td colspan="2"><table width="100%" border="0" cellspacing="0" cellpadding="4"><tr><td colspan="2">Our GLSA filing process is designed to improve QA speed and security performance. Please file all announcements in good, detailed English and try to be verbose where you can. If you have any queries, ask #gentoo-security. Thank you for your help in making a more secure Gentoo!<br><br></td></tr><? if(!$validEdit){ ?><tr><td valign="top"><b>Important: </b></td><td>Please check if your announcement is already in the pending queue or has already been released before filing any more announcements. Also note, that these announcements <b>must</b> be for a valid package in Portage.</td></tr><? } ?><tr><td valign="top"><b>Important: </b></td><td>For legal reasons, please do not copy and paste text from copyrighted sources without giving references; if you do make sure a reference is added as well as a reference pointer near the copyrighted text. Also make sure that the license on the copyrighted text allows copying.</td></tr></table><br></td></tr>
+ </table>
+ <br>
+ <? if(!$validEdit)
+ {
+ recentItems_invoke(true); ?>
+ <br>
+ <? }
+ if($HTTP_POST_VARS == array()) { echo generateInfo('Please complete all the fields which are marked in <i>italics</i>. To insert a new paragraph, use a double linebreak. To insert a preformatted code fragment, wrap your text in <span class="monospace">&lt;code&gt;</span> tags. Hover your mouse over a help icon for help with that particular field.<br><br>All other fields are optional, but recommended. <i>You may need to resize your browser window width to make this form functional.</i>'); } ?>
+ <?
+ function array_combine_recursive(&$into, $source)
+ {
+ $MergeMap = array_keys($source);
+ $MergeMap = array_flip($MergeMap);
+ foreach( $MergeMap as $key => $value ){ $MergeMap[$key] = -1; }
+
+ foreach( $into as $key => $value )
+ {
+ foreach( $source as $skey => $svalue )
+ {
+// echo('<pre>!!! '); print_r($into); print_r($source); print_r($MergeMap); echo('!!!</pre>');
+ if($value['arch'] == $svalue['arch'] && $value['name'] == $svalue['name'])
+ {
+ if(isset($value['unaffected']) && isset($svalue['unaffected']))
+ {
+ $into[$key]['unaffected'] = array_merge_recursive($value['unaffected'], $svalue['unaffected']);
+ }
+ elseif(isset($svalue['unaffected']))
+ {
+ $into[$key]['unaffected'] = $svalue['unaffected'];
+ }
+ if(isset($value['vulnerable']) && isset($svalue['vulnerable']))
+ {
+ $into[$key]['vulnerable'] = array_merge_recursive($value['vulnerable'], $svalue['vulnerable']);
+ }
+ elseif(isset($svalue['vulnerable']))
+ {
+ $into[$key]['vulnerable'] = $svalue['vulnerable'];
+ }
+
+ // This sets 'auto' to 'no' if unless both are 'yes'
+ $value['auto'] = SBMod(SBMod($value['auto']) & SBMod($svalue['auto']));
+ $MergeMap[$skey] = $key;
+ }
+ }
+ }
+ foreach( $MergeMap as $key => $value )
+ {
+ if($value == -1)
+ {
+ array_push($into, $source[$key]);
+ }
+ }
+// echo('<pre>!!! '); print_r($into); echo('!!!</pre>');
+ }
+
+ function array_uniqueArray($input)
+ {
+ $newArray = array();
+ foreach($input as $search)
+ {
+ if(!in_array($search, $newArray))
+ $newArray[] = $search;
+ }
+ return $newArray;
+ }
+
+ if(!$startEdit)
+ {
+ foreach($HTTP_POST_VARS as $key => $value)
+ {
+ $HTTP_POST_VARS[$key] = stripslashes($value);
+ }
+ }
+
+ foreach($HTTP_POST_VARS as $key => $value)
+ {
+ if(sscanf($key, 'GLSA_UP_%d_%d_', $ID1, $ID2) == 2)
+ {
+ if($GLSAProcessed['UP'.$ID1.$ID2] != true)
+ {
+ if($HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Name'] != '' && $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_VXP'] != '' && $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Version'] != '' &&
+ $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Arch'] != '')
+ {
+ $GLSAVersions_Add = array();
+ $GLSAVersions_Add[0]['arch'] = str_replace('all', '*', $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Arch']);
+ $GLSAVersions_Add[0]['auto'] = $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Auto'];
+ $GLSAVersions_Add[0]['name'] = $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Name'];
+ $GLSAVersions_Add[0]['unaffected'] = array(0 => array($HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_VXP'] => $HTTP_POST_VARS['GLSA_UP_'.$ID1.'_'.$ID2.'_Version']));
+
+ array_combine_recursive(&$GLSAVersions, $GLSAVersions_Add);
+// echo('<pre>!!!* '); print_r($GLSAVersions); echo('!!!</pre>');
+ }
+ $GLSAProcessed['UP'.$ID1.$ID2] = true;
+ }
+ }
+ if(sscanf($key, 'GLSA_VP_%d_%d_', $ID1, $ID2) == 2)
+ {
+ if($GLSAProcessed['VP'.$ID1.$ID2] != true)
+ {
+ if($HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Name'] != '' && $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_VXP'] != '' && $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Version'] != '' &&
+ $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Arch'] != '')
+ {
+ $GLSAVersions_Add = array();
+ $GLSAVersions_Add[0]['arch'] = str_replace('all', '*', $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Arch']);
+ $GLSAVersions_Add[0]['auto'] = $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Auto'];
+ $GLSAVersions_Add[0]['name'] = $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Name'];
+ $GLSAVersions_Add[0]['vulnerable'] = array(0 => array($HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_VXP'] => $HTTP_POST_VARS['GLSA_VP_'.$ID1.'_'.$ID2.'_Version']));
+
+ array_combine_recursive($GLSAVersions, $GLSAVersions_Add);
+ }
+ $GLSAProcessed['VP'.$ID1.$ID2] = true;
+ }
+ }
+ if(sscanf($key, 'GLSA_RF_%d_', $ID1) == 1)
+ {
+ if($GLSAProcessed['RF'.$ID1] != true)
+ {
+ if($HTTP_POST_VARS['GLSA_RF_'.$ID1.'_Title'] != '' && $HTTP_POST_VARS['GLSA_RF_'.$ID1.'_URL'] != '')
+ {
+ array_push($GLSAReferences, array($HTTP_POST_VARS['GLSA_RF_'.$ID1.'_Title'] => $HTTP_POST_VARS['GLSA_RF_'.$ID1.'_URL']));
+ }
+ $GLSAProcessed['RF'.$ID1] = true;
+ }
+ }
+ }
+
+ if(substr_count($HTTP_POST_VARS['GLSA_UP_Name'], '/') == 1 && $HTTP_POST_VARS['GLSA_UP_VXP'] != '' && $HTTP_POST_VARS['GLSA_UP_Version'] != '' &&
+ $HTTP_POST_VARS['GLSA_UP_Arch'] != '' && $HTTP_POST_VARS['GLSA_UP_Auto'] != '')
+ {
+ $GLSAVersions_Add = array();
+ $GLSAVersions_Add[0]['arch'] = str_replace('all', '*', strtolower($HTTP_POST_VARS['GLSA_UP_Arch']));
+ $GLSAVersions_Add[0]['auto'] = strtolower($HTTP_POST_VARS['GLSA_UP_Auto']);
+ $GLSAVersions_Add[0]['name'] = strtolower($HTTP_POST_VARS['GLSA_UP_Name']);
+ $GLSAVersions_Add[0]['unaffected'] = array(0 => array(strtolower($HTTP_POST_VARS['GLSA_UP_VXP']) => strtolower($HTTP_POST_VARS['GLSA_UP_Version'])));
+
+ array_combine_recursive(&$GLSAVersions, $GLSAVersions_Add);
+// echo('<pre>!!!* '); print_r($GLSAVersions); echo('!!!</pre>');
+ }
+ else if(($HTTP_POST_VARS['GLSA_UP_Name'] != '' && substr_count($HTTP_POST_VARS['GLSA_UP_Name'], '/') != 1) || $HTTP_POST_VARS['GLSA_UP_Version'] != '' || $HTTP_POST_VARS['GLSA_UP_Arch'] != '')
+ {
+ if(substr_count($HTTP_POST_VARS['GLSA_UP_Name'], '/') != 1)
+ echo generateWarning('The entered "unaffected" version expression was ignored - a valid package name such as "net-www/mozilla" is required!');
+ else if($HTTP_POST_VARS['GLSA_UP_Version'] == '')
+ echo generateWarning('The entered "unaffected" version expression was ignored - a package version is required!');
+ else if($HTTP_POST_VARS['GLSA_UP_Arch'] == '')
+ echo generateWarning('The entered "unaffected" version expression was ignored - a package architecture is required!');
+ }
+
+ if(substr_count($HTTP_POST_VARS['GLSA_VP_Name'], '/') == 1 && $HTTP_POST_VARS['GLSA_VP_VXP'] != '' && $HTTP_POST_VARS['GLSA_VP_Version'] != '' &&
+ $HTTP_POST_VARS['GLSA_VP_Arch'] != '' && $HTTP_POST_VARS['GLSA_UP_Auto'] != '')
+ {
+ $GLSAVersions_Add = array();
+ $GLSAVersions_Add[0]['arch'] = str_replace('all', '*', strtolower($HTTP_POST_VARS['GLSA_VP_Arch']));
+ $GLSAVersions_Add[0]['auto'] = strtolower($HTTP_POST_VARS['GLSA_VP_Auto']);
+ $GLSAVersions_Add[0]['name'] = strtolower($HTTP_POST_VARS['GLSA_VP_Name']);
+ $GLSAVersions_Add[0]['vulnerable'] = array(0 => array(strtolower($HTTP_POST_VARS['GLSA_VP_VXP']) => strtolower($HTTP_POST_VARS['GLSA_VP_Version'])));
+
+ array_combine_recursive(&$GLSAVersions, $GLSAVersions_Add);
+ }
+ else if(($HTTP_POST_VARS['GLSA_VP_Name'] != '' && substr_count($HTTP_POST_VARS['GLSA_VP_Name'], '/') != 1) || $HTTP_POST_VARS['GLSA_VP_Version'] != '' || $HTTP_POST_VARS['GLSA_VP_Arch'] != '')
+ {
+ if(substr_count($HTTP_POST_VARS['GLSA_VP_Name'], '/') != 1)
+ echo generateWarning('The entered "vulnerable" version expression was ignored - a valid package name such as "net-www/mozilla" is required!');
+ else if($HTTP_POST_VARS['GLSA_VP_Version'] == '')
+ echo generateWarning('The entered "vulnerable" version expression was ignored - a package version is required!');
+ else if($HTTP_POST_VARS['GLSA_VP_Arch'] == '')
+ echo generateWarning('The entered "vulnerable" version expression was ignored - a package architecture is required!');
+ }
+
+ if($HTTP_POST_VARS['GLSA_RF_Title'] != '' && $HTTP_POST_VARS['GLSA_RF_URL'] != '')
+ {
+ array_push($GLSAReferences, array($HTTP_POST_VARS['GLSA_RF_Title'] => $HTTP_POST_VARS['GLSA_RF_URL']));
+ }
+ else if($HTTP_POST_VARS['GLSA_RF_Title'] != '' || $HTTP_POST_VARS['GLSA_RF_URL'] != '')
+ {
+ if($HTTP_POST_VARS['GLSA_RF_Title'] == '')
+ echo generateWarning('The entered reference was ignored - a reference title is required!');
+ else if($HTTP_POST_VARS['GLSA_RF_URL'] == '')
+ {
+ if(strpos($HTTP_POST_VARS['GLSA_RF_Title'], 'CVE-') === 0)
+ array_push($GLSAReferences, array($HTTP_POST_VARS['GLSA_RF_Title'] => 'http://cve.mitre.org/cgi-bin/cvename.cgi?name='.$HTTP_POST_VARS['GLSA_RF_Title']));
+ else
+ echo generateWarning('The entered reference was ignored - a reference URL is required!');
+ }
+ }
+
+ // echo('<pre>'); print_r($GLSAVersions); echo('</pre>');
+
+ /*! Remove duplicate entries from the versions */
+ foreach($GLSAVersions as $key => $value)
+ {
+ if(isset($value['unaffected']))
+ $GLSAVersions[$key]['unaffected'] = array_uniqueArray($GLSAVersions[$key]['unaffected']);
+ if(isset($value['vulnerable']))
+ $GLSAVersions[$key]['vulnerable'] = array_uniqueArray($value['vulnerable']);
+ }
+
+ /*! If we're submitting, check things, and continue if OK */
+ $newLine = "\n";
+ if($HTTP_POST_VARS['Submit'] == 'Boilerplate')
+ {
+ $haveFixedVersion = false;
+ if(count($GLSAVersions) > 0)
+ {
+ foreach($GLSAVersions as $GLSAVersion)
+ {
+ if(count($GLSAVersion['unaffected']) > 0 && count($GLSAVersion['vulnerable']) > 0)
+ {
+ $haveFixedVersion = true;
+ break;
+ }
+ }
+ }
+ if($haveFixedVersion)
+ {
+ $tmp = explode('/', $GLSAVersion['name']);
+ $tmpV = current($GLSAVersion['unaffected']);
+ $tmpV = current($tmpV);
+
+ if(!$HTTP_POST_VARS['GLSA_Product'])
+ $HTTP_POST_VARS['GLSA_Product'] = $tmp[1];
+
+ if($HTTP_POST_VARS['GLSA_Workaround'])
+ $HTTP_POST_VARS['GLSA_Workaround'] .= $newLine.$newLine;
+ $HTTP_POST_VARS['GLSA_Workaround'] .= 'There is no known workaround at this time.';
+
+ if($HTTP_POST_VARS['GLSA_Resolution'])
+ $HTTP_POST_VARS['GLSA_Resolution'] .= $newLine.$newLine;
+ $HTTP_POST_VARS['GLSA_Resolution'] .= 'All '.$tmp[1].' users should upgrade to the latest version:'.$newLine.$newLine.'<code>'.$newLine.'# emerge --sync'.$newLine.'# emerge --ask --oneshot --verbose ">='.$GLSAVersion['name'].'-'.$tmpV.'"</code>';
+ } else
+ echo generateWarning('Not all fields are completed - please make sure you have completed all the necessary fields!');
+ }
+ else if($HTTP_POST_VARS['Submit'] == 'Preview' || $HTTP_POST_VARS['Submit'] == 'Confirm' || $HTTP_POST_VARS['Submit'] == 'Submit')
+ {
+ if($HTTP_POST_VARS['GLSA_Title'] != '' && $HTTP_POST_VARS['GLSA_Synopsis'] != '' && $HTTP_POST_VARS['GLSA_Priority'] != '' &&
+ $HTTP_POST_VARS['GLSA_Product'] != '' && $HTTP_POST_VARS['GLSA_ProductType'] != '' && $HTTP_POST_VARS['GLSA_Impact'] != '' &&
+ $HTTP_POST_VARS['GLSA_Description'] != '' && $HTTP_POST_VARS['GLSA_Workaround'] != '' && $HTTP_POST_VARS['GLSA_Resolution'] != '')
+ {
+ if($HTTP_POST_VARS['Submit'] == 'Confirm' || $HTTP_POST_VARS['Submit'] == 'Preview')
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable"><tr><td>';
+
+ $HTTP_POST_VARS['GLSA_ID'] ? $Parser->GLSAID = $HTTP_POST_VARS['GLSA_ID'] : $Parser->GLSAID = '!-- Insert ID here! --!';
+ $Parser->GLSAShortSummary = $HTTP_POST_VARS['GLSA_Title'];
+ $Parser->GLSASynopsis = $HTTP_POST_VARS['GLSA_Synopsis'];
+
+ $Parser->GLSAProductType = $HTTP_POST_VARS['GLSA_ProductType'];
+ $Parser->GLSAProduct = rtrim(htmlspecialchars($HTTP_POST_VARS['GLSA_Product']));
+
+ if($Parser->GLSARevision == '')
+ $Parser->GLSARevision = date('F d, Y').': 01';
+ if($Parser->GLSADate == '')
+ $Parser->GLSADate = date('F d, Y');
+
+ $Parser->GLSABugs = array();
+ $temp = explode(' ', $HTTP_POST_VARS['GLSA_Bugs']);
+ $HTTP_POST_VARS['GLSA_Bugs'] = '';
+ foreach( $temp as $bug ){ if(is_numeric($bug)) { $Parser->GLSABugs[] = $bug; $HTTP_POST_VARS['GLSA_Bugs'] .= $bug.' '; } }
+
+ if($HTTP_POST_VARS['GLSA_Access'])
+ {
+ $Parser->GLSAAccess = rtrim(strtolower($HTTP_POST_VARS['GLSA_Access']));
+ }
+
+ $Parser->GLSAPackages = $GLSAVersions;
+
+ if($HTTP_POST_VARS['GLSA_Background'])
+ $Parser->GLSABackground = paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Background']);
+
+ $Parser->GLSADescription = paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Description']);
+ $Parser->GLSASeverity = $HTTP_POST_VARS['GLSA_Priority'];
+ $Parser->GLSAImpact = paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Impact']);
+
+ $Parser->GLSAWorkaround = paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Workaround']);
+ $Parser->GLSAResolution = paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Resolution']);
+// echo 'Debug:';
+// echo paragraphifyFromPlain($HTTP_POST_VARS['GLSA_Resolution']), '###';
+
+ $Parser->GLSAReferences = array();
+ if(count($GLSAReferences) > 0)
+ {
+ foreach( $GLSAReferences as $reference )
+ {
+ $Parser->GLSAReferences[$reference[key($reference)]] = key($reference);
+ }
+ }
+
+ foreach($Parser->GLSAMetadata as $MetadataItem)
+ {
+ if($MetadataItem['tag'] == 'submitter')
+ $haveSubmitter = true;
+ }
+ if(!$haveSubmitter)
+ $Parser->GLSAMetadata[] = array('data' => array(), 'parent' => '', 'tag' => 'submitter', 'cdata' => authGetStatus(), 'timestamp' => genMetadataTimestamp());
+
+ $canSubmit = true;
+
+ if(($HTTP_POST_VARS['Submit'] == 'Preview' || $HTTP_POST_VARS['Submit'] == 'Confirm') && $validEdit && $continueEdit)
+ {
+ diffHighlight(diffStringToFile($Parser->GLSAToXML(), $GLSALocation = fileGrepper_getGLSAText($pool ? NULL: $GID1, $pool ? NULL: $GID2, $pool ? NULL: $GID3, $pool ? $ID : NULL, true)), $GLSALocation.'.new');
+
+ if($HTTP_POST_VARS['SpellMode'])
+ {
+ $speller = new Speller();
+ echo $newLine, '<pre>', $speller->HighlightSpell($Parser->GLSAToText()), '</pre>';
+ }
+
+ echo '</td></tr></table><br>';
+ }
+ else if($HTTP_POST_VARS['Submit'] == 'Confirm')
+ {
+ echo '<pre>'.$newLine, htmlspecialchars($Parser->GLSAToXML()), '</pre></td></tr></table><br>';
+ }
+ else if($HTTP_POST_VARS['Submit'] == 'Preview')
+ {
+ $speller = new Speller();
+ echo '<pre>'.$newLine, $speller->HighlightSpell($Parser->GLSAToText(true)), '</pre></td></tr></table><br>';
+ }
+ else if($HTTP_POST_VARS['Submit'] == 'Submit' && $validEdit && $continueEdit)
+ {
+ $diff = diffStringToFile($Parser->GLSAToXML(false), $GLSALocation = fileGrepper_getGLSAText($pool ? NULL: $GID1, $pool ? NULL: $GID2, $pool ? NULL: $GID3, $pool ? $ID : NULL, true));
+ if(fileGrepper_getGLSAText($pool ? NULL: $GID1, $pool ? NULL: $GID2, $pool ? NULL: $GID3, $pool ? $ID : NULL, false, $Parser->GLSAToXML()))
+ generateInfo('Updated GLSA successfully committed (ID <a href="frame-view.php?id='.$ID.'">'.$ID.'</a>!');
+ else
+ generateWarning('Could not commit updated GLSA!');
+ mailGLSA('%'.$GLSAMAddress, 'GLSA Update: ['.authGetStatus(true).'] '.$Parser->GLSAShortSummary, $newLine.$diff, array(), 'In-Reply-To: <glsa-'.$ID.'@glsamaker.gentoo.org>');
+ }
+ else if($HTTP_POST_VARS['Submit'] == 'Submit')
+ {
+ if(($hash = fileGrepper_commitToPool($Parser->GLSAToXML(false), true)) != false)
+ echo generateInfo('GLSA successfully committed to the pool; ID #'.$hash);
+ else
+ echo generateWarning('Could not commit GLSA to pool!');
+
+ if($hash == false)
+ $hash = md5(uniqid(''));
+
+ $Parser->GLSAID = $hash;
+ if(mailGLSA('%'.$GLSAMAddress, 'GLSA Draft: ['.authGetStatus(true).'] '.$hash, $Parser->GLSAToText(true), array($hash.'.xml' => $newLine.$Parser->GLSAToXML(false))))
+ echo generateInfo('GLSA successfully queued with ID #<a href="frame-view.php?id='.$hash.'">'.$hash.'</a>...');
+ else
+ echo generateWarning('Could not mail GLSA!');
+ }
+ }
+ else
+ echo generateWarning('Not all fields are completed - please make sure you have completed all the necessary fields!');
+ }
+
+ ?>
+ <form action="frame-new.php" method="post">
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b><i>Title:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars($HTTP_POST_VARS['GLSA_Title']); ?>" name="GLSA_Title" class="grayinput_fullWidth" size="64"><td width="30" align="right"><? echo generateHelpIcon('Please enter a quick 4 to 5 word description.'); ?></td></tr></table></td></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b>Access:</b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars($HTTP_POST_VARS['GLSA_Access']); ?>" name="GLSA_Access" class="grayinput_fullWidth" size="64"></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a range of the vulnerability, e.g. local or remote.'); ?></td></tr></table></td><td align="right" width="100"><b><i>Severity:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><select name="GLSA_Priority" style="width:100%;"><option value="low" <? if($HTTP_POST_VARS['GLSA_Priority'] == 'low') echo 'selected="selected"'; ?>>Low</option><option value="normal" <? if($HTTP_POST_VARS['GLSA_Priority'] == 'normal') echo 'selected="selected"'; ?>>Normal</option><option value="high" <? if($HTTP_POST_VARS['GLSA_Priority'] == 'high') echo 'selected="selected"'; ?>>High</option></select></td><td width="30" align="right"><? echo generateHelpIcon('Please select the priority.'); ?></td></tr></table></td></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Synopsis:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Synopsis" class="grayinput_fullWidth" cols="64" rows="2"><? echo $HTTP_POST_VARS['GLSA_Synopsis']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a synopsis of the vulnerability which should be concise and short.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b>Bug IDs:</b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars(rtrim($HTTP_POST_VARS['GLSA_Bugs'], ' ')); ?>" name="GLSA_Bugs" class="grayinput_fullWidth" size="64"><td width="30" align="right"><? echo generateHelpIcon('Enter any relevant Gentoo Bugzilla Bug IDs separated by spaces.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b><i>GLSA Keyword:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars($HTTP_POST_VARS['GLSA_Product']); ?>" name="GLSA_Product" class="grayinput_fullWidth" size="64"></td><td width="30" align="right"><? echo generateHelpIcon('Please enter one keyword to define the issue, e.g. OpenSSL.'); ?></td></tr></table></td><td align="right" width="150"><b><i>GLSA Category:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><select name="GLSA_ProductType" style="width:100%;"><option value="ebuild" <? if($HTTP_POST_VARS['GLSA_ProductType'] == 'ebuild') echo 'selected="selected"'; ?>>Ebuild</option><option value="informational" <? if($HTTP_POST_VARS['GLSA_ProductType'] == 'informational') echo 'selected="selected"'; ?>>Informational</option><option value="infrastructure" <? if($HTTP_POST_VARS['GLSA_ProductType'] == 'infrastructure') echo 'selected="selected"'; ?>>Infrastructure</option><option value="portage" <? if($HTTP_POST_VARS['GLSA_ProductType'] == 'portage') echo 'selected="selected"'; ?>>Portage</option></select></td><td width="30" align="right"><? echo generateHelpIcon('Please select the category of this GLSA.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="5" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Unaffected packages:</i></b></td>
+ <td class="body_rootTable_center"><acronym title="Enter a valid Portage package name, such as 'net-www/mozilla'.">Name</acronym></td><td colspan="2" class="body_rootTable_center"><acronym title="Pick an operator from the combo box and enter the version in the text box.">Version</acronym></td><td class="body_rootTable_center"><acronym title="Use '*' for all architectures or specify a supported architecture.">Architecture</acronym></td><td class="body_rootTable_center"><acronym title="Can this package be automatically updated by Portage?">Auto</acronym></td><td></td><td></td></tr>
+ <? foreach( $GLSAVersions as $key => $value )
+ {
+ if(isset( $value['unaffected'] ))
+ {
+ foreach( $value['unaffected'] as $vkey => $vvalue )
+ {
+ // <mark/>
+ echo '<tr><td></td><td width="20%"><input type="text" name="GLSA_UP_', $key, '_', $vkey, '_Name" class="grayinput_fullWidth" value="', htmlspecialchars($value['name']), '"></td><td width="1%">';
+ echo '<select class="center" name="GLSA_UP_', $key, '_', $vkey, '_VXP">';
+
+ $kvalue = key($vvalue);
+ if($kvalue == 'ge')
+ echo '<option value="ge" selected="selected" class="ge">&gt;=</option>';
+ else
+ echo '<option value="ge" class="ge">&gt;=</option>';
+
+ if($kvalue == 'gt')
+ echo '<option value="gt" selected="selected" class="gt">&gt;</option>';
+ else
+ echo '<option value="gt" class="gt">&gt;</option>';
+
+ if($kvalue == 'rge')
+ echo '<option value="rge" selected="selected" class="rge">*&gt;=</option>';
+ else
+ echo '<option value="rge" class="rge">*&gt;=</option>';
+
+ if($kvalue == 'rgt')
+ echo '<option value="rgt" selected="selected" class="rgt">*&gt;</option>';
+ else
+ echo '<option value="rgt" class="rgt">*&gt;</option>';
+
+ if($kvalue == 'le')
+ echo '<option value="le" selected="selected" class="dtop">&lt;=</option>';
+ else
+ echo '<option value="le" class="dtop">&lt;=</option>';
+
+ if($kvalue == 'lt')
+ echo '<option value="lt" selected="selected">&lt;</option>';
+ else
+ echo '<option value="lt">&lt;</option>';
+
+ if($kvalue == 'rle')
+ echo '<option value="rle" selected="selected">*&lt;=</option>';
+ else
+ echo '<option value="rle">*&lt;=</option>';
+
+ if($kvalue == 'rlt')
+ echo '<option value="rlt" selected="selected">*&lt;</option>';
+ else
+ echo '<option value="rlt">*&lt;</option>';
+
+ if($kvalue == 'eq')
+ echo '<option value="eq" selected="selected" class="eq">==</option>';
+ else
+ echo '<option value="eq" class="eq">==</option>';
+
+ echo '</select></td><td><input type="text" name="GLSA_UP_', $key, '_', $vkey, '_Version" class="grayinput_fullWidth" value="', htmlspecialchars($vvalue[key($vvalue)]), '"></td><td width="20%"><input type="text" name="GLSA_UP_', $key, '_', $vkey, '_Arch" class="grayinput_fullWidth" value="', htmlspecialchars($value['arch']), '"></td><td width="1%"><select class="center" name="GLSA_UP_', $key, '_', $vkey, '_Auto">';
+ if($value['auto'] == 'yes')
+ echo '<option value="yes" selected="selected">Yes</option>';
+ else
+ echo '<option value="yes">Yes</option>';
+
+ if($value['auto'] == 'no')
+ echo '<option value="no" selected="selected">No</option>';
+ else
+ echo '<option value="no">No</option>';
+
+ echo '</select></td><td width="1%">', generateButton('<b>Change</b>'), '</td><td width="20" align="right">', generateHelpIcon('Modify any field and press \'Change\'. To delete, make any field empty.'), '</td></tr>';
+ }
+ }
+ }
+ ?>
+ <tr><td></td><td><input type="text" name="GLSA_UP_Name" class="grayinput_fullWidth" size="32"></td><td><select class="center" name="GLSA_UP_VXP"><option value="ge" selected="selected" class="ge">&gt;=</option><option value="gt" class="gt">&gt;</option><option value="rge" class="rge">*&gt;=</option><option value="rgt" class="rgt">*&gt;</option><option value="le" class="dtop">&lt;=</option><option value="lt">&lt;</option><option value="rle">*&lt;=</option><option value="rlt">*&lt;</option><option value="eq" class="eq">==</option></select></td><td><input type="text" name="GLSA_UP_Version" class="grayinput_fullWidth" size="32"></td><td><input type="text" name="GLSA_UP_Arch" class="grayinput_fullWidth" size="32"></td><td><select class="center" name="GLSA_UP_Auto"><option value="yes">Yes</option><option value="no">No</option></select></td><td><? echo generateButton('<b>Change</b>'); ?></td><td width="25" align="right"><? echo generateHelpIcon('Complete all three fields and press \'Change\'.'); ?></td>
+ </tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="5" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Vulnerable packages:</i></b></td>
+ <td class="body_rootTable_center"><acronym title="Enter a valid Portage package name, such as 'net-www/mozilla'.">Name</acronym></td><td colspan="2" class="body_rootTable_center"><acronym title="Pick an operator from the combo box and enter the version in the text box.">Version</acronym></td><td class="body_rootTable_center"><acronym title="Use '*' for all architectures or specify a supported architecture.">Architecture</acronym></td><td class="body_rootTable_center"><acronym title="Can this package be automatically updated by Portage?">Auto</acronym></td><td></td><td></td></tr>
+ <? foreach( $GLSAVersions as $key => $value )
+ {
+ if(isset( $value['vulnerable'] ))
+ {
+ foreach( $value['vulnerable'] as $vkey => $vvalue )
+ {
+ echo '<tr><td></td><td width="20%"><input type="text" name="GLSA_VP_', $key, '_', $vkey, '_Name" class="grayinput_fullWidth" value="', htmlspecialchars($value['name']), '"></td><td width="1%"><select class="center" name="GLSA_VP_', $key, '_', $vkey, '_VXP">';
+
+ $kvalue = key($vvalue);
+
+ if($kvalue == 'le')
+ echo '<option value="le" selected="selected" class="le">&lt;=</option>';
+ else
+ echo '<option value="le" class="le">&lt;=</option>';
+
+ if($kvalue == 'lt')
+ echo '<option value="lt" selected="selected" class="lt">&lt;</option>';
+ else
+ echo '<option value="lt" class="lt">&lt;</option>';
+
+ if($kvalue == 'rle')
+ echo '<option value="rle" selected="selected" class="rle">*&lt;=</option>';
+ else
+ echo '<option value="rle" class="rle">*&lt;=</option>';
+
+ if($kvalue == 'rlt')
+ echo '<option value="rlt" selected="selected" class="rlt">*&lt;</option>';
+ else
+ echo '<option value="rlt" class="rlt">*&lt;</option>';
+
+ if($kvalue == 'ge')
+ echo '<option value="ge" selected="selected" class="dtop">&gt;=</option>';
+ else
+ echo '<option value="ge" class="dtop">&gt;=</option>';
+
+ if($kvalue == 'gt')
+ echo '<option value="gt" selected="selected">&gt;</option>';
+ else
+ echo '<option value="gt">&gt;</option>';
+
+ if($kvalue == 'rge')
+ echo '<option value="rge" selected="selected">*&gt;=</option>';
+ else
+ echo '<option value="rge">*&gt;=</option>';
+
+ if($kvalue == 'rgt')
+ echo '<option value="rgt" selected="selected">*&gt;</option>';
+ else
+ echo '<option value="rgt">*&gt;</option>';
+
+ if($kvalue == 'eq')
+ echo '<option value="eq" selected="selected" class="eq">==</option>';
+ else
+ echo '<option value="eq" class="eq">==</option>';
+
+ echo '</select></td><td><input type="text" name="GLSA_VP_', $key, '_', $vkey, '_Version" class="grayinput_fullWidth" value="', htmlspecialchars($vvalue[key($vvalue)]), '"></td><td width="20%"><input type="text" name="GLSA_VP_', $key, '_', $vkey, '_Arch" class="grayinput_fullWidth" size="32" value="', htmlspecialchars($value['arch']), '"></td><td width="1%"><select class="center" name="GLSA_VP_', $key, '_', $vkey, '_Auto">';
+
+ if($value['auto'] == 'yes')
+ echo '<option value="yes" selected="selected">Yes</option>';
+ else
+ echo '<option value="yes">Yes</option>';
+
+ if($value['auto'] == 'no')
+ echo '<option value="no" selected="selected">No</option>';
+ else
+ echo '<option value="no">No</option>';
+
+ echo '</select></td><td width="1%">', generateButton('<b>Change</b>'), '</td><td width="20" align="right">', generateHelpIcon('Modify any field and press \'Change\'. To delete, make any field empty.'), '</td></tr>';
+ }
+ }
+ }
+ ?>
+ <tr><td></td><td><input type="text" name="GLSA_VP_Name" class="grayinput_fullWidth" size="32"></td><td><select class="center" name="GLSA_VP_VXP"><option value="le" class="le">&lt;=</option><option value="lt" selected="selected" class="lt">&lt;</option><option value="rle" class="rle">*&lt;=</option><option value="rlt" class="rlt">*&lt;</option><option value="ge" class="dtop">&gt;=</option><option value="gt">&gt;</option><option value="rge">*&gt;=</option><option value="rgt">*&gt;</option><option value="eq" class="eq">==</option></select></td><td><input type="text" name="GLSA_VP_Version" class="grayinput_fullWidth" size="32"></td><td><input type="text" name="GLSA_VP_Arch" class="grayinput_fullWidth" size="32"></td><td><select class="center" name="GLSA_VP_Auto"><option value="yes">Yes</option><option value="no">No</option></select></td><td><? echo generateButton('<b>Change</b>'); ?></td><td width="25" align="right"><? echo generateHelpIcon('Complete all three fields and press \'Change\'.'); ?>
+ </td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b>Background:</b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Background" class="grayinput_fullWidth" cols="64" rows="2"><? echo $HTTP_POST_VARS['GLSA_Background']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a passage outlining the package.'); ?></td></tr></table></td></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Description:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Description" class="grayinput_fullWidth" cols="64" rows="4"><? echo $HTTP_POST_VARS['GLSA_Description']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a description of the vulnerability.'); ?></td></tr></table></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Impact:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Impact" class="grayinput_fullWidth" cols="64" rows="4"><? echo $HTTP_POST_VARS['GLSA_Impact']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter the impact of the vulnerability.') ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Workaround:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Workaround" class="grayinput_fullWidth" cols="64" rows="2"><? echo $HTTP_POST_VARS['GLSA_Workaround']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a workaround for the vulnerability, if one exists.'); ?></td></tr></table></td></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b><i>Resolution:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Resolution" class="grayinput_fullWidth" cols="64" rows="4"><? echo $HTTP_POST_VARS['GLSA_Resolution']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a resolution of the vulnerability, enclose commands with <code> tags.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="5" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b>References:</b></td>
+ <td class="body_rootTable_center" width="25%"><acronym title="Enter a title for the link, such as 'Reference to CERT Advisory'.">Title</acronym></td><td class="body_rootTable_center"><acronym title="Enter the URL to the reference.">URL</acronym></td></tr>
+ <?
+ foreach($GLSAReferences as $key => $reference)
+ {
+ echo '<tr><td></td><td width="25%"><input type="text" name="GLSA_RF_', $key, '_Title" class="grayinput_fullWidth" size="96" value="', htmlspecialchars(key($reference)), '"></td><td><input type="text" name="GLSA_RF_', $key, '_URL" class="grayinput_fullWidth" size="64" value="', htmlspecialchars($reference[key($reference)]), '"></td><td>', generateButton('<b>Change</b>'), '</td><td width="20" align="right">';
+ generateHelpIcon('Please complete both fields and press the \'Change\' button.');
+ echo '</td></tr>';
+ }
+ ?>
+ <tr><td></td><td width="25%"><input type="text" name="GLSA_RF_Title" class="grayinput_fullWidth" size="64"></td><td><input type="text" name="GLSA_RF_URL" class="grayinput_fullWidth" size="64"></td><td><? echo generateButton('<b>Change</b>'); ?></td><td width="25" align="right"><? generateHelpIcon('Please complete both fields and press the \'Change\' button.'); ?></td></tr>
+ </table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td><? echo generateButton('<b>Confirm</b>', 'name="Submit" value="Confirm"'); if(!$validEdit){ echo '&nbsp;', generateButton('<b>Preview</b>', 'name="Submit" value="Preview"'); } echo '&nbsp;', generateButton('<b>Boilerplate</b>', 'name="Submit" value="Boilerplate"'); ?><input type="checkbox" name="SpellMode"<? if($HTTP_POST_VARS['SpellMode']) echo ' checked'; ?>>Spell-checkify</input></td><td align="right"><? if($canSubmit) echo generateButton('<b>Submit</b>', 'name="Submit" value="Submit"'); else echo '<i>Please confirm your GLSA before submitting it!</i>'; ?></td></tr></table>
+ </td></tr>
+ </table>
+ <?
+ if($validEdit)
+ echo '<input type="hidden" name="GLSA_ID" value="', $ID, '">';
+ ?>
+ </form>
+ <? generateKonqBreak();
+ bodyHeader_invoke();
+ initEnd();
+
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-recent.php b/frame-recent.php
new file mode 100644
index 0000000..1215614
--- /dev/null
+++ b/frame-recent.php
@@ -0,0 +1,210 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/xml.glsaparser';
+
+initBegin('Repository');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Pooled Gentoo GLSA Announcements</i></b></font></td><td align="right"><img src="images/icons/queue.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</tr><tr><td colspan="2"><hr></td></tr>
+ <?
+ $recentItems = fileGrepper_parsePool();
+ for ($i = 0; $i < count($recentItems); $i++) {
+ $GLSABReady = false;
+ $GLSAMSubmitter = '???';
+ $GLSALReviewCounter = '-';
+ $GLSATReviewCounter = 0;
+ $GLSAUReviewed = 0;
+
+ echo '<tr><td colspan="2"><table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr>';
+ echo '<td width="250" align="center" class="monospace"><a href="frame-view.php?id=', $recentItems[$i][0], '">', $recentItems[$i][0], '</a></td>';
+ echo '<td width="150">', date('Y-m-d H:i:s', $recentItems[$i][1]), '</td><td width="32">';
+
+ $output = fileGrepper_getGLSAText(NULL, NULL, NULL, $recentItems[$i][0]);
+ $Parser = new GLSAParser();
+
+ if($output != '<nodata>')
+ {
+ $Parser->GLSAparse($output, true, true);
+ $header = $Parser->GLSAShortSummary;
+
+ // Set submitter
+ if(is_array($GLSAMAuthor =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'submitter')))
+ $GLSAMSubmitter = trim($GLSAMAuthor['cdata']);
+
+ // Check for reviews
+ $GLSAFlagsPending = false;
+ if(is_array($GLSAReviews =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'reviews')))
+ {
+ $GLSALReviewCounter = 0;
+ foreach($GLSAReviews['data'] as $GLSAMetadataItem)
+ {
+ $GLSACRevision = -1;
+ $GLSALReviewBuffer = false;
+ if(is_array($GLSAMetadataItem['data']) && count($GLSAMetadataItem['data']) > 0)
+ {
+ foreach($GLSAMetadataItem['data'] as $GLSAReview)
+ {
+ if($GLSAReview['tag'] == 'reviewApproval' || $GLSAReview['tag'] == 'reviewRejection' || $GLSAReview['tag'] == 'reviewComment'
+ && $GLSAMSubmitter != $GLSAMetadataItem['author'])
+ {
+ $GLSATReviewCounter++;
+
+ if($GLSAReview['tag'] == 'reviewApproval')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = true;
+
+ if($GLSAMetadataItem['author'] == authGetStatus())
+ $GLSAUReviewed = 2;
+ }
+ else if($GLSAReview['tag'] == 'reviewRejection')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = false;
+ }
+ else if($GLSAMetadataItem['author'] == authGetStatus() && $GLSAUReviewed == 0)
+ $GLSAUReviewed = 1;
+
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSACRevision = $GLSAReview['revision'];
+ if($GLSAReview['flag'] == '')
+ $GLSAFlagsPending = true;
+ }
+ }
+ $GLSALReviewCounter += $GLSALReviewBuffer;
+ }
+ }
+ }
+
+ // Check for a bugReady flag
+ if(is_array($Parser->searchMetadata($Parser->GLSAMetadata, 0, 'bugReady')))
+ $GLSABReady = true;
+ }
+ else { $header = '<i>Not available!</i>'; }
+ echo '<img align="left" src="images/icons/flag';
+ if(preg_match('/Fight[ -]?Club/i', $header) || substr($header, 0, 9) == 'Request: ')
+ echo '_star';
+ else if($GLSALReviewCounter == '-' && $GLSATReviewCounter == 0)
+ {
+ $GLSATReviewCounter = 'No';
+ echo '_disabled';
+ }
+ else if($GLSALReviewCounter >= $GLSAVNeededReviews)
+ echo '_green';
+ else if($GLSALReviewCounter > 0)
+ echo '_yellow';
+ echo '.';
+ generateImageExt();
+ echo '" title="', $GLSATReviewCounter, ' comment';
+ if($GLSATReviewCounter != 1)
+ echo 's';
+ if($GLSALReviewCounter > 0)
+ echo ' - ', $GLSALReviewCounter, ' review';
+ if($GLSALReviewCounter > 1)
+ echo 's';
+ if($GLSABReady)
+ echo ', marked as bug-ready';
+ echo '..."';
+ if($GLSABReady)
+ echo ' style="background: #dddddd; border: 1px solid #aaaaaa;"';
+ echo '></td><td width="32">';
+ if($GLSAMSubmitter != authGetStatus() && substr($header, 0, 9) != 'Request: ')
+ {
+ echo '<img src="images/icons/';
+ if($GLSAUReviewed == 0)
+ echo 'cross';
+ else if($GLSAUReviewed == 1)
+ echo 'tick_p';
+ else
+ echo 'tick';
+ echo '.';
+ generateImageExt();
+ echo '" title="';
+ if($GLSAUReviewed == 0)
+ echo 'Not reviewed';
+ else if($GLSAUReviewed == 1)
+ echo 'Reviewed as not positive';
+ else
+ echo 'Reviewed';
+ echo ' by you...">';
+ }
+ echo '</td><td width="32">';
+ if($GLSAFlagsPending)
+ {
+ echo '<img src="images/icons/warning.';
+ generateImageExt();
+ echo '" title="Not all comments reviewed - action may be required!">';
+ }
+ echo '</td><td><b>', $GLSAMSubmitter, '</b>: ', $header, '</td></tr></table>';
+ }
+
+ ?>
+ </td></tr>
+ <tr><td colspan="2">
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light">
+ <tr><td valign="top"><b>Key</b>:</td>
+ <td><img src="images/icons/flag_star.png" align="middle"> - GLSA Request; <img src="images/icons/flag_green.png" align="middle"> - GLSA approved but <b>not</b> necessarily bug ready!; <img src="images/icons/flag_yellow.png" align="middle"> - GLSA needs more approvals; <img src="images/icons/flag.png" align="middle"> - GLSA has no approvals; <span style="background: #dddddd; border: 1px solid #aaaaaa; height: 16px;">&nbsp;&nbsp;&nbsp;&nbsp;</span> - Bug ready marker; <img src="images/icons/tick.png" align="middle"> - GLSA approved by you; <img src="images/icons/tick_p.png" align="middle"> - GLSA reviewed by you but <b>not</b> approved; <img src="images/icons/cross.png" align="middle"> - GLSA not reviewed by you; <img src="images/icons/warning.png" align="middle"> - GLSA has comments which haven't been flagged so editing action <i>may</i> be required; if not please flag comments that are OK.
+ </td></tr></table>
+ </td></tr>
+ </table>
+ <br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Recent Gentoo GLSA Announcements</i></b></font></td><td align="right"><img src="images/icons/new.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</td></tr><tr><td colspan="2"><hr></td></tr>
+ <?
+ $recentItems = fileGrepper_parseTree(false, true);
+ for ($i = 0; $i < 15 && $i < count($recentItems); $i++) {
+
+ sscanf($recentItems[$i][0], '%04d%02d-%d', $ID1, $ID2, $ID3);
+ echo '<tr><td colspan="2" align="right"><table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr>';
+ echo '<td width="140" align="center"><a href="frame-view.php?id=', $recentItems[$i][0]. '">GLSA ', $recentItems[$i][0], '</a></td>';
+ echo '<td width="150">', date('Y-m-d H:i:s', $recentItems[$i][1]), '</td>';
+
+ $output = fileGrepper_getGLSAText($ID1, $ID2, $ID3);
+ $Parser = new GLSAParser();
+
+ if($output != '<nodata>')
+ {
+ $Parser->GLSAparse($output, true);
+ $header = $Parser->GLSAShortSummary;
+ }
+ else { $header = '<i>Not available!</i>'; }
+ echo '<td>', $header, '</td></tr></table>';
+ }
+
+ ?>
+ <br><i><? echo count($recentItems)-20; ?> out of <? echo count($recentItems); ?> GLSAs not shown...&nbsp;</i></td></tr>
+ </table>
+ <br>
+ <? bodyHeader_invoke(); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-request.php b/frame-request.php
new file mode 100644
index 0000000..bdfdbed
--- /dev/null
+++ b/frame-request.php
@@ -0,0 +1,122 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/common.auth';
+require_once './includes/io.mailer';
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/ui.newitems';
+require_once './includes/xml.glsaparser';
+
+initBegin('File a GLSA Request');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>File a GLSA Request</i></b></font></td><td align="right"><img src="images/icons/new.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</td></tr><tr><td colspan="2"><hr></td></tr>
+ <tr><td colspan="2"><table width="100%" border="0" cellspacing="0" cellpadding="4"><tr><td colspan="2">This page files a skeleton GLSA, placing it into the GLSA pool. It can then be edited as any other pooled GLSA. Although a Synopsis and Bug ID is not required for a request, it helps if you would add one in for identification purposes.<br></td></tr></table></td></tr>
+ </table>
+ <br>
+ <? /*! If we're submitting, check things, and continue if OK */
+ if($HTTP_POST_VARS['Submit'] == 'Submit')
+ {
+ if($HTTP_POST_VARS['GLSA_Title'])
+ {
+ $Parser = new GLSAParser();
+
+ $Parser->GLSAID = '!-- Insert ID here! --!';
+ $Parser->GLSAShortSummary = 'Request: '.$HTTP_POST_VARS['GLSA_Title'];
+ $Parser->GLSASynopsis = $HTTP_POST_VARS['GLSA_Synopsis'];
+
+ $Parser->GLSAProductType = 'ebuild';
+ $Parser->GLSASeverity = 'normal';
+
+ if($Parser->GLSARevision == '')
+ $Parser->GLSARevision = date('F d, Y').': 01';
+ if($Parser->GLSADate == '')
+ $Parser->GLSADate = date('F d, Y');
+
+ $Parser->GLSABugs = array();
+ $temp = explode(' ', $HTTP_POST_VARS['GLSA_Bugs']);
+ $HTTP_POST_VARS['GLSA_Bugs'] = '';
+ foreach( $temp as $bug ){ if(is_numeric($bug)) { $Parser->GLSABugs[] = $bug; $HTTP_POST_VARS['GLSA_Bugs'] .= $bug.' '; } }
+
+ $Parser->GLSAPackages[] = array('unaffected' => array(array('ge' => '1.2.3')), 'vulnerable' => array(array('lt' => '1.2.3')), 'name' => 'fill/me', 'arch' => '*', auto => 'yes');
+ $Parser->GLSAReferences = array();
+ $Parser->GLSAMetadata[] = array('data' => array(), 'parent' => '', 'tag' => 'requester', 'cdata' => authGetStatus(), 'timestamp' => genMetadataTimestamp());
+
+ if(($hash = fileGrepper_commitToPool($Parser->GLSAToXML(false), true)) != false)
+ echo generateInfo('Request successfully committed to the pool; ID #'.$hash);
+ else
+ echo generateWarning('Could not commit GLSA to pool!');
+
+ if($hash == false)
+ $hash = md5(uniqid(''));
+
+ $mString = 'Title: '.$HTTP_POST_VARS['GLSA_Title']."\n";
+ if($Parser->GLSASynopsis)
+ $mString .= 'Synopsis: '.$Parser->GLSASynopsis."\n";
+ if(count($Parser->GLSABugs) > 0)
+ $mString .= 'Bug IDs: '. rtrim($HTTP_POST_VARS['GLSA_Bugs'], ' ')."\n";
+
+ $Parser->GLSAID = $hash;
+ if(mailGLSA('%'.$GLSAMAddress, 'GLSA Request: ['.authGetStatus(true).'] '.$hash, $mString))
+ echo generateInfo('Request successfully queued with ID #'.$hash.'...');
+ else
+ echo generateWarning('Could not mail GLSA!');
+ }
+ else
+ {
+ echo generateWarning('The title is not complete - a title is needed for a GLSA Request!');
+ }
+ }
+
+ ?>
+ <form action="frame-request.php" method="post">
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b><i>Title:</i></b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars($HTTP_POST_VARS['GLSA_Title']); ?>" name="GLSA_Title" class="grayinput_fullWidth" size="64"><td width="30" align="right"><? echo generateHelpIcon('Please enter a quick 4 to 5 word description.'); ?></td></tr></table></td></tr></table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160" valign="top"><b>Synopsis:</b></td><td><table width="100%" cellspacing="0"><tr><td><textarea name="GLSA_Synopsis" class="grayinput_fullWidth" cols="64" rows="2"><? echo $HTTP_POST_VARS['GLSA_Synopsis']; ?></textarea></td><td width="30" align="right"><? echo generateHelpIcon('Please enter a synopsis of the vulnerability which should be concise and short.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right" width="160"><b>Bug IDs:</b></td><td><table width="100%" cellspacing="0"><tr><td><input type="text" value="<? echo htmlspecialchars(rtrim($HTTP_POST_VARS['GLSA_Bugs'], ' ')); ?>" name="GLSA_Bugs" class="grayinput_fullWidth" size="64"><td width="30" align="right"><? echo generateHelpIcon('Enter any relevant Gentoo Bugzilla Bug IDs separated by spaces.'); ?></td></tr></table></td></tr></table>
+ </td></tr>
+ </table><br>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light"><tr><td align="right"><button class="grayinput" name="Submit" value="Submit"><b>Submit</b></button></td></tr></table>
+ </td></tr>
+ </table>
+ </form>
+ <? generateKonqBreak();
+ bodyHeader_invoke();
+ initEnd();
+
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-search.php b/frame-search.php
new file mode 100644
index 0000000..53c6649
--- /dev/null
+++ b/frame-search.php
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/xml.glsaparser';
+
+initBegin('Search');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Gentoo GLSA Search</i></b></font></td><td align="right"><img src="images/icons/queue.lg.<? generateImageExt(); ?>" align="middle">&nbsp;</td></tr><tr><td colspan="2"><hr></td></tr>
+ <tr><td colspan="2">
+ <form action="frame-search.php" method="get">
+ <table class="body_rootTable_light" border="0" cellpadding="4" cellspacing="0" width="100%"><tr><td align="right" width="160"><b><i>Title contains:</i></b></td><td><table cellspacing="0" width="100%"><tr><td><input value="<? echo $HTTP_GET_VARS['queryString']; ?>" name="queryString" class="grayinput_fullWidth" size="64" type="text"></td><td align="right" width="90"><button class="grayinput"><b>Search</b></button></td></tr></table></td></tr></table><br>
+ <?
+ if($HTTP_GET_VARS['queryString'] != '')
+ {
+ $GLSAItems = fileGrepper_parseTree(true, true);
+ foreach ($GLSAItems as $GLSAItem)
+ {
+ sscanf($GLSAItem[0], '%04d%02d-%d', $ID1, $ID2, $ID3);
+ $output = fileGrepper_getGLSAText($ID1, $ID2, $ID3);
+
+ if($output != '<nodata>')
+ {
+ $Parser = new GLSAParser();
+ $Parser->GLSAparse($output, true);
+
+ if(strpos(strtolower($Parser->GLSAShortSummary), strtolower($HTTP_GET_VARS['queryString'])) !== false)
+ $GLSAMatches[$Parser->GLSAID] = $Parser->GLSAShortSummary;
+ }
+ }
+
+ echo '<table class="body_rootTable_light" border="0" cellpadding="4" cellspacing="0" width="100%"><tr><td>';
+ if(!isset($GLSAMatches))
+ echo 'No matches found; try another query...</td></tr></table>';
+ else
+ {
+ echo count($GLSAMatches), ' matches found (', count($GLSAItems), ' searched) for query string "', $HTTP_GET_VARS['queryString'], '":</td></tr></table><br><table class="body_rootTable_light" border="0" cellpadding="4" cellspacing="0" width="100%">';
+ foreach ($GLSAMatches as $GLSAID => $GLSATitle)
+ {
+ echo '<tr><td><a href="frame-view.php?id=', $GLSAID, '">', $GLSAID, '</a>: ', $GLSATitle, '</td></tr>';
+ }
+ }
+ }
+?>
+ </table>
+ </td></tr>
+ </table>
+ <br>
+ <? bodyHeader_invoke(); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-stats.graph.php b/frame-stats.graph.php
new file mode 100644
index 0000000..190ea30
--- /dev/null
+++ b/frame-stats.graph.php
@@ -0,0 +1,252 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/io.filegrepper';
+require_once './includes/xml.glsaparser';
+
+require_once './includes/jpgraph/jpgraph.php';
+require_once './includes/jpgraph/jpgraph_bar.php';
+require_once './includes/jpgraph/jpgraph_line.php';
+require_once './includes/jpgraph/jpgraph_regstat.php';
+
+$GetYear == '';
+$noPooled == false;
+
+is_numeric($HTTP_GET_VARS['year']) ? $GetYear = $HTTP_GET_VARS['year'] : false;
+is_numeric($GetYear) ? $Year = $GetYear : $Year = date('Y');
+
+isset($HTTP_GET_VARS['noPooled']) && $noPooled = true;
+
+$Days = 365+date('L', mktime(0,0,0,1,1,$Year));
+$XRoot = sqrt($Days)*1.48;
+$YRoot = sqrt($Days);
+
+clearstatcache();
+$GLSAItems = fileGrepper_parseTree(true, true);
+$GLSAPools = fileGrepper_parsePool(true);
+
+$LineData = array_fill(0, $Days, '-');
+$LineOvrd = $LineData;
+$LineURI = $LineData;
+$LineALT = $LineData;
+
+$i = 1;
+foreach ($GLSAItems as $GLSAItem)
+{
+ sscanf($GLSAItem[0], '%04d%02d-%d', $ID1, $ID2, $ID3);
+
+ $Parser = new GLSAParser();
+ $output = fileGrepper_getGLSAText($ID1, $ID2, $ID3);
+
+ if($output != '<nodata>')
+ {
+ $Parser->GLSAparse($output, true);
+ if(($date = strtotime($Parser->GLSADate)) >= mktime(0,0,0,1,1,$Year) && $date <= mktime(0,0,0,1,1,$Year+1))
+ {
+ $GLSAs[date('n', $date)][] = $date;
+ $MDate = date('m', $date)*$XRoot+((date('j', $date)-1)/date('t', $date))*$XRoot;
+ if($LineOvrs[$MDate] != '')
+ {
+ $LineOvrd[$MDate] = $LineOvrs[$MDate];
+ $LineALT[$MDate] .= ', '.padNumber($ID1).padNumber($ID2).'-'.padNumber($ID3);
+ }
+ else
+ {
+ $LineData[$MDate] = $i;
+ $LineOvrs[$MDate] = $i;
+ $LineALT[$MDate] = 'GLSA '.padNumber($ID1).padNumber($ID2).'-'.padNumber($ID3);
+ $LineURI[$MDate] = 'frame-view.php?id='.padNumber($ID1).padNumber($ID2).'-'.padNumber($ID3);
+ }
+ $i++;
+ }
+ }
+}
+
+// Set counter
+$GLSAReleased = $i - 1;
+
+if(!$noPooled)
+{
+ foreach ($GLSAPools as $GLSAPool)
+ {
+ $Parser = new GLSAParser();
+ $output = fileGrepper_getGLSAText(NULL, NULL, NULL, $GLSAPool[0]);
+
+ if($output != '<nodata>')
+ {
+ $Parser->GLSAparse($output, true);
+ if(($date = strtotime($Parser->GLSADate)) >= mktime(0,0,0,1,1,$Year) && $date <= mktime(0,0,0,1,1,$Year+1))
+ {
+ $PGLSAs[date('n', $date)][] = $date;
+ $i++;
+ }
+ }
+ }
+}
+
+for ($i = 1; $i <= 12; $i++)
+{
+ $XYPlot[] = ($XRoot*$i);
+ $XXPlot[] = count($GLSAs[$i]);
+ $XXPlot2[] = count($PGLSAs[$i]);
+}
+
+$i = 1;
+$peak = max($XXPlot);
+
+function xLabelFormat($input)
+{
+ global $i, $XYPlot;
+
+ if($input == 0 || $i > 12) return '';
+ return date('M', mktime(0,0,0,$i++,1,$Year));
+}
+
+function cPointFormat($yVal)
+{
+ global $LineOvrd;
+ if($LineOvrd[array_search($yVal, $LineOvrd)] != '-')
+ return array('', 'red', 'red');
+
+ return array('', '', '');
+}
+
+// Create the graph.
+$graph = new Graph(550,325);
+$graph->SetMarginColor('white');
+
+$graph->SetScale('int',0,0,0,$Days);
+$graph->SetY2Scale('int');
+
+// Adjust the margin slightly so that we use the
+// entire area (since we don't use a frame)
+$graph->SetMargin(40,40,30,80);
+
+// Box around plotarea
+$graph->SetBox();
+
+// No frame around the image
+$graph->SetFrame(false);
+
+// Setup the tab title
+$graph->title->Set('GLSA Release Statistics for '.$Year);
+
+// Setup the X and Y grid
+$graph->ygrid->Show(false, false);
+$graph->y2grid->SetFill(true,'#DDDDDD@0.5','#BBBBBB@0.5');
+$graph->y2grid->SetLineStyle('dashed');
+$graph->y2grid->SetColor('gray');
+$graph->y2grid->Show();
+$graph->xgrid->Show();
+$graph->xgrid->SetLineStyle('dashed');
+$graph->xgrid->SetColor('gray');
+
+// Set tick marks
+$graph->xaxis->scale->ticks->Set($XRoot, $XRoot);
+$graph->yaxis->scale->ticks->Set($YRoot, $YRoot);
+$graph->yaxis->scale->SetGrace(15,0);
+$graph->y2axis->scale->SetGrace(15,0);
+$graph->y2axis->SetPos('max');
+
+// Set tick marks to the exterior
+$graph->xaxis->SetTickSide(SIDE_BOTTOM);
+$graph->yaxis->SetTickSide(SIDE_LEFT);
+$graph->y2axis->SetTitle('GLSAs per month', 'middle');
+$graph->y2axis->SetTitleSide(SIDE_RIGHT);
+
+// Setup month labels on the X-axis
+$graph->xaxis->SetLabelFormatCallback('xLabelFormat');
+
+// Create a bar plot
+$bplot = new BarPlot($XXPlot, $XYPlot);
+$bplot->SetFillColor('skyblue@0.5');
+
+// Create a bar plot
+$bplot2 = new BarPlot($XXPlot2, $XYPlot);
+$bplot2->SetFillColor('yellow@0.8');
+
+// Setup values
+$bplot->value->Show();
+$bplot->value->SetFormat('%d');
+$bplot->value->SetFont(FF_FONT1,FS_BOLD);
+$bplot->SetValuePos('top');
+$bplot->SetShadow('gray@0.25', 3, 3);
+
+// Line plot
+$lplot = new LinePlot($LineData);
+$lplot->SetColor('#888888');
+
+$lplot->mark->SetType(MARK_STAR);
+$lplot->mark->SetColor('blue@0.5');
+$lplot->mark->SetFillColor('lightblue');
+$lplot->mark->SetSize(1);
+$lplot->mark->SetCallback('cPointFormat');
+
+// Create the red legend
+$rplot = new LinePlot($__TempVar__ = array(0 => '-'));
+$rplot->SetStyle('dotted');
+
+$rplot->mark->SetType(MARK_STAR);
+$rplot->mark->SetColor('red@0.5');
+$rplot->mark->SetFillColor('red');
+$rplot->mark->SetSize(6);
+$rplot->mark->SetCallback('cPointFormat');
+
+// Add Hotspots
+$lplot->SetCSIMTargets(&$LineURI, &$LineALT);
+
+// Add Legend
+$bplot->SetLegend('GLSAs per month');
+$bplot2->SetLegend('Pooled GLSAs');
+$bplot2->legendColor = '#c6e3bc';
+$lplot->SetLegend('GLSA');
+$rplot->SetLegend('Multiple GLSAs');
+
+$graph->legend->SetLayout(LEGEND_HOR);
+$graph->legend->Pos(0.5,0.925, 'center', 'bottom');
+$graph->footer->center->Set('Total: '.$GLSAReleased.' released GLSAs...');
+
+$graph->AddY2($bplot);
+if(!$noPooled)
+ $graph->AddY2($bplot2);
+$graph->Add($lplot);
+$graph->Add($rplot);
+
+// .. and finally send it back to the browser
+
+$graph->img->SetTransparent('white');
+if($GetMap)
+{
+ $ih = $graph->Stroke(_CSIM_SPECIALFILE);
+ $ImageMap = $graph->GetHTMLImageMap('GraphMap');
+}
+else
+ $graph->Stroke();
+
+?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-stats.php b/frame-stats.php
new file mode 100644
index 0000000..f180686
--- /dev/null
+++ b/frame-stats.php
@@ -0,0 +1,88 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+
+$rangeTree = fileGrepper_parseTree();
+$rangeYears = array();
+
+foreach($rangeTree as $rangeItem)
+{
+ if(!in_array(($rangeYear = date('Y', $rangeItem[1])), $rangeYears))
+ $rangeYears[] = $rangeYear;
+}
+asort($rangeYears);
+
+$GetYear = '-';
+if(in_array($HTTP_GET_VARS['year'], $rangeYears))
+ $GetYear = $HTTP_GET_VARS['year'];
+if($HTTP_GET_VARS['noPooled'] != '')
+ $noPooled = true;
+
+$GetMap = true;
+require_once './frame-stats.graph.php';
+
+initBegin('Statistics');
+bodyFooter_invoke();
+
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><font size="+1"><b><i>Gentoo Security Statistics</i></b></font><hr>
+ <? generateKonqBreak(); ?><dl><dt></dt><dd> The following graph shows GLSA releases along with the number of releases per month as well as the time of each GLSA in the month. Click on a star to view the GLSA. Red stars indicate that multiple GLSAs were released on that day; while the bars indicate the number of pooled GLSAs that still remain from that month.<? echo($ImageMap); ?>
+
+ <br><table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable_light">
+ <tr>
+ <td width="75" align="center"><b>Year:</b></td>
+ <td>
+ <? foreach($rangeYears as $rangeYear)
+ {
+ if(($GetYear == $rangeYear) || ($GetYear == '-' && $rangeYear == date('Y')))
+ echo '<i>';
+ else
+ echo '<a href="frame-stats.php?year='.$rangeYear.'">';
+ echo $rangeYear;
+ if(($GetYear == $rangeYear) || ($GetYear == '-' && $rangeYear == date('Y')))
+ echo '</i>';
+ else
+ echo '</a>';
+ echo '&nbsp;';
+ }
+ ?>
+ </td>
+ </tr>
+ </table>
+
+ <p align="middle"><img align="middle" alt="Gentoo GLSA Release Statistics" src="frame-stats.graph.php<? if($GetYear != '') { echo '?year='.$GetYear; } if($noPooled) { echo '&noPooled'; } ?>" usemap="GraphMap" border="0"></p><? generateKonqBreak(); generateKonqBreak(); ?></dd></dl>
+ </td></tr>
+ </table>
+
+ <br>
+ <? bodyHeader_invoke(); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/frame-view.php b/frame-view.php
new file mode 100644
index 0000000..50c01ab
--- /dev/null
+++ b/frame-view.php
@@ -0,0 +1,563 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once './includes/io.mailer';
+require_once './includes/common.spell';
+require_once './includes/ui.body';
+require_once './includes/ui.init';
+require_once './includes/xml.glsaparser';
+
+if($HTTP_GET_VARS['type'] == 'text')
+{
+ $plainOutput = true;
+ header('Content-type: text/plain');
+} else if($HTTP_GET_VARS['type'] == 'xml')
+{
+ $plainXML = true;
+ header('Content-type: text/plain');
+} else if($HTTP_GET_VARS['type'] == 'spell')
+{
+ $spellMode = true;
+ $speller = new Speller();
+ if($HTTP_GET_VARS['addSpell'] != '')
+ $speller->AddLocal($HTTP_GET_VARS['addSpell']);
+}
+
+if(!$plainOutput && !$plainXML)
+{
+ echo '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">';
+ initBegin('GLSA Viewer - '.$HTTP_GET_VARS['id']);
+
+ if(!isset($HTTP_GET_VARS['reviewsOnly']))
+ {
+ bodyFooter_invoke();
+
+ if($HTTP_GET_VARS['action'] == 'review')
+ {
+ $reviewChangeDetected = true;
+ $reviewingGLSA = true;
+ if($HTTP_POST_VARS['reviewType'] == '+')
+ $reviewApproval = 'reviewApproval';
+ else if($HTTP_POST_VARS['reviewType'] == '-')
+ $reviewApproval = 'reviewRejection';
+ else if($HTTP_POST_VARS['reviewType'] == 'C')
+ $reviewApproval = 'reviewComment';
+ else
+ $reviewChangeDetected = false;
+
+ $reviewComment = htmlspecialchars(stripslashes($HTTP_POST_VARS['reviewComment']));
+ if(trim($reviewComment) == '')
+ $reviewChangeDetected = false;
+ } else if ($HTTP_GET_VARS['action'] == 'reviewClear')
+ {
+ $reviewingGLSA = true;
+ $reviewClearingGLSA = true;
+ } else if ($HTTP_GET_VARS['action'] == 'toggleReady')
+ {
+ $reviewingGLSA = true;
+ $toggleReady = true;
+ }
+?>
+ <table width='100%' border='0' cellspacing='0' cellpadding='4' class='body_rootTable'>
+ <tr><td><font size='+1'><b><i>Gentoo Security GLSA Viewer</i></b></font><hr></td></tr><tr><td><? if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Gecko') != 0) echo '<img src="images/icons/glsa.', generateImageExt(true), '" align="right" style="padding-bottom: 6px; padding-left: 6px;" alt="Welcome image">'; ?><div><dl><dt></dt><dd>
+ <?
+ generateKonqBreak('');
+}}
+ if($HTTP_GET_VARS['id'] != '')
+ $ID = $HTTP_GET_VARS['id'];
+ if($HTTP_GET_VARS['moveTo'] != '')
+ $IDNew = $HTTP_GET_VARS['moveTo'];
+ if($IDNew)
+ {
+ if(($IDPassed = fileGrepper_moveGLSA($ID, $IDNew)) != '<error>')
+ $ID = $IDPassed;
+ else
+ generateWarning('Move failed!');
+ }
+ if(preg_match('/^[A-F0-9]{32}$/i', $ID)){ $valid = true; $pool = true; }
+ else if(sscanf($ID, '%04d%02d-%d', $ID1, $ID2, $ID3) == 3){ $valid = true; }
+
+ if($ID == ''){ $valid = false; echo 'No GLSA ID has been specified.'; }
+ else if ( $valid == false ) { echo 'Invalid GLSA ID [` ', $ID, ' `] specified!'; }
+ if(($plainOutput || $plainXML) && $valid == false) die();
+ if( $valid )
+ {
+ $output = fileGrepper_getGLSAText( $pool ? NULL: $ID1, $pool ? NULL: $ID2, $pool ? NULL: $ID3, $pool ? $ID : NULL);
+ if($output != '<nodata>')
+ {
+ $Parser = new GLSAParser();
+ $Parser->GLSAparse($output);
+
+ $GLSASubmitter =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'submitter');
+ if($reviewingGLSA)
+ {
+ // Check if we have a review block...
+ if(!is_array($GLSAReviews =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'reviews')) && !$reviewClearingGLSA && !$toggleReady && $reviewChangeDetected)
+ {
+ $Parser->GLSAMetadata[] = array('data' => array(
+ 0 => array(
+ 'data' => array(
+ 0 => array(
+ 'data' => array(),
+ 'revision' => 1,
+ 'tag' => $reviewApproval,
+ 'cdata' => $reviewComment,
+ 'timestamp' => genMetadataTimestamp()
+ )
+ ),
+ 'tag' => 'reviewSet',
+ 'author' => authGetStatus(true),
+ )
+ ), 'tag' => 'reviews');
+ } else if (!$reviewClearingGLSA && !$toggleReady && $reviewChangeDetected)
+ {
+ // Now find us a reviewSet
+ if(!is_array($GLSAReviewSet =& $Parser->searchMetadata($GLSAReviews['data'], 0, 'reviewSet', authGetStatus(true))))
+ {
+ $GLSAReviews['data'][] = array('data' => array(
+ 0 => array(
+ 'data' => array(),
+ 'revision' => 1,
+ 'tag' => $reviewApproval,
+ 'cdata' => $reviewComment,
+ 'timestamp' => genMetadataTimestamp()
+ )
+ ),
+ 'tag' => 'reviewSet',
+ 'author' => authGetStatus(true)
+ );
+ } else {
+ $GLSAReviewLastEntry = end($GLSAReviewSet['data']);
+ $GLSAReviewSet['data'][] = array('data' => array(),
+ 'revision' => $GLSAReviewLastEntry['revision']+1,
+ 'tag' => $reviewApproval,
+ 'cdata' => $reviewComment,
+ 'timestamp' => genMetadataTimestamp());
+ }
+ } else if($reviewClearingGLSA)
+ {
+ // Clearing reviews
+
+ // We have to use a classic $i iteration here since foreach doesn't return
+ // pointers...
+
+ for ($i = 0; is_array($GLSAReviews['data'][$i]); $i++)
+ {
+ if(is_array($GLSAReviews['data'][$i]['data']) &&
+ count($GLSAReviews['data'][$i]['data']) > 0)
+ {
+ for($ii = 0; is_array($GLSAReviews['data'][$i]['data'][$ii]); $ii++)
+ {
+ if($GLSAReviews['data'][$i]['data'][$ii]['tag'] == 'reviewApproval')
+ $GLSAReviews['data'][$i]['data'][$ii]['tag'] = 'reviewComment';
+ }
+ }
+ }
+ } else if($toggleReady)
+ {
+ if(!is_array($GLSAToggleFlag =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'bugReady')))
+ {
+ $Parser->GLSAMetadata[] = array('data' => '', 'parent' => '', 'tag' => 'bugReady', 'cdata' => authGetStatus(), 'timestamp' => genMetadataTimestamp());
+ } else
+ {
+ $GLSAToggleFlag = NULL;
+ if($GLSAToggleIndex = array_search(NULL, $Parser->GLSAMetadata, true))
+ unset($Parser->GLSAMetadata[$GLSAToggleIndex]);
+ }
+ }
+
+ // See if we're flagging any reviewSets
+ $reviewChangeMade = false;
+ foreach ($HTTP_POST_VARS as $Var => $Data)
+ {
+ if($Data == 'on' && substr($Var, 0, 24) == 'reviewCommentFlagToggle:')
+ {
+ $tmpArray = explode(':', $Var);
+ if($tmpArray[1] != '' && $tmpArray[2] != '' && is_array($GLSAReviewSet =& $Parser->searchMetadata($GLSAReviews['data'], 0, 'reviewSet', $tmpArray[1])))
+ {
+ if(is_array($GLSAReviewComment =& $Parser->searchMetadata($GLSAReviewSet['data'], 0, NULL, NULL, $tmpArray[2])))
+ {
+ $reviewChangeMade = true;
+ if($GLSAReviewComment['flag'] != '')
+ $GLSAReviewComment['flag'] = '';
+ else
+ $GLSAReviewComment['flag'] = authGetStatus(true).': '.genMetadataTimestamp();
+ }
+ }
+ }
+ }
+
+ // Commit
+ if($reviewChangeDetected || $reviewChangeMade || $toggleReady || $reviewClearingGLSA)
+ {
+ if(fileGrepper_getGLSAText($pool ? NULL: $ID1, $pool ? NULL: $ID2, $pool ? NULL: $ID3, $pool ? $ID : NULL, false, $Parser->GLSAToXML(false)))
+ generateInfo('Updated GLSA successfully committed! [[ <a href="frame-view.php?id='.$ID.'">Refresh</a> ]]');
+ else
+ generateWarning('Could not commit updated GLSA!');
+ }
+ }
+ if($plainOutput)
+ {
+ if(isset($HTTP_GET_VARS['errata']))
+ $updateMode = 2;
+ else if(isset($HTTP_GET_VARS['update']))
+ $updateMode = 1;
+ $GLSAOutput = $Parser->GLSAToText(false, $updateMode);
+ echo $GLSAOutput;
+ }
+ else if($plainXML)
+ {
+ echo $Parser->GLSAToXML(false);
+ }
+
+ if($plainOutput || $plainXML)
+ die();
+
+ if(!isset($HTTP_GET_VARS['reviewsOnly']))
+ {{
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ echo '<tr><td width="160" align="right" valign="top"><b>GLSA ID:</b></td><td>', $Parser->GLSAID;
+ if($Parser->GLSAID != $ID && !$IDNew)
+ echo '<b> &lt;&lt; Mismatch!</b>';
+ echo '</td></tr>';
+ if($Parser->GLSARevision)
+ echo '<tr><td width="160" align="right" valign="top"><b>GLSA Revision:</b></td><td>', $Parser->GLSARevision, '</td></tr>';
+ if($Parser->GLSADate)
+ echo '<tr><td width="160" align="right" valign="top"><b>GLSA Release Date:</b></td><td>', $Parser->GLSADate, '</td></tr>';
+ echo '</table><br><table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ echo '<tr><td width="160" align="right" valign="top"><b>Title:</b></td><td>'.__spell__($Parser->GLSAShortSummary, $spellMode, &$speller).'</td></tr>';
+ if($Parser->GLSAAccess)
+ echo '<tr><td width="160" align="right" valign="top"><b>Access:</b></td><td>'.ucfirst($Parser->GLSAAccess).'</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>Product:</b></td><td>'.$Parser->GLSAProduct.'</td></tr>';
+ if($Parser->GLSASeverity)
+ echo '<tr><td width="160" align="right" valign="top"><b>Severity:</b></td><td>'.ucfirst($Parser->GLSASeverity).'</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>Synopsis:</b></td><td>';
+ if(strlen($Parser->GLSASynopsis) > 160)
+ echo '<img src="images/icons/warning.', generateImageExt(true), '" align="top" style="float: right; padding-left: 6px;" alt="" title="Longer than 160 characters!">';
+ echo padHTML($Parser->GLSASynopsis, $spellMode, &$speller).'</td></tr>';
+ echo '</table><br>';
+ if(count($Parser->GLSABugs) > 0)
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light"><tr><td width="160" align="right" valign="top"><b>Related bugs:</b></td><td>';
+ foreach ($Parser->GLSABugs as $Bug)
+ {
+ echo '<a href="http://bugs.gentoo.org/show_bug.cgi?id=', $Bug, '">#', $Bug, '</a>&nbsp;';
+ }
+ echo '</td></tr></table><br>';
+ }
+ if(count($Parser->GLSAPackages) > 0)
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+
+ $i = 0;
+ echo '<tr><td width="160" align="right" valign="top"><b>Unaffected packages:</b></td><td>';
+ foreach ($Parser->GLSAPackages as $Package)
+ {
+ foreach ($Package['unaffected'] as $VersionArray)
+ {
+ foreach ($VersionArray as $VXP => $Version)
+ {
+ $PackageNames = explode('/', $Package['name']);
+ if($i != 0) echo '<br>'; $i++;
+ echo '<a href="http://packages.gentoo.org/packages/?category=', $PackageNames[0], ';name=', $PackageNames[1], '">', $Package['name'], '</a> ', VXPToText($VXP), ' ', $Version, ' on ', str_replace('*', 'all architectures', $Package['arch']);
+
+ if($Package['auto'] == 'no') echo ' - <i>remerge required!</i>';
+ }
+ }
+ }
+ $i = 0;
+ echo '<tr><td width="160" align="right" valign="top"><b>Vulnerable packages:</b></td><td>';
+ foreach ($Parser->GLSAPackages as $Package)
+ {
+ foreach ($Package['vulnerable'] as $VersionArray)
+ {
+ foreach ($VersionArray as $VXP => $Version)
+ {
+ $PackageNames = explode('/', $Package['name']);
+
+ if($i != 0) echo '<br>'; $i++;
+ echo '<a href="http://packages.gentoo.org/packages/?category=', $PackageNames[0], ';name=', $PackageNames[1], '">', $Package['name'], '</a> ', VXPToText($VXP), ' ', $Version, ' on ', str_replace('*', 'all architectures', $Package['arch']);
+ }
+ }
+ }
+ echo '</td></tr></table><br>';
+ }
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ if($Parser->GLSABackground != '')
+ echo '<tr><td width="160" align="right" valign="top"><b>Background:</b></td><td>', stripParagraphTags(padHTML($Parser->GLSABackground, $spellMode, &$speller)), '</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>Description:</b></td><td>', stripParagraphTags(padHTML($Parser->GLSADescription, $spellMode, &$speller)), '</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>Impact:</b></td><td>', stripParagraphTags(padHTML($Parser->GLSAImpact, $spellMode, &$speller)), '</td></tr>';
+ echo '</table><br><table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ echo '<tr><td width="160" align="right" valign="top"><b>Workaround:</b></td><td>', stripParagraphTags(padHTML($Parser->GLSAWorkaround, $spellMode, &$speller)), '</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>Resolution:</b></td><td>', stripParagraphTags(padHTML($Parser->GLSAResolution, $spellMode, &$speller)), '</td></tr>';
+ echo '</table><br>';
+ if(count($Parser->GLSAReferences) > 0)
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light"><tr><td width="160" align="right" valign="top"><b>References:</b></td><td>';
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Konqueror/') == 0)
+ echo '<ul>';
+ else
+ echo '<dl><dt></dt><dd>';
+
+ foreach ($Parser->GLSAReferences as $URI => $Text)
+ {
+ echo '<li><a href="', $URI, '">', $Text, '</a></li>';
+ }
+
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Konqueror/') == 0)
+ { if(substr_count($_SERVER['HTTP_USER_AGENT'], 'MSIE 6') == 0) echo '</ul>'; }
+ else
+ { echo '</dd></dl>'; }
+ echo '</td></tr></table><br>';
+ }
+
+ if($Parser->GLSAMetadata != array())
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light"><tr><td width="160" align="right" valign="top"><b>Metadata:</b></td><td>';
+ function __iterateMetadata__(&$MetadataSet, $iterationLevel = 0)
+ {
+ foreach($MetadataSet as $GLSAMetadataItem)
+ {
+ // Reviews are handled elsewhere...
+ if($iterationLevel == 0 && $GLSAMetadataItem['tag'] == 'reviews')
+ continue;
+
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="0"><tr>';
+ if($GLSAMetadataItem['revision'] != '')
+ echo '<td valign="top" width="1%"><b><u>R:</u> </b>', $GLSAMetadataItem['revision'], ';&nbsp;</td>';
+ else
+ echo '<td width="0%"></td>';
+ echo '<td valign="top" width="1%"><b>';
+ if($GLSAMetadataItem['author'])
+ echo $GLSAMetadataItem['author'], '::';
+ echo '<i>', ucfirst($GLSAMetadataItem['tag']), '</i></b>:&nbsp;</td><td>';
+ if($GLSAMetadataItem['cdata'])
+ echo ltrim($GLSAMetadataItem['cdata']);
+ if(is_array($GLSAMetadataItem['data']) && count($GLSAMetadataItem['data']) > 0)
+ __iterateMetadata__(&$GLSAMetadataItem['data'], $iterationLevel+1);
+ echo '</td>';
+ if($GLSAMetadataItem['timestamp'])
+ echo '<td align="right">(', $GLSAMetadataItem['timestamp'], ')</td>';
+ echo '</tr></table>';
+ }
+ }
+ __iterateMetadata__(&$Parser->GLSAMetadata);
+ echo '</td></tr></table><br>';
+ }
+
+ }}
+?>
+ <form action="frame-view.php?id=<? echo $ID; ?>&action=review" method="post">
+ <table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light"> <tr><td width="160" align="right" valign="top"><? if(isset($HTTP_GET_VARS['reviewsOnly'])) { echo '<a style="float: left;" href="#" onclick="javascript: parent.document.body.rows = \'100%, *\';">Hide Pane</a>'; } ?><b>Reviews:</b></td><td valign="top">
+<?
+ // Check if we have a review block...
+ if(!is_array($GLSAReviews =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'reviews')))
+ echo '<i>No reviews available!</i>';
+ else
+ {
+ echo '<div style="text-align: right;"><i>';
+ if(isset($HTTP_GET_VARS['reviewsOnly']))
+ $append = '&amp;reviewsOnly';
+ if(isset($HTTP_GET_VARS['hide_flagged_reviews']))
+ echo '<a href="frame-view.php?id=', $ID, $append, '">Show flagged reviews</a>';
+ else
+ echo '<a href="frame-view.php?id=', $ID, $append, '&amp;hide_flagged_reviews">Hide flagged reviews</a>';
+ echo '</i></div>';
+
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="1">';
+ $GLSALReviewCounter = 0;
+ foreach($GLSAReviews['data'] as $GLSAMetadataItem)
+ {
+ echo '<tr><td></td><td valign="top" align="right"><b>';
+ if($GLSAMetadataItem['author'])
+ echo $GLSAMetadataItem['author'];
+ echo '</b>:</td><td valign="top">&nbsp;</td><td width="100%">';
+ $GLSACRevision = -1;
+ $GLSALReviewBuffer = false;
+ if(is_array($GLSAMetadataItem['data']) && count($GLSAMetadataItem['data']) > 0)
+ {
+ $i = 0;
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ foreach($GLSAMetadataItem['data'] as $GLSAReview)
+ {
+ // Check if our tag is a supported type and filter out tags if needed if flagged
+ if(($GLSAReview['tag'] == 'reviewApproval' || $GLSAReview['tag'] == 'reviewRejection' || $GLSAReview['tag'] == 'reviewComment') && !(isset($HTTP_GET_VARS['hide_flagged_reviews']) && $GLSAReview['flag'] != ''))
+ {
+ echo '<tr><td valign="top" align="right" width="10"><acronym title="', ($GLSAReview['timestamp'] ? $GLSAReview['timestamp'] : 'No timestamp available!'), '">', padNumber($GLSAReview['revision']), '</acronym></td><td valign="top" width="1"><b>';
+ if($GLSAReview['tag'] == 'reviewApproval')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = true;
+ echo 'Approval:';
+ }
+ else if($GLSAReview['tag'] == 'reviewRejection')
+ {
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSALReviewBuffer = false;
+ echo '<i>Rejection</i>:';
+ }
+ else if($GLSAReview['tag'] == 'reviewComment')
+ echo 'Comment:';
+ if($GLSACRevision < $GLSAReview['revision'])
+ $GLSACRevision = $GLSAReview['revision'];
+
+ /* You don't have to understand this ... it just happens to work; plasmaroo */
+ echo '</b></td><td>', preg_replace(array('/(s\/[^\/]+?\/[^\/]*?\/[^\/.]*?) (s\/[^\/]+?\/[^\/]*?\/[^\/.]*?)/', '/(s\/[^\/]+?\/[^\/]*?\/[^\/.]*?) (s\/[^\/]+?\/[^\/]*?\/[^\/.]*?)/', '/s\/(.+?)[\\\]\/(.*?)\//', '/(^|[ .,;])(s\/[^\/]+?\/[^\/]*?\/)(\w+?)([ .,;]|$)/', '/(^|[ .,;])s\/(.+?)\/(.*?)\//', '/&backslash;/'), array('\1 <b>::</b> \2', '\1 <b>::</b> \2', 's/\1&backslash;\2/', '\1\2<i>\3</i>\4\5', '\1<span class="highlightComment"><b>s</b>/</span><span class="highlightAddedLine">\2</span><span class="highlightComment">/</span><span class="highlightRemovedLine">\3</span><span class="highlightComment">/</span>', '/'), trim($GLSAReview['cdata']));
+
+ // echo '<span class="datestamp">', preg_replace(array('/ \+.+$/', '/^\w+, /'), array('', ''), $GLSAReview['timestamp']), '</span>',
+ echo '</td><td width="45" align="right" valign="top">';
+ if($GLSAReview['flag'] == '')
+ echo '<img src="images/icons/flag.'.generateImageExt(true).'" align="top">';
+ else
+ echo '<img src="images/icons/flag_green.'.generateImageExt(true).'" title="', $GLSAReview['flag'], '" align="top">';
+ if(!isset($HTTP_GET_VARS['reviewsOnly']))
+ echo '&nbsp;<input type="checkbox" name="reviewCommentFlagToggle:', $GLSAMetadataItem['author'], ':', $GLSAReview['revision'], '">';
+ echo '</td></tr>';
+
+ $i++;
+ }
+ }
+ if($i == 0)
+ echo 'No reviews to show.';
+
+ echo '</table>';
+ $GLSALReviewCounter += $GLSALReviewBuffer;
+ }
+ }
+ echo '</table>';
+ }
+?>
+ </td></tr>
+<? if($pool && !isset($HTTP_GET_VARS['reviewsOnly']))
+ {?>
+ <tr><td width="160" align="right" valign="top"><b>Add Review:</b></td><td>
+ <table width="100%" border="0" cellspacing="0" cellpadding="1"><tr><td>
+ <textarea class="grayinput_fullWidth" name="reviewComment"></textarea></td><td width="205"><table width="100%" cellspacing="0" cellpadding="0"><tr><td align="center" width="100%">
+<? if(trim($GLSASubmitter['cdata']) != authGetStatus(true))
+ { ?>
+ <input type="radio" name="reviewType" value="+"><font color="#336633"><b>+</b></font></input>
+ <input type="radio" name="reviewType" value="-"><b><font color="#990033">-</font></b></input>
+<? } ?>
+ <input type="radio" name="reviewType" value="C"><b><font color="#9900FF">&plusmn;</font></b></input>
+ </td><td align="right"><button class="grayinput" type="submit"><b>Commit</b></button></td></tr></table></td></tr></table>
+<? }
+ echo '</td></tr></table><br><input type="hidden" name="id" value="', $Parser->GLSAID, '"><input type="hidden" name="action" value="review"></form>';
+/* echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">';
+ echo '<tr><td width="160" align="right" valign="top"><b>MD5:</b></td><td>', md5($output), '</td></tr>';
+ echo '<tr><td width="160" align="right" valign="top"><b>SHA1:</b></td><td>'.sha1($output).'</td></tr>';
+ echo '</table><br>';
+*/
+
+ if(!isset($HTTP_GET_VARS['reviewsOnly']))
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light"><tr><td width="160" align="right" valign="top"><b>XML:</b></td><td>';
+ if($pool)
+ echo '<a href="pool/', $ID, '.xml">', $ID, '.xml</a>';
+ else
+ echo '<a href="data/', $ID1, '/', padNumber($ID2), '/', padNumber($ID3), '.xml">', $Parser->GLSAID, '.xml</a>';
+ echo ' [ <a href="frame-fetch.php?id=', $ID, '">Fetch</a> ]';
+ echo ' [ <a href="frame-view.php?id=', $ID, '&amp;type=xml">Show as text</a> ]';
+
+ echo '</td></tr><tr><td width="160" align="right" valign="top"><b>Text:</b></td><td>';
+ echo '<a href="frame-view.php?id=', $ID, '&amp;type=text">', $ID, '.txt</a>';
+ echo ' [ <a href="frame-view.php?id=', $ID, '&amp;type=text&amp;update">Add update sections</a> ]';
+ echo ' [ <a href="frame-view.php?id=', $ID, '&amp;type=text&amp;errata">Add errata sections</a> ]';
+ echo '</td></tr><tr><td width="160" align="right" valign="top"><b>Spell:</b></td><td>';
+ if(!$spellMode)
+ echo '<a href="frame-view.php?id=', $ID, '&amp;type=spell">Show with spell-checking</a>';
+ else
+ echo '<a href="frame-view.php?id=', $ID, '">Show without spell-checking</a>';
+ echo '</td></tr></table>';
+ }
+
+ if(authGetLevel() && !isset($HTTP_GET_VARS['reviewsOnly']))
+ {?>
+ <form action="frame-view.php" name="form" method="get">
+ <input type="hidden" name="id" value="<? echo $ID; ?>">
+ <br><table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable_light">
+ <tr><td width="160" align="right" valign="middle"><b>Actions:</b></td><td>
+<?php echo '<a target="_top" href="frame-edit.php?editGLSA=', $ID, '">Edit</a> ';
+ echo ' <a target="_top" href="frame-new.php?editGLSA=', $ID, '">(NR)</a> ';
+ echo '<font color="gray"><b>::</b></font> ';
+ echo '<a href="frame-new.php?reGLSA=', $ID, '">Reuse</a> ';
+ if($pool)
+ {
+ $GLSABReady = is_array($Parser->searchMetadata($Parser->GLSAMetadata, 0, 'bugReady'));
+
+ echo '<font color="gray"><b>::</b></font> ';
+ echo '<a href="frame-view.php?id='.$ID.'&action=toggleReady">Toggle ready flag</a> ';
+ echo '<font color="gray"><b>::</b></font> ';
+
+ if($GLSALReviewCounter > 0) // && trim($GLSASubmitter['cdata']) == authGetStatus(true))
+ {
+ echo '<a href="frame-view.php?id='.$ID.'&action=reviewClear">Downgrade approvals</a> ';
+ echo '<font color="gray"><b>::</b></font> ';
+ }
+
+ if($GLSALReviewCounter == 0)
+ echo '<i>There are no positive reviews; ', $GLSAVNeededReviews, ' more are needed to allow a move...</i>';
+ else if($GLSALReviewCounter < $GLSAVNeededReviews)
+ {
+ echo '<i>', ($GLSAVNeededReviews-$GLSALReviewCounter), ' more review';
+ if(($GLSAVNeededReviews-$GLSALReviewCounter) == 1)
+ echo ' is';
+ else
+ echo 's are';
+ echo ' needed to allow a move!</i>';
+ }
+ else if(!$GLSABReady)
+ {
+ echo '<i>This GLSA needs to be marked as bugReady to allow a move!</i>';
+ }
+ else
+ {
+ if(substr_count($_SERVER['HTTP_USER_AGENT'],'Konqueror/') != 0)
+ echo '<a onclick="javascript:document.form.submit();" style="cursor:hand;">Move</a>';
+ else
+ echo '<a href="#ClickToMoveGLSA" onclick="document.form.submit();">Move</a>';
+ echo ' [ To: <input class="grayinput" name="moveTo" type="text" size="10" value="AutoMove"> ]';
+ }
+
+ }
+ echo '</td></tr></table></form>';
+ }
+ generateKonqBreak('');
+
+ }
+ else echo '<b>Error:</b> non-existent GLSA entered.';
+ if($plainOutput) die();
+ }
+
+ if(!isset($HTTP_GET_VARS['reviewsOnly']))
+ {
+ ?>
+ </dd></dl></div></td></tr>
+ </table>
+ <br>
+<? } bodyHeader_invoke(); initEnd(); ?>
+<?
+ // Local Variables: ***
+ // truncate-lines:true ***
+ // End: ***
+?>
diff --git a/framed.php b/framed.php
new file mode 100644
index 0000000..ba299f1
--- /dev/null
+++ b/framed.php
@@ -0,0 +1,10 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Frameset//EN" "http://www.w3.org/TR/REC-html40/frameset.dtd">
+<html>
+<head>
+ <title>Gentoo Security</title>
+</head>
+<frameset cols="230, *" border="1" name="frameSet">
+ <frame name="leftFrame" src="frame-left.php">
+ <frame name="rightFrame" src="frame-body.php">
+</frameset>
+</html>
diff --git a/images/bg-grid-dark.gif b/images/bg-grid-dark.gif
new file mode 100644
index 0000000..c2ef520
--- /dev/null
+++ b/images/bg-grid-dark.gif
Binary files differ
diff --git a/images/bg-grid.gif b/images/bg-grid.gif
new file mode 100644
index 0000000..a849ee3
--- /dev/null
+++ b/images/bg-grid.gif
Binary files differ
diff --git a/images/gentooLogo.gif b/images/gentooLogo.gif
new file mode 100644
index 0000000..04944ff
--- /dev/null
+++ b/images/gentooLogo.gif
Binary files differ
diff --git a/images/gentooLogo.png b/images/gentooLogo.png
new file mode 100644
index 0000000..558e5ac
--- /dev/null
+++ b/images/gentooLogo.png
Binary files differ
diff --git a/images/glsaLogo.gif b/images/glsaLogo.gif
new file mode 100644
index 0000000..08f8af2
--- /dev/null
+++ b/images/glsaLogo.gif
Binary files differ
diff --git a/images/glsaLogo.png b/images/glsaLogo.png
new file mode 100644
index 0000000..efe25c6
--- /dev/null
+++ b/images/glsaLogo.png
Binary files differ
diff --git a/images/icons/back.gif b/images/icons/back.gif
new file mode 100644
index 0000000..59b4350
--- /dev/null
+++ b/images/icons/back.gif
Binary files differ
diff --git a/images/icons/back.png b/images/icons/back.png
new file mode 100644
index 0000000..b6d9bd1
--- /dev/null
+++ b/images/icons/back.png
Binary files differ
diff --git a/images/icons/bugs.gif b/images/icons/bugs.gif
new file mode 100644
index 0000000..808d368
--- /dev/null
+++ b/images/icons/bugs.gif
Binary files differ
diff --git a/images/icons/bugs.png b/images/icons/bugs.png
new file mode 100644
index 0000000..5b246a6
--- /dev/null
+++ b/images/icons/bugs.png
Binary files differ
diff --git a/images/icons/cross.gif b/images/icons/cross.gif
new file mode 100644
index 0000000..4a786f1
--- /dev/null
+++ b/images/icons/cross.gif
Binary files differ
diff --git a/images/icons/cross.png b/images/icons/cross.png
new file mode 100644
index 0000000..9152973
--- /dev/null
+++ b/images/icons/cross.png
Binary files differ
diff --git a/images/icons/directory.gif b/images/icons/directory.gif
new file mode 100644
index 0000000..a42056b
--- /dev/null
+++ b/images/icons/directory.gif
Binary files differ
diff --git a/images/icons/directory.png b/images/icons/directory.png
new file mode 100644
index 0000000..e2a44ec
--- /dev/null
+++ b/images/icons/directory.png
Binary files differ
diff --git a/images/icons/flag.gif b/images/icons/flag.gif
new file mode 100644
index 0000000..87c38cf
--- /dev/null
+++ b/images/icons/flag.gif
Binary files differ
diff --git a/images/icons/flag.png b/images/icons/flag.png
new file mode 100644
index 0000000..3240b29
--- /dev/null
+++ b/images/icons/flag.png
Binary files differ
diff --git a/images/icons/flag_disabled.gif b/images/icons/flag_disabled.gif
new file mode 100644
index 0000000..d8ced40
--- /dev/null
+++ b/images/icons/flag_disabled.gif
Binary files differ
diff --git a/images/icons/flag_disabled.png b/images/icons/flag_disabled.png
new file mode 100644
index 0000000..e8fe4d6
--- /dev/null
+++ b/images/icons/flag_disabled.png
Binary files differ
diff --git a/images/icons/flag_green.gif b/images/icons/flag_green.gif
new file mode 100644
index 0000000..8a4cbca
--- /dev/null
+++ b/images/icons/flag_green.gif
Binary files differ
diff --git a/images/icons/flag_green.png b/images/icons/flag_green.png
new file mode 100644
index 0000000..d8cbc2e
--- /dev/null
+++ b/images/icons/flag_green.png
Binary files differ
diff --git a/images/icons/flag_star.gif b/images/icons/flag_star.gif
new file mode 100644
index 0000000..7af28b9
--- /dev/null
+++ b/images/icons/flag_star.gif
Binary files differ
diff --git a/images/icons/flag_star.png b/images/icons/flag_star.png
new file mode 100644
index 0000000..bd9b6da
--- /dev/null
+++ b/images/icons/flag_star.png
Binary files differ
diff --git a/images/icons/flag_yellow.gif b/images/icons/flag_yellow.gif
new file mode 100644
index 0000000..32553ad
--- /dev/null
+++ b/images/icons/flag_yellow.gif
Binary files differ
diff --git a/images/icons/flag_yellow.png b/images/icons/flag_yellow.png
new file mode 100644
index 0000000..762cf9c
--- /dev/null
+++ b/images/icons/flag_yellow.png
Binary files differ
diff --git a/images/icons/glsa.gif b/images/icons/glsa.gif
new file mode 100644
index 0000000..cc7030d
--- /dev/null
+++ b/images/icons/glsa.gif
Binary files differ
diff --git a/images/icons/glsa.png b/images/icons/glsa.png
new file mode 100644
index 0000000..4780940
--- /dev/null
+++ b/images/icons/glsa.png
Binary files differ
diff --git a/images/icons/help.gif b/images/icons/help.gif
new file mode 100644
index 0000000..34af0d2
--- /dev/null
+++ b/images/icons/help.gif
Binary files differ
diff --git a/images/icons/help.png b/images/icons/help.png
new file mode 100644
index 0000000..329417a
--- /dev/null
+++ b/images/icons/help.png
Binary files differ
diff --git a/images/icons/login.gif b/images/icons/login.gif
new file mode 100644
index 0000000..c2c3522
--- /dev/null
+++ b/images/icons/login.gif
Binary files differ
diff --git a/images/icons/login.png b/images/icons/login.png
new file mode 100644
index 0000000..4a390ae
--- /dev/null
+++ b/images/icons/login.png
Binary files differ
diff --git a/images/icons/new.gif b/images/icons/new.gif
new file mode 100644
index 0000000..93d7178
--- /dev/null
+++ b/images/icons/new.gif
Binary files differ
diff --git a/images/icons/new.lg.gif b/images/icons/new.lg.gif
new file mode 100644
index 0000000..e38de73
--- /dev/null
+++ b/images/icons/new.lg.gif
Binary files differ
diff --git a/images/icons/new.lg.png b/images/icons/new.lg.png
new file mode 100644
index 0000000..d0cbb6a
--- /dev/null
+++ b/images/icons/new.lg.png
Binary files differ
diff --git a/images/icons/new.png b/images/icons/new.png
new file mode 100644
index 0000000..df9f7ff
--- /dev/null
+++ b/images/icons/new.png
Binary files differ
diff --git a/images/icons/newbug.gif b/images/icons/newbug.gif
new file mode 100644
index 0000000..b545378
--- /dev/null
+++ b/images/icons/newbug.gif
Binary files differ
diff --git a/images/icons/newbug.png b/images/icons/newbug.png
new file mode 100644
index 0000000..9ffba35
--- /dev/null
+++ b/images/icons/newbug.png
Binary files differ
diff --git a/images/icons/query.gif b/images/icons/query.gif
new file mode 100644
index 0000000..43ced09
--- /dev/null
+++ b/images/icons/query.gif
Binary files differ
diff --git a/images/icons/query.png b/images/icons/query.png
new file mode 100644
index 0000000..75843db
--- /dev/null
+++ b/images/icons/query.png
Binary files differ
diff --git a/images/icons/queue.gif b/images/icons/queue.gif
new file mode 100644
index 0000000..2ffa670
--- /dev/null
+++ b/images/icons/queue.gif
Binary files differ
diff --git a/images/icons/queue.lg.png b/images/icons/queue.lg.png
new file mode 100644
index 0000000..43256fe
--- /dev/null
+++ b/images/icons/queue.lg.png
Binary files differ
diff --git a/images/icons/queue.png b/images/icons/queue.png
new file mode 100644
index 0000000..a0d179e
--- /dev/null
+++ b/images/icons/queue.png
Binary files differ
diff --git a/images/icons/reporting.gif b/images/icons/reporting.gif
new file mode 100644
index 0000000..7b2184b
--- /dev/null
+++ b/images/icons/reporting.gif
Binary files differ
diff --git a/images/icons/reporting.lg.png b/images/icons/reporting.lg.png
new file mode 100644
index 0000000..e2a27e7
--- /dev/null
+++ b/images/icons/reporting.lg.png
Binary files differ
diff --git a/images/icons/reporting.png b/images/icons/reporting.png
new file mode 100644
index 0000000..4e8091a
--- /dev/null
+++ b/images/icons/reporting.png
Binary files differ
diff --git a/images/icons/resolved.gif b/images/icons/resolved.gif
new file mode 100644
index 0000000..b0c25fd
--- /dev/null
+++ b/images/icons/resolved.gif
Binary files differ
diff --git a/images/icons/resolved.png b/images/icons/resolved.png
new file mode 100644
index 0000000..578f680
--- /dev/null
+++ b/images/icons/resolved.png
Binary files differ
diff --git a/images/icons/tick.gif b/images/icons/tick.gif
new file mode 100644
index 0000000..f8dec9f
--- /dev/null
+++ b/images/icons/tick.gif
Binary files differ
diff --git a/images/icons/tick.png b/images/icons/tick.png
new file mode 100644
index 0000000..543710f
--- /dev/null
+++ b/images/icons/tick.png
Binary files differ
diff --git a/images/icons/tick_p.gif b/images/icons/tick_p.gif
new file mode 100644
index 0000000..50fc977
--- /dev/null
+++ b/images/icons/tick_p.gif
Binary files differ
diff --git a/images/icons/tick_p.png b/images/icons/tick_p.png
new file mode 100644
index 0000000..e862fd7
--- /dev/null
+++ b/images/icons/tick_p.png
Binary files differ
diff --git a/images/icons/view.lg.png b/images/icons/view.lg.png
new file mode 100644
index 0000000..bc87706
--- /dev/null
+++ b/images/icons/view.lg.png
Binary files differ
diff --git a/images/icons/view_text.gif b/images/icons/view_text.gif
new file mode 100644
index 0000000..2e03775
--- /dev/null
+++ b/images/icons/view_text.gif
Binary files differ
diff --git a/images/icons/view_text.png b/images/icons/view_text.png
new file mode 100644
index 0000000..ef99a7d
--- /dev/null
+++ b/images/icons/view_text.png
Binary files differ
diff --git a/images/icons/viewmag.lg.png b/images/icons/viewmag.lg.png
new file mode 100644
index 0000000..12d2a12
--- /dev/null
+++ b/images/icons/viewmag.lg.png
Binary files differ
diff --git a/images/icons/viewmag.png b/images/icons/viewmag.png
new file mode 100644
index 0000000..3099a94
--- /dev/null
+++ b/images/icons/viewmag.png
Binary files differ
diff --git a/images/icons/warning.gif b/images/icons/warning.gif
new file mode 100644
index 0000000..75feead
--- /dev/null
+++ b/images/icons/warning.gif
Binary files differ
diff --git a/images/icons/warning.png b/images/icons/warning.png
new file mode 100644
index 0000000..9a22802
--- /dev/null
+++ b/images/icons/warning.png
Binary files differ
diff --git a/includes/common.auth b/includes/common.auth
new file mode 100644
index 0000000..e0537f3
--- /dev/null
+++ b/includes/common.auth
@@ -0,0 +1,62 @@
+<?
+
+/* IF YOU CAN READ THIS SEND A SCREENSHOT TO plasm@roo.me.uk; PHP/APACHE IS
+ CAUSING THIS ANNOYING PRINT-OUT CRAP EVERY SO OFTEN! :-( */
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo@gentoo.org > *
+ * < plasm@roo.me.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+/*! Return the username of the authenticated user to GLSAMaker;
+ if any.
+
+ @param getName If true and unauthenticated, return "User unknown...".
+ @return The username, or a value depending on getName.
+
+*/
+
+function authGetStatus($getName = false)
+{
+ $user = getenv('REMOTE_USER');
+ if($user == '' && $getName)
+ return('User unknown...');
+ else if($user == '')
+ return('You are not logged in!');
+ return($user);
+}
+
+/*! Return an integer specifying the authorization level. Currently,
+ true represents "Logged in user" and false represents "Unauthenticated
+ user".
+
+ @return The authentication level.
+*/
+
+function authGetLevel()
+{
+ return getenv('REMOTE_USER') != '';
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/common.diff b/includes/common.diff
new file mode 100644
index 0000000..118bf28
--- /dev/null
+++ b/includes/common.diff
@@ -0,0 +1,78 @@
+<?
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo@gentoo.org > *
+ * < plasm@roo.me.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+/*! Highlight a unified diff along with XML tags and output the
+ result.
+
+ @param diff A string containing the patch.
+ @param replaceTarget An expression by which the "new" file name of
+ the diff can be replaced.
+
+*/
+
+function diffHighlight($diff, $replaceTarget='\1', $spellOn = false)
+{
+ echo('<pre text-align="left">');
+ $diff = htmlspecialchars($diff);
+ $diff = preg_replace(array('/^\+\+\+ (.+) (.+)$/m', '/^--- (.+) (.+)$/m', '/^@@ (.+) (.+) @@$/m', '/^\+(.*)$/m', '/^-(.*)$/m', '/&lt;(\/?.+?)&gt;/'), array('<span class="highlightAddedLine">+++ </span><span class="highlightRemovedLine">'.$replaceTarget.' </span><span class="highlightComment">\2</span>', '<span class="highlightRemovedLine">--- \1 </span><span class="highlightComment">\2</span>', '<span class="highlightComment">@@ </span><span class="highlightRemovedLine">\1 </span><span class="highlightAddedLine">\2 </span><span class="highlightComment">@@</span>', '<span class="highlightAddedLine">+</span>\1', '<span class="highlightRemovedLine">-</span>\1', '<span class="highlightAddedLine">&lt;<span class="highlightRemovedLine">\1</span>&gt;</span>'), $diff);
+ echo($diff);
+ echo('</pre>');
+}
+
+/*! Run ``diff -u'' against the supplied string and the file. This function
+ does not use any temporary files and should be secure if the system does
+ not allow tracing of std(in|out) paths.
+
+ @param string The string containing the new revision.
+ @param file The file containing the old revision.
+ @return The patch as a string returned by ``diff''.
+
+*/
+
+function diffStringToFile($string, $file)
+{
+ $process = proc_open("diff -u $file -", array(0 => array('pipe', 'r'), 1 => array('pipe', 'w')), $pipes);
+ if (is_resource($process))
+ {
+ fwrite($pipes[0], $string);
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1])) {
+ $outputString .= fgets($pipes[1], 1024);
+ }
+ fclose($pipes[1]);
+ if(($errCode = proc_close($process)) > 2)
+ echo("<b>Error from includes.Diff inside [[ ".__FUNCTION__."//".__LINE__." ]]: Received erroneous code $errCode!</b>");
+ }
+ else
+ echo("<b>Error from includes.Diff inside [[ ".__FUNCTION__."//".__LINE__." ]]: Failed to spawn resource!</b>");
+
+ return $outputString;
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/common.diff.php b/includes/common.diff.php
new file mode 100644
index 0000000..5f2a8af
--- /dev/null
+++ b/includes/common.diff.php
@@ -0,0 +1,51 @@
+<?
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+function diffStringToFile($string, $file)
+{
+ $process = proc_open("diff -u - common.version", array(0 => array("pipe", "r"), 1 => array("pipe", "w")), $pipes);
+ if (is_resource($process))
+ {
+ fwrite($pipes[0], "Test!");
+ fclose($pipes[0]);
+
+ while (!feof($pipes[1])) {
+ $outputString .= fgets($pipes[1], 1024);
+ }
+ fclose($pipes[1]);
+ if(proc_close($process) > 2)
+ echo("<b>Error from includes.Diff inside [[ ".__FUNCTION__."//".__LINE__." ]]: Received erroneous code!</b>");
+ }
+ else
+ echo("<b>Error from includes.Diff inside [[ ".__FUNCTION__."//".__LINE__." ]]: Failed to spawn resource!</b>");
+
+ highlight_string($outputString);
+}
+phpinfo();
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/common.spell b/includes/common.spell
new file mode 100644
index 0000000..f190a02
--- /dev/null
+++ b/includes/common.spell
@@ -0,0 +1,86 @@
+<?
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2005 Tim Yamin < plasmaroo@gentoo.org > *
+ * < plasm@roo.me.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+class Speller {
+ var $pspell_handle;
+ var $pspell_config_handle;
+
+ function AddLocal($word)
+ {
+ pspell_add_to_personal($this->pspell_handle, $word);
+ pspell_save_wordlist($this->pspell_handle);
+ }
+
+ function HighlightSpell($text)
+ {
+ $output = '';
+
+ $words = preg_split('/(<\/?code>|\W)/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
+ foreach ($words as $word)
+ {
+ if($word == '<code>')
+ $state = true;
+ if($word == '</code>')
+ $state = false;
+ if($state)
+ {
+ $output .= $word;
+ continue;
+ }
+
+ if(pspell_check($this->pspell_handle, $word))
+ {
+ $output .= $word;
+ } else
+ {
+ $output .= '<span class="highlightAddedLine">'.$word.'</span>';
+ }
+ }
+ return $output;
+ }
+
+ function Speller($dict = 'en', $pconfig = '')
+ {
+ global $FILEROOT;
+
+ $this->pspell_config_handle = pspell_config_create($dict);
+ pspell_config_ignore($this->pspell_config_handle, 4);
+ pspell_config_mode($this->pspell_config_handle, PSPELL_NORMAL);
+ $this->pspell_handle = pspell_config_personal($this->pspell_config_handle, $FILEROOT.'/../spell.data');
+ $this->pspell_handle = pspell_new_config($this->pspell_config_handle);
+ }
+}
+
+function __spell__($string, $spell, &$speller)
+{
+ if($spell)
+ return $speller->HighlightSpell($string);
+ return $string;
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/common.version b/includes/common.version
new file mode 100644
index 0000000..f380129
--- /dev/null
+++ b/includes/common.version
@@ -0,0 +1,8 @@
+<?
+
+$GLSAMAddress='glsa-commits@gentoo.org';
+$GLSAMVersion='0.4.3.4';
+$GLSAMTag='-devSite';
+$GLSAVNeededReviews=2;
+
+?>
diff --git a/includes/io.filegrepper b/includes/io.filegrepper
new file mode 100644
index 0000000..352e833
--- /dev/null
+++ b/includes/io.filegrepper
@@ -0,0 +1,391 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo@gentoo.org > *
+ * < plasm@roo.me.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+$FILEROOT='/var/www/dev.gentoo.org/glsamaker/data';
+$FILEPOOLROOT='/var/www/dev.gentoo.org/glsamaker/pool';
+$FEXT='xml';
+
+require_once 'xml.glsaparser';
+
+/*! Return an array containing a GLSA tree of the available GLSAs
+ in $FILEROOT.
+
+ @param chronologicalOrder Sort the array in earliest to last order.
+ @param releaseOrder Sort the array by GLSA ID; not by modification date.
+ @param treeOrder Return GLSAs as they are in the data tree.
+ @return An array containg the GLSAs.
+
+*/
+
+function fileGrepper_parseTree($chronologicalOrder = false, $releaseOrder = false, $treeOrder = false)
+{
+ global $FILEROOT, $FEXT;
+ if ($GLSAHandle = @opendir($FILEROOT))
+ {
+ $GLSAYears = array();
+ while (false !== ($GLSAHandleItem = readdir($GLSAHandle)))
+ {
+ if ($GLSAHandleItem != '.' && $GLSAHandleItem != '..')
+ $GLSAYears[$GLSAHandleItem] = $GLSAHandleItem;
+ }
+
+ // Sort and parse array
+ arsort( $GLSAYears );
+
+ foreach( $GLSAYears as $GLSAYear )
+ {
+ if ($GLSAHandle = @opendir($FILEROOT.'/'.$GLSAYear))
+ {
+ $GLSAYears[$GLSAYear] = array();
+ while (false !== ($GLSAHandleItem = readdir($GLSAHandle)))
+ {
+ if($GLSAHandleItem != '.' && $GLSAHandleItem != '..')
+ $GLSAYears[$GLSAYear][$GLSAHandleItem] = $GLSAHandleItem;
+ }
+ arsort($GLSAYears[$GLSAYear]);
+ }
+
+ foreach( $GLSAYears[$GLSAYear] as $GLSAMonth )
+ {
+ if ($GLSAHandle = @opendir($FILEROOT.'/'.$GLSAYear.'/'.$GLSAMonth))
+ {
+ $GLSAYears[$GLSAYear][$GLSAMonth] = array();
+ while (false !== ($GLSAHandleItem = readdir($GLSAHandle)))
+ {
+ if($GLSAHandleItem != '.' && $GLSAHandleItem != '..' && strrchr($GLSAHandleItem, '.') == '.'.$FEXT)
+ {
+ $GLSAYears[$GLSAYear][$GLSAMonth][padNumber(basename($GLSAHandleItem, '.'.$FEXT))] = filemtime($FILEROOT.'/'.$GLSAYear.'/'.$GLSAMonth.'/'.$GLSAHandleItem);
+ }
+ }
+ arsort($GLSAYears[$GLSAYear][$GLSAMonth]);
+ }
+ }
+ }
+
+ if($treeOrder && $releaseOrder)
+ {
+ foreach($GLSAYears as $GLSAYear => $GLSAYearArray)
+ {
+ foreach($GLSAYearArray as $GLSAMonth => $GLSAMonthArray)
+ {
+ $GLSAMonthArray = array_flip($GLSAMonthArray);
+ arsort($GLSAMonthArray);
+ $GLSAYears[$GLSAYear][$GLSAMonth] = array_flip($GLSAMonthArray);
+ }
+ }
+ return $GLSAYears;
+ }
+ if($treeOrder)
+ return $GLSAYears;
+
+ // Serialize the array
+
+ $ResultArray = array();
+ $SortArray = array();
+ $MapArray = array();
+
+ foreach( $GLSAYears as $GLSAYear => $GLSAYearArray )
+ {
+ foreach( $GLSAYearArray as $GLSAMonth => $GLSAMonthArray )
+ {
+ if(!is_array($GLSAMonthArray))
+ {
+ echo '<b>Assertion from includes.IO.FileGrepper inside [[ ', __FUNCTION__, '//', __LINE__, ' ]] :: Empty monthly key $GLSAYear/$GLSAMonthArray found [ [ Possible permission ] Errors withheld! ] - no GLSAs are thus listed from that key...<br>&nbsp;</b>';
+ $GLSAMonthArray = array();
+ }
+ foreach( $GLSAMonthArray as $GLSAHandleItem => $GLSAHandleKey )
+ {
+ $ResultArray[] = $GLSAYear.$GLSAMonth.'-'.$GLSAHandleItem;
+ $SortArray[] = $GLSAHandleKey;
+ }
+ }
+ }
+
+ $i = 0;
+ if($releaseOrder)
+ {
+ $chronologicalOrder ? asort($ResultArray) : arsort($ResultArray);
+ foreach( $ResultArray as $ResultKey => $ResultItem )
+ {
+ $MapArray[$i++] = array($ResultItem, $SortArray[$ResultKey]);
+ }
+ }
+ else
+ {
+ $chronologicalOrder ? asort($SortArray) : arsort($SortArray);
+ foreach( $SortArray as $SortKey => $SortDate )
+ {
+ $MapArray[$i++] = array($ResultArray[$SortKey], $SortDate);
+ }
+ }
+
+ // DEBUG => echo '<pre>'; print_r($MapArray); echo '</pre>';
+ return $MapArray;
+ }
+ else
+ {
+ print_r('<b>Error: from Includes.IO.FileGrepper :: File tree traversal failed!</b>');
+ return false;
+ }
+
+}
+
+/*! Parse the pooled GLSAs; returning them in an array.
+
+ @param chronologicalOrder Sort the array in earliest to last order.
+ @return An array containg the GLSAs.
+
+*/
+
+function fileGrepper_parsePool($chronologicalOrder = false)
+{
+ global $FILEPOOLROOT, $FEXT;
+ if ($GLSAHandle = @opendir($FILEPOOLROOT))
+ {
+ $GLSAs = array();
+ while (false !== ($GLSAHandleItem = readdir($GLSAHandle)))
+ {
+ if ($GLSAHandleItem != '.' && $GLSAHandleItem != '..' && strrchr($GLSAHandleItem, '.') == '.'.$FEXT)
+ {
+ $GLSAs[str_replace('.'.$FEXT, '', $GLSAHandleItem)] = filemtime($FILEPOOLROOT.'/'.$GLSAHandleItem);
+ }
+ }
+
+ // Sort the modification date array and map the result data
+ $chronologicalOrder ? asort($GLSAs) : arsort($GLSAs);
+
+ $i = 0;
+ $MapArray = array();
+
+ foreach( $GLSAs as $SortKey => $SortDate )
+ {
+ $MapArray[$i] = array($SortKey, $SortDate);
+ $i++;
+ }
+ return $MapArray;
+ }
+ else
+ {
+ print_r('<b>Error: from Includes.IO.FileGrepper :: File tree traversal failed!</b>');
+ return false;
+ }
+
+}
+
+/*! Retrieve the text of a GLSA; or return the filename of the specified GLSA,
+ or edit the specified GLSA.
+
+ @param year The GLSA Year component; this should be set to NULL if the
+ GLSA is to be a pooled one.
+ @param month The GLSA Month component; this should be set to NULL if the
+ GLSA is to be a pooled one.
+ @param id The GLSA ID component; this should be set to NULL if the GLSA is
+ to be a pooled one.
+ @param override The GLSA Pool ID; this should be set to NULL if the GLSA is
+ not a pooled one.
+ @param generateStringOnly Return the path to the specified GLSA rather than
+ the text.
+ @param editGLSA Edit the specified GLSA to the XML specified in this parameter.
+ @return The GLSA Text or '<nodata>' in an error; the file string if
+ generateStringOnly is set, or a boolean returning the success or failure
+ or the edit write action.
+
+*/
+
+function fileGrepper_getGLSAText($year, $month, $id, $override = NULL, $generateStringOnly = false, $editGLSA = '')
+{
+ global $FILEROOT, $FILEPOOLROOT, $FEXT;
+ if($override != NULL)
+ {
+ if($generateStringOnly)
+ return($FILEPOOLROOT.'/'.$override.'.'.$FEXT);
+ else if(@is_file($FILEPOOLROOT.'/'.$override.'.'.$FEXT))
+ {
+ $fp = fopen($FILEPOOLROOT.'/'.$override.'.'.$FEXT, $editGLSA == '' ? 'r' : 'w');
+ if($editGLSA != '')
+ return(fwrite($fp, $editGLSA) == strlen($editGLSA) ? true : false);
+ else
+ $fd = fread($fp, filesize( $FILEPOOLROOT.'/'.$override.'.'.$FEXT));
+
+ return $fd;
+ }
+ return '<nodata>';
+ }
+
+ $year = padYear($year);
+ $month = padNumber($month);
+ $id = padNumber($id);
+
+ if($generateStringOnly === '-')
+ return($FILEROOT.'/'.$year.'/'.$month);
+ else if($generateStringOnly)
+ return($FILEROOT.'/'.$year.'/'.$month.'/'.$id.'.'.$FEXT);
+ else if(@is_file($FILEROOT.'/'.$year.'/'.$month.'/'.$id.'.'.$FEXT))
+ {
+ $fp = fopen($FILEROOT.'/'.$year.'/'.$month.'/'.$id.'.'.$FEXT, $editGLSA == '' ? 'r' : 'w');
+ if($editGLSA != '')
+ return(fwrite($fp, $editGLSA) == strlen($editGLSA) ? true : false);
+ else
+ $fd = fread($fp, filesize( $FILEROOT.'/'.$year.'/'.$month.'/'.$id.'.'.$FEXT ));
+
+ return $fd;
+ }
+ return '<nodata>';
+}
+
+/*! Add the GLSA to the pool.
+
+ @param data The GLSA XML
+ @param changeID This changes the string '!-- Insert ID here! --!' to
+ a hash.
+ @return The GLSA ID; or false on failure.
+
+*/
+
+function fileGrepper_commitToPool($data, $changeID = false)
+{
+
+ global $FILEPOOLROOT, $FEXT;
+ do { $hash = md5((double)microtime()*rand(1,2)); } while (@is_file($FILEPOOLROOT.'/'.$hash.'.'.$FEXT));
+ if($changeID) $data=str_replace('!-- Insert ID here! --!', $hash, $data);
+
+ if(!$fp = fopen($FILEPOOLROOT.'/'.$hash.'.'.$FEXT, 'w')) return false;
+ if(fwrite($fp, $data) != strlen($data)) return false;
+
+ return $hash;
+
+}
+
+/*! Move a GLSA.
+
+ @param ID The current GLSA ID
+ @param IDNew The new GLSA ID. If 'AutoMove' is specified
+ then the ID is automatically generated.
+ @return The new ID; or '<error>'.
+
+*/
+
+function fileGrepper_moveGLSA($ID, $IDNew = '')
+{
+ if($IDNew = 'AutoMove')
+ {
+ $ID1 = date('Y');
+ $ID2 = date('m');
+ $ID3 = '01';
+ $Tree = fileGrepper_parseTree(false, true, true);
+ if(count($Tree[$ID1]) > 0)
+ {
+ // Get the last GLSA = {The first array item}+1
+ if(count($Tree[$ID1][$ID2]) > 0)
+ $ID3 = key($Tree[$ID1][$ID2])+1;
+ }
+ $IDNew = $ID1.$ID2.'-'.padNumber($ID3);
+ }
+ if(ereg('^[A-Fa-f0-9]{32}$', $ID))
+ $oldFile = fileGrepper_getGLSAText(NULL, NULL, NULL, $ID, true);
+ else if(sscanf($ID, '%04d%02d-%d', $ID1, $ID2, $ID3) == 3)
+ $oldFile = fileGrepper_getGLSAText($ID1, $ID2, $ID3, NULL, true);
+ else
+ {
+ echo(generateWarning('Error: from Includes.IO.FileGrepper :: Invalid source ID [ '.$ID.' ] specified!'));
+ return '<error>';
+ }
+
+ if(ereg('^[A-Fa-f0-9]{32}$', $IDNew))
+ $newFile = fileGrepper_getGLSAText(NULL, NULL, NULL, $IDNew, true);
+ else if(sscanf($IDNew, '%04d%02d-%d', $ID1, $ID2, $ID3) == 3)
+ {
+ $newDirectory = fileGrepper_getGLSAText($ID1, $ID2, $ID3, NULL, '-');
+ $newFile = fileGrepper_getGLSAText($ID1, $ID2, $ID3, NULL, true);
+ }
+ else
+ {
+ echo(generateWarning('Error: from Includes.IO.FileGrepper.MoveGLSA :: Invalid destination ID [ '.$IDNew.' ] specified!'));
+ return '<error>';
+ }
+
+ if(@is_file($oldFile) && !@is_file($newFile))
+ {
+ if($newDirectory != '')
+ {
+ if(!@is_dir($newDirectory))
+ {
+ echo(generateInfo('Trying mkdir('.$newDirectory.')...'));
+ mkdir($newDirectory);
+ }
+ }
+ echo(generateInfo('Trying rename('.$oldFile.', '.$newFile.')...'));
+ if(rename($oldFile, $newFile))
+ {
+ $Parser = new GLSAParser();
+ $Parser->GLSAParse(fileGrepper_getGLSAText( $pool ? NULL: $ID1, $pool ? NULL: $ID2, $pool ? NULL: $ID3, $pool ? $ID : NULL ));
+
+ // Update ID
+ $Parser->GLSAID = $IDNew;
+
+ // Sanitize Review Metadata
+ $GLSAReviews =& $Parser->searchMetadata($Parser->GLSAMetadata, 0, 'reviews');
+ $GLSAReviews = NULL;
+ if($GLSAReviewsIndex = array_search(NULL, $Parser->GLSAMetadata, true))
+ unset($Parser->GLSAMetadata[$GLSAReviewsIndex]);
+
+ fileGrepper_getGLSAText( $pool ? NULL: $ID1, $pool ? NULL: $ID2, $pool ? NULL: $ID3, $pool ? $ID : NULL, false, $Parser->GLSAToXML('reset'));
+ return $IDNew;
+ }
+ else
+ return '<error>';
+ }
+ echo(generateWarning('Error: from Includes.IO.FileGrepper.MoveGLSA :: Missing or existing file [ '.$ID.' >> '.$IDNew.' ] specified!'));
+ return '<error>';
+}
+
+/*! Pad a number with a zero to make it at least two characters long. */
+
+function padNumber($input)
+{
+
+ if($input == '') return '00';
+
+ if(strlen($input) == 1) return '0'.$input;
+ return $input;
+
+}
+
+/*! Pad a year with '20' if a two-digit year is given; or return
+ the current year if a null string is given. */
+
+function padYear($input)
+{
+
+ if($input == '') return date('Y');
+ if(strlen($input) == 2) return '20'.$input;
+ return $input;
+
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/io.mailer b/includes/io.mailer
new file mode 100644
index 0000000..a9c16c8
--- /dev/null
+++ b/includes/io.mailer
@@ -0,0 +1,106 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+function getMailList()
+{
+ $list = array();
+ $input = file('/var/www/dev.gentoo.org/glsamaker/email.alias');
+
+ foreach ($input as $line)
+ {
+ if($line{0} != '#' && $line != '')
+ {
+ list($nick, $email) = explode('::', $line);
+
+ if($email != '' && $nick != '')
+ $list[$nick] = trim($email);
+ }
+ }
+
+ return $list;
+}
+
+function mailGLSA($mailTo, $mailSubject, $mailBody, $mailAttach = false, $mailHeader = '')
+{
+ $mimeBoundary = '<<<'.md5((double)microtime()*rand(1,2)).'>>>';
+ $lineBreak = "\r\n";
+
+ $header = 'From: GLSAMaker <plasmaroo@gentoo.org>'.$lineBreak;
+ if($mailTo{0} == '%')
+ {
+ $mailTo = substr($mailTo, 1);
+ $list = getMailList();
+
+ $i = 0;
+ foreach($list as $nick => $email)
+ {
+ if($i != 0)
+ $cc .= ', ';
+
+ $cc .= $nick.' <'.$email.'>';
+ $i = 1;
+ }
+
+ if(count($list) > 0)
+ $header .= 'Cc: '.$cc.$lineBreak;
+ }
+
+ $header .= $mailHeader.$lineBreak;
+ $header .= 'MIME-Version: 1.0'.$lineBreak;
+ $header .= 'Content-Type: multipart/mixed; boundary="'.$mimeBoundary.'"'.$lineBreak;
+
+ $body = 'This is a multi-part message in MIME format.'.$lineBreak.$lineBreak;
+ $body .= '--'.$mimeBoundary.$lineBreak;
+ $body .= 'Content-Type: text/plain;'.$lineBreak;
+ $body .= 'Content-Transfer-Encoding: 7bit'.$lineBreak.$lineBreak;
+ $body .= $mailBody.$lineBreak;
+
+ if(is_array($mailAttach))
+ {
+ foreach($mailAttach as $fileName => $fileData)
+ {
+ $body .= '--'.$mimeBoundary.$lineBreak;
+ $body .= 'Content-Disposition: attachment;'.$lineBreak;
+ if( !eregi('[[:print:]]+', $fileData) )
+ {
+ $body .= "Content-Type: application/octet-stream; name=\"$fileName\"".$lineBreak;
+ $body .= 'Content-Transfer-Encoding: base64'.$lineBreak;
+ $body .= chunk_split(base64_encode(implode('', $fileData))).$lineBreak;
+ }
+ else
+ {
+ $body .= "Content-Type: text/plain; name=\"$fileName\"".$lineBreak;
+ $body .= 'Content-Transfer-Encoding: 7bit'.$lineBreak;
+ $body .= $fileData.$lineBreak;
+ }
+ }
+ }
+ return mail($mailTo, $mailSubject, $body, $header);
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/jpgraph/imgdata_balls.inc b/includes/jpgraph/imgdata_balls.inc
new file mode 100644
index 0000000..714d55c
--- /dev/null
+++ b/includes/jpgraph/imgdata_balls.inc
@@ -0,0 +1,1065 @@
+<?php
+//=======================================================================
+// File: IMGDATA_ROUNDBALLS.INC
+// Description: Base64 encoded images for small round markers
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_balls.inc,v 1.4 2003/04/26 10:03:50 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Balls extends ImgData {
+ var $name = 'Round Balls';
+ var $an = array(MARK_IMG_LBALL => 'imgdata_large',
+ MARK_IMG_MBALL => 'imgdata_small',
+ MARK_IMG_SBALL => 'imgdata_xsmall',
+ MARK_IMG_BALL => 'imgdata_xsmall');
+ var $colors_1 = array('blue','lightblue','brown','darkgreen',
+ 'green','purple','red','gray','yellow','silver','gray');
+ var $index_1 = array('blue'=>9,'lightblue'=>1,'brown'=>6,'darkgreen'=>7,
+ 'green'=>8,'purple'=>4,'red'=>0,'gray'=>5,'silver'=>3,'yellow'=>2);
+ var $maxidx_1 = 9 ;
+
+ var $colors_2 = array('blue','bluegreen','brown','cyan',
+ 'darkgray','greengray','gray','green',
+ 'greenblue','lightblue','lightred',
+ 'purple','red','white','yellow');
+
+
+ var $index_2 = array('blue'=>9,'bluegreen'=>13,'brown'=>8,'cyan'=>12,
+ 'darkgray'=>5,'greengray'=>6,'gray'=>2,'green'=>10,
+ 'greenblue'=>3,'lightblue'=>1,'lightred'=>14,
+ 'purple'=>7,'red'=>0,'white'=>11,'yellow'=>4);
+
+ var $maxidx_2 = 14 ;
+
+
+ var $colors_3 = array('bluegreen','cyan','darkgray','greengray',
+ 'gray','graypurple','green','greenblue','lightblue',
+ 'lightred','navy','orange','purple','red','yellow');
+
+ var $index_3 = array('bluegreen'=>1,'cyan'=>11,'darkgray'=>14,'greengray'=>10,
+ 'gray'=>3,'graypurple'=>4,'green'=>9,'greenblue'=>7,
+ 'lightblue'=>13,'lightred'=>0,'navy'=>2,'orange'=>12,
+ 'purple'=>8,'red'=>5,'yellow'=>6);
+ var $maxidx_3 = 14 ;
+
+ var $colors,$index,$maxidx;
+ var $imgdata_large ;
+ var $imgdata_small ;
+ var $imgdata_xsmall ;
+
+
+ function GetImg($aMark,$aIdx) {
+ switch( $aMark ) {
+ case MARK_IMG_SBALL:
+ case MARK_IMG_BALL:
+ $this->colors = $this->colors_3;
+ $this->index = $this->index_3 ;
+ $this->maxidx = $this->maxidx_3 ;
+ break;
+ case MARK_IMG_MBALL:
+ $this->colors = $this->colors_2;
+ $this->index = $this->index_2 ;
+ $this->maxidx = $this->maxidx_2 ;
+ break;
+ default:
+ $this->colors = $this->colors_1;
+ $this->index = $this->index_1 ;
+ $this->maxidx = $this->maxidx_1 ;
+ break;
+ }
+ return parent::GetImg($aMark,$aIdx);
+ }
+
+ function ImgData_Balls() {
+
+//==========================================================
+// File: bl_red.png
+//==========================================================
+ $this->imgdata_large[0][0]= 1072 ;
+ $this->imgdata_large[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAByF'.
+ 'BMVEX/////////xsb/vb3/lIz/hIT/e3v/c3P/c2v/a2v/Y2P/'.
+ 'UlL/Skr/SkL/Qjn/MTH/MSn/KSn/ISH/IRj/GBj/GBD/EBD/EA'.
+ 'j/CAj/CAD/AAD3QkL3MTH3KSn3KSH3GBj3EBD3CAj3AAD1zMzv'.
+ 'QkLvISHvIRjvGBjvEBDvEAjvAADnUlLnSkrnMTnnKSnnIRjnGB'.
+ 'DnEBDnCAjnAADec3PeSkreISHeGBjeGBDeEAjWhITWa2vWUlLW'.
+ 'SkrWISnWGBjWEBDWEAjWCAjWAADOnp7Oa2vOGCHOGBjOGBDOEB'.
+ 'DOCAjOAADJrq7Gt7fGGBjGEBDGCAjGAADEpKS/v7+9QkK9GBC9'.
+ 'EBC9CAi9AAC1e3u1a2u1Skq1KSm1EBC1CAi1AACtEBCtCBCtCA'.
+ 'itAACngYGlCAilAACghIScOTmcCAicAACYgYGUGAiUCAiUAAiU'.
+ 'AACMKSmMEACMAACEa2uEGAiEAAB7GBh7CAB7AABzOTlzGBBzCA'.
+ 'BzAABrSkprOTlrGBhrAABjOTljAABaQkJaOTlaCABaAABSKSlS'.
+ 'GBhSAABKKSlKGBhKAABCGBhCCABCAAA5CAA5AAAxCAAxAAApCA'.
+ 'ApAAAhAAAYAACc9eRyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFD'.
+ 'UHLytKAAAB4UlEQVR4nGNgIAK4mGjrmNq6BmFIWMmISUpKSmk5'.
+ 'B8ZEokj4qoiLiQCBgqald3xaBpKMj6y4sLCQkJCIvIaFV0RaUR'.
+ 'lCSk5cWEiAn19ASN7QwisuraihHiajKyEixM/NwckjoKrvEACU'.
+ 'qumpg7pAUlREiJdNmZmLT9/cMzwps7Smc3I2WEpGUkxYkJuFiY'.
+ 'lTxszePzY1v7Shc2oX2D+K4iLCgjzsrOw8embuYUmZeTVtPVOn'.
+ 'gqSslYAOF+Ln4ZHWtXMPTcjMrWno7J82rRgoZWOsqaCgrqaqqm'.
+ 'fn5peQmlsK1DR52vRaoFSIs5GRoYG5ub27n19CYm5pdVPnxKnT'.
+ 'pjWDpLydnZwcHTz8QxMSEnJLgDL9U6dNnQ6Sio4PDAgICA+PTU'.
+ 'zNzSkph8hADIxKS46Pj0tKTc3MLSksqWrtmQySAjuDIT8rKy0r'.
+ 'Kz+vtLSmur6jb9JUIJgGdjxDQUVRUVFpaUVNQ1NrZ9+kKVOmTZ'.
+ 'k6vR0sldJUAwQNTU2dnX0TgOJTQLrSIYFY2dPW1NbW2TNxwtQp'.
+ 'U6ZMmjJt2rRGWNB3TO7vnzh5MsgSoB6gy7sREdY7bRrQEDAGOb'.
+ 'wXOQW0TJsOEpwClmxBTTbZ7UDVIPkp7dkYaYqhuLa5trYYUxwL'.
+ 'AADzm6uekAAcXAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bl_bluegreen.png
+//==========================================================
+ $this->imgdata_large[1][0]= 1368 ;
+ $this->imgdata_large[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMMFi8hE9b2uAAABOVJREFUeNq9lk2sJFUVx3+3qv'.
+ 'tW95t57zFvhiFxmCFRUJRoNCQiJARMhiFx/Igxii5goTG6ZDAu'.
+ '/EhcSCIrTAgLEiKsJ8ywABNZEMJXEDYCukAmjgjzBkK/j35V1d'.
+ '333FtV97io97pfzwxfG86qcu/N+Z3zP+fcW/Apmfk4hx57+R/6'.
+ 'Rqmc9ykhsWjlsUngAA1fXIQ7b73pI/186IGHnn9dH/8frC8v4I'.
+ 'PiG53uaerR4GmKkv31mB8cyfjd946ZTwR66qVX9OTWIi8UKUv9'.
+ 'BOrZXpYZvFeiBvzI0fgSUSFKwbVG+Pl1V3HH0VvNR4KeeukV/f'.
+ 'PmMmdHhst76aXD64AbeVQ9bjNHaiGOC2o3wLrAb2/4LL/84ffn'.
+ 'fCdzkOdayKpLppBemrBsU5Y1Zdmm9LJdGU6E/t4M24Q26jRDRL'.
+ 'j3mdc49cSTekFsMzs5XuTsyLDUNSDQ25NwKOly9YIl22MYhJr/'.
+ 'uoDtBBoT0CxBRGYOAhibIaOCe//2MpfM6KHnX9cXipSlbkKWmS'.
+ 'nk9iv38J0jixw7vJfrTMYBOvhSoQHJBS09ANELloAGDxW8tfoW'.
+ 'J+5/UC8CPS0LU7r3SpYarr7M8rmFjMPLXT6/33L4si7Z2GCrQC'.
+ '+0ctlOaNs9DReV8vSLr85ndPLpZ/WNvHW+01kAVFBOGvJx0wYg'.
+ 'Sp47RIQ4Emwa8FGJXlDxSCFo5YlVgAo2hwPue/hRndboTV3EW2'.
+ 'Wp3k6wBp8q56QiWzecW6vwQfnPRkAWhFgILnq08jQ+R2nlUzzN'.
+ 'uES9Q7Vd+9fba7NmWJW61db2247qACmcjxXr45psYphsFGSLBu'.
+ 'kIajxqtjNwHkvAjQt0sg3crhPA2+fPz0CuyNFOghsGsr19mnFg'.
+ 'DGwrRm8UoAtNmQPQtRXDgdC4HImCFEKcCE0oieUWUYq2LtbiGp'.
+ 'mBQmppfIkjw45DK0QNNkvQ0jMBtPL0UnDRM1rN+cxKwzvOo2NP'.
+ 'tykR9a1kfpZNDLMG6QDYJqCTBvUe1+uxs+YKyPoGrTwY2HhvC4'.
+ 'CDWQd5d4xNApNQEEMgjgLdUCLBQ5cprL/trwNwKG2IUmDqDFd5'.
+ 'sr5BWrlxuSdLDFEFlqAzXGc4zFjupqh6uqYihpxJcEgp026l2w'.
+ '7wFUv7Z6AvrfRo/n0OYzPwIKE3HUKAJg2otMBiElnsF7wngis9'.
+ '3ZDjNnLi7huCWUZfueZKTu/M0V3HvmkOFDVxVKDG04ScejSgW5'.
+ 'V0q5JYFEghuDLHlTmToqDeGOCKIVtrW9hsdmXufEcNLPSXuPHa'.
+ 'a+bvuh9df5AH/v5PDFmbWQC3Mx+TVvfGVTRB2CodNgT2JBX003'.
+ 'aANZAYS/BxCv32TV/l2C03G7jgmfjGiT/qmeEmibEYm7XzAO2k'.
+ 'A+pbgHhBgydqu54YO5eRiLCy7yDvPP6Xqf+5Z+Lu277OYuOpiw'.
+ 'H15oBmlNOMcmK5RbP+PrEscGU+DSAxdg4CICIkxnLP8aNz63Og'.
+ 'H3/rdvOb795GVhuaYo0oBc3GGrEsUPVTwO6a7LYd+X51x3Hu/t'.
+ 'lP5tS65FN+6okn9U+n/sqb596dTvhOF+02myXTmkQNrOw7yD3H'.
+ 'j14E+UDQjp24/0E9/eKrbA4HH3aMK1b2ccvXvswjv//1J/s5ud'.
+ 'Due/hRPfP+OmfOrk7vrn7a48ihA3zh8CH+8Iuffiw/n4r9H1ZZ'.
+ '0zz7G56hAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_yellow.png
+//==========================================================
+ $this->imgdata_large[2][0]= 1101 ;
+ $this->imgdata_large[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
+ 'BMVEX//////////+///+f//9b//8b//73//7X//63//6X//5T/'.
+ '/4z//4T//3P//2v//1r//0r//0L//zH//yn//yH//xj//xD//w'.
+ 'j//wD/90L/9zn/9zH/9xj/9xD/9wj/9wD39yn37zn37zH37yH3'.
+ '7xD37wj37wDv70Lv50rv50Lv5znv5yHv5xjv5wjv5wDn51Ln5x'.
+ 'Dn3jHn3iHn3hjn3hDn3gje3oze3nPe3lLe1oze1nPe1lLe1ine'.
+ '1iHe1hje1hDe1gje1gDW1qXW1mvWzqXWzkLWzhjWzhDWzgjWzg'.
+ 'DOzrXOzq3OzpzOzgDOxkrOxinOxhjOxhDOxgjOxgDGxqXGxnvG'.
+ 'xmvGvRjGvRDGvQjGvQDFxbnAvr6/v7+9vaW9vZS9vQi9vQC9tR'.
+ 'C9tQi9tQC7u7W1tZS1tXu1tTG1tQi1rRC1rQi1rQCtrYytrSGt'.
+ 'rQitrQCtpYStpSGtpQitpQClpYSlpXulpQClnBClnAilnACcnG'.
+ 'ucnAicnACclAiclACUlFqUlCmUlAiUlACUjFKUjAiUjACMjFKM'.
+ 'jEqMjACMhACEhACEewB7ezF7exB7ewB7cwBzcylzcwBzaxBzaw'.
+ 'BraxhrawhrawBrYxBrYwBjYwBjWgBaWgBaUgCXBwRMAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwkRFBKiJZ4hAAAB7ElEQVR4nI3S+1vS'.
+ 'UBgHcB67WJmIMWAVdDHEDLBC6Go0slj3Ft0m9RRBWQEmFZFDEM'.
+ 'Qgt0EMFBY7p/+198hj1kM/9N1+++x73rOd6XT/kStnTx4fPzd9'.
+ 'uwfOjFhomj7smAhwj/6Cm2O0xUwy6g7cCL99uCW3jtBmE7lsdr'.
+ 'fvejgpzP7uEDFRRoqy2k8xQPnypo2BUMP6waF9Vpf3ciiSzErL'.
+ 'XTkPc0zDe3bsHDAcc00yoVgqL3UWN2iENpspff+2vn6D0+NnZ9'.
+ '6lC5K6RuSqBTZn1O/a3rd7v/MSez+WyIpVFX8GuuCA9SjD4N6B'.
+ 'oRNTfo5PCAVR0fBXoIuOQzab1XjwwNHx00GOj8/nKtV1DdeArk'.
+ '24R+0ul9PjmbrHPYl+EipyU0OoQSjg8/m83kl/MMhx0fjCkqio'.
+ 'SMOE7t4JMAzDsizH81AqSdW2hroLPg4/CEF4PhKNx98vlevrbY'.
+ 'QQXgV6kXwVfjkTiSXmhYVcSa7DIE1DOENe7GM6lUym0l+EXKks'.
+ 'K20VAeH2M0JvVgrZfL5Qqkiy0lRVaMBd7H7EZUmsiJJcrTdVja'.
+ 'wGpdbTLj3/3qwrUOjAfGgg4LnNA5tdQx14Hm00QFBm65hfNzAm'.
+ '+yIFhFtzuj+z2MI/MQn6Uez5pz4Ua41G7VumB/6RX4zMr1TKBr'.
+ 'SXAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_silver.png
+//==========================================================
+ $this->imgdata_large[3][0]= 1481 ;
+ $this->imgdata_large[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAMAAAAM7l6QAAADAF'.
+ 'BMVEUAAADOzs7Gxsa9vb21tbXOxsbOzsbGzsb3///O1ta1vb2c'.
+ 'paVSWlpKWlpSY2ve5+97hIze7/9aY2vO5/9zhJRaa3tSY3PGzt'.
+ 'aMlJxrc3tja3NKUlpCSlK1vcZze4RSWmPW5/+Upb3G3v9zhJxS'.
+ 'Y3t7jKVaa4TO3veltc6ElK1re5Rjc4ycpbV7hJRaY3M5QlLn7/'.
+ '/Gzt6lrb2EjJzO3v9ja3vG1ve9zu+1xueltdacrc6UpcaMnL1C'.
+ 'SlqElLV7jK1zhKVre5zW3u/O1ue1vc6ttcaMlKVze4xrc4RSWm'.
+ 'tKUmPG1v+9zve1xu+tveeltd6crdbe5/+9xt6cpb17hJxaY3s5'.
+ 'QlrW3vfO1u/Gzue1vdattc6lrcaUnLWMlK2EjKVze5Rrc4xja4'.
+ 'RSWnNKUmtCSmO9xuecpcZ7hKVaY4TW3v/O1vfGzu+1vd6ttdal'.
+ 'rc69xu+UnL2MlLWEjK1ze5xrc5R7hK1ja4zO1v+1veettd6lrd'.
+ 'aMlL3Gzv/39//W1t7Gxs61tb29vcatrbWlpa2cnKWUlJyEhIx7'.
+ 'e4TW1ufGxta1tcZSUlqcnK3W1u+UlKW9vda1tc57e4ytrcalpb'.
+ '1ra3vOzu9jY3OUlK29vd6MjKWEhJxaWmtSUmNzc4xKSlpjY3tK'.
+ 'SmNCQlqUjJzOxs7///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'.
+ 'AAAAAAAAAAAAAAAAAAAAAAAAD///9fnkWVAAAAAnRSTlP/AOW3'.
+ 'MEoAAAABYktHRP+lB/LFAAAACXBIWXMAAABFAAAARQBP+QatAA'.
+ 'AB/klEQVR42mNgxAsYqCdd3+lcb4hLmj8wMMvEu8DCMqYbU9op'.
+ 'UEFB2MTb26eyysomFl06XEEhUCHLpAKo2z/fujikEUVaXUFBMB'.
+ 'BouLePuV+VVWGRciIXknSEsImCQd3//xwmPr65llaFcSFJHkjS'.
+ '3iYmWUDZ//8NfCr989NjNUMSUyTg0jneSiaCINn/gmlVQM12qg'.
+ 'lJnp5waTMTE5NAkCyHWZW/lXWNfUlikmdYK0zax7siS4EDKJtd'.
+ 'mQeU1XRwLBdLkRGASucWmGVnZ4dnhZvn5lmm29iVOWpnJqcuko'.
+ 'JKR1Wm5eTkRKYF5eblp9sU2ZeUJiV7zbfVg0pH56UFBQXNjIqK'.
+ 'jgkujItX1koKTVmYajsdKu2qETVhwgSXiUDZ2Bn9xqUeoZ5e0t'.
+ 'LzYYZ3B092ndjtOnmKTmycW1s7SHa+l5dtB8zlccE6RlN0dGbM'.
+ 'mDVbd5KupNBcL6+F82XgHouLj5vRP2PWLGNdd4+ppnxe8tJec6'.
+ 'XnNsKkm0uVQ5RDRHQTPTym68nPlZbvkfYCexsa5rpJ2qXa5Umm'.
+ 'ocmec3m8vHjmSs+fgxyhC5JDQ8WSPT2lvbzm8vDIe0nbtiBLN8'.
+ '8BigNdu1B6Lsje+fPbUFMLi5TMfGmvHi/puUAv23q2YCTFNqH5'.
+ 'MvPnSwPh3HasCbm3XUpv+nS5VtrkEkwAANSTpGHdye9PAAAASn'.
+ 'RFWHRzaWduYXR1cmUANGJkODkyYmE4MWZhNTk4MTIyNDJjNjUx'.
+ 'NzZhY2UxMDAzOGFhZjdhZWIyNzliNTM2ZGFmZDlkM2RiNDU3Zm'.
+ 'NlNT9CliMAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bl_purple.png
+//==========================================================
+ $this->imgdata_large[4][0]= 1149 ;
+ $this->imgdata_large[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACAV'.
+ 'BMVEX/////////7///5///1v//xv//rf//pf//lP//jP//hP//'.
+ 'c///a///Wv//Wvf/Uv//Sv//Qv//Qvf/Off/Mf//Kf//If//If'.
+ 'f/GP//GPf/EP//EPf/CP//CPf/CO//AP//APf3Oe/3Kff3Ke/3'.
+ 'Ie/3GO/3EO/3AO/vSu/vSufvOefvMefvIefvGOfvEOfvCOfvAO'.
+ 'fnUufnSufnMd7nId7nGN7nGNbnEN7nCN7nAN7ejN7ejNbec97e'.
+ 'c9beUtbeQtbeIdbeGNbeENbeCNbeANbWpdbWa9bWQs7WGM7WEM'.
+ '7WCM7WAM7Otc7Orc7OnM7OSsbOIb3OGMbOEMbOCMbOAM7OAMbG'.
+ 'pcbGnMbGe8bGa8bGKbXGEL3GCL3GAL3FucXBu73AvsC/v7+9pb'.
+ '29Ka29GLW9ELW9CLW9AL29ALW5rrm1lLW1e7W1MbW1GKW1EK21'.
+ 'CLW1CK21AK2tjK2thKWtMaWtIaWtGJytCK2tCKWtAK2tAKWlhK'.
+ 'Wle6WlEJylCJylAKWlAJyca5ycGJScEJScCJScAJycAJSUWpSU'.
+ 'UoyUKZSUEIyUCIyUAJSUAIyMUoyMSoyMIYSMEISMCISMAIyMAI'.
+ 'SECHuEAISEAHt7MXt7EHt7CHt7AHt7AHNzKXNzEGtzAHNzAGtr'.
+ 'GGtrEGNrCGtrAGtrAGNjCFpjAGNjAFpaAFpaAFIpZn4bAAAAAX'.
+ 'RSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsS'.
+ 'AdLdfvwAAAAHdElNRQfTAwkRFB0ymoOwAAAB9UlEQVR4nGNgIA'.
+ 'K42hhqGtm5+WFIWClKycvLK6gbuARGoEj4aMjLSElISUir6Tt7'.
+ 'x+aEIWR8leQlwEBSTc/CK7awLguuR0lGQkJMVFRUTFJVzwko1d'.
+ 'oFk9OQl5IQE+Dh5hVR0TV3CkkvbJgyASJjDZIR5GBl5eRX0TH1'.
+ 'DEqrbJ2ypBEspSgvJSXKw8bMxMavbOLoGZNf1TZlybw4oIyfLN'.
+ 'BxotxsLEzsQiaOHkFpBQ2905esrAZK2SpIAaUEuDm5+LTNPAKj'.
+ 'C+pbps1evrIDKGWnLictKSkuLKyoZQyUya9o7Z2+YMXKGUApew'.
+ 'M9PTVdXR0TEwf3wOjUirruafOXL18xFyjl72Kpb25qaurg4REU'.
+ 'EFVe2zJ5zpLlK1aCpbydnZ2dnDwDA6NTopLLeiZNXbB8BcTAyP'.
+ 'TQ0JDg4KCY1NS83JKmiVOBepYvX9UPlAovzEiPSU/LLyior2vq'.
+ 'mjZr3vLlIF01IC+XVhUWFlZW1Lc290ycOGfxohVATSsXx4Oksn'.
+ 'vaWlsb2tq6J0+bM2/RohVA81asbIcEYueU3t7JU6ZNnwNyGkhm'.
+ '+cp5CRCppJnzZ8+ZM3/JUogECBbBIixr8Yqly8FCy8F6ltUgoj'.
+ 'lz7sqVK2ByK+cVMSCDxoUrwWDVysXt8WhJKqG4Y8bcuTP6qrGk'.
+ 'QwwAABiMu7T4HMi4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_gray.png
+//==========================================================
+ $this->imgdata_large[5][0]= 905 ;
+ $this->imgdata_large[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAABO1'.
+ 'BMVEX////////3///39/fv7+/e5+fW3t7Wzs7WxsbG1tbGzsbG'.
+ 'xsbDxMS/v7++wMC+v7+9zsa9xsa9vb29tbW9ra29pa24uLi1xs'.
+ 'a1vb21tbWxtrattbWmpqalra2cra2cpaWcnJycjIyUpaWUnJyU'.
+ 'lJSUjIyMnJyMnJSMlJSMlIyMjJSMjIyElJSElIyEjIyEhIR7jI'.
+ 'x7hIR7hHt7e3t7e3N7e2tzhIRze3tze3Nzc3Nre3trc3Nrc2tr'.
+ 'a2tjc3Njc2tja3Nja2tjY2NjWlpaa2taY2taY2NaY1paWlpaUl'.
+ 'JSY2NSY1pSWlpSWlJSUlJSUkpKWlpKWlJKUlpKUlJKUkpKSkpK'.
+ 'SkJCUlJCUkJCSkpCSkJCQkI5Sko5QkI5Qjk5OUI5OTkxQkIxOT'.
+ 'kxMTkxMTEpMTEhMTEhKSkYISEpy7AFAAAAAXRSTlMAQObYZgAA'.
+ 'AAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdE'.
+ 'lNRQfTAwkRFQfW40uLAAABx0lEQVR4nI3SbXfSMBQA4NV3nce5'.
+ 'TecAHUywRMHSgFuBCFsQUqwBS1OsWQh0GTj//y8wZUzdwQ/efM'.
+ 'tzcm/uuXdj4z9ic/PR9k4qk1qDnf0X2/uZzKt8GaRvSubg4LVp'.
+ 'mkWzCGAT/i3Zsm2XNQHLsm2n2937LaaNnGoJFAEo27B50qN0ay'.
+ 'Wg26lXsw8fP8nmzcJb2CbsnF5JmmCE8ncN404KvLfsYwd7/MdV'.
+ 'Pdgl/VbKMIzbuwVgVZw2JlSKJTVJ3609vWUY957lgAUd1KNcqr'.
+ 'yWnOcOPn8q7d5/8PywAqsOOiVDrn42NFk+HQ7dVuXNYeFdBTpN'.
+ 'nY5JdZl8xI5Y+HXYaTVqEDp1hAnRohZM03EUjMdhn5wghOoNnD'.
+ 'wSK7KiiDPqEtz+iD4ctdyAifNYzUnScBSxwPd6GLfRURW7Ay5i'.
+ 'pS5bmrY8348C5vvUI+TLiIVSJrVA0heK/GDkJxYMRoyfCSmk4s'.
+ 'uWc3yic/oBo4yF374LGQs5Xw0GyQljI8bYmEsxVUoKxa6HMpAT'.
+ 'vgyhU2mR8uU1pXmsa8ezqb6U4mwWF/5MeY8uLtQ0nmmQ8UWYvb'.
+ 'EcJaYWar7QhztrO5Wr4Q4hDbAG/4hfTAF2iCiWrCEAAAAASUVO'.
+ 'RK5CYII=' ;
+
+//==========================================================
+// File: bl_brown.png
+//==========================================================
+ $this->imgdata_large[6][0]= 1053 ;
+ $this->imgdata_large[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABkAAAAZCAMAAADzN3VRAAABoV'.
+ 'BMVEX////Gzs7GvbXGrZTGpXu9nHO1nHO1nIy9taXGxs7GtaXO'.
+ 'nHPGlFrGjEq9hEq1hEqte0Klczmcazmce1KtnIzGxsbGvb3OlF'.
+ 'LOlFq9hFKte0qcc0KUYzGEWimMc1K9ta3OnGvOnGPWnGO9jFq9'.
+ 'jFKlc0KUazmMYzl7UilzUjGtpZzGxr3GnGPWpWvepXO1hFJ7Wj'.
+ 'FrSiFjUjG1ra3GnHPvxpT/5733zpythFKUa0KEYzlzUilaOSF7'.
+ 'Wjm9jErvvYz/99b///f/78bnrYS1hFqle0p7UjFrSiljQiFCMR'.
+ 'iMhHO9lGvGjFLWnGv/3q3////erXuthEqlc0paQiFKMRhSQin/'.
+ '1qX/997//++cc0pjSilaQilKORhCKRiclIy9pYzGlGPntYT33q'.
+ '3vvZSEWjlSOSE5KRB7c2O1lHutczmthFqte1JrWkqtjGtCKRBa'.
+ 'SjmljGuca0KMYzGMaznOztaclISUYzmEWjFKOSF7a1qEYzFaSi'.
+ 'GUjISEa0pKOSm9vb2llIxaQhg5IQiEc2tzY0paORilnJy1raVS'.
+ 'OSljUkJjWkKTpvQWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkREiei'.
+ 'zP2EAAAB9UlEQVR4nGWS/VfSUBjHL5QluhhBxtwyWcCus5Blpm'.
+ 'wDC4ONaWXCyBi7RMZmpQ2Bypm9W/byV3cHHo/W88s95/s5z/d5'.
+ 'uwCcCh/4L3zAf+bs0NC588On9QAYGSUuBINk6GI4cmnsBLk8Go'.
+ '1SFEGMkzRzZeLq5JE8FvDHouw1lqXiCZJOcnCKnx4AcP0GBqmZ'.
+ 'mRgRT9MMB4Wbs7cGSXNRik3dnp9fiMUzNCNKgpzN9bsaWaQo9s'.
+ '7dfH7pXiFTZCBU1JK27LmtBO8TDx7mV1eXHqXXyiIUFLWiVzHx'.
+ 'BxcJIvV4/cn6wkqmWOOwmVE3UQOAp6HxRKL5bGPj+VwhUhalFq'.
+ '8alm5vAt+LlySZTsebzcKrraIIW4JqZC3N3ga+1+EQTZKZta1M'.
+ 'pCZCSeDViqVrThsEdsLJZLJYLpZrHVGScrKBvTQNtQHY6XIM02'.
+ 'E6Ik7odRW1Dzy3N28n3kGuB3tQagm7UMBFXI/sATAs7L5vdbEs'.
+ '8Lycm923NB0j5wMe6KOsKIIyxcuqauxbrmlqyEWfPmPy5assY1'.
+ 'U1SvWKZWom9nK/HfQ3+v2HYZSMStayTNN0PYKqg11P1nWsWq7u'.
+ '4gJeY8g9PLrddNXRdW8Iryv86I3ja/9s26gvukhDdvUQnIjlKr'.
+ 'IdZCNH+3Xw779qbG63f//ZOzb6C4+ofdbzERrSAAAAAElFTkSu'.
+ 'QmCC' ;
+
+//==========================================================
+// File: bl_darkgreen.png
+//==========================================================
+ $this->imgdata_large[7][0]= 1113 ;
+ $this->imgdata_large[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAAB2l'.
+ 'BMVEX////////3///v///n/+/e99bW/+/W99bO786/v7++vr69'.
+ '/96999a7wb24vbu1/9a1zqW1u7itxrWosq6l772l1qWlxrWlxq'.
+ '2lva2cxpSU562U3q2UxqWUvaWUpZyM77WM57WMvYyMtZyMrZyM'.
+ 'pZSMnJSEvZyEtYyErZSElIx7zpR7xpx7xpR7vZR7jIRz1pRzxp'.
+ 'RzjIRrzpRrzoxrxoxrtYRrrYxrrXtrpYRrhHNjzoxjxoxjxoRj'.
+ 'vYRjtYRjrXtjpXtjlGNje2tazoxazoRaxoxaxoRavYRatYRatX'.
+ 'tarXtapXNanHNajFpae2tSzoRSxoRSvXtStXtSrXtSrXNSpXNS'.
+ 'nHNSnGtSlGtSlGNSjGtSjGNKvXtKtXNKrXNKpWtKnGtKlGNKjG'.
+ 'NKhGNKhFJKc1pKa1JCrWtCpWtCnGtClGNCjGNCjFpChFpCe1JC'.
+ 'a1JCY1I5pWs5nGM5lGM5jFo5hFo5e1o5c0o5WkoxjFoxhFoxhF'.
+ 'Ixe1Ixc1Ixc0oxa0ophFIpe0opc0opa0opa0IpY0IpWkIpWjkp'.
+ 'UkIpUjkhc0oha0IhY0IhWjkhWjEhUjkhUjEhSjEhSikhQjEhQi'.
+ 'kYWjkYSjEYSikYQjEYQikQSikQQikQQiEQOSExf8saAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwkRFCaDkWqUAAAB+ElEQVR4nI3S+1vS'.
+ 'UBgHcGZlPV0ks/vFrmQWFimJjiwiYUJWjFBWFhClyZCy5hLrwA'.
+ 'x2EIwJC1w7zf2vnU0re+iHvs9++7x7zznvORbLf+TA6ct9fYMX'.
+ 'jrfAUYefpp+/iM1ykxf/lmuhUZ/PTwXC8dml5Wcd23o5H5Mk6b'.
+ '5NUU8icXbhS67rNzn9JDnguOEYGQtEEtwC+Crs3RJ76P5A/znr'.
+ 'vsNX7wQnEiwHCtK7TTkW8rvdZ9uJtvZTLkxpHhSrP66bNEj7/P'.
+ '3WNoLYeeSWQQCIpe9lQw7RNEU5rDsIYtcJ14Nocg7kRUlBNkxn'.
+ 'YmGKcp7cv3vPwR7XOJPmc0VYU3Sv0e9NOBAYG7Hbz/cMjTMveZ'.
+ 'CHkqxuTBv0PhYJB4N3XR6PJ5rMAPMnpGUxDX1IxSeMTEaZp1OZ'.
+ 'nGAIQiYtsalUIhFlmGTy3sO3AizJCKn6DKYryxzHsWyaneMzr6'.
+ 'cWxRVZVlFTe4SpE3zm+U/4+whyiwJcWVMQNr3XONirVWAklxcE'.
+ 'EdbqchPhjhVzGpeqhUKhWBQhLElr9fo3pDaQPrw5xOl1CGG1JE'.
+ 'k1uYEBIVkrb02+o6RItfq6rBhbw/tuINT96766KhuqYpY3UFPF'.
+ 'BbY/19yZ1XF1U0UNBa9T7rZsz80K0jWk6bpWGW55UzbvTHZ+3t'.
+ 'vbAv/IT+K1uCmhIrKJAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bl_green.png
+//==========================================================
+ $this->imgdata_large[8][0]= 1484 ;
+ $this->imgdata_large[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMMFjM4kcoDJQAABVlJREFUeNq9ll2MJFUVx3/11V'.
+ 'Vd/TE9vU0v4zLDwJIF16jBqLAPhsRXEiDqg0QTJiQSjcSNvCzw'.
+ 'sBEDDxizhvAAxBgf1oR9QF9NiE9ESFZkQyZB5WtddmdnZ3qqqr'.
+ 'uqbt367Cofqu3ZZpWVaDzJfbkf53//55z/PVdZXV3l/2H6f7Lp'.
+ '5VdOV/4Nb+GmHpUeA7AdBNxc3kafNb73jRPK9Xwon8ToxVefqU'.
+ 'b91wibH5EkCQBCizFihTSviHUHR0hWws9xe3wvJ7/7nPKpgX5y'.
+ '9oFqt3eOgWniRBoAbUBGGqZUibSYaeoT2B5bnkdaSA6793Cv/S'.
+ 'QPPbihXBfo5VdOV+8dfgnvwAU62YH5fCZ12sDujFkwyegCqTrB'.
+ 'iUOKTOJKj8jr88jS8zy6cXwBTP048nuHX0I0nDlIp7RpTG7kM0'.
+ 'sdyAYsTVukUuWGhlWHMq0ITL92lnUp9R1Obz/GmTNnqn9bDD8/'.
+ '+0D1oX0O0zQZZDYCsK2j3Gl9jQqDfHiei8GfiKVLlsZkJaBAN1'.
+ '0i6PgwUbB0GxG5/PrtE/xLRr959Znqw9452oVNI+jiJhnr1pe4'.
+ 'k29zB1/nFr5Kj7tpt1YYhJ0FJ7nUYbcJQBgahN2MzeCP/OipR6'.
+ 'prgN6Qr6ELFQFUWoRpNVjlKwxZB8DCpE+PtfEKqV1cUzxpVudu'.
+ 'GTBHA5Y1g99e+dUio9O/P1Vpq+/WE5GGjDSMoAtAQjrf3C52IP'.
+ 'QxpY4WK2hpReka9Gfrhqgz0bACRoCWjDh56kQ1z9FeuUUQxVhK'.
+ 'B92sD1VahM+bAJgcoJhGjP/6Ln8rAgDiRCVRKiIzxMkkodBJ85'.
+ 'im1IlEHbE4k1xyNveL4YP8HarmGJIOpqyjeQmfNHmTvnqZTWBt'.
+ 'vIJXpPwlukJSuSTKGK3pEwtJmiX00ZlInTyNscImO6XBITvH1c'.
+ '8vVt2OucdKvIyeKRTNCivsEMgcpg6taYs30nfq0Gqg6hOSSFJ4'.
+ 'BSnJPht0IqEjWmOGocEI6F0J94F0qaL6BntTF0MtUfweKQKAPU'.
+ 'Wwp4OcVnQAmVb0p9DLOzjEhEKnGRmoRc7EzRGlwA6NujAKG4yP'.
+ '6Sjwc4aVznZ7DK0xXdkDoJf0kGmFBniFBOBGcZSCCSKd0IwN0k'.
+ 'IS+QZWCGVZex4BnUxya3+Zt9iugQbcRFpIAtuHvAZulPUdLhUJ'.
+ 'RqegI3WcqaSXddlT3idsWMSRRGkEtNwmyTifAwyBo7LP+11J0e'.
+ '7tM7pZOYblHkBLcqZ5LcYtw6Wbd4CM3SpE9foYZsIHoqDKCrbz'.
+ 'mLSQtPwmuhXgtBLs0GBdbXOhFGB7WBKO2F8GXt9/VO97Ya3atF'.
+ '7nUHnwGjGGQqcPxFEdFqURkEidiZszAERoYIsGju1hq21kWee3'.
+ 'bw15+8WpsvAy3K1+i3JkkhZyPpxxjjPOsfOYiZ+TFhLPzQnHOU'.
+ 'tpzGB2dgA4tscIkKIx19Cxg/fPL7vQJu47eXt1VvsDK8pwPueZ'.
+ 'PuZoQMOqhRoJHSs0kKLBWjvjYinmeQGw1TaX1RFdfZ3LMzYLjA'.
+ 'C++dkn6AaH2Nobk6cxEzdnuG0TdC8zvdJkN0hqkFkO/jwL0fxa'.
+ 'so8sBcuFzQ+/+MRC+BeAHnpwQzn++ee5KT9Eshuy46dcKAXm32'.
+ '0uzPQhS4GttkH2GQID2Wc0Y4LtAbDxhZ/x5A+e/uTG9+jGceXH'.
+ '9/ySnnIXnUzOxXe1038mW3ZynNmam4yYWkO+f9cv+Oljz16/lV'.
+ '9tDz/9nerc1hm8ZEScSRK7VvtYl1i1dklsOKyvc+zg/bzw1O8+'.
+ '/efkajt56kR1ydlEJBc5H46xzbrJ3dY9wrB7hGcff+6/+279L+'.
+ '0fHxyiE8XMLl4AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bl_blue.png
+//==========================================================
+ $this->imgdata_large[9][0]= 1169 ;
+ $this->imgdata_large[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAMAAACelLz8AAACEF'.
+ 'BMVEX/////////7//35//v1v/exv/Wvf/Wrf/Wpf/Orf+/v7+9'.
+ 'tc69jP+9hP+5ucW1tc6tlP+rq7Wlpdalpcalpb2cnM6cnMacc/'.
+ '+cWv+UlLWUjN6UjK2Uc/+Ma/+MUv+EhKWEa/+EQvd7e8Z7e7V7'.
+ 'e6V7c957Wv9za9Zza8ZzSv9ra5xrSv9rOf9rMe9jUudjQv9jOe'.
+ '9aWpRaUt5aUpRaSu9aSudSUoxSSs5SSoxSMf9KQtZKOfdKMedK'.
+ 'Kf9KKe9CKf9CKb1CKa1CIfdCIedCId45MXs5Kfc5If85Iec5Id'.
+ 'Y5GP8xMbUxMXsxKc4xKZQxIf8xGP8xGO8xGN4xGNYxGL0xGK0p'.
+ 'KXMpIYwpGP8pGO8pGOcpGNYpGM4pEP8pEPcpEOcpEN4pENYpEM'.
+ 'YpEL0hGKUhEP8hEPchEO8hEOchEN4hENYhEM4hEMYhELUhCP8h'.
+ 'CO8hCN4YGJwYGGsYEL0YEK0YEHMYCN4YCM4YCMYYCL0YCKUYAP'.
+ '8QEJQQEIwQEHsQEGsQCM4QCLUQCK0QCKUQCJwQCJQQCIwQCHMQ'.
+ 'CGsQAP8QAPcQAO8QAOcQAN4QANYQAM4QAMYQAL0QALUQAKUQAJ'.
+ 'QQAIQICGsICGMIAO8IANYIAL0IALUIAK0IAKUIAJwIAJQIAIwI'.
+ 'AIQIAHsIAHMIAGsIAGMAAN4AAMYAAK0AAJQAAIwAAIQAAHMAAG'.
+ 'sAAGMAAFrR1dDlAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkRFRPMOZ'.
+ '/2AAAB+klEQVR4nGNgIAIIqeqZmBqpi2JISNml5lVXV3d198Yo'.
+ 'oUjwm1SnxsbGRsSm5ZfNXO4tjCTjVh0ABhFx6QV9E1Y0S8JkuN'.
+ '3yAgLc7W3t/QPi4jPKJ8ye1yoIlTKpjvVy15eVUbN0i4zKLJ8w'.
+ 'ae6qcKgLqmMj3PUFWFl5NJ0CExLLJzbNW7BWCyxlXR0ba6/Axs'.
+ 'zELmfnkRBT0QiSKgXJCOflxUbYy3KyMHEoOrtEZ1c2TZ6/cMl6'.
+ 'eaCUamdsbIC7tjgPr4SBS3BMMVDTwkXr1hsDpYy6UmMj/O0tdX'.
+ 'QNbDxjknJLWqYsXLx0vStQynxGflpkZGCgs7Onp29SbtNkoMy6'.
+ 'pevCgFJWy3oyMuKjgoKCPWNCvEuqWhcsWrJ06XqQlPnMvrKyrM'.
+ 'TomJjkZAfHlNa2qdOWrlu63gcopbG8v7+hvLwip7g4JdSxsLZu'.
+ '8dKlS9ettwBKic2eNXHChIkTG5tKqgpr2uo6loLAehWQx0LnzJ'.
+ '49p6mpeXLLlNq6RUvqly6dvnR9Bx9ISnnlvLmT582bMr9t4aL2'.
+ '+vrp60GaDCGB6Ld6wfwFCxYCJZYsXQ+SmL6+FBryInVrFi1atH'.
+ 'jJkqVQsH6pNCzCJNvXrQW6CmQJREYFEc2CYevXrwMLAyXXl0oz'.
+ 'IAOt0vVQUGSIkabkDV3DwlzNVDAksAAAfUbNQRCwr88AAAAASU'.
+ 'VORK5CYII=' ;
+
+//==========================================================
+// File: bs_red.png
+//==========================================================
+ $this->imgdata_small[0][0]= 437 ;
+ $this->imgdata_small[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
+ 'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
+ 'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
+ 'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
+ 'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
+ 'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGDNEMgOYAAAAm0'.
+ 'lEQVR4nI3Q3RKCIBAFYGZMy9RKzX7MVUAUlQTe/+kS0K49d3wD'.
+ '7JlFaG+CvIR3FvzPXgpLatxevVVS+Jzv0BDGk/UJwOkQ1ph2g/'.
+ 'Ct5ACX4wNT1o/zzUoJUFUGBiGfVnDTYGJgmrWy8iKEtp0Bpd2d'.
+ 'jLGu56MB7f4JOOfDJAwoNwslk/jOUi+Jts6RVNrC1hkhPy50Ef'.
+ 'u79/ADQMQSGQ8bBywAAAAASUVORK5CYII=' ;
+
+
+//==========================================================
+// File: bs_lightblue.png
+//==========================================================
+ $this->imgdata_small[1][0]= 657 ;
+ $this->imgdata_small[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABVl'.
+ 'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
+ 'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
+ '+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
+ 'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
+ 'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
+ 'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
+ 'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
+ 'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
+ 'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
+ 'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGTok'.
+ '9Yp9AAAAtElEQVR4nGNgIBaw8wkpKghzwvksPAKiUsraprYiLF'.
+ 'ARXkE2JiZ1PXMHXzGIAIekOFBE08TGLTCOCyzCLyvDxsZqZOnk'.
+ 'E56kAhaRV9NQUjW2tPcMjs9wBYsY6Oobmlk7egRGpxZmgkW0zC'.
+ '2s7Jy9giKT8gohaiQcnVzc/UNjkrMLCyHmcHr7BYREJKTlFxbm'.
+ 'QOxiEIuKTUzJKgQCaZibpdOzQfwCOZibGRi4dcJyw3S4iQ4HAL'.
+ 'qvIlIAMH7YAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_gray.png
+//==========================================================
+ $this->imgdata_small[2][0]= 550 ;
+ $this->imgdata_small[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAMAAADH72RtAAABI1'.
+ 'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
+ 'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
+ 'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
+ 'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
+ 'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
+ 'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
+ '3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
+ 'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
+ 'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIA'.
+ 'AAsSAdLdfvwAAAAHdElNRQfTAwkUGiIctEHoAAAAfElEQVR4nI'.
+ '2N2xKDIAwF+bZ2kAa8cNFosBD//yvKWGh9dN+yk9kjxH28R7ze'.
+ 'wzBOYSX6CaNB927Z9qZ66KTSNmBM7UU9Hx2c5qjmJaWCaV5j4t'.
+ 'o1ANr40sn5a+x4biElrqHgrXMeac/c1nEpFHG0LSFoo/jO/BeF'.
+ 'lJnFbT58ayUf0BpA8wAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bs_greenblue.png
+//==========================================================
+ $this->imgdata_small[3][0]= 503 ;
+ $this->imgdata_small[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAxl'.
+ 'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
+ '9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
+ '17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
+ 'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
+ 'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
+ 'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
+ 'dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfT'.
+ 'AwkUGTIqLgJPAAAAqklEQVR4nI2QVxOCMBCEM6Mi2OiCvSslJB'.
+ 'CUoqjn//9TYgCfubf9Zu9uZxFqO+rscO7b6l/LljMZX29J2pNr'.
+ 'YjmX4ZaIEs2NeiWO19NNacl8rHAyD4LR6jjw6PMRdTjZE0JOiU'.
+ 'dDv2ALTlzRvSdCCfAHGCc7yRPSrAQRQOWxKc3C/IUjBlDdUcM8'.
+ '97vFGwBY9QsZGBc/A4DWZNbeXIPWZEZI0c2lqSute/gCO9MXGY'.
+ '4/IOkAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bs_yellow.png
+//==========================================================
+ $this->imgdata_small[4][0]= 507 ;
+ $this->imgdata_small[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAzF'.
+ 'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
+ 'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
+ 'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
+ 'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
+ 'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
+ '50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
+ 'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAH'.
+ 'dElNRQfTAwkUGSDZl3MHAAAAqElEQVR4nI3QWRNDMBAA4My09E'.
+ 'IF1SME0VT1okXvM/3//6kEfbZv+81eswA0DfHxRpOV+M+zkDGG'.
+ 'rL63zCoJ2ef2RLZDIqNqYexyvFrY9ePkxGWdpvfzC7tEGtIRly'.
+ 'nqzboFKMlizAXbNnZyiFUKAy4bZ+B6W0lRaQDLmg4h/k7eFwDL'.
+ 'OWIky8qhXUBQ7gKGmsxpC+ah1TdriwByqG8GQNDNr6kLjf/wAx'.
+ 'KgEq+FpPbfAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_darkgray.png
+//==========================================================
+ $this->imgdata_small[5][0]= 611 ;
+ $this->imgdata_small[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAMAAAAMCGV4AAABJl'.
+ 'BMVEX////////o8v/f6O7W4OnR3PXL1OTL0evEyLvCzePAwMC/'.
+ 'v7a8wsq7t7C1xum1vtS1q6GzopmyxeKsrsOqvNWoq7anvN+nsb'.
+ 'qhrcGgqbGfpq6cp7+bqMuVmJKRm7yPlKKMnL6FkKWFipOEkLSE'.
+ 'j6qEhoqAiaB+jqd8haF7hZR4iJt4g5l3hZl2gIt2cod1hJVzeY'.
+ 'VzboJvhp9sfJJsb41peY1pd5xpdoVod4xndI5lcHxka4BjcYVg'.
+ 'Z3BfboFbb4lbZnZbYntaZ4laZYVZV3JYYWpXX3JWWm5VX4RVW2'.
+ 'NUYX9SXHxPWn5OVFxNWWtNVXVMVWFKV3xHUGZGU3dGTldFSlxE'.
+ 'Sk9ESXBCRlNBS3k/SGs/RU4+R1k9R2U6RFU2PUg0PEQxNU0ECL'.
+ 'QWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAA'.
+ 'CxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGQmbJetrAAAAtklEQV'.
+ 'R4nGNgwAK4JZTNNOWlYDxhMT4ZDTOzQE1uMF9CiJWVU0LbxDlS'.
+ 'G8QVF+FnZ2KRNHAIiPUHaZGSlmZj5lH19A1KjLUA8lXU5MWllF'.
+ 'yjo30TYr2BfG19G11b37CEeN84H38gX1HbwTUkOjo+zjfG3hLI'.
+ 'l1exCvCNCwnxjfMz0gTyRdXNHXx9fUNCQu2MwU6SN3ZwD42LCH'.
+ 'W30IK4T8vUJSAkNMhDiwPqYiktXWN9JZj7UQAAjWEfhlG+kScA'.
+ 'AAAASUVORK5CYII=' ;
+
+
+//==========================================================
+// File: bs_darkgreen.png
+//==========================================================
+ $this->imgdata_small[6][0]= 666 ;
+ $this->imgdata_small[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABX1'.
+ 'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
+ 'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
+ 'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
+ 'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
+ '6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
+ 'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
+ 'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
+ 'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
+ 'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
+ 'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAwkUGRjxlcuZAAAAtElEQVR4nGNgIBZw8osqqIpzw/msfI'.
+ 'IiUmr6lo6SbFARASEOJiYtQ2uXADmIAJeEGFBE18LBMySBBywi'.
+ 'LC/LwcFiZuvmH5WiAxZR0tRW1DC3dfYJS8zyAouYGBibWtm7+o'.
+ 'TEpZfkgEX0rG3snNx9Q2NSCksgaqRd3Ty8gyLiU/NKSiDmcPsF'.
+ 'BodHJ2UUlZTkQ+xikIlNSE7LLgECZagL2VQyc0H8YnV2uD94jS'.
+ 'ILIo14iQ4HALarJBNwbJVNAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_purple.png
+//==========================================================
+ $this->imgdata_small[7][0]= 447 ;
+ $this->imgdata_small[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAnF'.
+ 'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
+ 'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
+ 'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
+ 'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
+ 'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGS'.
+ 'o5QpoZAAAAnElEQVR4nI3Q2xJDMBAG4MyQokWrZz3oSkJISJH3'.
+ 'f7dK0Gv/Xb7J7vyzCK0NjtPsHuH/2wlhTE7LnTNLCO/TFQjjIp'.
+ 'hHAA6bY06LSqppMAY47x+04HXTba2kAFlmQKr+YuVDCGUG2k6/'.
+ 'rNwYK8rKwKCnPxHnVS0aA3rag4UQslUGhrlk0Kpv1+sx3tLZ6w'.
+ 'dtYemMkOsnz8R3V9/hB87DEu2Wos5+AAAAAElFTkSuQmCC' ;
+
+
+//==========================================================
+// File: bs_brown.png
+//==========================================================
+ $this->imgdata_small[8][0]= 677 ;
+ $this->imgdata_small[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABaF'.
+ 'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
+ 'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
+ 'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
+ 'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
+ '/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
+ 'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
+ 'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
+ 'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
+ 'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
+ 'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
+ 'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLd'.
+ 'fvwAAAAHdElNRQfTAwkUGho0tvl2AAAAtklEQVR4nGNgIBaoSg'.
+ 'mLKGpowfkGMty8AqJKpi4mRlAROR5ONg4JFUv3YHOIgDo/HwsT'.
+ 'q6yps29EsjZYREFIkJ2ZS9/OMzA20wEsIi8uKSZtaOPmH5WSFw'.
+ 'YW0VRW07Vw8vCLSMguLwCL6FlaObp6B0TGZxSXQ9TouHv6+IXG'.
+ 'JGYWlpdDzNEKCgmPjkvLKS0vL4LYxWAen5SelV8OBNZQFxrZ5h'.
+ 'aC+GX2MDczMBh7pZakehkTHQ4AA0Am/jsB5gkAAAAASUVORK5C'.
+ 'YII=' ;
+
+//==========================================================
+// File: bs_blue.png
+//==========================================================
+ $this->imgdata_small[9][0]= 436 ;
+ $this->imgdata_small[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAk1'.
+ 'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
+ 'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
+ 'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
+ 'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
+ 'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGhNNakHSAAAAmk'.
+ 'lEQVR4nI3P2xKCIBAGYGfM6SBWo1nauIqogaDA+z9dK9Lhrv47'.
+ 'vtl/2A2CfxNlJRRp9IETYGraJeEb7ocLNKznia8A7Db7umWDUG'.
+ 'sxAzhurxRHxok4KQGqCuEhlL45oU1D2w5BztY4KRhj/bCAsetM'.
+ '2uObjwvY8/oX50JItYDxSyZSTrO2mNhvGMbaWAevnbFIcpuTr7'.
+ 't+5AkyfBIKSJHdSQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bs_green.png
+//==========================================================
+ $this->imgdata_small[10][0]= 452 ;
+ $this->imgdata_small[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAn1'.
+ 'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
+ '/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
+ 'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
+ '5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
+ 'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
+ 'AIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAw'.
+ 'kUGgW5vvSDAAAAnklEQVR4nI3QSxKCMAwA0M4gqCgoiiJ+kEAL'.
+ 'LQUq0PufzX7ENdnlJZNkgtDS2CYZvK6bf+7EoKLA9cH5SQzv6A'.
+ 'YloTywsAbYr44FrlgrXCMJwHl3xxVtuuFkJAPIcw2tGB9GcFli'.
+ 'oqEf5GTkSUhVMw2TtD0XSlnDOw3SznE5520vNEi7CwW9+Ayjyq'.
+ 'U/3+yPuq5gvhkhL0xlGnqL//AFf14UIh4mkEkAAAAASUVORK5C'.
+ 'YII=' ;
+
+
+//==========================================================
+// File: bs_white.png
+//==========================================================
+ $this->imgdata_small[11][0]= 480 ;
+ $this->imgdata_small[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAAQCAYAAADwMZRfAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFTsY/ewvBQAAAW1JREFUeJytkz2u4jAUhT/jic'.
+ 'gfBUKiZhE0bIKeVbCWrIKenp6eDiGlCEEEBArIxvzGU4xeZjLk'.
+ 'jWb05lRXuvbx+exr4bouX1Xjyw7Atz81F4uFBYjjGIDhcCjq1o'.
+ 'k6nN1uZwFerxfP55Msy1itVmRZBsB4PK6YveHkeW5d18XzPIIg'.
+ 'wPd9Wq0WnU6HMAxJkoQoiuynOIfDwUopkVIihKAoCgAcx6Hdbm'.
+ 'OMIU1T5vN55eBKEikljUYDIX6kFUKU9e8aDAZlmjcca+1b7TgO'.
+ '1+uVy+VS9nzfr8e53++VzdZaiqIgz3OMMWitOZ/PaK0JgqDeRC'.
+ 'mF53lIKYGfr3O73TDGoJQiTVO01nS73XqT4/FIs9kkCAIej0eZ'.
+ 'brPZEMcxSZKgtQZgMpmIWpN+vy+m06n1PK9yTx8Gy+WS/X5Pr9'.
+ 'er9GuHLYoiG4YhSilOpxPr9Zrtdlti/JriU5MPjUYjq7UuEWaz'.
+ '2d+P/b/qv/zi75oetJcv7QQXAAAAAElFTkSuQmCC' ;
+
+
+//==========================================================
+// File: bs_cyan.png
+//==========================================================
+ $this->imgdata_small[12][0]= 633 ;
+ $this->imgdata_small[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAABPl'.
+ 'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
+ '//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
+ 'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
+ '/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
+ 'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
+ '3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
+ '4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
+ 'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
+ 'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwkUGQDi+VPPAAAAtElEQVR4nGNgIBawikipyIiy'.
+ 'wfksfJpGRkamNtr8LFARPiMFHmFDcztXfwGoFi0jLiZuZRtnry'.
+ 'BddrCIiJEGL6eklYO7X3iCOFhE2thESdHawdUnJDZFDiyiamZh'.
+ 'aevk5h0UlZSpBhaRtbN3dPHwDY5MSM+EqBFzc/f0DgiLTkjLzI'.
+ 'SYw6bjHxgaEZeckZmpD7GLQSAqJj4xNRMIBGFuFtRLA/ENhGBu'.
+ 'ZmDgkJBXl5fgIDocAAKcINaFePT4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bs_bluegreen.png
+//==========================================================
+ $this->imgdata_small[13][0]= 493 ;
+ $this->imgdata_small[13][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAvV'.
+ 'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
+ 'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
+ '8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
+ 'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
+ '0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
+ 'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
+ 'AJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGxNUcXCT'.
+ 'AAAAqUlEQVR4nI2Q1xKCMBREM2NHLCCogAGCjd6SqLT8/2cZKT'.
+ '6zb3tm987OBWCsXoejp8rC35fi4+l6gXFZlD0Rz6fZ1tdDmKR9'.
+ 'RdOmkzmP7DDpilfX3SzvRgQ/Vr1uiZplfsCBiVf03RJd140wgj'.
+ 'kmNqMtuYXcxyYmNWJdRoYwzpM9qRvGujuCmSR7q7ARY00/MiWk'.
+ 'sCnjkobNEm1+HknDZgAqR0GKU43+wxdu2hYzbsHU6AAAAABJRU'.
+ '5ErkJggg==' ;
+
+//==========================================================
+// File: bs_lightred.png
+//==========================================================
+ $this->imgdata_small[14][0]= 532 ;
+ $this->imgdata_small[14][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAA3l'.
+ 'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
+ 'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
+ 'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
+ 'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
+ 'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
+ 'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
+ 'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAwkUGjoP2Nm+AAAAr0'.
+ 'lEQVR4nGNgIBaYiOk62imYwPnMkiIyso76yhJSzFARMxkRNk49'.
+ 'a3t5OW6oFk1LVkYOfWUHKxUXiEYzLS12DnN3VXkjIRtFsIiSk5'.
+ '6evqGqhYGKugAfWMRa1FpD2UHeQEXQRlgALCJur+rgbCUNFOAS'.
+ 'hqjRkZe3MpBTcwEKCEPMMTGSs3Xz8OQHCnBBHckt6OJpIyAMBD'.
+ 'wwN/MYc4H4LK4wNzMwmGrzcvFqmxIdDgDiHRT6VVQkrAAAAABJ'.
+ 'RU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_lightred.png
+//==========================================================
+ $this->imgdata_xsmall[0][0]= 432 ;
+ $this->imgdata_xsmall[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAA3l'.
+ 'BMVEX///////+/v7/Gvb0hGBj/5///3v//zu//1u//xucpGCG9'.
+ 'nK21lKVSQkp7Wms5KTExISlaOUpjQlIhEBj/tdbOhKXnrcbGjK'.
+ 'Wla4TetcbGnK2EWmv/rc73pcZ7UmOcY3vOpbW1jJzenLW9e5Rz'.
+ 'Slq1c4xrQlJSOULGhJz/pcb3nL2chIzOnK33rcbelK3WjKWMWm'.
+ 'vGe5SEUmM5ISnOtb3GrbXerb3vpb2ca3v/rcaUY3POhJxCKTF7'.
+ 'SlrWnK21e4ytc4TvnLXnlK2la3taOUK1lJxrSlLGhJRjQkpSMT'.
+ 'lw+q2nAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKBOgGhWjAAAAS0'.
+ 'lEQVR4nGNgQAEmunYmEJaMCKe1vBxYzJKVQ9lKBSSupKdnaKGi'.
+ 'zgdkiqs6WKnYcIGYJnK2HvzCwmCNgi42wsLCECNMeXlNUY0HAL'.
+ 'DaB7Du8MiEAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bxs_bluegreen.png
+//==========================================================
+ $this->imgdata_xsmall[1][0]= 397 ;
+ $this->imgdata_xsmall[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAvV'.
+ 'BMVEX///////+/v79j//855/8x3v851v9Spb1C1v8AOUqEtcZK'.
+ 'lK1StdYxzv8hxv8AY4QASmNSlK1KpcZKtd4YQlIYnM4YrecIvf'.
+ '8AtfcAre8AjL0AhLUAc5wAa5QAWnsAQloAKTkAGCFKhJxKrdYY'.
+ 'jL0Ypd4Atf8ArfcApecAnN4AlM4AjMYAe60Ac6UAY4wAUnNSnL'.
+ '0AlNYAWoQASmsAOVIAITGEtc4YWnsAUnsAMUqtvcaErcYAKUIA'.
+ 'GCkAECHUyVh/AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAA'.
+ 'AJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUKDVyF5Be'.
+ 'AAAASUlEQVR4nGNgQAFmYqJcEJaEOJ+UrD5YTJKFTZrfGCQuaq'.
+ 'glLWvMaQ5kqujo6hnbKIKYXPr68gp2dmCNJiZAlh3ECGsREWtU'.
+ '4wF1kwdpAHfnSwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_navy.png
+//==========================================================
+ $this->imgdata_xsmall[2][0]= 353 ;
+ $this->imgdata_xsmall[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
+ 'BMVEX///////+/v7+trcbGxv+EhM6EhNaEhP97e/9zc/9ra/9S'.
+ 'UsZKSrVSUs5jY/9SUtZKSsZSUudKSt5KSudKSv8YGIQpKf8YGK'.
+ 'UYGN4YGO8YGPcQEP8ICP8AAP8AAPcAAO8AAOcAAN4AANYAAM4A'.
+ 'AMYAAL0AALUAAK0AAKUAAJwAAJQAAIwAAIQAAHsAAHMAAGsAAG'.
+ 'ONFkFbAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJxXO4axZAAAAR0'.
+ 'lEQVR4nGNgQAGskhKsEJaslIi8ijpYTJaDU1FVAyQuKSujoKKh'.
+ 'LQ5kSigpqWro6oOYrOoaWroGBmCNWiCWAdQwUVFWVOMBOp4GCJ'.
+ 's5S60AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_gray.png
+//==========================================================
+ $this->imgdata_xsmall[3][0]= 492 ;
+ $this->imgdata_xsmall[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABI1'.
+ 'BMVEX///8AAAD8EAD8IAD8NAD8RAD8VAAYGBi/v7+goKCCgoJk'.
+ 'ZGRGRkb8yAD83AD87AD8/AD4+ADo+ADY+ADI+AC0+ACk+ACU+A'.
+ 'CE+AB0/ABk/ABU/ABE/AAw/AAg/AAQ/AAA/AAA+AAA6BAA2CAA'.
+ 'yDQAtEQApFQAlGQAhHQAdIgAZJgAVKgARLgAMMgAINwAEOwAAP'.
+ 'wAAPgIAPAQAOgYAOAkANgsANA0AMg8AMBEALhMALBUAKhcAKBo'.
+ 'AJhwAJB4AIiAAID////4+Pjy8vLs7Ozm5ubg4ODa2trT09PNzc'.
+ '3Hx8fBwcG7u7u1tbWurq6oqKiioqKcnJyWlpaQkJCJiYmDg4N9'.
+ 'fX13d3dxcXFra2tkZGReXl5YWFhSUlJMTExGRkZAQEA1BLn4AA'.
+ 'AAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEA'.
+ 'AAsRAX9kX5EAAAAHdElNRQfTAwkUKC74clmyAAAAQklEQVR4nG'.
+ 'NgQAVBYVCGt5dXYEQ0mOnp5h4QFgVmeri6+4dHxYMVeHoFRUTH'.
+ 'gTUFBIZBWAwMkZEx8bFQM2Lj0UwHANc/DV6yq/BiAAAAAElFTk'.
+ 'SuQmCC' ;
+
+//==========================================================
+// File: bxs_graypurple.png
+//==========================================================
+ $this->imgdata_xsmall[4][0]= 542 ;
+ $this->imgdata_xsmall[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABSl'.
+ 'BMVEX////////11P/MqdvKrNfAwMC+u7+9u7+4rr24lsi3rby3'.
+ 'lMe1rLq1o720q7i0oL20ksSzoryyqbaykMGxlb2wkL+vnbiujb'.
+ '2sjLuri7qpl7GoirWoibenmK2mla6mjLKmhrSllauki7CjhrCj'.
+ 'hLGihLChg6+ggq2fkqadkKOcfqqai6Gag6WYe6WXeqSWeaOTd6'.
+ 'CTd5+Rdp6RdZ6RdZ2Qg5eOc5qMcpiLcZeJb5WIbpOHbZKGbJGE'.
+ 'a4+CaY2AZ4t/Z4p/Zop/Zol+Zol7ZIZ6Y4V5YoR1ZH11X391Xn'.
+ '9zXX1yXXtxXHtvWnluWXhsV3VqVnNpVXJoVHFnU3BmUm9jUGth'.
+ 'VGdgTmheTGZcS2RcSmRaSWJYR19XRl5SQllRQlhQQVdPQFZOP1'.
+ 'VLPlFJO09IPE5IOk5FOEtEN0lDOEpDOElDNklCNkc/M0XhbrfD'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'EAAAsRAX9kX5EAAAAHdElNRQfTAwkUKCgREfyHAAAATUlEQVR4'.
+ 'nGNgQAEcIko8EBY3M5Ougy+IxSXMwmTsFsAHZMqrSRvZB0W7A5'.
+ 'k6FlYugXEZICaPr394Um4uSAFDRFRCbm4uxAihsDAhVOMBHT0L'.
+ 'hkeRpo8AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_red.png
+//==========================================================
+ $this->imgdata_xsmall[5][0]= 357 ;
+ $this->imgdata_xsmall[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAk1'.
+ 'BMVEX////////GxsbGra3/xsbOhITWhIT/hIT/e3v/c3P/a2vG'.
+ 'UlK1SkrOUlL/Y2PWUlLGSkrnUlLeSkrnSkr/SkqEGBj/KSmlGB'.
+ 'jeGBjvGBj3GBj/EBD/CAj/AAD3AADvAADnAADeAADWAADOAADG'.
+ 'AAC9AAC1AACtAAClAACcAACUAACMAACEAAB7AABzAABrAABjAA'.
+ 'BuukXBAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZ'.
+ 'cwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIyjy5SVMAAAAS0'.
+ 'lEQVR4nGNgQAFsUpJsEJastIi8ijpYTJaDU0FVgxXIlJKVUVDR'.
+ '0BYHMiUUlVQ1dPVBTDZ1dS1dAwOQAgYtbSDLAGIEq6goK6rxAD'.
+ 'yXBg73lwGUAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: bxs_yellow.png
+//==========================================================
+ $this->imgdata_xsmall[6][0]= 414 ;
+ $this->imgdata_xsmall[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAzF'.
+ 'BMVEX///////+/v79zYwCMewDOxoTWzoTezkr/5wj/5wDnzgDe'.
+ 'xgC1pQCtnACllACcjACUhABjWgDGvVK1rUrOxlLGvUqEexilnB'.
+ 'jv3hj35xj/7wj/7wD35wDv3gDn1gDezgDWxgDOvQDGtQC9rQCE'.
+ 'ewB7cwBzawBrYwDWzlLn3lLe1krn3kre1hi9tQC1rQCtpQClnA'.
+ 'CclACUjACMhAD/9wC/v7///8bOzoT//4T//3v//3P//2v//2Pn'.
+ '50r//0r//yn39xj//xD//wBjYwDO8noaAAAAAXRSTlMAQObYZg'.
+ 'AAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAH'.
+ 'dElNRQfTAwkUIzoBXFQEAAAAS0lEQVR4nGNgQAFsDhJsEJaTo5'.
+ '2skj5YzMnSSk7ZwBzIlOSUklPiMxYHMnW4FXT5VNVBTDZeXiNV'.
+ 'QUGQAgYBYyBLEGIEq5gYK6rxAH4kBmHBaMQQAAAAAElFTkSuQm'.
+ 'CC' ;
+
+//==========================================================
+// File: bxs_greenblue.png
+//==========================================================
+ $this->imgdata_xsmall[7][0]= 410 ;
+ $this->imgdata_xsmall[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAxl'.
+ 'BMVEX///////+/v79znJQhSkJ7raU5hHtjraVKnJRCjIRClIyU'.
+ '9++E595avbVaxr2/v7+ctbWcvb17nJxrjIx7paUxQkK9//+Mvb'.
+ '17ra2Evb17tbVCY2MQGBiU5+ec9/eM5+d71tZanJxjra1rvb1j'.
+ 'tbVSnJxara1rzs5jxsZKlJRChIQpUlIhQkJatbVSpaU5c3MxY2'.
+ 'MYMTEQISFavb1Sra1KnJxCjIw5e3sxa2spWlpClJQhSkoYOTkp'.
+ 'Y2MhUlIQKSkIGBgQMTH+e30mAAAAAXRSTlMAQObYZgAAAAFiS0'.
+ 'dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfT'.
+ 'AwkUJy5/6kV9AAAATUlEQVR4nGNgQAGCyuyCEJaGugKHviVYzF'.
+ 'hO3sxCWwDIVNLTM9PXtpEGMhW12Cy0DR1ATEFLSxZ7BweQAgYd'.
+ 'HUMHBweIEQKiogKoxgMAo/4H5AfSehsAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_purple.png
+//==========================================================
+ $this->imgdata_xsmall[8][0]= 364 ;
+ $this->imgdata_xsmall[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAnF'.
+ 'BMVEX///////+/v7/Gvca9rb3Grcb/xv+1hLWte629hL21e7XG'.
+ 'hMbWhNbOe87We9b/hP//e/97OXv/c///a///Y/+cOZz/Sv/WOd'.
+ 'bnOefvOe//Kf9jCGNrCGv/EP//CP/nCOf/AP/3APfvAO/nAOfe'.
+ 'AN7WANbOAM7GAMa9AL21ALWtAK2lAKWcAJyUAJSMAIyEAIR7AH'.
+ 'tzAHNrAGtjAGPP1sZnAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUIj'.
+ 'mBTjT/AAAASUlEQVR4nGNgQAGskhKsEJaCrJiSuhZYTEFASFlD'.
+ 'GyQuqSCnrK6tJwpkiquoamgbGIGYrFpaugbGxmCNunpAljHECB'.
+ 'ZBQRZU4wFSMAZsXeM71AAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_green.png
+//==========================================================
+ $this->imgdata_xsmall[9][0]= 370 ;
+ $this->imgdata_xsmall[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAAAn1'.
+ 'BMVEX///////+/v7+/v7/G/8aUxpSMvYyUzpSMzoyM1oxarVqE'.
+ '/4R7/3tavVpKnEpaxlpz/3Nr/2tKtUpj/2Na51pKzkpK1kpK50'.
+ 'pK/0oYcxgp/ykYlBgY3hgY7xgY9xgQ/xAI/wgA/wAA9wAA7wAA'.
+ '5wAA3gAA1gAAzgAAxgAAvQAAtQAArQAApQAAnAAAlAAAjAAAhA'.
+ 'AAewAAcwAAawAAYwA0tyxUAAAAAXRSTlMAQObYZgAAAAFiS0dE'.
+ 'AIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAw'.
+ 'kUKBrZxq0HAAAATElEQVR4nGNgQAGccrIcEJaivISyhjaIxa7I'.
+ 'I6CiqcMKZMopKqho6OhLA5kyqmqaOobGICartraeoYkJSAGDnj'.
+ '6QZQIxgk1Skg3VeABlVgbItqEBUwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bxs_darkgreen.png
+//==========================================================
+ $this->imgdata_xsmall[10][0]= 563 ;
+ $this->imgdata_xsmall[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABX1'.
+ 'BMVEX////////l/+nAwMC86r+8wb28wby8wLy78sCzw7SywrSx'.
+ 'wLKwvrGuvK+syK+ryq2rx62n36ym3aumxKmk2qij0Keh16ahva'.
+ 'Og1aSguKKe06KeuaCetZ+d0KGdtZ+bz6Cay56ZyZ2Zwp2Zr5qZ'.
+ 'rpqYwJuXyZuXrJmVw5mUxZiTxJeTw5eTq5WRwJWPtJKOvZKKuI'.
+ '6Kt42Kn4yJt42ItIuGsomFsYmEsIiEr4eDr4eBrIR/qoN+qIJ8'.
+ 'poB7pH56o356on14nnt2nXl0mndzmnZzmXZymHVwlXNvlHJukn'.
+ 'FtiHBqjm1qjW1oi2toiWpniWplh2hlhmdkhWdig2VggGNgf2Je'.
+ 'fmFdfGBde19bbl1aeFxXdFpWclhVclhVcVdUcFZTb1VSbVRQal'.
+ 'JPaVFKY0xKYkxJYUtIYEpHX0lEWkZCWERCV0NCVkM/U0A+U0A+'.
+ 'UUA+UEA9Uj89UT48Tj45TDvewfrHAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
+ 'RQfTAwkUKCFozUQjAAAATUlEQVR4nGNgQAGcoqrcEJYQB5OhSw'.
+ 'CIxSXGwWThGcIDZCppK5o7hyV6AZl6NnbuoSmFICZ3YHB0RkkJ'.
+ 'SAFDbEJaSUkJxAjeyEheVOMBQj4MOEkWew4AAAAASUVORK5CYI'.
+ 'I=' ;
+
+//==========================================================
+// File: bxs_cyan.png
+//==========================================================
+ $this->imgdata_xsmall[11][0]= 530 ;
+ $this->imgdata_xsmall[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABPl'.
+ 'BMVEX////////F///AwMCvxsaC1NSC0dGCz8+CzMyA//94//91'.
+ '//9q//9j//9X4uJX09NXz89Xx8dXxMRL//9L5uZL3d1L2NhLxs'.
+ 'ZLt7cv//8e9fUe8fEe7u4e398epqYehoYX//8L+PgK//8F9fUE'.
+ '/v4E5+cEb28EZ2cC//8C/v4C/f0CzMwCrq4Cjo4CdXUCaWkCZW'.
+ 'UB/PwA//8A/f0A+/sA8/MA7e0A7OwA6+sA5eUA5OQA4uIA4eEA'.
+ '3NwA2toA2NgA1dUA09MA0tIA0NAAysoAxsYAxcUAxMQAv78Avr'.
+ '4AvLwAtrYAtbUAs7MAsLAAra0Aq6sAqKgApaUApKQAoqIAoKAA'.
+ 'n58AmpoAlZUAk5MAkpIAkJAAj48AjIwAiYkAh4cAf38AfX0Ae3'.
+ 'sAenoAcnIAcHAAa2sAaWkAaGgAYmIUPEuTAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAA'.
+ 'AHdElNRQfTAwkUKQFKuFWqAAAATUlEQVR4nGNgQAGsUjJsEJaR'.
+ 'grC5qz9YzIiL28YriB3IlDZRsnYNiZUDMmXtHT2CE9JBTDb/wI'.
+ 'jkzEyQAoaomMTMzEyIERzy8hyoxgMAN2MLVPW0f4gAAAAASUVO'.
+ 'RK5CYII=' ;
+
+//==========================================================
+// File: bxs_orange.png
+//==========================================================
+ $this->imgdata_xsmall[12][0]= 572 ;
+ $this->imgdata_xsmall[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABaF'.
+ 'BMVEX//////////8X/3oD/3nj/1HX/0Gr/xGP/rkv/gBf+iS/2'.
+ 'bAL1agDxaQDuZwDrZwLpZQDmZQLlZADjcx7gZATeYQDdZgraXw'.
+ 'DZXwHYXgDXiEvXZAvUjlfUXwXTjVfTbR7ShUvRbR7RWwDMWQDL'.
+ 'WADKooLKWADJoYLJgkvHWATGoILFn4LFgEvFVgDEZx7EVQDDt6'.
+ '/DVQDCt6/CnoLChlfCVADAwMC+hFe+UgC8UgC6UQC4gVe4UAC3'.
+ 'gVe3UAC1gFe1eUu1TwC1TgCzTgCwTQKuTACrSgCqSgCpSgCpSQ'.
+ 'CodEulSACkRwCiRgCdRACcRACaQwCYQgCWQgKVQQCVQACUQACS'.
+ 'UR6RPwCOPgCNPQCLPACKPACJOwCEOQCBOAB+NwB9NgB8NgB7NQ'.
+ 'B6NwJ4NAB3RR52MwB0MgBuLwBtLwBsLwBqLgBpLQBkLQJiKgBh'.
+ 'KgBgKwRcKABbKQJbJwBaKQRaJwBYKAJVJQDZvdIYAAAAAXRSTl'.
+ 'MAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9k'.
+ 'X5EAAAAHdElNRQfTAwkUJBSSy88MAAAATUlEQVR4nGNgQAGqwo'.
+ 'paEBYPJ4eKezCIpc7HwmrqG6ENZMpLihm6RaWEAZl6Vo7ekRnF'.
+ 'IKZWSHhcTnk5SAFDfFJWeXk5xAjj1FRjVOMBeFwNcWYSLjsAAA'.
+ 'AASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_lightblue.png
+//==========================================================
+ $this->imgdata_xsmall[13][0]= 554 ;
+ $this->imgdata_xsmall[13][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAMAAAC67D+PAAABVl'.
+ 'BMVEX////////d///AwMC7wcS08P+y+P+xxdCwxM+uws2twMur'.
+ 'vsinzNynytylzuKhyN6e5v6d5P+d1fOcwNWcu8ub4f+at8iZ3v'.
+ '+ZvdGY2/yW2f+VscGU1vuT1fqTr72Sx+SSxeKR0fWRz/GPz/OP'.
+ 'rr+OyeqMy+6Myu2LyeyKxueJudSGw+SGorGDvt+Cvd6CvN2Aud'.
+ 'p+uNd+t9Z9tdV8tdR8tNN6sc94r813rct2q8h0qcZ0qMVzp8Rx'.
+ 'o8Bwor5tn7ptnrptnrlsnbhqmbRpmbNpi51ol7Flkqtkkqtkka'.
+ 'pjj6hijaRhjaZgi6NfiqJfiaFdh55bhJtag5pZgphYgJZYf5VX'.
+ 'cn9Ve5FSeI1RdopRdYlQdYlPc4dPcoZPcoVNcINLboBLbH9GZn'.
+ 'hGZXdFZHZEY3RDYnJCXW4/W2s/WWg+Wmo7VmU7VGM7U2E6VGM6'.
+ 'VGI5UV82T1wGxheQAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHU'.
+ 'gAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElNRQfTAwkUJziL'.
+ 'PvAsAAAATUlEQVR4nGNgQAHsQgqcEJYgG5Oegy+IxSHOxmTiFs'.
+ 'gFZMprKBnbB8e7AplaFlbOQUl5ICanX0BEWmEhSAFDVGxKYWEh'.
+ 'xAjusDBuVOMBJO8LrFHRAykAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bxs_darkgray.png
+//==========================================================
+ $this->imgdata_xsmall[14][0]= 574 ;
+ $this->imgdata_xsmall[14][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAABm'.
+ 'JLR0QAAAAAAAD5Q7t/AAAACXBIWXMAAAsRAAALEQF/ZF+RAAAB'.
+ 'iElEQVR42k3QPU8TYRwA8P//ebkXrgdIColXRAOEkJqbaExMut'.
+ 'DBhE1GNjYHPg+DG6ODiU6QOLjVxITBcFKBYCstlAC2Bz17fe76'.
+ 'vLD6+wg/1FpTRFR5lpaub/u1eGBGaAT4HneD4OlXx7avtDYUjT'.
+ 'HQabd2Ti8e3vVSKzxrtHS32wIpFVldno22Nqvvg2Bhl0gp/aNm'.
+ 'vJ3qqXAtLIva+ks1H0wqlSXi4+d6+OFTfRsAfHJx2d1od24rZP'.
+ 'xP2HzopINr1mkesX7ccojqif0v9crxWXODZTno3+dNGA7uWLsd'.
+ 'mUYU4fHJCViMG9umLBmM4L6fagZGg9QKfjZ+Qfy3C3G/B3mugF'.
+ 'IHHNcDf64E3KJALApk2p8CSolUUqLjFkyxOGMsTtFyJ+Wz57NQ'.
+ '8DghS4sLB0svioeZZo7nPhFoUKZDIVFbglkTTnl5/rC8snjAkJ'.
+ 'Bk/XV5LxHC/v7tR8jzTFPbg8LENK9WX0Vv31T2AEmCSmlKCCoh'.
+ 'ROnP1U1tPFYjJBRcbtzSf+GPsFTAQBq1n4AAAABKdEVYdHNpZ2'.
+ '5hdHVyZQBiYzYyMDIyNjgwYThjODMyMmUxNjk0NWUzZjljOGFh'.
+ 'N2VmZWFhMjA4OTE2ZjkwOTdhZWE1MzYyMjk0MWRkM2I5EqaPDA'.
+ 'AAAABJRU5ErkJggg==' ;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/imgdata_bevels.inc b/includes/jpgraph/imgdata_bevels.inc
new file mode 100644
index 0000000..e0db4b0
--- /dev/null
+++ b/includes/jpgraph/imgdata_bevels.inc
@@ -0,0 +1,106 @@
+<?php
+//=======================================================================
+// File: IMGDATA_BEVELS.INC
+// Description: Base64 encoded images for round bevels
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_bevels.inc,v 1.1 2003/03/23 13:37:36 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Bevels extends ImgData {
+ var $name = 'Round Bevels';
+ var $an = array(MARK_IMG_BEVEL => 'imgdata');
+
+ var $colors = array('green','purple','orange','red','yellow');
+ var $index = array('green'=>1,'purple'=>4,'orange'=>2,'red'=>0,'yellow'=>3);
+ var $maxidx = 4 ;
+
+ var $imgdata ;
+
+ function ImgData_Bevels() {
+//==========================================================
+// File: bullets_balls_red_013.png
+//==========================================================
+ $this->imgdata[0][0]= 337 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/f3+LFwcmNxMuxm62DmqKth1VpZmIWg6fv'.
+ 'HCa7K0BwMEytCjFnIyUlEBg9vhQvAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNhk+pYJVAAAAl0lEQVR4nE2Q2xLDIAgFHUWBKJf//9'.
+ 'oekmbafVDZARRbK/pYTKP9WNcNv64zzUdd9BjmrgnsVXRNSzO3'.
+ 'CJ5ahdhy0XKQkxld1kxb45j7dp0x2lBNOyVgQpMaoadX7Hs7zr'.
+ 'P1yKj47DKBnKaBKiSAkNss7O6PkMx6kIgYXISQJpcZCqdY6KR+'.
+ 'J1PkS5Xob/h7MNz8x6D3fz5DKQjpkZOBYAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: bullets_balls_green_013.png
+//==========================================================
+ $this->imgdata[1][0]= 344 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/e3+K3vriUub/Dm18j4xc3ob10k0ItqQlU'.
+ 'e5JBmwpxY1ENaKBgUh0iHgwsSre9AAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNTfJXtxZAAAAnklEQVR4nE2QWY4EMQhDUVhSIRC4/2'.
+ 'kbaqLp9p+f2AxAayAzDfiK9znPORuvH0x8Ss9z6I9sHp6tcxE9'.
+ 'nLmWmebmt5F5p2AR0+C9AWpLBjXRaZsCAT3SqklVp0YkAWaGtd'.
+ 'c5Z41/STYpPzW7BjyiRrwkVmQto/Cw9tNEMvsgcekyCyFPboIu'.
+ 'IsuXiKffYB4NK4r/h6d4g9HPPwCR7i8+GscIiiaonUAAAAAASU'.
+ 'VORK5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_oy_035.png
+//==========================================================
+ $this->imgdata[2][0]= 341 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/f3+K5tbqNwcjnkjXjbxR2i5anfEoNkbis'.
+ 'PBxpU0sZbZejKgdqIRIlERIwYtkYAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNgK0wEu5AAAAm0lEQVR4nE3QVxIEIQgEUErAgTHA/U'.
+ '+7zbipf9RXgoGo0liMmX6RdSPLPtZM9F4LuuSIaZtZWffiU6Iz'.
+ 'Y8SOMF0NogBj30ioGRGLZgiPvce1TbIRz6oBQEbOFGK0rIoxrn'.
+ '5hDomMA1cfGRCaRVhjS3gkzheM+4HtnlkXcvdZhWG4qZawewe6'.
+ '9Jnz/TKLB/ML6HUepn//QczazuwFO/0Ivpolhi4AAAAASUVORK'.
+ '5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_oy_036.png
+//==========================================================
+ $this->imgdata[3][0]= 340 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////////27t/e3+LO3hfYzz65ubiNwci6uQ12ipadgVGa'.
+ 'fwsNkbhnVkcaZ5dwSA8lFg7CEepmAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxEAAAsRAX9kX5EAAAAHdElN'.
+ 'RQfTAxcCBySi1nevAAAAjElEQVR4nFXPWw7EIAgFUNMoCMhj/6'.
+ 'staKczc/2RkwjS2glQ+w3YytgXCXCZpRo8gJdGxZadJws13CUP'.
+ '4SZI4MYiUxypeiGGw1XShVBTNN9kLXP2GRrZPFvKgd7z/sqGGV'.
+ '7C7r7r3l09alYN3iA8Yn+ImdVrNoEeSRqJPAaHfhZzLYwXstdZ'.
+ 'rP3n2bvdAI4INwtihiwAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: bullets_balls_pp_019.png
+//==========================================================
+ $this->imgdata[4][0]= 334 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABEAAAARCAMAAAAMs7fIAAAAM1'.
+ 'BMVEX////+/v7i4eO/w8eHxcvKroNVormtfkjrMN2BeXQrepPc'.
+ 'Esy4IL+OFaR7F25LHF8mFRh5XXtUAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAxcBNgkjEpIxAAAAlElEQVR4nE2QAQ7FIAhDDTAVndL7n3'.
+ 'ZV/7JfEwMvFIWUlkTMVNInbVv5ZeJqG7Smh2QTBwJBpsdizAZP'.
+ '5NyW0awhK8kYodnZxS6ECvPRp2sI+y7PBv1mN02KH7h77QCJ8D'.
+ '4VvY5NUgEmCwj6ZMzHtJRgRSXwC1gfcqJJH0GBnSnK1kUQ72DY'.
+ 'CPBv+MCS/e0jib77eQAJxwiEWm7hFwAAAABJRU5ErkJggg==' ;
+
+ }
+}
+
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/imgdata_diamonds.inc b/includes/jpgraph/imgdata_diamonds.inc
new file mode 100644
index 0000000..8d54ab8
--- /dev/null
+++ b/includes/jpgraph/imgdata_diamonds.inc
@@ -0,0 +1,179 @@
+<?php
+//=======================================================================
+// File: IMGDATA_DIAMONDS.INC
+// Description: Base64 encoded images for diamonds
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_diamonds.inc,v 1.1 2003/03/23 13:37:36 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Diamonds extends ImgData {
+ var $name = 'Diamonds';
+ var $an = array(MARK_IMG_DIAMOND =>'imgdata');
+ var $colors = array('lightblue','darkblue','gray',
+ 'blue','pink','purple','red','yellow');
+ var $index = array('lightblue' =>7,'darkblue'=>2,'gray'=>6,
+ 'blue'=>4,'pink'=>1,'purple'=>5,'red'=>0,'yellow'=>3);
+
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Diamonds() {
+//==========================================================
+// File: diam_red.png
+//==========================================================
+ $this->imgdata[0][0]= 668 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
+ 'BMVEX///////+cAAD/AADOAABjAABrAADWGBjOCAj/CAj/GBj/'.
+ 'EBCcCAiMOTl7KSl7ISFzGBilGBjOEBBrCAjv5+eMQkK1QkKtMT'.
+ 'GtKSnWKSn/KSlzEBCcEBDexsb/tbXOe3ucWlqcUlKUSkr/e3vn'.
+ 'a2u9UlL/a2uEMTHeUlLeSkqtOTn/UlL/SkrWOTn/QkL/OTmlIS'.
+ 'H/MTH/ISH39/f/9/f35+fezs7/5+fvzs7WtbXOra3nvb3/zs7G'.
+ 'nJzvtbXGlJTepaW9jIy1hITWlJS1e3uta2ulY2P/lJTnhITne3'.
+ 'vGY2O9Wlr/c3PeY2O1Skr/Y2P/WlreQkLWISGlEBCglEUaAAAA'.
+ 'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
+ 'sSAdLdfvwAAAAHdElNRQfTAwsWEw5WI4qnAAABGUlEQVR4nHXQ'.
+ '1XLDMBAFUKUCM1NiO8zcpIxpp8z0//9SWY7b2LHv6EU6s1qtAN'.
+ 'iMBAojLPkigpJvogKC4pxDuQipjanlICXof1RQDkYEF21mKIfg'.
+ '/GGKtjAmOKt9oSyuCU7OhyiDCQnjowGfRnooCJIkiWJvv8NxnG'.
+ 'nyNAwFcekvZpPP3mu7Vrp8fOq8DYbTyjdnAvBj7Jbd7nP95urs'.
+ '+MC2D6unF+Cu0VJULQBAlsOQuueN3Hrp2nGUvqppemBZ0aU7Se'.
+ 'SXvYZFMKaLJn7MH3btJmZEMEmGSOreqy0SI/4ffo3uiUOYEACy'.
+ 'OFopmNWlP5uZd9uPWmUoxvK9ilO9NtBo6mS7KkZD0fOJYqgGBU'.
+ 'S/T7OKCAA9tfsFOicXcbxt29cAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_pink.png
+//==========================================================
+ $this->imgdata[1][0]= 262 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgID/M5n/Zpn/zMz/mZn1xELhAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEi3tX8qUAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_blue.png
+//==========================================================
+ $this->imgdata[2][0]= 662 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA+V'.
+ 'BMVEX///+AgIAAAJwAAP8AAM4AAGMAAGsQEP8YGHMQEHMYGP8Q'.
+ 'EKUICJwICM5KSpQxMYQpKXsYGNYQEM4ICGsICP97e85aWpw5OY'.
+ 'xSUv85ObVCQt4xMa0pKa0hIaUpKf+9vd6EhLVra+dzc/9SUr1r'.
+ 'a/9aWt5SUt5CQrVaWv9KSv8hIXs5Of8xMf8pKdYhIdYYGKUhIf'.
+ '/Ozs739//v7/fn5+/v7//n5/fW1ufOzufOzu/W1v+trc69veel'.
+ 'pc6trd6UlMa9vf+MjL21tfe1tf+UlNZzc61ra6Wlpf+EhOeMjP'.
+ '9ra8ZSUpyEhP9CQoxKSrVCQv85Od4xMdYQENZnJhlWAAAAAXRS'.
+ 'TlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAd'.
+ 'LdfvwAAAAHdElNRQfTAwsWEx3Snct5AAABFklEQVR4nHXR5XbD'.
+ 'IBgGYM6AuHsaqbvOfeuknev9X8xISbplSd5/8JyXwwcA/I0AKm'.
+ 'PFchVBdvKNKggKQx2VIoRwMZihMiQE49YUlWBCcPL0hYq4ITh+'.
+ 'qKECUoLDZWqoQNA766F/mJHlHXblPJJNiyURhM5eU9cNw5BlmS'.
+ 'IrLOLxhzfotF7vwO2j3ez2ap/TmW4AIM7DoN9+tu+vLk6Pdg9O'.
+ '6ufXjfXLm6pxPACSJIpRFAa+/26DhuK6qjbiON40k0N3skjOvm'.
+ 'NijBmchF5mi+1jhQqDmWyIzPp1hUlrv8On5l+6mMm1tigFNyrt'.
+ '5R97g+FKKyGKkTNKesXPJTZXOFIrUoKiypcTQVHjK4g8H2dWEQ'.
+ 'B8bvUDLSQXSr41rmEAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_yellow.png
+//==========================================================
+ $this->imgdata[3][0]= 262 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgIBmMwCZZgD/zADMmQD/QLMZAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEwcv/zIDAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_lightblue.png
+//==========================================================
+ $this->imgdata[4][0]= 671 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/1'.
+ 'BMVEX///+AgIAAnP8A//8Azv8AY/8Aa/8I//8Y1v8Izv8Y//8Q'.
+ '//8InP8Qzv8Ypf85jP8he/8Yc/8Ia/8pe/8p//8p1v9Ctf8xrf'.
+ '8prf8QnP8Qc/9CjP+1//97//9r//9S//9K//9C//85//8x//8h'.
+ '//9r5/9K3v9S3v851v97zv9Svf85rf8hpf/G3v9SnP9anP9KlP'.
+ '8xhP/n7//v7+f3///n///O//+U//9z//9j//9a//975/9C3v8h'.
+ '1v+E5/+17/9j3v/O7//n9/+95/+l3v9jxv+U1v8Qpf9avf9Ktf'.
+ '+Uxv+11v97tf9rrf+cxv+Mvf9jpf+tzv+Etf/O3v/39/8Akkxr'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'IAAAsSAdLdfvwAAAAHdElNRQfTAwsWEiHk6Ya/AAABGUlEQVR4'.
+ 'nHXQ13KDMBAF0J2o0E01GHDvJa7p3em95/+/JQJMYjDc0Yt0Zr'.
+ 'VaAaxHgtxwbSGPkGQpOIeQ2ORxJiJmNWYZyAhZR0WcgQGhViU0'.
+ 'nEGoedDHGxgRapRPcRpXhOr7XZzCmLjaXk9IIjvkOEmSRLG62+'.
+ 'F5XlEElhA5sW21GvXj6mGlDBfnJ51lr9svnvEKwH1hu2QPbwd3'.
+ 'N9eXVzuL7/Hn29frdKaamgcgy67L3HFG9gDefV+dm5qme4YRXL'.
+ 'oVR374mRqUELZYosf84XAxISFRQuMh4rrH8YxGSP6HX6H97NNQ'.
+ 'KEAaR08qCeuSnx2a8zIPWqUowtKHSRK91rAw0elmVYQFVc8mhq'.
+ '7p5RD7Ps3IIwA9sfsFxFUX6eZ4Zh4AAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: diam_purple.png
+//==========================================================
+ $this->imgdata[5][0]= 657 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAMAAAC6CgRnAAAA/F'.
+ 'BMVEX///////8xAP/OAP+cAP9jAP9rAP+cCP85CP/OEP9SKf/O'.
+ 'CP9CEP9zGP9rCP+lGP/WOf/WIf9KIf9jOf+MQv+EMf97If9zEP'.
+ '+1Sv+lIf/ne//eUv/na//n5//Oxv/Wzv+chP9zUv97Wv9rQv9a'.
+ 'Mf9KGP/v5/+te/97Kf+9Y/+tOf+tKf+lEP/vtf/WMf/WKf/v7+'.
+ 'f39/+tnP+9rf9rSv9jQv9CGP+ljP+EY//Gtf+tlP+Ma/9zSv/e'.
+ 'zv+UUv+9lP+cWv+lY/+cUv+MOf+EKf+UQv/Opf/OhP/Ga/+1Qv'.
+ '/Oe/+9Uv/ntf/eWv/eSv/WGP/3zv/vlP/WEP//9/+pL4oHAAAA'.
+ 'AXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAA'.
+ 'sSAdLdfvwAAAAHdElNRQfTAwsWEjX+M1LCAAABDklEQVR4nHXQ'.
+ '1bLDIBAGYFqIEW+ksbr7cXd3ff93OUCamdOE/Mxw882yywLwPz'.
+ '+gNKotlRFUVnNUQlCxTMRFCKEdE+MgpJaEiIOU4DKaoSIygtb3'.
+ 'FBUQrm3xjPK4JvXjK0A5hFniYSBtIilQVYUm+X0KTVNiYah+2q'.
+ 'ulFb8nUbSovD2+TCavwXQWmnMA6ro+di+uR5cPzfPhVqPV3N1p'.
+ 'n3b3+rimAWAYhP3xnXd7P6oc9vadPsa1wYEs00dFQRAFehlX21'.
+ '25Sg9NOgwF5jeNTjVL9om0TjDc1lmeCKZ17nFPzhPtSRt6J06R'.
+ 'WKUoeG3MoXRa/wjLHGLodwZcotPqjsYngnWslRBZH91hWTbpD2'.
+ 'EdF1ECWW1SAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: diam_gray.png
+//==========================================================
+ $this->imgdata[6][0]= 262 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX//////wAzMzNmZmbMzMyZmZlq4Qo5AAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWExZFTxLxAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+
+//==========================================================
+// File: diam_blgr.png
+//==========================================================
+ $this->imgdata[7][0]= 262 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABsAAAAbBAMAAAB/+ulmAAAAEl'.
+ 'BMVEX///+AgIBmzP9m///M//+Z//8hMmBVAAAAAXRSTlMAQObY'.
+ 'ZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAA'.
+ 'AHdElNRQfTAwsWEwCxm6egAAAAbUlEQVR4nFXJwQ3AMAhDUdRm'.
+ 'kKojuCswABf2X6UEEiC+WF+PyDfoGEuvwXogq3Rk1Y6W0tBSG8'.
+ '6Uwpla6CmJnpoYKRsjjb/Y63vo9kIkLcZCCsbGYGwMRqIzEp1R'.
+ 'OBmFk9HQGA2N0ZEIz5HX+h/jailYpfz4dAAAAABJRU5ErkJggg'.
+ '==' ;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/imgdata_pushpins.inc b/includes/jpgraph/imgdata_pushpins.inc
new file mode 100644
index 0000000..a127b4f
--- /dev/null
+++ b/includes/jpgraph/imgdata_pushpins.inc
@@ -0,0 +1,519 @@
+<?php
+//=======================================================================
+// File: IMGDATA_PUSHPINS.INC
+// Description: Base64 encoded images for pushpins
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_pushpins.inc,v 1.2 2003/03/23 16:23:32 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_PushPins extends ImgData {
+ var $name = 'Push pins';
+ var $an = array(MARK_IMG_PUSHPIN => 'imgdata_small',
+ MARK_IMG_SPUSHPIN => 'imgdata_small',
+ MARK_IMG_LPUSHPIN => 'imgdata_large');
+
+ var $colors = array('blue','green','orange','pink','red');
+ var $index = array('red' => 0, 'orange' => 1, 'pink' => 2, 'blue' => 3, 'green' => 4 ) ;
+ var $maxidx = 4 ;
+ var $imgdata_large, $imgdata_small ;
+
+ function ImgData_PushPins() {
+
+ // The anchor should be where the needle "hits" the paper
+ // (bottom left corner)
+ $this->anchor_x = 0;
+ $this->anchor_y = 1;
+
+//==========================================================
+// File: ppl_red.png
+//==========================================================
+ $this->imgdata_large[0][0]= 2490 ;
+ $this->imgdata_large[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMKBh4Ryh89CgAACUdJREFUeJy9mNtTFFcexz+/7p'.
+ '4Lw1wZJKDGCAwmDAqUySamcCq1ed6k9mn3UfMP7F+1T3nYqn2J'.
+ 'lZdoDEjpbq0KG8EBFBFBEJye6Zmenkv32Ydu5GYiUMmeqq6uqT'.
+ '6Xz3zP73aOcIKmAQkIFyD3N/jrBPwlKjLQEglVlJKyUjR3u7cc'.
+ 'WLoP3/4dvv03LNrQ8I6x1rFbDML9kOmHvh7IRHU9JKmUSG8vpF'.
+ 'IoXX/TV0AiEM5A5jT0noFMFMJHXUt/d5f9TUAbhtQ3cPFruDog'.
+ '8klHMnmO0dGYe/myOJGINEwTz3F2higFXgy8PpAkOC+h8hoaCt'.
+ '4ppHFcQAWSgOQlyI/p+lUjmRxWAwNJd3xca/f34yoFi4tgmjtD'.
+ 'NIFkJ4xcgBCgVqEBFJ9DqcZea/gNAAVEg7AOGYnHe9XoaJd3+X'.
+ 'LISSSwnz6lsbKCZ9sHh4UVdBkwdA6cPwNnIfJPmC3Ctgft3wwQ'.
+ 'QPkvTZJJnbExzfvsM2nMzVG7e5fG48d4lnXwTwEYCjJxuHQBog'.
+ 'BHUfKkgAIIhiGk06hTp/Dm5qS1uYlXLvtWd4gPgIiCrAEcVckT'.
+ 'Ab5p7TaYJrK1hQaEenrwSiVfQdc91P0kSp7Ii89D5ksY/kAkLy'.
+ 'IZXFdXkQjS1YUSEbdcRu168V6+HTUNIKJDRwdE+sBIQmP9Ld59'.
+ 'bEBA3of4F/D+uXb7rGaaCSmXI3pPj64PDaHCYfEqFVSjgWo2D2'.
+ '73XlJNQTgCyQykIuBWoNKEeh1aLXBPBCggGdBOgxZVSjoajVhH'.
+ 'o5HWlIpq4bCQSgm9vXhK4ZZKh5SUYygp4J1EQVUD9xlU18BJQD'.
+ 'bUbJ5T5XJStyxN9fSI099P3baxV1dRloW2h2ivx/yakg2ot6F1'.
+ 'EkCa4G1D+zVEq5ArKTWM42Q6HUczQV7U66w9e0ZpdRXlOIQ5vF'.
+ 'VHUXILKify4jiEzkOqC3peQMoBQymFlMt4Dx6wUSxSsm2UZXEK'.
+ 'P30QvOUt8/2Sd78CdWwFDTA+gsw3cOlPcPUD+CQB52oQ21RKXM'.
+ 'eRhGXhOg7VoKrx8KuS4ygZhVg3ZI8FGIfwR9BVgAtfwxdXdP3L'.
+ '86nUR91dXelNXTeWWy10paQHX602YAP1ADASAL7LJvFtMpOCc0'.
+ 'cG3FHuGlz6Gr4YEpnoTCbzsdHRbOzy5RCRiLRMk5rjyOtAimwA'.
+ 'U4U3SurBN/0wnAASBCVDIKpB4kiAB5Ub0/UvO9LpPAMDGfn005'.
+ 'AxPCzxep3Q6iqPLUseBoufCZRsAE6g5g5kKIDfKUj3wnpAG8QB'.
+ '/Z1OIqANQuI65AtwNScyYXR2XlAXL2YZHzcklRKWl5GVFXFtGx'.
+ 'MoAiV/EQaAGH6BUQNWgQpwFngv+Ca8KUAQEBcwgTJHyMV7679R'.
+ 'XS8YqdSI6u/PMD5ukMtJY3GR2uQkr5aXeWVZOEALmA8WsIAxfL'.
+ 'd0goVLAdCOd+/YpgqeVtBv4yiA++q/RKKXixe7GB8PSyoljcVF'.
+ 'yg8fyubyMpulEk2lyAIfAAvAC+B+oOQFoAt/+0rAejB/EzjNri'.
+ 'vvqNnCd64jxcE39V8spnP+vMbAgDSePKE2NcXm06dslMuUlcID'.
+ 'TuFvqwXMBU8N39bGgRR+ki0Dz4L5DSAe9NGD7zq+6kcN1L6H2b'.
+ 'ao5WWaQHllRTafPmWrVMJUimoAQrBYJFjQwre7B6A8YAi8LCgD'.
+ '5DVo6/hbb/iHK1KggvFeD3hHziQKEMuiNTNDbXGRTdtmw7Iwla'.
+ 'KGH0oqwbscLOoG46rAY6AOzRhY74PT6QuUKEN4PegXxd/yEDTT'.
+ 'YMWOk+oEaLkuFdNk0zTZwjfkavDUArXWgGXgFb4dEShXhfYqlI'.
+ 'ow3w9rg3B6ED60IOOA5oEYQBrcpG+mj9bg0VG8GMJhVDZLyzAo'.
+ 'VSq8rFYxXXefcjVgG9+uisDrXUCApoKSBcUHMBmHhfcgNwhtD3'.
+ 'q9IG6Lr15b4OUTmPwBJt8JqGuapp05o0mhoHnptLQfPsR+8IBK'.
+ 'uYyNH3yr+B77LHheA3tK1Ta+IrMeTL2C6Xl48TOsNWDDgAz7s5'.
+ '/r+krP/eddCsbj8fDQ4GBm9MqVvvRXX2VULBayRGRzaYn1SoWa'.
+ 'UjgB4PIB5QK4ZgBXBKaAHxQsrED1H7CRgCUPwgHZDqACmhWwXv'.
+ '2aDRqGYeRyufS169cvThQKV88PDuYbW1vJ5VRK+5euqxWlPMdX'.
+ 'SRqgreHbZGN3ijfKBXBTAeh2Fdwi2MofshP/dvKwCmKhp4m83Y'.
+ 'vj8Xg4l8tlCoXC0MTExMTFkZE/1m37wvLGRvKRacoD1209E7Fc'.
+ 'pZwYREOQqEJ4z3HskHLsz4AoXykPIBSN0t3dTTQafROoHdumXC'.
+ '4fjoMiog0ODiauX7+eLxQKV3O53ETdti88nJnJ3rl505ifmWm3'.
+ 'arWSodR8GNbycDoNHy5C5jFold1k8d+DyvELNwg93d18/vnn9P'.
+ 'X1oes6nufx/Plz7t+/fxhQKSWJRCI5NjaWHxkZKdj1+sjSwkJm'.
+ '+uZN/dZ337VqCwullGUVdZjsgIUC5LqhrUPvCugWuApeApPAzY'.
+ 'PKHWyaphGNRunt7WVwcBARwfM8Ojo6sCzrMKBhGLphGFEF2Wq1'.
+ '2jc7M5OZ/vHH0MPbt93awkJJmeZsC6ZaMK3DCwvWdNioQUb5B6'.
+ 'AdBR+9SzkAz/NwHIeXL18iIui6TjgcJplMMjY2th8wHo+Hh4aG'.
+ 'MsPDw6fddru7+Phxx51bt/RbN260qwsLpZhlFZsw9QJ+2Pbrga'.
+ 'oJG2FY2oKwuTtVEz9uV34NbqdtbW0xPT1NNBoF4MyZM1y5coWu'.
+ 'rq5dQBHRcrlc4tq1a/l8Pj9RMs38ndu3Ez//9JNXLRZNyuXZJk'.
+ 'xVYKoExQpsK/+IaAuYb7no8zjC/R+A4zisrq7u+53NZjl16tQ+'.
+ 'QIlEIslsNpuPRCJXZ2dnh2/duNFRW1oy07a96MKd575yxRqU1B'.
+ '5vPMpF5HHa1tYW9+7do7Ozc/eQpZTSQ6FQt1Lq8pMnT/5w7969'.
+ 'nuLcXE1rNufO9fRMhlKpOyvt9qPtVmvb25fFfvvWbrepVCqHwo'.
+ 'xaX19vff/996ZhGC8qlkW9Wt1Onz073fXxxz+6MB+9e9dUjuO+'.
+ '7ebq9wLdB9hoNCrr6+s/4wf3FCJW3fPmTZhXsNWCprjuW66Dfr'.
+ '928KAfBhJAEgiJSLuzs7OSTqctoFkqlZRt26j/I+L/AGjPTN4d'.
+ 'Nqn4AAAAAElFTkSuQmCC' ;
+
+//==========================================================
+// File: ppl_orange.png
+//==========================================================
+ $this->imgdata_large[1][0]= 2753 ;
+ $this->imgdata_large[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQ0VCkHCzQAACk5JREFUeJytmGtzG0d2hp8zNw'.
+ 'AEcRdJ6EJK9FL0CqZUm9jWbkwq3vhDstl8dmLvz8rP2H8Q75ZT'.
+ 'pkRfpLgqsS6WIFEKGYkiSBCDO+banQ8DUpRWEkklXQUUqlCDfv'.
+ 'rp857pgfAOQ4AMOJdg4R/hX96Hf06bvDc5iT07i8yeg8ksiIAI'.
+ '4TBi/ds9/vivD/njapNHvRBfHXMu410AM+BUoVSF05NQsi1sO4'.
+ '8402AXwLQTuP31OAZO2aG0MEn14iSlnI1z3LnMk8IZYJyBwjIs'.
+ '/TWsVIWPJkvMFS4zMfMhUp5BsoCpAAEBLYKaMFGn00jBxnvu02'.
+ '35+JHmSJEnBpQEcPo38MmCxd/nS9Ry71Ga/g1W9a8gn0GsHkgA'.
+ '6DGjxkqb5CoO+YxF3A3p+jGjQUzoK+L/V0ADzFMwtSR8eLbAr8'.
+ 'uXOTf9NzhTc0geSLUQcYHgYEH786RMg0zWJHV2Aitv4x/HpHVS'.
+ 'QA2YBqTTGIUq5qkPMWaWkVwPnPtAA/BevmZcjxaaUtHh8pJJGu'.
+ 'DpCB9FvT7A7YT7S3p5vFMNzmWo/O0MSx/Ms3TqI8r59zFTfUQe'.
+ 'I7SBODE3tnfoIxYnNHligwik0zAzDdVpyKbA8sff5YAeMEwgkV'.
+ 'cufQeTJzZoCsaFLKXPTnNpoUTNsSgJmNoGsuNQjIDwYD2HlnZy'.
+ 'k++yxTKXZfKTU8zOpjhneeQYkorSmGERtIlICBKRbLX+y98YN3'.
+ 'ADcNIm+bJD4U3pPnmbEaRgYVRTGBkDSSsmxKfY7ZLuDJA4hdjl'.
+ 'JEgyBB2SJOvQ9RzTpNKoEwNq0CNFvOXR3/HxMgYVPObaz8kPmh'.
+ 'hkEWMatAfRONGGvLizyOE9P8KkpwhPDAgQKJQbELUD0oOIhbbH'.
+ 'JeVTmowxjAgZutB5AoOngA+2DdYrcTyOyYZP9+QpBvI29vwEhb'.
+ 'It042BVQgDy9KTMfkwQG1A9ACCLlgBBGUwxxoc52WDh2ATyEPp'.
+ '1hoaPvrEBh0Dq5an9OUsl/9hylk5b5c+mowLc4E2Jtw4Eoljyf'.
+ 'ogA/AGEAagNRjGyUxOmEycyVA5EWDBxrmUp3ytLIv/NJP69Goh'.
+ '+9mFydIvS5PZYkvH1oY/RFtKymlwBFQAgQd+kAA6qSQ8pvn2mp'.
+ 'SkJkuVFHPHBnQMrEt5Sl+e4/Lvp51PF1PF5Xy6WMvOWZXMom8z'.
+ 'OZTQ8+j5sbQiMEwopsCIwRtBGIJSCdzbTGo9NimkDcgdC7Bg49'.
+ 'TG5n4/nfr0Si77WdYp1YzyZEkWPdteaEnB7pPqBTxuIf/VgciE'.
+ 'SgasCPwh+GNIkaNNag1RiPge5pEhMQVjfoLcF+eoXSvbKxedwn'.
+ 'LKzC3KWbOi5/sW5a44/SHFUSgVA7SCzRG0AvA9mPOgFIETgu4n'.
+ 'Ww0wNQWFAqRSL6D2ZQYBdDrQ7R7jXiwgRcvIL02makuTmWtpM/'.
+ '+BlLMl5vuWzLVEuwH6oYnR1KS8kJINGXMM2YdfRlALoQoQQKeb'.
+ 'bDVwoMdxQMaLCwLo96HZTF5HbrEhmOftianfZisfzueKv7ZmrX'.
+ 'MsjhxKXZGBjzyeEHmSE3oWiggtyVGmE8DTIXTC5NxgAxOAGUM8'.
+ 'fun9mnSSLQ/CxNzOTgJ3LIMgoGwkKBiiMyaVviHVkdCO4FEKNv'.
+ 'LQzWBYHfITPa4UBVM0LR/WB7ARJsdDDTjA6deYFIFUOimJ3d0E'.
+ 'sNdLavYYgBpthqKcjiiJRO8K6CK0CsJTjfQAGaJtD9vQFAxNNQ'.
+ '1FB0yBAfA8gdMAIagLoCVAen0M00zMOTYShNDtoHs9CAIUoI4E'.
+ '1IBihCdNhsMhsj6NuV7BCC2IBpBqQaaFOENCCeiEsO1BO4RQgy'.
+ 'I5Hm4k4oIU9MrgZSAdBeTabZz+ODxKQRRBFBJo6IUc51anYRQo'.
+ 'dto+24FNxYCiaWKkQsj00KkO4gxRRkAngJ868M0u3OkkM+hxQA'.
+ 'cQ7YD7GO5XYSsPZybh/TCkFIYY+kWniTW4Q7jXgHvHMhiRpmuW'.
+ 'ca08GZkkZ/nY6TZMNhCnf2CuPoDVJvxpB+q9BHA8Ag1uH+oP4c'.
+ 'YEPCzDwmzSLquShHW/E0YRbG/BjZtw40hAy7aNzJlzRn75E6N0'.
+ 'qiwTzafI7kOU3gWrhzZC2iHcbsPqLlxvJnCt4KC1RYAL3I5hzY'.
+ 'Xv/huePYCtITQMKEnyB4KQvMURuJvw889HGSwUCs7CwkLpo6tX'.
+ 'Ty/+7nel6VLGDn/8N9m+eZuo1UP8iNhLau6b3RfmOsHBGTUYw9'.
+ 'WBNeDrGB4+h/4qNLKwTnLbHj9CJw/6GoIh9Jpvq0HHcayFhYXi'.
+ 'l3/4w9LK8vLKexfma3G/mb/3n1njTivS7tNQaaU1grQDjJ868D'.
+ 'Axx6vmxnBrY9C9IcSbSXbavNjb/S3eN6/0m1JcKBScixcvllZW'.
+ 'Vi6uLC8v12q1v/M8b/HxVjP//YYr32yE4dYWvShO0ogi14xwxq'.
+ 'F4rbnxZ3cMjtpvEEeMvwA0TdOYn5/PffHFF7Vr166tvPeLXyx7'.
+ 'nrd4+/btyg/frFo//Xgncnd67qCn78earQqcmYD3fSi1wPCTSV'.
+ '3gzqvm9uFOMl5nUAqFQn5paal26dKla57vf7D+6FHph9VV88af'.
+ 'vgq79bo70e3VT2l9A3hYg4UiRALVHTCHSZvYBm4A//6quf8zoG'.
+ '3bpuM4acMwKr1+//SDe/dK31+/bv90/Xrcq9fduNW6rbVeC+E7'.
+ 'gWdD2DKg4UEpBmPcm10RuScida31ntb62HAigoigDw6Gh0axWH'.
+ 'QWFhZKi4uLZ+I4PrVer2e+u37dXPvqq6hbr7tOp1NXWq89h6/b'.
+ '8FBB34WGBesdcPrj38lkMkGlUuml0+mu53nR3t4eo9HoSLhMJk'.
+ 'OlUiGdTuN5Hq7rvgA0TdO4cOFC7vPPP6/VarXldqdTu7m2lrv7'.
+ '7beq++BBO263b/tKrfWSXlbvwJ6CuAtDgTYiaBFMw6BSqfDxxx'.
+ '+rarWqGo0GN2/eZGtrC6XenAkRoVKpcPXqVWZmZmg0Gty6desF'.
+ 'oIhIOp3Ol8vlmmVZK3fv3Lm09uc/Zwbr653ccPgoNIzvnmn99Z'.
+ '7W9QG46lAaM5mM2l95GIYUi0VOnz7N7OwsWmsymQzyuse5Q8Mw'.
+ 'DNLpNDMzM5w/f/7A6AGgUkoajYa9urpayOXzUz/fvZutr68Pim'.
+ 'F4/2y1+n2o9Q/ru7uPesPhXnyo4A+vfHp6mmazybNnz9jZ2UFr'.
+ 'TbPZJAhe+8/aS0Mphed5NBoNABqNBqPR6MWBVWstvu/nnj9/Pv'.
+ 'vo0aPq5uZmPBgM/qcwPf39xV/9ajU1M3Nvq9PZaw8GoT50PjdN'.
+ 'k6mpKa5cucL58+eJ45j19XWePHnCzs4OnudhmiaWZRGGIVH05r'.
+ 'yEYYjrumxubrKxsfFyDQJ6NBp1Pc+7C4jWumBaVm+kVL2l1H2l'.
+ '1G6otS+H6V6z8u3tbVzXpdFooJRicXGRqakptre3uXXr1ltrcT'.
+ 'Qa8ezZszemWAE9rfUdYBOwtVLRbrPZ+48ff+wDvuu6Sr3MB4Dr'.
+ 'uty6desgfa1WC3iRyrNnz4pSSmezWUzTfGtYtNYcdvC/9sMlgP'.
+ 'n5N4cAAAAASUVORK5CYII=' ;
+
+//==========================================================
+// File: ppl_pink.png
+//==========================================================
+ $this->imgdata_large[2][0]= 2779 ;
+ $this->imgdata_large[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQolY9lkpgAACmhJREFUeJy9mOtzFNl5h5+3b9'.
+ 'Mz0kzPBWmEVtIiWYhIiC0HCDhB8lb8ISk7nzdZ5+/zJ/8BTmpT'.
+ '660CZLwG1pVFgBkgGIHECEaa+/T9nHzQCCQuRpCNz6mp6g893U'.
+ '8/c37ve3qEjxiC4OA4n/Lp/EUu/tsMM/+aEWduVBx7WhdkShcY'.
+ 'xUH2zo0Dwod/5N6vf8V//PoGdx8M8EOFPtK9jI8BdHCcMuVSmf'.
+ 'LxHLmSZdm2U8xIbmKETDGDZZnIy4dBbCynyGhphurEDBOlHFnn'.
+ 'qPcyPxTOwDCOccw7w5nlBRZWylI+ny/mZ6rL1dzUZ5/IWGZU3D'.
+ 'ZIOMQDDaJcHDVGWUbJBi9odVr0QoVSPzigIEaZ8vgSS/8wZU3/'.
+ 'k1fylipz5dLM2WlrZqHKaGCKbEbontq3KAKWQyZfZKTgYqc9Bp'.
+ '2I2PcJ4ogk/UEBQcwipbFZmT13vDBx8fhnE1Ofnp9yJopFyT3X'.
+ 'yANfks0QHSQMDaL37pOxMLIu2UyVkjVKLjyKSeuD8dAYCFkso1'.
+ 'gYMaeWJ40T56cl8yAi/O4FSa2P6kYczIDsgVpAqcDImZPMuAB1'.
+ 'dkLQtcc8a/bwox8IUHAxZVxGZMouSLVYwKuMkD5IxN+JSdsRJB'.
+ 'pexuTVgYYM6EoGmxkmg3/hEhNUMr/hd7dqbOzExMn/GRDAxWZc'.
+ 'j3I8HiXfMjF2FQowKw7pjoN6E/Llw/GBJj8qxVOMlX4ipxc/lY'.
+ 'kl2zBLkmrTcEzMkoNoRLVidLi/9g+Z3I+1xRHX5EcAihxnbPRv'.
+ 'OTU9kZSmpKPy9FTGrLimPZ1H+UiyGaF67w6n7E1DwMngFDxGvc'.
+ 'w70v0xZUby5IxjlIyMssUJrJwVWkXBdbXvSvwEibcSdKCAFI16'.
+ '4/sc0SRo9cGAGq1DwvQFzV6DVuBiV4zYnlEts6A2TSPcSiXoxo'.
+ 'QqJCEEFMbQ2b69o5qMiOOPqIMQkagu/aSL7waE8101WFShLjk9'.
+ 'yxgEvjRUiyYd+gwAjY2J9VpXfZ/JEXLhDp3OR6U4T97+hEnPwx'.
+ 'tv4HsRjy2tTQSFzQgDUnwSLBQRI+x1ZgcH87Vcv4SF19Kt0ezS'.
+ '1h9s0Ma25pgr/YJfnLnEysok0+ezjM6EBLldGqKIJYuDRhOQEJ'.
+ 'Oih8X9Q0xmcXNjlCofBJgn78wxVz7L2YWf8tPPz1hnfjbjzfxN'.
+ 'qVwutq2etZXUQSXikcXGIgUiUkJSDIQMJgYGJsaB3c7b1qQ4GZ'.
+ 'xSkdGZIwMeNLfK6uezMnvJK3pLxeVixfvMsyVjSNSO6IV9adPG'.
+ 'AArkEEz8oUkFmBjYGO80qfd6pCWIayD59wIKcsjcKqufn7JO/S'.
+ 'xfyi+5c24pey5rZ09mJRNkiDdT/tzbkBr3SYkpMYpgEaIJSYhI'.
+ 'kSOY1GhilAQk5ntDIojxCZ/kf87Pl85xbuWEnLiUy+cW3NNuJX'.
+ 'MmY5meKf6mT7wZS+THdOjxlG06tIlIOMZxchSxcFFEGAwAGGME'.
+ 'jwyZYSnWL3cXWiIUbUI6hO/vxXuFOV84ycmlBWthNeflTjuzTi'.
+ 'lzJmM5s46Ej0J63/ZoPmoy6PYxtYVNhmfs0mbAND1mmKVMBY1L'.
+ 'mxA1LN7WgXQbCApNhKJHRIM+DQbv7yQGhjnJ5NgFuXBuxpu5mD'.
+ 'udm3LPuY7pmZLUE6L1SIJaIPFuDAqyw9lnwDYv6NFHkWJh4ZDB'.
+ 'wCBFD3uMxsTAwcBAiElpE/KcPg36dIiOvpsRxDCyhmlP2YY9ZU'.
+ 'v8NMb/1id+FGO0DTztkSXLOONUqeITsMkW2zwnJEIDFhYGx+A1'.
+ 'kwK4mASkvKDPc3p0iYhRRwYUhZLUTyV6Eu0t4s1Y4kcx6W6KaM'.
+ 'EZThcXH59RRhGEgIAddnBwNEBKqqpUtWBIF22YDIhJsbEkJqFN'.
+ 'qLtERHs7GnUkwISEQAf0uj30bY39PzbiC6qrDu2cExJ69Nhhhz'.
+ '59UlIUipCQOnVi4sjG7ubJBy6um0C+he/0iDHQKIQERYyKFLqr'.
+ 'SI/W6kJCnvOcrWSLSquC1/Jw9Ks3R0FQKHr0uMc9bnCDGjX69A'.
+ 'H0XlcJkibN5jOe/alCZStHbjJL9lSMLkXExvCXRiDV6GZEeGeX'.
+ '3TvvBVQoEjfBL/v0rT75Th7VU5C8gktI6NLlMY+5yU3WWGODDf'.
+ 'r098tHpNFNH7/2lKdXXdz7efLzVaqJIBOCmK8AJUlI6g0aV+9y'.
+ '9+p7AR3bMQpTBWPy7yeN6fy0jNwewfpvC9Xe+3kFoUuXe9zj5n'.
+ 'BusEGHjh6GIAGawC2FWuvSvbbF1maFylZAsC1ISZADBiVNSJrP'.
+ 'eX73MY//skHP85z5+fnSxQsXj//4n39cmnPn7LbZlsajBmEnBL'.
+ '1nuEGDG9x4aa5Ldz+h0RCuBqwBv1Wo+7vs9r7n++0MmYeAM+zB'.
+ '+61EK1QUEnbbtN+9Bh3Hsebn54u//PdfLq9eWl2ZnZ1dSnaSwu'.
+ 'Pin40b9g3doKE0WoNIl65xj3v75njd3BBubQi6ExKmDWkMRKSl'.
+ 'tSbVKQcMao1Go5Ugb0+x53nOyZMnSysrKydXLq1cWlxa/McgCB'.
+ 'Yev3hU+GPrD3I5/q94k3pXYQY58q6B5Bs0HB//neaGx00gyWaz'.
+ 'VCoV7bquCoKAnZ0dfN/f03egLGj0m3XQNE1jdnY2/+WXXy6trq'.
+ '6uzP3oR5eCIFi4detW5feXL1vr679Let37zVB3/mQytjXJwmSB'.
+ 'wikHp9ShY0RESqObwPrr5oBERKhUKly4cIFqtUq9XufmzZtsbW'.
+ '2hXvuDwTTNtxZq8TyvsLy8vLS4uLgahOHphw8elL69fNlc++qr'.
+ 'uFOrNXPddm1cczVL5f5P+Lv5MuOJgTGxwYbZpZsCdeAq8M1Bcw'.
+ 'CGYeC6LtVqlRMnTjAyMkKn0yGXyx0N0LZt03Ec1zCMSrfXO37v'.
+ 'zp3S769csb+/ciXt1mrNdHf3ltZ6Lca8ZpJsduhtCdb2gEFJoQ'.
+ 'xADYHuHDS3f32lFEEQUK/XGRkZoVAocP78eZaXl9FaI/Jq25Uk'.
+ 'yWHAYrHozM/PlxYWFibTND32sFbLXrtyxVz76qukXas1M61WTW'.
+ 'm99gx+20TdN9jqtfjP7QzOwwYNp037Zd0DukDnIByA1pqdnR2+'.
+ '++472u02Z8+eZWJiAsMwDsEBRNGBzYJpmsaJEyfyX3zxxdLS0t'.
+ 'KlVqu1dP3q1cLta9ekU6u1dat1J9b6Sk9kraV1rYXegW7apDYw'.
+ 'kFY6fPc4MNTw88bwfZ/NzU2UUnieRxAEiAiGcXiXfcigiIjruo'.
+ 'VyubxkWdbK7fX1xWvffFMInjzBM82uMT5+p++6V1UUrSe7u03t'.
+ '+8lezlKt3gHyl0aSJDQaDa5fv876+vo+w6FzDq1BpZRsb2/bly'.
+ '9f9vL5/Njdu3fzG0+eMJHNxsfn532vXN5NPG/7abPZal6/Hvfe'.
+ 'kroPHfsm98f7AHW9Xo+//vrrlmVZm71+37QNw3JnZ9PK4uJGpV'.
+ 'pt4Dh+vLGhsrmcfv1iHzu01m89HjIdCon2fb8TBMHtvYeRUn50'.
+ '1Oj4vqp3Ok1f5LYSadfr9dQfDN642P/XeF2DA+SBAuA4jkOhUK'.
+ 'BQKESO43S11p3BYBDt7u4y+CtB/i/q7jp1GMiw2AAAAABJRU5E'.
+ 'rkJggg==' ;
+
+//==========================================================
+// File: ppl_blue.png
+//==========================================================
+ $this->imgdata_large[3][0]= 2284 ;
+ $this->imgdata_large[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFRAiTZAL3gAACHlJREFUeJy9mGtv29YZgJ9zKF'.
+ 'F3y/Q9jh05tuQkarKgbYasde0UBdZgwNou/Vqga/sD9mP2B4a1'.
+ 'BbZ9atFPxb5sqOtmXbI19bqsluPYiR3HN90vFEWRZx/IJI5zqa'.
+ 'x0OwBBSgR5Hj7v+55zSEFXTUgIJyA9C6/9RsjMjAyFIxxJCDc7'.
+ 'iBqKgyZACGg3G2x9+xXf/fG33P3mC9qNKsp1O+1JdkEnQTdgIO'.
+ 'ttCSMUi8gj072MnugllAyB9G8rBGi6RsToJTF6iuRoFi1kHKZf'.
+ '7fB8Iggj0/Dy23D2dakNTR3JDsXPvzstxmZGRMER1EwHhQAEgE'.
+ 'CLhIkPD6InY9S3djGLJVBtQP1Qb4HDAyoJYQOOZkPx49nhTH9i'.
+ '7MUBGT7egxkJgd70wZS/CUkoZtA/fRoE1DZ2ACiv52ibReCp4e'.
+ '7CIEHomxDiuVdGTqUnf/ZeOjR8fpiVXZul5ZrY3bWwbdcLr/dA'.
+ 'AAIpAwQjUWIjQ+g9HZvswiCgBVF9/SI6OSLGzo0i+oLi6+Utbq'.
+ '+bKEftgwOE/0Ohocf66M+cBjo22U2RQLIHMhmYnvaOpR9S8bSU'.
+ 'UqCURGpRkuMZMm9cIvPGJZLj0yBjT2LprkiSkykx9cuXIhOnUs'.
+ 'm+QNC2XdG02ggBTcvFabsPWwTPpBAChSCgh4kYBpoeplWp47Qs'.
+ '7EYDt21xINzd5GCAxLExRl89Z+nHjpbKMmjbmkgfDzI0JEW53K'.
+ 'Jaa6NcAOEX8v52uJzsBlAS6u0hcnTIccPRqhWPCUcLD+s1EaUp'.
+ 'HCEhEMCyHNpt9SjgIU12A6iw6xb123vYhaaKjB9tlgMD5X+uBp'.
+ 'zdkpg6azA8EaNQtKlVba+Xez4eCntnJrsDdFsW5nYFpxlFN846'.
+ 'DXe8utkM4mhi+EgQmjYbS2WqexZKk6BpjwJ2YlK5VjeA3pNDiH'.
+ 'YjRWPzPE7tmBo8EWwGhkXx+z3uXL7D3rU97LIF8RBEAl6lK/Uo'.
+ '6JNM1rZ2aTcr3eUgIQOGTgbdwXMGyRejenLYTvQGbAdRuetSud'.
+ 'OivVuFZgtCEgICghICnZoMhmlVTPR49LCAEkQUhk/B7KXe0MWf'.
+ 'nxj8xVR/cDheK14WZmtVMJSBnlGoN6FmQq0FLfdwJgORKPHRo/'.
+ 'Snzx4G0F/FjJ4KiOdmjPCrrx8bffnMybMv9MQGNG3rzlVqtR1B'.
+ 'sh/CYXCD4Aag1oCW7ZnUOjSp6WFi/QNEB8Y7BfTNjZyCmUvJ0I'.
+ 'XXT47MTp98Ybon9VZCk8cVazfqlNargsY34G7ByAlIjkHd9CCr'.
+ 'LbBdiHViUgiECuDKYCdz8b2cywREdiYZOj8zNnLuzOTzx6ODp+'.
+ 'OaGaqwVzBFqz0Idhz2loE7YEwBLaAJLQcKbW8qjAcBF5Jh0AMP'.
+ 'IOHe6kxgtb3UMO2OxkF//ffK28nQqxfvm3szrtnDVa799Qb/+v'.
+ 'NtsbNSpm3tAv8B+w7Ub0FhAyoBcMPec9oK6raXk48ziQBXQcmC'.
+ 'pT3YqHa0mpEBkTR6wz/Jjo2cy04+fzwxdDquNfQKO7sFUbpu0c'.
+ 'wp3JoAYsA42Bbkl4GCryUNDEM7Avm6Z/CgSYBWG8pNuFuDu1Wo'.
+ 'tjoxKIJGeHIiM/jmK9NnX5ycuJQMtUcqXPvLDTa+qIie4hAJ1U'.
+ 'vdrmO2HaDfB931twJgAn1A4lGT96obPHPLBbhVgUoTHHWo9aAA'.
+ 'JVAKpyKEmQNzWRENAsL18ycKjAFN/9gCNvzLB/390MMmE7pnDi'.
+ 'Bvwt0K5Jv3O+0oB22nJ1Vvjb/UMhOpcKknqN1OiMB2DNHU2G5s'.
+ 'sVndpGJVcZXjX1IAlvw9PmhRQcOFPhsSDkiBrQR1G7brgs0a7D'.
+ 'ag3FK4rguqBXarI4Nt1SJv5gls7TEWtJDRBO2GwnIs8maevFnA'.
+ 'Gx6awLZvzeTBu4kFbLigijC47pscpx0xyDfkvtUEnlarCDtrUC'.
+ 't2HGIhvPHVdVwqjTIrxRU2a5uUrYoP0QZ2gMvACl7+3V/LuKDq'.
+ 'sJuDy597516+CEezIHXv7vcgXQu2l+Bvn8He9Y4AE4kgk5P9DE'.
+ 'R6aFdq5Et5Nit3yTf3m9sBcsAN3+D98c0Fit5JawE25r1zg1Fo'.
+ '5B8GFD7g+nVYnu8EUEop9XTa0N/9dUbqcphP/rDJzbUClVbpgR'.
+ 'y2fXM3fND95qj75J8AC6BWPINfVSBieK+x+6cS5UCzCLu3oFV9'.
+ 'GqCMx2NGOp2Znpv7aXZudsool3T5J/179sxVlHJ4kGPrP2COBX'.
+ '/7DmiApWCjxIMXpYNznYuXM+6TAKWUMppOZzLvv//ery5cuDCT'.
+ 'SqVS336bCwr1JfAPB9r+2KAFwJS+OcETzZHz/7v3etl6ipz77X'.
+ 'GAMh6PG+l0OjM3NzczOzs3k0pNnFlbW43+e/GKtMqrblSsF03V'.
+ 'WHcJA0PjIAzvg9JTze2H67g9DjAwOTmZ+uCDD96anZ2dnZiYmF'.
+ '5dW41++Lvfa1fnr7qllVK9103mXNTnJgPA+YugsvB3HTaEl+Qs'.
+ 'AZ/yeHPPDCiTyaRx5syZbGoilV1dW00szC9oV+avusuLy0Xd0X'.
+ 'MgFkDM+zkYBZEHV8f7wwKu84zmngQoNU0LaZoWUa4K31y5qX/8'.
+ '4cfyyvwVN5/L10NOKNeg8UmDxoKF5Vfj1xXAgD0JrgAcvBDfel'.
+ 'a4g4AykUgY6XR6emJiIru2ttZXq9S0K19eUcuLy8WQE8o5OAsN'.
+ 'Ggsmpl+NpoL1g9X4UBU+C9xDgEKIwNTUVOqdd955M9mbnJ3/cj'.
+ '6Vu5aTheXCQXNdVeMzAwJSCGEA2XKpnF1cXIzlFnOVhJPIKdR+'.
+ 'c88ctq4AlVKsrKzw0UcfKcC5uXqzXnNqSzb2pwLxOHP/l7Z/BN'.
+ 'eB01LKt4HTrusKvGr8jB+hGn8MQAkYQMrfw4Nq/MFPtf+rdvDb'.
+ 'k8QL+/5Z4Uepxm7bfwHuTAVUWpWaqAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: ppl_green.png
+//==========================================================
+ $this->imgdata_large[4][0]= 2854 ;
+ $this->imgdata_large[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFQ4hANhluwAACrNJREFUeJytmF1zE1eagJ+3u9'.
+ 'XdkvUty2AbmLEtEzDBgZ0UpDBOalNTUzU3czl7tct/2n+wt3M/'.
+ 'NVM12SSTQQSyW2TA+QAJQogtYYFtyfrqL3WfvWj5g8AEjzfvhS'.
+ 'SXjk8//Zz3Pf3qCMcJAWxMKlT4kH+jwu/FknnJSUItKFHzCrKA'.
+ 'BggBQx5ziz/wn/yBz3hED4/oaJfSjgVoYjJJgTLTZCjohp7IGT'.
+ 'k5aZ4kb+bRTR30Q7djj8f/kpPMUSCFedRL6W8e8qMQNE6S4xpv'.
+ 'c5HrTPFubiJ3ZnlyOXV59rJYU5Z00h1c3d0brxAiUkScRijisk'.
+ '6XLTyiN3s8HuAJpniXa/q8/pt8Or+0kF8oXJm5YiydWcIpOrJu'.
+ 'rjOQwd54AQwsMpTJYhPSoYuLQ58An/DnBQSdImXO8avsTPbqpc'.
+ 'lLp67OXDVzMznZLGxSs2qyIRu4at8gKHQEC50kE1icxqCAdxST'.
+ 'xjEA44tqaJlERl8uLWvvnX5PHuQfcCdxh5qq0aX76vj4WgWyXO'.
+ 'QiNgBP8IAaddr08X8+wHFmJSQhBbPAZGoSZSt5wQs6qoNC7UEd'.
+ '4AEoLIQSCaCCy78Dv8Tiv1hjjW1CRj8XIAgEKqDtt9keboMJZa'.
+ 'vMjuzQVd3Xr9prTJo+GF/jKZea95R25Lxs8jg5qFGiwDnOS0mW'.
+ 'NE0rjNRIt3WbklUCA9mV3Zdz8OBT/JfCQLB0SKYVVjGFYSfx/E'.
+ '26ow4e6uDujlPFQpE0FU6P8qNTHdXJdEdda0qf0itWBVM3pa/3'.
+ 'ccUlIECJet0cAJoeYk5EZCeS5IwEoerSxccJBwRqFFf38QCTaO'.
+ 'TRVFKJm3NTbtLNSyh2IkhIXsvLCesEGNCWdmwyruSD/z9kUlRc'.
+ '3bqNlSxhJNJ43p5JITrOEis8Qtr0cXEpU/JT/pmO18n2vb42pU'.
+ '3JnDnHMBqyPlpnoAaxhr2llv1ZUBqEGlqYwDQMsskMOcMgVL3Y'.
+ 'ZOQTHAcQQiIGjHCwCaiovjrv4hbcpKuJJjIcDHm685RGr4GLCx'.
+ 'YHkAcrLoAoDSLBiAQrMkjqybHJCbxgh+7xAC1MpsgzwRwD3qHL'.
+ 'WyTIBdlAa6u2rHfXaew06PV78ZZjAwleNnkolECoH5i090wOcY'.
+ '+TgwYzFHiPi1zkOkXexeAMASnVU+LiyiA1wFUuaqggACLizeWw'.
+ 'ycMzyssmVYKkbpGyC5T+OUALk2mKLHKWf+ED/az+YW42d66YL+'.
+ 'aNrmEEzQCFEnKw368EgEvcN1m80eTIQIt0TFOjMJHkzNEBBYPp'.
+ 'sblf8QHzrORO5JaWZ5ZLl6cuJyyxpNPv4PZdoT+GyIxBfI5uUg'.
+ 'eJMCwP2/bIHO1JEudcgUUWOceKNq99mCvnzs5PzRcuTV4y5mRO'.
+ 'SMIjo47z5S7a94oQCNKgJsZwO7D/IDNg3/LLhRNXt4JohBb4aG'.
+ '82GLdXcf93mQ+Y43r2RHZp+cRy6cqJK4l8MS+tdItaqiYtc0Mm'.
+ 'QpfJARh98HYh9IiXVcaAo58wGb+LBAjbSPgCOcoSa0wzxXtc08'.
+ '/pv8mfyL+9MLVQvDJ1JVHJV6SZbFI1qtTsB+KlehRtRTGE8Afo'.
+ 'P4DRcAxiEudhAHjjzz+ubgX4oHowakHQOlqzICQwyVPITGVOXi'.
+ 'xfLF6aumzmczl5lHzMff2+fCdPaGttEkXoLQAO9B7C6EugPYby'.
+ 'gVPjGXc5eIbNAJPjGwiAbaAJUQv8wVG7GROkJFpyOqn/ovgLba'.
+ '44L0+sDaraXb6jzq7aBQWjBOyUoHcaopOgmaA3IRyNDZnA1HjO'.
+ 'HSBkr7eEFDAEngHrQCf+/s2A8cSiSkqcKUeeTjwFy2Jd78t3+L'.
+ 'TR4itIiBLwLQhzkJyB5Cx4HXDaENVQCBAQcRqFIHTRaBIvuYXg'.
+ 'AdsouuNxEL0ZUBHnSQp66R73zYfUtQ6OytKT8RckQAJQoLtgO5'.
+ 'BJgj0D/WfgdyHaAHx8THoUcbGx8ciwhUl3bDEiToURPooeI7pH'.
+ 'MziK9Yd9nU5a6GgKjOH41vsgI4hAcyC5AZkapF+AoYNrjjsuhx'.
+ 'FbtPmeB5ykyQQzTPAWAQWC8S9oAI0QRRuPb9jkmyMZNAOTklvC'.
+ 'GGYZaFkGmkVAh8h4DtKFMIBunG+pB5B5AIkGBDsQ+qBiL20caj'.
+ 'zhJknq5KlgMkLjJHJos4kYEbFJi5vc5eYbATVN02bNWe19+32t'.
+ 'aJWlFm3wbf8Rz5NbDFJdlOFBF/g7cBf0JkrbBb+F6j1DOduEkU'.
+ '8bWCOiSofPWadBnSZDWmgUkEMGhZCINut8S/0NBtPptFlZrBSu'.
+ 'vnt1+ndnflfIp9OJ/279Ubbbd+lP7KBKPoEBsgnqLph/BRzwdS'.
+ 'LnBUFvHcfdpRsGPAGqwMco6jynz+e0SPKYCHMfLX5VKHwcenR+'.
+ 'Igd1XTcqlUr+xn/cePv91fevzy8sLO2OtrOpWkqL7gXKSAVRdh'.
+ 'ZFEmEXoYkwBNqovoc/3GHH3aUR+jwC1oD/AWrANi4hGwyBzqEG'.
+ 'Vvb77Dgi0eT1VZzJZMxKpVJYXV1dXF1dXVm6sPSvruue3Xzcyj'.
+ '6/syvDzwj0lNazK6Fj5LFCRZouZpBABj6jXouu3+Np6HNvDHaf'.
+ 'g91t74msbMuOJicnSSaTKKUQEUQEpRSO69But1/dB0VEm5uby9'.
+ 'y4cWNpdXX1+sLCworrume//PuXpeqnVeOban0U1PW2kcx+O9L7'.
+ 'Te9sUB4lWFR9SqNtNGcHx+/RDD2+Am4D94CnQA8OjjlEhMnyJC'.
+ 'srK8zOzu7BiYioMAzZ2Njg9u3brwIqpSSXy2WXl5eXLly4sOo4'.
+ 'zoV6vV6oflrVP/7Tx8Hmw1Zb6ydqmpWp7ha8h4O3gjOhzVANmF'.
+ 'XPMNQWvdDnCXCXuHR+APqH4fbCtm2mp6eZn59H13WJuYXRaKSU'.
+ 'UiSTyVcBdV3XDcOwRaTU7/en19bWCn/79G+JL/76RbhZ22y7u+'.
+ '6ahl71nPDz/nO17m7wAxlabFOihy4+DvAcqAMbPzZ3OFzX5dmz'.
+ 'Z2iahoiosUUVhiGNRgPHcV4GzGQy5uLiYuH8+fMzo9FoslarJW'.
+ '9+elP75E+fBJu1zY7qqpqBUW3T/niohnVvy+1zm5aVtp+WE2XT'.
+ 'nrHFzbjh1tYLz3XdPjD4R3BKKba2tqhWq4dzUO3noBPn4H5PKy'.
+ 'LaO++8U7hx48byhQsXVne7u6tf3/v64t3P7mbq9+odt+OuaWi3'.
+ 'PLxbW2ytubjbQCgiMnt6VlaurWgz0zM0m02q1WrUaDSUUuqI56'.
+ 'ivDxE5MCgiYllWtlwuL5mmufLV/a/O/uXPf9Ff1F+80Lv6Yx29'.
+ '2qHzyZBh3cdvc7gaTZuZkzPh/Py8ACqVSv1/uPZDKXUAGEWRtF'.
+ 'qtxEcffZTL5XLF+2v39fqjeivshA/TpP83JLwzYFBzcA4370Cc'.
+ 'S81nTRBUs9lkOByi1GuOPI4Rh3+26JZlnSkWi781DOPXvV4v3+'.
+ '/2G0R8kSBxB/jew+tERK+c49m2TblcxrZtXNfl+fPneJ6HZVmU'.
+ 'y2VJJpNyaJ9TSinlOA5bW1u4rntkQA0oAG8D54gb9W3ianxM3A'.
+ 'e/cn73U3Hq1Cm5du2aPjs7a+ztcSIShmE4ajQa6tatWzQajZ+0'.
+ 'fbiKI+It4SvijVUj7kL2qvGfgkskEqTTaZmcnDROnTplJhIJTU'.
+ 'QiwPd9P/Q8T6XTaQzDIAiCfzjP/wFVfszuFqdHXgAAAABJRU5E'.
+ 'rkJggg==' ;
+
+
+//==========================================================
+// File: pp_red.png
+//==========================================================
+ $this->imgdata_small[0][0]= 384 ;
+ $this->imgdata_small[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhouFobZrQAAAQ1JREFUeJyV1dFtwyAQBuD/og'.
+ 'xQdYxa8gRY6hJ0jK6QdohMkTEuE5wUj5ERen05IoLvID7Jkn2G'.
+ 'j8MgTMyMXqRlUQBYq9ydmaL2h1cwqD7l30t+L1iwlbYFRegY7I'.
+ 'SHjkEifGg4ww3aBa/l4+9AhxWWr/dLhEunXUGHq6yGniw3QkOw'.
+ '3jJ7UBd82n/VVAlAtvsfp98lAj2sAJOhU4AeQ7DC1ubVBODWDJ'.
+ 'TtCsEWa6u5M1NeFs1NzgdtuhHGtj+9Q2IDppQUAL6Cyrlz0gDN'.
+ 'ohSMiJCt861672EiAhEhESG3woJ9V9OKTkwRKbdqz4cHmFLSFg'.
+ 's69+LvAZKdeZ/n89uLnd2g0S+gjd5g8zzjH5Y/eLLi+NPEAAAA'.
+ 'AElFTkSuQmCC' ;
+
+//==========================================================
+// File: pp_orange.png
+//==========================================================
+ $this->imgdata_small[1][0]= 403 ;
+ $this->imgdata_small[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhwAnApz5AAAASBJREFUeJyN1dFthDAMBuDf7S'.
+ '3BCm2VCRKpS4QxbhikW6IewzcBqm6Fm6JyH7iEEByCn5AJH38g'.
+ 'BBIRHNUzBAWAGNfe/SrUGv92CtNt309BrfFdMGPjvt9CD8Fyml'.
+ 'ZZaDchRgA/59FDMD18pvNoNyHxMnUmgLmPHoJ+CqqfMaNAH22C'.
+ 'fgqKRwR+GRpxGjXBEiuXDBWQhTK3plxijyWWvtKVS5KNG1xM8I'.
+ 'OBr7geV1WupDqpmTAPKjCqLhxk/z0PImQmjKrAuI6vMXlhFroD'.
+ 'vfdqITXWqg2YMSJEAFcReoag6UXU2DzPG8w5t09YYsAyLWvHrL'.
+ 'HUy6D3XmvMAAhAay8kAJpBosX4vt0G4+4Jam6s6Rz1fgFG0ncA'.
+ 'f3XfOQcA+Acv5IUSdQw9hgAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: pp_pink.png
+//==========================================================
+ $this->imgdata_small[2][0]= 419 ;
+ $this->imgdata_small[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhsQzvz1RwAAATBJREFUeJyd1MFthDAQheF/oi'.
+ 'gF+JYWQKICkCJRA1vGtrDbxFbhGvY0HVjCLeS2BeTiHFgTB2wg'.
+ 'eRISstCnmcG2qCpbuXf3ADBQzWsPfZfS9y9HsEu4/Fo33Wf4Fx'.
+ 'gxL3a1XkI3wbTNXHLoboVeLFUYDqObYBy+Fw/Uh9DdCmtOwIjF'.
+ 'YvG76CZoOhNGRmpO8zz30CJoOhMAqlDxFzQLppgXj2XaNlP7FF'.
+ 'GLL7ccMYCBgZERgCvXLBrfi2DEclmiKZwFY4tp6sW26bVfnede'.
+ 'e5Hc5dC2bUgrXGKqWrwcXnNYDjmCrcCIiQgDcFYV05kQ8SXmnB'.
+ 'NgPiVN06wrTDGAhz5EWY/FOccTk+cTnHM/YNu2YYllgFxCWuUM'.
+ 'ikzGx+2Gc+4N+CoJW8n+5a2UKm2aBoBvGA6L7wfl8aoAAAAASU'.
+ 'VORK5CYII=' ;
+
+
+//==========================================================
+// File: pp_blue.png
+//==========================================================
+ $this->imgdata_small[3][0]= 883 ;
+ $this->imgdata_small[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAMAAAC6V+0/AAACi1'.
+ 'BMVEX///8AAAAAADMAAGYAAJkAAMwAAP8zAAAzADMzAGYzAJkz'.
+ 'AMwzAP9mAABmADNmAGZmAJlmAMxmAP+ZAACZADOZAGaZAJmZAM'.
+ 'yZAP/MAADMADPMAGbMAJnMAMzMAP//AAD/ADP/AGb/AJn/AMz/'.
+ 'AP8AMwAAMzMAM2YAM5kAM8wAM/8zMwAzMzMzM2YzM5kzM8wzM/'.
+ '9mMwBmMzNmM2ZmM5lmM8xmM/+ZMwCZMzOZM2aZM5mZM8yZM//M'.
+ 'MwDMMzPMM2bMM5nMM8zMM///MwD/MzP/M2b/M5n/M8z/M/8AZg'.
+ 'AAZjMAZmYAZpkAZswAZv8zZgAzZjMzZmYzZpkzZswzZv9mZgBm'.
+ 'ZjNmZmZmZplmZsxmZv+ZZgCZZjOZZmaZZpmZZsyZZv/MZgDMZj'.
+ 'PMZmbMZpnMZszMZv//ZgD/ZjP/Zmb/Zpn/Zsz/Zv8AmQAAmTMA'.
+ 'mWYAmZkAmcwAmf8zmQAzmTMzmWYzmZkzmcwzmf9mmQBmmTNmmW'.
+ 'ZmmZlmmcxmmf+ZmQCZmTOZmWaZmZmZmcyZmf/MmQDMmTPMmWbM'.
+ 'mZnMmczMmf//mQD/mTP/mWb/mZn/mcz/mf8AzAAAzDMAzGYAzJ'.
+ 'kAzMwAzP8zzAAzzDMzzGYzzJkzzMwzzP9mzABmzDNmzGZmzJlm'.
+ 'zMxmzP+ZzACZzDOZzGaZzJmZzMyZzP/MzADMzDPMzGbMzJnMzM'.
+ 'zMzP//zAD/zDP/zGb/zJn/zMz/zP8A/wAA/zMA/2YA/5kA/8wA'.
+ '//8z/wAz/zMz/2Yz/5kz/8wz//9m/wBm/zNm/2Zm/5lm/8xm//'.
+ '+Z/wCZ/zOZ/2aZ/5mZ/8yZ///M/wDM/zPM/2bM/5nM/8zM////'.
+ '/wD//zP//2b//5n//8z///9jJVUgAAAAAXRSTlMAQObYZgAAAA'.
+ 'FiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElN'.
+ 'RQfTAwkWGTNerea3AAAAYUlEQVR4nHXNwQ3AIAxDUUfyoROxRZ'.
+ 'icARin0EBTIP3Hp1gBRqSqYo0seqjZpnngojlWBir5+b8o06lM'.
+ 'ha5uFKEpDZulV8l52axhVzqaCdxQp32qVSSwC1wN3fYiw7b76w'.
+ 'bN4SMue4/KbwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: pp_green.png
+//==========================================================
+ $this->imgdata_small[4][0]= 447 ;
+ $this->imgdata_small[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wMJFhkLdq9eKQAAAUxJREFUeJyN1LFVwzAQxvH/8f'.
+ 'IeDS0FLKABlN6eIwPYAzCHB0gWYI2jj+i1ABUTQN4TRSQ7iiWZ'.
+ 'qxLn9Mt9ydmiqrSq930AYFiu6YdKrf/hP1gYQn6960PxwBaYMG'.
+ 'E9UA3dBFtVQjdBOQmBakLennK0CapRwbZRZ3N0O/IeEsqp3HKL'.
+ 'Smtt5pUZgTPg4gdDud+6xoS97wM2rsxxmRSoTgoVcMZsXJkBho'.
+ 'SmKqCuOuEtls6nmGMFPTUmxBKx/MeyNfQGLoOOiC2ddsxb1Kzv'.
+ 'ZzUqu5IXbGDvBJf+hDisi77qFSuhq7Xpuu66TyJLRGbsXVUPxV'.
+ 'SxsgkzDMt0mKT3/RcjL8C5hHnvJToXY0xYRZ4xnVKsV/S+a8YA'.
+ 'AvCb3s9g13UhYj+TTo93B3fApRV1FVlEAD6H42DjN9/WvzDYuJ'.
+ 'dL5b1/ji+/IX8EGWP4AwRii8PdFHTqAAAAAElFTkSuQmCC' ;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/imgdata_squares.inc b/includes/jpgraph/imgdata_squares.inc
new file mode 100644
index 0000000..fadf2e0
--- /dev/null
+++ b/includes/jpgraph/imgdata_squares.inc
@@ -0,0 +1,152 @@
+<?php
+//=======================================================================
+// File: IMGDATA_SQUARES.INC
+// Description: Base64 encoded images for squares
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_squares.inc,v 1.1 2003/03/23 13:37:37 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+class ImgData_Squares extends ImgData {
+ var $name = 'Squares';
+ var $an = array(MARK_IMG_SQUARE =>'imgdata');
+
+ var $colors = array('bluegreen','blue','green',
+ 'lightblue','orange','purple','red','yellow');
+ var $index = array('bluegreen' =>2,'blue'=>5,'green'=>6,
+ 'lightblue'=>0,'orange'=>7,'purple'=>4,'red'=>3,'yellow'=>1);
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Squares () {
+//==========================================================
+//sq_lblue.png
+//==========================================================
+ $this->imgdata[0][0]= 362 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAIAAADZrBkAAAAABm'.
+ 'JLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsRAAALEQF/ZF+RAAAA'.
+ 'B3RJTUUH0wMLFgojiPx/ygAAAPdJREFUeNpj/P377+kzHx89/c'.
+ 'VAHNBQ5VBX52HavPWWjg6nnDQbkXoUFTnnL7zD9PPXrz17HxCj'.
+ 'E6Jn6fL7H7/+ZWJgYCBGJ7IeBgYGJogofp1oehDa8OjE1IOiDa'.
+ 'tOrHoYGBhY0NwD0enirMDAwMDFxYRVD7ptyDrNTAU0NXix6sGu'.
+ 'jYGBgZOT9e/f/0xMjFyczFgVsGAKCfBza2kKzpl3hIuT1c9Xb/'.
+ 'PW58/foKchJqx6tmy98vbjj8cvPm/afMnXW1JShA2fNmQ9EBFc'.
+ 'Opnw6MGjkwm/Hlw6mQjqwaqTiRg9mDoZv//4M2/+UYJ64EBWgj'.
+ 'cm2hwA8l24oNDl+DMAAAAASUVORK5CYII=' ;
+
+//==========================================================
+//sq_yellow.png
+//==========================================================
+ $this->imgdata[1][0]= 338 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAWl'.
+ 'BMVEX////+/+H+/9/9/9v8/8P8/8H8/7v8/7n6/4P5/335/3n5'.
+ '/3X4/1f4/1P3/031/w30/wn0/wPt+ADp9ADm8ADk7gDc5gDa5A'.
+ 'DL1ADFzgCwuACqsgClrABzeAC9M0MzAAAAAWJLR0QAiAUdSAAA'.
+ 'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEDlOgDj'.
+ 'EAAAB+SURBVHjaVcpbCsQgDEDRGERGKopjDa2a/W9zfLWj9/Nw'.
+ 'Ac21ZRBOtZlRN9ApzSYFaDUj79KIorRDbJNO9bN/GUSh2ZRJFJ'.
+ 'S18iorURBiyksO8buT0zkfYaUqzI91ckfhWhoGXTLzsDjI68Sz'.
+ 'pGMjrzPzauA/iXk1AtykmvgBC8UcWUdc9HkAAAAASUVORK5CYI'.
+ 'I=' ;
+
+//==========================================================
+//sq_blgr.png
+//==========================================================
+ $this->imgdata[2][0]= 347 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAZl'.
+ 'BMVEX////0+vv0+vrz+fry+frv+Png7e/d7e/a6+zY6+250tSz'.
+ '0tSyztCtztGM0NWIz9SDzdNfsLVcrrRZrbJOp61MpqtIr7dHn6'.
+ 'RErrZArLQ6q7M2g4kygYcsp68npa4ctr8QZ20JnqepKsl4AAAA'.
+ 'AWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU'.
+ '1FB9MDCxYEByp8tpUAAAB7SURBVHjaVcjRFoIgDADQWZpWJpjY'.
+ 'MsnG//9kzIFn3McLzfArDA3MndFjrhvgfDHFBEB9pt0CVzwrY3'.
+ 'n2yicjhY4vTSp0nbXtN+hCV53SHDWe61dZY+/9463r2XuifHAM'.
+ '0SoH+6xEcovUlCfefeFSIwfTTQ3fB+pi4lV/bTIgvmaA7a0AAA'.
+ 'AASUVORK5CYII=' ;
+
+//==========================================================
+//sq_red.png
+//==========================================================
+ $this->imgdata[3][0]= 324 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
+ 'BMVEX////++Pn99/j99ff99fb98/X98/T98PL55uj43+P24+bw'.
+ 'kKPvjaHviJ3teJHpxMnoL2Pjs73WW3rWNljVWXnUVnbUK1DTJk'.
+ '3SUHPOBz/KQmmxPVmuOFasNFOeIkWVka/fAAAAAWJLR0QAiAUd'.
+ 'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEHd'.
+ 'ceT+8AAABtSURBVHjaVchbAkMwEAXQq6i3VrQiQfa/zDYTw8z5'.
+ 'PCjGt9JVWFt1XWPh1fWNdfDy+tq6WPfRUPENNKnSnXNWPB4uv2'.
+ 'b54nSZ8jHrMtOxvWZZZtpD4KP6xLkO9/AhzhaCOMhJh68cOjzV'.
+ '/K/4Ac2cG+nBcaRuAAAAAElFTkSuQmCC' ;
+
+//==========================================================
+//sq_pink.png
+//==========================================================
+ $this->imgdata[4][0]= 445 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAApV'.
+ 'BMVEX////6+Pz69/v49Pr38/r17/jr4+/l3Onj2efh1ua/L+i+'.
+ 'q8m+Lue9Lua8qsS8LuW8LeS7pca5LOG4LN+2Y9O2YNW1ZdO1Kt'.
+ 'y0atC0aNGzb82zbc6zKtuzKdqycsuwa8qtJtOISZ2GRpuFN6GE'.
+ 'NqCDQpmCMZ+BPpd/LJ1/K519S5B9Jpx9Jpt9JZt6RY11BJZ1BJ'.
+ 'V0BJV0BJRzBJNvNoRtIoJUEmdZ/XbrAAAAAWJLR0QAiAUdSAAA'.
+ 'AAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYDF3iKMD'.
+ 'YAAACeSURBVHjaVczbEoIgGARgCiMtrexoWpaa2FHUgvd/tH4Y'.
+ 'BnEvv9ldhNPradPnnGBUTtPDzMRPSIF46SaBoR25dYjz3I20Lb'.
+ 'ek6BgQz73Il7KKpSgCO0pTHU0886J1sCe0ZYbALjGhjFnEM2es'.
+ 'VhZVI4d+B1QtfnV47ywCEaKeP/p7JdLejSYt0j6NIiOq1wJZIs'.
+ 'QTDA0ELHwhPBCwyR/Cni9cOmzJtwAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_blue.png
+//==========================================================
+ $this->imgdata[5][0]= 283 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAQl'.
+ 'BMVEX////4+fz39/z19vvy8vru7/ni4+7g4fHW1ue8vteXmt6B'.
+ 'hdhiZ7FQVaZETcxCSJo1Oq4zNoMjKakhJHcKFaMEC2jRVYdWAA'.
+ 'AAAWJLR0QAiAUdSAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0'.
+ 'SU1FB9MDCxYDN0PkEP4AAABfSURBVHjaVchHAoAgDATAVcCCIF'.
+ 'j4/1elJEjmOFDHKVgDv4iz640gLs+LMF6ZUv/VqcXXplU7Gqpy'.
+ 'PFzBT5qml9NzlOX259riWHlS4kOffviHD8PQYZx2EFMPRkw+9Q'.
+ 'FSnRPeWEDzKAAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_green.png
+//==========================================================
+ $this->imgdata[6][0]= 325 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAXV'.
+ 'BMVEX////2+vX1+vX1+fT0+fPz+PPx9/Dv9u7u9e3h7uHe697a'.
+ '6dnO2s3I1sa10LOvza2ay5aEwYBWlE9TqE5Tkk1RkEpMrUJMg0'.
+ 'hKiUNGpEFBojw8oTcsbScaYBMWlwmMT0NtAAAAAWJLR0QAiAUd'.
+ 'SAAAAAlwSFlzAAALEgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEFd'.
+ 'nFx90AAABuSURBVHjaVc9HAoAgDADB2HuJWLDx/2cKBITscW4L'.
+ '5byzMIWtZobNDZIZtrcCGZsRQ8GwvRSRNxIiMuysODKG3alikl'.
+ 'ueOPlpKTLBaRmOZxQxaXlfb5ZWI9om4WntrXiDSJzp7SBkwMQa'.
+ 'FEy0VR/NAB2kNuj7rgAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+//sq_orange.png
+//==========================================================
+ $this->imgdata[7][0]= 321 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAMAAABhEH5lAAAAUV'.
+ 'BMVEX/////8+n/8uf/8OP/59H/5Mv/zqH/zJ3/ypv/yJf/vYH/'.
+ 'u33/uXn/n0n/nUX/m0H/lzn/ljf/lDP/kS3/kCv/iR//hxv/fg'.
+ 'n/fAX/eQDYZgDW6ia5AAAAAWJLR0QAiAUdSAAAAAlwSFlzAAAL'.
+ 'EgAACxIB0t1+/AAAAAd0SU1FB9MDCxYEJIgbx+cAAAB2SURBVH'.
+ 'jaVczRCoQwDETRbLAWLZSGUA35/w/dVI0283i4DODew3YESmWW'.
+ 'kg5gWkoQAe6TleUQI/66Sy7i56+kLk7cht2N0+hcnJgQu0SqiC'.
+ '1SzSIbzWSi6gavqJ63wSduRi2f+kwyD5rEukwCdZ1kGAMGMfv9'.
+ 'AbWuGMOr5COSAAAAAElFTkSuQmCC' ;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/imgdata_stars.inc b/includes/jpgraph/imgdata_stars.inc
new file mode 100644
index 0000000..f877d0f
--- /dev/null
+++ b/includes/jpgraph/imgdata_stars.inc
@@ -0,0 +1,146 @@
+<?php
+//=======================================================================
+// File: IMGDATA_STARS.INC
+// Description: Base64 encoded images for stars
+// Created: 2003-03-20
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: imgdata_stars.inc,v 1.1 2003/03/23 13:37:37 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+
+class ImgData_Stars extends ImgData {
+ var $name = 'Stars';
+ var $an = array(MARK_IMG_STAR => 'imgdata');
+
+ var $colors = array('bluegreen','lightblue','purple','blue','green','pink','red','yellow');
+ var $index = array('bluegreen'=>3,'lightblue'=>4,'purple'=>1,
+ 'blue'=>5,'green'=>0,'pink'=>7,'red'=>2,'yellow'=>6);
+ var $maxidx = 7 ;
+ var $imgdata ;
+
+ function ImgData_Stars() {
+//==========================================================
+// File: bstar_green_001.png
+//==========================================================
+ $this->imgdata[0][0]= 329 ;
+ $this->imgdata[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAAUV'.
+ 'BMVEX///////+/v7+83rqcyY2Q/4R7/15y/1tp/05p/0lg/zdX'.
+ '/zdX/zVV/zdO/zFJ9TFJvDFD4yg+8Bw+3iU68hwurhYotxYosx'.
+ 'YokBoTfwANgQFUp7DWAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgF'.
+ 'HUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJj'.
+ 'CRyxgTAAAAcUlEQVR4nH3MSw6AIAwEUBL/IKBWwXL/g0pLojUS'.
+ 'ZzGLl8ko9Zumhr5iy66/GH0dp49llNPB5sTotDY5PVuLG6tnM9'.
+ 'CVKSIe1joSgPsAKSuANNaENFQvTAGzmheSkUpMBWeJZwqBT8wo'.
+ 'hmysD4bnnPsC/x8ItUdGPfAAAAAASUVORK5CYII=' ;
+//==========================================================
+// File: bstar_blred.png
+//==========================================================
+ $this->imgdata[1][0]= 325 ;
+ $this->imgdata[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79uRJ6jWPOSUtKrb+ejWO+gWPaGTruJTr6rZvF2'.
+ 'RqC2ocqdVuCeV+egV/GsnLuIXL66rMSpcOyATbipY/OdWOp+VK'.
+ 'aTU9WhV+yJKBoLAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJwynv1'.
+ 'XVAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_red_001.png
+//==========================================================
+ $this->imgdata[2][0]= 325 ;
+ $this->imgdata[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+eRFHzWG3SUmHnb37vWGr2WHG7Tlm+TljxZneg'.
+ 'Rk3KoaXgVmXnV2nxV227nJ++XGzErK3scIS4TVzzY3fqWG2mVF'.
+ 'zVU2PsV2rJFw9VAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJzCI0C'.
+ 'lSAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blgr_001.png
+//==========================================================
+ $this->imgdata[3][0]= 325 ;
+ $this->imgdata[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79Ehp5Yx/NSq9Jvw+dYwu9YzfZOmbtOmb5myPFG'.
+ 'gqChvcpWteBXvedXxvGcsbtcpb6su8RwzOxNmrhjyvNYwupUjK'.
+ 'ZTr9VXwOyJhmWNAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJTC65k'.
+ 'vQAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blgr_002.png
+//==========================================================
+ $this->imgdata[4][0]= 325 ;
+ $this->imgdata[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79EnpxY8/FS0dJv5+dY7+9Y9vBOubtOur5m8fFG'.
+ 'nKChycpW3uBX5+ZX8e2curtcvrqswsRw7OdNuLZj8/BY6udUpK'.
+ 'ZT1dRX7OtNkrW5AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJgXHeN'.
+ 'wwAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_blue_001.png
+//==========================================================
+ $this->imgdata[5][0]= 325 ;
+ $this->imgdata[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v79EY55Yi/NSetJvledYiO9YkPZOb7tObr5mkvFG'.
+ 'X6ChrcpWgOBXhedXi/Gcpbtcf76sssRwnOxNcbhjk/NYiepUbK'.
+ 'ZTfdVXh+ynNEzzAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJhStyP'.
+ 'zCAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_oy_007.png
+//==========================================================
+ $this->imgdata[6][0]= 325 ;
+ $this->imgdata[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+ejUTz11jSvVLn02/v1lj21li7q06+r07x2mag'.
+ 'lUbKxKHgy1bnz1fx1Ve7t5y+qlzEwqzs03C4pE3z2WPqz1imml'.
+ 'TVv1Ps01dGRjeyAAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJjsGGc'.
+ 'GbAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+
+//==========================================================
+// File: bstar_lred.png
+//==========================================================
+ $this->imgdata[7][0]= 325 ;
+ $this->imgdata[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAMAAABsDg4iAAAATl'.
+ 'BMVEX///+/v7+eRJPzWN3SUr7nb9TvWNj2WOS7Tqi+TqnxZtyg'.
+ 'Ro/KocPgVsjnV9LxV927nLa+XLTErL7scN24TarzY9/qWNemVJ'.
+ 'jVU8LsV9VCwcc9AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgA'.
+ 'AAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTAxYTJxi9ZY'.
+ 'GoAAAAcElEQVR4nH3MyQ6AIAwEUFIqiwju2///qLQmWiJxDnN4'.
+ 'mYxSv5lqGCs2nvaLLtZx/VhGOW1MjnPJWp0zsw2wsUY2jd09BY'.
+ 'DFmESC+BwA5UCUxhqAhqrA4CGrLpCMVGK4sZe4B+/5RLdiyMb6'.
+ 'on/PuS9CdQNC7yBXEQAAAABJRU5ErkJggg==' ;
+ }
+}
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph.php b/includes/jpgraph/jpgraph.php
new file mode 100644
index 0000000..b777170
--- /dev/null
+++ b/includes/jpgraph/jpgraph.php
@@ -0,0 +1,8736 @@
+<?php
+//=======================================================================
+// File: JPGRAPH.PHP
+// Description: PHP4 Graph Plotting library. Base module.
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph.php,v 1.242.2.49 2003/11/22 22:35:14 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+
+//------------------------------------------------------------------------
+// Directories for cache and font directory.
+//
+// CACHE_DIR:
+// The full absolute name of the directory to be used to store the
+// cached image files. This directory will not be used if the USE_CACHE
+// define (further down) is false. If you enable the cache please note that
+// this directory MUST be readable and writable for the process running PHP.
+// Must end with '/'
+//
+// TTF_DIR:
+// Directory where TTF fonts can be found. Must end with '/'
+//
+// The default values used if these defines are left commented out are:
+//
+// UNIX:
+// CACHE_DIR /tmp/jpgraph_cache/
+// TTF_DIR /usr/X11R6/lib/X11/fonts/truetype/
+//
+// WINDOWS:
+// CACHE_DIR $SERVER_TEMP/jpgraph_cache/
+// TTF_DIR $SERVER_SYSTEMROOT/fonts/
+//
+//------------------------------------------------------------------------
+// DEFINE("CACHE_DIR","/tmp/jpgraph_cache/");
+// DEFINE("TTF_DIR","/usr/X11R6/lib/X11/fonts/truetype/");
+
+// phpinfo();
+
+//-------------------------------------------------------------------------
+// Cache directory specification for use with CSIM graphs that are
+// using the cache.
+// The directory must be the filesysystem name as seen by PHP
+// and the 'http' version must be the same directory but as
+// seen by the HTTP server relative to the 'htdocs' ddirectory.
+// If a relative path is specified it is taken to be relative from where
+// the image script is executed.
+// Note: The default setting is to create a subdirectory in the
+// directory from where the image script is executed and store all files
+// there. As ususal this directory must be writeable by the PHP process.
+DEFINE("CSIMCACHE_DIR","csimcache/");
+DEFINE("CSIMCACHE_HTTP_DIR","csimcache/");
+
+//------------------------------------------------------------------------
+// Defines for font setup
+//------------------------------------------------------------------------
+
+// Actual name of the TTF file used together with FF_CHINESE aka FF_BIG5
+// This is the TTF file being used when the font family is specified as
+// either FF_CHINESE or FF_BIG5
+DEFINE('CHINESE_TTF_FONT','bkai00mp.ttf');
+
+// Special unicode cyrillic language support
+DEFINE("LANGUAGE_CYRILLIC",false);
+
+// If you are setting this config to true the conversion
+// will assume that the input text is windows 1251, if
+// false it will assume koi8-r
+DEFINE("CYRILLIC_FROM_WINDOWS",false);
+
+//------------------------------------------------------------------------
+// Various JpGraph Settings. Adjust accordingly to your
+// preferences. Note that cache functionality is turned off by
+// default (Enable by setting USE_CACHE to true)
+//------------------------------------------------------------------------
+
+// Deafult graphic format set to "auto" which will automatically
+// choose the best available format in the order png,gif,jpg
+// (The supported format depends on what your PHP installation supports)
+DEFINE("DEFAULT_GFORMAT","auto");
+
+// Should the image be a truecolor image?
+// Note 1: Has only effect with GD 2.0.1 and above.
+// Note 2: GD 2.0.1 + PHP 4.0.6 on Win32 crashes when trying to use
+// trucolor.
+// Note 3: MUST be enabled to get background images working with GD2
+DEFINE('USE_TRUECOLOR',true);
+
+// Specify what version of the GD library is installed.
+// If this is set to 'auto' the version will be automatically
+// determined.
+// However since determining the library takes ~1ms you can also
+// manually specify the version if you know what version you have.
+// This means that you should
+// set this define to true if you have GD 2.x installed to save 1ms.
+DEFINE("USE_LIBRARY_GD2",'auto');
+
+// Should the cache be used at all? By setting this to false no
+// files will be generated in the cache directory.
+// The difference from READ_CACHE being that setting READ_CACHE to
+// false will still create the image in the cache directory
+// just not use it. By setting USE_CACHE=false no files will even
+// be generated in the cache directory.
+DEFINE("USE_CACHE",false);
+
+// Should we try to find an image in the cache before generating it?
+// Set this define to false to bypass the reading of the cache and always
+// regenerate the image. Note that even if reading the cache is
+// disabled the cached will still be updated with the newly generated
+// image. Set also "USE_CACHE" below.
+DEFINE("READ_CACHE",true);
+
+// Determine if the error handler should be image based or purely
+// text based. Image based makes it easier since the script will
+// always return an image even in case of errors.
+DEFINE("USE_IMAGE_ERROR_HANDLER",true);
+
+// Determine if the library should also setup the default PHP
+// error handler to generate a graphic error mesage. This is useful
+// during development to be able to see the error message as an image
+// instead as a "red-cross" in a page where an image is expected.
+DEFINE("INSTALL_PHP_ERR_HANDLER",false);
+
+// If the color palette is full should JpGraph try to allocate
+// the closest match? If you plan on using background images or
+// gradient fills it might be a good idea to enable this.
+// If not you will otherwise get an error saying that the color palette is
+// exhausted. The drawback of using approximations is that the colors
+// might not be exactly what you specified.
+// Note1: This does only apply to paletted images, not truecolor
+// images since they don't have the limitations of maximum number
+// of colors.
+DEFINE("USE_APPROX_COLORS",true);
+
+// Should usage of deprecated functions and parameters give a fatal error?
+// (Useful to check if code is future proof.)
+DEFINE("ERR_DEPRECATED",true);
+
+// Should the time taken to generate each picture be branded to the lower
+// left in corner in each generated image? Useful for performace measurements
+// generating graphs
+DEFINE("BRAND_TIMING",false);
+
+// What format should be used for the timing string?
+DEFINE("BRAND_TIME_FORMAT","(%01.3fs)");
+
+//------------------------------------------------------------------------
+// The following constants should rarely have to be changed !
+//------------------------------------------------------------------------
+
+// What group should the cached file belong to
+// (Set to "" will give the default group for the "PHP-user")
+// Please note that the Apache user must be a member of the
+// specified group since otherwise it is impossible for Apache
+// to set the specified group.
+DEFINE("CACHE_FILE_GROUP","wwwadmin");
+
+// What permissions should the cached file have
+// (Set to "" will give the default persmissions for the "PHP-user")
+DEFINE("CACHE_FILE_MOD",0664);
+
+// Decide if we should use the bresenham circle algorithm or the
+// built in Arc(). Bresenham gives better visual apperance of circles
+// but is more CPU intensive and slower then the built in Arc() function
+// in GD. Turned off by default for speed
+DEFINE("USE_BRESENHAM",false);
+
+// Special file name to indicate that we only want to calc
+// the image map in the call to Graph::Stroke() used
+// internally from the GetHTMLCSIM() method.
+DEFINE("_CSIM_SPECIALFILE","_csim_special_");
+
+// HTTP GET argument that is used with image map
+// to indicate to the script to just generate the image
+// and not the full CSIM HTML page.
+DEFINE("_CSIM_DISPLAY","_jpg_csimd");
+
+// Special filename for Graph::Stroke(). If this filename is given
+// then the image will NOT be streamed to browser of file. Instead the
+// Stroke call will return the handler for the created GD image.
+DEFINE("_IMG_HANDLER","__handle");
+
+// Version info
+DEFINE('JPG_VERSION','1.14');
+
+// For internal use only
+DEFINE("_JPG_DEBUG",false);
+DEFINE("_FORCE_IMGTOFILE",false);
+DEFINE("_FORCE_IMGDIR",'/tmp/jpgimg/');
+
+
+//------------------------------------------------------------------------
+// Automatic settings of path for cache and font directory
+// if they have not been previously specified
+//------------------------------------------------------------------------
+if(USE_CACHE) {
+ if (!defined('CACHE_DIR')) {
+ if ( strstr( PHP_OS, 'WIN') ) {
+ if( empty($_SERVER['TEMP']) ) {
+ die('JpGraph Error: No path specified for CACHE_DIR. Please specify a path for that DEFINE in jpgraph.php');
+ }
+ else {
+ DEFINE('CACHE_DIR', $_SERVER['TEMP'] . '/');
+ }
+ } else {
+ DEFINE('CACHE_DIR','/tmp/jpgraph_cache/');
+ }
+ }
+}
+else {
+ DEFINE('CACHE_DIR', '');
+}
+
+if (!defined('TTF_DIR')) {
+ if (strstr( PHP_OS, 'WIN') ) {
+ if( empty($_SERVER['SystemRoot']) ) {
+ die('JpGraph Error: No path specified for TTF_DIR. Please specify a path for that DEFINE in jpgraph.php');
+ }
+ else {
+ DEFINE('TTF_DIR', $_SERVER['SystemRoot'] . '/fonts/');
+ }
+ } else {
+ DEFINE('TTF_DIR','/usr/X11R6/lib/X11/fonts/truetype/');
+ }
+}
+
+//------------------------------------------------------------------
+// Constants which are used as parameters for the method calls
+//------------------------------------------------------------------
+
+// TTF Font families
+DEFINE("FF_COURIER",10);
+DEFINE("FF_VERDANA",11);
+DEFINE("FF_TIMES",12);
+DEFINE("FF_COMIC",14);
+DEFINE("FF_ARIAL",15);
+DEFINE("FF_GEORGIA",16);
+DEFINE("FF_TREBUCHE",17);
+
+// Chinese font
+DEFINE("FF_SIMSUN",30);
+DEFINE("FF_CHINESE",31);
+DEFINE("FF_BIG5",31);
+
+
+// Gnome Vera font
+// Available from http://www.gnome.org/fonts/
+DEFINE("FF_VERA",19);
+DEFINE("FF_VERAMONO",20);
+DEFINE("FF_VERASERIF",21);
+
+
+// Older deprecated fonts
+DEFINE("FF_BOOK",91); // Deprecated fonts from 1.9
+DEFINE("FF_HANDWRT",92); // Deprecated fonts from 1.9
+
+// TTF Font styles
+DEFINE("FS_NORMAL",9001);
+DEFINE("FS_BOLD",9002);
+DEFINE("FS_ITALIC",9003);
+DEFINE("FS_BOLDIT",9004);
+DEFINE("FS_BOLDITALIC",9004);
+
+//Definitions for internal font, new style
+DEFINE("FF_FONT0",1);
+DEFINE("FF_FONT1",2);
+DEFINE("FF_FONT2",4);
+
+//Definitions for internal font, old style
+//Only defined here to be able to generate an error mesage when used
+DEFINE("FONT0",99); // Deprecated from 1.2
+DEFINE("FONT1",98); // Deprecated from 1.2
+DEFINE("FONT1_BOLD",97); // Deprecated from 1.2
+DEFINE("FONT2",96); // Deprecated from 1.2
+DEFINE("FONT2_BOLD",95); // Deprecated from 1.2
+
+// Tick density
+DEFINE("TICKD_DENSE",1);
+DEFINE("TICKD_NORMAL",2);
+DEFINE("TICKD_SPARSE",3);
+DEFINE("TICKD_VERYSPARSE",4);
+
+// Side for ticks and labels.
+DEFINE("SIDE_LEFT",-1);
+DEFINE("SIDE_RIGHT",1);
+DEFINE("SIDE_DOWN",-1);
+DEFINE("SIDE_BOTTOM",-1);
+DEFINE("SIDE_UP",1);
+DEFINE("SIDE_TOP",1);
+
+// Legend type stacked vertical or horizontal
+DEFINE("LEGEND_VERT",0);
+DEFINE("LEGEND_HOR",1);
+
+// Mark types for plot marks
+DEFINE("MARK_SQUARE",1);
+DEFINE("MARK_UTRIANGLE",2);
+DEFINE("MARK_DTRIANGLE",3);
+DEFINE("MARK_DIAMOND",4);
+DEFINE("MARK_CIRCLE",5);
+DEFINE("MARK_FILLEDCIRCLE",6);
+DEFINE("MARK_CROSS",7);
+DEFINE("MARK_STAR",8);
+DEFINE("MARK_X",9);
+DEFINE("MARK_LEFTTRIANGLE",10);
+DEFINE("MARK_RIGHTTRIANGLE",11);
+DEFINE("MARK_FLASH",12);
+DEFINE("MARK_IMG",13);
+DEFINE("MARK_FLAG1",14);
+DEFINE("MARK_FLAG2",15);
+DEFINE("MARK_FLAG3",16);
+DEFINE("MARK_FLAG4",17);
+
+// Builtin images
+DEFINE("MARK_IMG_PUSHPIN",50);
+DEFINE("MARK_IMG_SPUSHPIN",50);
+DEFINE("MARK_IMG_LPUSHPIN",51);
+DEFINE("MARK_IMG_DIAMOND",52);
+DEFINE("MARK_IMG_SQUARE",53);
+DEFINE("MARK_IMG_STAR",54);
+DEFINE("MARK_IMG_BALL",55);
+DEFINE("MARK_IMG_SBALL",55);
+DEFINE("MARK_IMG_MBALL",56);
+DEFINE("MARK_IMG_LBALL",57);
+DEFINE("MARK_IMG_BEVEL",58);
+
+// Styles for gradient color fill
+DEFINE("GRAD_VER",1);
+DEFINE("GRAD_VERT",1);
+DEFINE("GRAD_HOR",2);
+DEFINE("GRAD_MIDHOR",3);
+DEFINE("GRAD_MIDVER",4);
+DEFINE("GRAD_CENTER",5);
+DEFINE("GRAD_WIDE_MIDVER",6);
+DEFINE("GRAD_WIDE_MIDHOR",7);
+DEFINE("GRAD_LEFT_REFLECTION",8);
+DEFINE("GRAD_RIGHT_REFLECTION",9);
+
+// Inline defines
+DEFINE("INLINE_YES",1);
+DEFINE("INLINE_NO",0);
+
+// Format for background images
+DEFINE("BGIMG_FILLPLOT",1);
+DEFINE("BGIMG_FILLFRAME",2);
+DEFINE("BGIMG_COPY",3);
+DEFINE("BGIMG_CENTER",4);
+
+// Depth of objects
+DEFINE("DEPTH_BACK",0);
+DEFINE("DEPTH_FRONT",1);
+
+// Direction
+DEFINE("VERTICAL",1);
+DEFINE("HORIZONTAL",0);
+
+// Constants for types of static bands in plot area
+DEFINE("BAND_RDIAG",1); // Right diagonal lines
+DEFINE("BAND_LDIAG",2); // Left diagonal lines
+DEFINE("BAND_SOLID",3); // Solid one color
+DEFINE("BAND_VLINE",4); // Vertical lines
+DEFINE("BAND_HLINE",5); // Horizontal lines
+DEFINE("BAND_3DPLANE",6); // "3D" Plane
+DEFINE("BAND_HVCROSS",7); // Vertical/Hor crosses
+DEFINE("BAND_DIAGCROSS",8); // Diagonal crosses
+
+// Axis styles for scientific style axis
+DEFINE('AXSTYLE_SIMPLE',1);
+DEFINE('AXSTYLE_BOXIN',2);
+DEFINE('AXSTYLE_BOXOUT',3);
+DEFINE('AXSTYLE_YBOXIN',4);
+DEFINE('AXSTYLE_YBOXOUT',5);
+
+// Style for title backgrounds
+DEFINE('TITLEBKG_STYLE1',1);
+DEFINE('TITLEBKG_STYLE2',2);
+DEFINE('TITLEBKG_STYLE3',3);
+DEFINE('TITLEBKG_FRAME_NONE',0);
+DEFINE('TITLEBKG_FRAME_FULL',1);
+DEFINE('TITLEBKG_FRAME_BOTTOM',2);
+DEFINE('TITLEBKG_FRAME_BEVEL',3);
+DEFINE('TITLEBKG_FILLSTYLE_HSTRIPED',1);
+DEFINE('TITLEBKG_FILLSTYLE_VSTRIPED',2);
+DEFINE('TITLEBKG_FILLSTYLE_SOLID',3);
+
+// Style for background gradient fills
+DEFINE('BGRAD_FRAME',1);
+DEFINE('BGRAD_MARGIN',2);
+DEFINE('BGRAD_PLOT',3);
+
+// Width of tab titles
+DEFINE('TABTITLE_WIDTHFIT',0);
+DEFINE('TABTITLE_WIDTHFULL',-1);
+
+//
+// Get hold of gradient class (In Version 2.x)
+// A client of the library has to manually include this
+//
+include "jpgraph_gradient.php";
+
+//
+// First of all set up a default error handler
+//
+
+//=============================================================
+// The default trivial text error handler.
+//=============================================================
+class JpGraphErrObject {
+ function JpGraphErrObject() {
+ // Empty. Reserved for future use
+ }
+
+ // If aHalt is true then execution can't continue. Typical used for
+ // fatal errors
+ function Raise($aMsg,$aHalt=true) {
+ $aMsg = "<b>JpGraph Error:</b> ".$aMsg;
+ if( $aHalt )
+ die($aMsg);
+ else
+ echo $aMsg."<p>";
+ }
+}
+
+//==============================================================
+// An image based error handler
+//==============================================================
+class JpGraphErrObjectImg {
+
+ function Raise($aMsg,$aHalt=true) {
+ $img_iconerror =
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaV'.
+ 'BMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'.
+ 'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpY'.
+ 'iYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACx'.
+ 'IAAAsSAdLdfvwAAAAHdElNRQfTBgISOCqusfs5AAABLUlEQVR4'.
+ '2tWV3XKCMBBGWfkranCIVClKLd/7P2Q3QsgCxjDTq+6FE2cPH+'.
+ 'xJ0Ogn2lQbsT+Wrs+buAZAV4W5T6Bs0YXBBwpKgEuIu+JERAX6'.
+ 'wM2rHjmDdEITmsQEEmWADgZm6rAjhXsoMGY9B/NZBwJzBvn+e3'.
+ 'wHntCAJdGu9SviwIwoZVDxPB9+Rc0TSEbQr0j3SA1gwdSn6Db0'.
+ '6Tm1KfV6yzWGQO7zdpvyKLKBDmRFjzeB3LYgK7r6A/noDAfjtS'.
+ 'IXaIzbJSv6WgUebTMV4EoRB8a2mQiQjgtF91HdKDKZ1gtFtQjk'.
+ 'YcWaR5OKOhkYt+ZsTFdJRfPAApOpQYJTNHvCRSJR6SJngQadfc'.
+ 'vd69OLMddVOPCGVnmrFD8bVYd3JXfxXPtLR/+mtv59/ALWiiMx'.
+ 'qL72fwAAAABJRU5ErkJggg==' ;
+
+ if( headers_sent() ) {
+ // Special case for headers already sent error. Dont
+ // return an image since it can't be displayed
+ die("<b>JpGraph Error:</b> ".$aMsg);
+ }
+
+ $aMsg = wordwrap($aMsg,55);
+ $lines = substr_count($aMsg,"\n");
+
+ // Create the error icon GD
+ $erricon = Image::CreateFromString(base64_decode($img_iconerror));
+
+ // Create an image that contains the error text.
+ $w=400;
+ $h=100 + 15*max(0,$lines-3);
+
+ /*
+ $h=100;
+ if( strlen($aMsg) > 60 )
+ $h = 150;
+ */
+ $img = new Image($w,$h);
+
+
+ // Drop shadow
+ $img->SetColor("gray");
+ $img->FilledRectangle(5,5,$w-1,$h-1,10);
+ $img->SetColor("gray:0.7");
+ $img->FilledRectangle(5,5,$w-3,$h-3,10);
+
+ // Window background
+ $img->SetColor("lightblue");
+ $img->FilledRectangle(1,1,$w-5,$h-5);
+ $img->CopyCanvasH($img->img,$erricon,5,30,0,0,40,40);
+
+ // Window border
+ $img->SetColor("black");
+ $img->Rectangle(1,1,$w-5,$h-5);
+ $img->Rectangle(0,0,$w-4,$h-4);
+
+ // Window top row
+ $img->SetColor("darkred");
+ for($y=3; $y < 18; $y += 2 )
+ $img->Line(1,$y,$w-6,$y);
+
+ // "White shadow"
+ $img->SetColor("white");
+
+ // Left window edge
+ $img->Line(2,2,2,$h-5);
+ $img->Line(2,2,$w-6,2);
+
+ // "Gray button shadow"
+ $img->SetColor("darkgray");
+
+ // Gray window shadow
+ $img->Line(2,$h-6,$w-5,$h-6);
+ $img->Line(3,$h-7,$w-5,$h-7);
+
+ // Window title
+ $m = floor($w/2-5);
+ $l = 100;
+ $img->SetColor("lightgray:1.3");
+ $img->FilledRectangle($m-$l,2,$m+$l,16);
+
+ // Stroke text
+ $img->SetColor("darkred");
+ $img->SetFont(FF_FONT2,FS_BOLD);
+ $img->StrokeText($m-50,15,"JpGraph Error");
+ $img->SetColor("black");
+ $img->SetFont(FF_FONT1,FS_NORMAL);
+ $txt = new Text($aMsg,52,25);
+ $txt->Align("left","top");
+ $txt->Stroke($img);
+ $img->Headers();
+ $img->Stream();
+ die();
+ }
+}
+
+//
+// A wrapper class that is used to access the specified error object
+// (to hide the global error parameter and avoid having a GLOBAL directive
+// in all methods.
+//
+class JpGraphError {
+ function Install($aErrObject) {
+ GLOBAL $__jpg_err;
+ $__jpg_err = $aErrObject;
+ }
+ function Raise($aMsg,$aHalt=true){
+ GLOBAL $__jpg_err;
+ $tmp = new $__jpg_err;
+ $tmp->Raise($aMsg,$aHalt);
+ }
+}
+
+//
+// ... and install the default error handler
+//
+if( USE_IMAGE_ERROR_HANDLER ) {
+ JpGraphError::Install("JpGraphErrObjectImg");
+}
+else {
+ JpGraphError::Install("JpGraphErrObject");
+}
+
+//
+// Setup PHP error handler
+//
+
+function _phpErrorHandler($errno,$errmsg,$filename, $linenum, $vars) {
+ JpGraphError::Raise('In '.basename($filename).'#'.$linenum."\n".$errmsg);
+}
+
+if( INSTALL_PHP_ERR_HANDLER ) {
+ set_error_handler("_phpErrorHandler");
+ error_reporting(1);
+}
+
+//
+//Check if there were any warnings, perhaps some wrong includes by the
+//user
+//
+if( isset($GLOBALS['php_errormsg']) ) {
+ JpGraphError::Raise("<b>General PHP error:</b><br>".$GLOBALS['php_errormsg']);
+}
+
+
+//
+// Routine to determine if GD1 or GD2 is installed
+//
+function CheckGDVersion() {
+ $GDfuncList = get_extension_funcs('gd');
+ if( !$GDfuncList ) return 0 ;
+ else {
+ if( in_array('imagegd2',$GDfuncList) )
+ return 2;
+ else
+ return 1;
+ }
+}
+
+//
+// Check what version of the GD library is installed.
+//
+if( USE_LIBRARY_GD2 === 'auto' ) {
+ $gdversion = CheckGDVersion();
+ if( $gdversion == 2 ) {
+ $GLOBALS['gd2'] = true;
+ $GLOBALS['copyfunc'] = 'imagecopyresampled';
+ }
+ elseif( $gdversion == 1 ) {
+ $GLOBALS['gd2'] = false;
+ $GLOBALS['copyfunc'] = 'imagecopyresized';
+ }
+ else {
+ JpGraphError::Raise(" Your PHP installation does not seem to
+ have the required GD library.
+ Please see the PHP documentation on how to install and enable the GD library.");
+ }
+}
+else {
+ $GLOBALS['gd2'] = USE_LIBRARY_GD2;
+ $GLOBALS['copyfunc'] = USE_LIBRARY_GD2 ? 'imagecopyresampled' : 'imagecopyresized';
+}
+
+// Useful mathematical function
+function sign($a) {return $a >= 0 ? 1 : -1;}
+
+// Utility function to generate an image name based on the filename we
+// are running from and assuming we use auto detection of graphic format
+// (top level), i.e it is safe to call this function
+// from a script that uses JpGraph
+function GenImgName() {
+ global $HTTP_SERVER_VARS;
+ global $_SERVER;
+
+ // Determine what format we should use when we save the images
+ $supported = imagetypes();
+ if( $supported & IMG_PNG ) $img_format="png";
+ elseif( $supported & IMG_GIF ) $img_format="gif";
+ elseif( $supported & IMG_JPG ) $img_format="jpeg";
+
+ if( !isset($HTTP_SERVER_VARS['PHP_SELF']) )
+ JpGraphError::Raise(" Can't access PHP_SELF, PHP global variable. You can't run PHP from command line if you want to use the 'auto' naming of cache or image files.");
+ $fname = basename($HTTP_SERVER_VARS['PHP_SELF']);
+ $q = @$_SERVER['QUERY_STRING'];
+ if( !empty($q) ) {
+ $fname .= '?'.preg_replace("/\W/", "_", $q).'.'.$img_format;
+ }
+ else {
+ $fname = substr($fname,0,strlen($fname)-4).'.'.$img_format;
+ }
+ return $fname;
+}
+
+class LanguageConv {
+ var $g2312 = null ;
+
+ function Convert($aTxt,$aFF) {
+ if( LANGUAGE_CYRILLIC ) {
+ if( CYRILLIC_FROM_WINDOWS ) {
+ $aTxt = convert_cyr_string($aTxt, "w", "k");
+ }
+ $isostring = convert_cyr_string($aTxt, "k", "i");
+ $unistring = LanguageConv::iso2uni($isostring);
+ return $unistring;
+ }
+ elseif( $aFF === FF_SIMSUN ) {
+ // Do Chinese conversion
+ if( $this->g2312 == null ) {
+ include_once 'jpgraph_gb2312.php' ;
+ $this->g2312 = new GB2312toUTF8();
+ }
+ return $this->g2312->gb2utf8($aTxt);
+ }
+ elseif( $aFF === FF_CHINESE ) {
+ if( !function_exists('iconv') ) {
+ JpGraphError::Raise('Usage of FF_CHINESE (FF_BIG5) font family requires that your PHP setup has the iconv() function. By default this is not compiled into PHP (needs the "--width-iconv" when configured).');
+ }
+ return iconv('BIG-5','UTF8',$aTxt);
+ }
+ else
+ return $aTxt;
+ }
+
+ // Translate iso encoding to unicode
+ function iso2uni ($isoline){
+ for ($i=0; $i < strlen($isoline); $i++){
+ $thischar=substr($isoline,$i,1);
+ $charcode=ord($thischar);
+ $uniline.=($charcode>175) ? "&#" . (1040+($charcode-176)). ";" : $thischar;
+ }
+ return $uniline;
+ }
+}
+
+//===================================================
+// CLASS JpgTimer
+// Description: General timing utility class to handle
+// time measurement of generating graphs. Multiple
+// timers can be started.
+//===================================================
+class JpgTimer {
+ var $start;
+ var $idx;
+//---------------
+// CONSTRUCTOR
+ function JpgTimer() {
+ $this->idx=0;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Push a new timer start on stack
+ function Push() {
+ list($ms,$s)=explode(" ",microtime());
+ $this->start[$this->idx++]=floor($ms*1000) + 1000*$s;
+ }
+
+ // Pop the latest timer start and return the diff with the
+ // current time
+ function Pop() {
+ assert($this->idx>0);
+ list($ms,$s)=explode(" ",microtime());
+ $etime=floor($ms*1000) + (1000*$s);
+ $this->idx--;
+ return $etime-$this->start[$this->idx];
+ }
+} // Class
+
+$gJpgBrandTiming = BRAND_TIMING;
+//===================================================
+// CLASS DateLocale
+// Description: Hold localized text used in dates
+//===================================================
+class DateLocale {
+
+ var $iLocale = 'C'; // environmental locale be used by default
+
+ var $iDayAbb = null;
+ var $iShortDay = null;
+ var $iShortMonth = null;
+ var $iMonthName = null;
+
+//---------------
+// CONSTRUCTOR
+ function DateLocale() {
+ settype($this->iDayAbb, 'array');
+ settype($this->iShortDay, 'array');
+ settype($this->iShortMonth, 'array');
+ settype($this->iMonthName, 'array');
+
+
+ $this->Set('C');
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Set($aLocale) {
+ if ( in_array($aLocale, array_keys($this->iDayAbb)) ){
+ $this->iLocale = $aLocale;
+ return TRUE; // already cached nothing else to do!
+ }
+
+ $pLocale = setlocale(LC_TIME, 0); // get current locale for LC_TIME
+ $res = @setlocale(LC_TIME, $aLocale);
+ if ( ! $res ){
+ JpGraphError::Raise("You are trying to use the locale ($aLocale) which your PHP installation does not support. Hint: Use '' to indicate the default locale for this geographic region.");
+ return FALSE;
+ }
+
+ $this->iLocale = $aLocale;
+
+ for ( $i = 0, $ofs = 0 - strftime('%w'); $i < 7; $i++, $ofs++ ){
+ $day = strftime('%a', strtotime("$ofs day"));
+ $day{0} = strtoupper($day{0});
+ $this->iDayAbb[$aLocale][]= $day{0};
+ $this->iShortDay[$aLocale][]= $day;
+ }
+
+ for($i=1; $i<=12; ++$i) {
+ list($short ,$full) = explode('|', strftime("%b|%B",strtotime("2001-$i-01")));
+ $this->iShortMonth[$aLocale][] = ucfirst($short);
+ $this->iMonthName [$aLocale][] = ucfirst($full);
+ }
+
+ setlocale(LC_TIME, $pLocale);
+
+ return TRUE;
+ }
+
+
+ function GetDayAbb() {
+ return $this->iDayAbb[$this->iLocale];
+ }
+
+ function GetShortDay() {
+ return $this->iShortDay[$this->iLocale];
+ }
+
+ function GetShortMonth() {
+ return $this->iShortMonth[$this->iLocale];
+ }
+
+ function GetShortMonthName($aNbr) {
+ return $this->iShortMonth[$this->iLocale][$aNbr];
+ }
+
+ function GetLongMonthName($aNbr) {
+ return $this->iMonthName[$this->iLocale][$aNbr];
+ }
+
+ function GetMonth() {
+ return $this->iMonthName[$this->iLocale];
+ }
+}
+
+$gDateLocale = new DateLocale();
+$gJpgDateLocale = new DateLocale();
+
+
+//===================================================
+// CLASS FuncGenerator
+// Description: Utility class to help generate data for function plots.
+// The class supports both parametric and regular functions.
+//===================================================
+class FuncGenerator {
+ var $iFunc='',$iXFunc='',$iMin,$iMax,$iStepSize;
+
+ function FuncGenerator($aFunc,$aXFunc='') {
+ $this->iFunc = $aFunc;
+ $this->iXFunc = $aXFunc;
+ }
+
+ function E($aXMin,$aXMax,$aSteps=50) {
+ $this->iMin = $aXMin;
+ $this->iMax = $aXMax;
+ $this->iStepSize = ($aXMax-$aXMin)/$aSteps;
+
+ if( $this->iXFunc != '' )
+ $t = 'for($i='.$aXMin.'; $i<='.$aXMax.'; $i += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]='.$this->iXFunc.';}';
+ elseif( $this->iFunc != '' )
+ $t = 'for($x='.$aXMin.'; $x<='.$aXMax.'; $x += '.$this->iStepSize.') {$ya[]='.$this->iFunc.';$xa[]=$x;} $x='.$aXMax.';$ya[]='.$this->iFunc.';$xa[]=$x;';
+ else
+ JpGraphError::Raise('FuncGenerator : No function specified. ');
+
+ @eval($t);
+
+ // If there is an error in the function specifcation this is the only
+ // way we can discover that.
+ if( empty($xa) || empty($ya) )
+ JpGraphError::Raise('FuncGenerator : Syntax error in function specification ');
+
+ return array($xa,$ya);
+ }
+}
+
+//=======================================================
+// CLASS Footer
+// Description: Encapsulates the footer line in the Graph
+//=======================================================
+class Footer {
+ var $left,$center,$right;
+ var $iLeftMargin = 3;
+ var $iRightMargin = 3;
+ var $iBottomMargin = 3;
+
+ function Footer() {
+ $this->left = new Text();
+ $this->left->ParagraphAlign('left');
+ $this->center = new Text();
+ $this->center->ParagraphAlign('center');
+ $this->right = new Text();
+ $this->right->ParagraphAlign('right');
+ }
+
+ function Stroke($aImg) {
+ $y = $aImg->height - $this->iBottomMargin;
+ $x = $this->iLeftMargin;
+ $this->left->Align('left','bottom');
+ $this->left->Stroke($aImg,$x,$y);
+
+ $x = ($aImg->width - $this->iLeftMargin - $this->iRightMargin)/2;
+ $this->center->Align('center','bottom');
+ $this->center->Stroke($aImg,$x,$y);
+
+ $x = $aImg->width - $this->iRightMargin;
+ $this->right->Align('right','bottom');
+ $this->right->Stroke($aImg,$x,$y);
+ }
+}
+
+
+//===================================================
+// CLASS Graph
+// Description: Main class to handle graphs
+//===================================================
+class Graph {
+ var $cache=null; // Cache object (singleton)
+ var $img=null; // Img object (singleton)
+ var $plots=array(); // Array of all plot object in the graph (for Y 1 axis)
+ var $y2plots=array();// Array of all plot object in the graph (for Y 2 axis)
+ var $xscale=null; // X Scale object (could be instance of LinearScale or LogScale
+ var $yscale=null,$y2scale=null;
+ var $iIcons = array(); // Array of Icons to add to
+ var $cache_name; // File name to be used for the current graph in the cache directory
+ var $xgrid=null; // X Grid object (linear or logarithmic)
+ var $ygrid=null,$y2grid=null; //dito for Y
+ var $doframe=true,$frame_color=array(0,0,0), $frame_weight=1; // Frame around graph
+ var $boxed=false, $box_color=array(0,0,0), $box_weight=1; // Box around plot area
+ var $doshadow=false,$shadow_width=4,$shadow_color=array(102,102,102); // Shadow for graph
+ var $xaxis=null; // X-axis (instane of Axis class)
+ var $yaxis=null, $y2axis=null; // Y axis (instance of Axis class)
+ var $margin_color=array(200,200,200); // Margin color of graph
+ var $plotarea_color=array(255,255,255); // Plot area color
+ var $title,$subtitle,$subsubtitle; // Title and subtitle(s) text object
+ var $axtype="linlin"; // Type of axis
+ var $xtick_factor; // Factot to determine the maximum number of ticks depending on the plot with
+ var $texts=null, $y2texts=null; // Text object to ge shown in the graph
+ var $lines=null, $y2lines=null;
+ var $bands=null, $y2bands=null;
+ var $text_scale_off=0; // Text scale offset in world coordinates
+ var $background_image="",$background_image_type=-1,$background_image_format="png";
+ var $background_image_bright=0,$background_image_contr=0,$background_image_sat=0;
+ var $image_bright=0, $image_contr=0, $image_sat=0;
+ var $inline;
+ var $showcsim=0,$csimcolor="red"; //debug stuff, draw the csim boundaris on the image if <>0
+ var $grid_depth=DEPTH_BACK; // Draw grid under all plots as default
+ var $iAxisStyle = AXSTYLE_SIMPLE;
+ var $iCSIMdisplay=false,$iHasStroked = false;
+ var $footer;
+ var $csimcachename = '', $csimcachetimeout = 0;
+ var $iDoClipping = false;
+ var $y2orderback=true;
+ var $tabtitle;
+ var $bkg_gradtype=-1,$bkg_gradstyle=BGRAD_MARGIN;
+ var $bkg_gradfrom='navy', $bkg_gradto='silver';
+ var $titlebackground = false;
+ var $titlebackground_color = 'lightblue',
+ $titlebackground_style = 1,
+ $titlebackground_framecolor = 'blue',
+ $titlebackground_framestyle = 2,
+ $titlebackground_frameweight = 1,
+ $titlebackground_bevelheight = 3 ;
+ var $titlebkg_fillstyle=TITLEBKG_FILLSTYLE_SOLID;
+ var $titlebkg_scolor1='black',$titlebkg_scolor2='white';
+ var $framebevel = false, $framebeveldepth = 2 ;
+ var $framebevelborder = false, $framebevelbordercolor='black';
+ var $framebevelcolor1='white@0.4', $framebevelcolor2='black@0.4';
+ var $background_image_mix=100;
+ var $background_cflag = '';
+ var $background_cflag_type = BGIMG_FILLPLOT;
+ var $background_cflag_mix = 100;
+ var $iImgTrans=false,
+ $iImgTransHorizon = 100,$iImgTransSkewDist=150,
+ $iImgTransDirection = 1, $iImgTransMinSize = true,
+ $iImgTransFillColor='white',$iImgTransHighQ=false,
+ $iImgTransBorder=false,$iImgTransHorizonPos=0.5;
+
+//---------------
+// CONSTRUCTOR
+
+ // aWIdth Width in pixels of image
+ // aHeight Height in pixels of image
+ // aCachedName Name for image file in cache directory
+ // aTimeOut Timeout in minutes for image in cache
+ // aInline If true the image is streamed back in the call to Stroke()
+ // If false the image is just created in the cache
+ function Graph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
+ GLOBAL $gJpgBrandTiming;
+ // If timing is used create a new timing object
+ if( $gJpgBrandTiming ) {
+ global $tim;
+ $tim = new JpgTimer();
+ $tim->Push();
+ }
+
+ // Automatically generate the image file name based on the name of the script that
+ // generates the graph
+ if( $aCachedName=="auto" )
+ $aCachedName=GenImgName();
+
+ // Should the image be streamed back to the browser or only to the cache?
+ $this->inline=$aInline;
+
+ $this->img = new RotImage($aWidth,$aHeight);
+
+ $this->cache = new ImgStreamCache($this->img);
+ $this->cache->SetTimeOut($aTimeOut);
+
+ $this->title = new Text();
+ $this->title->ParagraphAlign('center');
+ $this->title->SetFont(FF_FONT2,FS_BOLD);
+ $this->title->SetMargin(3);
+ $this->title->SetAlign('center');
+
+ $this->subtitle = new Text();
+ $this->subtitle->ParagraphAlign('center');
+ $this->subtitle->SetMargin(2);
+ $this->subtitle->SetAlign('center');
+
+ $this->subsubtitle = new Text();
+ $this->subsubtitle->ParagraphAlign('center');
+ $this->subsubtitle->SetMargin(2);
+ $this->subsubtitle->SetAlign('center');
+
+ $this->legend = new Legend();
+ $this->footer = new Footer();
+
+ // If the cached version exist just read it directly from the
+ // cache, stream it back to browser and exit
+ if( $aCachedName!="" && READ_CACHE && $aInline )
+ if( $this->cache->GetAndStream($aCachedName) ) {
+ exit();
+ }
+
+ $this->cache_name = $aCachedName;
+ $this->SetTickDensity(); // Normal density
+
+ $this->tabtitle = new GraphTabTitle();
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Enable final image perspective transformation
+ function Set3DPerspective($aDir=1,$aHorizon=100,$aSkewDist=120,$aQuality=false,$aFillColor='#FFFFFF',$aBorder=false,$aMinSize=true,$aHorizonPos=0.5) {
+ $this->iImgTrans = true;
+ $this->iImgTransHorizon = $aHorizon;
+ $this->iImgTransSkewDist= $aSkewDist;
+ $this->iImgTransDirection = $aDir;
+ $this->iImgTransMinSize = $aMinSize;
+ $this->iImgTransFillColor=$aFillColor;
+ $this->iImgTransHighQ=$aQuality;
+ $this->iImgTransBorder=$aBorder;
+ $this->iImgTransHorizonPos=$aHorizonPos;
+ }
+
+
+
+ // Should the grid be in front or back of the plot?
+ function SetGridDepth($aDepth) {
+ $this->grid_depth=$aDepth;
+ }
+
+ // Specify graph angle 0-360 degrees.
+ function SetAngle($aAngle) {
+ $this->img->SetAngle($aAngle);
+ }
+
+ function SetAlphaBlending($aFlg=true) {
+ $this->img->SetAlphaBlending($aFlg);
+ }
+
+ // Shortcut to image margin
+ function SetMargin($lm,$rm,$tm,$bm) {
+ $this->img->SetMargin($lm,$rm,$tm,$bm);
+ }
+
+ function SetY2OrderBack($aBack=true) {
+ $this->y2orderback = $aBack;
+ }
+
+ // Rotate the graph 90 degrees and set the margin
+ // when we have done a 90 degree rotation
+ function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
+ $lm = $lm ==0 ? floor(0.2 * $this->img->width) : $lm ;
+ $rm = $rm ==0 ? floor(0.1 * $this->img->width) : $rm ;
+ $tm = $tm ==0 ? floor(0.2 * $this->img->height) : $tm ;
+ $bm = $bm ==0 ? floor(0.1 * $this->img->height) : $bm ;
+
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->img->SetMargin($tm-$adj,$bm-$adj,$rm+$adj,$lm+$adj);
+ $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
+ $this->SetAngle(90);
+ if( empty($this->yaxis) || empty($this->xaxis) ) {
+ JpgraphError::Raise('You must specify what scale to use with a call to Graph::SetScale()');
+ }
+ $this->xaxis->SetLabelAlign('right','center');
+ $this->yaxis->SetLabelAlign('center','bottom');
+ }
+
+ function SetClipping($aFlg=true) {
+ $this->iDoClipping = $aFlg ;
+ }
+
+ // Add a plot object to the graph
+ function Add(&$aPlot) {
+ if( $aPlot == null )
+ JpGraphError::Raise("Graph::Add() You tried to add a null plot to the graph.");
+ if( is_array($aPlot) && count($aPlot) > 0 )
+ $cl = get_class($aPlot[0]);
+ else
+ $cl = get_class($aPlot);
+
+ if( $cl == 'text' )
+ $this->AddText($aPlot);
+ elseif( $cl == 'plotline' )
+ $this->AddLine($aPlot);
+ elseif( $cl == 'plotband' )
+ $this->AddBand($aPlot);
+ elseif( $cl == 'iconplot' )
+ $this->AddIcon($aPlot);
+ else
+ $this->plots[] = &$aPlot;
+ }
+
+ function AddIcon(&$aIcon) {
+ if( is_array($aIcon) ) {
+ for($i=0; $i < count($aIcon); ++$i )
+ $this->iIcons[]=&$aIcon[$i];
+ }
+ else {
+ $this->iIcons[] = $aIcon ;
+ }
+ }
+
+ // Add plot to second Y-scale
+ function AddY2(&$aPlot) {
+ if( $aPlot == null )
+ JpGraphError::Raise("Graph::AddY2() You tried to add a null plot to the graph.");
+
+ if( is_array($aPlot) && count($aPlot) > 0 )
+ $cl = get_class($aPlot[0]);
+ else
+ $cl = get_class($aPlot);
+
+ if( $cl == 'text' )
+ $this->AddText($aPlot,true);
+ elseif( $cl == 'plotline' )
+ $this->AddLine($aPlot,true);
+ elseif( $cl == 'plotband' )
+ $this->AddBand($aPlot,true);
+ else
+ $this->y2plots[] = &$aPlot;
+ }
+
+ // Add text object to the graph
+ function AddText(&$aTxt,$aToY2=false) {
+ if( $aTxt == null )
+ JpGraphError::Raise("Graph::AddText() You tried to add a null text to the graph.");
+ if( $aToY2 ) {
+ if( is_array($aTxt) ) {
+ for($i=0; $i < count($aTxt); ++$i )
+ $this->y2texts[]=&$aTxt[$i];
+ }
+ else
+ $this->y2texts[] = &$aTxt;
+ }
+ else {
+ if( is_array($aTxt) ) {
+ for($i=0; $i < count($aTxt); ++$i )
+ $this->texts[]=&$aTxt[$i];
+ }
+ else
+ $this->texts[] = &$aTxt;
+ }
+ }
+
+ // Add a line object (class PlotLine) to the graph
+ function AddLine(&$aLine,$aToY2=false) {
+ if( $aLine == null )
+ JpGraphError::Raise("Graph::AddLine() You tried to add a null line to the graph.");
+
+ if( $aToY2 ) {
+ if( is_array($aLine) ) {
+ for($i=0; $i < count($aLine); ++$i )
+ $this->y2lines[]=&$aLine[$i];
+ }
+ else
+ $this->y2lines[] = &$aLine;
+ }
+ else {
+ if( is_array($aLine) ) {
+ for($i=0; $i<count($aLine); ++$i )
+ $this->lines[]=&$aLine[$i];
+ }
+ else
+ $this->lines[] = &$aLine;
+ }
+ }
+
+ // Add vertical or horizontal band
+ function AddBand(&$aBand,$aToY2=false) {
+ if( $aBand == null )
+ JpGraphError::Raise(" Graph::AddBand() You tried to add a null band to the graph.");
+
+ if( $aToY2 ) {
+ if( is_array($aBand) ) {
+ for($i=0; $i < count($aBand); ++$i )
+ $this->y2bands[] = &$aBand[$i];
+ }
+ else
+ $this->y2bands[] = &$aBand;
+ }
+ else {
+ if( is_array($aBand) ) {
+ for($i=0; $i < count($aBand); ++$i )
+ $this->bands[] = &$aBand[$i];
+ }
+ else
+ $this->bands[] = &$aBand;
+ }
+ }
+
+ function SetBackgroundGradient($aFrom='navy',$aTo='silver',$aGradType=GRAD_HOR,$aStyle=BGRAD_FRAME) {
+ $this->bkg_gradtype=$aGradType;
+ $this->bkg_gradstyle=$aStyle;
+ $this->bkg_gradfrom = $aFrom;
+ $this->bkg_gradto = $aTo;
+ }
+
+ // Set a country flag in the background
+ function SetBackgroundCFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) {
+ $this->background_cflag = $aName;
+ $this->background_cflag_type = $aBgType;
+ $this->background_cflag_mix = $aMix;
+ }
+
+ // Alias for the above method
+ function SetBackgroundCountryFlag($aName,$aBgType=BGIMG_FILLPLOT,$aMix=100) {
+ $this->background_cflag = $aName;
+ $this->background_cflag_type = $aBgType;
+ $this->background_cflag_mix = $aMix;
+ }
+
+
+ // Specify a background image
+ function SetBackgroundImage($aFileName,$aBgType=BGIMG_FILLPLOT,$aImgFormat="auto") {
+
+ if( $GLOBALS['gd2'] && !USE_TRUECOLOR ) {
+ JpGraphError::Raise("You are using GD 2.x and are trying to use a background images on a non truecolor image. To use background images with GD 2.x you <b>must</b> enable truecolor by setting the USE_TRUECOLOR constant to TRUE. Due to a bug in GD 2.0.1 using any truetype fonts with truecolor images will result in very poor quality fonts.");
+ }
+
+ // Get extension to determine image type
+ if( $aImgFormat == "auto" ) {
+ $e = explode('.',$aFileName);
+ if( !$e ) {
+ JpGraphError::Raise('Incorrect file name for Graph::SetBackgroundImage() : '.$aFileName.' Must have a valid image extension (jpg,gif,png) when using autodetection of image type');
+ }
+
+ $valid_formats = array('png', 'jpg', 'gif');
+ $aImgFormat = strtolower($e[count($e)-1]);
+ if ($aImgFormat == 'jpeg') {
+ $aImgFormat = 'jpg';
+ }
+ elseif (!in_array($aImgFormat, $valid_formats) ) {
+ JpGraphError::Raise('Unknown file extension ($aImgFormat) in Graph::SetBackgroundImage() for filename: '.$aFileName);
+ }
+ }
+
+ $this->background_image = $aFileName;
+ $this->background_image_type=$aBgType;
+ $this->background_image_format=$aImgFormat;
+ }
+
+ function SetBackgroundImageMix($aMix) {
+ $this->background_image_mix = $aMix ;
+ }
+
+ // Adjust brightness and constrast for background image
+ function AdjBackgroundImage($aBright,$aContr=0,$aSat=0) {
+ $this->background_image_bright=$aBright;
+ $this->background_image_contr=$aContr;
+ $this->background_image_sat=$aSat;
+ }
+
+ // Adjust brightness and constrast for image
+ function AdjImage($aBright,$aContr=0,$aSat=0) {
+ $this->image_bright=$aBright;
+ $this->image_contr=$aContr;
+ $this->image_sat=$aSat;
+ }
+
+ // Specify axis style (boxed or single)
+ function SetAxisStyle($aStyle) {
+ $this->iAxisStyle = $aStyle ;
+ }
+
+ // Set a frame around the plot area
+ function SetBox($aDrawPlotFrame=true,$aPlotFrameColor=array(0,0,0),$aPlotFrameWeight=1) {
+ $this->boxed = $aDrawPlotFrame;
+ $this->box_weight = $aPlotFrameWeight;
+ $this->box_color = $aPlotFrameColor;
+ }
+
+ // Specify color for the plotarea (not the margins)
+ function SetColor($aColor) {
+ $this->plotarea_color=$aColor;
+ }
+
+ // Specify color for the margins (all areas outside the plotarea)
+ function SetMarginColor($aColor) {
+ $this->margin_color=$aColor;
+ }
+
+ // Set a frame around the entire image
+ function SetFrame($aDrawImgFrame=true,$aImgFrameColor=array(0,0,0),$aImgFrameWeight=1) {
+ $this->doframe = $aDrawImgFrame;
+ $this->frame_color = $aImgFrameColor;
+ $this->frame_weight = $aImgFrameWeight;
+ }
+
+ function SetFrameBevel($aDepth=3,$aBorder=false,$aBorderColor='black',$aColor1='white@0.4',$aColor2='darkgray@0.4',$aFlg=true) {
+ $this->framebevel = $aFlg ;
+ $this->framebeveldepth = $aDepth ;
+ $this->framebevelborder = $aBorder ;
+ $this->framebevelbordercolor = $aBorderColor ;
+ $this->framebevelcolor1 = $aColor1 ;
+ $this->framebevelcolor2 = $aColor2 ;
+
+ $this->doshadow = false ;
+ }
+
+ // Set the shadow around the whole image
+ function SetShadow($aShowShadow=true,$aShadowWidth=5,$aShadowColor=array(102,102,102)) {
+ $this->doshadow = $aShowShadow;
+ $this->shadow_color = $aShadowColor;
+ $this->shadow_width = $aShadowWidth;
+ $this->footer->iBottomMargin += $aShadowWidth;
+ $this->footer->iRightMargin += $aShadowWidth;
+ }
+
+ // Specify x,y scale. Note that if you manually specify the scale
+ // you must also specify the tick distance with a call to Ticks::Set()
+ function SetScale($aAxisType,$aYMin=1,$aYMax=1,$aXMin=1,$aXMax=1) {
+ $this->axtype = $aAxisType;
+
+ if( $aYMax < $aYMin || $aXMax < $aXMin )
+ JpGraphError::Raise('Graph::SetScale(): Specified Max value must be larger than the specified Min value.');
+
+ $yt=substr($aAxisType,-3,3);
+ if( $yt=="lin" )
+ $this->yscale = new LinearScale($aYMin,$aYMax);
+ elseif( $yt == "int" ) {
+ $this->yscale = new LinearScale($aYMin,$aYMax);
+ $this->yscale->SetIntScale();
+ }
+ elseif( $yt=="log" )
+ $this->yscale = new LogScale($aYMin,$aYMax);
+ else
+ JpGraphError::Raise("Unknown scale specification for Y-scale. ($aAxisType)");
+
+ $xt=substr($aAxisType,0,3);
+ if( $xt == "lin" || $xt == "tex" ) {
+ $this->xscale = new LinearScale($aXMin,$aXMax,"x");
+ $this->xscale->textscale = ($xt == "tex");
+ }
+ elseif( $xt == "int" ) {
+ $this->xscale = new LinearScale($aXMin,$aXMax,"x");
+ $this->xscale->SetIntScale();
+ }
+ elseif( $xt == "log" )
+ $this->xscale = new LogScale($aXMin,$aXMax,"x");
+ else
+ JpGraphError::Raise(" Unknown scale specification for X-scale. ($aAxisType)");
+
+ $this->xscale->Init($this->img);
+ $this->yscale->Init($this->img);
+
+ $this->xaxis = new Axis($this->img,$this->xscale);
+ $this->yaxis = new Axis($this->img,$this->yscale);
+ $this->xgrid = new Grid($this->xaxis);
+ $this->ygrid = new Grid($this->yaxis);
+ $this->ygrid->Show();
+ }
+
+ // Specify secondary Y scale
+ function SetY2Scale($aAxisType="lin",$aY2Min=1,$aY2Max=1) {
+ if( $aAxisType=="lin" )
+ $this->y2scale = new LinearScale($aY2Min,$aY2Max);
+ elseif( $aAxisType == "int" ) {
+ $this->y2scale = new LinearScale($aY2Min,$aY2Max);
+ $this->y2scale->SetIntScale();
+ }
+ elseif( $aAxisType=="log" ) {
+ $this->y2scale = new LogScale($aY2Min,$aY2Max);
+ }
+ else JpGraphError::Raise("JpGraph: Unsupported Y2 axis type: $axtype<br>");
+
+ $this->y2scale->Init($this->img);
+ $this->y2axis = new Axis($this->img,$this->y2scale);
+ $this->y2axis->scale->ticks->SetDirection(SIDE_LEFT);
+ $this->y2axis->SetLabelSide(SIDE_RIGHT);
+
+ // Deafult position is the max x-value
+ $this->y2grid = new Grid($this->y2axis);
+ }
+
+ // Specify density of ticks when autoscaling 'normal', 'dense', 'sparse', 'verysparse'
+ // The dividing factor have been determined heuristically according to my aesthetic
+ // sense (or lack off) y.m.m.v !
+ function SetTickDensity($aYDensity=TICKD_NORMAL,$aXDensity=TICKD_NORMAL) {
+ $this->xtick_factor=30;
+ $this->ytick_factor=25;
+ switch( $aYDensity ) {
+ case TICKD_DENSE:
+ $this->ytick_factor=12;
+ break;
+ case TICKD_NORMAL:
+ $this->ytick_factor=25;
+ break;
+ case TICKD_SPARSE:
+ $this->ytick_factor=40;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->ytick_factor=100;
+ break;
+ default:
+ JpGraphError::Raise("JpGraph: Unsupported Tick density: $densy");
+ }
+ switch( $aXDensity ) {
+ case TICKD_DENSE:
+ $this->xtick_factor=15;
+ break;
+ case TICKD_NORMAL:
+ $this->xtick_factor=30;
+ break;
+ case TICKD_SPARSE:
+ $this->xtick_factor=45;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->xtick_factor=60;
+ break;
+ default:
+ JpGraphError::Raise("JpGraph: Unsupported Tick density: $densx");
+ }
+ }
+
+
+ // Get a string of all image map areas
+ function GetCSIMareas() {
+ if( !$this->iHasStroked )
+ $this->Stroke(_CSIM_SPECIALFILE);
+ $csim=$this->legend->GetCSIMAreas();
+
+ $n = count($this->plots);
+ for( $i=0; $i<$n; ++$i )
+ $csim .= $this->plots[$i]->GetCSIMareas();
+
+ $n = count($this->y2plots);
+ for( $i=0; $i<$n; ++$i )
+ $csim .= $this->y2plots[$i]->GetCSIMareas();
+
+ return $csim;
+ }
+
+ // Get a complete <MAP>..</MAP> tag for the final image map
+ function GetHTMLImageMap($aMapName) {
+ $im = "<MAP NAME=\"$aMapName\">\n";
+ $im .= $this->GetCSIMareas();
+ $im .= "</MAP>";
+ return $im;
+ }
+
+ function CheckCSIMCache($aCacheName,$aTimeOut=60) {
+ global $HTTP_SERVER_VARS;
+
+ if( $aCacheName=='auto' )
+ $aCacheName=basename($HTTP_SERVER_VARS['PHP_SELF']);
+
+ $this->csimcachename = CSIMCACHE_DIR.$aCacheName;
+ $this->csimcachetimeout = $aTimeOut;
+
+ // First determine if we need to check for a cached version
+ // This differs from the standard cache in the sense that the
+ // image and CSIM map HTML file is written relative to the directory
+ // the script executes in and not the specified cache directory.
+ // The reason for this is that the cache directory is not necessarily
+ // accessible from the HTTP server.
+ if( $this->csimcachename != '' ) {
+ $dir = dirname($this->csimcachename);
+ $base = basename($this->csimcachename);
+ $base = strtok($base,'.');
+ $suffix = strtok('.');
+ $basecsim = $dir.'/'.$base.'_csim_.html';
+ $baseimg = $dir.'/'.$base.'.'.$this->img->img_format;
+
+ $timedout=false;
+
+ // Does it exist at all ?
+
+ if( file_exists($basecsim) && file_exists($baseimg) ) {
+ // Check that it hasn't timed out
+ $diff=time()-filemtime($basecsim);
+ if( $this->csimcachetimeout>0 && ($diff > $this->csimcachetimeout*60) ) {
+ $timedout=true;
+ @unlink($basecsim);
+ @unlink($baseimg);
+ }
+ else {
+ if ($fh = @fopen($basecsim, "r")) {
+ fpassthru($fh);
+ exit();
+ }
+ else
+ JpGraphError::Raise(" Can't open cached CSIM \"$basecsim\" for reading.");
+ }
+ }
+ }
+ return false;
+ }
+
+ function StrokeCSIM($aScriptName='',$aCSIMName='',$aBorder=0) {
+ GLOBAL $HTTP_GET_VARS;
+
+ if( $aCSIMName=='' ) {
+ // create a random map name
+ srand ((double) microtime() * 1000000);
+ $r = rand(0,100000);
+ $aCSIMName='__mapname'.$r.'__';
+ }
+ if( empty($HTTP_GET_VARS[_CSIM_DISPLAY]) ) {
+ // First determine if we need to check for a cached version
+ // This differs from the standard cache in the sense that the
+ // image and CSIM map HTML file is written relative to the directory
+ // the script executes in and not the specified cache directory.
+ // The reason for this is that the cache directory is not necessarily
+ // accessible from the HTTP server.
+ if( $this->csimcachename != '' ) {
+ $dir = dirname($this->csimcachename);
+ $base = basename($this->csimcachename);
+ $base = strtok($base,'.');
+ $suffix = strtok('.');
+ $basecsim = $dir.'/'.$base.'_csim_.html';
+ $baseimg = $base.'.'.$this->img->img_format;
+
+ // Check that apache can write to directory specified
+
+ if( file_exists($dir) && !is_writeable($dir) ) {
+ JpgraphError::Raise('Apache/PHP does not have permission to write to the CSIM cache directory ('.$dir.'). Check permissions.');
+ }
+
+ // Make sure directory exists
+ $this->cache->MakeDirs($dir);
+
+ // Write the image file
+ $this->Stroke(CSIMCACHE_DIR.$baseimg);
+
+ // Construct wrapper HTML and write to file and send it back to browser
+ $htmlwrap = $this->GetHTMLImageMap($aCSIMName)."\n".
+ '<img src="'.CSIMCACHE_HTTP_DIR.$baseimg.'" ISMAP USEMAP="#'.$aCSIMName.'" border='.$aBorder.'>'."\n";
+ if($fh = @fopen($basecsim,'w') ) {
+ fwrite($fh,$htmlwrap);
+ fclose($fh);
+ echo $htmlwrap;
+ }
+ else
+ JpGraphError::Raise(" Can't write CSIM \"$basecsim\" for writing. Check free space and permissions.");
+ }
+ else {
+
+ if( $aScriptName=='' ) {
+ JpGraphError::Raise('Missing script name in call to StrokeCSIM(). You must specify the name of the actual image script as the first parameter to StrokeCSIM().');
+ exit();
+ }
+
+ // Construct the HTML wrapper page
+ // Get all user defined URL arguments
+ reset($HTTP_GET_VARS);
+
+ // This is a JPGRAPH internal defined that prevents
+ // us from recursively coming here again
+ $urlarg='?'._CSIM_DISPLAY.'=1';
+
+ while( list($key,$value) = each($HTTP_GET_VARS) ) {
+ if( is_array($value) ) {
+ $n = count($value);
+ for( $i=0; $i < $n; ++$i ) {
+ $urlarg .= '&'.$key.'%5B%5D='.urlencode($value[$i]);
+ }
+ }
+ else {
+ $urlarg .= '&'.$key.'='.urlencode($value);
+ }
+ }
+
+ echo $this->GetHTMLImageMap($aCSIMName);
+
+ echo "<img src='".$aScriptName.$urlarg."' ISMAP USEMAP='#".$aCSIMName."' border=$aBorder>";
+ }
+ }
+ else {
+ $this->Stroke();
+ }
+ }
+
+ function GetTextsYMinMax($aY2=false) {
+ if( $aY2 )
+ $txts = $this->y2texts;
+ else
+ $txts = $this->texts;
+ $n = count($txts);
+ $min=null;
+ $max=null;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $txts[$i]->iScalePosY !== null &&
+ $txts[$i]->iScalePosX !== null ) {
+ if( $min === null ) {
+ $min = $max = $txts[$i]->iScalePosY ;
+ }
+ else {
+ $min = min($min,$txts[$i]->iScalePosY);
+ $max = max($max,$txts[$i]->iScalePosY);
+ }
+ }
+ }
+ if( $min !== null ) {
+ return array($min,$max);
+ }
+ else
+ return null;
+ }
+
+ function GetTextsXMinMax($aY2=false) {
+ if( $aY2 )
+ $txts = $this->y2texts;
+ else
+ $txts = $this->texts;
+ $n = count($txts);
+ $min=null;
+ $max=null;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $txts[$i]->iScalePosY !== null &&
+ $txts[$i]->iScalePosX !== null ) {
+ if( $min === null ) {
+ $min = $max = $txts[$i]->iScalePosX ;
+ }
+ else {
+ $min = min($min,$txts[$i]->iScalePosX);
+ $max = max($max,$txts[$i]->iScalePosX);
+ }
+ }
+ }
+ if( $min !== null ) {
+ return array($min,$max);
+ }
+ else
+ return null;
+ }
+
+ function GetXMinMax() {
+ list($min,$ymin) = $this->plots[0]->Min();
+ list($max,$ymax) = $this->plots[0]->Max();
+ foreach( $this->plots as $p ) {
+ list($xmin,$ymin) = $p->Min();
+ list($xmax,$ymax) = $p->Max();
+ $min = Min($xmin,$min);
+ $max = Max($xmax,$max);
+ }
+ if( $this->y2axis != null ) {
+ foreach( $this->y2plots as $p ) {
+ list($xmin,$ymin) = $p->Min();
+ list($xmax,$ymax) = $p->Max();
+ $min = Min($xmin,$min);
+ $max = Max($xmax,$max);
+ }
+ }
+ return array($min,$max);
+ }
+
+ function AdjustMarginsForTitles() {
+ $totrequired =
+ ($this->title->t != '' ?
+ $this->title->GetTextHeight($this->img) + $this->title->margin + 5 : 0 ) +
+ ($this->subtitle->t != '' ?
+ $this->subtitle->GetTextHeight($this->img) + $this->subtitle->margin + 5 : 0 ) +
+ ($this->subsubtitle->t != '' ?
+ $this->subsubtitle->GetTextHeight($this->img) + $this->subsubtitle->margin + 5 : 0 ) ;
+
+
+ $btotrequired = 0;
+ if( !$this->xaxis->hide && !$this->xaxis->hide_labels ) {
+ // Minimum bottom margin
+ if( $this->xaxis->title->t != '' ) {
+ if( $this->img->a == 90 )
+ $btotrequired = $this->yaxis->title->GetTextHeight($this->img) + 5 ;
+ else
+ $btotrequired = $this->xaxis->title->GetTextHeight($this->img) + 5 ;
+ }
+ else
+ $btotrequired = 0;
+
+ if( $this->img->a == 90 ) {
+ $this->img->SetFont($this->yaxis->font_family,$this->yaxis->font_style,
+ $this->yaxis->font_size);
+ $lh = $this->img->GetTextHeight('Mg',$this->yaxis->label_angle);
+ }
+ else {
+ $this->img->SetFont($this->xaxis->font_family,$this->xaxis->font_style,
+ $this->xaxis->font_size);
+ $lh = $this->img->GetTextHeight('Mg',$this->xaxis->label_angle);
+ }
+
+ $btotrequired += $lh + 5;
+ }
+
+ if( $this->img->a == 90 ) {
+ // DO Nothing. It gets too messy to do this properly for 90 deg...
+ }
+ else{
+ if( $this->img->top_margin < $totrequired ) {
+ $this->SetMargin($this->img->left_margin,$this->img->right_margin,
+ $totrequired,$this->img->bottom_margin);
+ }
+ if( $this->img->bottom_margin < $btotrequired ) {
+ $this->SetMargin($this->img->left_margin,$this->img->right_margin,
+ $this->img->top_margin,$btotrequired);
+ }
+ }
+ }
+
+ // Stroke the graph
+ // $aStrokeFileName If != "" the image will be written to this file and NOT
+ // streamed back to the browser
+ function Stroke($aStrokeFileName="") {
+
+ // Fist make a sanity check that user has specified a scale
+ if( empty($this->yscale) ) {
+ JpGraphError::Raise('You must specify what scale to use with a call to Graph::SetScale().');
+ }
+
+ // Start by adjusting the margin so that potential titles will fit.
+ $this->AdjustMarginsForTitles();
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+ // Do any pre-stroke adjustment that is needed by the different plot types
+ // (i.e bar plots want's to add an offset to the x-labels etc)
+ for($i=0; $i < count($this->plots) ; ++$i ) {
+ $this->plots[$i]->PreStrokeAdjust($this);
+ $this->plots[$i]->DoLegend($this);
+ }
+
+ // Any plots on the second Y scale?
+ if( $this->y2scale != null ) {
+ for($i=0; $i<count($this->y2plots) ; ++$i ) {
+ $this->y2plots[$i]->PreStrokeAdjust($this);
+ $this->y2plots[$i]->DoLegend($this);
+ }
+ }
+
+ // Bail out if any of the Y-axis not been specified and
+ // has no plots. (This means it is impossible to do autoscaling and
+ // no other scale was given so we can't possible draw anything). If you use manual
+ // scaling you also have to supply the tick steps as well.
+ if( (!$this->yscale->IsSpecified() && count($this->plots)==0) ||
+ ($this->y2scale!=null && !$this->y2scale->IsSpecified() && count($this->y2plots)==0) ) {
+ //$e = "n=".count($this->y2plots)."\n";
+ $e = "Can't draw unspecified Y-scale.<br>\nYou have either:<br>\n";
+ $e .= "1. Specified an Y axis for autoscaling but have not supplied any plots<br>\n";
+ $e .= "2. Specified a scale manually but have forgot to specify the tick steps";
+ JpGraphError::Raise($e);
+ }
+
+ // Bail out if no plots and no specified X-scale
+ if( (!$this->xscale->IsSpecified() && count($this->plots)==0 && count($this->y2plots)==0) )
+ JpGraphError::Raise("<strong>JpGraph: Can't draw unspecified X-scale.</strong><br>No plots.<br>");
+
+ //Check if we should autoscale y-axis
+ if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax($this->plots);
+ $lres = $this->GetLinesYMinMax($this->lines);
+ if( is_array($lres) ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+ $tres = $this->GetTextsYMinMax();
+ if( is_array($tres) ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+ $this->yscale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor);
+ }
+ elseif( $this->yscale->IsSpecified() &&
+ ( $this->yscale->auto_ticks || !$this->yscale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->yscale->scale[0];
+ $max = $this->yscale->scale[1];
+ $this->yscale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor,
+ $this->yscale->auto_ticks);
+ }
+
+ if( $this->y2scale != null) {
+ if( !$this->y2scale->IsSpecified() && count($this->y2plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax($this->y2plots);
+
+ $lres = $this->GetLinesYMinMax($this->y2lines);
+ if( is_array($lres) ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+ $tres = $this->GetTextsYMinMax(true);
+ if( is_array($tres) ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+ $this->y2scale->AutoScale($this->img,$min,$max,$this->img->plotheight/$this->ytick_factor);
+ }
+ elseif( $this->y2scale->IsSpecified() &&
+ ( $this->y2scale->auto_ticks || !$this->y2scale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->y2scale->scale[0];
+ $max = $this->y2scale->scale[1];
+ $this->y2scale->AutoScale($this->img,$min,$max,
+ $this->img->plotheight/$this->ytick_factor,
+ $this->y2scale->auto_ticks);
+ }
+ }
+
+ //Check if we should autoscale x-axis
+ if( !$this->xscale->IsSpecified() ) {
+ if( substr($this->axtype,0,4) == "text" ) {
+ $max=0;
+ foreach( $this->plots as $p ) {
+ $max=max($max,$p->numpoints-1);
+ }
+ $min=0;
+ if( $this->y2axis != null ) {
+ foreach( $this->y2plots as $p ) {
+ $max=max($max,$p->numpoints-1);
+ }
+ }
+ $this->xscale->Update($this->img,$min,$max);
+ $this->xscale->ticks->Set($this->xaxis->tick_step,1);
+ $this->xscale->ticks->SupressMinorTickMarks();
+ }
+ else {
+ list($min,$max) = $this->GetXMinMax();
+ $lres = $this->GetLinesXMinMax($this->lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+
+ $lres = $this->GetLinesXMinMax($this->y2lines);
+ if( $lres ) {
+ list($linmin,$linmax) = $lres ;
+ $min = min($min,$linmin);
+ $max = max($max,$linmax);
+ }
+
+ $tres = $this->GetTextsXMinMax();
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+
+ $tres = $this->GetTextsXMinMax(true);
+ if( $tres ) {
+ list($tmin,$tmax) = $tres ;
+ $min = min($min,$tmin);
+ $max = max($max,$tmax);
+ }
+
+ $this->xscale->AutoScale($this->img,$min,$max,$this->img->plotwidth/$this->xtick_factor);
+ }
+
+ //Adjust position of y-axis and y2-axis to minimum/maximum of x-scale
+ if( !is_numeric($this->yaxis->pos) && !is_string($this->yaxis->pos) )
+ $this->yaxis->SetPos($this->xscale->GetMinVal());
+ if( $this->y2axis != null ) {
+ if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
+ $this->y2axis->SetPos($this->xscale->GetMaxVal());
+ $this->y2axis->SetTitleSide(SIDE_RIGHT);
+ }
+ }
+ elseif( $this->xscale->IsSpecified() &&
+ ( $this->xscale->auto_ticks || !$this->xscale->ticks->IsSpecified()) ) {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ $min = $this->xscale->scale[0];
+ $max = $this->xscale->scale[1];
+ $this->xscale->AutoScale($this->img,$min,$max,
+ $this->img->plotwidth/$this->xtick_factor,
+ false);
+
+ if( $this->y2axis != null ) {
+ if( !is_numeric($this->y2axis->pos) && !is_string($this->y2axis->pos) )
+ $this->y2axis->SetPos($this->xscale->GetMaxVal());
+ $this->y2axis->SetTitleSide(SIDE_RIGHT);
+ }
+
+ }
+
+ // If we have a negative values and x-axis position is at 0
+ // we need to supress the first and possible the last tick since
+ // they will be drawn on top of the y-axis (and possible y2 axis)
+ // The test below might seem strange the reasone being that if
+ // the user hasn't specified a value for position this will not
+ // be set until we do the stroke for the axis so as of now it
+ // is undefined.
+ // For X-text scale we ignore all this since the tick are usually
+ // much further in and not close to the Y-axis. Hence the test
+ // for 'text'
+
+ if( ($this->yaxis->pos==$this->xscale->GetMinVal() ||
+ (is_string($this->yaxis->pos) && $this->yaxis->pos=='min')) &&
+ !is_numeric($this->xaxis->pos) && $this->yscale->GetMinVal() < 0 &&
+ substr($this->axtype,0,4) != 'text' && $this->xaxis->pos!="min" ) {
+
+ //$this->yscale->ticks->SupressZeroLabel(false);
+ $this->xscale->ticks->SupressFirst();
+ if( $this->y2axis != null ) {
+ $this->xscale->ticks->SupressLast();
+ }
+ }
+ elseif( !is_numeric($this->yaxis->pos) && $this->yaxis->pos=='max' ) {
+ $this->xscale->ticks->SupressLast();
+ }
+
+
+ if( !$_csim ) {
+ $this->StrokePlotArea();
+ $this->StrokeAxis();
+ }
+
+ // Stroke bands
+ if( $this->bands != null && !$_csim)
+ for($i=0; $i < count($this->bands); ++$i) {
+ // Stroke all bands that asks to be in the background
+ if( $this->bands[$i]->depth == DEPTH_BACK )
+ $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+
+ if( $this->y2bands != null && $this->y2scale != null && !$_csim )
+ for($i=0; $i < count($this->y2bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->y2bands[$i]->depth == DEPTH_BACK )
+ $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+
+ if( $this->grid_depth == DEPTH_BACK && !$_csim) {
+ $this->ygrid->Stroke();
+ $this->xgrid->Stroke();
+ }
+
+ // Stroke Y2-axis
+ if( $this->y2axis != null && !$_csim) {
+ $this->y2axis->Stroke($this->xscale);
+ $this->y2grid->Stroke();
+ }
+
+ $oldoff=$this->xscale->off;
+ if(substr($this->axtype,0,4)=="text") {
+ $this->xscale->off +=
+ ceil($this->xscale->scale_factor*$this->text_scale_off*$this->xscale->ticks->minor_step);
+ }
+
+ if( $this->iDoClipping ) {
+ $oldimage = $this->img->CloneCanvasH();
+ }
+
+ if( ! $this->y2orderback ) {
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ $this->plots[$i]->StrokeMargin($this->img);
+ }
+ }
+
+ // Stroke all plots for Y2 axis
+ if( $this->y2scale != null )
+ for($i=0; $i< count($this->y2plots); ++$i ) {
+ $this->y2plots[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+ if( $this->y2orderback ) {
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ $this->plots[$i]->StrokeMargin($this->img);
+ }
+ }
+
+
+ if( $this->iDoClipping ) {
+ // Clipping only supports graphs at 0 and 90 degrees
+ if( $this->img->a == 0 ) {
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->plotwidth+1,$this->img->plotheight);
+ }
+ elseif( $this->img->a == 90 ) {
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->plotheight+1,$this->img->plotwidth);
+ }
+ else {
+ JpGraphError::Raise('You have enabled clipping. Cliping is only supported for graphs at 0 or 90 degrees rotation. Please adjust you current angle (='.$this->img->a.' degrees) or disable clipping.');
+ }
+ $this->img->Destroy();
+ $this->img->SetCanvasH($oldimage);
+ }
+
+ $this->xscale->off=$oldoff;
+
+ if( $this->grid_depth == DEPTH_FRONT && !$_csim ) {
+ $this->ygrid->Stroke();
+ $this->xgrid->Stroke();
+ }
+
+ // Stroke bands
+ if( $this->bands!= null )
+ for($i=0; $i < count($this->bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->bands[$i]->depth == DEPTH_FRONT )
+ $this->bands[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+
+ if( $this->y2bands!= null && $this->y2scale != null )
+ for($i=0; $i < count($this->y2bands); ++$i) {
+ // Stroke all bands that asks to be in the foreground
+ if( $this->y2bands[$i]->depth == DEPTH_FRONT )
+ $this->y2bands[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+
+
+ // Stroke any lines added
+ if( $this->lines != null ) {
+ for($i=0; $i < count($this->lines); ++$i) {
+ $this->lines[$i]->Stroke($this->img,$this->xscale,$this->yscale);
+ }
+ }
+
+ if( $this->y2lines != null && $this->y2scale != null ) {
+ for($i=0; $i < count($this->y2lines); ++$i) {
+ $this->y2lines[$i]->Stroke($this->img,$this->xscale,$this->y2scale);
+ }
+ }
+
+
+ // Finally draw the axis again since some plots may have nagged
+ // the axis in the edges.
+ if( !$_csim )
+ $this->StrokeAxis();
+
+ if( $this->y2scale != null && !$_csim )
+ $this->y2axis->Stroke($this->xscale);
+
+ if( !$_csim ) {
+ $this->StrokePlotBox();
+ }
+
+ if( !$_csim ) {
+ // The titles and legends never gets rotated so make sure
+ // that the angle is 0 before stroking them
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeTitles();
+ $this->footer->Stroke($this->img);
+ }
+
+ $this->legend->Stroke($this->img);
+
+ if( !$_csim ) {
+
+ $this->StrokeTexts();
+ $this->img->SetAngle($aa);
+
+ // Draw an outline around the image map
+ if(_JPG_DEBUG) {
+ $this->DisplayClientSideaImageMapAreas();
+ }
+
+ // Adjust the appearance of the image
+ $this->AdjustSaturationBrightnessContrast();
+
+ // Should we do any final image transformation
+ if( $this->iImgTrans ) {
+ if( !class_exists('ImgTrans') ) {
+ require_once('jpgraph_imgtrans.php');
+ //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
+ }
+
+ $tform = new ImgTrans($this->img->img);
+ $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
+ $this->iImgTransDirection,$this->iImgTransHighQ,
+ $this->iImgTransMinSize,$this->iImgTransFillColor,
+ $this->iImgTransBorder);
+ }
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,$aStrokeFileName);
+ }
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+ function StrokeAxis() {
+
+ // Stroke axis
+ if( $this->iAxisStyle != AXSTYLE_SIMPLE ) {
+ switch( $this->iAxisStyle ) {
+ case AXSTYLE_BOXIN :
+ $toppos = SIDE_DOWN;
+ $bottompos = SIDE_UP;
+ $leftpos = SIDE_RIGHT;
+ $rightpos = SIDE_LEFT;
+ break;
+ case AXSTYLE_BOXOUT :
+ $toppos = SIDE_UP;
+ $bottompos = SIDE_DOWN;
+ $leftpos = SIDE_LEFT;
+ $rightpos = SIDE_RIGHT;
+ break;
+ case AXSTYLE_YBOXIN:
+ $toppos = -100;
+ $bottompos = SIDE_UP;
+ $leftpos = SIDE_RIGHT;
+ $rightpos = SIDE_LEFT;
+ break;
+ case AXSTYLE_YBOXOUT:
+ $toppos = -100;
+ $bottompos = SIDE_DOWN;
+ $leftpos = SIDE_LEFT;
+ $rightpos = SIDE_RIGHT;
+ break;
+ default:
+ JpGRaphError::Raise('Unknown AxisStyle() : '.$this->iAxisStyle);
+ break;
+ }
+ $this->xaxis->SetPos('min');
+
+ // By default we hide the first label so it doesn't cross the
+ // Y-axis in case the positon hasn't been set by the user.
+ // However, if we use a box we always want the first value
+ // displayed so we make sure it will be displayed.
+ $this->xscale->ticks->SupressFirst(false);
+
+ $this->xaxis->SetLabelSide(SIDE_DOWN);
+ $this->xaxis->scale->ticks->SetSide($bottompos);
+ $this->xaxis->Stroke($this->yscale);
+
+ if( $toppos != -100 ) {
+ // To avoid side effects we work on a new copy
+ $maxis = $this->xaxis;
+ $maxis->SetPos('max');
+ $maxis->SetLabelSide(SIDE_UP);
+ $maxis->SetLabelMargin(7);
+ $this->xaxis->scale->ticks->SetSide($toppos);
+ $maxis->Stroke($this->yscale);
+ }
+
+ $this->yaxis->SetPos('min');
+ $this->yaxis->SetLabelMargin(10);
+ $this->yaxis->SetLabelSide(SIDE_LEFT);
+ $this->yaxis->scale->ticks->SetSide($leftpos);
+ $this->yaxis->Stroke($this->xscale);
+
+ $myaxis = $this->yaxis;
+ $myaxis->SetPos('max');
+ $myaxis->SetLabelMargin(10);
+ $myaxis->SetLabelSide(SIDE_RIGHT);
+ $myaxis->title->Set('');
+ $myaxis->scale->ticks->SetSide($rightpos);
+ $myaxis->Stroke($this->xscale);
+
+ }
+ else {
+ $this->xaxis->Stroke($this->yscale);
+ $this->yaxis->Stroke($this->xscale);
+ }
+ }
+
+
+ // Private helper function for backgound image
+ function LoadBkgImage($aImgFormat='',$aFile='') {
+ if( $aFile == '' )
+ $aFile = $this->background_image;
+ // Remove case sensitivity and setup appropriate function to create image
+ // Get file extension. This should be the LAST '.' separated part of the filename
+ $e = explode('.',$aFile);
+ $ext = strtolower($e[count($e)-1]);
+ if ($ext == "jpeg") {
+ $ext = "jpg";
+ }
+
+ if( trim($ext) == '' )
+ $ext = 'png'; // Assume PNG if no extension specified
+
+ if( $aImgFormat == '' )
+ $imgtag = $ext;
+ else
+ $imgtag = $aImgFormat;
+
+ $supported = imagetypes();
+ if( ( $ext == 'jpg' && !($supported & IMG_JPG) ) ||
+ ( $ext == 'gif' && !($supported & IMG_GIF) ) ||
+ ( $ext == 'png' && !($supported & IMG_PNG) ) ) {
+ JpGraphError::Raise('The image format of your background image ('.$aFile.') is not supported in your system configuration. ');
+ }
+
+
+ if( $imgtag == "jpg" || $imgtag == "jpeg")
+ {
+ $f = "imagecreatefromjpeg";
+ $imgtag = "jpg";
+ }
+ else
+ {
+ $f = "imagecreatefrom".$imgtag;
+ }
+
+ // Compare specified image type and file extension
+ if( $imgtag != $ext ) {
+ $t = " Background image seems to be of different type (has different file extension)".
+ " than specified imagetype. Specified: '".
+ $aImgFormat."'File: '".$aFile."'";
+ JpGraphError::Raise($t);
+ }
+
+ $img = @$f($aFile);
+ if( !$img ) {
+ JpGraphError::Raise(" Can't read background image: '".$aFile."'");
+ }
+ return $img;
+ }
+
+ function StrokeBackgroundGrad() {
+ if( $this->bkg_gradtype < 0 )
+ return;
+ $grad = new Gradient($this->img);
+ if( $this->bkg_gradstyle == BGRAD_PLOT ) {
+ $xl = $this->img->left_margin;
+ $yt = $this->img->top_margin;
+ $xr = $xl + $this->img->plotwidth+1 ;
+ $yb = $yt + $this->img->plotheight ;
+ }
+ else {
+ $xl = 0;
+ $yt = 0;
+ $xr = $xl + $this->img->width - 1;
+ $yb = $yt + $this->img->height - 1;
+ if( $this->doshadow ) {
+ $xr -= $this->shadow_width;
+ $yb -= $this->shadow_width;
+ }
+ }
+ if( $this->doframe ) {
+
+ $xl += $this->frame_weight;
+ $xr -= $this->frame_weight;
+ }
+ $grad->FilledRectangle($xl,$yt,$xr,$yb,
+ $this->bkg_gradfrom,$this->bkg_gradto,
+ $this->bkg_gradtype);
+ }
+
+ function StrokeFrameBackground() {
+ if( $this->background_image != "" && $this->background_cflag != "" ) {
+ JpGraphError::Raise('It is not possible to specify both a background image and a background country flag.');
+ }
+ if( $this->background_image != "" ) {
+ $bkgimg = $this->LoadBkgImage($this->background_image_format);
+ $this->img->_AdjBrightContrast($bkgimg,$this->background_image_bright,
+ $this->background_image_contr);
+ $this->img->_AdjSat($bkgimg,$this->background_image_sat);
+ }
+ elseif( $this->background_cflag != "" ) {
+ if( ! class_exists('FlagImages') ) {
+ JpGraphError::Raise('In order to use Country flags as
+ backgrounds you must include the "jpgraph_flags.php" file.');
+ }
+ $fobj = new FlagImages(FLAGSIZE4);
+ $dummy='';
+ $bkgimg = $fobj->GetImgByName($this->background_cflag,$dummy);
+ $this->background_image_mix = $this->background_cflag_mix;
+ $this->background_image_type = $this->background_cflag_type;
+ }
+ else {
+ return ;
+ }
+
+ $bw = ImageSX($bkgimg);
+ $bh = ImageSY($bkgimg);
+
+ // No matter what the angle is we always stroke the image and frame
+ // assuming it is 0 degree
+ $aa = $this->img->SetAngle(0);
+
+ switch( $this->background_image_type ) {
+ case BGIMG_FILLPLOT: // Resize to just fill the plotarea
+ $this->FillMarginArea();
+ $this->StrokeFrame();
+ $this->FillPlotArea();
+ $this->img->CopyMerge($bkgimg,
+ $this->img->left_margin,$this->img->top_margin,
+ 0,0,$this->img->plotwidth+1,$this->img->plotheight,
+ $bw,$bh,$this->background_image_mix);
+ break;
+ case BGIMG_FILLFRAME: // Fill the whole area from upper left corner, resize to just fit
+ $hadj=0; $vadj=0;
+ if( $this->doshadow ) {
+ $hadj = $this->shadow_width;
+ $vadj = $this->shadow_width;
+ }
+ $this->FillMarginArea();
+ $this->FillPlotArea();
+ $this->img->CopyMerge($bkgimg,0,0,0,0,$this->img->width-$hadj,$this->img->height-$vadj,
+ $bw,$bh,$this->background_image_mix);
+ $this->StrokeFrame();
+ break;
+ case BGIMG_COPY: // Just copy the image from left corner, no resizing
+ $this->FillMarginArea();
+ $this->FillPlotArea();
+ $this->img->CopyMerge($bkgimg,0,0,0,0,$bw,$bh,
+ $bw,$bh,$this->background_image_mix);
+ $this->StrokeFrame();
+ break;
+ case BGIMG_CENTER: // Center original image in the plot area
+ $this->FillMarginArea();
+ $this->FillPlotArea();
+ $centerx = round($this->img->plotwidth/2+$this->img->left_margin-$bw/2);
+ $centery = round($this->img->plotheight/2+$this->img->top_margin-$bh/2);
+ $this->img->CopyMerge($bkgimg,$centerx,$centery,0,0,$bw,$bh,
+ $bw,$bh,$this->background_image_mix);
+ $this->StrokeFrame();
+ break;
+ default:
+ JpGraphError::Raise(" Unknown background image layout");
+ }
+ $this->img->SetAngle($aa);
+ }
+
+ // Private
+ // Draw a frame around the image
+ function StrokeFrame() {
+ if( !$this->doframe ) return;
+
+ if( $this->background_image_type <= 1 &&
+ ($this->bkg_gradtype < 0 || ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_PLOT)) ) {
+ $c = $this->margin_color;
+ }
+ else {
+ $c = false;
+ }
+
+ if( $this->doshadow ) {
+ $this->img->SetColor($this->frame_color);
+ $this->img->ShadowRectangle(0,0,$this->img->width,$this->img->height,
+ $c,$this->shadow_width,$this->shadow_color);
+ }
+ elseif( $this->framebevel ) {
+ if( $c ) {
+ $this->img->SetColor($this->margin_color);
+ $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
+ $this->framebeveldepth,
+ $this->framebevelcolor1,$this->framebevelcolor2);
+ if( $this->framebevelborder ) {
+ $this->img->SetColor($this->framebevelbordercolor);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+ else {
+ $this->img->SetLineWeight($this->frame_weight);
+ if( $c ) {
+ $this->img->SetColor($this->margin_color);
+ $this->img->FilledRectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ $this->img->SetColor($this->frame_color);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+
+ function FillMarginArea() {
+ $hadj=0; $vadj=0;
+ if( $this->doshadow ) {
+ $hadj = $this->shadow_width;
+ $vadj = $this->shadow_width;
+ }
+
+ $this->img->SetColor($this->margin_color);
+// $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->height-1-$vadj);
+
+ $this->img->FilledRectangle(0,0,$this->img->width-1-$hadj,$this->img->top_margin);
+ $this->img->FilledRectangle(0,$this->img->top_margin,$this->img->left_margin,$this->img->height-1-$hadj);
+ $this->img->FilledRectangle($this->img->left_margin+1,
+ $this->img->height-$this->img->bottom_margin,
+ $this->img->width-1-$hadj,
+ $this->img->height-1-$hadj);
+ $this->img->FilledRectangle($this->img->width-$this->img->right_margin,
+ $this->img->top_margin+1,
+ $this->img->width-1-$hadj,
+ $this->img->height-$this->img->bottom_margin-1);
+ }
+
+ function FillPlotArea() {
+ $this->img->PushColor($this->plotarea_color);
+ $this->img->FilledRectangle($this->img->left_margin,
+ $this->img->top_margin,
+ $this->img->width-$this->img->right_margin,
+ $this->img->height-$this->img->bottom_margin);
+ $this->img->PopColor();
+ }
+
+ // Stroke the plot area with either a solid color or a background image
+ function StrokePlotArea() {
+ // Note: To be consistent we really should take a possible shadow
+ // into account. However, that causes some problem for the LinearScale class
+ // since in the current design it does not have any links to class Graph which
+ // means it has no way of compensating for the adjusted plotarea in case of a
+ // shadow. So, until I redesign LinearScale we can't compensate for this.
+ // So just set the two adjustment parameters to zero for now.
+ $boxadj = 0; //$this->doframe ? $this->frame_weight : 0 ;
+ $adj = 0; //$this->doshadow ? $this->shadow_width : 0 ;
+
+ if( $this->background_image != "" || $this->background_cflag != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeFrame();
+ $aa = $this->img->SetAngle($aa);
+ $this->StrokeBackgroundGrad();
+ if( $this->bkg_gradtype < 0 ||
+ ($this->bkg_gradtype > 0 && $this->bkg_gradstyle==BGRAD_MARGIN) ) {
+ $this->FillPlotArea();
+ }
+ }
+ $this->StrokeIcons();
+ }
+
+ function StrokeIcons() {
+ $n = count($this->iIcons);
+ for( $i=0; $i < $n; ++$i ) {
+ $this->iIcons[$i]->Stroke($this->img);
+ }
+ }
+
+ function StrokePlotBox() {
+ // Should we draw a box around the plot area?
+ if( $this->boxed ) {
+ $this->img->SetLineWeight(1);
+ $this->img->SetColor($this->box_color);
+ for($i=0; $i < $this->box_weight; ++$i ) {
+ $this->img->Rectangle(
+ $this->img->left_margin-$i,$this->img->top_margin-$i,
+ $this->img->width-$this->img->right_margin+$i,
+ $this->img->height-$this->img->bottom_margin+$i);
+ }
+ }
+ }
+
+ function SetTitleBackgroundFillStyle($aStyle,$aColor1='black',$aColor2='white') {
+ $this->titlebkg_fillstyle = $aStyle;
+ $this->titlebkg_scolor1 = $aColor1;
+ $this->titlebkg_scolor2 = $aColor2;
+ }
+
+ function SetTitleBackground($aBackColor='gray', $aStyle=TITLEBKG_STYLE1, $aFrameStyle=TITLEBKG_FRAME_NONE, $aFrameColor='black', $aFrameWeight=1, $aBevelHeight=3, $aEnable=true) {
+ $this->titlebackground = $aEnable;
+ $this->titlebackground_color = $aBackColor;
+ $this->titlebackground_style = $aStyle;
+ $this->titlebackground_framecolor = $aFrameColor;
+ $this->titlebackground_framestyle = $aFrameStyle;
+ $this->titlebackground_frameweight = $aFrameWeight;
+ $this->titlebackground_bevelheight = $aBevelHeight ;
+ }
+
+
+ function StrokeTitles() {
+
+ $margin=3;
+
+ if( $this->titlebackground ) {
+
+ // Find out height
+ $this->title->margin += 2 ;
+ $h = $this->title->GetTextHeight($this->img)+$this->title->margin+$margin;
+ if( $this->subtitle->t != "" && !$this->subtitle->hide ) {
+ $h += $this->subtitle->GetTextHeight($this->img)+$margin+
+ $this->subtitle->margin;
+ $h += 2;
+ }
+ if( $this->subsubtitle->t != "" && !$this->subsubtitle->hide ) {
+ $h += $this->subsubtitle->GetTextHeight($this->img)+$margin+
+ $this->subsubtitle->margin;
+ $h += 2;
+ }
+ $this->img->PushColor($this->titlebackground_color);
+ if( $this->titlebackground_style === TITLEBKG_STYLE1 ) {
+ // Inside the frame
+ if( $this->framebevel ) {
+ $x1 = $y1 = $this->framebeveldepth + 1 ;
+ $x2 = $this->img->width - $this->framebeveldepth - 2 ;
+ $this->title->margin += $this->framebeveldepth + 1 ;
+ $h += $y1 ;
+ $h += 2;
+ }
+ else {
+ $x1 = $y1 = $this->frame_weight;
+ $x2 = $this->img->width - 2*$x1;
+ }
+ }
+ elseif( $this->titlebackground_style === TITLEBKG_STYLE2 ) {
+ // Cover the frame as well
+ $x1 = $y1 = 0;
+ $x2 = $this->img->width - 1 ;
+ }
+ elseif( $this->titlebackground_style === TITLEBKG_STYLE3 ) {
+ // Cover the frame as well (the difference is that
+ // for style==3 a bevel frame border is on top
+ // of the title background)
+ $x1 = $y1 = 0;
+ $x2 = $this->img->width - 1 ;
+ $h += $this->framebeveldepth ;
+ $this->title->margin += $this->framebeveldepth ;
+ }
+ else {
+ JpGraphError::Raise('Unknown title background style.');
+ }
+
+ if( $this->titlebackground_framestyle === 3 ) {
+ $h += $this->titlebackground_bevelheight*2 + 1 ;
+ $this->title->margin += $this->titlebackground_bevelheight ;
+ }
+
+ if( $this->doshadow ) {
+ $x2 -= $this->shadow_width ;
+ }
+
+ $indent=0;
+ if( $this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL ) {
+ $ind = $this->titlebackground_bevelheight;
+ }
+
+ if( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_HSTRIPED ) {
+ $this->img->FilledRectangle2($x1+$ind,$y1+$ind,$x2-$ind,$h-$ind,
+ $this->titlebkg_scolor1,
+ $this->titlebkg_scolor2);
+ }
+ elseif( $this->titlebkg_fillstyle==TITLEBKG_FILLSTYLE_VSTRIPED ) {
+ $this->img->FilledRectangle2($x1+$ind,$y1+$ind,$x2-$ind,$h-$ind,
+ $this->titlebkg_scolor1,
+ $this->titlebkg_scolor2,2);
+ }
+ else {
+ // Solid fill
+ $this->img->FilledRectangle($x1,$y1,$x2,$h);
+ }
+ $this->img->PopColor();
+
+ $this->img->PushColor($this->titlebackground_framecolor);
+ $this->img->SetLineWeight($this->titlebackground_frameweight);
+ if( $this->titlebackground_framestyle == TITLEBKG_FRAME_FULL ) {
+ // Frame background
+ $this->img->Rectangle($x1,$y1,$x2,$h);
+ }
+ elseif( $this->titlebackground_framestyle == TITLEBKG_FRAME_BOTTOM ) {
+ // Bottom line only
+ $this->img->Line($x1,$h,$x2,$h);
+ }
+ elseif( $this->titlebackground_framestyle == TITLEBKG_FRAME_BEVEL ) {
+ $this->img->Bevel($x1,$y1,$x2,$h,$this->titlebackground_bevelheight);
+ }
+ $this->img->PopColor();
+
+ // This is clumsy. But we neeed to stroke the whole graph frame if it is
+ // set to bevel to get the bevel shading on top of the text background
+ if( $this->framebevel && $this->doframe &&
+ $this->titlebackground_style === 3 ) {
+ $this->img->Bevel(1,1,$this->img->width-2,$this->img->height-2,
+ $this->framebeveldepth,
+ $this->framebevelcolor1,$this->framebevelcolor2);
+ if( $this->framebevelborder ) {
+ $this->img->SetColor($this->framebevelbordercolor);
+ $this->img->Rectangle(0,0,$this->img->width-1,$this->img->height-1);
+ }
+ }
+ }
+
+ // Stroke title
+ $y = $this->title->margin;
+ if( $this->title->halign == 'center' )
+ $this->title->Center(0,$this->img->width,$y);
+ elseif( $this->title->halign == 'left' ) {
+ $this->title->SetPos($this->title->margin+2,$y);
+ }
+ elseif( $this->title->halign == 'right' ) {
+ $indent = 0;
+ if( $this->doshadow )
+ $indent = $this->shadow_width+2;
+ $this->title->SetPos($this->img->width-$this->title->margin-$indent,$y,'right');
+ }
+ $this->title->Stroke($this->img);
+
+ // ... and subtitle
+ $y += $this->title->GetTextHeight($this->img) + $margin + $this->subtitle->margin;
+ if( $this->subtitle->halign == 'center' )
+ $this->subtitle->Center(0,$this->img->width,$y);
+ elseif( $this->subtitle->halign == 'left' ) {
+ $this->subtitle->SetPos($this->subtitle->margin+2,$y);
+ }
+ elseif( $this->subtitle->halign == 'right' ) {
+ $indent = 0;
+ if( $this->doshadow )
+ $indent = $this->shadow_width+2;
+ $this->subtitle->SetPos($this->img->width-$this->subtitle->margin-$indent,$y,'right');
+ }
+ $this->subtitle->Stroke($this->img);
+
+ // ... and subsubtitle
+ $y += $this->subtitle->GetTextHeight($this->img) + $margin + $this->subsubtitle->margin;
+ if( $this->subsubtitle->halign == 'center' )
+ $this->subsubtitle->Center(0,$this->img->width,$y);
+ elseif( $this->subsubtitle->halign == 'left' ) {
+ $this->subsubtitle->SetPos($this->subsubtitle->margin+2,$y);
+ }
+ elseif( $this->subsubtitle->halign == 'right' ) {
+ $indent = 0;
+ if( $this->doshadow )
+ $indent = $this->shadow_width+2;
+ $this->subsubtitle->SetPos($this->img->width-$this->subsubtitle->margin-$indent,$y,'right');
+ }
+ $this->subsubtitle->Stroke($this->img);
+
+ // ... and fancy title
+ $this->tabtitle->Stroke($this->img);
+
+ }
+
+ function StrokeTexts() {
+ // Stroke any user added text objects
+ if( $this->texts != null ) {
+ for($i=0; $i < count($this->texts); ++$i) {
+ $this->texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->yscale);
+ }
+ }
+
+ if( $this->y2texts != null && $this->y2scale != null ) {
+ for($i=0; $i < count($this->y2texts); ++$i) {
+ $this->y2texts[$i]->StrokeWithScale($this->img,$this->xscale,$this->y2scale);
+ }
+ }
+
+ }
+
+ function DisplayClientSideaImageMapAreas() {
+ // Debug stuff - display the outline of the image map areas
+ $csim='';
+ foreach ($this->plots as $p) {
+ $csim.= $p->GetCSIMareas();
+ }
+ $csim .= $this->legend->GetCSIMareas();
+ if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
+ $this->img->SetColor($this->csimcolor);
+ for ($i=0; $i<count($coords[0]); $i++) {
+ if ($coords[1][$i]=="poly") {
+ preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
+ $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
+ for ($j=0; $j<count($pts[0]); $j++) {
+ $this->img->LineTo($pts[1][$j],$pts[2][$j]);
+ }
+ } else if ($coords[1][$i]=="rect") {
+ $pts = preg_split('/,/', $coords[2][$i]);
+ $this->img->SetStartPoint($pts[0],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[1]);
+ }
+ }
+ }
+ }
+
+ function AdjustSaturationBrightnessContrast() {
+ // Adjust the brightness and contrast of the image
+ if( $this->image_contr || $this->image_bright )
+ $this->img->AdjBrightContrast($this->image_bright,$this->image_contr);
+ if( $this->image_sat )
+ $this->img->AdjSat($this->image_sat);
+ }
+
+ // Text scale offset in world coordinates
+ function SetTextScaleOff($aOff) {
+ $this->text_scale_off = $aOff;
+ $this->xscale->text_scale_off = $aOff;
+ }
+
+ // Get Y min and max values for added lines
+ function GetLinesYMinMax( $aLines ) {
+ $n = count($aLines);
+ if( $n == 0 ) return false;
+ $min = $aLines[0]->scaleposition ;
+ $max = $min ;
+ $flg = false;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $aLines[$i]->direction == HORIZONTAL ) {
+ $flg = true ;
+ $v = $aLines[$i]->scaleposition ;
+ if( $min > $v ) $min = $v ;
+ if( $max < $v ) $max = $v ;
+ }
+ }
+ return $flg ? array($min,$max) : false ;
+ }
+
+ // Get X min and max values for added lines
+ function GetLinesXMinMax( $aLines ) {
+ $n = count($aLines);
+ if( $n == 0 ) return false ;
+ $min = $aLines[0]->scaleposition ;
+ $max = $min ;
+ $flg = false;
+ for( $i=0; $i < $n; ++$i ) {
+ if( $aLines[$i]->direction == VERTICAL ) {
+ $flg = true ;
+ $v = $aLines[$i]->scaleposition ;
+ if( $min > $v ) $min = $v ;
+ if( $max < $v ) $max = $v ;
+ }
+ }
+ return $flg ? array($min,$max) : false ;
+ }
+
+ // Get min and max values for all included plots
+ function GetPlotsYMinMax(&$aPlots) {
+ list($xmax,$max) = $aPlots[0]->Max();
+ list($xmin,$min) = $aPlots[0]->Min();
+ for($i=0; $i < count($aPlots); ++$i ) {
+ list($xmax,$ymax)=$aPlots[$i]->Max();
+ list($xmin,$ymin)=$aPlots[$i]->Min();
+ if (is_numeric($ymax)) $max=max($max,$ymax);
+ if (is_numeric($ymin)) $min=min($min,$ymin);
+ }
+ if( $min == '' ) $min = 0;
+ if( $max == '' ) $max = 0;
+ if( $min == 0 && $max == 0 ) {
+ // Special case if all values are 0
+ $min=0;$max=1;
+ }
+ return array($min,$max);
+ }
+
+} // Class
+
+
+//===================================================
+// CLASS TTF
+// Description: Handle TTF font names
+//===================================================
+class TTF {
+ var $font_files,$style_names;
+//---------------
+// CONSTRUCTOR
+ function TTF() {
+ $this->style_names=array(FS_NORMAL=>'normal',FS_BOLD=>'bold',FS_ITALIC=>'italic',FS_BOLDITALIC=>'bolditalic');
+ // File names for available fonts
+ $this->font_files=array(
+ FF_COURIER => array(FS_NORMAL=>'cour.ttf', FS_BOLD=>'courbd.ttf', FS_ITALIC=>'couri.ttf', FS_BOLDITALIC=>'courbi.ttf' ),
+ FF_GEORGIA => array(FS_NORMAL=>'georgia.ttf', FS_BOLD=>'georgiab.ttf', FS_ITALIC=>'georgiai.ttf', FS_BOLDITALIC=>'' ),
+ FF_TREBUCHE =>array(FS_NORMAL=>'trebuc.ttf', FS_BOLD=>'trebucbd.ttf', FS_ITALIC=>'trebucit.ttf', FS_BOLDITALIC=>'trebucbi.ttf' ),
+ FF_VERDANA => array(FS_NORMAL=>'verdana.ttf', FS_BOLD=>'verdanab.ttf', FS_ITALIC=>'verdanai.ttf', FS_BOLDITALIC=>'' ),
+ FF_TIMES => array(FS_NORMAL=>'times.ttf', FS_BOLD=>'timesbd.ttf', FS_ITALIC=>'timesi.ttf', FS_BOLDITALIC=>'timesbi.ttf' ),
+ FF_COMIC => array(FS_NORMAL=>'comic.ttf', FS_BOLD=>'comicbd.ttf', FS_ITALIC=>'.ttf', FS_BOLDITALIC=>'' ),
+ FF_ARIAL => array(FS_NORMAL=>'arial.ttf', FS_BOLD=>'arialbd.ttf', FS_ITALIC=>'ariali.ttf', FS_BOLDITALIC=>'arialbi.ttf' ) ,
+ FF_VERA => array(FS_NORMAL=>'Vera.ttf', FS_BOLD=>'VeraBd.ttf', FS_ITALIC=>'VeraIt.ttf', FS_BOLDITALIC=>'VeraBI.ttf' ),
+ FF_VERAMONO => array(FS_NORMAL=>'VeraMono.ttf', FS_BOLD=>'VeraMoBd.ttf', FS_ITALIC=>'VeraMoIt.ttf', FS_BOLDITALIC=>'VeraMoBI.ttf' ),
+ FF_VERASERIF => array(FS_NORMAL=>'VeraSe.ttf', FS_BOLD=>'VeraSeBd.ttf', FS_ITALIC=>'', FS_BOLDITALIC=>'' ) ,
+ FF_SIMSUN => array(FS_NORMAL=>'simsun.ttc', FS_BOLD=>'simhei.ttf', FS_ITALIC=>'', FS_BOLDITALIC=>'' ),
+ FF_CHINESE => array(FS_NORMAL=>CHINESE_TTF_FONT, FS_BOLD=>'', FS_ITALIC=>'', FS_BOLDITALIC=>'' )
+);
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Create the TTF file from the font specification
+ function File($family,$style=FS_NORMAL) {
+
+ if( $family == FF_HANDWRT || $family==FF_BOOK ) {
+ JpGraphError::Raise('Font families FF_HANDWRT and FF_BOOK are no longer available due to copyright problem with these fonts. Fonts can no longer be distributed with JpGraph. Please download fonts from http://corefonts.sourceforge.net/');
+ }
+
+ $fam = @$this->font_files[$family];
+ if( !$fam ) {
+ JpGraphError::Raise(
+ "Specified TTF font family (id=$family) is unknown or does not exist. ".
+ "Please note that TTF fonts are not distributed with JpGraph for copyright reasons.".
+ " You can find the MS TTF WEB-fonts (arial, courier etc) for download at ".
+ " http://corefonts.sourceforge.net/");
+ }
+ $f = @$fam[$style];
+
+ if( $f==='' )
+ JpGraphError::Raise('Style "'.$this->style_names[$style].'" is not available for font family '.$this->font_files[$family][FS_NORMAL].'.');
+ if( !$f )
+ JpGraphError::Raise("Unknown font style specification [$fam].");
+ $f = TTF_DIR.$f;
+ if( file_exists($f) === false || is_readable($f) === false ) {
+ JpGraphError::Raise("Font file \"$f\" is not readable or does not exist.");
+ }
+ return $f;
+ }
+} // Class
+
+//===================================================
+// CLASS LineProperty
+// Description: Holds properties for a line
+//===================================================
+class LineProperty {
+ var $iWeight=1, $iColor="black",$iStyle="solid";
+ var $iShow=true;
+
+//---------------
+// PUBLIC METHODS
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->iWeight = $aWeight;
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function Show($aShow=true) {
+ $this->iShow=$aShow;
+ }
+
+ function Stroke($aImg,$aX1,$aY1,$aX2,$aY2) {
+ if( $this->iShow ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->SetLineWeight($this->iWeight);
+ $aImg->SetLineStyle($this->iStyle);
+ $aImg->StyleLine($aX1,$aY1,$aX2,$aY2);
+ }
+ }
+}
+
+
+//===================================================
+// CLASS Text
+// Description: Arbitrary text object that can be added to the graph
+//===================================================
+class Text {
+ var $t,$x=0,$y=0,$halign="left",$valign="top",$color=array(0,0,0);
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
+ var $hide=false, $dir=0;
+ var $boxed=false; // Should the text be boxed
+ var $paragraph_align="left";
+ var $margin=0;
+ var $icornerradius=0,$ishadowwidth=3;
+ var $iScalePosY=null,$iScalePosX=null;
+ var $iWordwrap=0;
+
+//---------------
+// CONSTRUCTOR
+
+ // Create new text at absolute pixel coordinates
+ function Text($aTxt="",$aXAbsPos=0,$aYAbsPos=0) {
+ if( ! is_string($aTxt) ) {
+ JpGraphError::Raise('First argument to Text::Text() must be s atring.');
+ }
+ $this->t = $aTxt;
+ $this->x = round($aXAbsPos);
+ $this->y = round($aYAbsPos);
+ $this->margin = 0;
+ }
+//---------------
+// PUBLIC METHODS
+ // Set the string in the text object
+ function Set($aTxt) {
+ $this->t = $aTxt;
+ }
+
+ // Alias for Pos()
+ function SetPos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
+ $this->Pos($aXAbsPos,$aYAbsPos,$aHAlign,$aVAlign);
+ }
+
+ // Specify the position and alignment for the text object
+ function Pos($aXAbsPos=0,$aYAbsPos=0,$aHAlign="left",$aVAlign="top") {
+ $this->x = $aXAbsPos;
+ $this->y = $aYAbsPos;
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ }
+
+ function SetScalePos($aX,$aY) {
+ $this->iScalePosX = $aX;
+ $this->iScalePosY = $aY;
+ }
+
+ // Specify alignment for the text
+ function Align($aHAlign,$aVAlign="top",$aParagraphAlign="") {
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ if( $aParagraphAlign != "" )
+ $this->paragraph_align = $aParagraphAlign;
+ }
+
+ // Alias
+ function SetAlign($aHAlign,$aVAlign="top",$aParagraphAlign="") {
+ $this->Align($aHAlign,$aVAlign,$aParagraphAlign);
+ }
+
+ // Specifies the alignment for a multi line text
+ function ParagraphAlign($aAlign) {
+ $this->paragraph_align = $aAlign;
+ }
+
+ // Specifies the alignment for a multi line text
+ function SetParagraphAlign($aAlign) {
+ $this->paragraph_align = $aAlign;
+ }
+
+ function SetShadow($aShadowColor='gray',$aShadowWidth=3) {
+ $this->ishadowwidth=$aShadowWidth;
+ $this->shadow=$aShadowColor;
+ $this->boxed=true;
+ }
+
+ function SetWordWrap($aCol) {
+ $this->iWordwrap = $aCol ;
+ }
+
+ // Specify that the text should be boxed. fcolor=frame color, bcolor=border color,
+ // $shadow=drop shadow should be added around the text.
+ function SetBox($aFrameColor=array(255,255,255),$aBorderColor=array(0,0,0),$aShadowColor=false,$aCornerRadius=4,$aShadowWidth=3) {
+ if( $aFrameColor==false )
+ $this->boxed=false;
+ else
+ $this->boxed=true;
+ $this->fcolor=$aFrameColor;
+ $this->bcolor=$aBorderColor;
+ // For backwards compatibility when shadow was just true or false
+ if( $aShadowColor === true )
+ $aShadowColor = 'gray';
+ $this->shadow=$aShadowColor;
+ $this->icornerradius=$aCornerRadius;
+ $this->ishadowwidth=$aShadowWidth;
+ }
+
+ // Hide the text
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ // This looks ugly since it's not a very orthogonal design
+ // but I added this "inverse" of Hide() to harmonize
+ // with some classes which I designed more recently (especially)
+ // jpgraph_gantt
+ function Show($aShow=true) {
+ $this->hide=!$aShow;
+ }
+
+ // Specify font
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family=$aFamily;
+ $this->font_style=$aStyle;
+ $this->font_size=$aSize;
+ }
+
+ // Center the text between $left and $right coordinates
+ function Center($aLeft,$aRight,$aYAbsPos=false) {
+ $this->x = $aLeft + ($aRight-$aLeft )/2;
+ $this->halign = "center";
+ if( is_numeric($aYAbsPos) )
+ $this->y = $aYAbsPos;
+ }
+
+ // Set text color
+ function SetColor($aColor) {
+ $this->color = $aColor;
+ }
+
+ function SetAngle($aAngle) {
+ $this->SetOrientation($aAngle);
+ }
+
+ // Orientation of text. Note only TTF fonts can have an arbitrary angle
+ function SetOrientation($aDirection=0) {
+ if( is_numeric($aDirection) )
+ $this->dir=$aDirection;
+ elseif( $aDirection=="h" )
+ $this->dir = 0;
+ elseif( $aDirection=="v" )
+ $this->dir = 90;
+ else JpGraphError::Raise(" Invalid direction specified for text.");
+ }
+
+ // Total width of text
+ function GetWidth($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $w = $aImg->GetTextWidth($this->t,$this->dir);
+ return $w;
+ }
+
+ // Hight of font
+ function GetFontHeight($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetFontHeight();
+ return $h;
+
+ }
+
+ function GetTextHeight($aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetTextHeight($this->t,$this->dir);
+ return $h;
+ }
+
+ function GetHeight($aImg) {
+ // Synonym for GetTextHeight()
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetTextHeight($this->t,$this->dir);
+ return $h;
+ }
+
+ // Set the margin which will be interpretated differently depending
+ // on the context.
+ function SetMargin($aMarg) {
+ $this->margin = $aMarg;
+ }
+
+ function StrokeWithScale($aImg,$axscale,$ayscale) {
+ if( $this->iScalePosX === null ||
+ $this->iScalePosY === null ) {
+ $this->Stroke($aImg);
+ }
+ else {
+ $this->Stroke($aImg,
+ round($axscale->Translate($this->iScalePosX)),
+ round($ayscale->Translate($this->iScalePosY)));
+ }
+ }
+
+ // Display text in image
+ function Stroke($aImg,$x=null,$y=null) {
+
+ if( !empty($x) ) $this->x = round($x);
+ if( !empty($y) ) $this->y = round($y);
+
+ // Insert newlines
+ if( $this->iWordwrap > 0 ) {
+ $this->t = wordwrap($this->t,$this->iWordwrap,"\n");
+ }
+
+ // If position been given as a fraction of the image size
+ // calculate the absolute position
+ if( $this->x < 1 && $this->x > 0 ) $this->x *= $aImg->width;
+ if( $this->y < 1 && $this->y > 0 ) $this->y *= $aImg->height;
+
+ $aImg->PushColor($this->color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->SetTextAlign($this->halign,$this->valign);
+ if( $this->boxed ) {
+ if( $this->fcolor=="nofill" )
+ $this->fcolor=false;
+ $aImg->SetLineWeight(1);
+ $aImg->StrokeBoxedText($this->x,$this->y,$this->t,
+ $this->dir,$this->fcolor,$this->bcolor,$this->shadow,
+ $this->paragraph_align,5,5,$this->icornerradius,
+ $this->ishadowwidth);
+ }
+ else {
+ $aImg->StrokeText($this->x,$this->y,$this->t,$this->dir,
+ $this->paragraph_align);
+ }
+ $aImg->PopColor($this->color);
+ }
+} // Class
+
+class GraphTabTitle extends Text{
+ var $corner = 6 , $posx = 7, $posy = 4;
+ var $color='darkred',$fillcolor='lightyellow',$bordercolor='black';
+ var $align = 'left', $width=TABTITLE_WIDTHFIT;
+ function GraphTabTitle() {
+ $this->t = '';
+ $this->font_style = FS_BOLD;
+ $this->hide = true;
+ }
+
+ function SetColor($aTxtColor,$aFillColor='lightyellow',$aBorderColor='black') {
+ $this->color = $aTxtColor;
+ $this->fillcolor = $aFillColor;
+ $this->bordercolor = $aBorderColor;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->fillcolor = $aFillColor;
+ }
+
+ function SetTabAlign($aAlign) {
+ // Synonym for SetPos
+ $this->align = $aAlign;
+ }
+
+ function SetPos($aAlign) {
+ $this->align = $aAlign;
+ }
+
+ function SetWidth($aWidth) {
+ $this->width = $aWidth ;
+ }
+
+ function Set($t) {
+ $this->t = $t;
+ $this->hide = false;
+ }
+
+ function SetCorner($aD) {
+ $this->corner = $aD ;
+ }
+
+ function Stroke($aImg) {
+ if( $this->hide )
+ return;
+ $this->boxed = false;
+ $w = $this->GetWidth($aImg) + 2*$this->posx;
+ $h = $this->GetTextHeight($aImg) + 2*$this->posy;
+
+ $x = $aImg->left_margin;
+ $y = $aImg->top_margin;
+
+ if( $this->width === TABTITLE_WIDTHFIT ) {
+ if( $this->align == 'left' ) {
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ elseif( $this->align == 'center' ) {
+ $x += round($aImg->plotwidth/2) - round($w/2);
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner, $y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ else {
+ $x += $aImg->plotwidth -$w;
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+ }
+ }
+ else {
+ if( $this->width === TABTITLE_WIDTHFULL )
+ $w = $aImg->plotwidth ;
+ else
+ $w = $this->width ;
+
+ // Make the tab fit the width of the plot area
+ $p = array($x, $y,
+ $x, $y-$h+$this->corner,
+ $x + $this->corner,$y-$h,
+ $x + $w - $this->corner, $y-$h,
+ $x + $w, $y-$h+$this->corner,
+ $x + $w, $y);
+
+ }
+ $aImg->SetTextAlign('left','bottom');
+ $x += $this->posx;
+ $y -= $this->posy;
+
+ $aImg->SetColor($this->fillcolor);
+ $aImg->FilledPolygon($p);
+
+ $aImg->SetColor($this->bordercolor);
+ $aImg->Polygon($p,true);
+
+ $aImg->SetColor($this->color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->StrokeText($x,$y,$this->t,0,'center');
+ }
+
+}
+
+//===================================================
+// CLASS SuperScriptText
+// Description: Format a superscript text
+//===================================================
+class SuperScriptText extends Text {
+ var $iSuper="";
+ var $sfont_family="",$sfont_style="",$sfont_size=8;
+ var $iSuperMargin=2,$iVertOverlap=4,$iSuperScale=0.65;
+ var $iSDir=0;
+ var $iSimple=false;
+
+ function SuperScriptText($aTxt="",$aSuper="",$aXAbsPos=0,$aYAbsPos=0) {
+ parent::Text($aTxt,$aXAbsPos,$aYAbsPos);
+ $this->iSuper = $aSuper;
+ }
+
+ function FromReal($aVal,$aPrecision=2) {
+ // Convert a floating point number to scientific notation
+ $neg=1.0;
+ if( $aVal < 0 ) {
+ $neg = -1.0;
+ $aVal = -$aVal;
+ }
+
+ $l = floor(log10($aVal));
+ $a = sprintf("%0.".$aPrecision."f",round($aVal / pow(10,$l),$aPrecision));
+ $a *= $neg;
+ if( $this->iSimple && ($a == 1 || $a==-1) ) $a = '';
+
+ if( $a != '' )
+ $this->t = $a.' * 10';
+ else {
+ if( $neg == 1 )
+ $this->t = '10';
+ else
+ $this->t = '-10';
+ }
+ $this->iSuper = $l;
+ }
+
+ function Set($aTxt,$aSuper="") {
+ $this->t = $aTxt;
+ $this->iSuper = $aSuper;
+ }
+
+ function SetSuperFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=8) {
+ $this->sfont_family = $aFontFam;
+ $this->sfont_style = $aFontStyle;
+ $this->sfont_size = $aFontSize;
+ }
+
+ // Total width of text
+ function GetWidth(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $w = $aImg->GetTextWidth($this->t);
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $w += $aImg->GetTextWidth($this->iSuper);
+ $w += $this->iSuperMargin;
+ return $w;
+ }
+
+ // Hight of font (approximate the height of the text)
+ function GetFontHeight(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetFontHeight();
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $h += $aImg->GetFontHeight();
+ return $h;
+ }
+
+ // Hight of text
+ function GetTextHeight(&$aImg) {
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $h = $aImg->GetTextHeight($this->t);
+ $aImg->SetFont($this->sfont_family,$this->sfont_style,$this->sfont_size);
+ $h += $aImg->GetTextHeight($this->iSuper);
+ return $h;
+ }
+
+ function Stroke($aImg,$ax=-1,$ay=-1) {
+
+ // To position the super script correctly we need different
+ // cases to handle the alignmewnt specified since that will
+ // determine how we can interpret the x,y coordinates
+
+ $w = parent::GetWidth($aImg);
+ $h = parent::GetTextHeight($aImg);
+ switch( $this->valign ) {
+ case 'top':
+ $sy = $this->y;
+ break;
+ case 'center':
+ $sy = $this->y - $h/2;
+ break;
+ case 'bottom':
+ $sy = $this->y - $h;
+ break;
+ default:
+ JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown vertical alignment for text');
+ exit();
+ }
+
+ switch( $this->halign ) {
+ case 'left':
+ $sx = $this->x + $w;
+ break;
+ case 'center':
+ $sx = $this->x + $w/2;
+ break;
+ case 'right':
+ $sx = $this->x;
+ break;
+ default:
+ JpGraphError::Raise('PANIC: Internal error in SuperScript::Stroke(). Unknown horizontal alignment for text');
+ exit();
+ }
+
+ $sx += $this->iSuperMargin;
+ $sy += $this->iVertOverlap;
+
+ // Should we automatically determine the font or
+ // has the user specified it explicetly?
+ if( $this->sfont_family == "" ) {
+ if( $this->font_family <= FF_FONT2 ) {
+ if( $this->font_family == FF_FONT0 ) {
+ $sff = FF_FONT0;
+ }
+ elseif( $this->font_family == FF_FONT1 ) {
+ if( $this->font_style == FS_NORMAL )
+ $sff = FF_FONT0;
+ else
+ $sff = FF_FONT1;
+ }
+ else {
+ $sff = FF_FONT1;
+ }
+ $sfs = $this->font_style;
+ $sfz = $this->font_size;
+ }
+ else {
+ // TTF fonts
+ $sff = $this->font_family;
+ $sfs = $this->font_style;
+ $sfz = floor($this->font_size*$this->iSuperScale);
+ if( $sfz < 8 ) $sfz = 8;
+ }
+ $this->sfont_family = $sff;
+ $this->sfont_style = $sfs;
+ $this->sfont_size = $sfz;
+ }
+ else {
+ $sff = $this->sfont_family;
+ $sfs = $this->sfont_style;
+ $sfz = $this->sfont_size;
+ }
+
+ parent::Stroke($aImg,$ax,$ay);
+
+
+ // For the builtin fonts we need to reduce the margins
+ // since the bounding bx reported for the builtin fonts
+ // are much larger than for the TTF fonts.
+ if( $sff <= FF_FONT2 ) {
+ $sx -= 2;
+ $sy += 3;
+ }
+
+ $aImg->SetTextAlign('left','bottom');
+ $aImg->SetFont($sff,$sfs,$sfz);
+ $aImg->PushColor($this->color);
+ $aImg->StrokeText($sx,$sy,$this->iSuper,$this->iSDir,'left');
+ $aImg->PopColor();
+ }
+}
+
+
+//===================================================
+// CLASS Grid
+// Description: responsible for drawing grid lines in graph
+//===================================================
+class Grid {
+ var $img;
+ var $scale;
+ var $grid_color='#DDDDDD',$grid_mincolor='#DDDDDD';
+ var $type="solid";
+ var $show=false, $showMinor=false,$weight=1;
+ var $fill=false,$fillcolor=array('#EFEFEF','#BBCCFF');
+//---------------
+// CONSTRUCTOR
+ function Grid(&$aAxis) {
+ $this->scale = &$aAxis->scale;
+ $this->img = &$aAxis->img;
+ }
+//---------------
+// PUBLIC METHODS
+ function SetColor($aMajColor,$aMinColor=false) {
+ $this->grid_color=$aMajColor;
+ if( $aMinColor === false )
+ $aMinColor = $aMajColor ;
+ $this->grid_mincolor = $aMinColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ // Specify if grid should be dashed, dotted or solid
+ function SetLineStyle($aType) {
+ $this->type = $aType;
+ }
+
+ // Decide if both major and minor grid should be displayed
+ function Show($aShowMajor=true,$aShowMinor=false) {
+ $this->show=$aShowMajor;
+ $this->showMinor=$aShowMinor;
+ }
+
+ function SetFill($aFlg=true,$aColor1='lightgray',$aColor2='lightblue') {
+ $this->fill = $aFlg;
+ $this->fillcolor = array( $aColor1, $aColor2 );
+ }
+
+ // Display the grid
+ function Stroke() {
+ if( $this->showMinor ) {
+ $tmp = $this->grid_color;
+ $this->grid_color = $this->grid_mincolor;
+ $this->DoStroke($this->scale->ticks->ticks_pos);
+
+ $this->grid_color = $tmp;
+ $this->DoStroke($this->scale->ticks->maj_ticks_pos);
+ }
+ else {
+ $this->DoStroke($this->scale->ticks->maj_ticks_pos);
+ }
+ }
+
+//--------------
+// Private methods
+ // Draw the grid
+ function DoStroke(&$aTicksPos) {
+ if( !$this->show )
+ return;
+ $nbrgrids = count($aTicksPos);
+
+ if( $this->scale->type=="y" ) {
+ $xl=$this->img->left_margin;
+ $xr=$this->img->width-$this->img->right_margin;
+
+ if( $this->fill ) {
+ // Draw filled areas
+ $y2 = $aTicksPos[0];
+ $i=1;
+ while( $i < $nbrgrids ) {
+ $y1 = $y2;
+ $y2 = $aTicksPos[$i++];
+ $this->img->SetColor($this->fillcolor[$i & 1]);
+ $this->img->FilledRectangle($xl,$y1,$xr,$y2);
+ }
+ }
+
+ $this->img->SetColor($this->grid_color);
+ $this->img->SetLineWeight($this->weight);
+
+ // Draw grid lines
+ for($i=0; $i<$nbrgrids; ++$i) {
+ $y=$aTicksPos[$i];
+ if( $this->type == "solid" )
+ $this->img->Line($xl,$y,$xr,$y);
+ elseif( $this->type == "dotted" )
+ $this->img->DashedLine($xl,$y,$xr,$y,1,6);
+ elseif( $this->type == "dashed" )
+ $this->img->DashedLine($xl,$y,$xr,$y,2,4);
+ elseif( $this->type == "longdashed" )
+ $this->img->DashedLine($xl,$y,$xr,$y,8,6);
+ }
+ }
+ elseif( $this->scale->type=="x" ) {
+ $yu=$this->img->top_margin;
+ $yl=$this->img->height-$this->img->bottom_margin;
+ $limit=$this->img->width-$this->img->right_margin;
+
+ if( $this->fill ) {
+ // Draw filled areas
+ $x2 = $aTicksPos[0];
+ $i=1;
+ while( $i < $nbrgrids ) {
+ $x1 = $x2;
+ $x2 = min($aTicksPos[$i++],$limit) ;
+ $this->img->SetColor($this->fillcolor[$i & 1]);
+ $this->img->FilledRectangle($x1,$yu,$x2,$yl);
+ }
+ }
+
+ $this->img->SetColor($this->grid_color);
+ $this->img->SetLineWeight($this->weight);
+
+ // We must also test for limit since we might have
+ // an offset and the number of ticks is calculated with
+ // assumption offset==0 so we might end up drawing one
+ // to many gridlines
+ $i=0;
+ $x=$aTicksPos[$i];
+ while( $i<count($aTicksPos) && ($x=$aTicksPos[$i]) <= $limit ) {
+ if( $this->type == "solid" )
+ $this->img->Line($x,$yl,$x,$yu);
+ elseif( $this->type == "dotted" )
+ $this->img->DashedLine($x,$yl,$x,$yu,1,6);
+ elseif( $this->type == "dashed" )
+ $this->img->DashedLine($x,$yl,$x,$yu,2,4);
+ elseif( $this->type == "longdashed" )
+ $this->img->DashedLine($x,$yl,$x,$yu,8,6);
+ ++$i;
+ }
+ }
+ else {
+ JpGraphError::Raise('Internal error: Unknown grid axis ['.$this->scale->type.']');
+ }
+ return true;
+ }
+} // Class
+
+//===================================================
+// CLASS Axis
+// Description: Defines X and Y axis. Notes that at the
+// moment the code is not really good since the axis on
+// several occasion must know wheter it's an X or Y axis.
+// This was a design decision to make the code easier to
+// follow.
+//===================================================
+class Axis {
+ var $pos = false;
+ var $weight=1;
+ var $color=array(0,0,0),$label_color=array(0,0,0);
+ var $img=null,$scale=null;
+ var $hide=false;
+ var $ticks_label=false, $ticks_label_colors=null;
+ var $show_first_label=true,$show_last_label=true;
+ var $label_step=1; // Used by a text axis to specify what multiple of major steps
+ // should be labeled.
+ var $tick_step=1;
+ var $labelPos=0; // Which side of the axis should the labels be?
+ var $title=null,$title_adjust,$title_margin,$title_side=SIDE_LEFT;
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12,$label_angle=0;
+ var $tick_label_margin=5;
+ var $label_halign = '',$label_valign = '', $label_para_align='left';
+ var $hide_line=false,$hide_labels=false;
+ //var $hide_zero_label=false;
+
+//---------------
+// CONSTRUCTOR
+ function Axis(&$img,&$aScale,$color=array(0,0,0)) {
+ $this->img = &$img;
+ $this->scale = &$aScale;
+ $this->color = $color;
+ $this->title=new Text("");
+
+ if( $aScale->type=="y" ) {
+ $this->title_margin = 25;
+ $this->title_adjust="middle";
+ $this->title->SetOrientation(90);
+ $this->tick_label_margin=7;
+ $this->labelPos=SIDE_LEFT;
+ //$this->SetLabelFormat('%.1f');
+ }
+ else {
+ $this->title_margin = 5;
+ $this->title_adjust="high";
+ $this->title->SetOrientation(0);
+ $this->tick_label_margin=3;
+ $this->labelPos=SIDE_DOWN;
+ //$this->SetLabelFormat('%.0f');
+ }
+ }
+//---------------
+// PUBLIC METHODS
+
+ function SetLabelFormat($aFormStr) {
+ $this->scale->ticks->SetLabelFormat($aFormStr);
+ }
+
+ function SetLabelFormatString($aFormStr) {
+ $this->scale->ticks->SetLabelFormat($aFormStr);
+ }
+
+ function SetLabelFormatCallback($aFuncName) {
+ $this->scale->ticks->SetFormatCallback($aFuncName);
+ }
+
+ function SetLabelAlign($aHAlign,$aVAlign="top",$aParagraphAlign='left') {
+ $this->label_halign = $aHAlign;
+ $this->label_valign = $aVAlign;
+ $this->label_para_align = $aParagraphAlign;
+ }
+
+ // Don't display the first label
+ function HideFirstTickLabel($aShow=false) {
+ $this->show_first_label=$aShow;
+ }
+
+ function HideLastTickLabel($aShow=false) {
+ $this->show_last_label=$aShow;
+ }
+
+ function HideTicks($aHideMinor=true,$aHideMajor=true) {
+ $this->scale->ticks->SupressMinorTickMarks($aHideMinor);
+ $this->scale->ticks->SupressTickMarks($aHideMajor);
+ }
+
+ // Hide zero label
+ function HideZeroLabel($aFlag=true) {
+ $this->scale->ticks->SupressZeroLabel();
+ //$this->hide_zero_label = $aFlag;
+ }
+
+ function HideFirstLastLabel() {
+ // The two first calls to ticks method will supress
+ // automatically generated scale values. However, that
+ // will not affect manually specified value, e.g text-scales.
+ // therefor we also make a kludge here to supress manually
+ // specified scale labels.
+ $this->scale->ticks->SupressLast();
+ $this->scale->ticks->SupressFirst();
+ $this->show_first_label = false;
+ $this->show_last_label = false;
+ }
+
+ // Hide the axis
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ // Hide the actual axis-line, but still print the labels
+ function HideLine($aHide=true) {
+ $this->hide_line = $aHide;
+ }
+
+ function HideLabels($aHide=true) {
+ $this->hide_labels = $aHide;
+ }
+
+
+ // Weight of axis
+ function SetWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ // Axis color
+ function SetColor($aColor,$aLabelColor=false) {
+ $this->color = $aColor;
+ if( !$aLabelColor ) $this->label_color = $aColor;
+ else $this->label_color = $aLabelColor;
+ }
+
+ // Title on axis
+ function SetTitle($aTitle,$aAdjustAlign="high") {
+ $this->title->Set($aTitle);
+ $this->title_adjust=$aAdjustAlign;
+ }
+
+ // Specify distance from the axis
+ function SetTitleMargin($aMargin) {
+ $this->title_margin=$aMargin;
+ }
+
+ // Which side of the axis should the axis title be?
+ function SetTitleSide($aSideOfAxis) {
+ $this->title_side = $aSideOfAxis;
+ }
+
+ // Utility function to set the direction for tick marks
+ function SetTickDirection($aDir) {
+ // Will be deprecated from 1.7
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('Axis::SetTickDirection() is deprecated. Use Axis::SetTickSide() instead');
+ $this->scale->ticks->SetSide($aDir);
+ }
+
+ function SetTickSide($aDir) {
+ $this->scale->ticks->SetSide($aDir);
+ }
+
+ // Specify text labels for the ticks. One label for each data point
+ function SetTickLabels($aLabelArray,$aLabelColorArray=null) {
+ $this->ticks_label = $aLabelArray;
+ $this->ticks_label_colors = $aLabelColorArray;
+ }
+
+ // How far from the axis should the labels be drawn
+ function SetTickLabelMargin($aMargin) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('SetTickLabelMargin() is deprecated. Use Axis::SetLabelMargin() instead.');
+ $this->tick_label_margin=$aMargin;
+ }
+
+ function SetLabelMargin($aMargin) {
+ $this->tick_label_margin=$aMargin;
+ }
+
+ // Specify that every $step of the ticks should be displayed starting
+ // at $start
+ // DEPRECATED FUNCTION: USE SetTextTickInterval() INSTEAD
+ function SetTextTicks($step,$start=0) {
+ JpGraphError::Raise(" SetTextTicks() is deprecated. Use SetTextTickInterval() instead.");
+ }
+
+ // Specify that every $step of the ticks should be displayed starting
+ // at $start
+ function SetTextTickInterval($aStep,$aStart=0) {
+ $this->scale->ticks->SetTextLabelStart($aStart);
+ $this->tick_step=$aStep;
+ }
+
+ // Specify that every $step tick mark should have a label
+ // should be displayed starting
+ function SetTextLabelInterval($aStep) {
+ if( $aStep < 1 )
+ JpGraphError::Raise(" Text label interval must be specified >= 1.");
+ $this->label_step=$aStep;
+ }
+
+ // Which side of the axis should the labels be on?
+ function SetLabelPos($aSidePos) {
+ // This will be deprecated from 1.7
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('SetLabelPos() is deprecated. Use Axis::SetLabelSide() instead.');
+ $this->labelPos=$aSidePos;
+ }
+
+ function SetLabelSide($aSidePos) {
+ $this->labelPos=$aSidePos;
+ }
+
+ // Set the font
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family = $aFamily;
+ $this->font_style = $aStyle;
+ $this->font_size = $aSize;
+ }
+
+ // Position for axis line on the "other" scale
+ function SetPos($aPosOnOtherScale) {
+ $this->pos=$aPosOnOtherScale;
+ }
+
+ // Specify the angle for the tick labels
+ function SetLabelAngle($aAngle) {
+ $this->label_angle = $aAngle;
+ }
+
+ // Stroke the axis.
+ function Stroke($aOtherAxisScale) {
+ if( $this->hide ) return;
+ if( is_numeric($this->pos) ) {
+ $pos=$aOtherAxisScale->Translate($this->pos);
+ }
+ else { // Default to minimum of other scale if pos not set
+ if( ($aOtherAxisScale->GetMinVal() >= 0 && $this->pos==false) || $this->pos=="min" ) {
+ $pos = $aOtherAxisScale->scale_abs[0];
+ }
+ elseif($this->pos == "max") {
+ $pos = $aOtherAxisScale->scale_abs[1];
+ }
+ else { // If negative set x-axis at 0
+ $this->pos=0;
+ $pos=$aOtherAxisScale->Translate(0);
+ }
+ }
+ $this->img->SetLineWeight($this->weight);
+ $this->img->SetColor($this->color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ if( $this->scale->type == "x" ) {
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($this->img->left_margin,$pos,
+ $this->img->width-$this->img->right_margin,$pos+$this->weight-1);
+ $y=$pos+$this->img->GetFontHeight()+$this->title_margin+$this->title->margin;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($this->img->width-$this->img->right_margin,$y,"right","top");
+ elseif( $this->title_adjust=="middle" || $this->title_adjust=="center" )
+ $this->title->Pos(($this->img->width-$this->img->left_margin-$this->img->right_margin)/2+$this->img->left_margin,$y,"center","top");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($this->img->left_margin,$y,"left","top");
+ else {
+ JpGraphError::Raise('Unknown alignment specified for X-axis title. ('.$this->title_adjust.')');
+ }
+ }
+ elseif( $this->scale->type == "y" ) {
+ // Add line weight to the height of the axis since
+ // the x-axis could have a width>1 and we want the axis to fit nicely together.
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($pos-$this->weight+1,$this->img->top_margin,
+ $pos,$this->img->height-$this->img->bottom_margin+$this->weight-1);
+ $x=$pos ;
+ if( $this->title_side == SIDE_LEFT ) {
+ $x -= $this->title_margin;
+ $x -= $this->title->margin;
+ $halign="right";
+ }
+ else {
+ $x += $this->title_margin;
+ $x += $this->title->margin;
+ $halign="left";
+ }
+ // If the user has manually specified an hor. align
+ // then we override the automatic settings with this
+ // specifed setting. Since default is 'left' we compare
+ // with that. (This means a manually set 'left' align
+ // will have no effect.)
+ if( $this->title->halign != 'left' )
+ $halign = $this->title->halign;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($x,$this->img->top_margin,$halign,"top");
+ elseif($this->title_adjust=="middle" || $this->title_adjust=="center")
+ $this->title->Pos($x,($this->img->height-$this->img->top_margin-$this->img->bottom_margin)/2+$this->img->top_margin,$halign,"center");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($x,$this->img->height-$this->img->bottom_margin,$halign,"bottom");
+ else
+ JpGraphError::Raise('Unknown alignment specified for Y-axis title. ('.$this->title_adjust.')');
+
+ }
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+ if( !$this->hide_labels ) {
+ $this->StrokeLabels($pos);
+ }
+ $this->title->Stroke($this->img);
+ }
+
+//---------------
+// PRIVATE METHODS
+ // Draw all the tick labels on major tick marks
+ function StrokeLabels($aPos,$aMinor=false,$aAbsLabel=false) {
+
+ $this->img->SetColor($this->label_color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $yoff=$this->img->GetFontHeight()/2;
+
+ // Only draw labels at major tick marks
+ $nbr = count($this->scale->ticks->maj_ticks_label);
+
+ // We have the option to not-display the very first mark
+ // (Usefull when the first label might interfere with another
+ // axis.)
+ $i = $this->show_first_label ? 0 : 1 ;
+ if( !$this->show_last_label ) --$nbr;
+ // Now run through all labels making sure we don't overshoot the end
+ // of the scale.
+ $ncolor=0;
+ if( isset($this->ticks_label_colors) )
+ $ncolor=count($this->ticks_label_colors);
+
+ while( $i<$nbr ) {
+ // $tpos holds the absolute text position for the label
+ $tpos=$this->scale->ticks->maj_ticklabels_pos[$i];
+
+ // Note. the $limit is only used for the x axis since we
+ // might otherwise overshoot if the scale has been centered
+ // This is due to us "loosing" the last tick mark if we center.
+ if( $this->scale->type=="x" && $tpos > $this->img->width-$this->img->right_margin+1 ) {
+ return;
+ }
+ // we only draw every $label_step label
+ if( ($i % $this->label_step)==0 ) {
+
+ // Set specific label color if specified
+ if( $ncolor > 0 )
+ $this->img->SetColor($this->ticks_label_colors[$i % $ncolor]);
+
+ // If the label has been specified use that and in other case
+ // just label the mark with the actual scale value
+ $m=$this->scale->ticks->GetMajor();
+
+ // ticks_label has an entry for each data point and is the array
+ // that holds the labels set by the user. If the user hasn't
+ // specified any values we use whats in the automatically asigned
+ // labels in the maj_ticks_label
+ if( isset($this->ticks_label[$i*$m]) )
+ $label=$this->ticks_label[$i*$m];
+ else {
+ if( $aAbsLabel )
+ $label=abs($this->scale->ticks->maj_ticks_label[$i]);
+ else
+ $label=$this->scale->ticks->maj_ticks_label[$i];
+ if( $this->scale->textscale && $this->scale->ticks->label_formfunc == '' ) {
+ ++$label;
+ }
+ }
+
+ //if( $this->hide_zero_label && $label==0.0 ) {
+ // ++$i;
+ // continue;
+ //}
+
+ if( $this->scale->type == "x" ) {
+ if( $this->labelPos == SIDE_DOWN ) {
+ if( $this->label_angle==0 || $this->label_angle==90 ) {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign('center','top');
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+
+ }
+ else {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","top");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+
+ $this->img->StrokeText($tpos,$aPos+$this->tick_label_margin,$label,
+ $this->label_angle,$this->label_para_align);
+ }
+ else {
+ if( $this->label_angle==0 || $this->label_angle==90 ) {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("center","bottom");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+ else {
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","bottom");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ }
+ $this->img->StrokeText($tpos,$aPos-$this->tick_label_margin,$label,
+ $this->label_angle,$this->label_para_align);
+ }
+ }
+ else {
+ // scale->type == "y"
+ //if( $this->label_angle!=0 )
+ //JpGraphError::Raise(" Labels at an angle are not supported on Y-axis");
+ if( $this->labelPos == SIDE_LEFT ) { // To the left of y-axis
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("right","center");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ $this->img->StrokeText($aPos-$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
+ }
+ else { // To the right of the y-axis
+ if( $this->label_halign=='' && $this->label_valign=='')
+ $this->img->SetTextAlign("left","center");
+ else
+ $this->img->SetTextAlign($this->label_halign,$this->label_valign);
+ $this->img->StrokeText($aPos+$this->tick_label_margin,$tpos,$label,$this->label_angle,$this->label_para_align);
+ }
+ }
+ }
+ ++$i;
+ }
+ }
+
+} // Class
+
+//===================================================
+// CLASS Ticks
+// Description: Abstract base class for drawing linear and logarithmic
+// tick marks on axis
+//===================================================
+class Ticks {
+ var $minor_abs_size=3, $major_abs_size=5;
+ var $direction=1; // Should ticks be in(=1) the plot area or outside (=-1)?
+ var $scale;
+ var $is_set=false;
+ var $precision;
+ var $supress_zerolabel=false,$supress_first=false;
+ var $supress_last=false,$supress_tickmarks=false,$supress_minor_tickmarks=false;
+ var $mincolor="",$majcolor="";
+ var $weight=1;
+ var $label_formatstr=''; // C-style format string to use for labels
+ var $label_formfunc='';
+
+
+//---------------
+// CONSTRUCTOR
+ function Ticks(&$aScale) {
+ $this->scale=&$aScale;
+ $this->precision = -1;
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Set format string for automatic labels
+ function SetLabelFormat($aFormatString) {
+ $this->label_formatstr=$aFormatString;
+ }
+
+ function SetFormatCallback($aCallbackFuncName) {
+ $this->label_formfunc = $aCallbackFuncName;
+ }
+
+ // Don't display the first zero label
+ function SupressZeroLabel($aFlag=true) {
+ $this->supress_zerolabel=$aFlag;
+ }
+
+ // Don't display minor tick marks
+ function SupressMinorTickMarks($aHide=true) {
+ $this->supress_minor_tickmarks=$aHide;
+ }
+
+ // Don't display major tick marks
+ function SupressTickMarks($aHide=true) {
+ $this->supress_tickmarks=$aHide;
+ }
+
+ // Hide the first tick mark
+ function SupressFirst($aHide=true) {
+ $this->supress_first=$aHide;
+ }
+
+ // Hide the last tick mark
+ function SupressLast($aHide=true) {
+ $this->supress_last=$aHide;
+ }
+
+ // Size (in pixels) of minor tick marks
+ function GetMinTickAbsSize() {
+ return $this->minor_abs_size;
+ }
+
+ // Size (in pixels) of major tick marks
+ function GetMajTickAbsSize() {
+ return $this->major_abs_size;
+ }
+
+ function SetSize($aMajSize,$aMinSize=3) {
+ $this->major_abs_size = $aMajSize;
+ $this->minor_abs_size = $aMinSize;
+ }
+
+ // Have the ticks been specified
+ function IsSpecified() {
+ return $this->is_set;
+ }
+
+ // Set the distance between major and minor tick marks
+ function Set($aMaj,$aMin) {
+ // "Virtual method"
+ // Should be implemented by the concrete subclass
+ // if any action is wanted.
+ }
+
+ // Specify number of decimals in automatic labels
+ // Deprecated from 1.4. Use SetFormatString() instead
+ function SetPrecision($aPrecision) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('Ticks::SetPrecision() is deprecated. Use Ticks::SetLabelFormat() (or Ticks::SetFormatCallback()) instead');
+ $this->precision=$aPrecision;
+ }
+
+ function SetSide($aSide) {
+ $this->direction=$aSide;
+ }
+
+ // Which side of the axis should the ticks be on
+ function SetDirection($aSide=SIDE_RIGHT) {
+ $this->direction=$aSide;
+ }
+
+ // Set colors for major and minor tick marks
+ function SetMarkColor($aMajorColor,$aMinorColor="") {
+ $this->SetColor($aMajorColor,$aMinorColor);
+ }
+
+ function SetColor($aMajorColor,$aMinorColor="") {
+ $this->majcolor=$aMajorColor;
+
+ // If not specified use same as major
+ if( $aMinorColor=="" )
+ $this->mincolor=$aMajorColor;
+ else
+ $this->mincolor=$aMinorColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+} // Class
+
+//===================================================
+// CLASS LinearTicks
+// Description: Draw linear ticks on axis
+//===================================================
+class LinearTicks extends Ticks {
+ var $minor_step=1, $major_step=2;
+ var $xlabel_offset=0,$xtick_offset=0;
+ var $label_offset=0; // What offset should the displayed label have
+ // i.e should we display 0,1,2 or 1,2,3,4 or 2,3,4 etc
+ var $text_label_start=0;
+//---------------
+// CONSTRUCTOR
+ function LinearTicks() {
+ $this->precision = -1;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+
+ // Return major step size in world coordinates
+ function GetMajor() {
+ return $this->major_step;
+ }
+
+ // Return minor step size in world coordinates
+ function GetMinor() {
+ return $this->minor_step;
+ }
+
+ // Set Minor and Major ticks (in world coordinates)
+ function Set($aMajStep,$aMinStep=false) {
+ if( $aMinStep==false )
+ $aMinStep=$aMajStep;
+
+ if( $aMajStep <= 0 || $aMinStep <= 0 ) {
+ JpGraphError::Raise(" Minor or major step size is 0. Check that you haven't
+ got an accidental SetTextTicks(0) in your code.<p>
+ If this is not the case you might have stumbled upon a bug in JpGraph.
+ Please report this and if possible include the data that caused the
+ problem.");
+ }
+
+ $this->major_step=$aMajStep;
+ $this->minor_step=$aMinStep;
+ $this->is_set = true;
+ }
+
+ // Draw linear ticks
+ function Stroke(&$img,&$scale,$pos) {
+ $maj_step_abs = $scale->scale_factor*$this->major_step;
+ $min_step_abs = $scale->scale_factor*$this->minor_step;
+
+ if( $min_step_abs==0 || $maj_step_abs==0 )
+ JpGraphError::Raise(" A plot has an illegal scale. This could for example be that you are trying to use text autoscaling to draw a line plot with only one point or that the plot area is too small. Try increasing the graph size or correct the lineplot.");
+ $limit = $scale->scale_abs[1];
+ $nbrmajticks=floor(1.000001*(($scale->GetMaxVal()-$scale->GetMinVal())/$this->major_step))+1;
+ $first=0;
+
+ // If precision hasn't been specified set it to a sensible value
+ if( $this->precision==-1 ) {
+ $t = log10($this->minor_step);
+ if( $t > 0 )
+ $precision = 0;
+ else
+ $precision = -floor($t);
+ }
+ else
+ $precision = $this->precision;
+
+ $img->SetLineWeight($this->weight);
+
+ // Handle ticks on X-axis
+ if( $scale->type == "x" ) {
+
+ // Draw the minor tick marks
+
+ $yu = $pos - $this->direction*$this->GetMinTickAbsSize();
+ $label = $scale->GetMinVal();
+ $x=$scale->scale_abs[0];
+ $i=0;
+ $j=0;
+ $step = round($maj_step_abs/$min_step_abs);
+ while( $x < $limit ) {
+ $this->ticks_pos[]=$x;
+ $this->ticks_label[]=$label;
+ $label+=$this->minor_step;
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($x,$pos,$x,$yu);
+ if( $this->mincolor!="" ) $img->PopColor();
+ }
+
+ if( $i % $step == 0 ) {
+ $this->maj_ticks_pos[$j]=round($x);//$xtick;
+ ++$j;
+ }
+
+ ++$i;
+ $x += $min_step_abs;
+
+ }
+ $this->maj_ticks_pos[$j]=$x;
+
+ // Draw the major tick marks
+
+ $yu = $pos - $this->direction*$this->GetMajTickAbsSize();
+
+ // TODO: Add logic to set label_offset for text labels
+ $label = (float)$scale->GetMinVal()+$this->text_label_start+$this->label_offset;
+
+ $start_abs=$scale->scale_factor*$this->text_label_start;
+
+ $nbrmajticks=ceil(($scale->GetMaxVal()-$scale->GetMinVal()-$this->text_label_start )/$this->major_step)+1;
+
+
+ $x = $scale->scale_abs[0]+$start_abs+$this->xlabel_offset*$min_step_abs;
+ for( $i=0; $label <= $scale->GetMaxVal()+$this->label_offset; ++$i ) {
+
+ // Apply format
+ if( $this->label_formfunc != "" ) {
+ $f=$this->label_formfunc;
+ $l = call_user_func($f,$label);
+ }
+ elseif( $this->label_formatstr != '' )
+ $l = sprintf($this->label_formatstr,$label);
+ else {
+ $v = round($label,$precision);
+ $l = sprintf("%01.".$precision."f",$v);
+ }
+
+ if( ($this->supress_zerolabel && $l==0) ||
+ ($this->supress_first && $i==0) ||
+ ($this->supress_last && $i==$nbrmajticks-1) ) {
+ $l="";
+ }
+
+ $this->maj_ticks_label[$i]=$l;
+ $label+=$this->major_step;
+ $this->maj_ticklabels_pos[$i] = $x;
+// $this->maj_ticklabels_pos[$i] = $this->maj_ticks_pos[$i];
+
+ // The x-position of the tick marks can be different from the labels.
+ // Note that we record the tick position (not the label) so that the grid
+ // happen upon tick marks and not labels.
+ $xtick=$scale->scale_abs[0]+$start_abs+$this->xtick_offset*$min_step_abs+$i*$maj_step_abs;
+ $this->maj_ticks_pos[$i]=$xtick;
+ if(!($this->xtick_offset > 0 && $i==$nbrmajticks-1) &&
+ !$this->supress_tickmarks) {
+ if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
+ $img->Line($this->maj_ticks_pos[$i],$pos,$this->maj_ticks_pos[$i],$yu);
+ if( $this->majcolor!="" ) $img->PopColor();
+ }
+
+ $x += $maj_step_abs;
+ }
+
+
+ }
+ elseif( $scale->type == "y" ) {
+
+ // Draw the major tick marks
+ $xr = $pos + $this->direction*$this->GetMajTickAbsSize();
+ $label = $scale->GetMinVal();
+
+ $tmpmaj=array();
+ $tmpmin=array();
+
+ for( $i=0; $i<$nbrmajticks; ++$i) {
+ $y=$scale->scale_abs[0]+$i*$maj_step_abs;
+
+ $tmpmaj[]=$y;
+
+
+ // THe following two lines might seem to be unecessary but they are not!
+ // The reason being that for X-axis we separate the position of the labels
+ // and the tick marks which we don't do for the Y-axis.
+ // We therefore need to make sure both arrays are corcectly filled
+ // since Axis::StrokeLabels() uses the label positions and Grid::Stroke() uses
+ // the tick positions.
+ $this->maj_ticklabels_pos[$i]=$y;
+ $this->maj_ticks_pos[$i]=$y;
+
+ if( $this->label_formfunc != "" ) {
+ $f=$this->label_formfunc;
+ $l = call_user_func($f,$label);
+ }
+ elseif( $this->label_formatstr != "" )
+ $l = sprintf($this->label_formatstr,$label);
+ else
+ $l = sprintf("%01.".$precision."f",round($label,$precision));
+
+ if( ($this->supress_zerolabel && ($l + 0)==0) || ($this->supress_first && $i==0) ||
+ ($this->supress_last && $i==$nbrmajticks-1) ) {
+ $l="";
+ }
+
+ $this->maj_ticks_label[$i]=$l;
+ $label+=$this->major_step;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $img->PushColor($this->majcolor);
+ $img->Line($pos,$y,$xr,$y);
+ if( $this->majcolor!="" ) $img->PopColor();
+ }
+ }
+
+ // Draw the minor tick marks
+ $xr = $pos + $this->direction*$this->GetMinTickAbsSize();
+ $label = $scale->GetMinVal();
+ for( $i=0,$y=$scale->scale_abs[0]; $y>=$limit; ) {
+
+ $tmpmin[]=$y;
+
+ $this->ticks_pos[$i]=$y;
+ $this->ticks_label[$i]=$label;
+ $label+=$this->minor_step;
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($pos,$y,$xr,$y);
+ if( $this->mincolor!="" ) $img->PopColor();
+ }
+ ++$i;
+ $y=$scale->scale_abs[0]+$i*$min_step_abs;
+ }
+ }
+ }
+//---------------
+// PRIVATE METHODS
+ // Spoecify the offset of the displayed tick mark with the tick "space"
+ // Legal values for $o is [0,1] used to adjust where the tick marks and label
+ // should be positioned within the major tick-size
+ // $lo specifies the label offset and $to specifies the tick offset
+ // this comes in handy for example in bar graphs where we wont no offset for the
+ // tick but have the labels displayed halfway under the bars.
+ function SetXLabelOffset($aLabelOff,$aTickOff=-1) {
+ $this->xlabel_offset=$aLabelOff;
+ if( $aTickOff==-1 ) // Same as label offset
+ $this->xtick_offset=$aLabelOff;
+ else
+ $this->xtick_offset=$aTickOff;
+ if( $aLabelOff>0 )
+ $this->SupressLast(); // The last tick wont fit
+ }
+
+ // Which tick label should we start with?
+ function SetTextLabelStart($aTextLabelOff) {
+ $this->text_label_start=$aTextLabelOff;
+ }
+
+} // Class
+
+//===================================================
+// CLASS LinearScale
+// Description: Handle linear scaling between screen and world
+//===================================================
+class LinearScale {
+ var $scale=array(0,0);
+ var $scale_abs=array(0,0);
+ var $scale_factor; // Scale factor between world and screen
+ var $world_size; // Plot area size in world coordinates
+ var $world_abs_size; // Plot area size in pixels
+ var $off; // Offset between image edge and plot area
+ var $type; // is this x or y scale ?
+ var $ticks=null; // Store ticks
+ var $autoscale_min=false; // Forced minimum value, auto determine max
+ var $autoscale_max=false; // Forced maximum value, auto determine min
+ var $gracetop=0,$gracebottom=0;
+ var $intscale=false; // Restrict autoscale to integers
+ var $textscale=false; // Just a flag to let the Plot class find out if
+ // we are a textscale or not. This is a cludge since
+ // this ionformatyion is availabale in Graph::axtype but
+ // we don't have access to the graph object in the Plots
+ // stroke method. So we let graph store the status here
+ // when the linear scale is created. A real cludge...
+ var $text_scale_off = 0;
+ var $auto_ticks=false; // When using manual scale should the ticks be automatically set?
+ var $name = 'lin';
+//---------------
+// CONSTRUCTOR
+ function LinearScale($aMin=0,$aMax=0,$aType="y") {
+ assert($aType=="x" || $aType=="y" );
+ assert($aMin<=$aMax);
+
+ $this->type=$aType;
+ $this->scale=array($aMin,$aMax);
+ $this->world_size=$aMax-$aMin;
+ $this->ticks = new LinearTicks();
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Second phase constructor
+ function Init(&$aImg) {
+ $this->InitConstants($aImg);
+ // We want image to notify us when the margins changes so we
+ // can recalculate the constants.
+ // PHP <= 4.04 BUGWARNING: IT IS IMPOSSIBLE TO DO THIS IN THE CONSTRUCTOR
+ // SINCE (FOR SOME REASON) IT IS IMPOSSIBLE TO PASS A REFERENCE
+ // TO 'this' INSTEAD IT WILL ADD AN ANONYMOUS COPY OF THIS OBJECT WHICH WILL
+ // GET ALL THE NOTIFICATIONS. (This took a while to track down...)
+
+ // Add us as an observer to class Image
+ $aImg->AddObserver("InitConstants",$this);
+ }
+
+ // Check if scale is set or if we should autoscale
+ // We should do this is either scale or ticks has not been set
+ function IsSpecified() {
+ if( $this->GetMinVal()==$this->GetMaxVal() ) { // Scale not set
+ return false;
+ }
+ return true;
+ }
+
+ // Set the minimum data value when the autoscaling is used.
+ // Usefull if you want a fix minimum (like 0) but have an
+ // automatic maximum
+ function SetAutoMin($aMin) {
+ $this->autoscale_min=$aMin;
+ }
+
+ // Set the minimum data value when the autoscaling is used.
+ // Usefull if you want a fix minimum (like 0) but have an
+ // automatic maximum
+ function SetAutoMax($aMax) {
+ $this->autoscale_max=$aMax;
+ }
+
+ // If the user manually specifies a scale should the ticks
+ // still be set automatically?
+ function SetAutoTicks($aFlag=true) {
+ $this->auto_ticks = $aFlag;
+ }
+
+ // Specify scale "grace" value (top and bottom)
+ function SetGrace($aGraceTop,$aGraceBottom=0) {
+ if( $aGraceTop<0 || $aGraceBottom < 0 )
+ JpGraphError::Raise(" Grace must be larger then 0");
+ $this->gracetop=$aGraceTop;
+ $this->gracebottom=$aGraceBottom;
+ }
+
+ // Get the minimum value in the scale
+ function GetMinVal() {
+ return $this->scale[0];
+ }
+
+ // get maximum value for scale
+ function GetMaxVal() {
+ return $this->scale[1];
+ }
+
+ // Specify a new min/max value for sclae
+ function Update(&$aImg,$aMin,$aMax) {
+ $this->scale=array($aMin,$aMax);
+ $this->world_size=$aMax-$aMin;
+ $this->InitConstants($aImg);
+ }
+
+ // Translate between world and screen
+ function Translate($aCoord) {
+ if( !is_numeric($aCoord) ) {
+ if( $aCoord != '' && $aCoord != '-' )
+ JpGraphError::Raise('Your data contains non-numeric values.');
+ }
+ return $this->off+($aCoord - $this->GetMinVal()) * $this->scale_factor;
+ }
+
+ // Relative translate (don't include offset) usefull when we just want
+ // to know the relative position (in pixels) on the axis
+ function RelTranslate($aCoord) {
+ if( !is_numeric($aCoord) ) {
+ if( $aCoord != '' && $aCoord != '-' )
+ JpGraphError::Raise('Your data contains non-numeric values.');
+ }
+ return ($aCoord - $this->GetMinVal()) * $this->scale_factor;
+ }
+
+ // Restrict autoscaling to only use integers
+ function SetIntScale($aIntScale=true) {
+ $this->intscale=$aIntScale;
+ }
+
+ // Calculate an integer autoscale
+ function IntAutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
+ // Make sure limits are integers
+ $min=floor($min);
+ $max=ceil($max);
+ if( abs($min-$max)==0 ) {
+ --$min; ++$max;
+ }
+ $maxsteps = floor($maxsteps);
+
+ $gracetop=round(($this->gracetop/100.0)*abs($max-$min));
+ $gracebottom=round(($this->gracebottom/100.0)*abs($max-$min));
+ if( is_numeric($this->autoscale_min) ) {
+ $min = ceil($this->autoscale_min);
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
+ die();
+ }
+ }
+
+ if( is_numeric($this->autoscale_max) ) {
+ $max = ceil($this->autoscale_max);
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
+ die();
+ }
+ }
+
+ if( abs($min-$max ) == 0 ) {
+ ++$max;
+ --$min;
+ }
+
+ $min -= $gracebottom;
+ $max += $gracetop;
+
+ // First get tickmarks as multiples of 1, 10, ...
+ if( $majend ) {
+ list($num1steps,$adj1min,$adj1max,$maj1step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,1);
+ }
+ else {
+ $adj1min = $min;
+ $adj1max = $max;
+ list($num1steps,$maj1step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,1);
+ }
+
+ if( abs($min-$max) > 2 ) {
+ // Then get tick marks as 2:s 2, 20, ...
+ if( $majend ) {
+ list($num2steps,$adj2min,$adj2max,$maj2step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,5);
+ }
+ else {
+ $adj2min = $min;
+ $adj2max = $max;
+ list($num2steps,$maj2step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,5);
+ }
+ }
+ else {
+ $num2steps = 10000; // Dummy high value so we don't choose this
+ }
+
+ if( abs($min-$max) > 5 ) {
+ // Then get tickmarks as 5:s 5, 50, 500, ...
+ if( $majend ) {
+ list($num5steps,$adj5min,$adj5max,$maj5step) =
+ $this->IntCalcTicks($maxsteps,$min,$max,2);
+ }
+ else {
+ $adj5min = $min;
+ $adj5max = $max;
+ list($num5steps,$maj5step) =
+ $this->IntCalcTicksFreeze($maxsteps,$min,$max,2);
+ }
+ }
+ else {
+ $num5steps = 10000; // Dummy high value so we don't choose this
+ }
+
+ // Check to see whichof 1:s, 2:s or 5:s fit better with
+ // the requested number of major ticks
+ $match1=abs($num1steps-$maxsteps);
+ $match2=abs($num2steps-$maxsteps);
+ if( !empty($maj5step) && $maj5step > 1 )
+ $match5=abs($num5steps-$maxsteps);
+ else
+ $match5=10000; // Dummy high value
+
+ // Compare these three values and see which is the closest match
+ // We use a 0.6 weight to gravitate towards multiple of 5:s
+ if( $match1 < $match2 ) {
+ if( $match1 < $match5 )
+ $r=1;
+ else
+ $r=3;
+ }
+ else {
+ if( $match2 < $match5 )
+ $r=2;
+ else
+ $r=3;
+ }
+ // Minsteps are always the same as maxsteps for integer scale
+ switch( $r ) {
+ case 1:
+ $this->Update($img,$adj1min,$adj1max);
+ $this->ticks->Set($maj1step,$maj1step);
+ break;
+ case 2:
+ $this->Update($img,$adj2min,$adj2max);
+ $this->ticks->Set($maj2step,$maj2step);
+ break;
+ case 3:
+ $this->Update($img,$adj5min,$adj5max);
+ $this->ticks->Set($maj5step,$maj2step);
+ break;
+ }
+ }
+
+
+ // Calculate autoscale. Used if user hasn't given a scale and ticks
+ // $maxsteps is the maximum number of major tickmarks allowed.
+ function AutoScale(&$img,$min,$max,$maxsteps,$majend=true) {
+ if( $this->intscale ) {
+ $this->IntAutoScale($img,$min,$max,$maxsteps,$majend);
+ return;
+ }
+ if( abs($min-$max) < 0.00001 ) {
+ // We need some difference to be able to autoscale
+ // make it 5% above and 5% below value
+ if( $min==0 && $max==0 ) { // Special case
+ $min=-1; $max=1;
+ }
+ else {
+ $delta = (abs($max)+abs($min))*0.005;
+ $min -= $delta;
+ $max += $delta;
+ }
+ }
+
+ $gracetop=($this->gracetop/100.0)*abs($max-$min);
+ $gracebottom=($this->gracebottom/100.0)*abs($max-$min);
+ if( is_numeric($this->autoscale_min) ) {
+ $min = $this->autoscale_min;
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a min value with SetAutoMin() which is larger than the maximum value used for the scale. This is not possible.');
+ die();
+ }
+ if( abs($min-$max ) < 0.00001 )
+ $max *= 1.2;
+ }
+
+ if( is_numeric($this->autoscale_max) ) {
+ $max = $this->autoscale_max;
+ if( $min >= $max ) {
+ JpGraphError::Raise('You have specified a max value with SetAutoMax() which is smaller than the miminum value used for the scale. This is not possible.');
+ die();
+ }
+ if( abs($min-$max ) < 0.00001 )
+ $min *= 0.8;
+ }
+
+
+ $min -= $gracebottom;
+ $max += $gracetop;
+
+ // First get tickmarks as multiples of 0.1, 1, 10, ...
+ if( $majend ) {
+ list($num1steps,$adj1min,$adj1max,$min1step,$maj1step) =
+ $this->CalcTicks($maxsteps,$min,$max,1,2);
+ }
+ else {
+ $adj1min=$min;
+ $adj1max=$max;
+ list($num1steps,$min1step,$maj1step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,1,2,false);
+ }
+
+ // Then get tick marks as 2:s 0.2, 2, 20, ...
+ if( $majend ) {
+ list($num2steps,$adj2min,$adj2max,$min2step,$maj2step) =
+ $this->CalcTicks($maxsteps,$min,$max,5,2);
+ }
+ else {
+ $adj2min=$min;
+ $adj2max=$max;
+ list($num2steps,$min2step,$maj2step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,5,2,false);
+ }
+
+ // Then get tickmarks as 5:s 0.05, 0.5, 5, 50, ...
+ if( $majend ) {
+ list($num5steps,$adj5min,$adj5max,$min5step,$maj5step) =
+ $this->CalcTicks($maxsteps,$min,$max,2,5);
+ }
+ else {
+ $adj5min=$min;
+ $adj5max=$max;
+ list($num5steps,$min5step,$maj5step) =
+ $this->CalcTicksFreeze($maxsteps,$min,$max,2,5,false);
+ }
+
+ // Check to see whichof 1:s, 2:s or 5:s fit better with
+ // the requested number of major ticks
+ $match1=abs($num1steps-$maxsteps);
+ $match2=abs($num2steps-$maxsteps);
+ $match5=abs($num5steps-$maxsteps);
+ // Compare these three values and see which is the closest match
+ // We use a 0.8 weight to gravitate towards multiple of 5:s
+ $r=$this->MatchMin3($match1,$match2,$match5,0.8);
+ switch( $r ) {
+ case 1:
+ $this->Update($img,$adj1min,$adj1max);
+ $this->ticks->Set($maj1step,$min1step);
+ break;
+ case 2:
+ $this->Update($img,$adj2min,$adj2max);
+ $this->ticks->Set($maj2step,$min2step);
+ break;
+ case 3:
+ $this->Update($img,$adj5min,$adj5max);
+ $this->ticks->Set($maj5step,$min5step);
+ break;
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ // This method recalculates all constants that are depending on the
+ // margins in the image. If the margins in the image are changed
+ // this method should be called for every scale that is registred with
+ // that image. Should really be installed as an observer of that image.
+ function InitConstants(&$img) {
+ if( $this->type=="x" ) {
+ $this->world_abs_size=$img->width - $img->left_margin - $img->right_margin;
+ $this->off=$img->left_margin;
+ $this->scale_factor = 0;
+ if( $this->world_size > 0 )
+ $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
+ }
+ else { // y scale
+ $this->world_abs_size=$img->height - $img->top_margin - $img->bottom_margin;
+ $this->off=$img->top_margin+$this->world_abs_size;
+ $this->scale_factor = 0;
+ if( $this->world_size > 0 )
+ $this->scale_factor=-$this->world_abs_size/($this->world_size*1.0);
+ }
+ $size = $this->world_size * $this->scale_factor;
+ $this->scale_abs=array($this->off,$this->off + $size);
+ }
+
+ // Initialize the conversion constants for this scale
+ // This tries to pre-calculate as much as possible to speed up the
+ // actual conversion (with Translate()) later on
+ // $start =scale start in absolute pixels (for x-scale this is an y-position
+ // and for an y-scale this is an x-position
+ // $len =absolute length in pixels of scale
+ function SetConstants($aStart,$aLen) {
+ $this->world_abs_size=$aLen;
+ $this->off=$aStart;
+
+ if( $this->world_size<=0 ) {
+ // This should never ever happen !!
+ JpGraphError::Raise("JpGraph Fatal Error:<br>
+ You have unfortunately stumbled upon a bug in JpGraph. <br>
+ It seems like the scale range is ".$this->world_size." [for ".
+ $this->type." scale] <br>
+ Please report Bug #01 to jpgraph@aditus.nu and include the script
+ that gave this error. <br>
+ This problem could potentially be caused by trying to use \"illegal\"
+ values in the input data arrays (like trying to send in strings or
+ only NULL values) which causes the autoscaling to fail.");
+ }
+
+ // scale_factor = number of pixels per world unit
+ $this->scale_factor=$this->world_abs_size/($this->world_size*1.0);
+
+ // scale_abs = start and end points of scale in absolute pixels
+ $this->scale_abs=array($this->off,$this->off+$this->world_size*$this->scale_factor);
+ }
+
+
+ // Calculate number of ticks steps with a specific division
+ // $a is the divisor of 10**x to generate the first maj tick intervall
+ // $a=1, $b=2 give major ticks with multiple of 10, ...,0.1,1,10,...
+ // $a=5, $b=2 give major ticks with multiple of 2:s ...,0.2,2,20,...
+ // $a=2, $b=5 give major ticks with multiple of 5:s ...,0.5,5,50,...
+ // We return a vector of
+ // [$numsteps,$adjmin,$adjmax,$minstep,$majstep]
+ // If $majend==true then the first and last marks on the axis will be major
+ // labeled tick marks otherwise it will be adjusted to the closest min tick mark
+ function CalcTicks($maxsteps,$min,$max,$a,$b,$majend=true) {
+ $diff=$max-$min;
+ if( $diff==0 )
+ $ld=0;
+ else
+ $ld=floor(log10($diff));
+
+ // Gravitate min towards zero if we are close
+ if( $min>0 && $min < pow(10,$ld) ) $min=0;
+
+ //$majstep=pow(10,$ld-1)/$a;
+ $majstep=pow(10,$ld)/$a;
+ $minstep=$majstep/$b;
+
+ $adjmax=ceil($max/$minstep)*$minstep;
+ $adjmin=floor($min/$minstep)*$minstep;
+ $adjdiff = $adjmax-$adjmin;
+ $numsteps=$adjdiff/$majstep;
+
+ while( $numsteps>$maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=$adjdiff/$majstep;
+ ++$ld;
+ }
+
+ $minstep=$majstep/$b;
+ $adjmin=floor($min/$minstep)*$minstep;
+ $adjdiff = $adjmax-$adjmin;
+ if( $majend ) {
+ $adjmin = floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
+ }
+ else
+ $adjmax=ceil($max/$minstep)*$minstep;
+
+ return array($numsteps,$adjmin,$adjmax,$minstep,$majstep);
+ }
+
+ function CalcTicksFreeze($maxsteps,$min,$max,$a,$b) {
+ // Same as CalcTicks but don't adjust min/max values
+ $diff=$max-$min;
+ if( $diff==0 )
+ $ld=0;
+ else
+ $ld=floor(log10($diff));
+
+ //$majstep=pow(10,$ld-1)/$a;
+ $majstep=pow(10,$ld)/$a;
+ $minstep=$majstep/$b;
+ $numsteps=floor($diff/$majstep);
+
+ while( $numsteps > $maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=floor($diff/$majstep);
+ ++$ld;
+ }
+ $minstep=$majstep/$b;
+ return array($numsteps,$minstep,$majstep);
+ }
+
+
+ function IntCalcTicks($maxsteps,$min,$max,$a,$majend=true) {
+ $diff=$max-$min;
+ if( $diff==0 )
+ JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
+ else
+ $ld=floor(log10($diff));
+
+ // Gravitate min towards zero if we are close
+ if( $min>0 && $min < pow(10,$ld) ) $min=0;
+
+ if( $ld == 0 ) $ld=1;
+
+ if( $a == 1 )
+ $majstep = 1;
+ else
+ $majstep=pow(10,$ld)/$a;
+ $adjmax=ceil($max/$majstep)*$majstep;
+
+ $adjmin=floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $numsteps=$adjdiff/$majstep;
+ while( $numsteps>$maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=$adjdiff/$majstep;
+ ++$ld;
+ }
+
+ $adjmin=floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ if( $majend ) {
+ $adjmin = floor($min/$majstep)*$majstep;
+ $adjdiff = $adjmax-$adjmin;
+ $adjmax = ceil($adjdiff/$majstep)*$majstep+$adjmin;
+ }
+ else
+ $adjmax=ceil($max/$majstep)*$majstep;
+
+ return array($numsteps,$adjmin,$adjmax,$majstep);
+ }
+
+
+ function IntCalcTicksFreeze($maxsteps,$min,$max,$a) {
+ // Same as IntCalcTick but don't change min/max values
+ $diff=$max-$min;
+ if( $diff==0 )
+ JpGraphError::Raise('Can\'t automatically determine ticks since min==max.');
+ else
+ $ld=floor(log10($diff));
+
+ if( $ld == 0 ) $ld=1;
+
+ if( $a == 1 )
+ $majstep = 1;
+ else
+ $majstep=pow(10,$ld)/$a;
+
+ $numsteps=floor($diff/$majstep);
+ while( $numsteps > $maxsteps ) {
+ $majstep=pow(10,$ld)/$a;
+ $numsteps=floor($diff/$majstep);
+ ++$ld;
+ }
+
+ return array($numsteps,$majstep);
+ }
+
+
+
+ // Determine the minimum of three values witha weight for last value
+ function MatchMin3($a,$b,$c,$weight) {
+ if( $a < $b ) {
+ if( $a < ($c*$weight) )
+ return 1; // $a smallest
+ else
+ return 3; // $c smallest
+ }
+ elseif( $b < ($c*$weight) )
+ return 2; // $b smallest
+ return 3; // $c smallest
+ }
+} // Class
+
+//===================================================
+// CLASS RGB
+// Description: Color definitions as RGB triples
+//===================================================
+class RGB {
+ var $rgb_table;
+ var $img;
+ function RGB($aImg=null) {
+ $this->img = $aImg;
+
+ // Conversion array between color names and RGB
+ $this->rgb_table = array(
+ "aqua"=> array(0,255,255),
+ "lime"=> array(0,255,0),
+ "teal"=> array(0,128,128),
+ "whitesmoke"=>array(245,245,245),
+ "gainsboro"=>array(220,220,220),
+ "oldlace"=>array(253,245,230),
+ "linen"=>array(250,240,230),
+ "antiquewhite"=>array(250,235,215),
+ "papayawhip"=>array(255,239,213),
+ "blanchedalmond"=>array(255,235,205),
+ "bisque"=>array(255,228,196),
+ "peachpuff"=>array(255,218,185),
+ "navajowhite"=>array(255,222,173),
+ "moccasin"=>array(255,228,181),
+ "cornsilk"=>array(255,248,220),
+ "ivory"=>array(255,255,240),
+ "lemonchiffon"=>array(255,250,205),
+ "seashell"=>array(255,245,238),
+ "mintcream"=>array(245,255,250),
+ "azure"=>array(240,255,255),
+ "aliceblue"=>array(240,248,255),
+ "lavender"=>array(230,230,250),
+ "lavenderblush"=>array(255,240,245),
+ "mistyrose"=>array(255,228,225),
+ "white"=>array(255,255,255),
+ "black"=>array(0,0,0),
+ "darkslategray"=>array(47,79,79),
+ "dimgray"=>array(105,105,105),
+ "slategray"=>array(112,128,144),
+ "lightslategray"=>array(119,136,153),
+ "gray"=>array(190,190,190),
+ "lightgray"=>array(211,211,211),
+ "midnightblue"=>array(25,25,112),
+ "navy"=>array(0,0,128),
+ "cornflowerblue"=>array(100,149,237),
+ "darkslateblue"=>array(72,61,139),
+ "slateblue"=>array(106,90,205),
+ "mediumslateblue"=>array(123,104,238),
+ "lightslateblue"=>array(132,112,255),
+ "mediumblue"=>array(0,0,205),
+ "royalblue"=>array(65,105,225),
+ "blue"=>array(0,0,255),
+ "dodgerblue"=>array(30,144,255),
+ "deepskyblue"=>array(0,191,255),
+ "skyblue"=>array(135,206,235),
+ "lightskyblue"=>array(135,206,250),
+ "steelblue"=>array(70,130,180),
+ "lightred"=>array(211,167,168),
+ "lightsteelblue"=>array(176,196,222),
+ "lightblue"=>array(173,216,230),
+ "powderblue"=>array(176,224,230),
+ "paleturquoise"=>array(175,238,238),
+ "darkturquoise"=>array(0,206,209),
+ "mediumturquoise"=>array(72,209,204),
+ "turquoise"=>array(64,224,208),
+ "cyan"=>array(0,255,255),
+ "lightcyan"=>array(224,255,255),
+ "cadetblue"=>array(95,158,160),
+ "mediumaquamarine"=>array(102,205,170),
+ "aquamarine"=>array(127,255,212),
+ "darkgreen"=>array(0,100,0),
+ "darkolivegreen"=>array(85,107,47),
+ "darkseagreen"=>array(143,188,143),
+ "seagreen"=>array(46,139,87),
+ "mediumseagreen"=>array(60,179,113),
+ "lightseagreen"=>array(32,178,170),
+ "palegreen"=>array(152,251,152),
+ "springgreen"=>array(0,255,127),
+ "lawngreen"=>array(124,252,0),
+ "green"=>array(0,255,0),
+ "chartreuse"=>array(127,255,0),
+ "mediumspringgreen"=>array(0,250,154),
+ "greenyellow"=>array(173,255,47),
+ "limegreen"=>array(50,205,50),
+ "yellowgreen"=>array(154,205,50),
+ "forestgreen"=>array(34,139,34),
+ "olivedrab"=>array(107,142,35),
+ "darkkhaki"=>array(189,183,107),
+ "khaki"=>array(240,230,140),
+ "palegoldenrod"=>array(238,232,170),
+ "lightgoldenrodyellow"=>array(250,250,210),
+ "lightyellow"=>array(255,255,200),
+ "yellow"=>array(255,255,0),
+ "gold"=>array(255,215,0),
+ "lightgoldenrod"=>array(238,221,130),
+ "goldenrod"=>array(218,165,32),
+ "darkgoldenrod"=>array(184,134,11),
+ "rosybrown"=>array(188,143,143),
+ "indianred"=>array(205,92,92),
+ "saddlebrown"=>array(139,69,19),
+ "sienna"=>array(160,82,45),
+ "peru"=>array(205,133,63),
+ "burlywood"=>array(222,184,135),
+ "beige"=>array(245,245,220),
+ "wheat"=>array(245,222,179),
+ "sandybrown"=>array(244,164,96),
+ "tan"=>array(210,180,140),
+ "chocolate"=>array(210,105,30),
+ "firebrick"=>array(178,34,34),
+ "brown"=>array(165,42,42),
+ "darksalmon"=>array(233,150,122),
+ "salmon"=>array(250,128,114),
+ "lightsalmon"=>array(255,160,122),
+ "orange"=>array(255,165,0),
+ "darkorange"=>array(255,140,0),
+ "coral"=>array(255,127,80),
+ "lightcoral"=>array(240,128,128),
+ "tomato"=>array(255,99,71),
+ "orangered"=>array(255,69,0),
+ "red"=>array(255,0,0),
+ "hotpink"=>array(255,105,180),
+ "deeppink"=>array(255,20,147),
+ "pink"=>array(255,192,203),
+ "lightpink"=>array(255,182,193),
+ "palevioletred"=>array(219,112,147),
+ "maroon"=>array(176,48,96),
+ "mediumvioletred"=>array(199,21,133),
+ "violetred"=>array(208,32,144),
+ "magenta"=>array(255,0,255),
+ "violet"=>array(238,130,238),
+ "plum"=>array(221,160,221),
+ "orchid"=>array(218,112,214),
+ "mediumorchid"=>array(186,85,211),
+ "darkorchid"=>array(153,50,204),
+ "darkviolet"=>array(148,0,211),
+ "blueviolet"=>array(138,43,226),
+ "purple"=>array(160,32,240),
+ "mediumpurple"=>array(147,112,219),
+ "thistle"=>array(216,191,216),
+ "snow1"=>array(255,250,250),
+ "snow2"=>array(238,233,233),
+ "snow3"=>array(205,201,201),
+ "snow4"=>array(139,137,137),
+ "seashell1"=>array(255,245,238),
+ "seashell2"=>array(238,229,222),
+ "seashell3"=>array(205,197,191),
+ "seashell4"=>array(139,134,130),
+ "AntiqueWhite1"=>array(255,239,219),
+ "AntiqueWhite2"=>array(238,223,204),
+ "AntiqueWhite3"=>array(205,192,176),
+ "AntiqueWhite4"=>array(139,131,120),
+ "bisque1"=>array(255,228,196),
+ "bisque2"=>array(238,213,183),
+ "bisque3"=>array(205,183,158),
+ "bisque4"=>array(139,125,107),
+ "peachPuff1"=>array(255,218,185),
+ "peachpuff2"=>array(238,203,173),
+ "peachpuff3"=>array(205,175,149),
+ "peachpuff4"=>array(139,119,101),
+ "navajowhite1"=>array(255,222,173),
+ "navajowhite2"=>array(238,207,161),
+ "navajowhite3"=>array(205,179,139),
+ "navajowhite4"=>array(139,121,94),
+ "lemonchiffon1"=>array(255,250,205),
+ "lemonchiffon2"=>array(238,233,191),
+ "lemonchiffon3"=>array(205,201,165),
+ "lemonchiffon4"=>array(139,137,112),
+ "ivory1"=>array(255,255,240),
+ "ivory2"=>array(238,238,224),
+ "ivory3"=>array(205,205,193),
+ "ivory4"=>array(139,139,131),
+ "honeydew"=>array(193,205,193),
+ "lavenderblush1"=>array(255,240,245),
+ "lavenderblush2"=>array(238,224,229),
+ "lavenderblush3"=>array(205,193,197),
+ "lavenderblush4"=>array(139,131,134),
+ "mistyrose1"=>array(255,228,225),
+ "mistyrose2"=>array(238,213,210),
+ "mistyrose3"=>array(205,183,181),
+ "mistyrose4"=>array(139,125,123),
+ "azure1"=>array(240,255,255),
+ "azure2"=>array(224,238,238),
+ "azure3"=>array(193,205,205),
+ "azure4"=>array(131,139,139),
+ "slateblue1"=>array(131,111,255),
+ "slateblue2"=>array(122,103,238),
+ "slateblue3"=>array(105,89,205),
+ "slateblue4"=>array(71,60,139),
+ "royalblue1"=>array(72,118,255),
+ "royalblue2"=>array(67,110,238),
+ "royalblue3"=>array(58,95,205),
+ "royalblue4"=>array(39,64,139),
+ "dodgerblue1"=>array(30,144,255),
+ "dodgerblue2"=>array(28,134,238),
+ "dodgerblue3"=>array(24,116,205),
+ "dodgerblue4"=>array(16,78,139),
+ "steelblue1"=>array(99,184,255),
+ "steelblue2"=>array(92,172,238),
+ "steelblue3"=>array(79,148,205),
+ "steelblue4"=>array(54,100,139),
+ "deepskyblue1"=>array(0,191,255),
+ "deepskyblue2"=>array(0,178,238),
+ "deepskyblue3"=>array(0,154,205),
+ "deepskyblue4"=>array(0,104,139),
+ "skyblue1"=>array(135,206,255),
+ "skyblue2"=>array(126,192,238),
+ "skyblue3"=>array(108,166,205),
+ "skyblue4"=>array(74,112,139),
+ "lightskyblue1"=>array(176,226,255),
+ "lightskyblue2"=>array(164,211,238),
+ "lightskyblue3"=>array(141,182,205),
+ "lightskyblue4"=>array(96,123,139),
+ "slategray1"=>array(198,226,255),
+ "slategray2"=>array(185,211,238),
+ "slategray3"=>array(159,182,205),
+ "slategray4"=>array(108,123,139),
+ "lightsteelblue1"=>array(202,225,255),
+ "lightsteelblue2"=>array(188,210,238),
+ "lightsteelblue3"=>array(162,181,205),
+ "lightsteelblue4"=>array(110,123,139),
+ "lightblue1"=>array(191,239,255),
+ "lightblue2"=>array(178,223,238),
+ "lightblue3"=>array(154,192,205),
+ "lightblue4"=>array(104,131,139),
+ "lightcyan1"=>array(224,255,255),
+ "lightcyan2"=>array(209,238,238),
+ "lightcyan3"=>array(180,205,205),
+ "lightcyan4"=>array(122,139,139),
+ "paleturquoise1"=>array(187,255,255),
+ "paleturquoise2"=>array(174,238,238),
+ "paleturquoise3"=>array(150,205,205),
+ "paleturquoise4"=>array(102,139,139),
+ "cadetblue1"=>array(152,245,255),
+ "cadetblue2"=>array(142,229,238),
+ "cadetblue3"=>array(122,197,205),
+ "cadetblue4"=>array(83,134,139),
+ "turquoise1"=>array(0,245,255),
+ "turquoise2"=>array(0,229,238),
+ "turquoise3"=>array(0,197,205),
+ "turquoise4"=>array(0,134,139),
+ "cyan1"=>array(0,255,255),
+ "cyan2"=>array(0,238,238),
+ "cyan3"=>array(0,205,205),
+ "cyan4"=>array(0,139,139),
+ "darkslategray1"=>array(151,255,255),
+ "darkslategray2"=>array(141,238,238),
+ "darkslategray3"=>array(121,205,205),
+ "darkslategray4"=>array(82,139,139),
+ "aquamarine1"=>array(127,255,212),
+ "aquamarine2"=>array(118,238,198),
+ "aquamarine3"=>array(102,205,170),
+ "aquamarine4"=>array(69,139,116),
+ "darkseagreen1"=>array(193,255,193),
+ "darkseagreen2"=>array(180,238,180),
+ "darkseagreen3"=>array(155,205,155),
+ "darkseagreen4"=>array(105,139,105),
+ "seagreen1"=>array(84,255,159),
+ "seagreen2"=>array(78,238,148),
+ "seagreen3"=>array(67,205,128),
+ "seagreen4"=>array(46,139,87),
+ "palegreen1"=>array(154,255,154),
+ "palegreen2"=>array(144,238,144),
+ "palegreen3"=>array(124,205,124),
+ "palegreen4"=>array(84,139,84),
+ "springgreen1"=>array(0,255,127),
+ "springgreen2"=>array(0,238,118),
+ "springgreen3"=>array(0,205,102),
+ "springgreen4"=>array(0,139,69),
+ "chartreuse1"=>array(127,255,0),
+ "chartreuse2"=>array(118,238,0),
+ "chartreuse3"=>array(102,205,0),
+ "chartreuse4"=>array(69,139,0),
+ "olivedrab1"=>array(192,255,62),
+ "olivedrab2"=>array(179,238,58),
+ "olivedrab3"=>array(154,205,50),
+ "olivedrab4"=>array(105,139,34),
+ "darkolivegreen1"=>array(202,255,112),
+ "darkolivegreen2"=>array(188,238,104),
+ "darkolivegreen3"=>array(162,205,90),
+ "darkolivegreen4"=>array(110,139,61),
+ "khaki1"=>array(255,246,143),
+ "khaki2"=>array(238,230,133),
+ "khaki3"=>array(205,198,115),
+ "khaki4"=>array(139,134,78),
+ "lightgoldenrod1"=>array(255,236,139),
+ "lightgoldenrod2"=>array(238,220,130),
+ "lightgoldenrod3"=>array(205,190,112),
+ "lightgoldenrod4"=>array(139,129,76),
+ "yellow1"=>array(255,255,0),
+ "yellow2"=>array(238,238,0),
+ "yellow3"=>array(205,205,0),
+ "yellow4"=>array(139,139,0),
+ "gold1"=>array(255,215,0),
+ "gold2"=>array(238,201,0),
+ "gold3"=>array(205,173,0),
+ "gold4"=>array(139,117,0),
+ "goldenrod1"=>array(255,193,37),
+ "goldenrod2"=>array(238,180,34),
+ "goldenrod3"=>array(205,155,29),
+ "goldenrod4"=>array(139,105,20),
+ "darkgoldenrod1"=>array(255,185,15),
+ "darkgoldenrod2"=>array(238,173,14),
+ "darkgoldenrod3"=>array(205,149,12),
+ "darkgoldenrod4"=>array(139,101,8),
+ "rosybrown1"=>array(255,193,193),
+ "rosybrown2"=>array(238,180,180),
+ "rosybrown3"=>array(205,155,155),
+ "rosybrown4"=>array(139,105,105),
+ "indianred1"=>array(255,106,106),
+ "indianred2"=>array(238,99,99),
+ "indianred3"=>array(205,85,85),
+ "indianred4"=>array(139,58,58),
+ "sienna1"=>array(255,130,71),
+ "sienna2"=>array(238,121,66),
+ "sienna3"=>array(205,104,57),
+ "sienna4"=>array(139,71,38),
+ "burlywood1"=>array(255,211,155),
+ "burlywood2"=>array(238,197,145),
+ "burlywood3"=>array(205,170,125),
+ "burlywood4"=>array(139,115,85),
+ "wheat1"=>array(255,231,186),
+ "wheat2"=>array(238,216,174),
+ "wheat3"=>array(205,186,150),
+ "wheat4"=>array(139,126,102),
+ "tan1"=>array(255,165,79),
+ "tan2"=>array(238,154,73),
+ "tan3"=>array(205,133,63),
+ "tan4"=>array(139,90,43),
+ "chocolate1"=>array(255,127,36),
+ "chocolate2"=>array(238,118,33),
+ "chocolate3"=>array(205,102,29),
+ "chocolate4"=>array(139,69,19),
+ "firebrick1"=>array(255,48,48),
+ "firebrick2"=>array(238,44,44),
+ "firebrick3"=>array(205,38,38),
+ "firebrick4"=>array(139,26,26),
+ "brown1"=>array(255,64,64),
+ "brown2"=>array(238,59,59),
+ "brown3"=>array(205,51,51),
+ "brown4"=>array(139,35,35),
+ "salmon1"=>array(255,140,105),
+ "salmon2"=>array(238,130,98),
+ "salmon3"=>array(205,112,84),
+ "salmon4"=>array(139,76,57),
+ "lightsalmon1"=>array(255,160,122),
+ "lightsalmon2"=>array(238,149,114),
+ "lightsalmon3"=>array(205,129,98),
+ "lightsalmon4"=>array(139,87,66),
+ "orange1"=>array(255,165,0),
+ "orange2"=>array(238,154,0),
+ "orange3"=>array(205,133,0),
+ "orange4"=>array(139,90,0),
+ "darkorange1"=>array(255,127,0),
+ "darkorange2"=>array(238,118,0),
+ "darkorange3"=>array(205,102,0),
+ "darkorange4"=>array(139,69,0),
+ "coral1"=>array(255,114,86),
+ "coral2"=>array(238,106,80),
+ "coral3"=>array(205,91,69),
+ "coral4"=>array(139,62,47),
+ "tomato1"=>array(255,99,71),
+ "tomato2"=>array(238,92,66),
+ "tomato3"=>array(205,79,57),
+ "tomato4"=>array(139,54,38),
+ "orangered1"=>array(255,69,0),
+ "orangered2"=>array(238,64,0),
+ "orangered3"=>array(205,55,0),
+ "orangered4"=>array(139,37,0),
+ "deeppink1"=>array(255,20,147),
+ "deeppink2"=>array(238,18,137),
+ "deeppink3"=>array(205,16,118),
+ "deeppink4"=>array(139,10,80),
+ "hotpink1"=>array(255,110,180),
+ "hotpink2"=>array(238,106,167),
+ "hotpink3"=>array(205,96,144),
+ "hotpink4"=>array(139,58,98),
+ "pink1"=>array(255,181,197),
+ "pink2"=>array(238,169,184),
+ "pink3"=>array(205,145,158),
+ "pink4"=>array(139,99,108),
+ "lightpink1"=>array(255,174,185),
+ "lightpink2"=>array(238,162,173),
+ "lightpink3"=>array(205,140,149),
+ "lightpink4"=>array(139,95,101),
+ "palevioletred1"=>array(255,130,171),
+ "palevioletred2"=>array(238,121,159),
+ "palevioletred3"=>array(205,104,137),
+ "palevioletred4"=>array(139,71,93),
+ "maroon1"=>array(255,52,179),
+ "maroon2"=>array(238,48,167),
+ "maroon3"=>array(205,41,144),
+ "maroon4"=>array(139,28,98),
+ "violetred1"=>array(255,62,150),
+ "violetred2"=>array(238,58,140),
+ "violetred3"=>array(205,50,120),
+ "violetred4"=>array(139,34,82),
+ "magenta1"=>array(255,0,255),
+ "magenta2"=>array(238,0,238),
+ "magenta3"=>array(205,0,205),
+ "magenta4"=>array(139,0,139),
+ "mediumred"=>array(140,34,34),
+ "orchid1"=>array(255,131,250),
+ "orchid2"=>array(238,122,233),
+ "orchid3"=>array(205,105,201),
+ "orchid4"=>array(139,71,137),
+ "plum1"=>array(255,187,255),
+ "plum2"=>array(238,174,238),
+ "plum3"=>array(205,150,205),
+ "plum4"=>array(139,102,139),
+ "mediumorchid1"=>array(224,102,255),
+ "mediumorchid2"=>array(209,95,238),
+ "mediumorchid3"=>array(180,82,205),
+ "mediumorchid4"=>array(122,55,139),
+ "darkorchid1"=>array(191,62,255),
+ "darkorchid2"=>array(178,58,238),
+ "darkorchid3"=>array(154,50,205),
+ "darkorchid4"=>array(104,34,139),
+ "purple1"=>array(155,48,255),
+ "purple2"=>array(145,44,238),
+ "purple3"=>array(125,38,205),
+ "purple4"=>array(85,26,139),
+ "mediumpurple1"=>array(171,130,255),
+ "mediumpurple2"=>array(159,121,238),
+ "mediumpurple3"=>array(137,104,205),
+ "mediumpurple4"=>array(93,71,139),
+ "thistle1"=>array(255,225,255),
+ "thistle2"=>array(238,210,238),
+ "thistle3"=>array(205,181,205),
+ "thistle4"=>array(139,123,139),
+ "gray1"=>array(10,10,10),
+ "gray2"=>array(40,40,30),
+ "gray3"=>array(70,70,70),
+ "gray4"=>array(100,100,100),
+ "gray5"=>array(130,130,130),
+ "gray6"=>array(160,160,160),
+ "gray7"=>array(190,190,190),
+ "gray8"=>array(210,210,210),
+ "gray9"=>array(240,240,240),
+ "darkgray"=>array(100,100,100),
+ "darkblue"=>array(0,0,139),
+ "darkcyan"=>array(0,139,139),
+ "darkmagenta"=>array(139,0,139),
+ "darkred"=>array(139,0,0),
+ "silver"=>array(192, 192, 192),
+ "eggplant"=>array(144,176,168),
+ "lightgreen"=>array(144,238,144));
+ }
+//----------------
+// PUBLIC METHODS
+ // Colors can be specified as either
+ // 1. #xxxxxx HTML style
+ // 2. "colorname" as a named color
+ // 3. array(r,g,b) RGB triple
+ // This function translates this to a native RGB format and returns an
+ // RGB triple.
+ function Color($aColor) {
+ if (is_string($aColor)) {
+ // Strip of any alpha factor
+ $pos = strpos($aColor,'@');
+ if( $pos === false ) {
+ $alpha = 0;
+ }
+ else {
+ $pos2 = strpos($aColor,':');
+ if( $pos2===false )
+ $pos2 = $pos-1; // Sentinel
+ if( $pos > $pos2 ) {
+ $alpha = substr($aColor,$pos+1);
+ $aColor = substr($aColor,0,$pos);
+ }
+ else {
+ $alpha = substr($aColor,$pos+1,$pos2-$pos-1);
+ $aColor = substr($aColor,0,$pos).substr($aColor,$pos2);
+ }
+ }
+
+ // Extract potential adjustment figure at end of color
+ // specification
+ $pos = strpos($aColor,":");
+ if( $pos === false ) {
+ $adj = 1.0;
+ }
+ else {
+ $adj = 0.0 + substr($aColor,$pos+1);
+ $aColor = substr($aColor,0,$pos);
+ }
+ if( $adj < 0 )
+ JpGraphError::Raise('Adjustment factor for color must be > 0');
+
+ if (substr($aColor, 0, 1) == "#") {
+ $r = hexdec(substr($aColor, 1, 2));
+ $g = hexdec(substr($aColor, 3, 2));
+ $b = hexdec(substr($aColor, 5, 2));
+ } else {
+ if(!isset($this->rgb_table[$aColor]) )
+ JpGraphError::Raise(" Unknown color: $aColor");
+ $tmp=$this->rgb_table[$aColor];
+ $r = $tmp[0];
+ $g = $tmp[1];
+ $b = $tmp[2];
+ }
+ // Scale adj so that an adj=2 always
+ // makes the color 100% white (i.e. 255,255,255.
+ // and adj=1 neutral and adj=0 black.
+ if( $adj > 1 ) {
+ $m = ($adj-1.0)*(255-min(255,min($r,min($g,$b))));
+ return array(min(255,$r+$m), min(255,$g+$m), min(255,$b+$m),$alpha);
+ }
+ elseif( $adj < 1 ) {
+ $m = ($adj-1.0)*max(255,max($r,max($g,$b)));
+ return array(max(0,$r+$m), max(0,$g+$m), max(0,$b+$m),$alpha);
+ }
+ else {
+ return array($r,$g,$b,$alpha);
+ }
+
+ } elseif( is_array($aColor) ) {
+ if( count($aColor)==3 ) {
+ $aColor[3]=0;
+ return $aColor;
+ }
+ else
+ return $aColor;
+ }
+ else
+ JpGraphError::Raise(" Unknown color specification: $aColor , size=".count($aColor));
+ }
+
+ // Compare two colors
+ // return true if equal
+ function Equal($aCol1,$aCol2) {
+ $c1 = $this->Color($aCol1);
+ $c2 = $this->Color($aCol2);
+ if( $c1[0]==$c2[0] && $c1[1]==$c2[1] && $c1[2]==$c2[2] )
+ return true;
+ else
+ return false;
+ }
+
+ // Allocate a new color in the current image
+ // Return new color index, -1 if no more colors could be allocated
+ function Allocate($aColor,$aAlpha=0.0) {
+ list ($r, $g, $b, $a) = $this->color($aColor);
+ // If alpha is specified in the color string then this
+ // takes precedence over the second argument
+ if( $a > 0 )
+ $aAlpha = $a;
+ if(@$GLOBALS['gd2']==true) {
+ if( $aAlpha < 0 || $aAlpha > 1 ) {
+ JpGraphError::Raise('Alpha parameter for color must be between 0.0 and 1.0');
+ exit(1);
+ }
+ return imagecolorresolvealpha($this->img, $r, $g, $b, round($aAlpha * 127));
+ } else {
+ $index = imagecolorexact($this->img, $r, $g, $b);
+ if ($index == -1) {
+ $index = imagecolorallocate($this->img, $r, $g, $b);
+ if( USE_APPROX_COLORS && $index == -1 )
+ $index = imagecolorresolve($this->img, $r, $g, $b);
+ }
+ return $index;
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS Image
+// Description: Wrapper class with some goodies to form the
+// Interface to low level image drawing routines.
+//===================================================
+class Image {
+ var $img_format;
+ var $expired=true;
+ var $img;
+ var $left_margin=30,$right_margin=30,$top_margin=20,$bottom_margin=30;
+ var $plotwidth=0,$plotheight=0;
+ var $rgb=null;
+ var $current_color,$current_color_name;
+ var $lastx=0, $lasty=0;
+ var $width, $height;
+ var $line_weight=1;
+ var $line_style=1; // Default line style is solid
+ var $obs_list=array();
+ var $font_size=12,$font_family=FF_FONT1, $font_style=FS_NORMAL;
+ var $font_file='';
+ var $text_halign="left",$text_valign="bottom";
+ var $ttf=null;
+ var $use_anti_aliasing=false; // ANTIALIASING
+ var $quality=null;
+ var $colorstack=array(),$colorstackidx=0;
+ var $canvascolor = 'white' ;
+ var $langconv = null ;
+
+ //---------------
+ // CONSTRUCTOR
+ function Image($aWidth,$aHeight,$aFormat=DEFAULT_GFORMAT) {
+ $this->CreateImgCanvas($aWidth,$aHeight);
+ $this->SetAutoMargin();
+
+ if( !$this->SetImgFormat($aFormat) ) {
+ JpGraphError::Raise("JpGraph: Selected graphic format is either not supported or unknown [$aFormat]");
+ }
+ $this->ttf = new TTF();
+ $this->langconv = new LanguageConv();
+ }
+
+ // Should we use anti-aliasing. Note: This really slows down graphics!
+ function SetAntiAliasing() {
+ $this->use_anti_aliasing=true;
+ }
+
+ function CreateRawCanvas($aWidth=0,$aHeight=0) {
+ if( $aWidth <= 1 || $aHeight <= 1 ) {
+ JpGraphError::Raise("Illegal sizes specified for width or height when creating an image, (width=$aWidth, height=$aHeight)");
+ }
+ if( @$GLOBALS['gd2']==true && USE_TRUECOLOR ) {
+ $this->img = @imagecreatetruecolor($aWidth, $aHeight);
+ if( $this->img < 1 ) {
+ die("<b>JpGraph Error:</b> Can't create truecolor image. Check that you really have GD2 library installed.");
+ }
+ $this->SetAlphaBlending();
+ } else {
+ $this->img = @imagecreate($aWidth, $aHeight);
+ if( $this->img < 1 ) {
+ die("<b>JpGraph Error:</b> Can't create image. Check that you really have the GD library installed.");
+ }
+ }
+ if( $this->rgb != null )
+ $this->rgb->img = $this->img ;
+ else
+ $this->rgb = new RGB($this->img);
+ }
+
+ function CloneCanvasH() {
+ $oldimage = $this->img;
+ $this->CreateRawCanvas($this->width,$this->height);
+ imagecopy($this->img,$oldimage,0,0,0,0,$this->width,$this->height);
+ return $oldimage;
+ }
+
+ function CreateImgCanvas($aWidth=0,$aHeight=0) {
+
+ $old = array($this->img,$this->width,$this->height);
+
+ $aWidth = round($aWidth);
+ $aHeight = round($aHeight);
+
+ $this->width=$aWidth;
+ $this->height=$aHeight;
+
+
+ if( $aWidth==0 || $aHeight==0 ) {
+ // We will set the final size later.
+ // Note: The size must be specified before any other
+ // img routines that stroke anything are called.
+ $this->img = null;
+ $this->rgb = null;
+ return $old;
+ }
+
+ $this->CreateRawCanvas($aWidth,$aHeight);
+
+ // Set canvas color (will also be the background color for a
+ // a pallett image
+ $this->SetColor($this->canvascolor);
+ $this->FilledRectangle(0,0,$aWidth,$aHeight);
+
+ return $old ;
+ }
+
+ function CopyCanvasH($aToHdl,$aFromHdl,$aToX,$aToY,$aFromX,$aFromY,$aWidth,$aHeight,$aw=-1,$ah=-1) {
+ if( $aw === -1 ) {
+ $aw = $aWidth;
+ $ah = $aHeight;
+ $f = 'imagecopyresized';
+ }
+ else {
+ $f = $GLOBALS['copyfunc'] ;
+ }
+ $f($aToHdl,$aFromHdl,
+ $aToX,$aToY,$aFromX,$aFromY, $aWidth,$aHeight,$aw,$ah);
+ }
+
+ function Copy($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1) {
+ $this->CopyCanvasH($this->img,$fromImg,$toX,$toY,$fromX,$fromY,
+ $toWidth,$toHeight,$fromWidth,$fromHeight);
+ }
+
+ function CopyMerge($fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth=-1,$fromHeight=-1,$aMix=100) {
+ if( $aMix == 100 ) {
+ $this->CopyCanvasH($this->img,$fromImg,
+ $toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$fromWidth,$fromHeight);
+ }
+ else {
+ if( ($fromWidth != -1 && ($fromWidth != $toWidth)) ||
+ ($fromHeight != -1 && ($fromHeight != $fromHeight)) ) {
+ // Create a new canvas that will hold the re-scaled original from image
+ if( $toWidth <= 1 || $toHeight <= 1 ) {
+ JpGraphError::Raise('Illegal image size when copying image. Size for copied to image is 1 pixel or less.');
+ }
+ if( @$GLOBALS['gd2']==true && USE_TRUECOLOR ) {
+ $tmpimg = @imagecreatetruecolor($toWidth, $toHeight);
+ } else {
+ $tmpimg = @imagecreate($toWidth, $toHeight);
+ }
+ if( $tmpimg < 1 ) {
+ JpGraphError::Raise('Failed to create temporary GD canvas. Out of memory ?');
+ }
+ $this->CopyCanvasH($tmpimg,$fromImg,0,0,0,0,
+ $toWidth,$toHeight,$fromWidth,$fromHeight);
+ $fromImg = $tmpimg;
+ }
+ imagecopymerge($this->img,$fromImg,$toX,$toY,$fromX,$fromY,$toWidth,$toHeight,$aMix);
+ }
+ }
+
+ function GetWidth($aImg=null) {
+ if( $aImg === null )
+ $aImg = $this->img;
+ return imagesx($aImg);
+ }
+
+ function GetHeight($aImg=null) {
+ if( $aImg === null )
+ $aImg = $this->img;
+ return imagesy($aImg);
+ }
+
+ function CreateFromString($aStr) {
+ return imagecreatefromstring($aStr);
+ }
+
+ function SetCanvasH($aHdl) {
+ $this->img = $aHdl;
+ $this->rgb->img = $aHdl;
+ }
+
+ function SetCanvasColor($aColor) {
+ $this->canvascolor = $aColor ;
+ }
+
+ function SetAlphaBlending($aFlg=true) {
+ if( $GLOBALS['gd2'] )
+ ImageAlphaBlending($this->img,$aFlg);
+ else
+ JpGraphError::Raise('You only seem to have GD 1.x installed. To enable Alphablending requires GD 2.x or higher. Please install GD or make sure the constant USE_GD2 is specified correctly to reflect your installation. By default it tries to autodetect what version of GD you have installed. On some very rare occasions it may falsely detect GD2 where only GD1 is installed. You must then set USE_GD2 to false.');
+ }
+
+
+ function SetAutoMargin() {
+ GLOBAL $gJpgBrandTiming;
+ $min_bm=0;
+ /*
+ if( $gJpgBrandTiming )
+ $min_bm=15;
+ */
+ $lm = max(0,$this->width/7);
+ $rm = max(0,$this->width/10);
+ $tm = max(0,$this->height/7);
+ $bm = max($min_bm,$this->height/7);
+ $this->SetMargin($lm,$rm,$tm,$bm);
+ }
+
+
+ //---------------
+ // PUBLIC METHODS
+
+ // Add observer. The observer will be notified when
+ // the margin changes
+ function AddObserver($aMethod,&$aObject) {
+ $this->obs_list[]=array($aMethod,&$aObject);
+ }
+
+ // Call all observers
+ function NotifyObservers() {
+ // foreach($this->obs_list as $o)
+ // $o[1]->$o[0]($this);
+ for($i=0; $i < count($this->obs_list); ++$i) {
+ $obj = & $this->obs_list[$i][1];
+ $method = $this->obs_list[$i][0];
+ $obj->$method($this);
+ }
+ }
+
+ function SetFont($family,$style=FS_NORMAL,$size=10) {
+ if($family==FONT1_BOLD || $family==FONT2_BOLD || $family==FONT0 || $family==FONT1 || $family==FONT2 )
+ JpGraphError::Raise(" Usage of FONT0, FONT1, FONT2 is deprecated. Use FF_xxx instead.");
+
+
+ $this->font_family=$family;
+ $this->font_style=$style;
+ $this->font_size=$size;
+ $this->font_file='';
+ if( ($this->font_family==FF_FONT1 || $this->font_family==FF_FONT2) && $this->font_style==FS_BOLD ){
+ ++$this->font_family;
+ }
+ if( $this->font_family > FF_FONT2+1 ) { // A TTF font so get the font file
+
+ // Check that this PHP has support for TTF fonts
+ if( !function_exists('imagettfbbox') ) {
+ JpGraphError::Raise('This PHP build has not been configured with TTF support. You need to recompile your PHP installation with FreeType support.');
+ exit();
+ }
+ $this->font_file = $this->ttf->File($this->font_family,$this->font_style);
+ }
+ }
+
+ // Get the specific height for a text string
+ function GetTextHeight($txt="",$angle=0) {
+ $tmp = split("\n",$txt);
+ $n = count($tmp);
+ $m=0;
+ for($i=0; $i< $n; ++$i)
+ $m = max($m,strlen($tmp[$i]));
+
+ if( $this->font_family <= FF_FONT2+1 ) {
+ if( $angle==0 ) {
+ $h = imagefontheight($this->font_family);
+ if( $h === false ) {
+ JpGraphError::Raise('You have a misconfigured GD font support. The call to imagefontwidth() fails.');
+ }
+
+ return $n*$h;
+ }
+ else {
+ $w = @imagefontwidth($this->font_family);
+ if( $w === false ) {
+ JpGraphError::Raise('You have a misconfigured GD font support. The call to imagefontwidth() fails.');
+ }
+
+ return $m*$w;
+ }
+ }
+ else {
+ $bbox = $this->GetTTFBBox($txt,$angle);
+ return $bbox[1]-$bbox[5];
+ }
+ }
+
+ // Estimate font height
+ function GetFontHeight($angle=0) {
+ $txt = "XOMg";
+ return $this->GetTextHeight($txt,$angle);
+ }
+
+ // Approximate font width with width of letter "O"
+ function GetFontWidth($angle=0) {
+ $txt = 'O';
+ return $this->GetTextWidth($txt,$angle);
+ }
+
+ // Get actual width of text in absolute pixels
+ function GetTextWidth($txt,$angle=0) {
+
+ $tmp = split("\n",$txt);
+ $n = count($tmp);
+ if( $this->font_family <= FF_FONT2+1 ) {
+
+ $m=0;
+ for($i=0; $i < $n; ++$i) {
+ $l=strlen($tmp[$i]);
+ if( $l > $m ) {
+ $m = $l;
+ }
+ }
+
+ if( $angle==0 ) {
+ $w = @imagefontwidth($this->font_family);
+ if( $w === false ) {
+ JpGraphError::Raise('You have a misconfigured GD font support. The call to imagefontwidth() fails.');
+ }
+ return $m*$w;
+ }
+ else {
+ // 90 degrees internal so height becomes width
+ $h = @imagefontheight($this->font_family);
+ if( $h === false ) {
+ JpGraphError::Raise('You have a misconfigured GD font support. The call to imagefontheight() fails.');
+ }
+ return $n*$h;
+ }
+ }
+ else {
+ // For TTF fonts we must walk through a lines and find the
+ // widest one which we use as the width of the multi-line
+ // paragraph
+ $m=0;
+ for( $i=0; $i < $n; ++$i ) {
+ $bbox = $this->GetTTFBBox($tmp[$i],$angle);
+ $mm = $bbox[2] - $bbox[0];
+ if( $mm > $m )
+ $m = $mm;
+ }
+ return $m;
+ }
+ }
+
+ // Draw text with a box around it
+ function StrokeBoxedText($x,$y,$txt,$dir=0,$fcolor="white",$bcolor="black",
+ $shadowcolor=false,$paragraph_align="left",
+ $xmarg=6,$ymarg=4,$cornerradius=0,$dropwidth=3) {
+
+ if( !is_numeric($dir) ) {
+ if( $dir=="h" ) $dir=0;
+ elseif( $dir=="v" ) $dir=90;
+ else JpGraphError::Raise(" Unknown direction specified in call to StrokeBoxedText() [$dir]");
+ }
+
+ if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
+ $width=$this->GetTextWidth($txt,$dir) ;
+ $height=$this->GetTextHeight($txt,$dir) ;
+ }
+ else {
+ $width=$this->GetBBoxWidth($txt,$dir) ;
+ $height=$this->GetBBoxHeight($txt,$dir) ;
+ }
+
+ $height += 2*$ymarg;
+ $width += 2*$xmarg;
+
+ if( $this->text_halign=="right" ) $x -= $width;
+ elseif( $this->text_halign=="center" ) $x -= $width/2;
+ if( $this->text_valign=="bottom" ) $y -= $height;
+ elseif( $this->text_valign=="center" ) $y -= $height/2;
+
+ if( $shadowcolor ) {
+ $this->PushColor($shadowcolor);
+ $this->FilledRoundedRectangle($x-$xmarg+$dropwidth,$y-$ymarg+$dropwidth,
+ $x+$width+$dropwidth,$y+$height-$ymarg+$dropwidth,
+ $cornerradius);
+ $this->PopColor();
+ $this->PushColor($fcolor);
+ $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,
+ $x+$width,$y+$height-$ymarg,
+ $cornerradius);
+ $this->PopColor();
+ $this->PushColor($bcolor);
+ $this->RoundedRectangle($x-$xmarg,$y-$ymarg,
+ $x+$width,$y+$height-$ymarg,$cornerradius);
+ $this->PopColor();
+ }
+ else {
+ if( $fcolor ) {
+ $oc=$this->current_color;
+ $this->SetColor($fcolor);
+ $this->FilledRoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius);
+ $this->current_color=$oc;
+ }
+ if( $bcolor ) {
+ $oc=$this->current_color;
+ $this->SetColor($bcolor);
+ $this->RoundedRectangle($x-$xmarg,$y-$ymarg,$x+$width,$y+$height-$ymarg,$cornerradius);
+ $this->current_color=$oc;
+ }
+ }
+
+ $h=$this->text_halign;
+ $v=$this->text_valign;
+ $this->SetTextAlign("left","top");
+ $this->StrokeText($x, $y, $txt, $dir, $paragraph_align);
+ $this->SetTextAlign($h,$v);
+ }
+
+ // Set text alignment
+ function SetTextAlign($halign,$valign="bottom") {
+ $this->text_halign=$halign;
+ $this->text_valign=$valign;
+ }
+
+
+ function _StrokeBuiltinFont($x,$y,$txt,$dir=0,$paragraph_align="left") {
+
+ if( is_numeric($dir) && $dir!=90 && $dir!=0)
+ JpGraphError::Raise(" Internal font does not support drawing text at arbitrary angle. Use TTF fonts instead.");
+
+ $h=$this->GetTextHeight($txt);
+ $fh=$this->GetFontHeight();
+ $w=$this->GetTextWidth($txt);
+
+ if( $this->text_halign=="right")
+ $x -= $dir==0 ? $w : $h;
+ elseif( $this->text_halign=="center" ) {
+ // For center we subtract 1 pixel since this makes the middle
+ // be prefectly in the middle
+ $x -= $dir==0 ? $w/2-1 : $h/2;
+ }
+ if( $this->text_valign=="top" )
+ $y += $dir==0 ? $h : $w;
+ elseif( $this->text_valign=="center" )
+ $y += $dir==0 ? $h/2 : $w/2;
+
+ if( $dir==90 )
+ imagestringup($this->img,$this->font_family,$x,$y,$txt,$this->current_color);
+ else {
+ if( ereg("\n",$txt) ) {
+ $tmp = split("\n",$txt);
+ for($i=0; $i < count($tmp); ++$i) {
+ $w1 = $this->GetTextWidth($tmp[$i]);
+ if( $paragraph_align=="left" ) {
+ imagestring($this->img,$this->font_family,$x,$y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ elseif( $paragraph_align=="right" ) {
+ imagestring($this->img,$this->font_family,$x+($w-$w1),
+ $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ else {
+ imagestring($this->img,$this->font_family,$x+$w/2-$w1/2,
+ $y-$h+1+$i*$fh,$tmp[$i],$this->current_color);
+ }
+ }
+ }
+ else {
+ //Put the text
+ imagestring($this->img,$this->font_family,$x,$y-$h+1,$txt,$this->current_color);
+ }
+ }
+ }
+
+ function AddTxtCR($aTxt) {
+ // If the user has just specified a '\n'
+ // instead of '\n\t' we have to add '\r' since
+ // the width will be too muchy otherwise since when
+ // we print we stroke the individually lines by hand.
+ $e = explode("\n",$aTxt);
+ $n = count($e);
+ for($i=0; $i<$n; ++$i) {
+ $e[$i]=str_replace("\r","",$e[$i]);
+ }
+ return implode("\n\r",$e);
+ }
+
+ function GetTTFBBox($aTxt,$aAngle=0) {
+ $bbox = @ImageTTFBBox($this->font_size,$aAngle,$this->font_file,$aTxt);
+ if( $bbox === false ) {
+ JpGraphError::Raise("There is either a configuration problem with TrueType or a problem reading font file (".$this->font_file."). Make sure file exists and is in a readable place for the HTTP process. (If 'basedir' restriction is enabled in PHP then the font file must be located in the document root.). It might also be a wrongly installed FreeType library. Try uppgrading to at least FreeType 2.1.13 and recompile GD with the correct setup so it can find the new FT library.");
+ }
+ return $bbox;
+ }
+
+ function GetBBoxTTF($aTxt,$aAngle=0) {
+ // Normalize the bounding box to become a minimum
+ // enscribing rectangle
+
+ $aTxt = $this->AddTxtCR($aTxt);
+
+ if( !is_readable($this->font_file) ) {
+ JpGraphError::Raise('Can not read font file ('.$this->font_file.') in call to Image::GetBBoxTTF. Please make sure that you have set a font before calling this method and that the font is installed in the TTF directory.');
+ }
+ $bbox = $this->GetTTFBBox($aTxt,$aAngle);
+
+ if( $aAngle==0 )
+ return $bbox;
+ if( $aAngle >= 0 ) {
+ if( $aAngle <= 90 ) { //<=0
+ $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
+ $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
+ }
+ elseif( $aAngle <= 180 ) { //<= 2
+ $bbox = array($bbox[4],$bbox[7],$bbox[0],$bbox[7],
+ $bbox[0],$bbox[3],$bbox[4],$bbox[3]);
+ }
+ elseif( $aAngle <= 270 ) { //<= 3
+ $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
+ $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
+ }
+ else {
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ }
+ elseif( $aAngle < 0 ) {
+ if( $aAngle <= -270 ) { // <= -3
+ $bbox = array($bbox[6],$bbox[1],$bbox[2],$bbox[1],
+ $bbox[2],$bbox[5],$bbox[6],$bbox[5]);
+ }
+ elseif( $aAngle <= -180 ) { // <= -2
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ elseif( $aAngle <= -90 ) { // <= -1
+ $bbox = array($bbox[2],$bbox[5],$bbox[6],$bbox[5],
+ $bbox[6],$bbox[1],$bbox[2],$bbox[1]);
+ }
+ else {
+ $bbox = array($bbox[0],$bbox[3],$bbox[4],$bbox[3],
+ $bbox[4],$bbox[7],$bbox[0],$bbox[7]);
+ }
+ }
+ return $bbox;
+ }
+
+ function GetBBoxHeight($aTxt,$aAngle=0) {
+ $box = $this->GetBBoxTTF($aTxt,$aAngle);
+ return $box[1]-$box[7]+1;
+ }
+
+ function GetBBoxWidth($aTxt,$aAngle=0) {
+ $box = $this->GetBBoxTTF($aTxt,$aAngle);
+ return $box[2]-$box[0]+1;
+ }
+
+ function _StrokeTTF($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+
+ // Setupo default inter line margin for paragraphs to
+ // 25% of the font height.
+ $ConstLineSpacing = 0.25 ;
+
+ // Remember the anchor point before adjustment
+ if( $debug ) {
+ $ox=$x;
+ $oy=$y;
+ }
+
+ if( !ereg("\n",$txt) || ($dir>0 && ereg("\n",$txt)) ) {
+ // Format a single line
+
+ $txt = $this->AddTxtCR($txt);
+
+ $bbox=$this->GetBBoxTTF($txt,$dir);
+
+ // Align x,y ot lower left corner of bbox
+ $x -= $bbox[0];
+ $y -= $bbox[1];
+
+ // Note to self: "topanchor" is deprecated after we changed the
+ // bopunding box stuff.
+ if( $this->text_halign=="right" || $this->text_halign=="topanchor" )
+ $x -= $bbox[2]-$bbox[0];
+ elseif( $this->text_halign=="center" ) $x -= ($bbox[2]-$bbox[0])/2;
+
+ if( $this->text_valign=="top" ) $y += abs($bbox[5])+$bbox[1];
+ elseif( $this->text_valign=="center" ) $y -= ($bbox[5]-$bbox[1])/2;
+
+ ImageTTFText ($this->img, $this->font_size, $dir, $x, $y,
+ $this->current_color,$this->font_file,$txt);
+
+ if( $debug ) {
+ // Draw the bounding rectangle and the bounding box
+ $box=@ImageTTFBBox($this->font_size,$dir,$this->font_file,$txt);
+ $p = array();
+ $p1 = array();
+ for($i=0; $i < 4; ++$i) {
+ $p[] = $bbox[$i*2]+$x;
+ $p[] = $bbox[$i*2+1]+$y;
+ $p1[] = $box[$i*2]+$x;
+ $p1[] = $box[$i*2+1]+$y;
+ }
+
+ // Draw bounding box
+ $this->PushColor('green');
+ $this->Polygon($p1,true);
+ $this->PopColor();
+
+ // Draw bounding rectangle
+ $this->PushColor('darkgreen');
+ $this->Polygon($p,true);
+ $this->PopColor();
+
+ // Draw a cross at the anchor point
+ $this->PushColor('red');
+ $this->Line($ox-15,$oy,$ox+15,$oy);
+ $this->Line($ox,$oy-15,$ox,$oy+15);
+ $this->PopColor();
+ }
+ }
+ else {
+ // Format a text paragraph
+ $fh=$this->GetFontHeight();
+
+ // Line margin is 25% of font height
+ $linemargin=round($fh*$ConstLineSpacing);
+ $fh += $linemargin;
+ $w=$this->GetTextWidth($txt);
+
+ $y -= $linemargin/2;
+ $tmp = split("\n",$txt);
+ $nl = count($tmp);
+ $h = $nl * $fh;
+
+ if( $this->text_halign=="right")
+ $x -= $dir==0 ? $w : $h;
+ elseif( $this->text_halign=="center" ) {
+ $x -= $dir==0 ? $w/2 : $h/2;
+ }
+
+ if( $this->text_valign=="top" )
+ $y += $dir==0 ? $h : $w;
+ elseif( $this->text_valign=="center" )
+ $y += $dir==0 ? $h/2 : $w/2;
+
+ // Here comes a tricky bit.
+ // Since we have to give the position for the string at the
+ // baseline this means thaht text will move slightly up
+ // and down depending on any of it's character descend below
+ // the baseline, for example a 'g'. To adjust the Y-position
+ // we therefore adjust the text with the baseline Y-offset
+ // as used for the current font and size. This will keep the
+ // baseline at a fixed positoned disregarding the actual
+ // characters in the string.
+ $standardbox = $this->GetTTFBBox('Gg',$dir);
+ $yadj = $standardbox[1];
+ $xadj = $standardbox[0];
+ for($i=0; $i < $nl; ++$i) {
+ $wl = $this->GetTextWidth($tmp[$i]);
+ $bbox = $this->GetTTFBBox($tmp[$i],$dir);
+ if( $paragraph_align=="left" ) {
+ $xl = $x;
+ }
+ elseif( $paragraph_align=="right" ) {
+ $xl = $x + ($w-$wl);
+ }
+ else {
+ // Center
+ $xl = $x + $w/2 - $wl/2 ;
+ }
+
+ $xl -= $bbox[0];
+ $yl = $y - $yadj;
+ $xl = $xl - $xadj;
+ ImageTTFText ($this->img, $this->font_size, $dir,
+ $xl, $yl-($h-$fh)+$fh*$i,
+ $this->current_color,$this->font_file,$tmp[$i]);
+
+
+ if( $debug ) {
+ // Draw the bounding rectangle around each line
+ $box=@ImageTTFBBox($this->font_size,$dir,$this->font_file,$tmp[$i]);
+ $p = array();
+ for($j=0; $j < 4; ++$j) {
+ $p[] = $bbox[$j*2]+$xl;
+ $p[] = $bbox[$j*2+1]+$yl-($h-$fh)+$fh*$i;
+ }
+
+ // Draw bounding rectangle
+ $this->PushColor('darkgreen');
+ $this->Polygon($p,true);
+ $this->PopColor();
+ }
+ }
+
+ if( $debug ) {
+
+ // Draw a cross at the anchor point
+ $this->PushColor('red');
+ $this->Line($ox-25,$oy,$ox+25,$oy);
+ $this->Line($ox,$oy-25,$ox,$oy+25);
+ $this->PopColor();
+ }
+
+ }
+ }
+
+ function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+
+ $x = round($x);
+ $y = round($y);
+
+ // Do special language encoding
+ $txt = $this->langconv->Convert($txt,$this->font_family);
+
+ if( !is_numeric($dir) )
+ JpGraphError::Raise(" Direction for text most be given as an angle between 0 and 90.");
+
+ if( $this->font_family >= FF_FONT0 && $this->font_family <= FF_FONT2+1) {
+ $this->_StrokeBuiltinFont($x,$y,$txt,$dir,$paragraph_align,$debug);
+ }
+ elseif($this->font_family >= FF_COURIER && $this->font_family <= FF_BOOK) {
+ $this->_StrokeTTF($x,$y,$txt,$dir,$paragraph_align,$debug);
+ }
+ else
+ JpGraphError::Raise(" Unknown font font family specification. ");
+ }
+
+ function SetMargin($lm,$rm,$tm,$bm) {
+ $this->left_margin=$lm;
+ $this->right_margin=$rm;
+ $this->top_margin=$tm;
+ $this->bottom_margin=$bm;
+ $this->plotwidth=$this->width - $this->left_margin-$this->right_margin ;
+ $this->plotheight=$this->height - $this->top_margin-$this->bottom_margin ;
+ if( $this->plotwidth < 0 || $this->plotheight < 0 )
+ JpGraphError::raise("To small plot area. ($lm,$rm,$tm,$bm : $this->plotwidth x $this->plotheight). With the given image size and margins there is to little space left for the plot. Increase the plot size or reduce the margins.");
+ $this->NotifyObservers();
+ }
+
+ function SetTransparent($color) {
+ imagecolortransparent ($this->img,$this->rgb->allocate($color));
+ }
+
+ function SetColor($color,$aAlpha=0) {
+ $this->current_color_name = $color;
+ $this->current_color=$this->rgb->allocate($color,$aAlpha);
+ if( $this->current_color == -1 ) {
+ $tc=imagecolorstotal($this->img);
+ JpGraphError::Raise("Can't allocate any more colors.
+ Image has already allocated maximum of <b>$tc colors</b>.
+ This might happen if you have anti-aliasing turned on
+ together with a background image or perhaps gradient fill
+ since this requires many, many colors. Try to turn off
+ anti-aliasing.<p>
+ If there is still a problem try downgrading the quality of
+ the background image to use a smaller pallete to leave some
+ entries for your graphs. You should try to limit the number
+ of colors in your background image to 64.<p>
+ If there is still problem set the constant
+<pre>
+DEFINE(\"USE_APPROX_COLORS\",true);
+</pre>
+ in jpgraph.php This will use approximative colors
+ when the palette is full.
+ <p>
+ Unfortunately there is not much JpGraph can do about this
+ since the palette size is a limitation of current graphic format and
+ what the underlying GD library suppports.");
+ }
+ return $this->current_color;
+ }
+
+ function PushColor($color) {
+ if( $color != "" ) {
+ $this->colorstack[$this->colorstackidx]=$this->current_color_name;
+ $this->colorstack[$this->colorstackidx+1]=$this->current_color;
+ $this->colorstackidx+=2;
+ $this->SetColor($color);
+ }
+ else {
+ JpGraphError::Raise("Color specified as empty string in PushColor().");
+ }
+ }
+
+ function PopColor() {
+ if($this->colorstackidx<1)
+ JpGraphError::Raise(" Negative Color stack index. Unmatched call to PopColor()");
+ $this->current_color=$this->colorstack[--$this->colorstackidx];
+ $this->current_color_name=$this->colorstack[--$this->colorstackidx];
+ }
+
+
+ // Why this duplication? Because this way we can call this method
+ // for any image and not only the current objsct
+ function AdjSat($sat) {
+ if( $GLOBALS['gd2'] && USE_TRUECOLOR )
+ return;
+ $this->_AdjSat($this->img,$sat);
+ }
+
+ function _AdjSat($img,$sat) {
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $rgb[0]=$colarr["red"];
+ $rgb[1]=$colarr["green"];
+ $rgb[2]=$colarr["blue"];
+ $rgb = $this->AdjRGBSat($rgb,$sat);
+ imagecolorset ($img, $i, $rgb[0], $rgb[1], $rgb[2]);
+ }
+ }
+
+ function AdjBrightContrast($bright,$contr=0) {
+ if( $GLOBALS['gd2'] && USE_TRUECOLOR )
+ return;
+ $this->_AdjBrightContrast($this->img,$bright,$contr);
+ }
+
+ function _AdjBrightContrast($img,$bright,$contr=0) {
+ if( $bright < -1 || $bright > 1 || $contr < -1 || $contr > 1 )
+ JpGraphError::Raise(" Parameters for brightness and Contrast out of range [-1,1]");
+ $nbr = imagecolorstotal ($img);
+ for( $i=0; $i<$nbr; ++$i ) {
+ $colarr = imagecolorsforindex ($img,$i);
+ $r = $this->AdjRGBBrightContrast($colarr["red"],$bright,$contr);
+ $g = $this->AdjRGBBrightContrast($colarr["green"],$bright,$contr);
+ $b = $this->AdjRGBBrightContrast($colarr["blue"],$bright,$contr);
+ imagecolorset ($img, $i, $r, $g, $b);
+ }
+ }
+
+ // Private helper function for adj sat
+ // Adjust saturation for RGB array $u. $sat is a value between -1 and 1
+ // Note: Due to GD inability to handle true color the RGB values are only between
+ // 8 bit. This makes saturation quite sensitive for small increases in parameter sat.
+ //
+ // Tip: To get a grayscale picture set sat=-100, values <-100 changes the colors
+ // to it's complement.
+ //
+ // Implementation note: The saturation is implemented directly in the RGB space
+ // by adjusting the perpendicular distance between the RGB point and the "grey"
+ // line (1,1,1). Setting $sat>0 moves the point away from the line along the perp.
+ // distance and a negative value moves the point closer to the line.
+ // The values are truncated when the color point hits the bounding box along the
+ // RGB axis.
+ // DISCLAIMER: I'm not 100% sure this is he correct way to implement a color
+ // saturation function in RGB space. However, it looks ok and has the expected effect.
+ function AdjRGBSat($rgb,$sat) {
+ // TODO: Should be moved to the RGB class
+ // Grey vector
+ $v=array(1,1,1);
+
+ // Dot product
+ $dot = $rgb[0]*$v[0]+$rgb[1]*$v[1]+$rgb[2]*$v[2];
+
+ // Normalize dot product
+ $normdot = $dot/3; // dot/|v|^2
+
+ // Direction vector between $u and its projection onto $v
+ for($i=0; $i<3; ++$i)
+ $r[$i] = $rgb[$i] - $normdot*$v[$i];
+
+ // Adjustment factor so that sat==1 sets the highest RGB value to 255
+ if( $sat > 0 ) {
+ $m=0;
+ for( $i=0; $i<3; ++$i) {
+ if( sign($r[$i]) == 1 && $r[$i]>0)
+ $m=max($m,(255-$rgb[$i])/$r[$i]);
+ }
+ $tadj=$m;
+ }
+ else
+ $tadj=1;
+
+ $tadj = $tadj*$sat;
+ for($i=0; $i<3; ++$i) {
+ $un[$i] = round($rgb[$i] + $tadj*$r[$i]);
+ if( $un[$i]<0 ) $un[$i]=0; // Truncate color when they reach 0
+ if( $un[$i]>255 ) $un[$i]=255;// Avoid potential rounding error
+ }
+ return $un;
+ }
+
+ // Private helper function for AdjBrightContrast
+ function AdjRGBBrightContrast($rgb,$bright,$contr) {
+ // TODO: Should be moved to the RGB class
+ // First handle contrast, i.e change the dynamic range around grey
+ if( $contr <= 0 ) {
+ // Decrease contrast
+ $adj = abs($rgb-128) * (-$contr);
+ if( $rgb < 128 ) $rgb += $adj;
+ else $rgb -= $adj;
+ }
+ else { // $contr > 0
+ // Increase contrast
+ if( $rgb < 128 ) $rgb = $rgb - ($rgb * $contr);
+ else $rgb = $rgb + ((255-$rgb) * $contr);
+ }
+
+ // Add (or remove) various amount of white
+ $rgb += $bright*255;
+ $rgb=min($rgb,255);
+ $rgb=max($rgb,0);
+ return $rgb;
+ }
+
+ function SetLineWeight($weight) {
+ $this->line_weight = $weight;
+ }
+
+ function SetStartPoint($x,$y) {
+ $this->lastx=round($x);
+ $this->lasty=round($y);
+ }
+
+ function Arc($cx,$cy,$w,$h,$s,$e) {
+ // GD Arc doesn't like negative angles
+ while( $s < 0) $s += 360;
+ while( $e < 0) $e += 360;
+
+ imagearc($this->img,round($cx),round($cy),round($w),round($h),
+ $s,$e,$this->current_color);
+ }
+
+ function FilledArc($xc,$yc,$w,$h,$s,$e,$style="") {
+
+ if( $GLOBALS['gd2'] ) {
+ while( $s < 0 ) $s += 360;
+ while( $e < 0 ) $e += 360;
+ if( $style=="" )
+ $style=IMG_ARC_PIE;
+ imagefilledarc($this->img,round($xc),round($yc),round($w),round($h),
+ round($s),round($e),$this->current_color,$style);
+ return;
+ }
+
+
+ // In GD 1.x we have to do it ourself interesting enough there is surprisingly
+ // little difference in time between doing it PHP and using the optimised GD
+ // library (roughly ~20%) I had expected it to be at least 100% slower doing it
+ // manually with a polygon approximation in PHP.....
+ $fillcolor = $this->current_color_name;
+
+ $w /= 2; // We use radius in our calculations instead
+ $h /= 2;
+
+ // Setup the angles so we have the same conventions as the builtin
+ // FilledArc() which is a little bit strange if you ask me....
+
+ $s = 360-$s;
+ $e = 360-$e;
+
+ if( $e > $s ) {
+ $e = $e - 360;
+ $da = $s - $e;
+ }
+ $da = $s-$e;
+
+ // We use radians
+ $s *= M_PI/180;
+ $e *= M_PI/180;
+ $da *= M_PI/180;
+
+ // Calculate a polygon approximation
+ $p[0] = $xc;
+ $p[1] = $yc;
+
+ // Heuristic on how many polygons we need to make the
+ // arc look good
+ $numsteps = round(8 * abs($da) * ($w+$h)*($w+$h)/1500);
+
+ if( $numsteps == 0 ) return;
+ if( $numsteps < 7 ) $numsteps=7;
+ $delta = abs($da)/$numsteps;
+
+ $pa=array();
+ $a = $s;
+ for($i=1; $i<=$numsteps; ++$i ) {
+ $p[2*$i] = round($xc + $w*cos($a));
+ $p[2*$i+1] = round($yc - $h*sin($a));
+ //$a = $s + $i*$delta;
+ $a -= $delta;
+ $pa[2*($i-1)] = $p[2*$i];
+ $pa[2*($i-1)+1] = $p[2*$i+1];
+ }
+
+ // Get the last point at the exact ending angle to avoid
+ // any rounding errors.
+ $p[2*$i] = round($xc + $w*cos($e));
+ $p[2*$i+1] = round($yc - $h*sin($e));
+ $pa[2*($i-1)] = $p[2*$i];
+ $pa[2*($i-1)+1] = $p[2*$i+1];
+ $i++;
+
+ $p[2*$i] = $xc;
+ $p[2*$i+1] = $yc;
+ if( $fillcolor != "" ) {
+ $this->PushColor($fillcolor);
+ imagefilledpolygon($this->img,$p,count($p)/2,$this->current_color);
+ $this->PopColor();
+ }
+ }
+
+ function FilledCakeSlice($cx,$cy,$w,$h,$s,$e) {
+ $this->CakeSlice($cx,$cy,$w,$h,$s,$e,$this->current_color_name);
+ }
+
+ function CakeSlice($xc,$yc,$w,$h,$s,$e,$fillcolor="",$arccolor="") {
+ $s = round($s); $e = round($e);
+ $w = round($w); $h = round($h);
+ $xc = round($xc); $yc = round($yc);
+ $this->PushColor($fillcolor);
+ $this->FilledArc($xc,$yc,2*$w,2*$h,$s,$e);
+ $this->PopColor();
+ if( $arccolor != "" ) {
+ $this->PushColor($arccolor);
+ // We add 2 pixels to make the Arc() better aligned with
+ // the filled arc.
+ if( $GLOBALS['gd2'] ) {
+ imagefilledarc($this->img,$xc,$yc,2*$w,2*$h,$s,$e,$this->current_color_name,
+ IMG_ARC_NOFILL | IMG_ARC_EDGED ) ;
+ }
+ else {
+ $this->Arc($xc,$yc,2*$w+2,2*$h+2,$s,$e);
+ $xx = $w * cos(2*M_PI - $s*M_PI/180) + $xc;
+ $yy = $yc - $h * sin(2*M_PI - $s*M_PI/180);
+ $this->Line($xc,$yc,$xx,$yy);
+ $xx = $w * cos(2*M_PI - $e*M_PI/180) + $xc;
+ $yy = $yc - $h * sin(2*M_PI - $e*M_PI/180);
+ $this->Line($xc,$yc,$xx,$yy);
+ }
+ $this->PopColor();
+ }
+ }
+
+ function Ellipse($xc,$yc,$w,$h) {
+ $this->Arc($xc,$yc,$w,$h,0,360);
+ }
+
+ // Breseham circle gives visually better result then using GD
+ // built in arc(). It takes some more time but gives better
+ // accuracy.
+ function BresenhamCircle($xc,$yc,$r) {
+ $d = 3-2*$r;
+ $x = 0;
+ $y = $r;
+ while($x<=$y) {
+ $this->Point($xc+$x,$yc+$y);
+ $this->Point($xc+$x,$yc-$y);
+ $this->Point($xc-$x,$yc+$y);
+ $this->Point($xc-$x,$yc-$y);
+
+ $this->Point($xc+$y,$yc+$x);
+ $this->Point($xc+$y,$yc-$x);
+ $this->Point($xc-$y,$yc+$x);
+ $this->Point($xc-$y,$yc-$x);
+
+ if( $d<0 ) $d += 4*$x+6;
+ else {
+ $d += 4*($x-$y)+10;
+ --$y;
+ }
+ ++$x;
+ }
+ }
+
+ function Circle($xc,$yc,$r) {
+ if( USE_BRESENHAM )
+ $this->BresenhamCircle($xc,$yc,$r);
+ else {
+
+ /*
+ // Some experimental code snippet to see if we can get a decent
+ // result doing a trig-circle
+ // Create an approximated circle with 0.05 rad resolution
+ $end = 2*M_PI;
+ $l = $r/10;
+ if( $l < 3 ) $l=3;
+ $step_size = 2*M_PI/(2*$r*M_PI/$l);
+ $pts = array();
+ $pts[] = $r + $xc;
+ $pts[] = $yc;
+ for( $a=$step_size; $a <= $end; $a += $step_size ) {
+ $pts[] = round($xc + $r*cos($a));
+ $pts[] = round($yc - $r*sin($a));
+ }
+ imagepolygon($this->img,$pts,count($pts)/2,$this->current_color);
+ */
+
+ $this->Arc($xc,$yc,$r*2,$r*2,0,360);
+
+ // For some reason imageellipse() isn't in GD 2.0.1, PHP 4.1.1
+ //imageellipse($this->img,$xc,$yc,$r,$r,$this->current_color);
+ }
+ }
+
+ function FilledCircle($xc,$yc,$r) {
+ if( $GLOBALS['gd2'] ) {
+ imagefilledellipse($this->img,round($xc),round($yc),
+ 2*$r,2*$r,$this->current_color);
+ }
+ else {
+ for( $i=1; $i < 2*$r; $i += 2 ) {
+ // To avoid moire patterns we have to draw some
+ // 1 extra "skewed" filled circles
+ $this->Arc($xc,$yc,$i,$i,0,360);
+ $this->Arc($xc,$yc,$i+1,$i,0,360);
+ $this->Arc($xc,$yc,$i+1,$i+1,0,360);
+ }
+ }
+ }
+
+ // Linear Color InterPolation
+ function lip($f,$t,$p) {
+ $p = round($p,1);
+ $r = $f[0] + ($t[0]-$f[0])*$p;
+ $g = $f[1] + ($t[1]-$f[1])*$p;
+ $b = $f[2] + ($t[2]-$f[2])*$p;
+ return array($r,$g,$b);
+ }
+
+ // Anti-aliased line.
+ // Note that this is roughly 8 times slower then a normal line!
+ function WuLine($x1,$y1,$x2,$y2) {
+ // Get foreground line color
+ $lc = imagecolorsforindex($this->img,$this->current_color);
+ $lc = array($lc["red"],$lc["green"],$lc["blue"]);
+
+ $dx = $x2-$x1;
+ $dy = $y2-$y1;
+
+ if( abs($dx) > abs($dy) ) {
+ if( $dx<0 ) {
+ $dx = -$dx;$dy = -$dy;
+ $tmp=$x2;$x2=$x1;$x1=$tmp;
+ $tmp=$y2;$y2=$y1;$y1=$tmp;
+ }
+ $x=$x1<<16; $y=$y1<<16;
+ $yinc = ($dy*65535)/$dx;
+ while( ($x >> 16) < $x2 ) {
+
+ $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
+ if( $bc <= 0 ) {
+ JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and that truecolor is enabled.');
+ }
+ $bc=array($bc["red"],$bc["green"],$bc["blue"]);
+
+ $this->SetColor($this->lip($lc,$bc,($y & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
+ $this->SetColor($this->lip($lc,$bc,(~$y & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,($y>>16)+1,$this->current_color);
+ $x += 65536; $y += $yinc;
+ }
+ }
+ else {
+ if( $dy<0 ) {
+ $dx = -$dx;$dy = -$dy;
+ $tmp=$x2;$x2=$x1;$x1=$tmp;
+ $tmp=$y2;$y2=$y1;$y1=$tmp;
+ }
+ $x=$x1<<16; $y=$y1<<16;
+ $xinc = ($dx*65535)/$dy;
+ while( ($y >> 16) < $y2 ) {
+
+ $bc = @imagecolorsforindex($this->img,imagecolorat($this->img,$x>>16,$y>>16));
+ if( $bc <= 0 ) {
+ JpGraphError::Raise('Problem with color palette and your GD setup. Please disable anti-aliasing or use GD2 with true-color. If you have GD2 library installed please make sure that you have set the USE_GD2 constant to true and truecolor is enabled.');
+
+ }
+
+ $bc=array($bc["red"],$bc["green"],$bc["blue"]);
+
+ $this->SetColor($this->lip($lc,$bc,($x & 0xFFFF)/65535));
+ imagesetpixel($this->img,$x>>16,$y>>16,$this->current_color);
+ $this->SetColor($this->lip($lc,$bc,(~$x & 0xFFFF)/65535));
+ imagesetpixel($this->img,($x>>16)+1,$y>>16,$this->current_color);
+ $y += 65536; $x += $xinc;
+ }
+ }
+ $this->SetColor($lc);
+ imagesetpixel($this->img,$x2,$y2,$this->current_color);
+ imagesetpixel($this->img,$x1,$y1,$this->current_color);
+ }
+
+ // Set line style dashed, dotted etc
+ function SetLineStyle($s) {
+ if( is_numeric($s) ) {
+ if( $s<1 || $s>5 )
+ JpGraphError::Raise(" Illegal numeric argument to SetLineStyle(): ($s)");
+ }
+ elseif( is_string($s) ) {
+ if( $s == "solid" ) $s=1;
+ elseif( $s == "dotted" ) $s=2;
+ elseif( $s == "dashed" ) $s=3;
+ elseif( $s == "longdashed" ) $s=4;
+ elseif( $s == "none" ) $s=5;
+ else JpGraphError::Raise(" Illegal string argument to SetLineStyle(): $s");
+ }
+ else JpGraphError::Raise(" Illegal argument to SetLineStyle $s");
+ $this->line_style=$s;
+ }
+
+ // Same as Line but take the line_style into account
+ function StyleLine($x1,$y1,$x2,$y2) {
+ switch( $this->line_style ) {
+ case 1:// Solid
+ $this->Line($x1,$y1,$x2,$y2);
+ break;
+ case 2: // Dotted
+ $this->DashedLine($x1,$y1,$x2,$y2,1,6);
+ break;
+ case 3: // Dashed
+ $this->DashedLine($x1,$y1,$x2,$y2,2,4);
+ break;
+ case 4: // Longdashes
+ $this->DashedLine($x1,$y1,$x2,$y2,8,6);
+ break;
+ case 5: // No-line
+ break;
+ default:
+ JpGraphError::Raise(" Unknown line style: $this->line_style ");
+ break;
+ }
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+
+ $x1 = round($x1);
+ $x2 = round($x2);
+ $y1 = round($y1);
+ $y2 = round($y2);
+
+ if( $this->line_weight==0 ) return;
+ if( $this->use_anti_aliasing ) {
+ $dx = $x2-$x1;
+ $dy = $y2-$y1;
+ // Vertical, Horizontal or 45 lines don't need anti-aliasing
+ if( $dx!=0 && $dy!=0 && $dx!=$dy ) {
+ $this->WuLine($x1,$y1,$x2,$y2);
+ return;
+ }
+ }
+ if( $this->line_weight==1 ) {
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ }
+ elseif( $x1==$x2 ) { // Special case for vertical lines
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ $w1=floor($this->line_weight/2);
+ $w2=floor(($this->line_weight-1)/2);
+ for($i=1; $i<=$w1; ++$i)
+ imageline($this->img,$x1+$i,$y1,$x2+$i,$y2,$this->current_color);
+ for($i=1; $i<=$w2; ++$i)
+ imageline($this->img,$x1-$i,$y1,$x2-$i,$y2,$this->current_color);
+ }
+ elseif( $y1==$y2 ) { // Special case for horizontal lines
+ imageline($this->img,$x1,$y1,$x2,$y2,$this->current_color);
+ $w1=floor($this->line_weight/2);
+ $w2=floor(($this->line_weight-1)/2);
+ for($i=1; $i<=$w1; ++$i)
+ imageline($this->img,$x1,$y1+$i,$x2,$y2+$i,$this->current_color);
+ for($i=1; $i<=$w2; ++$i)
+ imageline($this->img,$x1,$y1-$i,$x2,$y2-$i,$this->current_color);
+ }
+ else { // General case with a line at an angle
+ $a = atan2($y1-$y2,$x2-$x1);
+ // Now establish some offsets from the center. This gets a little
+ // bit involved since we are dealing with integer functions and we
+ // want the apperance to be as smooth as possible and never be thicker
+ // then the specified width.
+
+ // We do the trig stuff to make sure that the endpoints of the line
+ // are perpendicular to the line itself.
+ $dx=(sin($a)*$this->line_weight/2);
+ $dy=(cos($a)*$this->line_weight/2);
+
+ $pnts = array($x2+$dx,$y2+$dy,$x2-$dx,$y2-$dy,$x1-$dx,$y1-$dy,$x1+$dx,$y1+$dy);
+ imagefilledpolygon($this->img,$pnts,count($pnts)/2,$this->current_color);
+ }
+ $this->lastx=$x2; $this->lasty=$y2;
+ }
+
+ function Polygon($p,$closed=FALSE) {
+ if( $this->line_weight==0 ) return;
+ $n=count($p);
+ $oldx = $p[0];
+ $oldy = $p[1];
+ for( $i=2; $i < $n; $i+=2 ) {
+ $this->Line($oldx,$oldy,$p[$i],$p[$i+1]);
+ $oldx = $p[$i];
+ $oldy = $p[$i+1];
+ }
+ if( $closed )
+ $this->Line($oldx,$oldy,$p[0],$p[1]);
+ }
+
+ function FilledPolygon($pts) {
+ $n=count($pts);
+ if( $n == 0 ) {
+ JpGraphError::Raise('NULL data specified for a filled polygon. Check that your data is not NULL.');
+ }
+ for($i=0; $i < $n; ++$i)
+ $pts[$i] = round($pts[$i]);
+ imagefilledpolygon($this->img,$pts,count($pts)/2,$this->current_color);
+ }
+
+ function Rectangle($xl,$yu,$xr,$yl) {
+ $this->Polygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl,$xl,$yu));
+ }
+
+ function FilledRectangle($xl,$yu,$xr,$yl) {
+ $this->FilledPolygon(array($xl,$yu,$xr,$yu,$xr,$yl,$xl,$yl));
+ }
+
+ function FilledRectangle2($xl,$yu,$xr,$yl,$color1,$color2,$style=1) {
+ // Fill a rectangle with lines of two colors
+ if( $style===1 ) {
+ // Horizontal stripe
+ if( $yl < $yu ) {
+ $t = $yl; $yl=$yu; $yu=$t;
+ }
+ for( $y=$yu; $y <= $yl; ++$y) {
+ $this->SetColor($color1);
+ $this->Line($xl,$y,$xr,$y);
+ ++$y;
+ $this->SetColor($color2);
+ $this->Line($xl,$y,$xr,$y);
+ }
+ }
+ else {
+ if( $xl < $xl ) {
+ $t = $xl; $xl=$xr; $xr=$t;
+ }
+ for( $x=$xl; $x <= $xr; ++$x) {
+ $this->SetColor($color1);
+ $this->Line($x,$yu,$x,$yl);
+ ++$x;
+ $this->SetColor($color2);
+ $this->Line($x,$yu,$x,$yl);
+ }
+ }
+ }
+
+ function ShadowRectangle($xl,$yu,$xr,$yl,$fcolor=false,$shadow_width=3,$shadow_color=array(102,102,102)) {
+ // This is complicated by the fact that we must also handle the case where
+ // the reactangle has no fill color
+ $this->PushColor($shadow_color);
+ $this->FilledRectangle($xr-$shadow_width,$yu+$shadow_width,$xr,$yl-$shadow_width-1);
+ $this->FilledRectangle($xl+$shadow_width,$yl-$shadow_width,$xr,$yl);
+ //$this->FilledRectangle($xl+$shadow_width,$yu+$shadow_width,$xr,$yl);
+ $this->PopColor();
+ if( $fcolor==false )
+ $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ else {
+ $this->PushColor($fcolor);
+ $this->FilledRectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ $this->PopColor();
+ $this->Rectangle($xl,$yu,$xr-$shadow_width-1,$yl-$shadow_width-1);
+ }
+ }
+
+ function FilledRoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
+ if( $r==0 ) {
+ $this->FilledRectangle($xt,$yt,$xr,$yl);
+ return;
+ }
+
+ // To avoid overlapping fillings (which will look strange
+ // when alphablending is enabled) we have no choice but
+ // to fill the five distinct areas one by one.
+
+ // Center square
+ $this->FilledRectangle($xt+$r,$yt+$r,$xr-$r,$yl-$r);
+ // Top band
+ $this->FilledRectangle($xt+$r,$yt,$xr-$r,$yt+$r-1);
+ // Bottom band
+ $this->FilledRectangle($xt+$r,$yl-$r+1,$xr-$r,$yl);
+ // Left band
+ $this->FilledRectangle($xt,$yt+$r+1,$xt+$r-1,$yl-$r);
+ // Right band
+ $this->FilledRectangle($xr-$r+1,$yt+$r,$xr,$yl-$r);
+
+ // Topleft & Topright arc
+ $this->FilledArc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->FilledArc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Bottomleft & Bottom right arc
+ $this->FilledArc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->FilledArc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ }
+
+ function RoundedRectangle($xt,$yt,$xr,$yl,$r=5) {
+
+ if( $r==0 ) {
+ $this->Rectangle($xt,$yt,$xr,$yl);
+ return;
+ }
+
+ // Top & Bottom line
+ $this->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->Line($xt+$r,$yl,$xr-$r,$yl);
+
+ // Left & Right line
+ $this->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->Line($xr,$yt+$r,$xr,$yl-$r);
+
+ // Topleft & Topright arc
+ $this->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Bottomleft & Bottomright arc
+ $this->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+ }
+
+ function FilledBevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='darkgray@0.4') {
+ $this->FilledRectangle($x1,$y1,$x2,$y2);
+ $this->Bevel($x1,$y1,$x2,$y2,$depth,$color1,$color2);
+ }
+
+ function Bevel($x1,$y1,$x2,$y2,$depth=2,$color1='white@0.4',$color2='black@0.5') {
+ $this->PushColor($color1);
+ for( $i=0; $i < $depth; ++$i ) {
+ $this->Line($x1+$i,$y1+$i,$x1+$i,$y2-$i);
+ $this->Line($x1+$i,$y1+$i,$x2-$i,$y1+$i);
+ }
+ $this->PopColor();
+
+ $this->PushColor($color2);
+ for( $i=0; $i < $depth; ++$i ) {
+ $this->Line($x1+$i,$y2-$i,$x2-$i,$y2-$i);
+ $this->Line($x2-$i,$y1+$i,$x2-$i,$y2-$i-1);
+ }
+ $this->PopColor();
+ }
+
+ function StyleLineTo($x,$y) {
+ $this->StyleLine($this->lastx,$this->lasty,$x,$y);
+ $this->lastx=$x;
+ $this->lasty=$y;
+ }
+
+ function LineTo($x,$y) {
+ $this->Line($this->lastx,$this->lasty,$x,$y);
+ $this->lastx=$x;
+ $this->lasty=$y;
+ }
+
+ function Point($x,$y) {
+ imagesetpixel($this->img,round($x),round($y),$this->current_color);
+ }
+
+ function Fill($x,$y) {
+ imagefill($this->img,round($x),round($y),$this->current_color);
+ }
+
+ function FillToBorder($x,$y,$aBordColor) {
+ $bc = $this->rgb->allocate($aBordColor);
+ if( $bc == -1 ) {
+ JpGraphError::Raise('Image::FillToBorder : Can not allocate more colors');
+ exit();
+ }
+ imagefilltoborder($this->img,round($x),round($y),$bc,$this->current_color);
+ }
+
+ function DashedLine($x1,$y1,$x2,$y2,$dash_length=1,$dash_space=4) {
+ // Code based on, but not identical to, work by Ariel Garza and James Pine
+ $line_length = ceil (sqrt(pow(($x2 - $x1),2) + pow(($y2 - $y1),2)) );
+ $dx = ($line_length) ? ($x2 - $x1) / $line_length : 0;
+ $dy = ($line_length) ? ($y2 - $y1) / $line_length : 0;
+ $lastx = $x1; $lasty = $y1;
+ $xmax = max($x1,$x2);
+ $xmin = min($x1,$x2);
+ $ymax = max($y1,$y2);
+ $ymin = min($y1,$y2);
+ for ($i = 0; $i < $line_length; $i += ($dash_length + $dash_space)) {
+ $x = ($dash_length * $dx) + $lastx;
+ $y = ($dash_length * $dy) + $lasty;
+
+ // The last section might overshoot so we must take a computational hit
+ // and check this.
+ if( $x>$xmax ) $x=$xmax;
+ if( $y>$ymax ) $y=$ymax;
+
+ if( $x<$xmin ) $x=$xmin;
+ if( $y<$ymin ) $y=$ymin;
+
+ $this->Line($lastx,$lasty,$x,$y);
+ $lastx = $x + ($dash_space * $dx);
+ $lasty = $y + ($dash_space * $dy);
+ }
+ }
+
+ function SetExpired($aFlg=true) {
+ $this->expired = $aFlg;
+ }
+
+ // Generate image header
+ function Headers() {
+
+ // In case we are running from the command line with the client version of
+ // PHP we can't send any headers.
+ $sapi = php_sapi_name();
+ if( $sapi == 'cli' )
+ return;
+
+ if( headers_sent() ) {
+
+ echo "<table border=1><tr><td><font color=darkred size=4><b>JpGraph Error:</b>
+HTTP headers have already been sent.</font></td></tr><tr><td><b>Explanation:</b><br>HTTP headers have already been sent back to the browser indicating the data as text before the library got a chance to send it's image HTTP header to this browser. This makes it impossible for the library to send back image data to the browser (since that would be interpretated as text by the browser and show up as junk text).<p>Most likely you have some text in your script before the call to <i>Graph::Stroke()</i>. If this texts gets sent back to the browser the browser will assume that all data is plain text. Look for any text, even spaces and newlines, that might have been sent back to the browser. <p>For example it is a common mistake to leave a blank line before the opening \"<b>&lt;?php</b>\".</td></tr></table>";
+
+ die();
+
+ }
+
+ if ($this->expired) {
+ header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
+ header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
+ header("Cache-Control: no-cache, must-revalidate");
+ header("Pragma: no-cache");
+ }
+ header("Content-type: image/$this->img_format");
+ }
+
+ // Adjust image quality for formats that allow this
+ function SetQuality($q) {
+ $this->quality = $q;
+ }
+
+ // Stream image to browser or to file
+ function Stream($aFile="") {
+ $func="image".$this->img_format;
+ if( $this->img_format=="jpeg" && $this->quality != null ) {
+ $res = @$func($this->img,$aFile,$this->quality);
+ }
+ else {
+ if( $aFile != "" ) {
+ $res = @$func($this->img,$aFile);
+ }
+ else
+ $res = @$func($this->img);
+ }
+ if( !$res )
+ JpGraphError::Raise("Can't create or stream image to file $aFile Check that PHP has enough permission to write a file to the current directory.");
+ }
+
+ // Clear resource tide up by image
+ function Destroy() {
+ imagedestroy($this->img);
+ }
+
+ // Specify image format. Note depending on your installation
+ // of PHP not all formats may be supported.
+ function SetImgFormat($aFormat) {
+ $aFormat = strtolower($aFormat);
+ $tst = true;
+ $supported = imagetypes();
+ if( $aFormat=="auto" ) {
+ if( $supported & IMG_PNG )
+ $this->img_format="png";
+ elseif( $supported & IMG_JPG )
+ $this->img_format="jpeg";
+ elseif( $supported & IMG_GIF )
+ $this->img_format="gif";
+ else
+ JpGraphError::Raise(" Your PHP (and GD-lib) installation does not appear to support any known graphic formats.".
+ "You need to first make sure GD is compiled as a module to PHP. If you also want to use JPEG images".
+ "you must get the JPEG library. Please see the PHP docs for details.");
+
+ return true;
+ }
+ else {
+ if( $aFormat=="jpeg" || $aFormat=="png" || $aFormat=="gif" ) {
+ if( $aFormat=="jpeg" && !($supported & IMG_JPG) )
+ $tst=false;
+ elseif( $aFormat=="png" && !($supported & IMG_PNG) )
+ $tst=false;
+ elseif( $aFormat=="gif" && !($supported & IMG_GIF) )
+ $tst=false;
+ else {
+ $this->img_format=$aFormat;
+ return true;
+ }
+ }
+ else
+ $tst=false;
+ if( !$tst )
+ JpGraphError::Raise(" Your PHP installation does not support the chosen graphic format: $aFormat");
+ }
+ }
+} // CLASS
+
+//===================================================
+// CLASS RotImage
+// Description: Exactly as Image but draws the image at
+// a specified angle around a specified rotation point.
+//===================================================
+class RotImage extends Image {
+ var $m=array();
+ var $a=0;
+ var $dx=0,$dy=0,$transx=0,$transy=0;
+
+ function RotImage($aWidth,$aHeight,$a=0,$aFormat=DEFAULT_GFORMAT) {
+ $this->Image($aWidth,$aHeight,$aFormat);
+ $this->dx=$this->left_margin+$this->plotwidth/2;
+ $this->dy=$this->top_margin+$this->plotheight/2;
+ $this->SetAngle($a);
+ }
+
+ function SetCenter($dx,$dy) {
+ $old_dx = $this->dx;
+ $old_dy = $this->dy;
+ $this->dx=$dx;
+ $this->dy=$dy;
+ $this->SetAngle($this->a);
+ return array($old_dx,$old_dy);
+ }
+
+ function SetTranslation($dx,$dy) {
+ $old = array($this->transx,$this->transy);
+ $this->transx = $dx;
+ $this->transy = $dy;
+ return $old;
+ }
+
+ function UpdateRotMatrice() {
+ $a = $this->a;
+ $a *= M_PI/180;
+ $sa=sin($a); $ca=cos($a);
+ // Create the rotation matrix
+ $this->m[0][0] = $ca;
+ $this->m[0][1] = -$sa;
+ $this->m[0][2] = $this->dx*(1-$ca) + $sa*$this->dy ;
+ $this->m[1][0] = $sa;
+ $this->m[1][1] = $ca;
+ $this->m[1][2] = $this->dy*(1-$ca) - $sa*$this->dx ;
+ }
+
+ function SetAngle($a) {
+ $tmp = $this->a;
+ $this->a = $a;
+ $this->UpdateRotMatrice();
+ return $tmp;
+ }
+
+ function Circle($xc,$yc,$r) {
+ // Circle get's rotated through the Arc() call
+ // made in the parent class
+ parent::Circle($xc,$yc,$r);
+ }
+
+ function FilledCircle($xc,$yc,$r) {
+ // If we use GD1 then Image::FilledCircle will use a
+ // call to Arc so it will get rotated through the Arc
+ // call.
+ if( $GLOBALS['gd2'] ) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ }
+ parent::FilledCircle($xc,$yc,$r);
+ }
+
+
+ function Arc($xc,$yc,$w,$h,$s,$e) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ $s += $this->a;
+ $e += $this->a;
+ parent::Arc($xc,$yc,$w,$h,$s,$e);
+ }
+
+ function FilledArc($xc,$yc,$w,$h,$s,$e) {
+ list($xc,$yc) = $this->Rotate($xc,$yc);
+ $s += $this->a;
+ $e += $this->a;
+ parent::FilledArc($xc,$yc,$w,$h,$s,$e);
+ }
+
+ function SetMargin($lm,$rm,$tm,$bm) {
+ parent::SetMargin($lm,$rm,$tm,$bm);
+ $this->dx=$this->left_margin+$this->plotwidth/2;
+ $this->dy=$this->top_margin+$this->plotheight/2;
+ $this->UpdateRotMatrice();
+ }
+
+ function Rotate($x,$y) {
+ // Optimization. Ignore rotation if Angle==0 || ANgle==360
+ if( $this->a == 0 || $this->a == 360 ) {
+ return array($x + $this->transx, $y + $this->transy );
+ }
+ else {
+ $x1=round($this->m[0][0]*$x + $this->m[0][1]*$y,1) + $this->m[0][2] + $this->transx;
+ $y1=round($this->m[1][0]*$x + $this->m[1][1]*$y,1) + $this->m[1][2] + $this->transy;
+ return array($x1,$y1);
+ }
+ }
+
+ function ArrRotate($pnts) {
+ for($i=0; $i < count($pnts)-1; $i+=2)
+ list($pnts[$i],$pnts[$i+1]) = $this->Rotate($pnts[$i],$pnts[$i+1]);
+ return $pnts;
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->Rotate($x1,$y1);
+ list($x2,$y2) = $this->Rotate($x2,$y2);
+ parent::Line($x1,$y1,$x2,$y2);
+ }
+
+ function Rectangle($x1,$y1,$x2,$y2) {
+ // Rectangle uses Line() so it will be rotated through that call
+ parent::Rectangle($x1,$y1,$x2,$y2);
+ }
+
+ function FilledRectangle($x1,$y1,$x2,$y2) {
+ if( $y1==$y2 || $x1==$x2 )
+ $this->Line($x1,$y1,$x2,$y2);
+ else
+ $this->FilledPolygon(array($x1,$y1,$x2,$y1,$x2,$y2,$x1,$y2));
+ }
+
+ function Polygon($pnts,$closed=FALSE) {
+ //Polygon uses Line() so it will be rotated through that call
+ parent::Polygon($pnts,$closed);
+ }
+
+ function FilledPolygon($pnts) {
+ parent::FilledPolygon($this->ArrRotate($pnts));
+ }
+
+ function Point($x,$y) {
+ list($xp,$yp) = $this->Rotate($x,$y);
+ parent::Point($xp,$yp);
+ }
+
+ function StrokeText($x,$y,$txt,$dir=0,$paragraph_align="left",$debug=false) {
+ list($xp,$yp) = $this->Rotate($x,$y);
+ parent::StrokeText($xp,$yp,$txt,$dir,$paragraph_align,$debug);
+ }
+}
+
+//===================================================
+// CLASS ImgStreamCache
+// Description: Handle caching of graphs to files
+//===================================================
+class ImgStreamCache {
+ var $cache_dir;
+ var $img=null;
+ var $timeout=0; // Infinite timeout
+ //---------------
+ // CONSTRUCTOR
+ function ImgStreamCache(&$aImg, $aCacheDir=CACHE_DIR) {
+ $this->img = &$aImg;
+ $this->cache_dir = $aCacheDir;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Specify a timeout (in minutes) for the file. If the file is older then the
+ // timeout value it will be overwritten with a newer version.
+ // If timeout is set to 0 this is the same as infinite large timeout and if
+ // timeout is set to -1 this is the same as infinite small timeout
+ function SetTimeout($aTimeout) {
+ $this->timeout=$aTimeout;
+ }
+
+ // Output image to browser and also write it to the cache
+ function PutAndStream(&$aImage,$aCacheFileName,$aInline,$aStrokeFileName) {
+ // Some debugging code to brand the image with numbe of colors
+ // used
+ GLOBAL $gJpgBrandTiming;
+
+ if( $gJpgBrandTiming ) {
+ global $tim;
+ $t=$tim->Pop()/1000.0;
+ $c=$aImage->SetColor("black");
+ $t=sprintf(BRAND_TIME_FORMAT,round($t,3));
+ imagestring($this->img->img,2,5,$this->img->height-20,$t,$c);
+ }
+
+ // Check if we should stroke the image to an arbitrary file
+ if( _FORCE_IMGTOFILE ) {
+ $aStrokeFileName = _FORCE_IMGDIR.GenImgName();
+ }
+
+ if( $aStrokeFileName!="" ) {
+ if( $aStrokeFileName == "auto" )
+ $aStrokeFileName = GenImgName();
+ if( file_exists($aStrokeFileName) ) {
+ // Delete the old file
+ if( !@unlink($aStrokeFileName) )
+ JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
+ }
+ $aImage->Stream($aStrokeFileName);
+ return;
+ }
+
+ if( $aCacheFileName != "" && USE_CACHE) {
+
+ $aCacheFileName = $this->cache_dir . $aCacheFileName;
+ if( file_exists($aCacheFileName) ) {
+ if( !$aInline ) {
+ // If we are generating image off-line (just writing to the cache)
+ // and the file exists and is still valid (no timeout)
+ // then do nothing, just return.
+ $diff=time()-filemtime($aCacheFileName);
+ if( $diff < 0 )
+ JpGraphError::Raise(" Cached imagefile ($aCacheFileName) has file date in the future!!");
+ if( $this->timeout>0 && ($diff <= $this->timeout*60) )
+ return;
+ }
+ if( !@unlink($aCacheFileName) )
+ JpGraphError::Raise(" Can't delete cached image $aStrokeFileName. Permission problem?");
+ $aImage->Stream($aCacheFileName);
+ }
+ else {
+ $this->MakeDirs(dirname($aCacheFileName));
+ if( !is_writeable(dirname($aCacheFileName)) ) {
+ JpGraphError::Raise('PHP has not enough permissions to write to the cache file '.$aCacheFileName.'. Please make sure that the user running PHP has write permission for this file if you wan to use the cache system with JpGraph.');
+ }
+ $aImage->Stream($aCacheFileName);
+ }
+
+ $res=true;
+ // Set group to specified
+ if( CACHE_FILE_GROUP != "" )
+ $res = @chgrp($aCacheFileName,CACHE_FILE_GROUP);
+ if( CACHE_FILE_MOD != "" )
+ $res = @chmod($aCacheFileName,CACHE_FILE_MOD);
+ if( !$res )
+ JpGraphError::Raise(" Can't set permission for cached image $aStrokeFileName. Permission problem?");
+
+ $aImage->Destroy();
+ if( $aInline ) {
+ if ($fh = @fopen($aCacheFileName, "rb") ) {
+ $this->img->Headers();
+ fpassthru($fh);
+ return;
+ }
+ else
+ JpGraphError::Raise(" Cant open file from cache [$aFile]");
+ }
+ }
+ elseif( $aInline ) {
+ $this->img->Headers();
+ $aImage->Stream();
+ return;
+ }
+ }
+
+ // Check if a given image is in cache and in that case
+ // pass it directly on to web browser. Return false if the
+ // image file doesn't exist or exists but is to old
+ function GetAndStream($aCacheFileName) {
+ $aCacheFileName = $this->cache_dir.$aCacheFileName;
+ if ( USE_CACHE && file_exists($aCacheFileName) && $this->timeout>=0 ) {
+ $diff=time()-filemtime($aCacheFileName);
+ if( $this->timeout>0 && ($diff > $this->timeout*60) ) {
+ return false;
+ }
+ else {
+ if ($fh = @fopen($aCacheFileName, "rb")) {
+ $this->img->Headers();
+ fpassthru($fh);
+ return true;
+ }
+ else
+ JpGraphError::Raise(" Can't open cached image \"$aCacheFileName\" for reading.");
+ }
+ }
+ return false;
+ }
+
+ //---------------
+ // PRIVATE METHODS
+ // Create all necessary directories in a path
+ function MakeDirs($aFile) {
+ $dirs = array();
+ while ( !(file_exists($aFile)) ) {
+ $dirs[] = $aFile;
+ $aFile = dirname($aFile);
+ }
+ for ($i = sizeof($dirs)-1; $i>=0; $i--) {
+ if(! @mkdir($dirs[$i],0777) )
+ JpGraphError::Raise(" Can't create directory $aFile. Make sure PHP has write permission to this directory.");
+ // We also specify mode here after we have changed group.
+ // This is necessary if Apache user doesn't belong the
+ // default group and hence can't specify group permission
+ // in the previous mkdir() call
+ if( CACHE_FILE_GROUP != "" ) {
+ $res=true;
+ $res =@chgrp($dirs[$i],CACHE_FILE_GROUP);
+ $res &= @chmod($dirs[$i],0777);
+ if( !$res )
+ JpGraphError::Raise(" Can't set permissions for $aFile. Permission problems?");
+ }
+ }
+ return true;
+ }
+} // CLASS Cache
+
+//===================================================
+// CLASS Legend
+// Description: Responsible for drawing the box containing
+// all the legend text for the graph
+//===================================================
+DEFINE('_DEFAULT_LPM_SIZE',8);
+class Legend {
+ var $color=array(0,0,0); // Default fram color
+ var $fill_color=array(235,235,235); // Default fill color
+ var $shadow=true; // Shadow around legend "box"
+ var $shadow_color='gray';
+ var $txtcol=array();
+ var $mark_abs_size=_DEFAULT_LPM_SIZE;
+ var $xmargin=10,$ymargin=6,$shadow_width=2;
+ var $xpos=0.05, $ypos=0.15, $xabspos=-1, $yabspos=-1;
+ var $halign="right", $valign="top";
+ var $font_family=FF_FONT1,$font_style=FS_NORMAL,$font_size=12;
+ var $font_color='black';
+ var $hide=false,$layout_n=1;
+ var $weight=1,$frameweight=1;
+ var $csimareas='';
+ var $reverse = false ;
+//---------------
+// CONSTRUCTOR
+ function Legend() {
+ // Empty
+ }
+//---------------
+// PUBLIC METHODS
+ function Hide($aHide=true) {
+ $this->hide=$aHide;
+ }
+
+ function SetShadow($aShow='gray',$aWidth=2) {
+ if( is_string($aShow) ) {
+ $this->shadow_color = $aShow;
+ $this->shadow=true;
+ }
+ else
+ $this->shadow=$aShow;
+ $this->shadow_width=$aWidth;
+ }
+
+ function SetMarkAbsSize($aSize) {
+ $this->mark_abs_size = $aSize ;
+ }
+
+ function SetLineWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ function SetFrameWeight($aWeight) {
+ $this->frameweight = $aWeight;
+ }
+
+ function SetLayout($aDirection=LEGEND_VERT) {
+ $this->layout_n = $aDirection==LEGEND_VERT ? 1 : 99 ;
+ }
+
+ function SetColumns($aCols) {
+ $this->layout_n = $aCols ;
+ }
+
+ function SetLineSpacing($aSpacing) {
+ $this->ymargin = $aSpacing ;
+ }
+
+ function SetReverse($f=true) {
+ $this->reverse = $f ;
+ }
+
+ // Set color on frame around box
+ function SetColor($aFontColor,$aColor='black') {
+ $this->font_color=$aFontColor;
+ $this->color=$aColor;
+ }
+
+ function SetFont($aFamily,$aStyle=FS_NORMAL,$aSize=10) {
+ $this->font_family = $aFamily;
+ $this->font_style = $aStyle;
+ $this->font_size = $aSize;
+ }
+
+ function SetPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ $this->Pos($aX,$aY,$aHAlign,$aVAlign);
+ }
+
+ function SetAbsPos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ $this->xabspos=$aX;
+ $this->yabspos=$aY;
+ $this->halign=$aHAlign;
+ $this->valign=$aVAlign;
+ }
+
+
+ function Pos($aX,$aY,$aHAlign="right",$aVAlign="top") {
+ if( !($aX<1 && $aY<1) )
+ JpGraphError::Raise(" Position for legend must be given as percentage in range 0-1");
+ $this->xpos=$aX;
+ $this->ypos=$aY;
+ $this->halign=$aHAlign;
+ $this->valign=$aVAlign;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill_color=$aColor;
+ }
+
+ function Add($aTxt,$aColor,$aPlotmark="",$aLinestyle=0,$csimtarget="",$csimalt="") {
+ $this->txtcol[]=array($aTxt,$aColor,$aPlotmark,$aLinestyle,$csimtarget,$csimalt);
+ }
+
+ function GetCSIMAreas() {
+ return $this->csimareas;
+ }
+
+ function Stroke(&$aImg) {
+ if( $this->hide ) return;
+
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+
+ if( $this->reverse ) {
+ $this->txtcol = array_reverse($this->txtcol);
+ }
+
+ $n=count($this->txtcol);
+ if( $n == 0 ) return;
+
+ // Find out the max width and height of each column to be able
+ // to size the legend box.
+ $numcolumns = ($n > $this->layout_n ? $this->layout_n : $n);
+ for( $i=0; $i < $numcolumns; ++$i ) {
+ $colwidth[$i] = $aImg->GetTextWidth($this->txtcol[$i][0]) +
+ 2*$this->xmargin + 2*$this->mark_abs_size;
+ $colheight[$i] = 0;
+ }
+
+ // Find our maximum height in each row
+ $rows = -1 ;
+ for( $i=0; $i < $n; ++$i ) {
+ $h = max($this->mark_abs_size,$aImg->GetTextHeight($this->txtcol[$i][0]))+$this->ymargin;
+ if( $i % $numcolumns == 0 ) {
+ $rows++;
+ $rowheight[$rows] = 0;
+ }
+ $rowheight[$rows] = max($rowheight[$rows],$h);
+ }
+
+ $abs_height = 0;
+ for( $i=0; $i <= $rows; ++$i ) {
+ $abs_height += $rowheight[$i] ;
+ }
+
+ // Make sure that the height is at least as high as mark size + ymargin
+ $abs_height = max($abs_height,$this->mark_abs_size+$this->ymargin);
+ $abs_height += 2*$this->ymargin;
+
+ // Find out the maximum width in each column
+ for( $i=$numcolumns; $i < $n; ++$i ) {
+ $colwidth[$i % $numcolumns] = max(
+ $aImg->GetTextWidth($this->txtcol[$i][0])+2*$this->xmargin+2*$this->mark_abs_size,
+ $colwidth[$i % $numcolumns]);
+ }
+
+ // Get the total width
+ $mtw = 0;
+ for( $i=0; $i < $numcolumns; ++$i ) {
+ $mtw += $colwidth[$i] + $this->xmargin;
+ }
+
+ // Find out maximum width we need for legend box
+ $abs_width = $mtw+$this->xmargin;
+
+ if( $this->xabspos === -1 && $this->yabspos === -1 ) {
+ $this->xabspos = $this->xpos*$aImg->width ;
+ $this->yabspos = $this->ypos*$aImg->height ;
+ }
+
+ // Positioning of the legend box
+ if( $this->halign=="left" )
+ $xp = $this->xabspos;
+ elseif( $this->halign=="center" )
+ $xp = $this->xabspos - $abs_width/2;
+ else
+ $xp = $aImg->width - $this->xabspos - $abs_width;
+
+ $yp=$this->yabspos;
+ if( $this->valign=="center" )
+ $yp-=$abs_height/2;
+ elseif( $this->valign=="bottom" )
+ $yp-=$abs_height;
+
+ // Stroke legend box
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->frameweight);
+
+ if( $this->shadow )
+ $aImg->ShadowRectangle($xp,$yp,$xp+$abs_width+$this->shadow_width,
+ $yp+$abs_height+$this->shadow_width,
+ $this->fill_color,$this->shadow_width,$this->shadow_color);
+ else {
+ $aImg->SetColor($this->fill_color);
+ $aImg->FilledRectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
+ $aImg->SetColor($this->color);
+ $aImg->Rectangle($xp,$yp,$xp+$abs_width,$yp+$abs_height);
+ }
+
+ // x1,y1 is the position for the legend mark
+ $aImg->SetLineWeight($this->weight);
+ $x1=$xp+$this->mark_abs_size+8;
+ $y1=$yp + $this->mark_abs_size/2 + $this->ymargin/2;
+
+ $f2 = round($aImg->GetTextHeight('X')/2);
+
+ $grad = new Gradient($aImg);
+
+ // Now stroke each legend in turn
+ $i = 1 ; $row = 0;
+ foreach($this->txtcol as $p) {
+ $x1 = round($x1); $y1=round($y1);
+ if ( $p[2] != "" && $p[2]->GetType() > -1 ) {
+ // Make a plot mark legend
+ $aImg->SetColor($p[1]);
+ if( $p[3] > 0 ) {
+ $aImg->SetLineStyle($p[3]);
+ $aImg->StyleLine($x1-$this->mark_abs_size,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
+ }
+ // Stroke a mark with the standard size
+ // (As long as it is not an image mark )
+ if( $p[2]->GetType() != MARK_IMG ) {
+ $p[2]->iFormatCallback = '';
+
+ // Since size for circles is specified as the radius
+ // this means that we must half the size to make the total
+ // width behave as the other marks
+
+ if( $p[2]->GetType() == MARK_FILLEDCIRCLE ||
+ $p[2]->GetType() == MARK_CIRCLE ) {
+ $p[2]->SetSize($this->mark_abs_size/2);
+ $p[2]->Stroke($aImg,$x1,$y1+$f2);
+
+ }
+ else {
+ $p[2]->SetSize($this->mark_abs_size);
+ $p[2]->Stroke($aImg,$x1,$y1+$f2);
+ }
+ }
+ }
+ elseif ( $p[2] != "" && (is_string($p[3]) || $p[3]>0 ) ) {
+ // Draw a styled line
+ $aImg->SetColor($p[1]);
+ $aImg->SetLineStyle($p[3]);
+ $aImg->StyleLine($x1-1,$y1+$f2,$x1+$this->mark_abs_size,$y1+$f2);
+ $aImg->StyleLine($x1-1,$y1+$f2+1,$x1+$this->mark_abs_size,$y1+$f2+1);
+ }
+ else {
+ // Draw a colored box
+ $color = $p[1] ;
+ $ym = round($y1 + $f2 - $this->mark_abs_size/2);
+ if( is_array($color) && count($color)==3 ) {
+ // The client want a gradient color
+ $grad->FilledRectangle($x1,$ym,
+ $x1+$this->mark_abs_size,$ym+$this->mark_abs_size,
+ $color[0],$color[1],$color[2]);
+ }
+ else {
+ $aImg->SetColor($p[1]);
+ $aImg->FilledRectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
+ }
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+ $aImg->Rectangle($x1,$ym,$x1+$this->mark_abs_size,$ym+$this->mark_abs_size);
+ }
+ $aImg->SetColor($this->font_color);
+ $aImg->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $aImg->SetTextAlign("left","top");
+ $aImg->StrokeText(round($x1+$this->mark_abs_size+$this->xmargin),$y1,$p[0]);
+
+ // Add CSIM for Legend if defined
+ if( $p[4] != "" ) {
+ $xe = $x1 + $this->xmargin+$this->mark_abs_size+$aImg->GetTextWidth($p[0]);
+ $ye = $y1 + max($this->mark_abs_size,$aImg->GetTextHeight($p[0]));
+ $coords = "$x1,$y1,$xe,$y1,$xe,$ye,$x1,$ye";
+ if( ! empty($p[4]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$p[4]."\"";
+ if( !empty($p[5]) ) {
+ $tmp=sprintf($p[5],$p[0]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+ if( $i >= $this->layout_n ) {
+ $x1 = $xp+$this->mark_abs_size+8;
+ //$y1 += max($aImg->GetTextHeight($p[0]),$this->mark_abs_size)+$this->ymargin;
+ $y1 += $rowheight[$row++];
+ $i = 1;
+ }
+ else {
+ $x1 += $colwidth[($i-1) % $numcolumns] + $this->xmargin;
+ ++$i;
+ }
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS DisplayValue
+// Description: Used to print data values at data points
+//===================================================
+class DisplayValue {
+ var $show=false,$format="%.1f",$negformat="";
+ var $iFormCallback='';
+ var $angle=0;
+ var $ff=FF_FONT1,$fs=FS_NORMAL,$fsize=10;
+ var $color="navy",$negcolor="";
+ var $margin=5,$valign="",$halign="center";
+ var $iHideZero=false;
+
+ function Show($aFlag=true) {
+ $this->show=$aFlag;
+ }
+
+ function SetColor($aColor,$aNegcolor="") {
+ $this->color = $aColor;
+ $this->negcolor = $aNegcolor;
+ }
+
+ function SetFont($aFontFamily,$aFontStyle=FS_NORMAL,$aFontSize=10) {
+ $this->ff=$aFontFamily;
+ $this->fs=$aFontStyle;
+ $this->fsize=$aFontSize;
+ }
+
+ function SetMargin($aMargin) {
+ $this->margin = $aMargin;
+ }
+
+ function SetAngle($aAngle) {
+ $this->angle = $aAngle;
+ }
+
+ function SetAlign($aHAlign,$aVAlign='') {
+ $this->halign = $aHAlign;
+ $this->valign = $aVAlign;
+ }
+
+ function SetFormat($aFormat,$aNegFormat="") {
+ $this->format= $aFormat;
+ $this->negformat= $aNegFormat;
+ }
+
+ function SetFormatCallback($aFunc) {
+ $this->iFormCallback = $aFunc;
+ }
+
+ function HideZero($aFlag=true) {
+ $this->iHideZero=$aFlag;
+ }
+
+ function Stroke($img,$aVal,$x,$y) {
+
+ if( $this->show )
+ {
+ if( $this->negformat=="" ) $this->negformat=$this->format;
+ if( $this->negcolor=="" ) $this->negcolor=$this->color;
+
+ if( $aVal===NULL || (is_string($aVal) && ($aVal=="" || $aVal=="-" || $aVal=="x" ) ) )
+ return;
+
+ if( is_numeric($aVal) && $aVal==0 && $this->iHideZero ) {
+ return;
+ }
+
+ // Since the value is used in different cirumstances we need to check what
+ // kind of formatting we shall use. For example, to display values in a line
+ // graph we simply display the formatted value, but in the case where the user
+ // has already specified a text string we don't fo anything.
+ if( $this->iFormCallback != '' ) {
+ $f = $this->iFormCallback;
+ $sval = call_user_func($f,$aVal);
+ }
+ elseif( is_numeric($aVal) ) {
+ if( $aVal >= 0 )
+ $sval=sprintf($this->format,$aVal);
+ else
+ $sval=sprintf($this->negformat,$aVal);
+ }
+ else
+ $sval=$aVal;
+
+ $y = $y-sign($aVal)*$this->margin;
+
+ $txt = new Text($sval,$x,$y);
+ $txt->SetFont($this->ff,$this->fs,$this->fsize);
+ if( $this->valign == "" ) {
+ if( $aVal >= 0 )
+ $valign = "bottom";
+ else
+ $valign = "top";
+ }
+ else
+ $valign = $this->valign;
+ $txt->Align($this->halign,$valign);
+
+ $txt->SetOrientation($this->angle);
+ if( $aVal > 0 )
+ $txt->SetColor($this->color);
+ else
+ $txt->SetColor($this->negcolor);
+ $txt->Stroke($img);
+ }
+ }
+}
+
+//===================================================
+// CLASS IconPlot
+// Description: Make it possible to add a (small) image
+// to the graph
+//===================================================
+class IconPlot {
+ var $iHorAnchor='left',$iVertAnchor='top';
+ var $iX=0,$iY=0;
+ var $iFile='';
+ var $iScale=1.0,$iMix=100;
+ var $iAnchors = array('left','right','top','bottom','center');
+ var $iCountryFlag='',$iCountryStdSize=3;
+
+ function IconPlot($aFile="",$aX=0,$aY=0,$aScale=1.0,$aMix=100) {
+ $this->iFile = $aFile;
+ $this->iX=$aX;
+ $this->iY=$aY;
+ $this->iScale= $aScale;
+ if( $aMix < 0 || $aMix > 100 ) {
+ JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
+ }
+ $this->iMix = $aMix ;
+ }
+
+ function SetCountryFlag($aFlag,$aX=0,$aY=0,$aScale=1.0,$aMix=100,$aStdSize=3) {
+ $this->iCountryFlag = $aFlag;
+ $this->iX=$aX;
+ $this->iY=$aY;
+ $this->iScale= $aScale;
+ if( $aMix < 0 || $aMix > 100 ) {
+ JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
+ }
+ $this->iMix = $aMix;
+ $this->iCountryStdSize = $aStdSize;
+ }
+
+ function SetPos($aX,$aY) {
+ $this->iX=$aX;
+ $this->iY=$aY;
+ }
+
+ function SetScale($aScale) {
+ $this->iScale = $aScale;
+ }
+
+ function SetMix($aMix) {
+ if( $aMix < 0 || $aMix > 100 ) {
+ JpGraphError::Raise('Mix value for icon must be between 0 and 100.');
+ }
+ $this->iMix = $aMix ;
+ }
+
+ function SetAnchor($aXAnchor='left',$aYAnchor='center') {
+ if( !in_array($aXAnchor,$this->iAnchors) ||
+ !in_array($aYAnchor,$this->iAnchors) ) {
+ JpGraphError::Raise("Anchor position for icons must be one of 'top', 'bottom', 'left', 'right' or 'center'");
+ }
+ $this->iHorAnchor=$aXAnchor;
+ $this->iVertAnchor=$aYAnchor;
+ }
+
+ function Stroke($aImg) {
+ if( $this->iFile != '' && $this->iCountryFlag != '' ) {
+ JpGraphError::Raise('It is not possible to specify both an image file and a country flag for the same icon.');
+ }
+ if( $this->iFile != '' ) {
+ $gdimg = Graph::LoadBkgImage('',$this->iFile);
+ }
+ else {
+ if( ! class_exists('FlagImages') ) {
+ JpGraphError::Raise('In order to use Country flags as icons you must include the "jpgraph_flags.php" file.');
+ }
+ $fobj = new FlagImages($this->iCountryStdSize);
+ $dummy='';
+ $gdimg = $fobj->GetImgByName($this->iCountryFlag,$dummy);
+ }
+ if( $this->iX >= 0 && $this->iX <= 1.0 ) {
+ $w = imagesx($aImg->img);
+ $this->iX = round($w*$this->iX);
+ }
+ if( $this->iY >= 0 && $this->iY <= 1.0 ) {
+ $h = imagesy($aImg->img);
+ $this->iY = round($h*$this->iY);
+ }
+ $iconw = imagesx($gdimg);
+ $iconh = imagesy($gdimg);
+
+ if( $this->iHorAnchor == 'center' )
+ $this->iX -= round($iconw*$this->iScale/2);
+ if( $this->iHorAnchor == 'right' )
+ $this->iX -= round($iconw*$this->iScale);
+ if( $this->iVertAnchor == 'center' )
+ $this->iY -= round($iconh*$this->iScale/2);
+ if( $this->iVertAnchor == 'bottom' )
+ $this->iY -= round($iconh*$this->iScale);
+
+ $aImg->CopyMerge($gdimg,$this->iX,$this->iY,0,0,
+ round($iconw*$this->iScale),round($iconh*$this->iScale),
+ $iconw,$iconh,
+ $this->iMix);
+ }
+}
+
+
+//===================================================
+// CLASS Plot
+// Description: Abstract base class for all concrete plot classes
+//===================================================
+class Plot {
+ var $line_weight=1;
+ var $coords=array();
+ var $legend='',$hidelegend=false;
+ var $csimtargets=array(); // Array of targets for CSIM
+ var $csimareas=""; // Resultant CSIM area tags
+ var $csimalts=null; // ALT:s for corresponding target
+ var $color="black";
+ var $numpoints=0;
+ var $weight=1;
+ var $value;
+ var $center=false;
+ var $legendcsimtarget='';
+ var $legendcsimalt='';
+//---------------
+// CONSTRUCTOR
+ function Plot(&$aDatay,$aDatax=false) {
+ $this->numpoints = count($aDatay);
+ if( $this->numpoints==0 )
+ JpGraphError::Raise(" Empty data array specified for plot. Must have at least one data point.");
+ $this->coords[0]=$aDatay;
+ if( is_array($aDatax) )
+ $this->coords[1]=$aDatax;
+ $this->value = new DisplayValue();
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Stroke the plot
+ // "virtual" function which must be implemented by
+ // the subclasses
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ JpGraphError::Raise("JpGraph: Stroke() must be implemented by concrete subclass to class Plot");
+ }
+
+ function HideLegend($f=true) {
+ $this->hidelegend = $f;
+ }
+
+ function DoLegend(&$graph) {
+ if( !$this->hidelegend )
+ $this->Legend($graph);
+ }
+
+ function StrokeDataValue($img,$aVal,$x,$y) {
+ $this->value->Stroke($img,$aVal,$x,$y);
+ }
+
+ // Set href targets for CSIM
+ function SetCSIMTargets($aTargets,$aAlts=null) {
+ $this->csimtargets=$aTargets;
+ $this->csimalts=$aAlts;
+ }
+
+ // Get all created areas
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ // "Virtual" function which gets called before any scale
+ // or axis are stroked used to do any plot specific adjustment
+ function PreStrokeAdjust(&$aGraph) {
+ if( substr($aGraph->axtype,0,4) == "text" && (isset($this->coords[1])) )
+ JpGraphError::Raise("JpGraph: You can't use a text X-scale with specified X-coords. Use a \"int\" or \"lin\" scale instead.");
+ return true;
+ }
+
+ // Get minimum values in plot
+ function Min() {
+ if( isset($this->coords[1]) )
+ $x=$this->coords[1];
+ else
+ $x="";
+ if( $x != "" && count($x) > 0 )
+ $xm=min($x);
+ else
+ $xm=0;
+ $y=$this->coords[0];
+ if( count($y) > 0 ) {
+ $ym = $y[0];
+ $cnt = count($y);
+ $i=0;
+ while( $i<$cnt && !is_numeric($ym=$y[$i]) )
+ $i++;
+ while( $i < $cnt) {
+ if( is_numeric($y[$i]) )
+ $ym=min($ym,$y[$i]);
+ ++$i;
+ }
+ }
+ else
+ $ym="";
+ return array($xm,$ym);
+ }
+
+ // Get maximum value in plot
+ function Max() {
+ if( isset($this->coords[1]) )
+ $x=$this->coords[1];
+ else
+ $x="";
+
+ if( $x!="" && count($x) > 0 )
+ $xm=max($x);
+ else {
+ //$xm=count($this->coords[0])-1; // We count from 0..(n-1)
+ $xm = $this->numpoints-1;
+ }
+ $y=$this->coords[0];
+ if( count($y) > 0 ) {
+ if( !isset($y[0]) ) {
+ $y[0] = 0;
+// Change in 1.5.1 Don't treat this as an error any more. Just silently concert to 0
+// JpGraphError::Raise(" You have not specified a y[0] value!!");
+ }
+ $cnt = count($y);
+ $i=0;
+ while( $i<$cnt && !is_numeric($ym=$y[$i]) )
+ $i++;
+ while( $i < $cnt ) {
+ if( is_numeric($y[$i]) ) $ym=max($ym,$y[$i]);
+ ++$i;
+ }
+ }
+ else
+ $ym="";
+ return array($xm,$ym);
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
+ $this->legend = $aLegend;
+ $this->legendcsimtarget = $aCSIM;
+ $this->legendcsimalt = $aCSIMAlt;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ function SetLineWeight($aWeight=1) {
+ $this->line_weight=$aWeight;
+ }
+
+ function SetCenter($aCenter=true) {
+ $this->center = $aCenter;
+ }
+
+ // This method gets called by Graph class to plot anything that should go
+ // into the margin after the margin color has been set.
+ function StrokeMargin(&$aImg) {
+ return true;
+ }
+
+ // Framework function the chance for each plot class to set a legend
+ function Legend(&$aGraph) {
+ if( $this->legend != '' )
+ {
+ if($this->legendColor != '')
+ $aGraph->legend->Add($this->legend,$this->legendColor,'',0,$this->legendcsimtarget,$this->legendcsimalt);
+ else
+ $aGraph->legend->Add($this->legend,$this->color,'',0,$this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+
+} // Class
+
+require_once "jpgraph_plotmark.inc" ;
+
+//==============================================================================
+// The following section contains classes to implement the "band" functionality
+//==============================================================================
+
+// Utility class to hold coordinates for a rectangle
+class Rectangle {
+ var $x,$y,$w,$h;
+ var $xe, $ye;
+ function Rectangle($aX,$aY,$aWidth,$aHeight) {
+ $this->x=$aX;
+ $this->y=$aY;
+ $this->w=$aWidth;
+ $this->h=$aHeight;
+ $this->xe=$aX+$aWidth-1;
+ $this->ye=$aY+$aHeight-1;
+ }
+}
+
+//=====================================================================
+// Class RectPattern
+// Base class for pattern hierarchi that is used to display patterned
+// bands on the graph. Any subclass that doesn't override Stroke()
+// must at least implement method DoPattern(&$aImg) which is responsible
+// for drawing the pattern onto the graph.
+//=====================================================================
+class RectPattern {
+ var $color;
+ var $weight;
+ var $rect=null;
+ var $doframe=true;
+ var $linespacing; // Line spacing in pixels
+ var $iBackgroundColor=-1; // Default is no background fill
+
+ function RectPattern($aColor,$aWeight=1) {
+ $this->color = $aColor;
+ $this->weight = $aWeight;
+ }
+
+ function SetBackground($aBackgroundColor) {
+ $this->iBackgroundColor=$aBackgroundColor;
+ }
+
+ function SetPos(&$aRect) {
+ $this->rect = $aRect;
+ }
+
+ function ShowFrame($aShow=true) {
+ $this->doframe=$aShow;
+ }
+
+ function SetDensity($aDens) {
+ if( $aDens < 1 || $aDens > 100 )
+ JpGraphError::Raise(" Desity for pattern must be between 1 and 100. (You tried $aDens)");
+ // 1% corresponds to linespacing=50
+ // 100 % corresponds to linespacing 1
+ $this->linespacing = floor(((100-$aDens)/100.0)*50)+1;
+
+ }
+
+ function Stroke(&$aImg) {
+ if( $this->rect == null )
+ JpGraphError::Raise(" No positions specified for pattern.");
+
+ if( !(is_numeric($this->iBackgroundColor) && $this->iBackgroundColor==-1) ) {
+ $aImg->SetColor($this->iBackgroundColor);
+ $aImg->FilledRectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
+ }
+
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+
+ // Virtual function implemented by subclass
+ $this->DoPattern($aImg);
+
+ // Frame around the pattern area
+ if( $this->doframe )
+ $aImg->Rectangle($this->rect->x,$this->rect->y,$this->rect->xe,$this->rect->ye);
+ }
+
+}
+
+
+//=====================================================================
+// Class RectPatternSolid
+// Implements a solid band
+//=====================================================================
+class RectPatternSolid extends RectPattern {
+
+ function RectPatternSolid($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ }
+
+ function DoPattern(&$aImg) {
+ $aImg->SetColor($this->color);
+ $aImg->FilledRectangle($this->rect->x,$this->rect->y,
+ $this->rect->xe,$this->rect->ye);
+ }
+}
+
+//=====================================================================
+// Class RectPatternHor
+// Implements horizontal line pattern
+//=====================================================================
+class RectPatternHor extends RectPattern {
+
+ function RectPatternHor($aColor="black",$aWeight=1,$aLineSpacing=7) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ function DoPattern(&$aImg) {
+ $x0 = $this->rect->x;
+ $x1 = $this->rect->xe;
+ $y = $this->rect->y;
+ while( $y < $this->rect->ye ) {
+ $aImg->Line($x0,$y,$x1,$y);
+ $y += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternVert
+// Implements vertical line pattern
+//=====================================================================
+class RectPatternVert extends RectPattern {
+ var $linespacing=10; // Line spacing in pixels
+
+ function RectPatternVert($aColor="black",$aWeight=1,$aLineSpacing=7) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ //--------------------
+ // Private methods
+ //
+ function DoPattern(&$aImg) {
+ $x = $this->rect->x;
+ $y0 = $this->rect->y;
+ $y1 = $this->rect->ye;
+ while( $x < $this->rect->xe ) {
+ $aImg->Line($x,$y0,$x,$y1);
+ $x += $this->linespacing;
+ }
+ }
+}
+
+
+//=====================================================================
+// Class RectPatternRDiag
+// Implements right diagonal pattern
+//=====================================================================
+class RectPatternRDiag extends RectPattern {
+ var $linespacing; // Line spacing in pixels
+
+ function RectPatternRDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->linespacing = $aLineSpacing;
+ }
+
+ function DoPattern(&$aImg) {
+ // --------------------
+ // | / / / / /|
+ // |/ / / / / |
+ // | / / / / |
+ // --------------------
+ $xe = $this->rect->xe;
+ $ye = $this->rect->ye;
+ $x0 = $this->rect->x + round($this->linespacing/2);
+ $y0 = $this->rect->y;
+ $x1 = $this->rect->x;
+ $y1 = $this->rect->y + round($this->linespacing/2);
+
+ while($x0<=$xe && $y1<=$ye) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $y1 += $this->linespacing;
+ }
+
+ if( $xe-$x1 > $ye-$y0 ) {
+ // Width larger than height
+ $x1 = $this->rect->x + ($y1-$ye);
+ $y1 = $ye;
+ $y0 = $this->rect->y;
+ while( $x0 <= $xe ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+
+ $y0=$this->rect->y + ($x0-$xe);
+ $x0=$xe;
+ }
+ else {
+ // Height larger than width
+ $diff = $x0-$xe;
+ $y0 = $diff+$this->rect->y;
+ $x0 = $xe;
+ $x1 = $this->rect->x;
+ while( $y1 <= $ye ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y1 += $this->linespacing;
+ $y0 += $this->linespacing;
+ }
+
+ $diff = $y1-$ye;
+ $y1 = $ye;
+ $x1 = $diff + $this->rect->x;
+ }
+
+ while( $y0 <= $ye ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternLDiag
+// Implements left diagonal pattern
+//=====================================================================
+class RectPatternLDiag extends RectPattern {
+ var $linespacing; // Line spacing in pixels
+
+ function RectPatternLDiag($aColor="black",$aWeight=1,$aLineSpacing=12) {
+ $this->linespacing = $aLineSpacing;
+ parent::RectPattern($aColor,$aWeight);
+ }
+
+ function DoPattern(&$aImg) {
+ // --------------------
+ // |\ \ \ \ \ |
+ // | \ \ \ \ \|
+ // | \ \ \ \ |
+ // |------------------|
+ $xe = $this->rect->xe;
+ $ye = $this->rect->ye;
+ $x0 = $this->rect->x + round($this->linespacing/2);
+ $y0 = $this->rect->ye;
+ $x1 = $this->rect->x;
+ $y1 = $this->rect->ye - round($this->linespacing/2);
+
+ while($x0<=$xe && $y1>=$this->rect->y) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $y1 -= $this->linespacing;
+ }
+ if( $xe-$x1 > $ye-$this->rect->y ) {
+ // Width larger than height
+ $x1 = $this->rect->x + ($this->rect->y-$y1);
+ $y0=$ye; $y1=$this->rect->y;
+ while( $x0 <= $xe ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $x0 += $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+
+ $y0=$this->rect->ye - ($x0-$xe);
+ $x0=$xe;
+ }
+ else {
+ // Height larger than width
+ $diff = $x0-$xe;
+ $y0 = $ye-$diff;
+ $x0 = $xe;
+ while( $y1 >= $this->rect->y ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 -= $this->linespacing;
+ $y1 -= $this->linespacing;
+ }
+ $diff = $this->rect->y - $y1;
+ $x1 = $this->rect->x + $diff;
+ $y1 = $this->rect->y;
+ }
+ while( $y0 >= $this->rect->y ) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $y0 -= $this->linespacing;
+ $x1 += $this->linespacing;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPattern3DPlane
+// Implements "3D" plane pattern
+//=====================================================================
+class RectPattern3DPlane extends RectPattern {
+ var $alpha=50; // Parameter that specifies the distance
+ // to "simulated" horizon in pixel from the
+ // top of the band. Specifies how fast the lines
+ // converge.
+
+ function RectPattern3DPlane($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->SetDensity(10); // Slightly larger default
+ }
+
+ function SetHorizon($aHorizon) {
+ $this->alpha=$aHorizon;
+ }
+
+ function DoPattern(&$aImg) {
+ // "Fake" a nice 3D grid-effect.
+ $x0 = $this->rect->x + $this->rect->w/2;
+ $y0 = $this->rect->y;
+ $x1 = $x0;
+ $y1 = $this->rect->ye;
+ $x0_right = $x0;
+ $x1_right = $x1;
+
+ // BTW "apa" means monkey in Swedish but is really a shortform for
+ // "alpha+a" which was the labels I used on paper when I derived the
+ // geometric to get the 3D perspective right.
+ // $apa is the height of the bounding rectangle plus the distance to the
+ // artifical horizon (alpha)
+ $apa = $this->rect->h + $this->alpha;
+
+ // Three cases and three loops
+ // 1) The endpoint of the line ends on the bottom line
+ // 2) The endpoint ends on the side
+ // 3) Horizontal lines
+
+ // Endpoint falls on bottom line
+ $middle=$this->rect->x + $this->rect->w/2;
+ $dist=$this->linespacing;
+ $factor=$this->alpha /($apa);
+ while($x1>$this->rect->x) {
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $aImg->Line($x0_right,$y0,$x1_right,$y1);
+ $x1 = $middle - $dist;
+ $x0 = $middle - $dist * $factor;
+ $x1_right = $middle + $dist;
+ $x0_right = $middle + $dist * $factor;
+ $dist += $this->linespacing;
+ }
+
+ // Endpoint falls on sides
+ $dist -= $this->linespacing;
+ $d=$this->rect->w/2;
+ $c = $apa - $d*$apa/$dist;
+ while( $x0>$this->rect->x ) {
+ $aImg->Line($x0,$y0,$this->rect->x,$this->rect->ye-$c);
+ $aImg->Line($x0_right,$y0,$this->rect->xe,$this->rect->ye-$c);
+ $dist += $this->linespacing;
+ $x0 = $middle - $dist * $factor;
+ $x1 = $middle - $dist;
+ $x0_right = $middle + $dist * $factor;
+ $c = $apa - $d*$apa/$dist;
+ }
+
+ // Horizontal lines
+ // They need some serious consideration since they are a function
+ // of perspective depth (alpha) and density (linespacing)
+ $x0=$this->rect->x;
+ $x1=$this->rect->xe;
+ $y=$this->rect->ye;
+
+ // The first line is drawn directly. Makes the loop below slightly
+ // more readable.
+ $aImg->Line($x0,$y,$x1,$y);
+ $hls = $this->linespacing;
+
+ // A correction factor for vertical "brick" line spacing to account for
+ // a) the difference in number of pixels hor vs vert
+ // b) visual apperance to make the first layer of "bricks" look more
+ // square.
+ $vls = $this->linespacing*0.6;
+
+ $ds = $hls*($apa-$vls)/$apa;
+ // Get the slope for the "perspective line" going from bottom right
+ // corner to top left corner of the "first" brick.
+
+ // Uncomment the following lines if you want to get a visual understanding
+ // of what this helpline does. BTW this mimics the way you would get the
+ // perspective right when drawing on paper.
+ /*
+ $x0 = $middle;
+ $y0 = $this->rect->ye;
+ $len=floor(($this->rect->ye-$this->rect->y)/$vls);
+ $x1 = $middle+round($len*$ds);
+ $y1 = $this->rect->ye-$len*$vls;
+ $aImg->PushColor("red");
+ $aImg->Line($x0,$y0,$x1,$y1);
+ $aImg->PopColor();
+ */
+
+ $y -= $vls;
+ $k=($this->rect->ye-($this->rect->ye-$vls))/($middle-($middle-$ds));
+ $dist = $hls;
+ while( $y>$this->rect->y ) {
+ $aImg->Line($this->rect->x,$y,$this->rect->xe,$y);
+ $adj = $k*$dist/(1+$dist*$k/$apa);
+ if( $adj < 2 ) $adj=1;
+ $y = $this->rect->ye - round($adj);
+ $dist += $hls;
+ }
+ }
+}
+
+//=====================================================================
+// Class RectPatternCross
+// Vert/Hor crosses
+//=====================================================================
+class RectPatternCross extends RectPattern {
+ var $vert=null;
+ var $hor=null;
+ function RectPatternCross($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->vert = new RectPatternVert($aColor,$aWeight);
+ $this->hor = new RectPatternHor($aColor,$aWeight);
+ }
+
+ function SetOrder($aDepth) {
+ $this->vert->SetOrder($aDepth);
+ $this->hor->SetOrder($aDepth);
+ }
+
+ function SetPos(&$aRect) {
+ parent::SetPos($aRect);
+ $this->vert->SetPos($aRect);
+ $this->hor->SetPos($aRect);
+ }
+
+ function SetDensity($aDens) {
+ $this->vert->SetDensity($aDens);
+ $this->hor->SetDensity($aDens);
+ }
+
+ function DoPattern(&$aImg) {
+ $this->vert->DoPattern($aImg);
+ $this->hor->DoPattern($aImg);
+ }
+}
+
+//=====================================================================
+// Class RectPatternDiagCross
+// Vert/Hor crosses
+//=====================================================================
+
+class RectPatternDiagCross extends RectPattern {
+ var $left=null;
+ var $right=null;
+ function RectPatternDiagCross($aColor="black",$aWeight=1) {
+ parent::RectPattern($aColor,$aWeight);
+ $this->right = new RectPatternRDiag($aColor,$aWeight);
+ $this->left = new RectPatternLDiag($aColor,$aWeight);
+ }
+
+ function SetOrder($aDepth) {
+ $this->left->SetOrder($aDepth);
+ $this->right->SetOrder($aDepth);
+ }
+
+ function SetPos(&$aRect) {
+ parent::SetPos($aRect);
+ $this->left->SetPos($aRect);
+ $this->right->SetPos($aRect);
+ }
+
+ function SetDensity($aDens) {
+ $this->left->SetDensity($aDens);
+ $this->right->SetDensity($aDens);
+ }
+
+ function DoPattern(&$aImg) {
+ $this->left->DoPattern($aImg);
+ $this->right->DoPattern($aImg);
+ }
+
+}
+
+//=====================================================================
+// Class RectPatternFactory
+// Factory class for rectangular pattern
+//=====================================================================
+class RectPatternFactory {
+ function RectPatternFactory() {
+ // Empty
+ }
+ function Create($aPattern,$aColor,$aWeight=1) {
+ switch($aPattern) {
+ case BAND_RDIAG:
+ $obj = new RectPatternRDiag($aColor,$aWeight);
+ break;
+ case BAND_LDIAG:
+ $obj = new RectPatternLDiag($aColor,$aWeight);
+ break;
+ case BAND_SOLID:
+ $obj = new RectPatternSolid($aColor,$aWeight);
+ break;
+ case BAND_VLINE:
+ $obj = new RectPatternVert($aColor,$aWeight);
+ break;
+ case BAND_HLINE:
+ $obj = new RectPatternHor($aColor,$aWeight);
+ break;
+ case BAND_3DPLANE:
+ $obj = new RectPattern3DPlane($aColor,$aWeight);
+ break;
+ case BAND_HVCROSS:
+ $obj = new RectPatternCross($aColor,$aWeight);
+ break;
+ case BAND_DIAGCROSS:
+ $obj = new RectPatternDiagCross($aColor,$aWeight);
+ break;
+ default:
+ JpGraphError::Raise(" Unknown pattern specification ($aPattern)");
+ }
+ return $obj;
+ }
+}
+
+
+//=====================================================================
+// Class PlotBand
+// Factory class which is used by the client.
+// It is responsible for factoring the corresponding pattern
+// concrete class.
+//=====================================================================
+class PlotBand {
+ var $prect=null;
+ var $depth;
+ var $dir, $min, $max;
+
+ function PlotBand($aDir,$aPattern,$aMin,$aMax,$aColor="black",$aWeight=1,$aDepth=DEPTH_BACK) {
+ $f = new RectPatternFactory();
+ $this->prect = $f->Create($aPattern,$aColor,$aWeight);
+ if( is_numeric($aMin) && is_numeric($aMax) && ($aMin > $aMax) )
+ JpGraphError::Raise('Min value for plotband is larger than specified max value. Please correct.');
+ $this->dir = $aDir;
+ $this->min = $aMin;
+ $this->max = $aMax;
+ $this->depth=$aDepth;
+ }
+
+ // Set position. aRect contains absolute image coordinates
+ function SetPos(&$aRect) {
+ assert( $this->prect != null ) ;
+ $this->prect->SetPos($aRect);
+ }
+
+ function ShowFrame($aFlag=true) {
+ $this->prect->ShowFrame($aFlag);
+ }
+
+ // Set z-order. In front of pplot or in the back
+ function SetOrder($aDepth) {
+ $this->depth=$aDepth;
+ }
+
+ function SetDensity($aDens) {
+ $this->prect->SetDensity($aDens);
+ }
+
+ function GetDir() {
+ return $this->dir;
+ }
+
+ function GetMin() {
+ return $this->min;
+ }
+
+ function GetMax() {
+ return $this->max;
+ }
+
+ // Display band
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ assert( $this->prect != null ) ;
+ if( $this->dir == HORIZONTAL ) {
+ if( $this->min === 'min' ) $this->min = $aYScale->GetMinVal();
+ if( $this->max === 'max' ) $this->max = $aYScale->GetMaxVal();
+
+ // Only draw the bar if it actually appears in the range
+ if ($this->min < $aYScale->GetMaxVal() && $this->max > $aYScale->GetMinVal()) {
+
+ // Trucate to limit of axis
+ $this->min = max($this->min, $aYScale->GetMinVal());
+ $this->max = min($this->max, $aYScale->GetMaxVal());
+
+ $x=$aXScale->scale_abs[0];
+ $y=$aYScale->Translate($this->max);
+ $width=$aXScale->scale_abs[1]-$aXScale->scale_abs[0]+1;
+ $height=abs($y-$aYScale->Translate($this->min))+1;
+ $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
+ $this->prect->Stroke($aImg);
+ }
+ }
+ else { // VERTICAL
+ if( $this->min === 'min' ) $this->min = $aXScale->GetMinVal();
+ if( $this->max === 'max' ) $this->max = $aXScale->GetMaxVal();
+
+ // Only draw the bar if it actually appears in the range
+ if ($this->min < $aXScale->GetMaxVal() && $this->max > $aXScale->GetMinVal()) {
+
+ // Trucate to limit of axis
+ $this->min = max($this->min, $aXScale->GetMinVal());
+ $this->max = min($this->max, $aXScale->GetMaxVal());
+
+ $y=$aYScale->scale_abs[1];
+ $x=$aXScale->Translate($this->min);
+ $height=abs($aYScale->scale_abs[1]-$aYScale->scale_abs[0]);
+ $width=abs($x-$aXScale->Translate($this->max));
+ $this->prect->SetPos(new Rectangle($x,$y,$width,$height));
+ $this->prect->Stroke($aImg);
+ }
+ }
+ }
+}
+
+//===================================================
+// CLASS PlotLine
+// Description:
+// Data container class to hold properties for a static
+// line that is drawn directly in the plot area.
+// Usefull to add static borders inside a plot to show
+// for example set-values
+//===================================================
+class PlotLine {
+ var $weight=1;
+ var $color="black";
+ var $direction=-1;
+ var $scaleposition;
+
+//---------------
+// CONSTRUCTOR
+ function PlotLine($aDir=HORIZONTAL,$aPos=0,$aColor="black",$aWeight=1) {
+ $this->direction = $aDir;
+ $this->color=$aColor;
+ $this->weight=$aWeight;
+ $this->scaleposition=$aPos;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetPosition($aScalePosition) {
+ $this->scaleposition=$aScalePosition;
+ }
+
+ function SetDirection($aDir) {
+ $this->direction = $aDir;
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight=$aWeight;
+ }
+
+ function Stroke(&$aImg,&$aXScale,&$aYScale) {
+ $aImg->SetColor($this->color);
+ $aImg->SetLineWeight($this->weight);
+ if( $this->direction == VERTICAL ) {
+ $ymin_abs=$aYScale->Translate($aYScale->GetMinVal());
+ $ymax_abs=$aYScale->Translate($aYScale->GetMaxVal());
+ $xpos_abs=$aXScale->Translate($this->scaleposition);
+ $aImg->Line($xpos_abs, $ymin_abs, $xpos_abs, $ymax_abs);
+ }
+ elseif( $this->direction == HORIZONTAL ) {
+ $xmin_abs=$aXScale->Translate($aXScale->GetMinVal());
+ $xmax_abs=$aXScale->Translate($aXScale->GetMaxVal());
+ $ypos_abs=$aYScale->Translate($this->scaleposition);
+ $aImg->Line($xmin_abs, $ypos_abs, $xmax_abs, $ypos_abs);
+ }
+ else
+ JpGraphError::Raise(" Illegal direction for static line");
+ }
+}
+
+// <EOF>
+?>
diff --git a/includes/jpgraph/jpgraph_bar.php b/includes/jpgraph/jpgraph_bar.php
new file mode 100644
index 0000000..f5b265f
--- /dev/null
+++ b/includes/jpgraph/jpgraph_bar.php
@@ -0,0 +1,794 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_BAR.PHP
+// Description: Bar plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_bar.php,v 1.54.2.12 2003/11/24 07:36:02 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+*/
+
+// Pattern for Bars
+DEFINE('PATTERN_DIAG1',1);
+DEFINE('PATTERN_DIAG2',2);
+DEFINE('PATTERN_DIAG3',3);
+DEFINE('PATTERN_DIAG4',4);
+DEFINE('PATTERN_CROSS1',5);
+DEFINE('PATTERN_CROSS2',6);
+DEFINE('PATTERN_CROSS3',7);
+DEFINE('PATTERN_CROSS4',8);
+DEFINE('PATTERN_STRIPE1',9);
+DEFINE('PATTERN_STRIPE2',10);
+
+//===================================================
+// CLASS BarPlot
+// Description: Main code to produce a bar plot
+//===================================================
+class BarPlot extends Plot {
+ var $width=0.4; // in percent of major ticks
+ var $abswidth=-1; // Width in absolute pixels
+ var $fill=false,$fill_color="lightblue"; // Default is to fill with light blue
+ var $ybase=0; // Bars start at 0
+ var $align="center";
+ var $grad=false,$grad_style=1;
+ var $grad_fromcolor=array(50,50,200),$grad_tocolor=array(255,255,255);
+ var $bar_shadow=false;
+ var $bar_shadow_color="black";
+ var $bar_shadow_hsize=3,$bar_shadow_vsize=3;
+ var $valuepos='top';
+ var $iPattern=-1,$iPatternDensity=80,$iPatternColor='black';
+
+//---------------
+// CONSTRUCTOR
+ function BarPlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ ++$this->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Set a drop shadow for the bar (or rather an "up-right" shadow)
+ function SetShadow($color="black",$hsize=3,$vsize=3,$show=true) {
+ $this->bar_shadow=$show;
+ $this->bar_shadow_color=$color;
+ $this->bar_shadow_vsize=$vsize;
+ $this->bar_shadow_hsize=$hsize;
+
+ // Adjust the value margin to compensate for shadow
+ $this->value->margin += $vsize;
+ }
+
+ // DEPRECATED use SetYBase instead
+ function SetYMin($aYStartValue) {
+ //die("JpGraph Error: Deprecated function SetYMin. Use SetYBase() instead.");
+ $this->ybase=$aYStartValue;
+ }
+
+ // Specify the base value for the bars
+ function SetYBase($aYStartValue) {
+ $this->ybase=$aYStartValue;
+ }
+
+ function Legend(&$graph) {
+ if( $this->grad && $this->legend!="" && !$this->fill ) {
+ $color=array($this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
+ $graph->legend->Add($this->legend,$color,"",0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ elseif( $this->fill_color && $this->legend!="" ) {
+ if( is_array($this->fill_color) )
+ {
+ if ($this->legendColor != '')
+ $graph->legend->Add($this->legend,$this->legendColor,"",0,$this->legendcsimtarget,$this->legendcsimalt);
+ else
+ $graph->legend->Add($this->legend,$this->fill_color[0],"",0,$this->legendcsimtarget,$this->legendcsimalt);
+ }
+ else
+ {
+ if ($this->legendColor != '')
+ $graph->legend->Add($this->legend,$this->legendColor,"",0,$this->legendcsimtarget,$this->legendcsimalt);
+ else
+ $graph->legend->Add($this->legend,$this->fill_color,"",0,$this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ parent::PreStrokeAdjust($graph);
+
+ // If we are using a log Y-scale we want the base to be at the
+ // minimum Y-value unless the user have specifically set some other
+ // value than the default.
+ if( substr($graph->axtype,-3,3)=="log" && $this->ybase==0 )
+ $this->ybase = $graph->yaxis->scale->GetMinVal();
+
+ // For a "text" X-axis scale we will adjust the
+ // display of the bars a little bit.
+ if( substr($graph->axtype,0,3)=="tex" ) {
+ // Position the ticks between the bars
+ $graph->xaxis->scale->ticks->SetXLabelOffset(0.5,0);
+
+ // Center the bars
+ if( $this->align == "center" )
+ $graph->SetTextScaleOff(0.5-$this->width/2);
+ elseif( $this->align == "right" )
+ $graph->SetTextScaleOff(1-$this->width);
+
+ }
+ else {
+ // We only set an absolute width for linear and int scale
+ // for text scale the width will be set to a fraction of
+ // the majstep width.
+ if( $this->abswidth == -1 ) {
+ // Not set
+ // set width to a visuable sensible default
+ if(count($this->coords[0]) == 0)
+ $this->abswidth = 24;
+ else
+ $this->abswidth = $graph->img->plotwidth/(2*count($this->coords[0]));
+ }
+ }
+ }
+
+ function Min() {
+ $m = parent::Min();
+ if( $m[1] >= $this->ybase )
+ $m[1] = $this->ybase;
+ return $m;
+ }
+
+ function Max() {
+ $m = parent::Max();
+ if( $m[1] <= $this->ybase )
+ $m[1] = $this->ybase;
+ return $m;
+ }
+
+ // Specify width as fractions of the major stepo size
+ function SetWidth($aFractionWidth) {
+ $this->width=$aFractionWidth;
+ }
+
+ // Specify width in absolute pixels. If specified this
+ // overrides SetWidth()
+ function SetAbsWidth($aWidth) {
+ $this->abswidth=$aWidth;
+ }
+
+ function SetAlign($aAlign) {
+ $this->align=$aAlign;
+ }
+
+ function SetNoFill() {
+ $this->grad = false;
+ $this->fill_color=false;
+ $this->fill=false;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill = true ;
+ $this->fill_color=$aColor;
+ }
+
+ function SetFillGradient($from_color,$to_color,$style) {
+ $this->grad=true;
+ $this->grad_fromcolor=$from_color;
+ $this->grad_tocolor=$to_color;
+ $this->grad_style=$style;
+ }
+
+ function SetValuePos($aPos) {
+ $this->valuepos = $aPos;
+ }
+
+ function SetPattern($aPattern, $aColor='black'){
+ $this->iPatternColor = $aColor;
+ switch( $aPattern ) {
+ case PATTERN_DIAG1:
+ $this->iPattern = 1;
+ $this->iPatternDensity = 90;
+ break;
+ case PATTERN_DIAG2:
+ $this->iPattern = 1;
+ $this->iPatternDensity = 75;
+ break;
+ case PATTERN_DIAG3:
+ $this->iPattern = 2;
+ $this->iPatternDensity = 90;
+ break;
+ case PATTERN_DIAG4:
+ $this->iPattern = 2;
+ $this->iPatternDensity = 75;
+ break;
+ case PATTERN_CROSS1:
+ $this->iPattern = 8;
+ $this->iPatternDensity = 90;
+ break;
+ case PATTERN_CROSS2:
+ $this->iPattern = 8;
+ $this->iPatternDensity = 78;
+ break;
+ case PATTERN_CROSS3:
+ $this->iPattern = 8;
+ $this->iPatternDensity = 65;
+ break;
+ case PATTERN_CROSS4:
+ $this->iPattern = 7;
+ $this->iPatternDensity = 90;
+ break;
+ case PATTERN_STRIPE1:
+ $this->iPattern = 5;
+ $this->iPatternDensity = 90;
+ break;
+ case PATTERN_STRIPE2:
+ $this->iPattern = 5;
+ $this->iPatternDensity = 75;
+ break;
+ default:
+ JpGraphError::Raise('Unknown pattern specified in call to BarPlot::SetPattern()');
+
+ }
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ $numpoints = count($this->coords[0]);
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ die("JpGraph Error: Number of X and Y points are not equal.<br>
+ Number of X-points:".count($this->coords[1])."<br>
+ Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+
+ $numbars=count($this->coords[0]);
+
+ // Use GetMinVal() instead of scale[0] directly since in the case
+ // of log scale we get a correct value. Log scales will have negative
+ // values for values < 1 while still not representing negative numbers.
+ if( $yscale->GetMinVal() >= 0 )
+ $zp=$yscale->scale_abs[0];
+ else {
+ $zp=$yscale->Translate(0);
+ }
+
+ if( $this->abswidth > -1 ) {
+ $abswidth=$this->abswidth;
+ }
+ else
+ $abswidth=round($this->width*$xscale->scale_factor,0);
+
+ for($i=0; $i<$numbars; $i++) {
+
+ // If value is NULL, or 0 then don't draw a bar at all
+ if ($this->coords[0][$i] === null ||
+ $this->coords[0][$i] === '' ||
+ $this->coords[0][$i] === 0 ) continue;
+
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+
+ $x=$xscale->Translate($x);
+
+ if( !$xscale->textscale ) {
+ if($this->align=="center")
+ $x -= $abswidth/2;
+ elseif($this->align=="right")
+ $x -= $abswidth;
+ }
+
+
+ // Stroke fill color and fill gradient
+ $pts=array(
+ $x,$zp,
+ $x,$yscale->Translate($this->coords[0][$i]),
+ $x+$abswidth,$yscale->Translate($this->coords[0][$i]),
+ $x+$abswidth,$zp);
+ if( $this->grad ) {
+ $grad = new Gradient($img);
+ $grad->FilledRectangle($pts[2],$pts[3],
+ $pts[6],$pts[7],
+ $this->grad_fromcolor,$this->grad_tocolor,$this->grad_style);
+ }
+ elseif( !empty($this->fill_color) ) {
+ if(is_array($this->fill_color)) {
+ $img->PushColor($this->fill_color[$i % count($this->fill_color)]);
+ } else {
+ $img->PushColor($this->fill_color);
+ }
+ $img->FilledPolygon($pts);
+ $img->PopColor();
+ }
+
+
+ // Remember value of this bar
+ $val=$this->coords[0][$i];
+
+ if( !empty($val) && !is_numeric($val) ) {
+ JpGraphError::Raise('All values for a barplot must be numeric. You have specified value['.$i.'] == \''.$val.'\'');
+ }
+
+ // Determine the shadow
+ if( $this->bar_shadow && $val != 0) {
+
+ $ssh = $this->bar_shadow_hsize;
+ $ssv = $this->bar_shadow_vsize;
+ // Create points to create a "upper-right" shadow
+ if( $val > 0 ) {
+ $sp[0]=$pts[6]; $sp[1]=$pts[7];
+ $sp[2]=$pts[4]; $sp[3]=$pts[5];
+ $sp[4]=$pts[2]; $sp[5]=$pts[3];
+ $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
+ $sp[8]=$pts[4]+$ssh; $sp[9]=$pts[5]-$ssv;
+ $sp[10]=$pts[6]+$ssh; $sp[11]=$pts[7]-$ssv;
+ }
+ elseif( $val < 0 ) {
+ $sp[0]=$pts[4]; $sp[1]=$pts[5];
+ $sp[2]=$pts[6]; $sp[3]=$pts[7];
+ $sp[4]=$pts[0]; $sp[5]=$pts[1];
+ $sp[6]=$pts[0]+$ssh; $sp[7]=$pts[1]-$ssv;
+ $sp[8]=$pts[6]+$ssh; $sp[9]=$pts[7]-$ssv;
+ $sp[10]=$pts[4]+$ssh; $sp[11]=$pts[5]-$ssv;
+ }
+ if( is_array($this->bar_shadow_color) ) {
+ $numcolors = count($this->bar_shadow_color);
+ if( $numcolors == 0 ) {
+ JpGraphError::Raise('You have specified an empty array for shadow colors in the bar plot.');
+ }
+ $img->PushColor($this->bar_shadow_color[$i % $numcolors]);
+ }
+ else {
+ $img->PushColor($this->bar_shadow_color);
+ }
+ $img->FilledPolygon($sp);
+ $img->PopColor();
+ }
+
+ // Stroke the pattern
+ if( $this->iPattern > -1 ) {
+ $f = new RectPatternFactory();
+ $prect = $f->Create($this->iPattern,$this->iPatternColor,1);
+ $prect->SetDensity($this->iPatternDensity);
+ $prect->SetPos(new Rectangle($pts[2],$pts[3],$pts[4]-$pts[0]+1,$pts[1]-$pts[3]+1));
+ $prect->Stroke($img);
+ }
+
+ // Stroke the outline of the bar
+ if( is_array($this->color) )
+ $img->SetColor($this->color[$i % count($this->color)]);
+ else
+ $img->SetColor($this->color);
+
+ $pts[] = $pts[0];
+ $pts[] = $pts[1];
+
+ if( $this->weight > 0 ) {
+ $img->SetLineWeight($this->weight);
+ $img->Polygon($pts);
+ }
+
+ // Determine how to best position the values of the individual bars
+ $x=$pts[2]+($pts[4]-$pts[2])/2;
+ if( $this->valuepos=='top' ) {
+ $y=$pts[3];
+ if( $img->a === 90 ) {
+ if( $val < 0 )
+ $this->value->SetAlign('right','center');
+ else
+ $this->value->SetAlign('left','center');
+
+ }
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='max' ) {
+ $y=$pts[3];
+ if( $img->a === 90 ) {
+ if( $val < 0 )
+ $this->value->SetAlign('left','center');
+ else
+ $this->value->SetAlign('right','center');
+ }
+ else {
+ $this->value->SetAlign('center','top');
+ }
+ $this->value->SetMargin(-3);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='center' ) {
+ $y = ($pts[3] + $pts[1])/2;
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(0);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ elseif( $this->valuepos=='bottom' || $this->valuepos=='min' ) {
+ $y=$pts[1];
+ if( $img->a === 90 ) {
+ if( $val < 0 )
+ $this->value->SetAlign('right','center');
+ else
+ $this->value->SetAlign('left','center');
+ }
+ $this->value->SetMargin(3);
+ $this->value->Stroke($img,$val,$x,$y);
+ }
+ else {
+ JpGraphError::Raise('Unknown position for values on bars :'.$this->valuepos);
+ die();
+ }
+ // Create the client side image map
+ $rpts = $img->ArrRotate($pts);
+ $csimcoord=round($rpts[0]).", ".round($rpts[1]);
+ for( $j=1; $j < 4; ++$j){
+ $csimcoord .= ", ".round($rpts[2*$j]).", ".round($rpts[2*$j+1]);
+ }
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= '<area shape="poly" coords="'.$csimcoord.'" ';
+ $this->csimareas .= " href=\"".$this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $sval=sprintf($this->csimalts[$i],$this->coords[0][$i]);
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+ return true;
+ }
+} // Class
+
+//===================================================
+// CLASS GroupBarPlot
+// Description: Produce grouped bar plots
+//===================================================
+class GroupBarPlot extends BarPlot {
+ var $plots;
+ var $width=0.7;
+ var $nbrplots=0;
+ var $numpoints;
+//---------------
+// CONSTRUCTOR
+ function GroupBarPlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ if( $this->nbrplots < 1 ) {
+ JpGraphError::Raise('You must have at least one barplot in the array to be able to create a Grouped Bar Plot.');
+ }
+ $this->numpoints = $plots[0]->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ $n = count($this->plots);
+ for($i=0; $i < $n; ++$i) {
+ $c = get_class($this->plots[$i]);
+ $sc = is_subclass_of($this->plots[$i],'barplot');
+ if( $c !== 'barplot' && !$sc ) {
+ JpGraphError::Raise('One of the objects submitted to GroupBar is not a BarPlot. Make sure that you create the Group Bar plot from an array of BarPlot or AccBarPlot objects.');
+ }
+ $this->plots[$i]->DoLegend($graph);
+ }
+ }
+
+ function Min() {
+ list($xmin,$ymin) = $this->plots[0]->Min();
+ $n = count($this->plots);
+ for($i=0; $i < $n; ++$i) {
+ list($xm,$ym) = $this->plots[$i]->Min();
+ $xmin = max($xmin,$xm);
+ $ymin = min($ymin,$ym);
+ }
+ return array($xmin,$ymin);
+ }
+
+ function Max() {
+ list($xmax,$ymax) = $this->plots[0]->Max();
+ $n = count($this->plots);
+ for($i=0; $i < $n; ++$i) {
+ list($xm,$ym) = $this->plots[$i]->Max();
+ $xmax = max($xmax,$xm);
+ $ymax = max($ymax,$ym);
+ }
+ return array($xmax,$ymax);
+ }
+
+ function GetCSIMareas() {
+ $n = count($this->plots);
+ $csimareas='';
+ for($i=0; $i < $n; ++$i) {
+ $csimareas .= $this->plots[$i]->csimareas;
+ }
+ return $csimareas;
+ }
+
+ // Stroke all the bars next to each other
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $tmp=$xscale->off;
+ $n = count($this->plots);
+ $subwidth = $this->width/$this->nbrplots ;
+ for( $i=0; $i < $n; ++$i ) {
+ $this->plots[$i]->ymin=$this->ybase;
+ $this->plots[$i]->SetWidth($subwidth);
+
+ // If the client have used SetTextTickInterval() then
+ // major_step will be > 1 and the positioning will fail.
+ // If we assume it is always one the positioning will work
+ // fine with a text scale but this will not work with
+ // arbitrary linear scale
+ $xscale->off = $tmp+$i*round(/*$xscale->ticks->major_step* */
+ $xscale->scale_factor*$subwidth);
+ $this->plots[$i]->Stroke($img,$xscale,$yscale);
+ }
+ $xscale->off=$tmp;
+ }
+} // Class
+
+//===================================================
+// CLASS AccBarPlot
+// Description: Produce accumulated bar plots
+//===================================================
+class AccBarPlot extends BarPlot {
+ var $plots=null,$nbrplots=0,$numpoints=0;
+//---------------
+// CONSTRUCTOR
+ function AccBarPlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ $this->numpoints = $plots[0]->numpoints;
+ $this->value = new DisplayValue();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ $n = count($this->plots);
+ for( $i=$n-1; $i >= 0; --$i ) {
+ $c = get_class($this->plots[$i]);
+ if( $c !== 'barplot' ) {
+ JpGraphError::Raise('One of the objects submitted to AccBar is not a BarPlot. Make sure that you create the AccBar plot from an array of BarPlot objects.');
+ }
+ $this->plots[$i]->DoLegend($graph);
+ }
+ }
+
+ function Max() {
+ list($xmax) = $this->plots[0]->Max();
+ $nmax=0;
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x) = $this->plots[$i]->Max();
+ $xmax = max($xmax,$x);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for bar $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=0;
+ if( $this->plots[0]->coords[0][$i] > 0 )
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ if( $this->plots[$j]->coords[0][$i] > 0 )
+ $y += $this->plots[$j]->coords[0][$i];
+ }
+ $ymax[$i] = $y;
+ }
+ $ymax = max($ymax);
+
+ // Bar always start at baseline
+ if( $ymax <= $this->ybase )
+ $ymax = $this->ybase;
+ return array($xmax,$ymax);
+ }
+
+ function Min() {
+ $nmax=0;
+ list($xmin,$ysetmin) = $this->plots[0]->Min();
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x,$y) = $this->plots[$i]->Min();
+ $xmin = Min($xmin,$x);
+ $ysetmin = Min($y,$ysetmin);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for bar $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymin[$i] = $y;
+ }
+ $ymin = Min($ysetmin,Min($ymin));
+ // Bar always start at baseline
+ if( $ymin >= $this->ybase )
+ $ymin = $this->ybase;
+ return array($xmin,$ymin);
+ }
+
+ // Stroke acc bar plot
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $img->SetLineWeight($this->weight);
+ for($i=0; $i < $this->numpoints-1; $i++) {
+ $accy = 0;
+ $accy_neg = 0;
+ for($j=0; $j < $this->nbrplots; ++$j ) {
+
+ $img->SetColor($this->plots[$j]->color);
+
+ if ( $this->plots[$j]->coords[0][$i] >= 0) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
+ $accyt=$yscale->Translate($accy);
+ $accy+=$this->plots[$j]->coords[0][$i];
+ }
+ else {
+ //if ( $this->plots[$j]->coords[0][$i] < 0 || $accy_neg < 0 ) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
+ $accyt=$yscale->Translate($accy_neg);
+ $accy_neg+=$this->plots[$j]->coords[0][$i];
+ }
+
+ $xt=$xscale->Translate($i);
+
+ if( $this->abswidth > -1 )
+ $abswidth=$this->abswidth;
+ else
+ $abswidth=round($this->width*$xscale->scale_factor,0);
+
+ $pts=array($xt,$accyt,$xt,$yt,$xt+$abswidth,$yt,$xt+$abswidth,$accyt);
+
+ if( $this->bar_shadow ) {
+ $ssh = $this->bar_shadow_hsize;
+ $ssv = $this->bar_shadow_vsize;
+
+ // We must also differ if we are a positive or negative bar.
+ if( $j === 0 ) {
+ // This gets extra complicated since we have to
+ // see all plots to see if we are negative. It could
+ // for example be that all plots are 0 until the very
+ // last one. We therefore need to save the initial setup
+ // for both the negative and positive case
+
+ // In case the final bar is positive
+ $sp[0]=$pts[6]+1; $sp[1]=$pts[7];
+ $sp[2]=$pts[6]+$ssh; $sp[3]=$pts[7]-$ssv;
+
+ // In case the final bar is negative
+ $nsp[0]=$pts[0]; $nsp[1]=$pts[1];
+ $nsp[2]=$pts[0]+$ssh; $nsp[3]=$pts[1]-$ssv;
+ $nsp[4]=$pts[6]+$ssh; $nsp[5]=$pts[7]-$ssv;
+ $nsp[10]=$pts[6]+1; $nsp[11]=$pts[7];
+ }
+
+ if( $j === $this->nbrplots-1 ) {
+ // If this is the last plot of the bar and
+ // the total value is larger than 0 then we
+ // add the shadow.
+ if( is_array($this->bar_shadow_color) ) {
+ $numcolors = count($this->bar_shadow_color);
+ if( $numcolors == 0 ) {
+ JpGraphError::Raise('You have specified an empty array for shadow colors in the bar plot.');
+ }
+ $img->PushColor($this->bar_shadow_color[$i % $numcolors]);
+ }
+ else {
+ $img->PushColor($this->bar_shadow_color);
+ }
+
+ if( $accy > 0 ) {
+ $sp[4]=$pts[4]+$ssh; $sp[5]=$pts[5]-$ssv;
+ $sp[6]=$pts[2]+$ssh; $sp[7]=$pts[3]-$ssv;
+ $sp[8]=$pts[2]; $sp[9]=$pts[3]-1;
+ $sp[10]=$pts[4]+1; $sp[11]=$pts[5];
+ $img->FilledPolygon($sp,4);
+ }
+ elseif( $accy_neg < 0 ) {
+ $nsp[6]=$pts[4]+$ssh; $nsp[7]=$pts[5]-$ssv;
+ $nsp[8]=$pts[4]+1; $nsp[9]=$pts[5];
+ $img->FilledPolygon($nsp,4);
+ }
+ $img->PopColor();
+ }
+ }
+
+ // If value is NULL or 0, then don't draw a bar at all
+ if ($this->plots[$j]->coords[0][$i] == 0 ) continue;
+
+ if( $this->plots[$j]->grad ) {
+ $grad = new Gradient($img);
+ $grad->FilledRectangle(
+ $pts[2],$pts[3],
+ $pts[6],$pts[7],
+ $this->plots[$j]->grad_fromcolor,
+ $this->plots[$j]->grad_tocolor,
+ $this->plots[$j]->grad_style);
+ } else {
+ if (is_array($this->plots[$j]->fill_color) ) {
+ $numcolors = count($this->plots[$j]->fill_color);
+ $img->SetColor($this->plots[$j]->fill_color[$i % $numcolors]);
+ }
+ else {
+ $img->SetColor($this->plots[$j]->fill_color);
+ }
+ $img->FilledPolygon($pts);
+ $img->SetColor($this->plots[$j]->color);
+ }
+
+
+ // CSIM array
+
+ if( $i < count($this->plots[$j]->csimtargets) ) {
+ // Create the client side image map
+ $rpts = $img->ArrRotate($pts);
+ $csimcoord=round($rpts[0]).", ".round($rpts[1]);
+ for( $k=1; $k < 4; ++$k){
+ $csimcoord .= ", ".round($rpts[2*$k]).", ".round($rpts[2*$k+1]);
+ }
+ if( ! empty($this->plots[$j]->csimtargets[$i]) ) {
+ $this->csimareas.= '<area shape="poly" coords="'.$csimcoord.'" ';
+ $this->csimareas.= " href=\"".$this->plots[$j]->csimtargets[$i]."\"";
+ if( !empty($this->plots[$j]->csimalts[$i]) ) {
+ $sval=sprintf($this->plots[$j]->csimalts[$i],$this->plots[$j]->coords[0][$i]);
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ $pts[] = $pts[0];
+ $pts[] = $pts[1];
+ $img->Polygon($pts);
+ }
+
+ // Draw labels for each acc.bar
+
+ $x=$pts[2]+($pts[4]-$pts[2])/2;
+ $y=$yscale->Translate($accy);
+ if($this->bar_shadow) $x += $ssh;
+ $this->value->Stroke($img,$accy,$x,$y);
+
+ $accy = 0;
+ $accy_neg = 0;
+ for($j=0; $j<$this->nbrplots; ++$j ) {
+
+ // We don't print 0 values in an accumulated bar plot
+ if( $this->plots[$j]->coords[0][$i] == 0 ) continue;
+
+ if ($this->plots[$j]->coords[0][$i] > 0) {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy);
+ $accyt=$yscale->Translate($accy);
+ $y = $accyt-($accyt-$yt)/2;
+ $accy+=$this->plots[$j]->coords[0][$i];
+ } else {
+ $yt=$yscale->Translate($this->plots[$j]->coords[0][$i]+$accy_neg);
+ $accyt=$yscale->Translate($accy_neg);
+ //$y=0;
+ $accy_neg+=$this->plots[$j]->coords[0][$i];
+ $y = $accyt-($accyt-$yt)/2; // TODO : Check this fix
+ }
+ $this->plots[$j]->value->SetAlign("center","center");
+ $this->plots[$j]->value->SetMargin(0);
+ $this->plots[$j]->value->Stroke($img,$this->plots[$j]->coords[0][$i],$x,$y);
+ }
+
+ }
+ return true;
+ }
+} // Class
+
+/* EOF */
+?>
diff --git a/includes/jpgraph/jpgraph_canvas.php b/includes/jpgraph/jpgraph_canvas.php
new file mode 100644
index 0000000..7fc7bd0
--- /dev/null
+++ b/includes/jpgraph/jpgraph_canvas.php
@@ -0,0 +1,73 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_CANVAS.PHP
+// Description: Canvas drawing extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_canvas.php,v 1.12.2.1 2003/10/07 02:59:33 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS CanvasGraph
+// Description: Creates a simple canvas graph which
+// might be used together with the basic Image drawing
+// primitives. Useful to auickoly produce some arbitrary
+// graphic which benefits from all the functionality in the
+// graph liek caching for example.
+//===================================================
+class CanvasGraph extends Graph {
+//---------------
+// CONSTRUCTOR
+ function CanvasGraph($aWidth=300,$aHeight=200,$aCachedName="",$timeout=0,$inline=1) {
+ $this->Graph($aWidth,$aHeight,$aCachedName,$timeout,$inline);
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function InitFrame() {
+ $this->StrokePlotArea();
+ }
+
+ // Method description
+ function Stroke($aStrokeFileName="") {
+ if( $this->texts != null ) {
+ for($i=0; $i<count($this->texts); ++$i) {
+ $this->texts[$i]->Stroke($this->img);
+ }
+ }
+ $this->StrokeTitles();
+
+ // Should we do any final image transformation
+ if( $this->iImgTrans ) {
+ if( !class_exists('ImgTrans') ) {
+ require_once('jpgraph_imgtrans.php');
+ }
+
+ $tform = new ImgTrans($this->img->img);
+ $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
+ $this->iImgTransDirection,$this->iImgTransHighQ,
+ $this->iImgTransMinSize,$this->iImgTransFillColor,
+ $this->iImgTransBorder);
+ }
+
+
+ // If the filename is given as the special _IMG_HANDLER
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+} // Class
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_canvtools.php b/includes/jpgraph/jpgraph_canvtools.php
new file mode 100644
index 0000000..e7acd38
--- /dev/null
+++ b/includes/jpgraph/jpgraph_canvtools.php
@@ -0,0 +1,517 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_CANVTOOLS.PHP
+// Description: Some utilities for text and shape drawing on a canvas
+// Created: 2002-08-23
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_canvtools.php,v 1.9 2002/12/01 10:00:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+DEFINE('CORNER_TOPLEFT',0);
+DEFINE('CORNER_TOPRIGHT',1);
+DEFINE('CORNER_BOTTOMRIGHT',2);
+DEFINE('CORNER_BOTTOMLEFT',3);
+
+
+//===================================================
+// CLASS CanvasScale
+// Description: Define a scale for canvas so we
+// can abstract away with absolute pixels
+//===================================================
+
+class CanvasScale {
+ var $g;
+ var $w,$h;
+ var $ixmin=0,$ixmax=10,$iymin=0,$iymax=10;
+
+ function CanvasScale(&$graph,$xmin=0,$xmax=10,$ymin=0,$ymax=10) {
+ $this->g = &$graph;
+ $this->w = $graph->img->width;
+ $this->h = $graph->img->height;
+ $this->ixmin = $xmin;
+ $this->ixmax = $xmax;
+ $this->iymin = $ymin;
+ $this->iymax = $ymax;
+ }
+
+ function Set($xmin=0,$xmax=10,$ymin=0,$ymax=10) {
+ $this->ixmin = $xmin;
+ $this->ixmax = $xmax;
+ $this->iymin = $ymin;
+ $this->iymax = $ymax;
+ }
+
+ function Translate($x,$y) {
+ $xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
+ $yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
+ return array($xp,$yp);
+ }
+
+ function TranslateX($x) {
+ $xp = round(($x-$this->ixmin)/($this->ixmax - $this->ixmin) * $this->w);
+ return $xp;
+ }
+
+ function TranslateY($y) {
+ $yp = round(($y-$this->iymin)/($this->iymax - $this->iymin) * $this->h);
+ return $yp;
+ }
+
+}
+
+
+//===================================================
+// CLASS Shape
+// Description: Methods to draw shapes on canvas
+//===================================================
+class Shape {
+ var $img,$scale;
+
+ function Shape(&$aGraph,&$scale) {
+ $this->img = &$aGraph->img;
+ $this->img->SetColor('black');
+ $this->scale = &$scale;
+ }
+
+ function SetColor($aColor) {
+ $this->img->SetColor($aColor);
+ }
+
+ function Line($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->Line($x1,$y1,$x2,$y2);
+ }
+
+ function Polygon($p,$aClosed=false) {
+ $n=count($p);
+ for($i=0; $i < $n; $i+=2 ) {
+ $p[$i] = $this->scale->TranslateX($p[$i]);
+ $p[$i+1] = $this->scale->TranslateY($p[$i+1]);
+ }
+ $this->img->Polygon($p,$aClosed);
+ }
+
+ function FilledPolygon($p) {
+ $n=count($p);
+ for($i=0; $i < $n; $i+=2 ) {
+ $p[$i] = $this->scale->TranslateX($p[$i]);
+ $p[$i+1] = $this->scale->TranslateY($p[$i+1]);
+ }
+ $this->img->FilledPolygon($p);
+ }
+
+
+ // Draw a bezier curve with defining points in the $aPnts array
+ // using $aSteps steps.
+ // 0=x0, 1=y0
+ // 2=x1, 3=y1
+ // 4=x2, 5=y2
+ // 6=x3, 7=y3
+ function Bezier($p,$aSteps=40) {
+ $x0 = $p[0];
+ $y0 = $p[1];
+ // Calculate coefficients
+ $cx = 3*($p[2]-$p[0]);
+ $bx = 3*($p[4]-$p[2])-$cx;
+ $ax = $p[6]-$p[0]-$cx-$bx;
+ $cy = 3*($p[3]-$p[1]);
+ $by = 3*($p[5]-$p[3])-$cy;
+ $ay = $p[7]-$p[1]-$cy-$by;
+
+ // Step size
+ $delta = 1.0/$aSteps;
+
+ $x_old = $x0;
+ $y_old = $y0;
+ for($t=$delta; $t<=1.0; $t+=$delta) {
+ $tt = $t*$t; $ttt=$tt*$t;
+ $x = $ax*$ttt + $bx*$tt + $cx*$t + $x0;
+ $y = $ay*$ttt + $by*$tt + $cy*$t + $y0;
+ $this->Line($x_old,$y_old,$x,$y);
+ $x_old = $x;
+ $y_old = $y;
+ }
+ $this->Line($x_old,$y_old,$p[6],$p[7]);
+ }
+
+ function Rectangle($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->Rectangle($x1,$y1,$x2,$y2);
+ }
+
+ function FilledRectangle($x1,$y1,$x2,$y2) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ $this->img->FilledRectangle($x1,$y1,$x2,$y2);
+ }
+
+ function Circle($x1,$y1,$r) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ if( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->Circle($x1,$y1,$r);
+ }
+
+ function FilledCircle($x1,$y1,$r) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ if( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->FilledCircle($x1,$y1,$r);
+ }
+
+ function RoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+
+ if( $r == null )
+ $r = 5;
+ elseif( $r >= 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->RoundedRectangle($x1,$y1,$x2,$y2,$r);
+ }
+
+ function FilledRoundedRectangle($x1,$y1,$x2,$y2,$r=null) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+
+ if( $r == null )
+ $r = 5;
+ elseif( $r > 0 )
+ $r = $this->scale->TranslateX($r);
+ else
+ $r = -$r;
+ $this->img->FilledRoundedRectangle($x1,$y1,$x2,$y2,$r);
+ }
+
+ function ShadowRectangle($x1,$y1,$x2,$y2,$fcolor=false,$shadow_width=null,$shadow_color=array(102,102,102)) {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ list($x2,$y2) = $this->scale->Translate($x2,$y2);
+ if( $shadow_width == null )
+ $shadow_width=4;
+ else
+ $shadow_width=$this->scale->TranslateX($shadow_width);
+ $this->img->ShadowRectangle($x1,$y1,$x2,$y2,$fcolor,$shadow_width,$shadow_color);
+ }
+
+ function SetTextAlign($halign,$valign="bottom") {
+ $this->img->SetTextAlign($halign,$valign="bottom");
+ }
+
+ function StrokeText($x1,$y1,$txt,$dir=0,$paragraph_align="left") {
+ list($x1,$y1) = $this->scale->Translate($x1,$y1);
+ $this->img->StrokeText($x1,$y1,$txt,$dir,$paragraph_align);
+ }
+
+ // A rounded rectangle where one of the corner has been moved "into" the
+ // rectangle 'iw' width and 'ih' height. Corners:
+ // 0=Top left, 1=top right, 2=bottom right, 3=bottom left
+ function IndentedRectangle($xt,$yt,$w,$h,$iw=0,$ih=0,$aCorner=3,$aFillColor="",$r=4) {
+
+ list($xt,$yt) = $this->scale->Translate($xt,$yt);
+ list($w,$h) = $this->scale->Translate($w,$h);
+ list($iw,$ih) = $this->scale->Translate($iw,$ih);
+
+ $xr = $xt + $w - 0;
+ $yl = $yt + $h - 0;
+
+ switch( $aCorner ) {
+ case 0: // Upper left
+
+ // Bottom line, left & right arc
+ $this->img->Line($xt+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Right line, Top right arc
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$r);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Top line, Top left arc
+ $this->img->Line($xt+$iw+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$iw+$r,$yt+$r,$r*2,$r*2,180,270);
+
+ // Left line
+ $this->img->Line($xt,$yt+$ih+$r,$xt,$yl-$r);
+
+ // Indent horizontal, Lower left arc
+ $this->img->Line($xt+$r,$yt+$ih,$xt+$iw-$r,$yt+$ih);
+ $this->img->Arc($xt+$r,$yt+$ih+$r,$r*2,$r*2,180,270);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xt+$iw,$yt+$r,$xt+$iw,$yt+$ih-$r);
+ $this->img->Arc($xt+$iw-$r,$yt+$ih-$r,$r*2,$r*2,0,90);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xr-$r,$yl-$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 1: // Upper right
+
+ // Bottom line, left & right arc
+ $this->img->Line($xt+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Left line, Top left arc
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+
+ // Top line, Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$iw-$r,$yt);
+ $this->img->Arc($xr-$iw-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Right line
+ $this->img->Line($xr,$yt+$ih+$r,$xr,$yl-$r);
+
+ // Indent horizontal, Lower right arc
+ $this->img->Line($xr-$iw+$r,$yt+$ih,$xr-$r,$yt+$ih);
+ $this->img->Arc($xr-$r,$yt+$ih+$r,$r*2,$r*2,270,360);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xr-$iw,$yt+$r,$xr-$iw,$yt+$ih-$r);
+ $this->img->Arc($xr-$iw+$r,$yt+$ih-$r,$r*2,$r*2,90,180);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xt+$r,$yl-$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 2: // Lower right
+ // Top line, Top left & Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Left line, Bottom left arc
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$r);
+ $this->img->Arc($xt+$r,$yl-$r,$r*2,$r*2,90,180);
+
+ // Bottom line, Bottom right arc
+ $this->img->Line($xt+$r,$yl,$xr-$iw-$r,$yl);
+ $this->img->Arc($xr-$iw-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Right line
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$ih-$r);
+
+ // Indent horizontal, Lower right arc
+ $this->img->Line($xr-$r,$yl-$ih,$xr-$iw+$r,$yl-$ih);
+ $this->img->Arc($xr-$r,$yl-$ih-$r,$r*2,$r*2,0,90);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xr-$iw,$yl-$r,$xr-$iw,$yl-$ih+$r);
+ $this->img->Arc($xr-$iw+$r,$yl-$ih+$r,$r*2,$r*2,180,270);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xt+$r,$yt+$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+
+ case 3: // Lower left
+ // Top line, Top left & Top right arc
+ $this->img->Line($xt+$r,$yt,$xr-$r,$yt);
+ $this->img->Arc($xt+$r,$yt+$r,$r*2,$r*2,180,270);
+ $this->img->Arc($xr-$r,$yt+$r,$r*2,$r*2,270,360);
+
+ // Right line, Bottom right arc
+ $this->img->Line($xr,$yt+$r,$xr,$yl-$r);
+ $this->img->Arc($xr-$r,$yl-$r,$r*2,$r*2,0,90);
+
+ // Bottom line, Bottom left arc
+ $this->img->Line($xt+$iw+$r,$yl,$xr-$r,$yl);
+ $this->img->Arc($xt+$iw+$r,$yl-$r,$r*2,$r*2,90,180);
+
+ // Left line
+ $this->img->Line($xt,$yt+$r,$xt,$yl-$ih-$r);
+
+ // Indent horizontal, Lower left arc
+ $this->img->Line($xt+$r,$yl-$ih,$xt+$iw-$r,$yl-$ih);
+ $this->img->Arc($xt+$r,$yl-$ih-$r,$r*2,$r*2,90,180);
+
+ // Indent vertical, Indent arc
+ $this->img->Line($xt+$iw,$yl-$ih+$r,$xt+$iw,$yl-$r);
+ $this->img->Arc($xt+$iw-$r,$yl-$ih+$r,$r*2,$r*2,270,360);
+
+ if( $aFillColor != '' ) {
+ $bc = $this->img->current_color_name;
+ $this->img->PushColor($aFillColor);
+ $this->img->FillToBorder($xr-$r,$yt+$r,$bc);
+ $this->img->PopColor();
+ }
+
+ break;
+ }
+ }
+}
+
+
+//===================================================
+// CLASS RectangleText
+// Description: Draws a text paragraph inside a
+// rounded, possible filled, rectangle.
+//===================================================
+class CanvasRectangleText {
+ var $ix,$iy,$iw,$ih,$ir=4;
+ var $iTxt,$iColor='black',$iFillColor='',$iFontColor='black';
+ var $iParaAlign='center';
+ var $iAutoBoxMargin=5;
+ var $iShadowWidth=3,$iShadowColor='';
+
+ function CanvasRectangleText($aTxt='',$xl=0,$yt=0,$w=0,$h=0) {
+ $this->iTxt = new Text($aTxt);
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function SetShadow($aColor='gray',$aWidth=3) {
+ $this->iShadowColor = $aColor;
+ $this->iShadowWidth = $aWidth;
+ }
+
+ function SetFont($FontFam,$aFontStyle,$aFontSize=12) {
+ $this->iTxt->SetFont($FontFam,$aFontStyle,$aFontSize);
+ }
+
+ function SetTxt($aTxt) {
+ $this->iTxt->Set($aTxt);
+ }
+
+ function ParagraphAlign($aParaAlign) {
+ $this->iParaAlign = $aParaAlign;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->iFillColor = $aFillColor;
+ }
+
+ function SetAutoMargin($aMargin) {
+ $this->iAutoBoxMargin=$aMargin;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetFontColor($aColor) {
+ $this->iFontColor = $aColor;
+ }
+
+ function SetPos($xl=0,$yt=0,$w=0,$h=0) {
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function Pos($xl=0,$yt=0,$w=0,$h=0) {
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function Set($aTxt,$xl,$yt,$w=0,$h=0) {
+ $this->iTxt->Set($aTxt);
+ $this->ix = $xl;
+ $this->iy = $yt;
+ $this->iw = $w;
+ $this->ih = $h;
+ }
+
+ function SetCornerRadius($aRad=5) {
+ $this->ir = $aRad;
+ }
+
+ function Stroke($aImg,$scale) {
+
+ // If coordinates are specifed as negative this means we should
+ // treat them as abolsute (pixels) coordinates
+ if( $this->ix > 0 ) {
+ $this->ix = $scale->TranslateX($this->ix) ;
+ }
+ else {
+ $this->ix = -$this->ix;
+ }
+
+ if( $this->iy > 0 ) {
+ $this->iy = $scale->TranslateY($this->iy) ;
+ }
+ else {
+ $this->iy = -$this->iy;
+ }
+
+ list($this->iw,$this->ih) = $scale->Translate($this->iw,$this->ih) ;
+
+ if( $this->iw == 0 )
+ $this->iw = round($this->iTxt->GetWidth($aImg) + $this->iAutoBoxMargin);
+ if( $this->ih == 0 ) {
+ $this->ih = round($this->iTxt->GetTextHeight($aImg) + $this->iAutoBoxMargin);
+ }
+
+ if( $this->iShadowColor != '' ) {
+ $aImg->PushColor($this->iShadowColor);
+ $aImg->FilledRoundedRectangle($this->ix+$this->iShadowWidth,
+ $this->iy+$this->iShadowWidth,
+ $this->ix+$this->iw-1+$this->iShadowWidth,
+ $this->iy+$this->ih-1+$this->iShadowWidth,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ if( $this->iFillColor != '' ) {
+ $aImg->PushColor($this->iFillColor);
+ $aImg->FilledRoundedRectangle($this->ix,$this->iy,
+ $this->ix+$this->iw-1,
+ $this->iy+$this->ih-1,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ if( $this->iColor != '' ) {
+ $aImg->PushColor($this->iColor);
+ $aImg->RoundedRectangle($this->ix,$this->iy,
+ $this->ix+$this->iw-1,
+ $this->iy+$this->ih-1,
+ $this->ir);
+ $aImg->PopColor();
+ }
+
+ $this->iTxt->Align('center','center');
+ $this->iTxt->ParagraphAlign($this->iParaAlign);
+ $this->iTxt->SetColor($this->iFontColor);
+ $this->iTxt->Stroke($aImg, $this->ix+$this->iw/2, $this->iy+$this->ih/2);
+
+ return array($this->iw, $this->ih);
+
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_error.php b/includes/jpgraph/jpgraph_error.php
new file mode 100644
index 0000000..9301393
--- /dev/null
+++ b/includes/jpgraph/jpgraph_error.php
@@ -0,0 +1,154 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_ERROR.PHP
+// Description: Error plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_error.php,v 1.16 2003/02/04 22:47:16 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS ErrorPlot
+// Description: Error plot with min/max value for
+// each datapoint
+//===================================================
+class ErrorPlot extends Plot {
+ var $errwidth=2;
+//---------------
+// CONSTRUCTOR
+ function ErrorPlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ $this->numpoints /= 2;
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ if( $this->center ) {
+ $a=0.5; $b=0.5;
+ ++$this->numpoints;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+
+ // Method description
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $numpoints=count($this->coords[0])/2;
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $exist_x )
+ $xs=$this->coords[1][0];
+ else
+ $xs=0;
+
+
+ for( $i=0; $i<$numpoints; ++$i) {
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+ $xt = $xscale->Translate($x);
+ $yt1 = $yscale->Translate($this->coords[0][$i*2]);
+ $yt2 = $yscale->Translate($this->coords[0][$i*2+1]);
+ $img->Line($xt,$yt1,$xt,$yt2);
+ $img->Line($xt-$this->errwidth,$yt1,$xt+$this->errwidth,$yt1);
+ $img->Line($xt-$this->errwidth,$yt2,$xt+$this->errwidth,$yt2);
+ }
+ return true;
+ }
+} // Class
+
+
+//===================================================
+// CLASS ErrorLinePlot
+// Description: Combine a line and error plot
+// THIS IS A DEPRECATED PLOT TYPE JUST KEPT FOR
+// BACKWARD COMPATIBILITY
+//===================================================
+class ErrorLinePlot extends ErrorPlot {
+ var $line=null;
+//---------------
+// CONSTRUCTOR
+ function ErrorLinePlot(&$datay,$datax=false) {
+ $this->ErrorPlot($datay,$datax);
+ // Calculate line coordinates as the average of the error limits
+ for($i=0; $i < count($datay); $i+=2 ) {
+ $ly[]=($datay[$i]+$datay[$i+1])/2;
+ }
+ $this->line=new LinePlot($ly,$datax);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ if( $this->legend != "" )
+ $graph->legend->Add($this->legend,$this->color);
+ $this->line->Legend($graph);
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ parent::Stroke($img,$xscale,$yscale);
+ $this->line->Stroke($img,$xscale,$yscale);
+ }
+} // Class
+
+
+//===================================================
+// CLASS LineErrorPlot
+// Description: Combine a line and error plot
+//===================================================
+class LineErrorPlot extends ErrorPlot {
+ var $line=null;
+//---------------
+// CONSTRUCTOR
+ // Data is (val, errdeltamin, errdeltamax)
+ function LineErrorPlot(&$datay,$datax=false) {
+ $ly=array(); $ey=array();
+ $n = count($datay);
+ if( $n % 3 != 0 ) {
+ JpGraphError::Raise('Error in input data to LineErrorPlot.'.
+ 'Number of data points must be a multiple of 3');
+ }
+ for($i=0; $i < count($datay); $i+=3 ) {
+ $ly[]=$datay[$i];
+ $ey[]=$datay[$i]+$datay[$i+1];
+ $ey[]=$datay[$i]+$datay[$i+2];
+ }
+ $this->ErrorPlot($ey,$datax);
+ $this->line=new LinePlot($ly,$datax);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ if( $this->legend != "" )
+ $graph->legend->Add($this->legend,$this->color);
+ $this->line->Legend($graph);
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ parent::Stroke($img,$xscale,$yscale);
+ $this->line->Stroke($img,$xscale,$yscale);
+ }
+} // Class
+
+
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_flags.php b/includes/jpgraph/jpgraph_flags.php
new file mode 100644
index 0000000..2e750bc
--- /dev/null
+++ b/includes/jpgraph/jpgraph_flags.php
@@ -0,0 +1,360 @@
+<?php
+//=======================================================================
+// File: JPGRAPH_FLAGS.PHP
+// Description: Class Jpfile. Handles plotmarks
+// Created: 2003-06-28
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_flags.php,v 1.2.2.6 2003/08/26 20:33:56 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+//------------------------------------------------------------
+// Defines for the different basic sizes of flags
+//------------------------------------------------------------
+DEFINE('FLAGSIZE1',1);
+DEFINE('FLAGSIZE2',2);
+DEFINE('FLAGSIZE3',3);
+DEFINE('FLAGSIZE4',4);
+
+class FlagImages {
+
+ var $iCountryNameMap = array(
+ 'Afghanistan' => 'afgh',
+ 'Republic of Angola' => 'agla',
+ 'Republic of Albania' => 'alba',
+ 'Alderney' => 'alde',
+ 'Democratic and Popular Republic of Algeria' => 'alge',
+ 'Territory of American Samoa' => 'amsa',
+ 'Principality of Andorra' => 'andr',
+ 'British Overseas Territory of Anguilla' => 'angu',
+ 'Antarctica' => 'anta',
+ 'Argentine Republic' => 'arge',
+ 'League of Arab States' => 'arle',
+ 'Republic of Armenia' => 'arme',
+ 'Aruba' => 'arub',
+ 'Commonwealth of Australia' => 'astl',
+ 'Republic of Austria' => 'aust',
+ 'Azerbaijani Republic' => 'azer',
+ 'British Antarctic Territory' => 'bant',
+ 'Kingdom of Belgium' => 'belg',
+ 'British Overseas Territory of Bermuda' => 'berm',
+ 'Commonwealth of the Bahamas' => 'bhms',
+ 'Kingdom of Bahrain' => 'bhrn',
+ 'Republic of Belarus' => 'blru',
+ 'Republic of Bolivia' => 'blva',
+ 'Belize' => 'blze',
+ 'Republic of Benin' => 'bnin',
+ 'Republic of Botswana' => 'bots',
+ 'Federative Republic of Brazil' => 'braz',
+ 'Barbados' => 'brbd',
+ 'British Indian Ocean Territory' => 'brin',
+ 'Brunei Darussalam' => 'brun',
+ 'Republic of Burkina' => 'bufa',
+ 'Republic of Bulgaria' => 'bulg',
+ 'Republic of Burundi' => 'buru',
+ 'Overseas Territory of the British Virgin Islands' => 'bvis',
+ 'Central African Republic' => 'cafr',
+ 'Kingdom of Cambodia' => 'camb',
+ 'Republic of Cameroon' => 'came',
+ 'Dominion of Canada' => 'cana',
+ 'Caribbean Community' => 'cari',
+ 'Republic of Cape Verde' => 'cave',
+ 'Republic of Chad' => 'chad',
+ 'Republic of Chile' => 'chil',
+ 'Territory of Christmas Island' => 'chms',
+ 'Commonwealth of Independent States' => 'cins',
+ 'Cook Islands' => 'ckis',
+ 'Republic of Colombia' => 'clmb',
+ 'Territory of Cocos Islands' => 'cois',
+ 'Commonwealth' => 'comn',
+ 'Union of the Comoros' => 'como',
+ 'Republic of the Congo' => 'cong',
+ 'Republic of Costa Rica' => 'corc',
+ 'Republic of Croatia' => 'croa',
+ 'Republic of Cuba' => 'cuba',
+ 'British Overseas Territory of the Cayman Islands' => 'cyis',
+ 'Republic of Cyprus' => 'cypr',
+ 'The Czech Republic' => 'czec',
+ 'Kingdom of Denmark' => 'denm',
+ 'Republic of Djibouti' => 'djib',
+ 'Commonwealth of Dominica' => 'domn',
+ 'Dominican Republic' => 'dore',
+ 'Republic of Ecuador' => 'ecua',
+ 'Arab Republic of Egypt' => 'egyp',
+ 'Republic of El Salvador' => 'elsa',
+ 'England' => 'engl',
+ 'Republic of Equatorial Guinea' => 'eqgu',
+ 'State of Eritrea' => 'erit',
+ 'Republic of Estonia' => 'estn',
+ 'Ethiopia' => 'ethp',
+ 'European Union' => 'euun',
+ 'British Overseas Territory of the Falkland Islands' => 'fais',
+ 'International Federation of Vexillological Associations' => 'fiav',
+ 'Republic of Fiji' => 'fiji',
+ 'Republic of Finland' => 'finl',
+ 'Territory of French Polynesia' => 'fpol',
+ 'French Republic' => 'fran',
+ 'Overseas Department of French Guiana' => 'frgu',
+ 'Gabonese Republic' => 'gabn',
+ 'Republic of the Gambia' => 'gamb',
+ 'Republic of Georgia' => 'geor',
+ 'Federal Republic of Germany' => 'germ',
+ 'Republic of Ghana' => 'ghan',
+ 'Gibraltar' => 'gibr',
+ 'Hellenic Republic' => 'grec',
+ 'State of Grenada' => 'gren',
+ 'Overseas Department of Guadeloupe' => 'guad',
+ 'Territory of Guam' => 'guam',
+ 'Republic of Guatemala' => 'guat',
+ 'The Bailiwick of Guernsey' => 'guer',
+ 'Republic of Guinea' => 'guin',
+ 'Republic of Haiti' => 'hait',
+ 'Hong Kong Special Administrative Region' => 'hokn',
+ 'Republic of Honduras' => 'hond',
+ 'Republic of Hungary' => 'hung',
+ 'Republic of Iceland' => 'icel',
+ 'International Committee of the Red Cross' => 'icrc',
+ 'Republic of India' => 'inda',
+ 'Republic of Indonesia' => 'indn',
+ 'Republic of Iraq' => 'iraq',
+ 'Republic of Ireland' => 'irel',
+ 'Organization of the Islamic Conference' => 'isco',
+ 'Isle of Man' => 'isma',
+ 'State of Israel' => 'isra',
+ 'Italian Republic' => 'ital',
+ 'Jamaica' => 'jama',
+ 'Japan' => 'japa',
+ 'The Bailiwick of Jersey' => 'jers',
+ 'Hashemite Kingdom of Jordan' => 'jord',
+ 'Republic of Kazakhstan' => 'kazk',
+ 'Republic of Kenya' => 'keny',
+ 'Republic of Kiribati' => 'kirb',
+ 'State of Kuwait' => 'kuwa',
+ 'Kyrgyz Republic' => 'kyrg',
+ 'Republic of Latvia' => 'latv',
+ 'Lebanese Republic' => 'leba',
+ 'Kingdom of Lesotho' => 'lest',
+ 'Republic of Liberia' => 'libe',
+ 'Principality of Liechtenstein' => 'liec',
+ 'Republic of Lithuania' => 'lith',
+ 'Grand Duchy of Luxembourg' => 'luxe',
+ 'Macao Special Administrative Region' => 'maca',
+ 'Republic of Macedonia' => 'mace',
+ 'Republic of Madagascar' => 'mada',
+ 'Republic of the Marshall Islands' => 'mais',
+ 'Republic of Mali' => 'mali',
+ 'Federation of Malaysia' => 'mals',
+ 'Republic of Malta' => 'malt',
+ 'Republic of Malawi' => 'malw',
+ 'Overseas Department of Martinique' => 'mart',
+ 'Islamic Republic of Mauritania' => 'maur',
+ 'Territorial Collectivity of Mayotte' => 'mayt',
+ 'United Mexican States' => 'mexc',
+ 'Federated States of Micronesia' => 'micr',
+ 'Midway Islands' => 'miis',
+ 'Republic of Moldova' => 'mold',
+ 'Principality of Monaco' => 'mona',
+ 'Republic of Mongolia' => 'mong',
+ 'British Overseas Territory of Montserrat' => 'mont',
+ 'Kingdom of Morocco' => 'morc',
+ 'Republic of Mozambique' => 'moza',
+ 'Republic of Mauritius' => 'mrts',
+ 'Union of Myanmar' => 'myan',
+ 'Republic of Namibia' => 'namb',
+ 'North Atlantic Treaty Organization' => 'nato',
+ 'Republic of Nauru' => 'naur',
+ 'Turkish Republic of Northern Cyprus' => 'ncyp',
+ 'Netherlands Antilles' => 'nean',
+ 'Kingdom of Nepal' => 'nepa',
+ 'Kingdom of the Netherlands' => 'neth',
+ 'Territory of Norfolk Island' => 'nfis',
+ 'Federal Republic of Nigeria' => 'ngra',
+ 'Republic of Nicaragua' => 'nica',
+ 'Republic of Niger' => 'nigr',
+ 'Niue' => 'niue',
+ 'Commonwealth of the Northern Mariana Islands' => 'nmar',
+ 'Province of Northern Ireland' => 'noir',
+ 'Nordic Council' => 'nord',
+ 'Kingdom of Norway' => 'norw',
+ 'Territory of New Caledonia and Dependencies' => 'nwca',
+ 'New Zealand' => 'nwze',
+ 'Organization of American States' => 'oast',
+ 'Organization of African Unity' => 'oaun',
+ 'International Olympic Committee' => 'olym',
+ 'Sultanate of Oman' => 'oman',
+ 'Islamic Republic of Pakistan' => 'paks',
+ 'Republic of Palau' => 'pala',
+ 'Independent State of Papua New Guinea' => 'pang',
+ 'Republic of Paraguay' => 'para',
+ 'Republic of the Philippines' => 'phil',
+ 'British Overseas Territory of the Pitcairn Islands' => 'piis',
+ 'Republic of Poland' => 'pola',
+ 'Republic of Portugal' => 'port',
+ 'Commonwealth of Puerto Rico' => 'purc',
+ 'State of Qatar' => 'qata',
+ 'Russian Federation' => 'russ',
+ 'Republic of Rwanda' => 'rwan',
+ 'Kingdom of Saudi Arabia' => 'saar',
+ 'Republic of San Marino' => 'sama',
+ 'Nordic Sami Conference' => 'sami',
+ 'Sark' => 'sark',
+ 'Scotland' => 'scot',
+ 'Principality of Seborga' => 'sebo',
+ 'Republic of Sierra Leone' => 'sile',
+ 'Republic of Singapore' => 'sing',
+ 'Republic of Korea' => 'skor',
+ 'Republic of Slovenia' => 'slva',
+ 'Somali Republic' => 'smla',
+ 'Republic of Somaliland' => 'smld',
+ 'Republic of South Africa' => 'soaf',
+ 'Solomon Islands' => 'sois',
+ 'Kingdom of Spain' => 'span',
+ 'Secretariat of the Pacific Community' => 'spco',
+ 'Democratic Socialist Republic of Sri Lanka' => 'srla',
+ 'Saint Lucia' => 'stlu',
+ 'Republic of the Sudan' => 'suda',
+ 'Republic of Suriname' => 'surn',
+ 'Slovak Republic' => 'svka',
+ 'Kingdom of Sweden' => 'swdn',
+ 'Swiss Confederation' => 'swit',
+ 'Syrian Arab Republic' => 'syra',
+ 'Kingdom of Swaziland' => 'szld',
+ 'Republic of China' => 'taiw',
+ 'Republic of Tajikistan' => 'tajk',
+ 'United Republic of Tanzania' => 'tanz',
+ 'Kingdom of Thailand' => 'thal',
+ 'Autonomous Region of Tibet' => 'tibe',
+ 'Turkmenistan' => 'tkst',
+ 'Togolese Republic' => 'togo',
+ 'Tokelau' => 'toke',
+ 'Kingdom of Tonga' => 'tong',
+ 'Tristan da Cunha' => 'trdc',
+ 'Tromelin' => 'tris',
+ 'Republic of Tunisia' => 'tuns',
+ 'Republic of Turkey' => 'turk',
+ 'Tuvalu' => 'tuva',
+ 'United Arab Emirates' => 'uaem',
+ 'Republic of Uganda' => 'ugan',
+ 'Ukraine' => 'ukrn',
+ 'United Kingdom of Great Britain' => 'unkg',
+ 'United Nations' => 'unna',
+ 'United States of America' => 'unst',
+ 'Oriental Republic of Uruguay' => 'urgy',
+ 'Virgin Islands of the United States' => 'usvs',
+ 'Republic of Uzbekistan' => 'uzbk',
+ 'State of the Vatican City' => 'vacy',
+ 'Republic of Vanuatu' => 'vant',
+ 'Bolivarian Republic of Venezuela' => 'venz',
+ 'Republic of Yemen' => 'yemn',
+ 'Democratic Republic of Congo' => 'zare',
+ 'Republic of Zimbabwe' => 'zbwe' ) ;
+
+
+ var $iFlagCount = -1;
+ var $iFlagSetMap = array(
+ FLAGSIZE1 => 'flags_thumb35x35',
+ FLAGSIZE2 => 'flags_thumb60x60',
+ FLAGSIZE3 => 'flags_thumb100x100',
+ FLAGSIZE4 => 'flags'
+ );
+
+ var $iFlagData ;
+ var $iOrdIdx=array();
+
+ function FlagImages($aSize=FLAGSIZE1) {
+ switch($aSize) {
+ case FLAGSIZE1 :
+ case FLAGSIZE2 :
+ case FLAGSIZE3 :
+ case FLAGSIZE4 :
+ $file = dirname(__FILE__).'/'.$this->iFlagSetMap[$aSize].'.dat';
+ $fp = fopen($file,'rb');
+ $rawdata = fread($fp,filesize($file));
+ $this->iFlagData = unserialize($rawdata);
+ break;
+ default:
+ JpGraphError::Raise('Unknown flag size. ('.$aSize.')');
+ die();
+ }
+ $this->iFlagCount = count($this->iCountryNameMap);
+ }
+
+ function GetNum() {
+ return $this->iFlagCount;
+ }
+
+ function GetImgByName($aName,&$outFullName) {
+ $idx = $this->GetIdxByName($aName,$outFullName);
+ return $this->GetImgByIdx($idx);
+ }
+
+ function GetImgByIdx($aIdx) {
+ if( array_key_exists($aIdx,$this->iFlagData) ) {
+ $d = $this->iFlagData[$aIdx][1];
+ return Image::CreateFromString($d);
+ }
+ else {
+ JpGraphError::Raise("Flag index \" $aIdx\" does not exist.");
+ }
+ }
+
+ function GetIdxByOrdinal($aOrd,&$outFullName) {
+ $aOrd--;
+ $n = count($this->iOrdIdx);
+ if( $n == 0 ) {
+ reset($this->iCountryNameMap);
+ $this->iOrdIdx=array();
+ $i=0;
+ while( list($key,$val) = each($this->iCountryNameMap) ) {
+ $this->iOrdIdx[$i++] = array($val,$key);
+ }
+ $tmp=$this->iOrdIdx[$aOrd];
+ $outFullName = $tmp[1];
+ return $tmp[0];
+
+ }
+ elseif( $aOrd >= 0 && $aOrd < $n ) {
+ $tmp=$this->iOrdIdx[$aOrd];
+ $outFullName = $tmp[1];
+ return $tmp[0];
+ }
+ else {
+ JpGraphError::Raise('Invalid ordinal number specified for flag index.');
+ }
+ }
+
+ function GetIdxByName($aName,&$outFullName) {
+
+ if( is_integer($aName) ) {
+ $idx = $this->GetIdxByOrdinal($aName,$outFullName);
+ return $idx;
+ }
+
+ $found=false;
+ $aName = strtolower($aName);
+ $nlen = strlen($aName);
+ reset($this->iCountryNameMap);
+ // Match partial full country name or exact idx name
+ while( list($key,$val) = each($this->iCountryNameMap) ) {
+ if( strpos(strtolower($key), $aName) !== false ||
+ ($nlen == strlen($val) && $val == $aName) ) {
+ $found=true;
+ break;
+ }
+ }
+ if( $found ) {
+ $outFullName = $key;
+ return $val;
+ }
+ else {
+ JpGraphError::Raise("The (partial) country name \"$aName\" does not have a cooresponding flag image. The flag may still exist but under another name, e.g. insted of \"usa\" try \"united states\".");
+ }
+ }
+}
+
+
+
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_gantt.php b/includes/jpgraph/jpgraph_gantt.php
new file mode 100644
index 0000000..c24d6f8
--- /dev/null
+++ b/includes/jpgraph/jpgraph_gantt.php
@@ -0,0 +1,3566 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_GANTT.PHP
+// Description: JpGraph Gantt plot extension
+// Created: 2001-11-12
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gantt.php,v 1.46.2.36 2003/11/15 17:06:35 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (c) 2002 Johan Persson
+//========================================================================
+*/
+
+// Scale Header types
+DEFINE("GANTT_HDAY",1);
+DEFINE("GANTT_HWEEK",2);
+DEFINE("GANTT_HMONTH",4);
+DEFINE("GANTT_HYEAR",8);
+DEFINE("GANTT_HHOUR",16);
+DEFINE("GANTT_HMIN",32);
+
+// Bar patterns
+DEFINE("GANTT_RDIAG",BAND_RDIAG); // Right diagonal lines
+DEFINE("GANTT_LDIAG",BAND_LDIAG); // Left diagonal lines
+DEFINE("GANTT_SOLID",BAND_SOLID); // Solid one color
+DEFINE("GANTT_VLINE",BAND_VLINE); // Vertical lines
+DEFINE("GANTT_HLINE",BAND_HLINE); // Horizontal lines
+DEFINE("GANTT_3DPLANE",BAND_3DPLANE); // "3D" Plane
+DEFINE("GANTT_HVCROSS",BAND_HVCROSS); // Vertical/Hor crosses
+DEFINE("GANTT_DIAGCROSS",BAND_DIAGCROSS); // Diagonal crosses
+
+// Conversion constant
+DEFINE("SECPERDAY",3600*24);
+
+// Locales. ONLY KEPT FOR BACKWARDS COMPATIBILITY
+// You should use the proper locale strings directly
+// from now on.
+DEFINE("LOCALE_EN","en_UK");
+DEFINE("LOCALE_SV","sv_SE");
+
+// Layout of bars
+DEFINE("GANTT_EVEN",1);
+DEFINE("GANTT_FROMTOP",2);
+
+// Style for minute header
+DEFINE("MINUTESTYLE_MM",0); // 15
+DEFINE("MINUTESTYLE_CUSTOM",2); // Custom format
+
+
+// Style for hour header
+DEFINE("HOURSTYLE_HM24",0); // 13:10
+DEFINE("HOURSTYLE_HMAMPM",1); // 1:10pm
+DEFINE("HOURSTYLE_H24",2); // 13
+DEFINE("HOURSTYLE_HAMPM",3); // 1pm
+DEFINE("HOURSTYLE_CUSTOM",4); // User defined
+
+// Style for day header
+DEFINE("DAYSTYLE_ONELETTER",0); // "M"
+DEFINE("DAYSTYLE_LONG",1); // "Monday"
+DEFINE("DAYSTYLE_LONGDAYDATE1",2); // "Monday 23 Jun"
+DEFINE("DAYSTYLE_LONGDAYDATE2",3); // "Monday 23 Jun 2003"
+DEFINE("DAYSTYLE_SHORT",4); // "Mon"
+DEFINE("DAYSTYLE_SHORTDAYDATE1",5); // "Mon 23/6"
+DEFINE("DAYSTYLE_SHORTDAYDATE2",6); // "Mon 23 Jun"
+DEFINE("DAYSTYLE_SHORTDAYDATE3",7); // "Mon 23"
+DEFINE("DAYSTYLE_SHORTDATE1",8); // "23/6"
+DEFINE("DAYSTYLE_SHORTDATE2",9); // "23 Jun"
+DEFINE("DAYSTYLE_SHORTDATE3",10); // "Mon 23"
+DEFINE("DAYSTYLE_CUSTOM",11); // "M"
+
+// Styles for week header
+DEFINE("WEEKSTYLE_WNBR",0);
+DEFINE("WEEKSTYLE_FIRSTDAY",1);
+DEFINE("WEEKSTYLE_FIRSTDAY2",2);
+DEFINE("WEEKSTYLE_FIRSTDAYWNBR",3);
+DEFINE("WEEKSTYLE_FIRSTDAY2WNBR",4);
+
+// Styles for month header
+DEFINE("MONTHSTYLE_SHORTNAME",0);
+DEFINE("MONTHSTYLE_LONGNAME",1);
+DEFINE("MONTHSTYLE_LONGNAMEYEAR2",2);
+DEFINE("MONTHSTYLE_SHORTNAMEYEAR2",3);
+DEFINE("MONTHSTYLE_LONGNAMEYEAR4",4);
+DEFINE("MONTHSTYLE_SHORTNAMEYEAR4",5);
+
+
+// Types of constrain links
+DEFINE('CONSTRAIN_STARTSTART',0);
+DEFINE('CONSTRAIN_STARTEND',1);
+DEFINE('CONSTRAIN_ENDSTART',2);
+DEFINE('CONSTRAIN_ENDEND',3);
+
+// Arrow direction for constrain links
+DEFINE('ARROW_DOWN',0);
+DEFINE('ARROW_UP',1);
+DEFINE('ARROW_LEFT',2);
+DEFINE('ARROW_RIGHT',3);
+
+// Arrow type for constrain type
+DEFINE('ARROWT_SOLID',0);
+DEFINE('ARROWT_OPEN',1);
+
+// Arrow size for constrain lines
+DEFINE('ARROW_S1',0);
+DEFINE('ARROW_S2',1);
+DEFINE('ARROW_S3',2);
+DEFINE('ARROW_S4',3);
+DEFINE('ARROW_S5',4);
+
+// Activity types for use with utility method CreateSimple()
+DEFINE('ACTYPE_NORMAL',0);
+DEFINE('ACTYPE_GROUP',1);
+DEFINE('ACTYPE_MILESTONE',2);
+
+DEFINE('ACTINFO_3D',1);
+DEFINE('ACTINFO_2D',0);
+
+
+// Check if array_fill() exists
+if (!function_exists('array_fill')) {
+ function array_fill($iStart, $iLen, $vValue) {
+ $aResult = array();
+ for ($iCount = $iStart; $iCount < $iLen + $iStart; $iCount++) {
+ $aResult[$iCount] = $vValue;
+ }
+ return $aResult;
+ }
+}
+
+//===================================================
+// CLASS GanttActivityInfo
+// Description:
+//===================================================
+class GanttActivityInfo {
+ var $iColor='black';
+ var $iBackgroundColor='lightgray';
+ var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10,$iFontColor='black';
+ var $iTitles=array();
+ var $iWidth=array(),$iHeight=-1;
+ var $iLeftColMargin=4,$iRightColMargin=1,$iTopColMargin=1,$iBottomColMargin=3;
+ var $iTopHeaderMargin = 4;
+ var $vgrid = null;
+ var $iStyle=1;
+ var $iShow=true;
+ var $iHeaderAlign='center';
+
+ function GanttActivityInfo() {
+ $this->vgrid = new LineProperty();
+ }
+
+ function Hide($aF=true) {
+ $this->iShow=!$aF;
+ }
+
+ function Show($aF=true) {
+ $this->iShow=$aF;
+ }
+
+ // Specify font
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function SetColumnMargin($aLeft,$aRight) {
+ $this->iLeftColMargin = $aLeft;
+ $this->iRightColMargin = $aRight;
+ }
+
+ function SetFontColor($aFontColor) {
+ $this->iFontColor = $aFontColor;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetBackgroundColor($aColor) {
+ $this->iBackgroundColor = $aColor;
+ }
+
+ function SetColTitles($aTitles,$aWidth=null) {
+ $this->iTitles = $aTitles;
+ $this->iWidth = $aWidth;
+ }
+
+ function SetMinColWidth($aWidths) {
+ $n = min(count($this->iTitles),count($aWidths));
+ for($i=0; $i < $n; ++$i ) {
+ if( !empty($aWidths[$i]) ) {
+ if( empty($this->iWidth[$i]) ) {
+ $this->iWidth[$i] = $aWidths[$i];
+ }
+ else {
+ $this->iWidth[$i] = max($this->iWidth[$i],$aWidths[$i]);
+ }
+ }
+ }
+ }
+
+ function GetWidth($aImg) {
+ $txt = new TextProperty();
+ $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $n = count($this->iTitles) ;
+ $rm=$this->iRightColMargin;
+ $w = 0;
+ for($h=0, $i=0; $i < $n; ++$i ) {
+ $w += $this->iLeftColMargin;
+ $txt->Set($this->iTitles[$i]);
+ if( !empty($this->iWidth[$i]) ) {
+ $w1 = max($txt->GetWidth($aImg)+$rm,$this->iWidth[$i]);
+ }
+ else {
+ $w1 = $txt->GetWidth($aImg)+$rm;
+ }
+ $this->iWidth[$i] = $w1;
+ $w += $w1;
+ $h = max($h,$txt->GetHeight($aImg));
+ }
+ $this->iHeight = $h+$this->iTopHeaderMargin;
+ $txt='';
+ return $w;
+ }
+
+ function GetColStart($aImg,&$ioStart,$aAddLeftMargin=false) {
+ $n = count($this->iTitles) ;
+ $adj = $aAddLeftMargin ? $this->iLeftColMargin : 0;
+ $ioStart=array($aImg->left_margin+$adj);
+ for( $i=1; $i < $n; ++$i ) {
+ $ioStart[$i] = $ioStart[$i-1]+$this->iLeftColMargin+$this->iWidth[$i-1];
+ }
+ }
+
+ // Adjust headers left, right or centered
+ function SetHeaderAlign($aAlign) {
+ $this->iHeaderAlign=$aAlign;
+ }
+
+ function Stroke($aImg,$aXLeft,$aYTop,$aXRight,$aYBottom,$aUseTextHeight=false) {
+
+ if( !$this->iShow ) return;
+
+ $txt = new TextProperty();
+ $txt->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $txt->SetColor($this->iFontColor);
+ $txt->SetAlign($this->iHeaderAlign,'top');
+ $n=count($this->iTitles);
+
+ if( $n == 0 )
+ return;
+
+ $x = $aXLeft;
+ $h = $this->iHeight;
+ $yTop = $aUseTextHeight ? $aYBottom-$h-$this->iTopColMargin-$this->iBottomColMargin : $aYTop ;
+
+ if( $h < 0 ) {
+ JpGraphError::Raise('Internal error. Height for ActivityTitles is < 0');
+ }
+
+ $aImg->SetLineWeight(1);
+ // Set background color
+ $aImg->SetColor($this->iBackgroundColor);
+ $aImg->FilledRectangle($aXLeft,$yTop,$aXRight,$aYBottom-1);
+
+ if( $this->iStyle == 1 ) {
+ // Make a 3D effect
+ $aImg->SetColor('white');
+ $aImg->Line($aXLeft,$yTop+1,
+ $aXRight,$yTop+1);
+ }
+
+ for($i=0; $i < $n; ++$i ) {
+ if( $this->iStyle == 1 ) {
+ // Make a 3D effect
+ $aImg->SetColor('white');
+ $aImg->Line($x+1,$yTop,$x+1,$aYBottom);
+ }
+ $x += $this->iLeftColMargin;
+ $txt->Set($this->iTitles[$i]);
+
+ // Adjust the text anchor position according to the choosen alignment
+ $xp = $x;
+ if( $this->iHeaderAlign == 'center' ) {
+ $xp = (($x-$this->iLeftColMargin)+($x+$this->iWidth[$i]))/2;
+ }
+ elseif( $this->iHeaderAlign == 'right' ) {
+ $xp = $x +$this->iWidth[$i]-$this->iRightColMargin;
+ }
+
+ $txt->Stroke($aImg,$xp,$yTop+$this->iTopHeaderMargin);
+ $x += $this->iWidth[$i];
+ if( $i < $n-1 ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->Line($x,$yTop,$x,$aYBottom);
+ }
+ }
+
+ $aImg->SetColor($this->iColor);
+ $aImg->Line($aXLeft,$yTop, $aXRight,$yTop);
+
+ // Stroke vertical column dividers
+ $cols=array();
+ $this->GetColStart($aImg,$cols);
+ $n=count($cols);
+ for( $i=1; $i < $n; ++$i ) {
+ $this->vgrid->Stroke($aImg,$cols[$i],$aYBottom,$cols[$i],
+ $aImg->height - $aImg->bottom_margin);
+ }
+ }
+}
+
+
+//===================================================
+// CLASS GanttGraph
+// Description: Main class to handle gantt graphs
+//===================================================
+class GanttGraph extends Graph {
+ var $scale; // Public accessible
+ var $iObj=array(); // Gantt objects
+ var $iLabelHMarginFactor=0.2; // 10% margin on each side of the labels
+ var $iLabelVMarginFactor=0.4; // 40% margin on top and bottom of label
+ var $iLayout=GANTT_FROMTOP; // Could also be GANTT_EVEN
+ var $iSimpleFont = FF_FONT1,$iSimpleFontSize=11;
+ var $iSimpleStyle=GANTT_RDIAG,$iSimpleColor='yellow',$iSimpleBkgColor='red';
+ var $iSimpleProgressBkgColor='gray',$iSimpleProgressColor='darkgreen';
+ var $iSimpleProgressStyle=GANTT_SOLID;
+//---------------
+// CONSTRUCTOR
+ // Create a new gantt graph
+ function GanttGraph($aWidth=0,$aHeight=0,$aCachedName="",$aTimeOut=0,$aInline=true) {
+
+ // Backward compatibility
+ if( $aWidth == -1 ) $aWidth=0;
+ if( $aHeight == -1 ) $aHeight=0;
+
+ if( $aWidth< 0 || $aHeight < 0 ) {
+ JpgraphError::Raise("You can't specify negative sizes for Gantt graph dimensions. Use 0 to indicate that you want the library to automatically determine a dimension.");
+ }
+ Graph::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline);
+ $this->scale = new GanttScale($this->img);
+ if( $aWidth > 0 )
+ $this->img->SetMargin($aWidth/17,$aWidth/17,$aHeight/7,$aHeight/10);
+
+ $this->scale->ShowHeaders(GANTT_HWEEK|GANTT_HDAY);
+ $this->SetBox();
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ //
+
+ function SetSimpleFont($aFont,$aSize) {
+ $this->iSimpleFont = $aFont;
+ $this->iSimpleFontSize = $aSize;
+ }
+
+ function SetSimpleStyle($aBand,$aColor,$aBkgColor) {
+ $this->iSimpleStyle = $aBand;
+ $this->iSimpleColor = $aColor;
+ $this->iSimpleBkgColor = $aSimpleBkgColor;
+ }
+
+ // A utility function to help create basic Gantt charts
+ function CreateSimple($data,$constrains=array(),$progress=array()) {
+
+ for( $i=0; $i < count($data); ++$i) {
+ switch( $data[$i][1] ) {
+ case ACTYPE_GROUP:
+ // Create a slightly smaller height bar since the
+ // "wings" at the end will make it look taller
+ $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',8);
+ $a->title->SetFont($this->iSimpleFont,FS_BOLD,$this->iSimpleFontSize);
+ $a->rightMark->Show();
+ $a->rightMark->SetType(MARK_RIGHTTRIANGLE);
+ $a->rightMark->SetWidth(8);
+ $a->rightMark->SetColor('black');
+ $a->rightMark->SetFillColor('black');
+
+ $a->leftMark->Show();
+ $a->leftMark->SetType(MARK_LEFTTRIANGLE);
+ $a->leftMark->SetWidth(8);
+ $a->leftMark->SetColor('black');
+ $a->leftMark->SetFillColor('black');
+
+ $a->SetPattern(BAND_SOLID,'black');
+ $csimpos = 6;
+ break;
+
+ case ACTYPE_NORMAL:
+ $a = new GanttBar($data[$i][0],$data[$i][2],$data[$i][3],$data[$i][4],'',10);
+ $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
+ $a->SetPattern($this->iSimpleStyle,$this->iSimpleColor);
+ $a->SetFillColor($this->iSimpleBkgColor);
+ // Check if this activity should have a constrain line
+ $n = count($constrains);
+ for( $j=0; $j < $n; ++$j ) {
+ if( $constrains[$j][0]==$data[$i][0] ) {
+ $a->SetConstrain($constrains[$j][1],$constrains[$j][2],'black',ARROW_S2,ARROWT_SOLID);
+ break;
+ }
+ }
+
+ // Check if this activity have a progress bar
+ $n = count($progress);
+ for( $j=0; $j < $n; ++$j ) {
+ if( $progress[$j][0]==$data[$i][0] ) {
+ $a->progress->Set($progress[$j][1]);
+ $a->progress->SetPattern($this->iSimpleProgressStyle,
+ $this->iSimpleProgressColor);
+ $a->progress->SetFillColor($this->iSimpleProgressBkgColor);
+ //$a->progress->SetPattern($progress[$j][2],$progress[$j][3]);
+ break;
+ }
+ }
+ $csimpos = 6;
+ break;
+
+ case ACTYPE_MILESTONE:
+ $a = new MileStone($data[$i][0],$data[$i][2],$data[$i][3]);
+ $a->title->SetFont($this->iSimpleFont,FS_NORMAL,$this->iSimpleFontSize);
+ $csimpos = 5;
+ break;
+ default:
+ die('Unknown activity type');
+ break;
+ }
+
+ // Setup caption
+ $a->caption->Set($data[$i][$csimpos-1]);
+
+ // Check if this activity should have a CSIM target ?
+ if( !empty($data[$i][$csimpos]) ) {
+ $a->SetCSIMTarget($data[$i][$csimpos]);
+ $a->SetCSIMAlt($data[$i][$csimpos+1]);
+ }
+ if( !empty($data[$i][$csimpos+2]) ) {
+ $a->title->SetCSIMTarget($data[$i][$csimpos+2]);
+ $a->title->SetCSIMAlt($data[$i][$csimpos+3]);
+ }
+
+ $this->Add($a);
+ }
+ }
+
+
+ // Set what headers should be shown
+ function ShowHeaders($aFlg) {
+ $this->scale->ShowHeaders($aFlg);
+ }
+
+ // Specify the fraction of the font height that should be added
+ // as vertical margin
+ function SetLabelVMarginFactor($aVal) {
+ $this->iLabelVMarginFactor = $aVal;
+ }
+
+ // Synonym to the method above
+ function SetVMarginFactor($aVal) {
+ $this->iLabelVMarginFactor = $aVal;
+ }
+
+
+ // Add a new Gantt object
+ function Add($aObject) {
+ if( is_array($aObject) && count($aObject) > 0 ) {
+ $cl = get_class($aObject[0]);
+ if( $cl == 'iconplot' ) {
+ $this->AddIcon($aObject);
+ }
+ else {
+ for($i=0; $i < count($aObject); ++$i)
+ $this->iObj[] = $aObject[$i];
+ }
+ }
+ else {
+ $cl = get_class($aObject);
+ if( $cl == 'iconplot' ) {
+ $this->AddIcon($aObject);
+ }
+ else {
+ $this->iObj[] = $aObject;
+ }
+ }
+ }
+
+ // Override inherit method from Graph and give a warning message
+ function SetScale() {
+ JpGraphError::Raise("SetScale() is not meaningfull with Gantt charts.");
+ }
+
+ // Specify the date range for Gantt graphs (if this is not set it will be
+ // automtically determined from the input data)
+ function SetDateRange($aStart,$aEnd) {
+ // Adjust the start and end so that the indicate the
+ // begining and end of respective start and end days
+ if( strpos($aStart,':') === false )
+ $aStart = date('Y-m-d 00:00',strtotime($aStart));
+ if( strpos($aEnd,':') === false )
+ $aEnd = date('Y-m-d 23:59',strtotime($aEnd));
+ $this->scale->SetRange($aStart,$aEnd);
+ }
+
+ // Get the maximum width of the activity titles columns for the bars
+ // The name is lightly misleading since we from now on can have
+ // multiple columns in the label section. When this was first written
+ // it only supported a single label, hence the name.
+ function GetMaxLabelWidth() {
+ $m=50;
+ if( $this->iObj != null ) {
+ $marg = $this->scale->actinfo->iLeftColMargin+$this->scale->actinfo->iRightColMargin;
+ $m = $this->iObj[0]->title->GetWidth($this->img)+$marg;
+ for($i=1; $i < count($this->iObj); ++$i) {
+ if( $this->iObj[$i]->title->HasTabs() ) {
+ list($tot,$w) = $this->iObj[$i]->title->GetWidth($this->img,true);
+ $m=max($m,$tot);
+ }
+ else
+ $m=max($m,$this->iObj[$i]->title->GetWidth($this->img));
+ }
+ }
+ return $m;
+ }
+
+ // Get the maximum height of the titles for the bars
+ function GetMaxLabelHeight() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->title->GetHeight($this->img);
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->title->GetHeight($this->img));
+ }
+ }
+ return $m;
+ }
+
+ function GetMaxBarAbsHeight() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->GetAbsHeight($this->img);
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->GetAbsHeight($this->img));
+ }
+ }
+ return $m;
+ }
+
+ // Get the maximum used line number (vertical position) for bars
+ function GetBarMaxLineNumber() {
+ $m=0;
+ if( $this->iObj != null ) {
+ $m = $this->iObj[0]->GetLineNbr();
+ for($i=1; $i<count($this->iObj); ++$i) {
+ $m=max($m,$this->iObj[$i]->GetLineNbr());
+ }
+ }
+ return $m;
+ }
+
+ // Get the minumum and maximum used dates for all bars
+ function GetBarMinMax() {
+ $max=$this->scale->NormalizeDate($this->iObj[0]->GetMaxDate());
+ $min=$this->scale->NormalizeDate($this->iObj[0]->GetMinDate());
+ for($i=1; $i < count($this->iObj); ++$i) {
+ $max=Max($max,$this->scale->NormalizeDate($this->iObj[$i]->GetMaxDate()));
+ $min=Min($min,$this->scale->NormalizeDate($this->iObj[$i]->GetMinDate()));
+ }
+ $minDate = date("Y-m-d",$min);
+ $min = strtotime($minDate);
+ $maxDate = date("Y-m-d 23:59",$max);
+ $max = strtotime($maxDate);
+ return array($min,$max);
+ }
+
+ // Create a new auto sized canvas if the user hasn't specified a size
+ // The size is determined by what scale the user has choosen and hence
+ // the minimum width needed to display the headers. Some margins are
+ // also added to make it better looking.
+ function AutoSize() {
+ if( $this->img->img == null ) {
+ // The predefined left, right, top, bottom margins.
+ // Note that the top margin might incease depending on
+ // the title.
+ $lm=30;$rm=30;$tm=20;$bm=30;
+ if( BRAND_TIMING ) $bm += 10;
+
+ // First find out the height
+ $n=$this->GetBarMaxLineNumber()+1;
+ $m=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
+ $height=$n*((1+$this->iLabelVMarginFactor)*$m);
+
+ // Add the height of the scale titles
+ $h=$this->scale->GetHeaderHeight();
+ $height += $h;
+
+ // Calculate the top margin needed for title and subtitle
+ if( $this->title->t != "" ) {
+ $tm += $this->title->GetFontHeight($this->img);
+ }
+ if( $this->subtitle->t != "" ) {
+ $tm += $this->subtitle->GetFontHeight($this->img);
+ }
+
+ // ...and then take the bottom and top plot margins into account
+ $height += $tm + $bm + $this->scale->iTopPlotMargin + $this->scale->iBottomPlotMargin;
+ // Now find the minimum width for the chart required
+
+ // If day scale or smaller is shown then we use the day font width
+ // as the base size unit.
+ // If only weeks or above is displayed we use a modified unit to
+ // get a smaller image.
+
+ if( $this->scale->IsDisplayDay() || $this->scale->IsDisplayHour() ||
+ $this->scale->IsDisplayMinute() ) {
+ // Add 2 pixel margin on each side
+ $fw=$this->scale->day->GetFontWidth($this->img)+4;
+ }
+ elseif( $this->scale->IsDisplayWeek() ) {
+ $fw = 8;
+ }
+ elseif( $this->scale->IsDisplayMonth() ) {
+ $fw = 4;
+ }
+ else {
+ $fw = 2;
+ }
+ $nd=$this->scale->GetNumberOfDays();
+
+ if( $this->scale->IsDisplayDay() ) {
+ // If the days are displayed we also need to figure out
+ // how much space each day's title will require.
+ switch( $this->scale->day->iStyle ) {
+ case DAYSTYLE_LONG :
+ $txt = "Monday";
+ break;
+ case DAYSTYLE_LONGDAYDATE1 :
+ $txt = "Monday 23 Jun";
+ break;
+ case DAYSTYLE_LONGDAYDATE2 :
+ $txt = "Monday 23 Jun 2003";
+ break;
+ case DAYSTYLE_SHORT :
+ $txt = "Mon";
+ break;
+ case DAYSTYLE_SHORTDAYDATE1 :
+ $txt = "Mon 23/6";
+ break;
+ case DAYSTYLE_SHORTDAYDATE2 :
+ $txt = "Mon 23 Jun";
+ break;
+ case DAYSTYLE_SHORTDAYDATE3 :
+ $txt = "Mon 23";
+ break;
+ case DAYSTYLE_SHORTDATE1 :
+ $txt = "23/6";
+ break;
+ case DAYSTYLE_SHORTDATE2 :
+ $txt = "23 Jun";
+ break;
+ case DAYSTYLE_SHORTDATE3 :
+ $txt = "Mon 23";
+ break;
+ case DAYSTYLE_CUSTOM :
+ $txt = date($this->scale->day->iLabelFormStr,
+ strtotime('2003-12-20 18:00'));
+ break;
+ case DAYSTYLE_ONELETTER :
+ default:
+ $txt = "M";
+ break;
+ }
+ $fw = $this->scale->day->GetStrWidth($this->img,$txt)+6;
+ }
+
+ // If we have hours enabled we must make sure that each day has enough
+ // space to fit the number of hours to be displayed.
+ if( $this->scale->IsDisplayHour() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->hour->iStyle ) {
+ case HOURSTYLE_HMAMPM:
+ $txt = '12:00pm';
+ break;
+ case HOURSTYLE_H24:
+ // 13
+ $txt = '24';
+ break;
+ case HOURSTYLE_HAMPM:
+ $txt = '12pm';
+ break;
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->scale->hour->iLabelFormStr,strtotime('2003-12-20 18:00'));
+ break;
+ case HOURSTYLE_HM24:
+ default:
+ $txt = '24:00';
+ break;
+ }
+
+ $hfw = $this->scale->hour->GetStrWidth($this->img,$txt)+6;
+ $mw = $hfw;
+ if( $this->scale->IsDisplayMinute() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->minute->iStyle ) {
+ case HOURSTYLE_CUSTOM:
+ $txt2 = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ $txt2 = '15';
+ break;
+ }
+
+ $mfw = $this->scale->minute->GetStrWidth($this->img,$txt2)+6;
+ $n2 = ceil(60 / $this->scale->minute->GetIntervall() );
+ $mw = $n2 * $mfw;
+ }
+ $hfw = $hfw < $mw ? $mw : $hfw ;
+ $n = ceil(24*60 / $this->scale->TimeToMinutes($this->scale->hour->GetIntervall()) );
+ $hw = $n * $hfw;
+ $fw = $fw < $hw ? $hw : $fw ;
+ }
+
+ // We need to repeat this code block here as well.
+ // THIS iS NOT A MISTAKE !
+ // We really need it since we need to adjust for minutes both in the case
+ // where hour scale is shown and when it is not shown.
+
+ if( $this->scale->IsDisplayMinute() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space. We therefore create a typical string for the choosen format
+ // and determine the length of that string.
+ switch( $this->scale->minute->iStyle ) {
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->scale->minute->iLabelFormStr,strtotime('2005-05-15 18:55'));
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ $txt = '15';
+ break;
+ }
+
+ $mfw = $this->scale->minute->GetStrWidth($this->img,$txt)+6;
+ $n = ceil(60 / $this->scale->TimeToMinutes($this->scale->minute->GetIntervall()) );
+ $mw = $n * $mfw;
+ $fw = $fw < $mw ? $mw : $fw ;
+ }
+
+ // If we display week we must make sure that 7*$fw is enough
+ // to fit up to 10 characters of the week font (if the week is enabled)
+ if( $this->scale->IsDisplayWeek() ) {
+ // Depending on what format the user has choose we need different amount
+ // of space
+ $fsw = strlen($this->scale->week->iLabelFormStr);
+ if( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $fsw += 8;
+ }
+ elseif( $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ) {
+ $fsw += 7;
+ }
+ else {
+ $fsw += 4;
+ }
+
+ $ww = $fsw*$this->scale->week->GetFontWidth($this->img);
+ if( 7*$fw < $ww ) {
+ $fw = ceil($ww/7);
+ }
+ }
+
+ if( !$this->scale->IsDisplayDay() && !$this->scale->IsDisplayHour() &&
+ !( ($this->scale->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->scale->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR) && $this->scale->IsDisplayWeek() ) ) {
+ // If we don't display the individual days we can shrink the
+ // scale a little bit. This is a little bit pragmatic at the
+ // moment and should be re-written to take into account
+ // a) What scales exactly are shown and
+ // b) what format do they use so we know how wide we need to
+ // make each scale text space at minimum.
+ $fw /= 2;
+ if( !$this->scale->IsDisplayWeek() ) {
+ $fw /= 1.8;
+ }
+ }
+
+ // Has the user specified a width or do we need to
+ // determine it?
+ if( $this->img->width <= 0 ) {
+ // Now determine the width for the activity titles column
+
+ // Firdst find out the maximum width of each object column
+ $cw = $this->GetMaxActInfoColWidth() ;
+ $this->scale->actinfo->SetMinColWidth($cw);
+ $titlewidth = max(max($this->GetMaxLabelWidth(),
+ $this->scale->tableTitle->GetWidth($this->img)),
+ $this->scale->actinfo->GetWidth($this->img));
+
+ // Add the width of the vertivcal divider line
+ $titlewidth += $this->scale->divider->iWeight*2;
+
+
+ // Now get the total width taking
+ // titlewidth, left and rigt margin, dayfont size
+ // into account
+ $width = $titlewidth + $nd*$fw + $lm+$rm;
+ }
+ else
+ $width = $this->img->width;
+
+ $this->img->CreateImgCanvas($width,$height);
+ $this->img->SetMargin($lm,$rm,$tm,$bm);
+ }
+ }
+
+ // Return an array width the maximum width for each activity
+ // column. This is used when we autosize the columns where we need
+ // to find out the maximum width of each column. In order to do that we
+ // must walk through all the objects, sigh...
+ function GetMaxActInfoColWidth() {
+ $n = count($this->iObj);
+ if( $n == 0 ) return;
+ $w = array();
+ $m = $this->scale->actinfo->iLeftColMargin + $this->scale->actinfo->iRightColMargin;
+
+ for( $i=0; $i < $n; ++$i ) {
+ $tmp = $this->iObj[$i]->title->GetColWidth($this->img,$m);
+ $nn = count($tmp);
+ for( $j=0; $j < $nn; ++$j ) {
+ if( empty($w[$j]) )
+ $w[$j] = $tmp[$j];
+ else
+ $w[$j] = max($w[$j],$tmp[$j]);
+ }
+ }
+ return $w;
+ }
+
+ // Stroke the gantt chart
+ function Stroke($aStrokeFileName="") {
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // Should we autoscale dates?
+ if( !$this->scale->IsRangeSet() ) {
+ list($min,$max) = $this->GetBarMinMax();
+ $this->scale->SetRange($min,$max);
+ }
+
+ $this->scale->AdjustStartEndDay();
+
+ // Check if we should autoscale the image
+ $this->AutoSize();
+
+ // Should we start from the top or just spread the bars out even over the
+ // available height
+ $this->scale->SetVertLayout($this->iLayout);
+ if( $this->iLayout == GANTT_FROMTOP ) {
+ $maxheight=max($this->GetMaxLabelHeight(),$this->GetMaxBarAbsHeight());
+ $this->scale->SetVertSpacing($maxheight*(1+$this->iLabelVMarginFactor));
+ }
+ // If it hasn't been set find out the maximum line number
+ if( $this->scale->iVertLines == -1 )
+ $this->scale->iVertLines = $this->GetBarMaxLineNumber()+1;
+
+ $maxwidth=max($this->scale->actinfo->GetWidth($this->img),
+ max($this->GetMaxLabelWidth(),
+ $this->scale->tableTitle->GetWidth($this->img)));
+
+ $this->scale->SetLabelWidth($maxwidth+$this->scale->divider->iWeight);//*(1+$this->iLabelHMarginFactor));
+
+ if( !$_csim )
+ $this->StrokePlotArea();
+
+ $this->scale->Stroke();
+
+ if( !$_csim ) {
+ // Due to rounding we need to draw the box + pixel to the right
+ $this->img->right_margin--;
+ $this->StrokePlotBox();
+ $this->img->right_margin++;
+ }
+
+ $n = count($this->iObj);
+ for($i=0; $i < $n; ++$i) {
+ //$this->iObj[$i]->SetLabelLeftMargin(round($maxwidth*$this->iLabelHMarginFactor/2));
+ $this->iObj[$i]->Stroke($this->img,$this->scale);
+ }
+
+ if( !$_csim ) {
+ $this->StrokeConstrains();
+ $this->StrokeTitles();
+ $this->footer->Stroke($this->img);
+
+ // Should we do any final image transformation
+ if( $this->iImgTrans ) {
+ if( !class_exists('ImgTrans') ) {
+ require_once('jpgraph_imgtrans.php');
+ }
+
+ $tform = new ImgTrans($this->img->img);
+ $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
+ $this->iImgTransDirection,$this->iImgTransHighQ,
+ $this->iImgTransMinSize,$this->iImgTransFillColor,
+ $this->iImgTransBorder);
+ }
+
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+
+ function StrokeConstrains() {
+ $n = count($this->iObj);
+
+ // Stroke all constrains
+ for($i=0; $i < $n; ++$i) {
+
+ $numConstrains = count($this->iObj[$i]->constraints);
+
+ for( $k = 0; $k < $numConstrains; $k++ ) {
+ $vpos = $this->iObj[$i]->constraints[$k]->iConstrainRow;
+ if( $vpos >= 0 ) {
+ $c1 = $this->iObj[$i]->iConstrainPos;
+
+ // Find out which object is on the target row
+ $targetobj = -1;
+ for( $j=0; $j < $n && $targetobj == -1; ++$j ) {
+ if( $this->iObj[$j]->iVPos == $vpos ) {
+ $targetobj = $j;
+ }
+ }
+ if( $targetobj == -1 ) {
+ JpGraphError::Raise('You have specifed a constrain from row='.
+ $this->iObj[$i]->iVPos.
+ ' to row='.$vpos.' which does not have any activity.');
+ exit();
+ }
+ $c2 = $this->iObj[$targetobj]->iConstrainPos;
+ if( count($c1) == 4 && count($c2 ) == 4) {
+ switch( $this->iObj[$i]->constraints[$k]->iConstrainType ) {
+ case CONSTRAIN_ENDSTART:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[2],$c1[3],$c2[0],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[2],$c1[1],$c2[0],$c2[3]);
+ }
+ $link->SetPath(3);
+ break;
+ case CONSTRAIN_STARTEND:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[0],$c1[3],$c2[2],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[0],$c1[1],$c2[2],$c2[3]);
+ }
+ $link->SetPath(0);
+ break;
+ case CONSTRAIN_ENDEND:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[2],$c1[3],$c2[2],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[2],$c1[1],$c2[2],$c2[3]);
+ }
+ $link->SetPath(1);
+ break;
+ case CONSTRAIN_STARTSTART:
+ if( $c1[1] < $c2[1] ) {
+ $link = new GanttLink($c1[0],$c1[3],$c2[0],$c2[1]);
+ }
+ else {
+ $link = new GanttLink($c1[0],$c1[1],$c2[0],$c2[3]);
+ }
+ $link->SetPath(3);
+ break;
+ default:
+ JpGraphError::Raise('Unknown constrain type specified from row='.
+ $this->iObj[$i]->iVPos.
+ ' to row='.$vpos);
+ break;
+ }
+
+ $link->SetColor($this->iObj[$i]->constraints[$k]->iConstrainColor);
+ $link->SetArrow($this->iObj[$i]->constraints[$k]->iConstrainArrowSize,
+ $this->iObj[$i]->constraints[$k]->iConstrainArrowType);
+
+ $link->Stroke($this->img);
+ }
+ }
+ }
+ }
+ }
+
+ function GetCSIMAreas() {
+ if( !$this->iHasStroked )
+ $this->Stroke(_CSIM_SPECIALFILE);
+ $csim='';
+ $n = count($this->iObj);
+ for( $i=$n-1; $i >= 0; --$i )
+ $csim .= $this->iObj[$i]->GetCSIMArea();
+ return $csim;
+ }
+}
+
+//===================================================
+// CLASS PredefIcons
+// Description: Predefined icons for use with Gantt charts
+//===================================================
+DEFINE('GICON_WARNINGRED',0);
+DEFINE('GICON_TEXT',1);
+DEFINE('GICON_ENDCONS',2);
+DEFINE('GICON_MAIL',3);
+DEFINE('GICON_STARTCONS',4);
+DEFINE('GICON_CALC',5);
+DEFINE('GICON_MAGNIFIER',6);
+DEFINE('GICON_LOCK',7);
+DEFINE('GICON_STOP',8);
+DEFINE('GICON_WARNINGYELLOW',9);
+DEFINE('GICON_FOLDEROPEN',10);
+DEFINE('GICON_FOLDER',11);
+DEFINE('GICON_TEXTIMPORTANT',12);
+
+class PredefIcons {
+ var $iBuiltinIcon = null;
+ var $iLen = -1 ;
+
+ function GetLen() {
+ return $this->iLen ;
+ }
+
+ function GetImg($aIdx) {
+ if( $aIdx < 0 || $aIdx >= $this->iLen ) {
+ JpGraphError::Raise('Illegal icon index for Gantt builtin icon ['.$aIdx.']');
+ }
+ return Image::CreateFromString(base64_decode($this->iBuiltinIcon[$aIdx][1]));
+ }
+
+ function PredefIcons() {
+ //==========================================================
+ // warning.png
+ //==========================================================
+ $this->iBuiltinIcon[0][0]= 1043 ;
+ $this->iBuiltinIcon[0][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAA'.
+ 'B3RJTUUH0wgKFSgilWPhUQAAA6BJREFUeNrtl91rHFUYh5/3zMx+Z5JNUoOamCZNaqTZ6IWIkqRiQWmi1IDetHfeiCiltgXBP8AL'.
+ '0SIUxf/AvfRSBS9EKILFFqyIH9CEmFZtPqrBJLs7c+b1YneT3WTTbNsUFPLCcAbmzPt73o9zzgzs2Z793231UOdv3w9k9Z2uzOdA'.
+ '5+2+79yNeL7Hl7hw7oeixRMZ6PJM26W18DNAm/Vh7lR8fqh97NmMF11es1iFpMATqdirwMNA/J4DpIzkr5YsAF1PO6gIMYHRdPwl'.
+ 'oO2elmB+qH3sm7XozbkgYvy8SzYnZPtcblyM6I+5z3jQ+0vJfgpEu56BfI9vUkbyi2HZd1QJoeWRiAjBd4SDCW8SSAOy6wBHMzF7'.
+ 'YdV2A+ROuvRPLfHoiSU0EMY/cDAIhxJeGngKaN1VgHyPL7NBxI1K9P4QxBzw3K1zJ/zkG8B9uwaQ7/HNsRZv9kohBGD0o7JqMYS/'.
+ '/ynPidQw/LrBiPBcS/yFCT95DvB2BWAy4575PaQbQKW+tPd3GCItu2odKI++YxiKu0d26oWmAD7paZU/rLz37VqIijD2YbnzNBBE'.
+ 'IBHf8K8qjL7vYhCGErEU8CTg3xXAeMp96GrJEqkyXkm9Bhui1xfsunjdGhcYLq+IzjsGmBt5YH/cmJkFq6gIqlon3u4LxdKGuCIo'.
+ 'Qu41g0E41po+2R33Xt5uz9kRIB2UTle7PnfKrROP1HD4sRjZlq0lzhwoZ6rDNeTi3nEg1si/7FT7kYQbXS6E5E65tA5uRF9tutq0'.
+ 'K/VwAF+/FbIYWt6+tjQM/AqUms7A4Wy6d7YSfSNxgMmzi0ycWWworio4QJvj4LpuL5BqugTnXzzqJsJwurrlNhJXFaavW67NRw3F'.
+ 'q+aJcCQVe9fzvJGmAY7/dPH0gi0f64OveGxa+usCuQMeZ0+kt8BVrX+qPO9Bzx0MgqBvs+a2PfDdYIf+WAjXU1ub4tqNaPPzRs8A'.
+ 'blrli+WVn79cXn0cWKl+tGx7HLc7pu3CSmnfitL+l1UihAhwjFkPQev4K/fSABjBM8JCaFuurJU+rgW41SroA8aNMVNAFtgHJCsn'.
+ 'XGy/58QVxAC9MccJtZ5kIzNlW440WrJ2ea4YPA9cAooA7i0A/gS+iqLoOpB1HOegqrYB3UBmJrAtQAJwpwPr1Ry92wVlgZsiYlW1'.
+ 'uX1gU36dymgqYxJIJJNJT1W9QqHgNwFQBGYqo94OwHZQUuPD7ACglSvc+5n5T9m/wfJJX4U9qzEAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // edit.png
+ //==========================================================
+ $this->iBuiltinIcon[1][0]= 959 ;
+ $this->iBuiltinIcon[1][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAFgAWABY9j+ZuwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDAwbIEXOA6AAAAM8SURBVHicpdRPaBxlHMbx76ZvsmOTmm1dsEqQSIIsEmGVBAQjivEQ'.
+ 'PAUJngpWsAWlBw8egpQepKwplN4ULEG9CjkEyUFKlSJrWTG0IU51pCsdYW2ncUPjdtp9Z+f3vuNhu8nKbmhaf5cZeGc+PO8zf1Lc'.
+ 'm0KhkACICCKCMeaBjiLC0tLSnjNvPmuOHRpH0TZTU1M8zBi9wakzn7OFTs5sw8YYACYmJrre7HkeuVyu69qPF77hlT1XmZ0eQ03O'.
+ 'wOLJTvhBx1rLz18VmJ0eY+jVd2FxDkKXnvYLHgb97OgLzE4ON9Hzc1B1QaQzsed5O0Lta3Ec89OnR5h5McfQ+Mw2qgQUnfBOPbZ3'.
+ 'bK3l+xOvMT0+3ERLp5FNF6UEjcL32+DdVmGt5WLhDYYPZrbRqreFumXwql0S3w9tnDvLWD5PZigPpdOwuYpSCo3C8wU3UHxQdHbf'.
+ 'cZIkNM6dxcnlUM4k1eUFMlUPpUADbpkttFarHe6oYqeOr6yt4RzMQHYUcUsQVtGicHDwKprViuLDkkOtVnsHCHZVRVy/zcj1i5Af'.
+ 'h8AjdIts+hUcGcYPK3iBtKM3gD/uAzf/AdY2mmmVgy6X8YNNKmGIvyloPcB8SUin07RQ4EZHFdsdG0wkJEnEaHAJxvKEpSLeaokV'.
+ 'r4zWmhUZYLlY4b1D03y5eIEWCtS7vsciAgiIxkQRabWOrlQor66y4pUphoJb1jiO4uO5o0S3q6RSqVbiOmC7VCEgAhLSaDQ48dH7'.
+ 'vD46REY0iysegSjKQciRt99ib7qXwX0O+pG4teM6YKHLB9JMq4mTmF9/+AKA4wvLZByH7OgYL7+UY2qvw/7Bfg5kHiXjJFyv3CGO'.
+ 'Y1rof+BW4t/XLiPG0DCGr79d4XzRxRnIMn98huXSTYyJ6et1UNYQhRvcinpJq86H3wGPPPM0iBDd+QffD1g4eZjLvuG7S1Wef26E'.
+ 'J7L7eSx7gAHVg7V3MSbi6m/r93baBd6qQjerAJg/9Ql/XrvG0ON1+vv7GH3qSfY5fahUnSTpwZgIEQesaVXRPbHRG/xyJSAxMYlp'.
+ 'EOm71HUINiY7mGb95l/8jZCyQmJjMDGJjUmsdCROtZ0n/P/Z8v4Fs2MTUUf7vYoAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // endconstrain.png
+ //==========================================================
+ $this->iBuiltinIcon[2][0]= 666 ;
+ $this->iBuiltinIcon[2][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ALEREILkh0+eQAAAIXSURBVHictZU9aFNRFMd/N81HX77aptJUWmp1LHRpIcWhg5sIDlUQ'.
+ 'LAXB4t7RRUpwEhy7iQ46CCIoSHcl0CFaoVARU2MFMYktadLXJNok7x2HtCExvuYFmnO4w/3gx+Gc/z1HKRTdMEdXqHbB/sgc/sic'.
+ 'nDoYAI8XwDa8o1RMLT+2hAsigtTvbIGVqhX46szUifBGswUeCPgAGB7QeLk0X4Ork+HOxo1VgSqGASjMqkn8W4r4vVtEgI/RRQEL'.
+ 'vaoGD85cl5V3nySR/S1mxWxab7f35PnntNyMJeRr9kCMqiHTy09EoeToLwggx6ymiMOD/VwcD7Oa/MHkcIiQx026WGYto5P/U+ZZ'.
+ '7gD0QwDuT5z9N3LrVPi0Xs543eQPKkRzaS54eviJIp4tMFQFMllAWN2qcRZHBnixNM8NYD162xq8u7ePSQ+GX2Pjwxc2dB2cLtB8'.
+ '7GgamCb0anBYBeChMtl8855CarclxU1gvViiUK4w2OMkNDnGeJ8bt9fH90yOnOkCwLFTwhzykhvtYzOWoBBbY//R3dbaNTYhf2RO'.
+ 'QpeuUMzv188MlwuHy0H13HnE48UzMcL0WAtUHX8OxZHoG1URiFw7rnLLCswuSPD1ulze/iWjT2PSf+dBXRFtVVGIvzqph0pQL7VE'.
+ 'avXYaXXxPwsnt0imdttCocMmZBdK7YU9D8wuNOW0nXc6QWzPsSa5naZ1beb9BbGB6dxGtMnXAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // mail.png
+ //==========================================================
+ $this->iBuiltinIcon[3][0]= 1122 ;
+ $this->iBuiltinIcon[3][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AJHAMfFvL9OU8AAAPfSURBVHictZRdaBRXFMd/987H7tbNx8aYtGCrEexDsOBDaKHFxirb'.
+ 'h0qhsiY0ykppKq1osI99C4H2WSiFFMHWUhXBrjRi0uCmtSEUGgP1QWqhWjGkoW7M1kTX3WRn5p4+TJJNGolQ6IXDnDtz+N0z/3PP'.
+ 'UWBIpdpYa23b9g09PZ2kUrOrvmUyGVKp1Ao/mUyi56YnVgWfO/P1CihAd/dJMpmaNROIRq8BkM1m0bH6TasC3j6QXgFdXI+DR6PR'.
+ 'JX/Pno8B+KLnMKqlpUU8z8MYs2RBEDzWf9J+0RcRbMdxGBsbw/fmCXwPMUEYID4iAVp8wIRmDIHMo4yHSIBSASKC+CWE0C/PF9jU'.
+ '3B6Cp+4M07C5FUtKGNvGwQJctPgIsgD2wRhEIqAMGB+UQYkHJgYYZD7P1HwVlmWhHcfhyk83KeRGUW4t6CgoG5SNUS4KBWgQDUov'.
+ '7AGlwYASBVqH0Bk49dXpCviVV3dw/tI1Bvr7kMIIlh0NYUpjlF0BAYvcxSXmEVLKceHSCJm+PnbueBHbtkNwTXUNBzo6aGpq4sSZ'.
+ 'GwT5H7BsF6Wdf1GWHQAoM0upeI9PT1yioS7B7tdaSdSuw7KsUGMAy7HYsmUztTW1nMwM0txssX1rlHjjS5jy/Uq2YkK/eJuLl6/z'.
+ 'x+1xkslW6mrixGIODx8EFSlEBC0+tmXT0NhA2763iEUjnLv4C8XpUbSbAB1mKkGJ3J83Od77HW5EszvZSqK2iljMIeJaRGNuJePF'.
+ '6mspY7BJ1DXwQnCd2fxGRq5OUCz8xt72dyhMZcn++Cu3xu9SKhdp2b4ZHWnAtTSxmIWlhcIjlksR3lNBYzlxZsb7+f7ne+xtSzOd'.
+ 'u83szH1OnThOPp/n+a0beeP1l4mvq+PU2Qyd+5PY1RuwlAqLYFaBfbTbyPSdfgaH77A//QF4f1O/vpr6RJyq+C5Kc/M8FbFxXItY'.
+ 'xOHDrvfo/fxLDnbsJBp5BowBReVWYAzabeTh5ABDw7cWoNNL3YYYNtSv57lnn6Z+Qx01VeuIuBa2DV1HD3H63BAPZu4u1WGpeLHq'.
+ 'Rh7+NcjA0O+0p4+CNwXigwnbWlQQdpuEpli+n+PIkcOc//YKuckJJFh2K2anrjFw+QZt6S6kPImIF/b+cqAJD1LihWAxC61twBTo'.
+ 'fPcQF/oGsVW5ovHQlavs2/8+uYnRVSOUgHAmmAClBIOBwKC0gPjhIRgEIX2wg7NnwpZW3d3d4vs+vu8TBMGK51rvPM9b8hdteZxd'.
+ 'LBbVR8feJDs0Rlv6GFKeXJ21rNRXESxMPR+CBUl0nN7PjtO+dye7Up/8v1I88bf/ixT/AO1/hZsqW+C6AAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // startconstrain.png
+ //==========================================================
+ $this->iBuiltinIcon[4][0]= 725 ;
+ $this->iBuiltinIcon[4][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDgAACw4BQL7hQQAAAAd0SU1FB9ALEREICJp5fBkAAAJSSURBVHic3dS9a1NRGMfx77kxtS+xqS9FG6p1ER3qVJpBQUUc3CRU'.
+ 'BwURVLB1EAuKIP0THJQiiNRJBK3iJl18AyeltRZa0bbaJMbUNmlNSm5e7s25j0NqpSSmyag/OMM9POdzDuflwn8djz8gClVRrVEV'.
+ 'ur4Bl1FTNSzLrSS6vbml0jUUwSXj8Qfk3PkLtLW2AeBIybmrgz3+gFzpucjlE4f4btuFTuWuCF5XDr3a3UPf6cM8GQvxzbsRAJdh'.
+ 'ScfxSywml5j7mVypN0eGEJ0tebIre+zxB6Tv7jPReS2hREpOvpmUXU+H5eC913JnNCSRVE60pUVbWoZjprR39Yq70bdqj4pW7PEH'.
+ '5FpvL9e79jOTTHM7ssDL6CJZ08LbvAGnrpZg2mI2Z/MlZfN8IkxuSwu4V9+WIrj7zFlOHfXzKrLIi2SGh5ECKjnNVNxkQEc55vOw'.
+ 'rb6O8JLFdHyJ+ayFElUeHvjwkfteL/V7fKTSkFvIQE4DoLI2Mz/muTkTApcBKIwaN8pwIUrKw+ajWwDknAO0d/r4zFaMuRS63sWm'.
+ 'RoOdm+vRIriUYjKexrQV+t1o0YEVwfZSVJmD/dIABJuO0LG3lRFx0GOfiAELE9OgCrfU0XnIp5FwGLEy5WEAOxlR5uN+ARhP7GN3'.
+ '5w7Gv4bQI2+xpt4jjv2nWBmIlcExE2vDAHYioszBZXw6CPE4ADoWVHmd/tuwlZR9eXYyoszBfpiNQqaAOU5+TXRN+DeeenADPT9b'.
+ 'EVgKVsutKPl0TGWGhwofoquaoKK4apsq/tH/e/kFwBMXLgAEKK4AAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // calc.png
+ //==========================================================
+ $this->iBuiltinIcon[5][0]= 589 ;
+ $this->iBuiltinIcon[5][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAA4AIwBbgMF12wAAAAlwSFlz'.
+ 'AAALEQAACxEBf2RfkQAAAAd0SU1FB9AHBxQeFsqn0wQAAAHKSURBVHicnZWff+RAGIef3U/gcOEgUAgUCgcLhYXCwsHBQeGgUDgs'.
+ 'FgMHB4VA/4Bg4XChWFgIFIqBwkJhsRAYeOGF+TQHmWSTTbKd9pU37/x45jvfTDITXEynAbdWKVQB0NazcVm0alcL4rJaRVzm+w/e'.
+ '3iwAkzbYRcnnYgI04GCvsxxSPabYaEdt2Ra6D0atcvvvDmyrMWBX1zPq2ircP/Tk98DiJtjV/fim6ziOCL6dDHZNhxQ3arIMsox4'.
+ 'vejleL2Ay9+jaw6A+4OSICG2cacGKhsGxg+CxeqAQS0Y7BYJvowq7iGMOhXHEfzpvpQkA9bLKgOgWKt+4Lo1mM9hs9m17QNsJ70P'.
+ 'Fjc/O52joogoX8MZKiBiAFxd9Z1vcj9wfSpUlDRNMcYQxzFpmnJ0FPH8nDe1MQaWSz9woQpWSZKEojDkeaWoKAyr1tlu+s48wfVx'.
+ 'u7n5i7jthmGIiEGcT+36PP+gFeJrxWLhb0UA/lb4ggGs1T0rZs0zwM/ZjNfilcIY5tutPxgOW3F6dUX464LrKILLiw+A7WErrl+2'.
+ 'rABG1EL/BilZP8DjU2uR4U+2E49P1Z8QJmNXUzl24A9GBT0IruCfi86d9x+D12RGzt+pNAAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // mag.png
+ //==========================================================
+ $this->iBuiltinIcon[6][0]= 1415 ;
+ $this->iBuiltinIcon[6][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlz'.
+ 'AAALDAAACwwBP0AiyAAAAAd0SU1FB9ALDxEWDY6Ul+UAAAUESURBVHicdZVrbFRFGIafsyyF0nalV1R6WiggaAptlzsr1OgEogmC'.
+ '0IgoBAsBgkIrBAPEhBj/AP6xRTCUFEwRI4jcgsitXMrFCJptJWvBNpXYbbXtbtttt6e7e86ec/yxadlCfZPJZDIz73zzzjfvR2VL'.
+ 'F7U+hf0HD2JduIzTFy6SlJRkPtkcDgdCCE65OxFC8NPV6wghyM7OptankJ2dzbSC5QghEEIgCSHog9PpNAF27dlN6miZuPgElB4/'.
+ 'nmY3O7ZtByA1NVUCkGWZweD1eklJScESTbqxuIjrd+/x6uIl5M19hSy7nfGOeUxf+g7VjU1sKi7C4/GYsiyz7tAJAD4/cRaA1tZW'.
+ 'AHIPnECUVGD1+/3U19ebG4uLeHf1akamjsIwoVnVCOvQEdLoVILYYmMo3PIxSBJflpSaDX5FAmju1QAYv/8k/s8+wLVxOU0jR2LZ'.
+ '8sMFAApWrCApbRRDrRZirBYSLBKaoRPQw3SFernf2sav7T0Ubt4KwL4FMwF4Vu8FoHBCKgCzDhwHwLIhZ7y5a89u4m2JhA0wTdDC'.
+ 'OrphEjJMNElCHxKDEjaobmvlfo/Krj27CQQCJsCGJW8C0KXqAMxMiosQA8hZWcTFx9OsaniDKh1qmG7VoFsL0x0K06kbeAMhWpRe'.
+ '/KpG+gwHAKUnz7Dz3BUMw6DK18nuw99wt0Nh6VdHI8RJicmETQgFg7SFwjSrGv+oKp6ghldV6dZ0ugJBlF6FmCESQ2w2AIqXLsan'.
+ 'BrFYLJTnTCBrdBqveeopWZiPFaBHUegJhegMqGgxEkHDwB/UaQ9rdIV06v0+TD2EEQjQFtAY0dsNgNvt5sialQAIIXh7wQKuVf6J'.
+ 'gTsSccPDWlQstClBGjr9eHpVWvUQncEwdYEedF8noQ4vmYmpZMTH0nTvDn25vLbrNmu7bvfnsYEbAMnhcPDgwQPzUo2LJusw/mhp'.
+ 'QwlHNO0KBAnoIfxtrcQMT2De1Mm891wyUzNlUlJSpIyMDBobGzlzr5rFM/Koq6vrP8ASGxsLwPmKcvIShjPGZiPOakE3VFB8hHwd'.
+ 'vJAxhrk5L7Ly+RQuH/sWgPdXrwFg/6HDFBUsIj09nehfbAWwPWOT9n5RYhqGwarNWxkRM5TRCfF4U1PQsDDJFk9uYhwXvzvKjm3b'.
+ 'KSsro3DJInNW5RXp7u2bAKSlpeH1esnPz6eqqgqLpmmcr3Fht9ulfaV7mZk1Bs+lM6T1djM9fhg5egDPpTNMy5TZsW07kydPYdWM'.
+ 'aXx96ixOp9O8cfUa80srmDpjOgAulytiQqZpMnvObLbt/JTtHxXj9/tRVdU0DGOAufRpevPDTeac0hJyc3NxOOawfv161lVWS6eX'.
+ 'z+9/UOCxu1VWVvaTRGv16NFfjB2bNeAQp9NpTpmSM4DcbrdL0WsGDKLRR+52uwe1yP8jb2lpYfikyY9t80n03UCWZeaXVjw1f+zs'.
+ 'Oen+/d+pqanhzp2fKSsrw+l0mi6XiyPl5ZGITdN8fAVJwjRNJEmi1qfw1kw7siyTnJxMe3s71dXV3GpoZO64DG41NPJylvxU5D/e'.
+ 'qJKsfWQD9IkaZ2RmUvr9aV4aGYcQgjfO3aWoYBF5eXm4ewIsu/CbdPz1aWb0/p1bNoOrQxlUiuiaFo3c3FyEEOx9+C9CCD6paaTW'.
+ 'p/TXyYkTJ0Xe59jf7QOyAKDWp/QXxcFQ61P4pT3ShBBcvnUHIQTjxmX19/8BCeVg+/GPpskAAAAASUVORK5CYII=' ;
+
+ //==========================================================
+ // lock.png
+ //==========================================================
+ $this->iBuiltinIcon[7][0]= 963 ;
+ $this->iBuiltinIcon[7][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALCwAACwsBbQSEtwAAAAd0SU1FB9AKAw0XDmwMOwIAAANASURBVHic7ZXfS1t3GMY/3+PprI7aisvo2YU6h6ATA8JW4rrlsF4U'.
+ 'qiAsF9mhl0N2cYTRy9G/wptAYWPD9iJtRy5asDe7cYFmyjaXOLaMImOrmkRrjL9yTmIS3120JybWQgfb3R74wuc8Lzw858vLOUpE'.
+ 'OK6pqSm2trbY39+nu7tbPHYch7m5OcLhMIA67kWj0aMQEWk6tm17rNm2LSIie3t7ksvlJJ1OSyqVkls3Z8SyLMnlcqTTaVKpFLdu'.
+ 'zmBZVj1HeY2VUti2TSQSQSml2bZdi0QirK2tMT09zerqKtlslqGhISYnJ4nHv2N+foFsNquOe9FotLlxOBwmk8lgWRbhcFgymYxY'.
+ 'liUi0mqaJoAuIi2macrdO7fFsizx3to0Te7euV1vrXtXEgqFmJmZYWVlhXK5LB4/U9kwDL784kYV0A3DYHd3m4sXRymXywKoRi8U'.
+ 'Ch01DgQCJBIJLMsiEAhIIpHw2uLz+eqtYrEYIqKZpimxWEyCwaCMjY01zYPBIJpXqVQqsby8TLVabWKA/v5+RkZGMAyDrq4ulFKH'.
+ 'HsfjcWZnZ+ns7KTRqwcnk0mKxSKFQqGJlVKtruuSTCYB6O3trW9UI/v9/iZPB/j8s2HOnX0FgHfeXpeffnzK+fWf+fijvhLs0PtG'.
+ 'D/n1OJ9+MsrlSwb3733DwMCAt1EyPj6uACYmJp56168NU6nUqFSE9nZdPE7+WqC/r4NKTagcCJVqDaUUB5VDAA4Pa9x7sMLlSwan'.
+ 'WjRmv13D7/erpaWlo604qOp88OF7LC48rPNosMq5Th+Dgxd4/XyA1rbzADi7j8jnf2P++wdcvSr8MJ/i8eomAKlUqn41OsDAQDeD'.
+ 'g++yuPCwzm/2vU8+n2a7sMFfj79mp7BBuVzioFSiXHJx3SKuW2Rzy0Up9dxnQVvODALQerqNRn4ZKe0Mvtc6TpzpmqbxalcY9Ato'.
+ '2v06t515C73YQftZB9GLnDrt4LoujuPgOA4Ui+C6yOpXJwZrJ7r/gv4P/u+D9W7fLxTz+1ScQxrZ3atRLaVxdjbY2d184R6/sLHe'.
+ 'opHP7/Do90Ua+WWUyezzZHObP/7cfX54/dowE1d66s8TV3oE+Mfn+L/zb4XmHPjRG9YjAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // stop.png
+ //==========================================================
+ $this->iBuiltinIcon[8][0]= 889 ;
+ $this->iBuiltinIcon[8][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAABYAAAAWCAYAAADEtGw7AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9AJDwEvNyD6M/0AAAL2SURBVHic1ZTLaxVnGIefb2bO5OScHJN4oWrFNqcUJYoUEgU3/Qf6'.
+ 'F7gwCkIrvdBLUtqqiLhSg9bgBduFSHZdiG5ctkJ3xRDbUFwUmghNzBDanPGMkzOX79LFJGPMOSd204U/+Bbzvd/78F4H/ieJdoad'.
+ 'pZKxRFszAI/DcP0HazXY22v+HB01kee1PA/v3zfnjx4xgGnHcNZe7OvuNj+cOEF1ZATv5nUA4jhBSgmADCVWo8Ge2Of9wb18P/G7'.
+ 'oUXmYi30zqlTVEdGWLh1g2D6MYlKkXGE0Vl8aa2GEB149+4xXSzyoOIw/mimiZV/DPb25pFOj13A9gOMEChhUEqhVYqWKUk9QAUp'.
+ 'sT/P4s8PmKlUmNhQaIJbkDVqBbpw6wZ2zUc4Nm+ePku5p4eOrgpueQOFUoVCVxcD4+N07dpF9+5tVJeWGPBjhvr7WF1zC8ASgtcP'.
+ 'H8a7eZ1odh4sh50nzwCw9ZNh3M4Stutiu0X2nB/LyjZ6lcIbVTpdQU/jWVPzLADM8+ZGBRdtC7wrF/O7bR99iu26VL86iU4SAH4b'.
+ 'Po5d6AQhstMSvGyI4wS5FJBKSRwnzF8byx/u+PjzzMF1mfryQ1K/jnCahqp1xEopjFLoNEFJSRJHzF799gWHqa+/QKcSUXBI609f'.
+ 'Al5W4teQSiHDOipNUKnMI13RvnOXAIEKQixvGWya98SC560MFwPiqEG86JM8q79Q06lvhnOndy5/B6GPCUOMUu3BQgg8z0M3GmBZ'.
+ 'iGJn3v2VmsqnfzNx7FDueODuj8ROCFpjtG5TCmOYv32bJ09msP0ISydMfnAUgF8/O45RAA6WTPjlvXcB+Gn7FuRf/zAnNX6x3ARe'.
+ 'PSdmqL+P/YHkwMGDOGWDZTlQcNBRhPEComgB/YeHfq2InF1kLlXUOkpMbio1bd7aATRD/X0M1lPeSlM2vt2X1XBZjZnpLG2tmZO6'.
+ 'LbQVOIcP+HG2UauH3xgwBqOz9Cc3l1tC24Fz+MvUDroeGNb5if9H/1dM/wLPCYMw9fryKgAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // error.png
+ //==========================================================
+ $this->iBuiltinIcon[9][0]= 541 ;
+ $this->iBuiltinIcon[9][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAMAAAC7IEhfAAAAaVBMVEX//////2Xy8mLl5V/Z2VvMzFi/v1WyslKlpU+ZmUyMjEh/'.
+ 'f0VyckJlZT9YWDxMTDjAwMDy8sLl5bnY2K/MzKW/v5yyspKlpYiYmH+MjHY/PzV/f2xycmJlZVlZWU9MTEXY2Ms/PzwyMjLFTjea'.
+ 'AAAAAXRSTlMAQObYZgAAAAFiS0dEAIgFHUgAAAAJcEhZcwAACxIAAAsSAdLdfvwAAAAHdElNRQfTCAkUMSj9wWSOAAABLUlEQVR4'.
+ '2s2U3ZKCMAxGjfzJanFAXFkUle/9H9JUKA1gKTN7Yy6YMjl+kNPK5rlZVSuxf1ZRnlZxFYAm93NnIKvR+MEHUgqBXx93wZGIUrSe'.
+ 'h+ctEgbpiMo3iQ4kioHCGxir/ZYUbr7AgPXs9bX0BCYM8vN/cPe8oQYzom3tVsSBMVHEoOJ5dm5F1RsIe9CtqGgRacCAkUvRtevT'.
+ 'e2pd6vOWF+gCuc/brcuhyARakBU9FgK5bUBWdHEH8tHpDsZnRTZQGzdLVvQ3CzyYZiTAmSIODEwzFCAdJopuvbpeZDisJ4pKEcjD'.
+ 'ijWPJhU1MjCo9dkYfiUVjQNTDKY6CVbR6A0niUSZjRwFanR0l9i/TyvGnFdqwStq5axMfDbyBksld/FUumvxS/Bd9VyJvQDWiiMx'.
+ 'iOsCHgAAAABJRU5ErkJggg==' ;
+
+ //==========================================================
+ // openfolder.png
+ //==========================================================
+ $this->iBuiltinIcon[10][0]= 2040 ;
+ $this->iBuiltinIcon[10][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABGdBTUEAALGPC/xhBQAAAAZiS0dEANAAtwClFht71AAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9AKDQ4RIXMeaLcAAAd1SURBVHicxZd7jBXVHcc/58zcvTNzH8vusqw8FsTsKiCUUh5WBZXG'.
+ 'GkOptmqwNWsWLKXFGlEpzZI0AWNKSy0WhDS22gJKtWlTsSRqzYIuLGB2WVvDIwQMZQMsy2OFfdzde+/OnHP6x907vJaFpjb9JZM5'.
+ 'c85Mfp/f9/s7Jxn4P4e41gtSyp78WGvtfdEAcqDFYUOH9HS0NhGk9tPb/ilSyp789UUB2AMuqhQy3Uzm7HGkE6W3dTNZMRI3EcWO'.
+ 'jf9ClLmWBT3dzW8jUsevWHCG3UpWl+IkHSxnbDh/Mcz12NevBcuWXTmf6TjnXvJ88gDmVB3pw3+nt3UzHa1NqMzBS2zqPLGFjtMN'.
+ 'ZNr3XdW+qyqwZcFk76HX/tHWfuQvyO4W7qhaHwL8efkMRlRUpPv7rqD0RrJ+FgAjLy1a20OIxZJEEuNCRfIApj+om4bGM3u2/sYU'.
+ '9J41d8973f3Dhg1pISTV1dXXBRNJxPGFCzhou+DCQrScZOkktNaeDZjamgeZ9MgiYmVDccvHhjAzJw0NTh8/alyZMaVJicp0iTHj'.
+ 'JpgNv38tjWUhhGROdbUL9W5/MH5XCkjlcibi+KIop5LVHLKEu8A/f4r286doa9pGrGwYAAsfqbbH3b8MgO/Nqgy6WvdbbXHMkEFJ'.
+ '4xUOMVEvaTZu3BgmvF4Yk4hz9rO/Ulr5cE9owae/rcGxohSOuiWkC2IjcIqKyPZm+OmCH7GhoZEF077EEzVVweAbJ+riEeO0Ey8y'.
+ 'UubqOHn0AOgMwvf59txnBrSp9dgxKmf/+kIP1NY8SFk0jh5ajmNHAWg5b2E5EexojGHjbiVRMoRMNs0LC+Yz46vTuH3enN7BI8fr'.
+ 'qFdo0BoVZNC9aVSQ4fNjBzEmQJiARxb+/AqYPMAVB5FsPU5v37g9OxgLhe14ZM5/ju052E6MNZvf5pmHHuLmmWOkEysxUtpGAtme'.
+ 'dtHTflJkezqQto3jFRnLssyf1jydxiiM7zNnye/c3ZsqLu2BN5fcMfzrv/hby1tPzmRUoihcTJ87CwQI2yLtDcIqsIjYUf51qBlf'.
+ 'OnScOSrdQUOMURkiXsLUzJnvbGhoBGDHH5cGyZLhOpYoNl5hqYnYEXOu5fDl9eYAHntx98n8hFHZcPHUuTSxSASAeK/CGIOxJJ0f'.
+ 'bOGNPU280dgkq6Y2yu8vfjCIlwwzr+/ZQ/PHO0gOLuO5qsftDQ2NbN+4OCgqG6WTxWVaq6zpF+DiSHWnicdylp3r6aZTWthIOrNp'.
+ 'ktHcvBu0sHX1Sm6ozB3B42d90zZA9bQp7PvgPSzXZfnqX/HS4DKKK2+x69Y/HURs26iBAN5ccsfw7774UcumF37C6f07KSt2OHji'.
+ 'DEUJD0tISjyPrrSPlAKvN0JP/U4O1NfjuhG2rvklN1SOpfXwftpbTqAyKRrff5fb7rs9V1R7m4wlz2ihA3HpmXflUWyOH2umpLiY'.
+ 'ui3v8M+6bWzfsRNbSgqkxaCkiy0simMuEWEhpcRzIhQWOIAh6tiAwS4owInFiTou5dOnMnl2NR++ujBwXEc9terD6M43nrj6LgAB'.
+ 'QnDPA9/irtkP8JRS7Hr/3T6YekDQ1pEiEXOwpUVJzCVlZZFS4mZtkpEo9ChAkDp/jtLMBACy6S4RiQghLyv5cgBRPnKUOX6smUGF'.
+ 'hSil0MYw9d77mPy1e5mnFE3batm3czvb6nYgEJztSFGU9LCRlMRdUjIH0+lnEMIwPNXD3NumoVJnrMCJaiciMUZfvQnz4QcBSvV1'.
+ 'vjE5GK358t0zmXDnDB79saLpo20c+aSRD+t25JTp7GZQwsEWFiVxl6hlUf/WO9z32CxmL1rOe6u/I2KuwGhzLQCB7/sYY9Bah3el'.
+ 'FKbvrrVm4vS7GH/7ncx+chEHGz7myCeNbPtoO0JI2jq78WIRLGkzsqs7V5SfFV5EovXACoiqqsfNpk2vo5VCWtYFBfoU0VoTBAFa'.
+ 'a7TRaK2p+MoURk+cxMzq+Rzbv49DDbuo27UTW9h0dedssPxuK+kIfN8XxhgDYPVXf2Fh4XKtFIl4AiklAlBKAYRKKK36wHIweTCt'.
+ 'NfHiEkaOn8j0+7/BmDFjaT30GbHywSxcuZkpFfFg+m1jjZ/NmnVvNfRvwd69e8WBA/uNFAIh4JVXXmHsmDHE4vEQQgjQ2lxQIm9N'.
+ 'nz35q3BEOZOHzaG2thaA4mRU+L29It+IV21CpbRQfeMFC35gRB/M2rVrubnyZmLxWJhECBEmz/eHyo/7lMlH3LFFujsthNFCCGOu'.
+ '+WNyeUgpjSVzMKtWraKyshLPdcPEeYWCIEBdpIxSivr6eta8vI7d6+cGnhdV06pe1QP+F/QXWmuRL+jZZ58LlVmxYgUVFRV4rhtu'.
+ '4TzMxXAA6XRaRAtsYUkx8I/JtSJQOlSwpmZpCLN8+fPcdNNoHMfB9/0QJgRoP295TlR7UVv8xxZcHMuWIZ9/Hn35vG3JEGZpzVJG'.
+ 'jx5N1IlitKahsZE1L69j69qHgx+urFX/lQL9JYdLlfnZihUhzOLFi8N3Ml1dthOxVH/f/8/CtqSJ2JaJ2JZ59J7RPsC/AViJsQS/'.
+ 'dBntAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // folder.png
+ //==========================================================
+ $this->iBuiltinIcon[11][0]= 1824 ;
+ $this->iBuiltinIcon[11][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALEAAACxABrSO9dQAAAAd0SU1FB9ECAQgFFyd9cRUAAAadSURBVHiczdhvbBP3Hcfx9/2xfefEOA5JoCNNnIT8AdtZmYBETJsI'.
+ '6+jQOlQihT1AYgytqzZpD1atfyYqlT1h0lRpT7aRJ4NQpRvZGELVuo5Ua9jEJDIETQsNQyPBsUJMWGPnj//e+e72wNg4xElMR6ed'.
+ 'ZNln3933dZ/f93f6yfB/sgmrHdDV1WXlPg8NDZUDScD8LFFFEZZlWYZhWMFg0Orq6sq/gDJAfFy1iiZy9OjrVnj4JzQ1rMWqfxm/'.
+ '309jYyNtbW0kEgnu3bvH4cOH88c/jqSKQl4/XGkd+eVtAN46up1LH92ktqYS++ZX8Pv9NDQ0sGnTJlKpFOFwmO7u7vy5IyMjeVRd'.
+ 'XV1+WEOh0IrY4pDnq6wXX/sTiCJaMkFZdRNqxefoe7VtCSqXVDqdZnZ2ltraWkzTpKqqijt3JpFlG7dvj7NzZ1f++qFQyA3EClHL'.
+ 'Ql743nFkhxPDtJAd5eTaYSVUfX09lZWVlJWVIUnSg7sVQMBCUcu4ceMGe/bsIRQK1QAzOcyykIM9P0KyudAyCWyqG8nhwqa4SkLt'.
+ '3r0bVVVxu924XC40TUOWZUQxe97CwgIdHR2LMHIxSCaVInVvFElxE0vMY1Pd2NUKJMWNTXHlUfF//4vETJCelwbpFm3MjP2dt37x'.
+ 'AlN+PzU1NViWRSwW4+7du3g8HjweD4qi5EFAJzAExIpCANbooxhplfB0FJvTg6xWIqsVRVF6MopkU3FXPcnkJxGU0VEAdF2noqKC'.
+ 'W3/8DpnqLjzep2lubsblcjE8PExHR8fboVDID9xYFpLBDpJF0jDQIncQpWlkm31FlFLtp9PfyuW/vYQj1kPSuRW/38+lj27S2Q7v'.
+ '/aWXUBVUffVNtm3blivVCEwsC5Eyc5iiApEpDEAXMqQdldhSiWVQHjJagud+8Fuexck/zv+K82dfoSbSCsDe75/km+4GVPd6+l5t'.
+ '4zJHcqVUYN2yEEtZQDCSJCueRAYsPY49HsFIZVG6p25JUumFafT4DKJN4amtT7Nz38sk5+5A70HMtEYyMkFiZhxzjQ/poXrLQrRU'.
+ 'DFGEeFpAlkQkm4pRiCpIKodKzk0T/2QMh+piPjxKZPwiSkUtu/b9mNnJEWS7E8nhAmvpM60oJDkXJxqNozxRRUxPIesispBBlsXV'.
+ 'UaKEFo8gzoaJhz8s2lOmrpUG+WBhJ9/60g+Z+fDXTAXfxllRjl1VkO0OFATsYhYliiK21ZKKhhHnFveUqSdKgwAEOp7F2v51vvw8'.
+ 'XH7/N1wd/BlTweuUV65BdtgfoLTSkipsdD3tRi0VYpommUwGwzDwdT5HYEc3giAwcvH3jLz3BlPB67jWeZBEKYsSBWwpHZtNKo4q'.
+ 'aHTDsJeeiGEYWJaFZVmYpommaRiGQdPnv0bb1m8gSRL/vPIOV979aR4lmAJ2p4qCgCxksNuKJ6VNpx4NYhgGpmkuQhmGQTqdxjAM'.
+ 'qr2d7HtxEEEQuH1tkKvvvkF44tqDnrIcKJKAPf1g+LAUElq8dIiu60sApmnm93Pfzc7OYhgGrie+wFe++ztcLhcT1wf54PzPCU9c'.
+ 'w7XWjWS3IdsdOAUBWZAxrRJnTQ6SG5bce2FCpmkughmGQSqVYm5uDtnj44sH38TtdhP6+Dwf//V4ttHXrkGURZJaic8RgHQ6jWma'.
+ 'SJKUL5RLKNfIOczDKF3XSSaTRCIRhLJWntp3nGfWrSMxc5OLf3iNP4+68T9Ub9nF76lTpxgfHycajZJKpdA0LZ9GbjYV7hcDWZaF'.
+ 'pmnMz88Ti8UYunSLmu1HFi2aVkxkaGjINTY2ttDb24vX6+XQoUNs3ryZ8vJyIDu1BUFYkkxhgxeiWlpaOHPmDE1NTdTX1xe98eWG'.
+ 'JnF/9dQZCoXUYDA4AOD1ejlw4ACtra2Ul5fniwmCkEcUJiUIAoFAgL6+Pnw+H21tbfT39z8SxCS7hHsfWH9/8dL4MKqnp4eWlhac'.
+ 'TmcekEvMNE2am5s5ceIEgUCA9vZ2Tp48ic/nY3j4UsmQHCYOjJHtpeBKqL1799Lc3IzT6UTXdRobGxkYGKC9vZ3W1tZ8Ko86NJ8a'.
+ 'tXHjRo4dO8bp06fZsmULGzZsoL+/n0AggNfr5ezZs/8VpGTU5OSkc//+/acBfD4f1dXV7Nq1i4aGBs6dO4fP5+Pq1SuPBbIiyjTN'.
+ 'RUnV1dUNXLhwAa/Xy44dO4jFYgBEo9FFF1r134BPuYlk16LrAYXsAlmtq6sbKDwoFAp9m+ykuP5ZQVZF3f8tCdwCov8LyHIoAANI'.
+ 'AXf/A1TI0XCDh7OWAAAAAElFTkSuQmCC' ;
+
+ //==========================================================
+ // file_important.png
+ //==========================================================
+ $this->iBuiltinIcon[12][0]= 1785 ;
+ $this->iBuiltinIcon[12][1]=
+ 'iVBORw0KGgoAAAANSUhEUgAAACIAAAAiCAYAAAA6RwvCAAAABGdBTUEAALGPC/xhBQAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlz'.
+ 'AAALDwAACw8BkvkDpQAAAAd0SU1FB9ECDAcjDeD3lKsAAAZ2SURBVHicrZhPaFzHHcc/897s7lutJCsr2VHsOHWMk0MPbsBUrcnF'.
+ 'OFRdSo6FNhdB6SGHlpDmYtJCDyoxyKe6EBxKQkt7KKL0T6ABo0NbciqigtC6PhWKI2NFqqxdSd7V2/dmftPDvPd212t55dCBYfbN'.
+ 'zpvfZ77z+/1mdhUjytWrV93Hf/24eD5z9gwiMlDjOKbb7dLtdhER2u02u7u73Lp1CxEZBw4AeZwdNQqkMd9wbziFGINJUt6rRbz5'.
+ '1ptUq1XK5TJBEAAUMHt7e+zu7gKwvLzMysoKwAng/uNg9CgQgFKlgg1DUJ67Vqtx6tQpZmdniaIIpRTOOZRSdDoddnZ2aLfbLC8v'.
+ 's7S0xJUrV7ZGwQSj1PhhfRodVdDlMrpc5vup5Z2fvMPdu3fZ29vDWjvwztjYGPV6nVqtRqVS4dKlSywtLQFsAdOH2XwsCEApg3jl'.
+ 'w98Rak2gvYjNZpNms0mSJDjnHgkDMDc3dySYQ0Ea8w139YUX0OUKulzyg7UmCEO+l1huvHuDra0t9vf3h1TJYSqVypFhHquIrlQI'.
+ 'S5qv/uIDAC7/4bcEQYAKvK+0Wq1DVQGIoog7d+4cCeaRII35hrt+8SsEOkRlUaEyR0UpFIrXHxyMVKVUKnHv3r0jwRwaNelBjBjL'.
+ 'Sz/7KYuLiwAsLi7y4z/9kY9e+TpkCuSqjI+Po7XuAWeKXLt2DWNMUZMkwRjDhQsXWFtbK6JpCCT3jfQgxomPtPX19YHWicM5x3c2'.
+ '73Pj3Ru8/aO3mZqaolKpoHVvyuvXr/Ppnf/Q7uzz380NPtu4y/qnG+ztd1hfX2dtbQ3gIvDnRyqSxl1UoPjyz98D4PTp0wPtq39Z'.
+ '4fdzLxegrVaLVqvF5OQkYRgWqpRKJZ77wvNsbW1RG5tgfKLOTH2G7Z1twqBQrgrMDvhInjfSOCY5iIv+hYWFgRZArEWsZWF941Bf'.
+ 'SdMUgMnJCWpjVU4cn+HUyePM1Gc4+fRUPkzBI5w1jbukcczLv/5l0XfmzJmBFuCba38r/CRXpT+CrDUoZ0jjB4RYonJAOYRobJKT'.
+ 'z5zgqfqxAbsFSH6mpHFM2qdGXh4VnoViD6mSJF2cTQeqDqBaKVHWmonJCWpZjhkC6anR5WsffTgwaHV1FaUUq6urA/2v3f5k4LnV'.
+ 'arG9tUn3oI2YBCcWHYAxMVYs1qZEZY2SFB2aYZDGfMN9d7uJiWPSeFiNo5Rclc3NTXZbO6RpF7EJVixYA9agwwDnUiqlEPdQ3imi'.
+ 'Jo27BGHIt/7x9yEjc3Nzh27Na7c/4TdffKl4bja3ae5MUIu0T/HOEIaOpJt4gwoSsVTK4SBIY77hFtY3ABBjBiZ90rKwvsH77/+K'.
+ 't37wOhO1iPpTk4SBw1mLsz6CnKQ4l3qV+kE+t9XHlNZOk+bUJLVIE1VCcIJWQmJ6qjj30NbcXLkZMt8YPig+Z3n1G5fZ39/j/vY2'.
+ '9ckqZT2Ochbn0p4qNkU/dDfUADdXbh4HXgRO4zNdEU0XL1784PLly5w9e7Z4SazFOfGrEotDcOKrcoJPmrYIXf/Zop3QNd1skuGt'.
+ 'cUAb2MgAxvHZTgFUq1Wmp6eZnZ0F8JlTjDduDThBnDeECEoJtbGIp6enqEblzCcEZ1PECU4yVRiOGgd0gc+AB0CZvkv1sWPHOHfu'.
+ 'HOfPn8da41cpkkltEBEPJhYnBkTQJcdYVKGkgRxCfBsq5xXNgAa2Bn+hjTOgHEKBP8pzRUxykIH4ifLJRTJAl+UMBJzPHQ6bfe/f'.
+ 'cWIzPxlUpD+zugzIZtVk1d8znBAqRxgoQuVQgSJQ3h9C5QhDRYgjUILCAzlnEdsHYTKfMTEBcP7F54YUGVmc2GLlIn6ve6v0ahSt'.
+ '8X25TzjJ+rIx1grKpQPWR4LkGVVsMgghvS0qjPdvm5OeceOTWA5Evo2mFzkjQfL7hZPUy5yvvF/uPFQL3+nbDmsLCEmT3sTmCTNr'.
+ 'rogT6yFsOix3ftw7OwQhkvSU6CuinhCk0+kAkFoBazEEICHaHHiPVmU0gnUp4EAc1mYrF0EBVpwPi34VrBkwPxKk3W5ju/e5/c+d'.
+ 'bGUHIAIuydTIE5zfc5Wr4lJcahHnHTP3CVGm78DrgY38N+DEibp7dmYKdAQmBh1hjEFjis+9CTWYGK21H6PxPyOI0DobYwzZF/z7'.
+ '7jadTvJtYG0kCD7lfwl49ijgT1gc0AH+dZSJA/xB+Mz/GSIvFoj/B7H1mAd8CO/zAAAAAElFTkSuQmCC' ;
+
+ $this->iLen = count($this->iBuiltinIcon);
+ }
+}
+
+//===================================================
+// Global cache for builtin images
+//===================================================
+$_gPredefIcons = new PredefIcons();
+
+//===================================================
+// CLASS IconImage
+// Description: Holds properties for an icon image
+//===================================================
+class IconImage {
+ var $iGDImage=null;
+ var $iWidth,$iHeight;
+ var $ixalign='left',$iyalign='center';
+ var $iScale=1.0;
+
+ function IconImage($aIcon,$aScale=1) {
+ GLOBAL $_gPredefIcons ;
+ if( is_string($aIcon) ) {
+ $this->iGDImage = Graph::LoadBkgImage('',$aIcon);
+ }
+ elseif( is_integer($aIcon) ) {
+ // Builtin image
+ $this->iGDImage = $_gPredefIcons->GetImg($aIcon);
+ }
+ else {
+ JpGraphError::Raise('Argument to IconImage must be string or integer');
+ }
+ $this->iScale = $aScale;
+ $this->iWidth = Image::GetWidth($this->iGDImage);
+ $this->iHeight = Image::GetHeight($this->iGDImage);
+ }
+
+ function GetWidth() {
+ return round($this->iScale*$this->iWidth);
+ }
+
+ function GetHeight() {
+ return round($this->iScale*$this->iHeight);
+ }
+
+ function SetAlign($aX='left',$aY='center') {
+
+ $this->ixalign = $aX;
+ $this->iyalign = $aY;
+
+ }
+
+ function Stroke($aImg,$x,$y) {
+
+ if( $this->ixalign == 'right' ) {
+ $x -= $this->iWidth;
+ }
+ elseif( $this->ixalign == 'center' ) {
+ $x -= round($this->iWidth/2*$this->iScale);
+ }
+
+ if( $this->iyalign == 'bottom' ) {
+ $y -= $this->iHeight;
+ }
+ elseif( $this->iyalign == 'center' ) {
+ $y -= round($this->iHeight/2*$this->iScale);
+ }
+
+ $aImg->Copy($this->iGDImage,
+ $x,$y,0,0,
+ round($this->iWidth*$this->iScale),round($this->iHeight*$this->iScale),
+ $this->iWidth,$this->iHeight);
+ }
+}
+
+
+//===================================================
+// CLASS TextProperty
+// Description: Holds properties for a text
+//===================================================
+class TextProperty {
+ var $iFFamily=FF_FONT1,$iFStyle=FS_NORMAL,$iFSize=10;
+ var $iColor="black";
+ var $iShow=true;
+ var $iText="";
+ var $iHAlign="left",$iVAlign="bottom";
+ var $csimtarget='',$csimalt='';
+
+//---------------
+// CONSTRUCTOR
+ function TextProperty($aTxt='') {
+ $this->iText = $aTxt;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Set($aTxt) {
+ $this->iText = $aTxt;
+ }
+
+ function SetCSIMTarget($aTarget,$aAltText='') {
+ if( is_string($aTarget) )
+ $aTarget = array($aTarget);
+ $this->csimtarget=$aTarget;
+ if( is_string($aAltText) )
+ $aAltText = array($aAltText);
+ $this->csimalt=$aAltText;
+ }
+
+ function SetCSIMAlt($aAltText) {
+ if( is_string($aAltText) )
+ $aAltText = array($aAltText);
+ $this->csimalt=$aAltText;
+ }
+
+ // Set text color
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function HasTabs() {
+ if( is_string($this->iText) ) {
+ return substr_count($this->iText,"\t") > 0;
+ }
+ elseif( is_array($this->iText) ) {
+ return false;
+ }
+ }
+
+ // Get number of tabs in string
+ function GetNbrTabs() {
+ if( is_string($this->iText) ) {
+ return substr_count($this->iText,"\t") ;
+ }
+ else{
+ return 0;
+ }
+ }
+
+ // Set alignment
+ function Align($aHAlign,$aVAlign="bottom") {
+ $this->iHAlign=$aHAlign;
+ $this->iVAlign=$aVAlign;
+ }
+
+ // Synonym
+ function SetAlign($aHAlign,$aVAlign="bottom") {
+ $this->iHAlign=$aHAlign;
+ $this->iVAlign=$aVAlign;
+ }
+
+ // Specify font
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function IsColumns() {
+ return is_array($this->iText) ;
+ }
+
+ // Get width of text. If text contains several columns separated by
+ // tabs then return both the total width as well as an array with a
+ // width for each column.
+ function GetWidth($aImg,$aUseTabs=false,$aTabExtraMargin=1.1) {
+ $errmsg = 'Unknown type in Gantt object title specification';
+ $extra_margin=4;
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ if( is_string($this->iText) ) {
+ if( strlen($this->iText) == 0 ) return 0;
+ $tmp = split("\t",$this->iText);
+ if( count($tmp) <= 1 || !$aUseTabs ) {
+ return $aImg->GetTextWidth($this->iText)+2*$extra_margin;
+ }
+ else {
+ $tot=0;
+ for($i=0; $i < count($tmp); ++$i) {
+ $res[$i] = $aImg->GetTextWidth($tmp[$i]);
+ $tot += $res[$i]*$aTabExtraMargin;
+ }
+ return array(round($tot),$res);
+ }
+ }
+ elseif( is_object($this->iText) ) {
+ // A single icon
+ return $this->iText->GetWidth()+2*$extra_margin;
+ }
+ elseif( is_array($this->iText) ) {
+ // Must be an array of texts. In this case we return the sum of the
+ // length + a fixed margin of 4 pixels on each text string
+ $n = count($this->iText);
+ for( $i=0, $w=0; $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_string($tmp) ) {
+ $w += $aImg->GetTextWidth($tmp)+$extra_margin;
+ }
+ else {
+ if( is_object($tmp) === false ) {
+ JpGraphError::Raise($errmsg);
+ }
+ $w += $tmp->GetWidth()+$extra_margin;
+ }
+ }
+ return $w;
+ }
+ else {
+ JpGraphError::Raise($errmsg);
+ }
+ }
+
+ // for the case where we have multiple columns this function returns the width of each
+ // column individually. If there is no columns just return the width of the single
+ // column as an array of one
+ function GetColWidth($aImg,$aMargin=0) {
+ $errmsg = 'Unknown type in Gantt object title specification';
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ if( is_array($this->iText) ) {
+ $n = count($this->iText);
+ for( $i=0, $w=array(); $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_string($tmp) ) {
+ $w[$i] = $aImg->GetTextWidth($this->iText[$i])+$aMargin;
+ }
+ else {
+ if( is_object($tmp) === false ) {
+ JpGraphError::Raise($errmsg);
+ }
+ $w[$i] = $tmp->GetWidth()+$aMargin;
+ }
+ }
+ return $w;
+ }
+ else {
+ return array($this->GetWidth($aImg));
+ }
+ }
+
+ // Get total height of text
+ function GetHeight($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontHeight();
+ }
+
+ // Unhide/hide the text
+ function Show($aShow=true) {
+ $this->iShow=$aShow;
+ }
+
+ // Stroke text at (x,y) coordinates. If the text contains tabs then the
+ // x parameter should be an array of positions to be used for each successive
+ // tab mark. If no array is supplied then the tabs will be ignored.
+ function Stroke($aImg,$aX,$aY) {
+ if( $this->iShow ) {
+ $aImg->SetColor($this->iColor);
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ $aImg->SetTextAlign($this->iHAlign,$this->iVAlign);
+ if( $this->GetNbrTabs() <= 1 ) {
+ if( is_string($this->iText) ) {
+ // Get rid of any "\t" characters and stroke string
+ if( is_array($aX) ) $aX=$aX[0];
+ if( is_array($aY) ) $aY=$aY[0];
+ $aImg->StrokeText($aX,$aY,str_replace("\t"," ",$this->iText));
+ }
+ else {
+ $n = count($this->iText);
+ $ax = is_array($aX) ;
+ $ay = is_array($aY) ;
+ if( $ax && $ay ) {
+ // Nothing; both are already arrays
+ }
+ elseif( $ax ) {
+ $aY = array_fill(0,$n,$aY);
+ }
+ elseif( $ay ) {
+ $aX = array_fill(0,$n,$aX);
+ }
+ else {
+ $aX = array_fill(0,$n,$aX);
+ $aY = array_fill(0,$n,$aY);
+ }
+ $n = min($n, count($aX) ) ;
+ $n = min($n, count($aY) ) ;
+ for($i=0; $i < $n; ++$i ) {
+ $tmp = $this->iText[$i];
+ if( is_object($tmp) ) {
+ $tmp->Stroke($aImg,$aX[$i],$aY[$i]);
+ }
+ else
+ $aImg->StrokeText($aX[$i],$aY[$i],str_replace("\t"," ",$tmp));
+ }
+ }
+ }
+ else {
+ $tmp = split("\t",$this->iText);
+ $n = min(count($tmp),count($aX));
+ for($i=0; $i < $n; ++$i) {
+ $aImg->StrokeText($aX[$i],$aY,$tmp[$i]);
+ }
+ }
+ }
+ }
+}
+
+//===================================================
+// CLASS HeaderProperty
+// Description: Data encapsulating class to hold property
+// for each type of the scale headers
+//===================================================
+class HeaderProperty {
+ var $iTitleVertMargin=3,$iFFamily=FF_FONT0,$iFStyle=FS_NORMAL,$iFSize=8;
+ var $iFrameColor="black",$iFrameWeight=1;
+ var $iShowLabels=true,$iShowGrid=true;
+ var $iBackgroundColor="white";
+ var $iWeekendBackgroundColor="lightgray",$iSundayTextColor="red"; // these are only used with day scale
+ var $iTextColor="black";
+ var $iLabelFormStr="%d";
+ var $grid,$iStyle=0;
+ var $iIntervall = 1;
+
+//---------------
+// CONSTRUCTOR
+ function HeaderProperty() {
+ $this->grid = new LineProperty();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Show($aShow=true) {
+ $this->iShowLabels = $aShow;
+ }
+
+ function SetIntervall($aInt) {
+ $this->iIntervall = $aInt;
+ }
+
+ function GetIntervall() {
+ return $this->iIntervall ;
+ }
+
+ function SetFont($aFFamily,$aFStyle=FS_NORMAL,$aFSize=10) {
+ $this->iFFamily = $aFFamily;
+ $this->iFStyle = $aFStyle;
+ $this->iFSize = $aFSize;
+ }
+
+ function SetFontColor($aColor) {
+ $this->iTextColor = $aColor;
+ }
+
+ function GetFontHeight($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontHeight();
+ }
+
+ function GetFontWidth($aImg) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetFontWidth();
+ }
+
+ function GetStrWidth($aImg,$aStr) {
+ $aImg->SetFont($this->iFFamily,$this->iFStyle,$this->iFSize);
+ return $aImg->GetTextWidth($aStr);
+ }
+
+ function SetStyle($aStyle) {
+ $this->iStyle = $aStyle;
+ }
+
+ function SetBackgroundColor($aColor) {
+ $this->iBackgroundColor=$aColor;
+ }
+
+ function SetFrameWeight($aWeight) {
+ $this->iFrameWeight=$aWeight;
+ }
+
+ function SetFrameColor($aColor) {
+ $this->iFrameColor=$aColor;
+ }
+
+ // Only used by day scale
+ function SetWeekendColor($aColor) {
+ $this->iWeekendBackgroundColor=$aColor;
+ }
+
+ // Only used by day scale
+ function SetSundayFontColor($aColor) {
+ $this->iSundayTextColor=$aColor;
+ }
+
+ function SetTitleVertMargin($aMargin) {
+ $this->iTitleVertMargin=$aMargin;
+ }
+
+ function SetLabelFormatString($aStr) {
+ $this->iLabelFormStr=$aStr;
+ }
+
+ function SetFormatString($aStr) {
+ $this->SetLabelFormatString($aStr);
+ }
+
+
+}
+
+//===================================================
+// CLASS GanttScale
+// Description: Responsible for calculating and showing
+// the scale in a gantt chart. This includes providing methods for
+// converting dates to position in the chart as well as stroking the
+// date headers (days, week, etc).
+//===================================================
+class GanttScale {
+ var $minute,$hour,$day,$week,$month,$year;
+ var $divider,$dividerh,$tableTitle;
+ var $iStartDate=-1,$iEndDate=-1;
+ // Number of gantt bar position (n.b not necessariliy the same as the number of bars)
+ // we could have on bar in position 1, and one bar in position 5 then there are two
+ // bars but the number of bar positions is 5
+ var $iVertLines=-1;
+ // The width of the labels (defaults to the widest of all labels)
+ var $iLabelWidth;
+ // Out image to stroke the scale to
+ var $iImg;
+ var $iTableHeaderBackgroundColor="white",$iTableHeaderFrameColor="black";
+ var $iTableHeaderFrameWeight=1;
+ var $iAvailableHeight=-1,$iVertSpacing=-1,$iVertHeaderSize=-1;
+ var $iDateLocale;
+ var $iVertLayout=GANTT_EVEN;
+ var $iTopPlotMargin=10,$iBottomPlotMargin=15;
+ var $iUsePlotWeekendBackground=true;
+ var $iWeekStart = 1; // Default to have weekends start on Monday
+ var $actinfo;
+
+//---------------
+// CONSTRUCTOR
+ function GanttScale(&$aImg) {
+ $this->iImg = &$aImg;
+ $this->iDateLocale = new DateLocale();
+
+ $this->minute = new HeaderProperty();
+ $this->minute->SetIntervall(15);
+ $this->minute->SetLabelFormatString('i');
+ $this->minute->SetFont(FF_FONT0);
+ $this->minute->grid->SetColor("gray");
+
+ $this->hour = new HeaderProperty();
+ $this->hour->SetFont(FF_FONT0);
+ $this->hour->SetIntervall(6);
+ $this->hour->SetStyle(HOURSTYLE_HM24);
+ $this->hour->SetLabelFormatString('H:i');
+ $this->hour->grid->SetColor("gray");
+
+ $this->day = new HeaderProperty();
+ $this->day->grid->SetColor("gray");
+ $this->day->SetLabelFormatString('l');
+
+ $this->week = new HeaderProperty();
+ $this->week->SetLabelFormatString("w%d");
+ $this->week->SetFont(FF_FONT1);
+
+ $this->month = new HeaderProperty();
+ $this->month->SetFont(FF_FONT1,FS_BOLD);
+
+ $this->year = new HeaderProperty();
+ $this->year->SetFont(FF_FONT1,FS_BOLD);
+
+ $this->divider=new LineProperty();
+ $this->dividerh=new LineProperty();
+ $this->dividerh->SetWeight(2);
+ $this->divider->SetWeight(6);
+ $this->divider->SetColor('gray');
+ $this->divider->SetStyle('fancy');
+
+ $this->tableTitle=new TextProperty();
+ $this->tableTitle->Show(false);
+ $this->actinfo = new GanttActivityInfo();
+ }
+
+//---------------
+// PUBLIC METHODS
+ // Specify what headers should be visible
+ function ShowHeaders($aFlg) {
+ $this->day->Show($aFlg & GANTT_HDAY);
+ $this->week->Show($aFlg & GANTT_HWEEK);
+ $this->month->Show($aFlg & GANTT_HMONTH);
+ $this->year->Show($aFlg & GANTT_HYEAR);
+ $this->hour->Show($aFlg & GANTT_HHOUR);
+ $this->minute->Show($aFlg & GANTT_HMIN);
+
+ // Make some default settings of gridlines whihc makes sense
+ if( $aFlg & GANTT_HWEEK ) {
+ $this->month->grid->Show(false);
+ $this->year->grid->Show(false);
+ }
+ if( $aFlg & GANTT_HHOUR ) {
+ $this->day->grid->SetColor("black");
+ }
+ }
+
+ // Should the weekend background stretch all the way down in the plotarea
+ function UseWeekendBackground($aShow) {
+ $this->iUsePlotWeekendBackground = $aShow;
+ }
+
+ // Have a range been specified?
+ function IsRangeSet() {
+ return $this->iStartDate!=-1 && $this->iEndDate!=-1;
+ }
+
+ // Should the layout be from top or even?
+ function SetVertLayout($aLayout) {
+ $this->iVertLayout = $aLayout;
+ }
+
+ // Which locale should be used?
+ function SetDateLocale($aLocale) {
+ $this->iDateLocale->Set($aLocale);
+ }
+
+ // Number of days we are showing
+ function GetNumberOfDays() {
+ return round(($this->iEndDate-$this->iStartDate)/SECPERDAY);
+ }
+
+ // The width of the actual plot area
+ function GetPlotWidth() {
+ $img=$this->iImg;
+ return $img->width - $img->left_margin - $img->right_margin;
+ }
+
+ // Specify the width of the titles(labels) for the activities
+ // (This is by default set to the minimum width enought for the
+ // widest title)
+ function SetLabelWidth($aLabelWidth) {
+ $this->iLabelWidth=$aLabelWidth;
+ }
+
+ // Which day should the week start?
+ // 0==Sun, 1==Monday, 2==Tuesday etc
+ function SetWeekStart($aStartDay) {
+ $this->iWeekStart = $aStartDay % 7;
+
+ //Recalculate the startday since this will change the week start
+ $this->SetRange($this->iStartDate,$this->iEndDate);
+ }
+
+ // Do we show min scale?
+ function IsDisplayMinute() {
+ return $this->minute->iShowLabels;
+ }
+
+ // Do we show day scale?
+ function IsDisplayHour() {
+ return $this->hour->iShowLabels;
+ }
+
+
+ // Do we show day scale?
+ function IsDisplayDay() {
+ return $this->day->iShowLabels;
+ }
+
+ // Do we show week scale?
+ function IsDisplayWeek() {
+ return $this->week->iShowLabels;
+ }
+
+ // Do we show month scale?
+ function IsDisplayMonth() {
+ return $this->month->iShowLabels;
+ }
+
+ // Do we show year scale?
+ function IsDisplayYear() {
+ return $this->year->iShowLabels;
+ }
+
+ // Specify spacing (in percent of bar height) between activity bars
+ function SetVertSpacing($aSpacing) {
+ $this->iVertSpacing = $aSpacing;
+ }
+
+ // Specify scale min and max date either as timestamp or as date strings
+ // Always round to the nearest week boundary
+ function SetRange($aMin,$aMax) {
+ $this->iStartDate = $this->NormalizeDate($aMin);
+ $this->iEndDate = $this->NormalizeDate($aMax);
+ }
+
+
+ // Adjust the start and end date so they fit to beginning/ending
+ // of the week taking the specified week start day into account.
+ function AdjustStartEndDay() {
+
+ if( !($this->IsDisplayYear() ||$this->IsDisplayMonth() || $this->IsDisplayWeek()) ) {
+ // Don't adjust
+ return;
+ }
+
+ // Get day in week for start and ending date (Sun==0)
+ $ds=strftime("%w",$this->iStartDate);
+ $de=strftime("%w",$this->iEndDate);
+
+ // We want to start on iWeekStart day. But first we subtract a week
+ // if the startdate is "behind" the day the week start at.
+ // This way we ensure that the given start date is always included
+ // in the range. If we don't do this the nearest correct weekday in the week
+ // to start at might be later than the start date.
+ if( $ds < $this->iWeekStart )
+ $d = strtotime('-7 day',$this->iStartDate);
+ else
+ $d = $this->iStartDate;
+ $adjdate = strtotime(($this->iWeekStart-$ds).' day',$d /*$this->iStartDate*/ );
+ $this->iStartDate = $adjdate;
+
+ // We want to end on the last day of the week
+ $preferredEndDay = ($this->iWeekStart+6)%7;
+ if( $preferredEndDay != $de ) {
+ // Solve equivalence eq: $de + x ~ $preferredDay (mod 7)
+ $adj = (7+($preferredEndDay - $de)) % 7;
+ $adjdate = strtotime("+$adj day",$this->iEndDate);
+ $this->iEndDate = $adjdate;
+ }
+ }
+
+ // Specify background for the table title area (upper left corner of the table)
+ function SetTableTitleBackground($aColor) {
+ $this->iTableHeaderBackgroundColor = $aColor;
+ }
+
+///////////////////////////////////////
+// PRIVATE Methods
+
+ // Determine the height of all the scale headers combined
+ function GetHeaderHeight() {
+ $img=$this->iImg;
+ $height=1;
+ if( $this->minute->iShowLabels ) {
+ $height += $this->minute->GetFontHeight($img);
+ $height += $this->minute->iTitleVertMargin;
+ }
+ if( $this->hour->iShowLabels ) {
+ $height += $this->hour->GetFontHeight($img);
+ $height += $this->hour->iTitleVertMargin;
+ }
+ if( $this->day->iShowLabels ) {
+ $height += $this->day->GetFontHeight($img);
+ $height += $this->day->iTitleVertMargin;
+ }
+ if( $this->week->iShowLabels ) {
+ $height += $this->week->GetFontHeight($img);
+ $height += $this->week->iTitleVertMargin;
+ }
+ if( $this->month->iShowLabels ) {
+ $height += $this->month->GetFontHeight($img);
+ $height += $this->month->iTitleVertMargin;
+ }
+ if( $this->year->iShowLabels ) {
+ $height += $this->year->GetFontHeight($img);
+ $height += $this->year->iTitleVertMargin;
+ }
+ return $height;
+ }
+
+ // Get width (in pixels) for a single day
+ function GetDayWidth() {
+ return ($this->GetPlotWidth()-$this->iLabelWidth+1)/$this->GetNumberOfDays();
+ }
+
+ // Get width (in pixels) for a single hour
+ function GetHourWidth() {
+ return $this->GetDayWidth() / 24 ;
+ }
+
+ function GetMinuteWidth() {
+ return $this->GetHourWidth() / 60 ;
+ }
+
+ // Nuber of days in a year
+ function GetNumDaysInYear($aYear) {
+ if( $this->IsLeap($aYear) )
+ return 366;
+ else
+ return 365;
+ }
+
+ // Get week number
+ function GetWeekNbr($aDate) {
+ // We can't use the internal strftime() since it gets the weeknumber
+ // wrong since it doesn't follow ISO on all systems since this is
+ // system linrary dependent.
+ // Even worse is that this works differently if we are on a Windows
+ // or UNIX box (it even differs between UNIX boxes how strftime()
+ // is natively implemented)
+ //
+ // Credit to Nicolas Hoizey <nhoizey@phpheaven.net> for this elegant
+ // version of Week Nbr calculation.
+
+ $day = $this->NormalizeDate($aDate);
+
+ /*-------------------------------------------------------------------------
+ According to ISO-8601 :
+ "Week 01 of a year is per definition the first week that has the Thursday in this year,
+ which is equivalent to the week that contains the fourth day of January.
+ In other words, the first week of a new year is the week that has the majority of its
+ days in the new year."
+
+ Be carefull, with PHP, -3 % 7 = -3, instead of 4 !!!
+
+ day of year = date("z", $day) + 1
+ offset to thursday = 3 - (date("w", $day) + 6) % 7
+ first thursday of year = 1 + (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $day)))) % 7
+ week number = (thursday's day of year - first thursday's day of year) / 7 + 1
+ ---------------------------------------------------------------------------*/
+
+ $thursday = $day + 60 * 60 * 24 * (3 - (date("w", $day) + 6) % 7); // take week's thursday
+ $week = 1 + (date("z", $thursday) - (11 - date("w", mktime(0, 0, 0, 1, 1, date("Y", $thursday)))) % 7) / 7;
+
+ return $week;
+ }
+
+ // Is year a leap year?
+ function IsLeap($aYear) {
+ // Is the year a leap year?
+ //$year = 0+date("Y",$aDate);
+ if( $aYear % 4 == 0)
+ if( !($aYear % 100 == 0) || ($aYear % 400 == 0) )
+ return true;
+ return false;
+ }
+
+ // Get current year
+ function GetYear($aDate) {
+ return 0+Date("Y",$aDate);
+ }
+
+ // Return number of days in a year
+ function GetNumDaysInMonth($aMonth,$aYear) {
+ $days=array(31,28,31,30,31,30,31,31,30,31,30,31);
+ $daysl=array(31,29,31,30,31,30,31,31,30,31,30,31);
+ if( $this->IsLeap($aYear))
+ return $daysl[$aMonth];
+ else
+ return $days[$aMonth];
+ }
+
+ // Get day in month
+ function GetMonthDayNbr($aDate) {
+ return 0+strftime("%d",$aDate);
+ }
+
+ // Get day in year
+ function GetYearDayNbr($aDate) {
+ return 0+strftime("%j",$aDate);
+ }
+
+ // Get month number
+ function GetMonthNbr($aDate) {
+ return 0+strftime("%m",$aDate);
+ }
+
+ // Translate a date to screen coordinates (horizontal scale)
+ function TranslateDate($aDate) {
+ //
+ // In order to handle the problem with Daylight savings time
+ // the scale written with equal number of seconds per day beginning
+ // with the start date. This means that we "cement" the state of
+ // DST as it is in the start date. If later the scale includes the
+ // switchover date (depends on the locale) we need to adjust back
+ // if the date we try to translate has a different DST status since
+ // we would otherwise be off by one hour.
+ $aDate = $this->NormalizeDate($aDate);
+ $tmp = localtime($aDate);
+ $cloc = $tmp[8];
+ $tmp = localtime($this->iStartDate);
+ $sloc = $tmp[8];
+ $offset = 0;
+ if( $sloc != $cloc) {
+ if( $sloc )
+ $offset = 3600;
+ else
+ $offset = -3600;
+ }
+ $img=$this->iImg;
+ return ($aDate-$this->iStartDate-$offset)/SECPERDAY*$this->GetDayWidth()+$img->left_margin+$this->iLabelWidth;;
+ }
+
+ // Get screen coordinatesz for the vertical position for a bar
+ function TranslateVertPos($aPos) {
+ $img=$this->iImg;
+ $ph=$this->iAvailableHeight;
+ if( $aPos > $this->iVertLines )
+ JpGraphError::Raise("Illegal vertical position $aPos");
+ if( $this->iVertLayout == GANTT_EVEN ) {
+ // Position the top bar at 1 vert spacing from the scale
+ return round($img->top_margin + $this->iVertHeaderSize + ($aPos+1)*$this->iVertSpacing);
+ }
+ else {
+ // position the top bar at 1/2 a vert spacing from the scale
+ return round($img->top_margin + $this->iVertHeaderSize + $this->iTopPlotMargin + ($aPos+1)*$this->iVertSpacing);
+ }
+ }
+
+ // What is the vertical spacing?
+ function GetVertSpacing() {
+ return $this->iVertSpacing;
+ }
+
+ // Convert a date to timestamp
+ function NormalizeDate($aDate) {
+ if( is_string($aDate) )
+ return strtotime($aDate);
+ elseif( is_int($aDate) || is_float($aDate) )
+ return $aDate;
+ else
+ JpGraphError::Raise("Unknown date format in GanttScale ($aDate).");
+ }
+
+
+ // Convert a time string to minutes
+
+ function TimeToMinutes($aTimeString) {
+ // Split in hours and minutes
+ $pos=strpos($aTimeString,':');
+ $minint=60;
+ if( $pos === false ) {
+ $hourint = $aTimeString;
+ $minint = 0;
+ }
+ else {
+ $hourint = floor(substr($aTimeString,0,$pos));
+ $minint = floor(substr($aTimeString,$pos+1));
+ }
+ $minint += 60 * $hourint;
+ return $minint;
+ }
+
+ // Stroke the day scale (including gridlines)
+ function StrokeMinutes($aYCoord,$getHeight=false) {
+ $img=$this->iImg;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->minute->iShowLabels ) {
+ $img->SetFont($this->minute->iFFamily,$this->minute->iFStyle,$this->minute->iFSize);
+ $yb = $yt + $img->GetFontHeight() +
+ $this->minute->iTitleVertMargin + $this->minute->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb = $img->width-$img->right_margin+1;
+ $img->SetColor($this->minute->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $day = date('w',$this->iStartDate);
+ $minint = $this->minute->GetIntervall() ;
+
+ if( 60 % $minint !== 0 ) {
+ JpGraphError::Raise('Intervall for minutes must divide the hour evenly, e.g. 1,5,10,12,15,20,30 etc You have specified an intervall of '.$minint.' minutes.');
+ }
+
+
+ $n = 60 / $minint;
+ $datestamp = $this->iStartDate;
+ $width = $this->GetHourWidth() / $n ;
+ if( $width < 8 ) {
+ // TO small width to draw minute scale
+ JpGraphError::Raise('The available width ('.$width.') for minutes are to small for this scale to be displayed. Please use auto-sizing or increase the width of the graph.');
+ }
+
+ $nh = ceil(24*60 / $this->TimeToMinutes($this->hour->GetIntervall()) );
+ $nd = $this->GetNumberOfDays();
+ // Convert to intervall to seconds
+ $minint *= 60;
+ for($j=0; $j < $nd; ++$j, $day += 1, $day %= 7) {
+ for( $k=0; $k < $nh; ++$k ) {
+ for($i=0; $i < $n ;++$i, $x+=$width, $datestamp += $minint ) {
+ if( $day==6 || $day==0 ) {
+
+ $img->PushColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground )
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
+ $img->PopColor();
+
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+
+ switch( $this->minute->iStyle ) {
+ case MINUTESTYLE_CUSTOM:
+ $txt = date($this->minute->iLabelFormStr,$datestamp);
+ break;
+ case MINUTESTYLE_MM:
+ default:
+ // 15
+ $txt = date('i',$datestamp);
+ break;
+ }
+ $img->StrokeText(round($x+$width/2),round($yb-$this->minute->iTitleVertMargin),$txt);
+
+ // FIXME: The rounding problem needs to be solved properly ...
+ //
+ // Fix a rounding problem the wrong way ..
+ // If we also have hour scale then don't draw the firsta or last
+ // gridline since that will be overwritten by the hour scale gridline if such exists.
+ // However, due to the propagation of rounding of the 'x+=width' term in the loop
+ // this might sometimes be one pixel of so we fix this by not drawing it.
+ // The proper way to fix it would be to re-calculate the scale for each step and
+ // not using the additive term.
+ if( !(($i == $n || $i==0) && $this->hour->iShowLabels && $this->hour->grid->iShow) ) {
+ $img->SetColor($this->minute->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->minute->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ }
+ }
+ }
+ }
+ $img->SetColor($this->minute->iFrameColor);
+ $img->SetLineWeight($this->minute->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke the day scale (including gridlines)
+ function StrokeHours($aYCoord,$getHeight=false) {
+ $img=$this->iImg;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->hour->iShowLabels ) {
+ $img->SetFont($this->hour->iFFamily,$this->hour->iFStyle,$this->hour->iFSize);
+ $yb = $yt + $img->GetFontHeight() +
+ $this->hour->iTitleVertMargin + $this->hour->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb = $img->width-$img->right_margin+1;
+ $img->SetColor($this->hour->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $tmp = $this->hour->GetIntervall() ;
+ $minint = $this->TimeToMinutes($tmp);
+ if( 1440 % $minint !== 0 ) {
+ JpGraphError::Raise('Intervall for hours must divide the day evenly, e.g. 0:30, 1:00, 1:30, 4:00 etc. You have specified an intervall of '.$tmp);
+ }
+
+ $n = ceil(24*60 / $minint );
+ $datestamp = $this->iStartDate;
+ $day = date('w',$this->iStartDate);
+ $doback = !$this->minute->iShowLabels;
+ $width = $this->GetDayWidth() / $n ;
+ for($j=0; $j < $this->GetNumberOfDays(); ++$j, $day += 1,$day %= 7) {
+ for($i=0; $i < $n ;++$i, $x+=$width) {
+ if( $day==6 || $day==0 ) {
+
+ $img->PushColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground && $doback )
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,$x+$width,$yb-$this->day->iFrameWeight);
+ $img->PopColor();
+
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+
+ switch( $this->hour->iStyle ) {
+ case HOURSTYLE_HMAMPM:
+ // 1:35pm
+ $txt = date('g:ia',$datestamp);
+ break;
+ case HOURSTYLE_H24:
+ // 13
+ $txt = date('H',$datestamp);
+ break;
+ case HOURSTYLE_HAMPM:
+ $txt = date('ga',$datestamp);
+ break;
+ case HOURSTYLE_CUSTOM:
+ $txt = date($this->hour->iLabelFormStr,$datestamp);
+ break;
+ case HOURSTYLE_HM24:
+ default:
+ $txt = date('H:i',$datestamp);
+ break;
+ }
+ $img->StrokeText(round($x+$width/2),round($yb-$this->hour->iTitleVertMargin),$txt);
+ $img->SetColor($this->hour->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->hour->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ //$datestamp += $minint*60
+ $datestamp = mktime(date('H',$datestamp),date('i',$datestamp)+$minint,0,
+ date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
+
+ }
+ }
+ $img->SetColor($this->hour->iFrameColor);
+ $img->SetLineWeight($this->hour->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+
+ // Stroke the day scale (including gridlines)
+ function StrokeDays($aYCoord,$getHeight=false) {
+ $wdays=$this->iDateLocale->GetDayAbb();
+ $img=$this->iImg;
+ $daywidth=$this->GetDayWidth();
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $yt=$aYCoord+$img->top_margin;
+ if( $this->day->iShowLabels ) {
+ $img->SetFont($this->day->iFFamily,$this->day->iFStyle,$this->day->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->day->iTitleVertMargin + $this->day->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $xb=$img->width-$img->right_margin+1;
+ $img->SetColor($this->day->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $x = $xt;
+ $img->SetTextAlign("center");
+ $day = date('w',$this->iStartDate);
+ $datestamp = $this->iStartDate;
+
+ $doback = !($this->hour->iShowLabels || $this->minute->iShowLabels);
+
+ for($i=0; $i < $this->GetNumberOfDays(); ++$i, $x+=$daywidth, $day += 1,$day %= 7) {
+ if( $day==6 || $day==0 ) {
+ $img->SetColor($this->day->iWeekendBackgroundColor);
+ if( $this->iUsePlotWeekendBackground && $doback)
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
+ $x+$daywidth,$img->height-$img->bottom_margin);
+ else
+ $img->FilledRectangle($x,$yt+$this->day->iFrameWeight,
+ $x+$daywidth,$yb-$this->day->iFrameWeight);
+ }
+ switch( $this->day->iStyle ) {
+ case DAYSTYLE_LONG:
+ // "Monday"
+ $txt = date('l',$datestamp);
+ break;
+ case DAYSTYLE_SHORT:
+ // "Mon"
+ $txt = date('D',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE1:
+ // "Mon 23/6"
+ $txt = date('D j/n',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE2:
+ // "Mon 23 Jun"
+ $txt = date('D j M',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDAYDATE3:
+ // "Mon 23 Jun 2003"
+ $txt = date('D j M Y',$datestamp);
+ break;
+ case DAYSTYLE_LONGDAYDATE1:
+ // "Monday 23 Jun"
+ $txt = date('l j M',$datestamp);
+ break;
+ case DAYSTYLE_LONGDAYDATE2:
+ // "Monday 23 Jun 2003"
+ $txt = date('l j M Y',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE1:
+ // "23/6"
+ $txt = date('j/n',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE2:
+ // "23 Jun"
+ $txt = date('j M',$datestamp);
+ break;
+ case DAYSTYLE_SHORTDATE3:
+ // "Mon 23"
+ $txt = date('D j',$datestamp);
+ break;
+ case DAYSTYLE_CUSTOM:
+ // Custom format
+ $txt = date($this->day->iLabelFormStr,$datestamp);
+ break;
+ case DAYSTYLE_ONELETTER:
+ default:
+ // "M"
+ $txt = substr(date('D',$datestamp),0,1);
+ break;
+ }
+
+ if( $day==0 )
+ $img->SetColor($this->day->iSundayTextColor);
+ else
+ $img->SetColor($this->day->iTextColor);
+ $img->StrokeText(round($x+$daywidth/2+1),
+ round($yb-$this->day->iTitleVertMargin),$txt);
+ $img->SetColor($this->day->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->day->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ $datestamp = mktime(0,0,0,date("m",$datestamp),date("d",$datestamp)+1,date("Y",$datestamp));
+ //$datestamp += SECPERDAY;
+
+ }
+ $img->SetColor($this->day->iFrameColor);
+ $img->SetLineWeight($this->day->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb - $img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke week header and grid
+ function StrokeWeeks($aYCoord,$getHeight=false) {
+ if( $this->week->iShowLabels ) {
+ $img=$this->iImg;
+ $yt=$aYCoord+$img->top_margin;
+ $img->SetFont($this->week->iFFamily,$this->week->iFStyle,$this->week->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->week->iTitleVertMargin + $this->week->iFrameWeight;
+
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $weekwidth=$this->GetDayWidth()*7;
+ $wdays=$this->iDateLocale->GetDayAbb();
+ $xb=$img->width-$img->right_margin+1;
+ $week = $this->iStartDate;
+ $weeknbr=$this->GetWeekNbr($week);
+ $img->SetColor($this->week->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $img->SetColor($this->week->grid->iColor);
+ $x = $xt;
+ if( $this->week->iStyle==WEEKSTYLE_WNBR ) {
+ $img->SetTextAlign("center");
+ $txtOffset = $weekwidth/2+1;
+ }
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $img->SetTextAlign("left");
+ $txtOffset = 3;
+ }
+ else
+ JpGraphError::Raise("Unknown formatting style for week.");
+
+ for($i=0; $i<$this->GetNumberOfDays()/7; ++$i, $x+=$weekwidth) {
+ $img->PushColor($this->week->iTextColor);
+
+ if( $this->week->iStyle==WEEKSTYLE_WNBR )
+ $txt = sprintf($this->week->iLabelFormStr,$weeknbr);
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR )
+ $txt = date("j/n",$week);
+ elseif( $this->week->iStyle==WEEKSTYLE_FIRSTDAY2 ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $monthnbr = date("n",$week)-1;
+ $shortmonth = $this->iDateLocale->GetShortMonthName($monthnbr);
+ $txt = Date("j",$week)." ".$shortmonth;
+ }
+
+ if( $this->week->iStyle==WEEKSTYLE_FIRSTDAYWNBR ||
+ $this->week->iStyle==WEEKSTYLE_FIRSTDAY2WNBR ) {
+ $w = sprintf($this->week->iLabelFormStr,$weeknbr);
+ $txt .= ' '.$w;
+ }
+
+ $img->StrokeText(round($x+$txtOffset),
+ round($yb-$this->week->iTitleVertMargin),$txt);
+
+ $week = strtotime('+7 day',$week);
+ $weeknbr = $this->GetWeekNbr($week);
+ $img->PopColor();
+ $img->Line($x,$yt,$x,$yb);
+ $this->week->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ }
+ $img->SetColor($this->week->iFrameColor);
+ $img->SetLineWeight($this->week->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Format the mont scale header string
+ function GetMonthLabel($aMonthNbr,$year) {
+ $sn = $this->iDateLocale->GetShortMonthName($aMonthNbr);
+ $ln = $this->iDateLocale->GetLongMonthName($aMonthNbr);
+ switch($this->month->iStyle) {
+ case MONTHSTYLE_SHORTNAME:
+ $m=$sn;
+ break;
+ case MONTHSTYLE_LONGNAME:
+ $m=$ln;
+ break;
+ case MONTHSTYLE_SHORTNAMEYEAR2:
+ $m=$sn." '".substr("".$year,2);
+ break;
+ case MONTHSTYLE_SHORTNAMEYEAR4:
+ $m=$sn." ".$year;
+ break;
+ case MONTHSTYLE_LONGNAMEYEAR2:
+ $m=$ln." '".substr("".$year,2);
+ break;
+ case MONTHSTYLE_LONGNAMEYEAR4:
+ $m=$ln." ".$year;
+ break;
+ }
+ return $m;
+ }
+
+ // Stroke month scale and gridlines
+ function StrokeMonths($aYCoord,$getHeight=false) {
+ if( $this->month->iShowLabels ) {
+ $img=$this->iImg;
+ $img->SetFont($this->month->iFFamily,$this->month->iFStyle,$this->month->iFSize);
+ $yt=$aYCoord+$img->top_margin;
+ $yb=$yt + $img->GetFontHeight() + $this->month->iTitleVertMargin + $this->month->iFrameWeight;
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+ $monthnbr = $this->GetMonthNbr($this->iStartDate)-1;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $xb=$img->width-$img->right_margin+1;
+
+ $img->SetColor($this->month->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+
+ $img->SetLineWeight($this->month->grid->iWeight);
+ $img->SetColor($this->month->iTextColor);
+ $year = 0+strftime("%Y",$this->iStartDate);
+ $img->SetTextAlign("center");
+ if( $this->GetMonthNbr($this->iStartDate) == $this->GetMonthNbr($this->iEndDate)
+ && $this->GetYear($this->iStartDate)==$this->GetYear($this->iEndDate) ) {
+ $monthwidth=$this->GetDayWidth()*($this->GetMonthDayNbr($this->iEndDate) - $this->GetMonthDayNbr($this->iStartDate) + 1);
+ }
+ else {
+ $monthwidth=$this->GetDayWidth()*($this->GetNumDaysInMonth($monthnbr,$year)-$this->GetMonthDayNbr($this->iStartDate)+1);
+ }
+ // Is it enough space to stroke the first month?
+ $monthName = $this->GetMonthLabel($monthnbr,$year);
+ if( $monthwidth >= 1.2*$img->GetTextWidth($monthName) ) {
+ $img->SetColor($this->month->iTextColor);
+ $img->StrokeText(round($xt+$monthwidth/2+1),
+ round($yb-$this->month->iTitleVertMargin),
+ $monthName);
+ }
+ $x = $xt + $monthwidth;
+ while( $x < $xb ) {
+ $img->SetColor($this->month->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->month->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ $monthnbr++;
+ if( $monthnbr==12 ) {
+ $monthnbr=0;
+ $year++;
+ }
+ $monthName = $this->GetMonthLabel($monthnbr,$year);
+ $monthwidth=$this->GetDayWidth()*$this->GetNumDaysInMonth($monthnbr,$year);
+ if( $x + $monthwidth < $xb )
+ $w = $monthwidth;
+ else
+ $w = $xb-$x;
+ if( $w >= 1.2*$img->GetTextWidth($monthName) ) {
+ $img->SetColor($this->month->iTextColor);
+ $img->StrokeText(round($x+$w/2+1),
+ round($yb-$this->month->iTitleVertMargin),$monthName);
+ }
+ $x += $monthwidth;
+ }
+ $img->SetColor($this->month->iFrameColor);
+ $img->SetLineWeight($this->month->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke year scale and gridlines
+ function StrokeYears($aYCoord,$getHeight=false) {
+ if( $this->year->iShowLabels ) {
+ $img=$this->iImg;
+ $yt=$aYCoord+$img->top_margin;
+ $img->SetFont($this->year->iFFamily,$this->year->iFStyle,$this->year->iFSize);
+ $yb=$yt + $img->GetFontHeight() + $this->year->iTitleVertMargin + $this->year->iFrameWeight;
+
+ if( $getHeight ) {
+ return $yb - $img->top_margin;
+ }
+
+ $xb=$img->width-$img->right_margin+1;
+ $xt=$img->left_margin+$this->iLabelWidth;
+ $year = $this->GetYear($this->iStartDate);
+ $img->SetColor($this->year->iBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $img->SetLineWeight($this->year->grid->iWeight);
+ $img->SetTextAlign("center");
+ if( $year == $this->GetYear($this->iEndDate) )
+ $yearwidth=$this->GetDayWidth()*($this->GetYearDayNbr($this->iEndDate)-$this->GetYearDayNbr($this->iStartDate)+1);
+ else
+ $yearwidth=$this->GetDayWidth()*($this->GetNumDaysInYear($year)-$this->GetYearDayNbr($this->iStartDate)+1);
+
+ // The space for a year must be at least 20% bigger than the actual text
+ // so we allow 10% margin on each side
+ if( $yearwidth >= 1.20*$img->GetTextWidth("".$year) ) {
+ $img->SetColor($this->year->iTextColor);
+ $img->StrokeText(round($xt+$yearwidth/2+1),
+ round($yb-$this->year->iTitleVertMargin),
+ $year);
+ }
+ $x = $xt + $yearwidth;
+ while( $x < $xb ) {
+ $img->SetColor($this->year->grid->iColor);
+ $img->Line($x,$yt,$x,$yb);
+ $this->year->grid->Stroke($img,$x,$yb,$x,$img->height-$img->bottom_margin);
+ $year += 1;
+ $yearwidth=$this->GetDayWidth()*$this->GetNumDaysInYear($year);
+ if( $x + $yearwidth < $xb )
+ $w = $yearwidth;
+ else
+ $w = $xb-$x;
+ if( $w >= 1.2*$img->GetTextWidth("".$year) ) {
+ $img->SetColor($this->year->iTextColor);
+ $img->StrokeText(round($x+$w/2+1),
+ round($yb-$this->year->iTitleVertMargin),
+ $year);
+ }
+ $x += $yearwidth;
+ }
+ $img->SetColor($this->year->iFrameColor);
+ $img->SetLineWeight($this->year->iFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ return $yb-$img->top_margin;
+ }
+ return $aYCoord;
+ }
+
+ // Stroke table title (upper left corner)
+ function StrokeTableHeaders($aYBottom) {
+ $img=$this->iImg;
+ $xt=$img->left_margin;
+ $yt=$img->top_margin;
+ $xb=$xt+$this->iLabelWidth;
+ $yb=$aYBottom+$img->top_margin;
+
+ if( $this->tableTitle->iShow ) {
+ $img->SetColor($this->iTableHeaderBackgroundColor);
+ $img->FilledRectangle($xt,$yt,$xb,$yb);
+ $this->tableTitle->Align("center","top");
+ $this->tableTitle->Stroke($img,$xt+($xb-$xt)/2+1,$yt+2);
+ $img->SetColor($this->iTableHeaderFrameColor);
+ $img->SetLineWeight($this->iTableHeaderFrameWeight);
+ $img->Rectangle($xt,$yt,$xb,$yb);
+ }
+
+ $this->actinfo->Stroke($img,$xt,$yt,$xb,$yb,$this->tableTitle->iShow);
+
+
+ // Draw the horizontal dividing line
+ $this->dividerh->Stroke($img,$xt,$yb,$img->width-$img->right_margin,$yb);
+
+ // Draw the vertical dividing line
+ // We do the width "manually" since we want the line only to grow
+ // to the left
+ $fancy = $this->divider->iStyle == 'fancy' ;
+ if( $fancy ) {
+ $this->divider->iStyle = 'solid';
+ }
+
+ $tmp = $this->divider->iWeight;
+ $this->divider->iWeight=1;
+ $y = $img->height-$img->bottom_margin;
+ for($i=0; $i < $tmp; ++$i ) {
+ $this->divider->Stroke($img,$xb-$i,$yt,$xb-$i,$y);
+ }
+
+ // Should we draw "fancy" divider
+ if( $fancy ) {
+ $img->SetLineWeight(1);
+ $img->SetColor($this->iTableHeaderFrameColor);
+ $img->Line($xb,$yt,$xb,$y);
+ $img->Line($xb-$tmp+1,$yt,$xb-$tmp+1,$y);
+ $img->SetColor('white');
+ $img->Line($xb-$tmp+2,$yt,$xb-$tmp+2,$y);
+ }
+
+
+ }
+
+ // Main entry point to stroke scale
+ function Stroke() {
+ if( !$this->IsRangeSet() )
+ JpGraphError::Raise("Gantt scale has not been specified.");
+ $img=$this->iImg;
+
+ // If minutes are displayed then hour interval must be 1
+ if( $this->IsDisplayMinute() && $this->hour->GetIntervall() > 1 ) {
+ JpGraphError::Raise('If you display both hour and minutes the hour intervall must be 1 (Otherwise it doesn\' make sense to display minutes).');
+ }
+
+ // Stroke all headers. As argument we supply the offset from the
+ // top which depends on any previous headers
+
+ // First find out the height of each header
+ $offy=$this->StrokeYears(0,true);
+ $offm=$this->StrokeMonths($offy,true);
+ $offw=$this->StrokeWeeks($offm,true);
+ $offd=$this->StrokeDays($offw,true);
+ $offh=$this->StrokeHours($offd,true);
+ $offmin=$this->StrokeMinutes($offh,true);
+
+ // ... then we can stroke them in the "backwards order to ensure that
+ // the larger scale gridlines is stroked over the smaller scale gridline
+ $this->StrokeMinutes($offh);
+ $this->StrokeHours($offd);
+ $this->StrokeDays($offw);
+ $this->StrokeWeeks($offm);
+ $this->StrokeMonths($offy);
+ $this->StrokeYears(0);
+
+ // Now when we now the oaverall size of the scale headers
+ // we can stroke the overall table headers
+ $this->StrokeTableHeaders($offmin);
+
+ // Now we can calculate the correct scaling factor for each vertical position
+ $this->iAvailableHeight = $img->height - $img->top_margin - $img->bottom_margin - $offd;
+ $this->iVertHeaderSize = $offmin;
+ if( $this->iVertSpacing == -1 )
+ $this->iVertSpacing = $this->iAvailableHeight / $this->iVertLines;
+ }
+}
+
+
+//===================================================
+// CLASS GanttConstraint
+// Just a structure to store all the values for a constraint
+//===================================================
+class GanttConstraint {
+ var $iConstrainType;
+ var $iConstrainRow;
+ var $iConstrainColor;
+ var $iConstrainArrowSize;
+ var $iConstrainArrowType;
+
+//---------------
+// CONSTRUCTOR
+ function GanttConstraint($aRow,$aType,$aColor,$aArrowSize,$aArrowType){
+ $this->iConstrainType = $aType;
+ $this->iConstrainRow = $aRow;
+ $this->iConstrainColor=$aColor;
+ $this->iConstrainArrowSize=$aArrowSize;
+ $this->iConstrainArrowType=$aArrowType;
+ }
+}
+
+
+//===================================================
+// CLASS GanttPlotObject
+// The common signature for a Gantt object
+//===================================================
+class GanttPlotObject {
+ var $iVPos=0; // Vertical position
+ var $iLabelLeftMargin=2; // Title margin
+ var $iStart=""; // Start date
+ var $title,$caption;
+ var $iCaptionMargin=5;
+ var $csimarea='',$csimtarget='',$csimalt='';
+
+ var $constraints = array();
+ var $iConstrainPos=array();
+
+ function GanttPlotObject() {
+ $this->title = new TextProperty();
+ $this->title->Align("left","center");
+ $this->caption = new TextProperty();
+ }
+
+ function GetCSIMArea() {
+ return $this->csimarea;
+ }
+
+ function SetCSIMTarget($aTarget,$aAlt='') {
+ if( !is_string($aTarget) ) {
+ $tv = substr(var_export($aTarget,true),0,40);
+ JpGraphError::Raise('CSIM Target must be specified as a string.'."\nStart of target is:\n$tv");
+ }
+ if( !is_string($aAlt) ) {
+ $tv = substr(var_export($aAlt,true),0,40);
+ JpGraphError::Raise('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
+ }
+
+ $this->csimtarget=$aTarget;
+ $this->csimalt=$aAlt;
+ }
+
+ function SetCSIMAlt($aAlt) {
+ if( !is_string($aAlt) ) {
+ $tv = substr(var_export($aAlt,true),0,40);
+ JpGraphError::Raise('CSIM Alt text must be specified as a string.'."\nStart of alt text is:\n$tv");
+ }
+ $this->csimalt=$aAlt;
+ }
+
+ function SetConstrain($aRow,$aType,$aColor='black',$aArrowSize=ARROW_S2,$aArrowType=ARROWT_SOLID) {
+ $this->constraints[] = new GanttConstraint($aRow, $aType, $aColor, $aArrowSize, $aArrowType);
+ }
+
+ function SetConstrainPos($xt,$yt,$xb,$yb) {
+ $this->iConstrainPos = array($xt,$yt,$xb,$yb);
+ }
+
+ /*
+ function GetConstrain() {
+ return array($this->iConstrainRow,$this->iConstrainType);
+ }
+ */
+
+ function GetMinDate() {
+ return $this->iStart;
+ }
+
+ function GetMaxDate() {
+ return $this->iStart;
+ }
+
+ function SetCaptionMargin($aMarg) {
+ $this->iCaptionMargin=$aMarg;
+ }
+
+ function GetAbsHeight($aImg) {
+ return 0;
+ }
+
+ function GetLineNbr() {
+ return $this->iVPos;
+ }
+
+ function SetLabelLeftMargin($aOff) {
+ $this->iLabelLeftMargin=$aOff;
+ }
+
+ function StrokeActInfo($aImg,$aScale,$aYPos) {
+ $cols=array();
+ $aScale->actinfo->GetColStart($aImg,$cols,true);
+ $this->title->Stroke($aImg,$cols,$aYPos);
+ }
+}
+
+//===================================================
+// CLASS Progress
+// Holds parameters for the progress indicator
+// displyed within a bar
+//===================================================
+class Progress {
+ var $iProgress=-1, $iColor="black", $iFillColor='black';
+ var $iPattern=GANTT_SOLID;
+ var $iDensity=98, $iHeight=0.65;
+
+ function Set($aProg) {
+ if( $aProg < 0.0 || $aProg > 1.0 )
+ JpGraphError::Raise("Progress value must in range [0, 1]");
+ $this->iProgress = $aProg;
+ }
+
+ function SetPattern($aPattern,$aColor="blue",$aDensity=98) {
+ $this->iPattern = $aPattern;
+ $this->iColor = $aColor;
+ $this->iDensity = $aDensity;
+ }
+
+ function SetFillColor($aColor) {
+ $this->iFillColor = $aColor;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iHeight = $aHeight;
+ }
+}
+
+//===================================================
+// CLASS GanttBar
+// Responsible for formatting individual gantt bars
+//===================================================
+class GanttBar extends GanttPlotObject {
+ var $iEnd;
+ var $iHeightFactor=0.5;
+ var $iFillColor="white",$iFrameColor="black";
+ var $iShadow=false,$iShadowColor="darkgray",$iShadowWidth=1,$iShadowFrame="black";
+ var $iPattern=GANTT_RDIAG,$iPatternColor="blue",$iPatternDensity=95;
+ var $leftMark,$rightMark;
+ var $progress;
+//---------------
+// CONSTRUCTOR
+ function GanttBar($aPos,$aLabel,$aStart,$aEnd,$aCaption="",$aHeightFactor=0.6) {
+ parent::GanttPlotObject();
+ $this->iStart = $aStart;
+ // Is the end date given as a date or as number of days added to start date?
+ if( is_string($aEnd) ) {
+ // If end date has been specified without a time we will asssume
+ // end date is at the end of that date
+ if( strpos($aEnd,':') === false )
+ $this->iEnd = strtotime($aEnd)+SECPERDAY-1;
+ else
+ $this->iEnd = $aEnd;
+ }
+ elseif(is_int($aEnd) || is_float($aEnd) )
+ $this->iEnd = strtotime($aStart)+round($aEnd*SECPERDAY);
+ $this->iVPos = $aPos;
+ $this->iHeightFactor = $aHeightFactor;
+ $this->title->Set($aLabel);
+ $this->caption = new TextProperty($aCaption);
+ $this->caption->Align("left","center");
+ $this->leftMark =new PlotMark();
+ $this->leftMark->Hide();
+ $this->rightMark=new PlotMark();
+ $this->rightMark->Hide();
+ $this->progress = new Progress();
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetShadow($aShadow=true,$aColor="gray") {
+ $this->iShadow=$aShadow;
+ $this->iShadowColor=$aColor;
+ }
+
+ function GetMaxDate() {
+ return $this->iEnd;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iHeightFactor = $aHeight;
+ }
+
+ function SetColor($aColor) {
+ $this->iFrameColor = $aColor;
+ }
+
+ function SetFillColor($aColor) {
+ $this->iFillColor = $aColor;
+ }
+
+ function GetAbsHeight($aImg) {
+ if( is_int($this->iHeightFactor) || $this->leftMark->show || $this->rightMark->show ) {
+ $m=-1;
+ if( is_int($this->iHeightFactor) )
+ $m = $this->iHeightFactor;
+ if( $this->leftMark->show )
+ $m = max($m,$this->leftMark->width*2);
+ if( $this->rightMark->show )
+ $m = max($m,$this->rightMark->width*2);
+ return $m;
+ }
+ else
+ return -1;
+ }
+
+ function SetPattern($aPattern,$aColor="blue",$aDensity=95) {
+ $this->iPattern = $aPattern;
+ $this->iPatternColor = $aColor;
+ $this->iPatternDensity = $aDensity;
+ }
+
+ function Stroke($aImg,$aScale) {
+ $factory = new RectPatternFactory();
+ $prect = $factory->Create($this->iPattern,$this->iPatternColor);
+ $prect->SetDensity($this->iPatternDensity);
+
+ // If height factor is specified as a float between 0,1 then we take it as meaning
+ // percetage of the scale width between horizontal line.
+ // If it is an integer > 1 we take it to mean the absolute height in pixels
+ if( $this->iHeightFactor > -0.0 && $this->iHeightFactor <= 1.1)
+ $vs = $aScale->GetVertSpacing()*$this->iHeightFactor;
+ elseif(is_int($this->iHeightFactor) && $this->iHeightFactor>2 && $this->iHeightFactor<200)
+ $vs = $this->iHeightFactor;
+ else
+ JpGraphError::Raise("Specified height (".$this->iHeightFactor.") for gantt bar is out of range.");
+
+ // Clip date to min max dates to show
+ $st = $aScale->NormalizeDate($this->iStart);
+ $en = $aScale->NormalizeDate($this->iEnd);
+
+
+ $limst = max($st,$aScale->iStartDate);
+ $limen = min($en,$aScale->iEndDate);
+
+ $xt = round($aScale->TranslateDate($limst));
+ $xb = round($aScale->TranslateDate($limen));
+ $yt = round($aScale->TranslateVertPos($this->iVPos)-$vs-($aScale->GetVertSpacing()/2-$vs/2));
+ $yb = round($aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2-$vs/2));
+ $middle = round($yt+($yb-$yt)/2);
+ $this->StrokeActInfo($aImg,$aScale,$middle);
+
+ // CSIM for title
+ if( ! empty($this->title->csimtarget) ) {
+ $colwidth = $this->title->GetColWidth($aImg);
+ $colstarts=array();
+ $aScale->actinfo->GetColStart($aImg,$colstarts,true);
+ $n = min(count($colwidth),count($this->title->csimtarget));
+ for( $i=0; $i < $n; ++$i ) {
+ $title_xt = $colstarts[$i];
+ $title_xb = $title_xt + $colwidth[$i];
+ $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
+ if( ! empty($this->title->csimalt[$i]) ) {
+ $tmp = $this->title->csimalt[$i];
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+ }
+
+ // Check if the bar is totally outside the current scale range
+ if( $en < $aScale->iStartDate || $st > $aScale->iEndDate )
+ return;
+
+
+ // Remember the positions for the bar
+ $this->SetConstrainPos($xt,$yt,$xb,$yb);
+
+ $prect->ShowFrame(false);
+ $prect->SetBackground($this->iFillColor);
+ if( $this->iShadow ) {
+ $aImg->SetColor($this->iFrameColor);
+ $aImg->ShadowRectangle($xt,$yt,$xb,$yb,$this->iFillColor,$this->iShadowWidth,$this->iShadowColor);
+ $prect->SetPos(new Rectangle($xt+1,$yt+1,$xb-$xt-$this->iShadowWidth-2,$yb-$yt-$this->iShadowWidth-2));
+ $prect->Stroke($aImg);
+ }
+ else {
+ $prect->SetPos(new Rectangle($xt,$yt,$xb-$xt+1,$yb-$yt+1));
+ $prect->Stroke($aImg);
+ $aImg->SetColor($this->iFrameColor);
+ $aImg->Rectangle($xt,$yt,$xb,$yb);
+ }
+
+ // CSIM for bar
+ if( $this->csimtarget != '' ) {
+
+ $coords = "$xt,$yt,$xb,$yt,$xb,$yb,$xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtarget."\"";
+ if( $this->csimalt != '' ) {
+ $tmp = $this->csimalt;
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+
+ // Draw progress bar inside activity bar
+ if( $this->progress->iProgress > 0 ) {
+
+ $xtp = $aScale->TranslateDate($st);
+ $xbp = $aScale->TranslateDate($en);
+ $len = ($xbp-$xtp)*$this->progress->iProgress;
+
+ $endpos = $xtp+$len;
+ if( $endpos > $xt ) {
+ $len -= ($xt-$xtp);
+
+ // Make sure that the progess bar doesn't extend over the end date
+ if( $xtp+$len-1 > $xb )
+ $len = $xb - $xtp + 1;
+
+ if( $xtp < $xt )
+ $xtp = $xt;
+
+ $prog = $factory->Create($this->progress->iPattern,$this->progress->iColor);
+ $prog->SetDensity($this->progress->iDensity);
+ $prog->SetBackground($this->progress->iFillColor);
+ $barheight = ($yb-$yt+1);
+ if( $this->iShadow )
+ $barheight -= $this->iShadowWidth;
+ $progressheight = floor($barheight*$this->progress->iHeight);
+ $marg = ceil(($barheight-$progressheight)/2);
+ $pos = new Rectangle($xtp,$yt + $marg, $len,$barheight-2*$marg);
+ $prog->SetPos($pos);
+ $prog->Stroke($aImg);
+ }
+ }
+
+ // We don't plot the end mark if the bar has been capped
+ if( $limst == $st ) {
+ $y = $middle;
+ // We treat the RIGHT and LEFT triangle mark a little bi
+ // special so that these marks are placed right under the
+ // bar.
+ if( $this->leftMark->GetType() == MARK_LEFTTRIANGLE ) {
+ $y = $yb ;
+ }
+ $this->leftMark->Stroke($aImg,$xt,$y);
+ }
+ if( $limen == $en ) {
+ $y = $middle;
+ // We treat the RIGHT and LEFT triangle mark a little bi
+ // special so that these marks are placed right under the
+ // bar.
+ if( $this->rightMark->GetType() == MARK_RIGHTTRIANGLE ) {
+ $y = $yb ;
+ }
+ $this->rightMark->Stroke($aImg,$xb,$y);
+
+ $margin = $this->iCaptionMargin;
+ if( $this->rightMark->show )
+ $margin += $this->rightMark->GetWidth();
+ $this->caption->Stroke($aImg,$xb+$margin,$middle);
+ }
+ }
+}
+
+//===================================================
+// CLASS MileStone
+// Responsible for formatting individual milestones
+//===================================================
+class MileStone extends GanttPlotObject {
+ var $mark;
+
+//---------------
+// CONSTRUCTOR
+ function MileStone($aVPos,$aLabel,$aDate,$aCaption="") {
+ GanttPlotObject::GanttPlotObject();
+ $this->caption->Set($aCaption);
+ $this->caption->Align("left","center");
+ $this->caption->SetFont(FF_FONT1,FS_BOLD);
+ $this->title->Set($aLabel);
+ $this->title->SetColor("darkred");
+ $this->mark = new PlotMark();
+ $this->mark->SetWidth(10);
+ $this->mark->SetType(MARK_DIAMOND);
+ $this->mark->SetColor("darkred");
+ $this->mark->SetFillColor("darkred");
+ $this->iVPos = $aVPos;
+ $this->iStart = $aDate;
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function GetAbsHeight($aImg) {
+ return max($this->title->GetHeight($aImg),$this->mark->GetWidth());
+ }
+
+ function Stroke($aImg,$aScale) {
+ // Put the mark in the middle at the middle of the day
+ $d = $aScale->NormalizeDate($this->iStart)+SECPERDAY/2;
+ $x = $aScale->TranslateDate($d);
+ $y = $aScale->TranslateVertPos($this->iVPos)-($aScale->GetVertSpacing()/2);
+
+ $this->StrokeActInfo($aImg,$aScale,$y);
+
+ // CSIM for title
+ if( ! empty($this->title->csimtarget) ) {
+
+ $yt = round($y - $this->title->GetHeight($aImg)/2);
+ $yb = round($y + $this->title->GetHeight($aImg)/2);
+
+ $colwidth = $this->title->GetColWidth($aImg);
+ $colstarts=array();
+ $aScale->actinfo->GetColStart($aImg,$colstarts,true);
+ $n = min(count($colwidth),count($this->title->csimtarget));
+ for( $i=0; $i < $n; ++$i ) {
+ $title_xt = $colstarts[$i];
+ $title_xb = $title_xt + $colwidth[$i];
+ $coords = "$title_xt,$yt,$title_xb,$yt,$title_xb,$yb,$title_xt,$yb";
+ $this->csimarea .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->title->csimtarget[$i]."\"";
+ if( ! empty($this->title->csimalt[$i]) ) {
+ $tmp = $this->title->csimalt[$i];
+ $this->csimarea .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimarea .= ">\n";
+ }
+ }
+
+ if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
+ return;
+
+ // Remember the coordinates for any constrains linking to
+ // this milestone
+ $w = $this->mark->GetWidth()/2;
+ $this->SetConstrainPos($x,round($y-$w),$x,round($y+$w));
+
+ // Setup CSIM
+ if( $this->csimtarget != '' ) {
+ $this->mark->SetCSIMTarget( $this->csimtarget );
+ $this->mark->SetCSIMAlt( $this->csimalt );
+ }
+
+ $this->mark->Stroke($aImg,$x,$y);
+ $this->caption->Stroke($aImg,$x+$this->mark->width/2+$this->iCaptionMargin,$y);
+
+ $this->csimarea .= $this->mark->GetCSIMAreas();
+ }
+}
+
+
+//===================================================
+// CLASS GanttVLine
+// Responsible for formatting individual milestones
+//===================================================
+
+class TextPropertyBelow extends TextProperty {
+ function TextPropertyBelow($aTxt='') {
+ parent::TextProperty($aTxt);
+ }
+
+ function GetColWidth($aImg,$margin) {
+ // Since we are not stroking the title in the columns
+ // but rather under the graph we want this to return 0.
+ return array(0);
+ }
+}
+
+class GanttVLine extends GanttPlotObject {
+
+ var $iLine,$title_margin=3;
+ var $iDayOffset=1; // Defult to right edge of day
+
+//---------------
+// CONSTRUCTOR
+ function GanttVLine($aDate,$aTitle="",$aColor="black",$aWeight=3,$aStyle="dashed") {
+ GanttPlotObject::GanttPlotObject();
+ $this->iLine = new LineProperty();
+ $this->iLine->SetColor($aColor);
+ $this->iLine->SetWeight($aWeight);
+ $this->iLine->SetStyle($aStyle);
+ $this->iStart = $aDate;
+ $this->title = new TextPropertyBelow();
+ $this->title->Set($aTitle);
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ function SetDayOffset($aOff=0.5) {
+ if( $aOff < 0.0 || $aOff > 1.0 )
+ JpGraphError::Raise("Offset for vertical line must be in range [0,1]");
+ $this->iDayOffset = $aOff;
+ }
+
+ function SetTitleMargin($aMarg) {
+ $this->title_margin = $aMarg;
+ }
+
+ function Stroke($aImg,$aScale) {
+ $d = $aScale->NormalizeDate($this->iStart);
+ if( $d < $aScale->iStartDate || $d > $aScale->iEndDate )
+ return;
+ $x = $aScale->TranslateDate($d);
+ $y1 = $aScale->iVertHeaderSize+$aImg->top_margin;
+ $y2 = $aImg->height - $aImg->bottom_margin;
+ $this->iLine->Stroke($aImg,$x,$y1,$x,$y2);
+ $this->title->Align("center","top");
+ $this->title->Stroke($aImg,$x,$y2+$this->title_margin);
+ }
+}
+
+//===================================================
+// CLASS LinkArrow
+// Handles the drawing of a an arrow
+//===================================================
+class LinkArrow {
+ var $ix,$iy;
+ var $isizespec = array(
+ array(2,3),array(3,5),array(3,8),array(6,15),array(8,22));
+ var $iDirection=ARROW_DOWN,$iType=ARROWT_SOLID,$iSize=ARROW_S2;
+ var $iColor='black';
+
+ function LinkArrow($x,$y,$aDirection,$aType=ARROWT_SOLID,$aSize=ARROW_S2) {
+ $this->iDirection = $aDirection;
+ $this->iType = $aType;
+ $this->iSize = $aSize;
+ $this->ix = $x;
+ $this->iy = $y;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetSize($aSize) {
+ $this->iSize = $aSize;
+ }
+
+ function SetType($aType) {
+ $this->iType = $aType;
+ }
+
+ function Stroke($aImg) {
+ list($dx,$dy) = $this->isizespec[$this->iSize];
+ $x = $this->ix;
+ $y = $this->iy;
+ switch ( $this->iDirection ) {
+ case ARROW_DOWN:
+ $c = array($x,$y,$x-$dx,$y-$dy,$x+$dx,$y-$dy,$x,$y);
+ break;
+ case ARROW_UP:
+ $c = array($x,$y,$x-$dx,$y+$dy,$x+$dx,$y+$dy,$x,$y);
+ break;
+ case ARROW_LEFT:
+ $c = array($x,$y,$x+$dy,$y-$dx,$x+$dy,$y+$dx,$x,$y);
+ break;
+ case ARROW_RIGHT:
+ $c = array($x,$y,$x-$dy,$y-$dx,$x-$dy,$y+$dx,$x,$y);
+ break;
+ default:
+ JpGraphError::Raise('Unknown arrow direction for link.');
+ die();
+ break;
+ }
+ $aImg->SetColor($this->iColor);
+ switch( $this->iType ) {
+ case ARROWT_SOLID:
+ $aImg->FilledPolygon($c);
+ break;
+ case ARROWT_OPEN:
+ $aImg->Polygon($c);
+ break;
+ default:
+ JpGraphError::Raise('Unknown arrow type for link.');
+ die();
+ break;
+ }
+ }
+}
+
+//===================================================
+// CLASS GanttLink
+// Handles the drawing of a link line between 2 points
+//===================================================
+
+class GanttLink {
+ var $iArrowType='';
+ var $ix1,$ix2,$iy1,$iy2;
+ var $iPathType=2,$iPathExtend=15;
+ var $iColor='black',$iWeight=1;
+ var $iArrowSize=ARROW_S2,$iArrowType=ARROWT_SOLID;
+
+ function GanttLink($x1=0,$y1=0,$x2=0,$y2=0) {
+ $this->ix1 = $x1;
+ $this->ix2 = $x2;
+ $this->iy1 = $y1;
+ $this->iy2 = $y2;
+ }
+
+ function SetPos($x1,$y1,$x2,$y2) {
+ $this->ix1 = $x1;
+ $this->ix2 = $x2;
+ $this->iy1 = $y1;
+ $this->iy2 = $y2;
+ }
+
+ function SetPath($aPath) {
+ $this->iPathType = $aPath;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function SetArrow($aSize,$aType=ARROWT_SOLID) {
+ $this->iArrowSize = $aSize;
+ $this->iArrowType = $aType;
+ }
+
+ function SetWeight($aWeight) {
+ $this->iWeight = $aWeight;
+ }
+
+ function Stroke($aImg) {
+ // The way the path for the arrow is constructed is partly based
+ // on some heuristics. This is not an exact science but draws the
+ // path in a way that, for me, makes esthetic sence. For example
+ // if the start and end activities are very close we make a small
+ // detour to endter the target horixontally. If there are more
+ // space between axctivities then no suh detour is made and the
+ // target is "hit" directly vertical. I have tried to keep this
+ // simple. no doubt this could become almost infinitive complex
+ // and have some real AI. Feel free to modify this.
+ // This will no-doubt be tweaked as times go by. One design aim
+ // is to avoid having the user choose what types of arrow
+ // he wants.
+
+ // The arrow is drawn between (x1,y1) to (x2,y2)
+ $x1 = $this->ix1 ;
+ $x2 = $this->ix2 ;
+ $y1 = $this->iy1 ;
+ $y2 = $this->iy2 ;
+
+ // Depending on if the target is below or above we have to
+ // handle thi different.
+ if( $y2 > $y1 ) {
+ $arrowtype = ARROW_DOWN;
+ $midy = round(($y2-$y1)/2+$y1);
+ if( $x2 > $x1 ) {
+ switch ( $this->iPathType ) {
+ case 0:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 1:
+ case 2:
+ case 3:
+ $c = array($x1,$y1,$x2,$y1,$x2,$y2);
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type (='.$this->iPathType .') specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ else {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 2:
+ // Always extend out horizontally a bit from the first point
+ // If we draw a link back in time (end to start) and the bars
+ // are very close we also change the path so it comes in from
+ // the left on the activity
+ $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
+ $x1+$this->iPathExtend,$midy,
+ $x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $y2-$midy < 6 ) {
+ $c = array($x1,$y1,$x1,$midy,
+ $x2-$this->iPathExtend,$midy,
+ $x2-$this->iPathExtend,$y2,
+ $x2,$y2);
+ $arrowtype = ARROW_RIGHT;
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ $arrow = new LinkArrow($x2,$y2,$arrowtype);
+ }
+ else {
+ // Y2 < Y1
+ $arrowtype = ARROW_UP;
+ $midy = round(($y1-$y2)/2+$y2);
+ if( $x2 > $x1 ) {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $midy-$y2 < 8 ) {
+ $arrowtype = ARROW_RIGHT;
+ $c = array($x1,$y1,$x1,$y2,$x2,$y2);
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ break;
+ }
+ }
+ else {
+ switch ( $this->iPathType ) {
+ case 0:
+ case 1:
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ break;
+ case 2:
+ // Always extend out horizontally a bit from the first point
+ $c = array($x1,$y1,$x1+$this->iPathExtend,$y1,
+ $x1+$this->iPathExtend,$midy,
+ $x2,$midy,$x2,$y2);
+ break;
+ case 3:
+ if( $midy-$y2 < 16 ) {
+ $arrowtype = ARROW_RIGHT;
+ $c = array($x1,$y1,$x1,$midy,$x2-$this->iPathExtend,$midy,
+ $x2-$this->iPathExtend,$y2,
+ $x2,$y2);
+ }
+ else {
+ $c = array($x1,$y1,$x1,$midy,$x2,$midy,$x2,$y2);
+ }
+ break;
+ default:
+ JpGraphError::Raise('Internal error: Unknown path type specified for link.');
+ exit(1);
+ break;
+ }
+ }
+ $arrow = new LinkArrow($x2,$y2,$arrowtype);
+ }
+ $aImg->SetColor($this->iColor);
+ $aImg->SetLineWeight($this->iWeight);
+ $aImg->Polygon($c);
+ $aImg->SetLineWeight(1);
+ $arrow->SetColor($this->iColor);
+ $arrow->SetSize($this->iArrowSize);
+ $arrow->SetType($this->iArrowType);
+ $arrow->Stroke($aImg);
+ }
+}
+
+// <EOF>
+?>
diff --git a/includes/jpgraph/jpgraph_gb2312.php b/includes/jpgraph/jpgraph_gb2312.php
new file mode 100644
index 0000000..dc36e59
--- /dev/null
+++ b/includes/jpgraph/jpgraph_gb2312.php
@@ -0,0 +1,1554 @@
+<?php
+//=======================================================================
+// File: JPGRAPH_GB2312.PHP
+// Description: PHP4 Graph Plotting library. Chinese font conversions
+// Created: 2003-05-30
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gb2312.php,v 1.1 2003/05/29 15:59:18 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+
+
+class GB2312toUTF8 {
+// --------------------------------------------------------------------
+// This code table is used to translate GB2312 code (key) to
+// it's corresponding Unicode value (data)
+// --------------------------------------------------------------------
+ var $codetable = array(
+ 8481 => 12288, 8482 => 12289, 8483 => 12290, 8484 => 12539, 8485 => 713,
+ 8486 => 711, 8487 => 168, 8488 => 12291, 8489 => 12293, 8490 => 8213,
+ 8491 => 65374, 8492 => 8214, 8493 => 8230, 8494 => 8216, 8495 => 8217,
+ 8496 => 8220, 8497 => 8221, 8498 => 12308, 8499 => 12309, 8500 => 12296,
+ 8501 => 12297, 8502 => 12298, 8503 => 12299, 8504 => 12300, 8505 => 12301,
+ 8506 => 12302, 8507 => 12303, 8508 => 12310, 8509 => 12311, 8510 => 12304,
+ 8511 => 12305, 8512 => 177, 8513 => 215, 8514 => 247, 8515 => 8758,
+ 8516 => 8743, 8517 => 8744, 8518 => 8721, 8519 => 8719, 8520 => 8746,
+ 8521 => 8745, 8522 => 8712, 8523 => 8759, 8524 => 8730, 8525 => 8869,
+ 8526 => 8741, 8527 => 8736, 8528 => 8978, 8529 => 8857, 8530 => 8747,
+ 8531 => 8750, 8532 => 8801, 8533 => 8780, 8534 => 8776, 8535 => 8765,
+ 8536 => 8733, 8537 => 8800, 8538 => 8814, 8539 => 8815, 8540 => 8804,
+ 8541 => 8805, 8542 => 8734, 8543 => 8757, 8544 => 8756, 8545 => 9794,
+ 8546 => 9792, 8547 => 176, 8548 => 8242, 8549 => 8243, 8550 => 8451,
+ 8551 => 65284, 8552 => 164, 8553 => 65504, 8554 => 65505, 8555 => 8240,
+ 8556 => 167, 8557 => 8470, 8558 => 9734, 8559 => 9733, 8560 => 9675,
+ 8561 => 9679, 8562 => 9678, 8563 => 9671, 8564 => 9670, 8565 => 9633,
+ 8566 => 9632, 8567 => 9651, 8568 => 9650, 8569 => 8251, 8570 => 8594,
+ 8571 => 8592, 8572 => 8593, 8573 => 8595, 8574 => 12307, 8753 => 9352,
+ 8754 => 9353, 8755 => 9354, 8756 => 9355, 8757 => 9356, 8758 => 9357,
+ 8759 => 9358, 8760 => 9359, 8761 => 9360, 8762 => 9361, 8763 => 9362,
+ 8764 => 9363, 8765 => 9364, 8766 => 9365, 8767 => 9366, 8768 => 9367,
+ 8769 => 9368, 8770 => 9369, 8771 => 9370, 8772 => 9371, 8773 => 9332,
+ 8774 => 9333, 8775 => 9334, 8776 => 9335, 8777 => 9336, 8778 => 9337,
+ 8779 => 9338, 8780 => 9339, 8781 => 9340, 8782 => 9341, 8783 => 9342,
+ 8784 => 9343, 8785 => 9344, 8786 => 9345, 8787 => 9346, 8788 => 9347,
+ 8789 => 9348, 8790 => 9349, 8791 => 9350, 8792 => 9351, 8793 => 9312,
+ 8794 => 9313, 8795 => 9314, 8796 => 9315, 8797 => 9316, 8798 => 9317,
+ 8799 => 9318, 8800 => 9319, 8801 => 9320, 8802 => 9321, 8805 => 12832,
+ 8806 => 12833, 8807 => 12834, 8808 => 12835, 8809 => 12836, 8810 => 12837,
+ 8811 => 12838, 8812 => 12839, 8813 => 12840, 8814 => 12841, 8817 => 8544,
+ 8818 => 8545, 8819 => 8546, 8820 => 8547, 8821 => 8548, 8822 => 8549,
+ 8823 => 8550, 8824 => 8551, 8825 => 8552, 8826 => 8553, 8827 => 8554,
+ 8828 => 8555, 8993 => 65281, 8994 => 65282, 8995 => 65283, 8996 => 65509,
+ 8997 => 65285, 8998 => 65286, 8999 => 65287, 9000 => 65288, 9001 => 65289,
+ 9002 => 65290, 9003 => 65291, 9004 => 65292, 9005 => 65293, 9006 => 65294,
+ 9007 => 65295, 9008 => 65296, 9009 => 65297, 9010 => 65298, 9011 => 65299,
+ 9012 => 65300, 9013 => 65301, 9014 => 65302, 9015 => 65303, 9016 => 65304,
+ 9017 => 65305, 9018 => 65306, 9019 => 65307, 9020 => 65308, 9021 => 65309,
+ 9022 => 65310, 9023 => 65311, 9024 => 65312, 9025 => 65313, 9026 => 65314,
+ 9027 => 65315, 9028 => 65316, 9029 => 65317, 9030 => 65318, 9031 => 65319,
+ 9032 => 65320, 9033 => 65321, 9034 => 65322, 9035 => 65323, 9036 => 65324,
+ 9037 => 65325, 9038 => 65326, 9039 => 65327, 9040 => 65328, 9041 => 65329,
+ 9042 => 65330, 9043 => 65331, 9044 => 65332, 9045 => 65333, 9046 => 65334,
+ 9047 => 65335, 9048 => 65336, 9049 => 65337, 9050 => 65338, 9051 => 65339,
+ 9052 => 65340, 9053 => 65341, 9054 => 65342, 9055 => 65343, 9056 => 65344,
+ 9057 => 65345, 9058 => 65346, 9059 => 65347, 9060 => 65348, 9061 => 65349,
+ 9062 => 65350, 9063 => 65351, 9064 => 65352, 9065 => 65353, 9066 => 65354,
+ 9067 => 65355, 9068 => 65356, 9069 => 65357, 9070 => 65358, 9071 => 65359,
+ 9072 => 65360, 9073 => 65361, 9074 => 65362, 9075 => 65363, 9076 => 65364,
+ 9077 => 65365, 9078 => 65366, 9079 => 65367, 9080 => 65368, 9081 => 65369,
+ 9082 => 65370, 9083 => 65371, 9084 => 65372, 9085 => 65373, 9086 => 65507,
+ 9249 => 12353, 9250 => 12354, 9251 => 12355, 9252 => 12356, 9253 => 12357,
+ 9254 => 12358, 9255 => 12359, 9256 => 12360, 9257 => 12361, 9258 => 12362,
+ 9259 => 12363, 9260 => 12364, 9261 => 12365, 9262 => 12366, 9263 => 12367,
+ 9264 => 12368, 9265 => 12369, 9266 => 12370, 9267 => 12371, 9268 => 12372,
+ 9269 => 12373, 9270 => 12374, 9271 => 12375, 9272 => 12376, 9273 => 12377,
+ 9274 => 12378, 9275 => 12379, 9276 => 12380, 9277 => 12381, 9278 => 12382,
+ 9279 => 12383, 9280 => 12384, 9281 => 12385, 9282 => 12386, 9283 => 12387,
+ 9284 => 12388, 9285 => 12389, 9286 => 12390, 9287 => 12391, 9288 => 12392,
+ 9289 => 12393, 9290 => 12394, 9291 => 12395, 9292 => 12396, 9293 => 12397,
+ 9294 => 12398, 9295 => 12399, 9296 => 12400, 9297 => 12401, 9298 => 12402,
+ 9299 => 12403, 9300 => 12404, 9301 => 12405, 9302 => 12406, 9303 => 12407,
+ 9304 => 12408, 9305 => 12409, 9306 => 12410, 9307 => 12411, 9308 => 12412,
+ 9309 => 12413, 9310 => 12414, 9311 => 12415, 9312 => 12416, 9313 => 12417,
+ 9314 => 12418, 9315 => 12419, 9316 => 12420, 9317 => 12421, 9318 => 12422,
+ 9319 => 12423, 9320 => 12424, 9321 => 12425, 9322 => 12426, 9323 => 12427,
+ 9324 => 12428, 9325 => 12429, 9326 => 12430, 9327 => 12431, 9328 => 12432,
+ 9329 => 12433, 9330 => 12434, 9331 => 12435, 9505 => 12449, 9506 => 12450,
+ 9507 => 12451, 9508 => 12452, 9509 => 12453, 9510 => 12454, 9511 => 12455,
+ 9512 => 12456, 9513 => 12457, 9514 => 12458, 9515 => 12459, 9516 => 12460,
+ 9517 => 12461, 9518 => 12462, 9519 => 12463, 9520 => 12464, 9521 => 12465,
+ 9522 => 12466, 9523 => 12467, 9524 => 12468, 9525 => 12469, 9526 => 12470,
+ 9527 => 12471, 9528 => 12472, 9529 => 12473, 9530 => 12474, 9531 => 12475,
+ 9532 => 12476, 9533 => 12477, 9534 => 12478, 9535 => 12479, 9536 => 12480,
+ 9537 => 12481, 9538 => 12482, 9539 => 12483, 9540 => 12484, 9541 => 12485,
+ 9542 => 12486, 9543 => 12487, 9544 => 12488, 9545 => 12489, 9546 => 12490,
+ 9547 => 12491, 9548 => 12492, 9549 => 12493, 9550 => 12494, 9551 => 12495,
+ 9552 => 12496, 9553 => 12497, 9554 => 12498, 9555 => 12499, 9556 => 12500,
+ 9557 => 12501, 9558 => 12502, 9559 => 12503, 9560 => 12504, 9561 => 12505,
+ 9562 => 12506, 9563 => 12507, 9564 => 12508, 9565 => 12509, 9566 => 12510,
+ 9567 => 12511, 9568 => 12512, 9569 => 12513, 9570 => 12514, 9571 => 12515,
+ 9572 => 12516, 9573 => 12517, 9574 => 12518, 9575 => 12519, 9576 => 12520,
+ 9577 => 12521, 9578 => 12522, 9579 => 12523, 9580 => 12524, 9581 => 12525,
+ 9582 => 12526, 9583 => 12527, 9584 => 12528, 9585 => 12529, 9586 => 12530,
+ 9587 => 12531, 9588 => 12532, 9589 => 12533, 9590 => 12534, 9761 => 913,
+ 9762 => 914, 9763 => 915, 9764 => 916, 9765 => 917, 9766 => 918,
+ 9767 => 919, 9768 => 920, 9769 => 921, 9770 => 922, 9771 => 923,
+ 9772 => 924, 9773 => 925, 9774 => 926, 9775 => 927, 9776 => 928,
+ 9777 => 929, 9778 => 931, 9779 => 932, 9780 => 933, 9781 => 934,
+ 9782 => 935, 9783 => 936, 9784 => 937, 9793 => 945, 9794 => 946,
+ 9795 => 947, 9796 => 948, 9797 => 949, 9798 => 950, 9799 => 951,
+ 9800 => 952, 9801 => 953, 9802 => 954, 9803 => 955, 9804 => 956,
+ 9805 => 957, 9806 => 958, 9807 => 959, 9808 => 960, 9809 => 961,
+ 9810 => 963, 9811 => 964, 9812 => 965, 9813 => 966, 9814 => 967,
+ 9815 => 968, 9816 => 969, 10017 => 1040, 10018 => 1041, 10019 => 1042,
+ 10020 => 1043, 10021 => 1044, 10022 => 1045, 10023 => 1025, 10024 => 1046,
+ 10025 => 1047, 10026 => 1048, 10027 => 1049, 10028 => 1050, 10029 => 1051,
+ 10030 => 1052, 10031 => 1053, 10032 => 1054, 10033 => 1055, 10034 => 1056,
+ 10035 => 1057, 10036 => 1058, 10037 => 1059, 10038 => 1060, 10039 => 1061,
+ 10040 => 1062, 10041 => 1063, 10042 => 1064, 10043 => 1065, 10044 => 1066,
+ 10045 => 1067, 10046 => 1068, 10047 => 1069, 10048 => 1070, 10049 => 1071,
+ 10065 => 1072, 10066 => 1073, 10067 => 1074, 10068 => 1075, 10069 => 1076,
+ 10070 => 1077, 10071 => 1105, 10072 => 1078, 10073 => 1079, 10074 => 1080,
+ 10075 => 1081, 10076 => 1082, 10077 => 1083, 10078 => 1084, 10079 => 1085,
+ 10080 => 1086, 10081 => 1087, 10082 => 1088, 10083 => 1089, 10084 => 1090,
+ 10085 => 1091, 10086 => 1092, 10087 => 1093, 10088 => 1094, 10089 => 1095,
+ 10090 => 1096, 10091 => 1097, 10092 => 1098, 10093 => 1099, 10094 => 1100,
+ 10095 => 1101, 10096 => 1102, 10097 => 1103, 10273 => 257, 10274 => 225,
+ 10275 => 462, 10276 => 224, 10277 => 275, 10278 => 233, 10279 => 283,
+ 10280 => 232, 10281 => 299, 10282 => 237, 10283 => 464, 10284 => 236,
+ 10285 => 333, 10286 => 243, 10287 => 466, 10288 => 242, 10289 => 363,
+ 10290 => 250, 10291 => 468, 10292 => 249, 10293 => 470, 10294 => 472,
+ 10295 => 474, 10296 => 476, 10297 => 252, 10298 => 234, 10309 => 12549,
+ 10310 => 12550, 10311 => 12551, 10312 => 12552, 10313 => 12553, 10314 => 12554,
+ 10315 => 12555, 10316 => 12556, 10317 => 12557, 10318 => 12558, 10319 => 12559,
+ 10320 => 12560, 10321 => 12561, 10322 => 12562, 10323 => 12563, 10324 => 12564,
+ 10325 => 12565, 10326 => 12566, 10327 => 12567, 10328 => 12568, 10329 => 12569,
+ 10330 => 12570, 10331 => 12571, 10332 => 12572, 10333 => 12573, 10334 => 12574,
+ 10335 => 12575, 10336 => 12576, 10337 => 12577, 10338 => 12578, 10339 => 12579,
+ 10340 => 12580, 10341 => 12581, 10342 => 12582, 10343 => 12583, 10344 => 12584,
+ 10345 => 12585, 10532 => 9472, 10533 => 9473, 10534 => 9474, 10535 => 9475,
+ 10536 => 9476, 10537 => 9477, 10538 => 9478, 10539 => 9479, 10540 => 9480,
+ 10541 => 9481, 10542 => 9482, 10543 => 9483, 10544 => 9484, 10545 => 9485,
+ 10546 => 9486, 10547 => 9487, 10548 => 9488, 10549 => 9489, 10550 => 9490,
+ 10551 => 9491, 10552 => 9492, 10553 => 9493, 10554 => 9494, 10555 => 9495,
+ 10556 => 9496, 10557 => 9497, 10558 => 9498, 10559 => 9499, 10560 => 9500,
+ 10561 => 9501, 10562 => 9502, 10563 => 9503, 10564 => 9504, 10565 => 9505,
+ 10566 => 9506, 10567 => 9507, 10568 => 9508, 10569 => 9509, 10570 => 9510,
+ 10571 => 9511, 10572 => 9512, 10573 => 9513, 10574 => 9514, 10575 => 9515,
+ 10576 => 9516, 10577 => 9517, 10578 => 9518, 10579 => 9519, 10580 => 9520,
+ 10581 => 9521, 10582 => 9522, 10583 => 9523, 10584 => 9524, 10585 => 9525,
+ 10586 => 9526, 10587 => 9527, 10588 => 9528, 10589 => 9529, 10590 => 9530,
+ 10591 => 9531, 10592 => 9532, 10593 => 9533, 10594 => 9534, 10595 => 9535,
+ 10596 => 9536, 10597 => 9537, 10598 => 9538, 10599 => 9539, 10600 => 9540,
+ 10601 => 9541, 10602 => 9542, 10603 => 9543, 10604 => 9544, 10605 => 9545,
+ 10606 => 9546, 10607 => 9547, 12321 => 21834, 12322 => 38463, 12323 => 22467,
+ 12324 => 25384, 12325 => 21710, 12326 => 21769, 12327 => 21696, 12328 => 30353,
+ 12329 => 30284, 12330 => 34108, 12331 => 30702, 12332 => 33406, 12333 => 30861,
+ 12334 => 29233, 12335 => 38552, 12336 => 38797, 12337 => 27688, 12338 => 23433,
+ 12339 => 20474, 12340 => 25353, 12341 => 26263, 12342 => 23736, 12343 => 33018,
+ 12344 => 26696, 12345 => 32942, 12346 => 26114, 12347 => 30414, 12348 => 20985,
+ 12349 => 25942, 12350 => 29100, 12351 => 32753, 12352 => 34948, 12353 => 20658,
+ 12354 => 22885, 12355 => 25034, 12356 => 28595, 12357 => 33453, 12358 => 25420,
+ 12359 => 25170, 12360 => 21485, 12361 => 21543, 12362 => 31494, 12363 => 20843,
+ 12364 => 30116, 12365 => 24052, 12366 => 25300, 12367 => 36299, 12368 => 38774,
+ 12369 => 25226, 12370 => 32793, 12371 => 22365, 12372 => 38712, 12373 => 32610,
+ 12374 => 29240, 12375 => 30333, 12376 => 26575, 12377 => 30334, 12378 => 25670,
+ 12379 => 20336, 12380 => 36133, 12381 => 25308, 12382 => 31255, 12383 => 26001,
+ 12384 => 29677, 12385 => 25644, 12386 => 25203, 12387 => 33324, 12388 => 39041,
+ 12389 => 26495, 12390 => 29256, 12391 => 25198, 12392 => 25292, 12393 => 20276,
+ 12394 => 29923, 12395 => 21322, 12396 => 21150, 12397 => 32458, 12398 => 37030,
+ 12399 => 24110, 12400 => 26758, 12401 => 27036, 12402 => 33152, 12403 => 32465,
+ 12404 => 26834, 12405 => 30917, 12406 => 34444, 12407 => 38225, 12408 => 20621,
+ 12409 => 35876, 12410 => 33502, 12411 => 32990, 12412 => 21253, 12413 => 35090,
+ 12414 => 21093, 12577 => 34180, 12578 => 38649, 12579 => 20445, 12580 => 22561,
+ 12581 => 39281, 12582 => 23453, 12583 => 25265, 12584 => 25253, 12585 => 26292,
+ 12586 => 35961, 12587 => 40077, 12588 => 29190, 12589 => 26479, 12590 => 30865,
+ 12591 => 24754, 12592 => 21329, 12593 => 21271, 12594 => 36744, 12595 => 32972,
+ 12596 => 36125, 12597 => 38049, 12598 => 20493, 12599 => 29384, 12600 => 22791,
+ 12601 => 24811, 12602 => 28953, 12603 => 34987, 12604 => 22868, 12605 => 33519,
+ 12606 => 26412, 12607 => 31528, 12608 => 23849, 12609 => 32503, 12610 => 29997,
+ 12611 => 27893, 12612 => 36454, 12613 => 36856, 12614 => 36924, 12615 => 40763,
+ 12616 => 27604, 12617 => 37145, 12618 => 31508, 12619 => 24444, 12620 => 30887,
+ 12621 => 34006, 12622 => 34109, 12623 => 27605, 12624 => 27609, 12625 => 27606,
+ 12626 => 24065, 12627 => 24199, 12628 => 30201, 12629 => 38381, 12630 => 25949,
+ 12631 => 24330, 12632 => 24517, 12633 => 36767, 12634 => 22721, 12635 => 33218,
+ 12636 => 36991, 12637 => 38491, 12638 => 38829, 12639 => 36793, 12640 => 32534,
+ 12641 => 36140, 12642 => 25153, 12643 => 20415, 12644 => 21464, 12645 => 21342,
+ 12646 => 36776, 12647 => 36777, 12648 => 36779, 12649 => 36941, 12650 => 26631,
+ 12651 => 24426, 12652 => 33176, 12653 => 34920, 12654 => 40150, 12655 => 24971,
+ 12656 => 21035, 12657 => 30250, 12658 => 24428, 12659 => 25996, 12660 => 28626,
+ 12661 => 28392, 12662 => 23486, 12663 => 25672, 12664 => 20853, 12665 => 20912,
+ 12666 => 26564, 12667 => 19993, 12668 => 31177, 12669 => 39292, 12670 => 28851,
+ 12833 => 30149, 12834 => 24182, 12835 => 29627, 12836 => 33760, 12837 => 25773,
+ 12838 => 25320, 12839 => 38069, 12840 => 27874, 12841 => 21338, 12842 => 21187,
+ 12843 => 25615, 12844 => 38082, 12845 => 31636, 12846 => 20271, 12847 => 24091,
+ 12848 => 33334, 12849 => 33046, 12850 => 33162, 12851 => 28196, 12852 => 27850,
+ 12853 => 39539, 12854 => 25429, 12855 => 21340, 12856 => 21754, 12857 => 34917,
+ 12858 => 22496, 12859 => 19981, 12860 => 24067, 12861 => 27493, 12862 => 31807,
+ 12863 => 37096, 12864 => 24598, 12865 => 25830, 12866 => 29468, 12867 => 35009,
+ 12868 => 26448, 12869 => 25165, 12870 => 36130, 12871 => 30572, 12872 => 36393,
+ 12873 => 37319, 12874 => 24425, 12875 => 33756, 12876 => 34081, 12877 => 39184,
+ 12878 => 21442, 12879 => 34453, 12880 => 27531, 12881 => 24813, 12882 => 24808,
+ 12883 => 28799, 12884 => 33485, 12885 => 33329, 12886 => 20179, 12887 => 27815,
+ 12888 => 34255, 12889 => 25805, 12890 => 31961, 12891 => 27133, 12892 => 26361,
+ 12893 => 33609, 12894 => 21397, 12895 => 31574, 12896 => 20391, 12897 => 20876,
+ 12898 => 27979, 12899 => 23618, 12900 => 36461, 12901 => 25554, 12902 => 21449,
+ 12903 => 33580, 12904 => 33590, 12905 => 26597, 12906 => 30900, 12907 => 25661,
+ 12908 => 23519, 12909 => 23700, 12910 => 24046, 12911 => 35815, 12912 => 25286,
+ 12913 => 26612, 12914 => 35962, 12915 => 25600, 12916 => 25530, 12917 => 34633,
+ 12918 => 39307, 12919 => 35863, 12920 => 32544, 12921 => 38130, 12922 => 20135,
+ 12923 => 38416, 12924 => 39076, 12925 => 26124, 12926 => 29462, 13089 => 22330,
+ 13090 => 23581, 13091 => 24120, 13092 => 38271, 13093 => 20607, 13094 => 32928,
+ 13095 => 21378, 13096 => 25950, 13097 => 30021, 13098 => 21809, 13099 => 20513,
+ 13100 => 36229, 13101 => 25220, 13102 => 38046, 13103 => 26397, 13104 => 22066,
+ 13105 => 28526, 13106 => 24034, 13107 => 21557, 13108 => 28818, 13109 => 36710,
+ 13110 => 25199, 13111 => 25764, 13112 => 25507, 13113 => 24443, 13114 => 28552,
+ 13115 => 37108, 13116 => 33251, 13117 => 36784, 13118 => 23576, 13119 => 26216,
+ 13120 => 24561, 13121 => 27785, 13122 => 38472, 13123 => 36225, 13124 => 34924,
+ 13125 => 25745, 13126 => 31216, 13127 => 22478, 13128 => 27225, 13129 => 25104,
+ 13130 => 21576, 13131 => 20056, 13132 => 31243, 13133 => 24809, 13134 => 28548,
+ 13135 => 35802, 13136 => 25215, 13137 => 36894, 13138 => 39563, 13139 => 31204,
+13140 => 21507, 13141 => 30196, 13142 => 25345, 13143 => 21273, 13144 => 27744,
+13145 => 36831, 13146 => 24347, 13147 => 39536, 13148 => 32827, 13149 => 40831,
+13150 => 20360, 13151 => 23610, 13152 => 36196, 13153 => 32709, 13154 => 26021,
+13155 => 28861, 13156 => 20805, 13157 => 20914, 13158 => 34411, 13159 => 23815,
+13160 => 23456, 13161 => 25277, 13162 => 37228, 13163 => 30068, 13164 => 36364,
+13165 => 31264, 13166 => 24833, 13167 => 31609, 13168 => 20167, 13169 => 32504,
+13170 => 30597, 13171 => 19985, 13172 => 33261, 13173 => 21021, 13174 => 20986,
+13175 => 27249, 13176 => 21416, 13177 => 36487, 13178 => 38148, 13179 => 38607,
+13180 => 28353, 13181 => 38500, 13182 => 26970, 13345 => 30784, 13346 => 20648,
+13347 => 30679, 13348 => 25616, 13349 => 35302, 13350 => 22788, 13351 => 25571,
+13352 => 24029, 13353 => 31359, 13354 => 26941, 13355 => 20256, 13356 => 33337,
+13357 => 21912, 13358 => 20018, 13359 => 30126, 13360 => 31383, 13361 => 24162,
+13362 => 24202, 13363 => 38383, 13364 => 21019, 13365 => 21561, 13366 => 28810,
+13367 => 25462, 13368 => 38180, 13369 => 22402, 13370 => 26149, 13371 => 26943,
+13372 => 37255, 13373 => 21767, 13374 => 28147, 13375 => 32431, 13376 => 34850,
+13377 => 25139, 13378 => 32496, 13379 => 30133, 13380 => 33576, 13381 => 30913,
+13382 => 38604, 13383 => 36766, 13384 => 24904, 13385 => 29943, 13386 => 35789,
+13387 => 27492, 13388 => 21050, 13389 => 36176, 13390 => 27425, 13391 => 32874,
+13392 => 33905, 13393 => 22257, 13394 => 21254, 13395 => 20174, 13396 => 19995,
+13397 => 20945, 13398 => 31895, 13399 => 37259, 13400 => 31751, 13401 => 20419,
+13402 => 36479, 13403 => 31713, 13404 => 31388, 13405 => 25703, 13406 => 23828,
+13407 => 20652, 13408 => 33030, 13409 => 30209, 13410 => 31929, 13411 => 28140,
+13412 => 32736, 13413 => 26449, 13414 => 23384, 13415 => 23544, 13416 => 30923,
+13417 => 25774, 13418 => 25619, 13419 => 25514, 13420 => 25387, 13421 => 38169,
+13422 => 25645, 13423 => 36798, 13424 => 31572, 13425 => 30249, 13426 => 25171,
+13427 => 22823, 13428 => 21574, 13429 => 27513, 13430 => 20643, 13431 => 25140,
+13432 => 24102, 13433 => 27526, 13434 => 20195, 13435 => 36151, 13436 => 34955,
+13437 => 24453, 13438 => 36910, 13601 => 24608, 13602 => 32829, 13603 => 25285,
+13604 => 20025, 13605 => 21333, 13606 => 37112, 13607 => 25528, 13608 => 32966,
+13609 => 26086, 13610 => 27694, 13611 => 20294, 13612 => 24814, 13613 => 28129,
+13614 => 35806, 13615 => 24377, 13616 => 34507, 13617 => 24403, 13618 => 25377,
+13619 => 20826, 13620 => 33633, 13621 => 26723, 13622 => 20992, 13623 => 25443,
+13624 => 36424, 13625 => 20498, 13626 => 23707, 13627 => 31095, 13628 => 23548,
+13629 => 21040, 13630 => 31291, 13631 => 24764, 13632 => 36947, 13633 => 30423,
+13634 => 24503, 13635 => 24471, 13636 => 30340, 13637 => 36460, 13638 => 28783,
+13639 => 30331, 13640 => 31561, 13641 => 30634, 13642 => 20979, 13643 => 37011,
+13644 => 22564, 13645 => 20302, 13646 => 28404, 13647 => 36842, 13648 => 25932,
+13649 => 31515, 13650 => 29380, 13651 => 28068, 13652 => 32735, 13653 => 23265,
+13654 => 25269, 13655 => 24213, 13656 => 22320, 13657 => 33922, 13658 => 31532,
+13659 => 24093, 13660 => 24351, 13661 => 36882, 13662 => 32532, 13663 => 39072,
+13664 => 25474, 13665 => 28359, 13666 => 30872, 13667 => 28857, 13668 => 20856,
+13669 => 38747, 13670 => 22443, 13671 => 30005, 13672 => 20291, 13673 => 30008,
+13674 => 24215, 13675 => 24806, 13676 => 22880, 13677 => 28096, 13678 => 27583,
+13679 => 30857, 13680 => 21500, 13681 => 38613, 13682 => 20939, 13683 => 20993,
+13684 => 25481, 13685 => 21514, 13686 => 38035, 13687 => 35843, 13688 => 36300,
+13689 => 29241, 13690 => 30879, 13691 => 34678, 13692 => 36845, 13693 => 35853,
+13694 => 21472, 13857 => 19969, 13858 => 30447, 13859 => 21486, 13860 => 38025,
+13861 => 39030, 13862 => 40718, 13863 => 38189, 13864 => 23450, 13865 => 35746,
+13866 => 20002, 13867 => 19996, 13868 => 20908, 13869 => 33891, 13870 => 25026,
+13871 => 21160, 13872 => 26635, 13873 => 20375, 13874 => 24683, 13875 => 20923,
+13876 => 27934, 13877 => 20828, 13878 => 25238, 13879 => 26007, 13880 => 38497,
+13881 => 35910, 13882 => 36887, 13883 => 30168, 13884 => 37117, 13885 => 30563,
+13886 => 27602, 13887 => 29322, 13888 => 29420, 13889 => 35835, 13890 => 22581,
+13891 => 30585, 13892 => 36172, 13893 => 26460, 13894 => 38208, 13895 => 32922,
+13896 => 24230, 13897 => 28193, 13898 => 22930, 13899 => 31471, 13900 => 30701,
+13901 => 38203, 13902 => 27573, 13903 => 26029, 13904 => 32526, 13905 => 22534,
+13906 => 20817, 13907 => 38431, 13908 => 23545, 13909 => 22697, 13910 => 21544,
+13911 => 36466, 13912 => 25958, 13913 => 39039, 13914 => 22244, 13915 => 38045,
+13916 => 30462, 13917 => 36929, 13918 => 25479, 13919 => 21702, 13920 => 22810,
+13921 => 22842, 13922 => 22427, 13923 => 36530, 13924 => 26421, 13925 => 36346,
+13926 => 33333, 13927 => 21057, 13928 => 24816, 13929 => 22549, 13930 => 34558,
+13931 => 23784, 13932 => 40517, 13933 => 20420, 13934 => 39069, 13935 => 35769,
+13936 => 23077, 13937 => 24694, 13938 => 21380, 13939 => 25212, 13940 => 36943,
+13941 => 37122, 13942 => 39295, 13943 => 24681, 13944 => 32780, 13945 => 20799,
+13946 => 32819, 13947 => 23572, 13948 => 39285, 13949 => 27953, 13950 => 20108,
+14113 => 36144, 14114 => 21457, 14115 => 32602, 14116 => 31567, 14117 => 20240,
+14118 => 20047, 14119 => 38400, 14120 => 27861, 14121 => 29648, 14122 => 34281,
+14123 => 24070, 14124 => 30058, 14125 => 32763, 14126 => 27146, 14127 => 30718,
+14128 => 38034, 14129 => 32321, 14130 => 20961, 14131 => 28902, 14132 => 21453,
+14133 => 36820, 14134 => 33539, 14135 => 36137, 14136 => 29359, 14137 => 39277,
+14138 => 27867, 14139 => 22346, 14140 => 33459, 14141 => 26041, 14142 => 32938,
+14143 => 25151, 14144 => 38450, 14145 => 22952, 14146 => 20223, 14147 => 35775,
+14148 => 32442, 14149 => 25918, 14150 => 33778, 14151 => 38750, 14152 => 21857,
+14153 => 39134, 14154 => 32933, 14155 => 21290, 14156 => 35837, 14157 => 21536,
+14158 => 32954, 14159 => 24223, 14160 => 27832, 14161 => 36153, 14162 => 33452,
+14163 => 37210, 14164 => 21545, 14165 => 27675, 14166 => 20998, 14167 => 32439,
+14168 => 22367, 14169 => 28954, 14170 => 27774, 14171 => 31881, 14172 => 22859,
+14173 => 20221, 14174 => 24575, 14175 => 24868, 14176 => 31914, 14177 => 20016,
+14178 => 23553, 14179 => 26539, 14180 => 34562, 14181 => 23792, 14182 => 38155,
+14183 => 39118, 14184 => 30127, 14185 => 28925, 14186 => 36898, 14187 => 20911,
+14188 => 32541, 14189 => 35773, 14190 => 22857, 14191 => 20964, 14192 => 20315,
+14193 => 21542, 14194 => 22827, 14195 => 25975, 14196 => 32932, 14197 => 23413,
+14198 => 25206, 14199 => 25282, 14200 => 36752, 14201 => 24133, 14202 => 27679,
+14203 => 31526, 14204 => 20239, 14205 => 20440, 14206 => 26381, 14369 => 28014,
+14370 => 28074, 14371 => 31119, 14372 => 34993, 14373 => 24343, 14374 => 29995,
+14375 => 25242, 14376 => 36741, 14377 => 20463, 14378 => 37340, 14379 => 26023,
+14380 => 33071, 14381 => 33105, 14382 => 24220, 14383 => 33104, 14384 => 36212,
+14385 => 21103, 14386 => 35206, 14387 => 36171, 14388 => 22797, 14389 => 20613,
+14390 => 20184, 14391 => 38428, 14392 => 29238, 14393 => 33145, 14394 => 36127,
+14395 => 23500, 14396 => 35747, 14397 => 38468, 14398 => 22919, 14399 => 32538,
+14400 => 21648, 14401 => 22134, 14402 => 22030, 14403 => 35813, 14404 => 25913,
+14405 => 27010, 14406 => 38041, 14407 => 30422, 14408 => 28297, 14409 => 24178,
+14410 => 29976, 14411 => 26438, 14412 => 26577, 14413 => 31487, 14414 => 32925,
+14415 => 36214, 14416 => 24863, 14417 => 31174, 14418 => 25954, 14419 => 36195,
+14420 => 20872, 14421 => 21018, 14422 => 38050, 14423 => 32568, 14424 => 32923,
+14425 => 32434, 14426 => 23703, 14427 => 28207, 14428 => 26464, 14429 => 31705,
+14430 => 30347, 14431 => 39640, 14432 => 33167, 14433 => 32660, 14434 => 31957,
+14435 => 25630, 14436 => 38224, 14437 => 31295, 14438 => 21578, 14439 => 21733,
+14440 => 27468, 14441 => 25601, 14442 => 25096, 14443 => 40509, 14444 => 33011,
+14445 => 30105, 14446 => 21106, 14447 => 38761, 14448 => 33883, 14449 => 26684,
+14450 => 34532, 14451 => 38401, 14452 => 38548, 14453 => 38124, 14454 => 20010,
+14455 => 21508, 14456 => 32473, 14457 => 26681, 14458 => 36319, 14459 => 32789,
+14460 => 26356, 14461 => 24218, 14462 => 32697, 14625 => 22466, 14626 => 32831,
+14627 => 26775, 14628 => 24037, 14629 => 25915, 14630 => 21151, 14631 => 24685,
+14632 => 40858, 14633 => 20379, 14634 => 36524, 14635 => 20844, 14636 => 23467,
+14637 => 24339, 14638 => 24041, 14639 => 27742, 14640 => 25329, 14641 => 36129,
+14642 => 20849, 14643 => 38057, 14644 => 21246, 14645 => 27807, 14646 => 33503,
+14647 => 29399, 14648 => 22434, 14649 => 26500, 14650 => 36141, 14651 => 22815,
+14652 => 36764, 14653 => 33735, 14654 => 21653, 14655 => 31629, 14656 => 20272,
+14657 => 27837, 14658 => 23396, 14659 => 22993, 14660 => 40723, 14661 => 21476,
+14662 => 34506, 14663 => 39592, 14664 => 35895, 14665 => 32929, 14666 => 25925,
+14667 => 39038, 14668 => 22266, 14669 => 38599, 14670 => 21038, 14671 => 29916,
+14672 => 21072, 14673 => 23521, 14674 => 25346, 14675 => 35074, 14676 => 20054,
+14677 => 25296, 14678 => 24618, 14679 => 26874, 14680 => 20851, 14681 => 23448,
+14682 => 20896, 14683 => 35266, 14684 => 31649, 14685 => 39302, 14686 => 32592,
+14687 => 24815, 14688 => 28748, 14689 => 36143, 14690 => 20809, 14691 => 24191,
+14692 => 36891, 14693 => 29808, 14694 => 35268, 14695 => 22317, 14696 => 30789,
+14697 => 24402, 14698 => 40863, 14699 => 38394, 14700 => 36712, 14701 => 39740,
+14702 => 35809, 14703 => 30328, 14704 => 26690, 14705 => 26588, 14706 => 36330,
+14707 => 36149, 14708 => 21053, 14709 => 36746, 14710 => 28378, 14711 => 26829,
+14712 => 38149, 14713 => 37101, 14714 => 22269, 14715 => 26524, 14716 => 35065,
+14717 => 36807, 14718 => 21704, 14881 => 39608, 14882 => 23401, 14883 => 28023,
+14884 => 27686, 14885 => 20133, 14886 => 23475, 14887 => 39559, 14888 => 37219,
+14889 => 25000, 14890 => 37039, 14891 => 38889, 14892 => 21547, 14893 => 28085,
+14894 => 23506, 14895 => 20989, 14896 => 21898, 14897 => 32597, 14898 => 32752,
+14899 => 25788, 14900 => 25421, 14901 => 26097, 14902 => 25022, 14903 => 24717,
+14904 => 28938, 14905 => 27735, 14906 => 27721, 14907 => 22831, 14908 => 26477,
+14909 => 33322, 14910 => 22741, 14911 => 22158, 14912 => 35946, 14913 => 27627,
+14914 => 37085, 14915 => 22909, 14916 => 32791, 14917 => 21495, 14918 => 28009,
+14919 => 21621, 14920 => 21917, 14921 => 33655, 14922 => 33743, 14923 => 26680,
+14924 => 31166, 14925 => 21644, 14926 => 20309, 14927 => 21512, 14928 => 30418,
+14929 => 35977, 14930 => 38402, 14931 => 27827, 14932 => 28088, 14933 => 36203,
+14934 => 35088, 14935 => 40548, 14936 => 36154, 14937 => 22079, 14938 => 40657,
+14939 => 30165, 14940 => 24456, 14941 => 29408, 14942 => 24680, 14943 => 21756,
+14944 => 20136, 14945 => 27178, 14946 => 34913, 14947 => 24658, 14948 => 36720,
+14949 => 21700, 14950 => 28888, 14951 => 34425, 14952 => 40511, 14953 => 27946,
+14954 => 23439, 14955 => 24344, 14956 => 32418, 14957 => 21897, 14958 => 20399,
+14959 => 29492, 14960 => 21564, 14961 => 21402, 14962 => 20505, 14963 => 21518,
+14964 => 21628, 14965 => 20046, 14966 => 24573, 14967 => 29786, 14968 => 22774,
+14969 => 33899, 14970 => 32993, 14971 => 34676, 14972 => 29392, 14973 => 31946,
+14974 => 28246, 15137 => 24359, 15138 => 34382, 15139 => 21804, 15140 => 25252,
+15141 => 20114, 15142 => 27818, 15143 => 25143, 15144 => 33457, 15145 => 21719,
+15146 => 21326, 15147 => 29502, 15148 => 28369, 15149 => 30011, 15150 => 21010,
+15151 => 21270, 15152 => 35805, 15153 => 27088, 15154 => 24458, 15155 => 24576,
+15156 => 28142, 15157 => 22351, 15158 => 27426, 15159 => 29615, 15160 => 26707,
+15161 => 36824, 15162 => 32531, 15163 => 25442, 15164 => 24739, 15165 => 21796,
+15166 => 30186, 15167 => 35938, 15168 => 28949, 15169 => 28067, 15170 => 23462,
+15171 => 24187, 15172 => 33618, 15173 => 24908, 15174 => 40644, 15175 => 30970,
+15176 => 34647, 15177 => 31783, 15178 => 30343, 15179 => 20976, 15180 => 24822,
+15181 => 29004, 15182 => 26179, 15183 => 24140, 15184 => 24653, 15185 => 35854,
+15186 => 28784, 15187 => 25381, 15188 => 36745, 15189 => 24509, 15190 => 24674,
+15191 => 34516, 15192 => 22238, 15193 => 27585, 15194 => 24724, 15195 => 24935,
+15196 => 21321, 15197 => 24800, 15198 => 26214, 15199 => 36159, 15200 => 31229,
+15201 => 20250, 15202 => 28905, 15203 => 27719, 15204 => 35763, 15205 => 35826,
+15206 => 32472, 15207 => 33636, 15208 => 26127, 15209 => 23130, 15210 => 39746,
+15211 => 27985, 15212 => 28151, 15213 => 35905, 15214 => 27963, 15215 => 20249,
+15216 => 28779, 15217 => 33719, 15218 => 25110, 15219 => 24785, 15220 => 38669,
+15221 => 36135, 15222 => 31096, 15223 => 20987, 15224 => 22334, 15225 => 22522,
+15226 => 26426, 15227 => 30072, 15228 => 31293, 15229 => 31215, 15230 => 31637,
+15393 => 32908, 15394 => 39269, 15395 => 36857, 15396 => 28608, 15397 => 35749,
+15398 => 40481, 15399 => 23020, 15400 => 32489, 15401 => 32521, 15402 => 21513,
+15403 => 26497, 15404 => 26840, 15405 => 36753, 15406 => 31821, 15407 => 38598,
+15408 => 21450, 15409 => 24613, 15410 => 30142, 15411 => 27762, 15412 => 21363,
+15413 => 23241, 15414 => 32423, 15415 => 25380, 15416 => 20960, 15417 => 33034,
+15418 => 24049, 15419 => 34015, 15420 => 25216, 15421 => 20864, 15422 => 23395,
+15423 => 20238, 15424 => 31085, 15425 => 21058, 15426 => 24760, 15427 => 27982,
+15428 => 23492, 15429 => 23490, 15430 => 35745, 15431 => 35760, 15432 => 26082,
+15433 => 24524, 15434 => 38469, 15435 => 22931, 15436 => 32487, 15437 => 32426,
+15438 => 22025, 15439 => 26551, 15440 => 22841, 15441 => 20339, 15442 => 23478,
+15443 => 21152, 15444 => 33626, 15445 => 39050, 15446 => 36158, 15447 => 30002,
+15448 => 38078, 15449 => 20551, 15450 => 31292, 15451 => 20215, 15452 => 26550,
+15453 => 39550, 15454 => 23233, 15455 => 27516, 15456 => 30417, 15457 => 22362,
+15458 => 23574, 15459 => 31546, 15460 => 38388, 15461 => 29006, 15462 => 20860,
+15463 => 32937, 15464 => 33392, 15465 => 22904, 15466 => 32516, 15467 => 33575,
+15468 => 26816, 15469 => 26604, 15470 => 30897, 15471 => 30839, 15472 => 25315,
+15473 => 25441, 15474 => 31616, 15475 => 20461, 15476 => 21098, 15477 => 20943,
+15478 => 33616, 15479 => 27099, 15480 => 37492, 15481 => 36341, 15482 => 36145,
+15483 => 35265, 15484 => 38190, 15485 => 31661, 15486 => 20214, 15649 => 20581,
+15650 => 33328, 15651 => 21073, 15652 => 39279, 15653 => 28176, 15654 => 28293,
+15655 => 28071, 15656 => 24314, 15657 => 20725, 15658 => 23004, 15659 => 23558,
+15660 => 27974, 15661 => 27743, 15662 => 30086, 15663 => 33931, 15664 => 26728,
+15665 => 22870, 15666 => 35762, 15667 => 21280, 15668 => 37233, 15669 => 38477,
+15670 => 34121, 15671 => 26898, 15672 => 30977, 15673 => 28966, 15674 => 33014,
+15675 => 20132, 15676 => 37066, 15677 => 27975, 15678 => 39556, 15679 => 23047,
+15680 => 22204, 15681 => 25605, 15682 => 38128, 15683 => 30699, 15684 => 20389,
+15685 => 33050, 15686 => 29409, 15687 => 35282, 15688 => 39290, 15689 => 32564,
+15690 => 32478, 15691 => 21119, 15692 => 25945, 15693 => 37237, 15694 => 36735,
+15695 => 36739, 15696 => 21483, 15697 => 31382, 15698 => 25581, 15699 => 25509,
+15700 => 30342, 15701 => 31224, 15702 => 34903, 15703 => 38454, 15704 => 25130,
+15705 => 21163, 15706 => 33410, 15707 => 26708, 15708 => 26480, 15709 => 25463,
+15710 => 30571, 15711 => 31469, 15712 => 27905, 15713 => 32467, 15714 => 35299,
+15715 => 22992, 15716 => 25106, 15717 => 34249, 15718 => 33445, 15719 => 30028,
+15720 => 20511, 15721 => 20171, 15722 => 30117, 15723 => 35819, 15724 => 23626,
+15725 => 24062, 15726 => 31563, 15727 => 26020, 15728 => 37329, 15729 => 20170,
+15730 => 27941, 15731 => 35167, 15732 => 32039, 15733 => 38182, 15734 => 20165,
+15735 => 35880, 15736 => 36827, 15737 => 38771, 15738 => 26187, 15739 => 31105,
+15740 => 36817, 15741 => 28908, 15742 => 28024, 15905 => 23613, 15906 => 21170,
+15907 => 33606, 15908 => 20834, 15909 => 33550, 15910 => 30555, 15911 => 26230,
+15912 => 40120, 15913 => 20140, 15914 => 24778, 15915 => 31934, 15916 => 31923,
+15917 => 32463, 15918 => 20117, 15919 => 35686, 15920 => 26223, 15921 => 39048,
+15922 => 38745, 15923 => 22659, 15924 => 25964, 15925 => 38236, 15926 => 24452,
+15927 => 30153, 15928 => 38742, 15929 => 31455, 15930 => 31454, 15931 => 20928,
+15932 => 28847, 15933 => 31384, 15934 => 25578, 15935 => 31350, 15936 => 32416,
+15937 => 29590, 15938 => 38893, 15939 => 20037, 15940 => 28792, 15941 => 20061,
+15942 => 37202, 15943 => 21417, 15944 => 25937, 15945 => 26087, 15946 => 33276,
+15947 => 33285, 15948 => 21646, 15949 => 23601, 15950 => 30106, 15951 => 38816,
+15952 => 25304, 15953 => 29401, 15954 => 30141, 15955 => 23621, 15956 => 39545,
+15957 => 33738, 15958 => 23616, 15959 => 21632, 15960 => 30697, 15961 => 20030,
+15962 => 27822, 15963 => 32858, 15964 => 25298, 15965 => 25454, 15966 => 24040,
+15967 => 20855, 15968 => 36317, 15969 => 36382, 15970 => 38191, 15971 => 20465,
+15972 => 21477, 15973 => 24807, 15974 => 28844, 15975 => 21095, 15976 => 25424,
+15977 => 40515, 15978 => 23071, 15979 => 20518, 15980 => 30519, 15981 => 21367,
+15982 => 32482, 15983 => 25733, 15984 => 25899, 15985 => 25225, 15986 => 25496,
+15987 => 20500, 15988 => 29237, 15989 => 35273, 15990 => 20915, 15991 => 35776,
+15992 => 32477, 15993 => 22343, 15994 => 33740, 15995 => 38055, 15996 => 20891,
+15997 => 21531, 15998 => 23803, 16161 => 20426, 16162 => 31459, 16163 => 27994,
+16164 => 37089, 16165 => 39567, 16166 => 21888, 16167 => 21654, 16168 => 21345,
+16169 => 21679, 16170 => 24320, 16171 => 25577, 16172 => 26999, 16173 => 20975,
+16174 => 24936, 16175 => 21002, 16176 => 22570, 16177 => 21208, 16178 => 22350,
+16179 => 30733, 16180 => 30475, 16181 => 24247, 16182 => 24951, 16183 => 31968,
+16184 => 25179, 16185 => 25239, 16186 => 20130, 16187 => 28821, 16188 => 32771,
+16189 => 25335, 16190 => 28900, 16191 => 38752, 16192 => 22391, 16193 => 33499,
+16194 => 26607, 16195 => 26869, 16196 => 30933, 16197 => 39063, 16198 => 31185,
+16199 => 22771, 16200 => 21683, 16201 => 21487, 16202 => 28212, 16203 => 20811,
+16204 => 21051, 16205 => 23458, 16206 => 35838, 16207 => 32943, 16208 => 21827,
+16209 => 22438, 16210 => 24691, 16211 => 22353, 16212 => 21549, 16213 => 31354,
+16214 => 24656, 16215 => 23380, 16216 => 25511, 16217 => 25248, 16218 => 21475,
+16219 => 25187, 16220 => 23495, 16221 => 26543, 16222 => 21741, 16223 => 31391,
+16224 => 33510, 16225 => 37239, 16226 => 24211, 16227 => 35044, 16228 => 22840,
+16229 => 22446, 16230 => 25358, 16231 => 36328, 16232 => 33007, 16233 => 22359,
+16234 => 31607, 16235 => 20393, 16236 => 24555, 16237 => 23485, 16238 => 27454,
+16239 => 21281, 16240 => 31568, 16241 => 29378, 16242 => 26694, 16243 => 30719,
+16244 => 30518, 16245 => 26103, 16246 => 20917, 16247 => 20111, 16248 => 30420,
+16249 => 23743, 16250 => 31397, 16251 => 33909, 16252 => 22862, 16253 => 39745,
+16254 => 20608, 16417 => 39304, 16418 => 24871, 16419 => 28291, 16420 => 22372,
+16421 => 26118, 16422 => 25414, 16423 => 22256, 16424 => 25324, 16425 => 25193,
+16426 => 24275, 16427 => 38420, 16428 => 22403, 16429 => 25289, 16430 => 21895,
+16431 => 34593, 16432 => 33098, 16433 => 36771, 16434 => 21862, 16435 => 33713,
+16436 => 26469, 16437 => 36182, 16438 => 34013, 16439 => 23146, 16440 => 26639,
+16441 => 25318, 16442 => 31726, 16443 => 38417, 16444 => 20848, 16445 => 28572,
+16446 => 35888, 16447 => 25597, 16448 => 35272, 16449 => 25042, 16450 => 32518,
+16451 => 28866, 16452 => 28389, 16453 => 29701, 16454 => 27028, 16455 => 29436,
+16456 => 24266, 16457 => 37070, 16458 => 26391, 16459 => 28010, 16460 => 25438,
+16461 => 21171, 16462 => 29282, 16463 => 32769, 16464 => 20332, 16465 => 23013,
+16466 => 37226, 16467 => 28889, 16468 => 28061, 16469 => 21202, 16470 => 20048,
+16471 => 38647, 16472 => 38253, 16473 => 34174, 16474 => 30922, 16475 => 32047,
+16476 => 20769, 16477 => 22418, 16478 => 25794, 16479 => 32907, 16480 => 31867,
+16481 => 27882, 16482 => 26865, 16483 => 26974, 16484 => 20919, 16485 => 21400,
+16486 => 26792, 16487 => 29313, 16488 => 40654, 16489 => 31729, 16490 => 29432,
+16491 => 31163, 16492 => 28435, 16493 => 29702, 16494 => 26446, 16495 => 37324,
+16496 => 40100, 16497 => 31036, 16498 => 33673, 16499 => 33620, 16500 => 21519,
+16501 => 26647, 16502 => 20029, 16503 => 21385, 16504 => 21169, 16505 => 30782,
+16506 => 21382, 16507 => 21033, 16508 => 20616, 16509 => 20363, 16510 => 20432,
+16673 => 30178, 16674 => 31435, 16675 => 31890, 16676 => 27813, 16677 => 38582,
+16678 => 21147, 16679 => 29827, 16680 => 21737, 16681 => 20457, 16682 => 32852,
+16683 => 33714, 16684 => 36830, 16685 => 38256, 16686 => 24265, 16687 => 24604,
+16688 => 28063, 16689 => 24088, 16690 => 25947, 16691 => 33080, 16692 => 38142,
+16693 => 24651, 16694 => 28860, 16695 => 32451, 16696 => 31918, 16697 => 20937,
+16698 => 26753, 16699 => 31921, 16700 => 33391, 16701 => 20004, 16702 => 36742,
+16703 => 37327, 16704 => 26238, 16705 => 20142, 16706 => 35845, 16707 => 25769,
+16708 => 32842, 16709 => 20698, 16710 => 30103, 16711 => 29134, 16712 => 23525,
+16713 => 36797, 16714 => 28518, 16715 => 20102, 16716 => 25730, 16717 => 38243,
+16718 => 24278, 16719 => 26009, 16720 => 21015, 16721 => 35010, 16722 => 28872,
+16723 => 21155, 16724 => 29454, 16725 => 29747, 16726 => 26519, 16727 => 30967,
+16728 => 38678, 16729 => 20020, 16730 => 37051, 16731 => 40158, 16732 => 28107,
+16733 => 20955, 16734 => 36161, 16735 => 21533, 16736 => 25294, 16737 => 29618,
+16738 => 33777, 16739 => 38646, 16740 => 40836, 16741 => 38083, 16742 => 20278,
+16743 => 32666, 16744 => 20940, 16745 => 28789, 16746 => 38517, 16747 => 23725,
+16748 => 39046, 16749 => 21478, 16750 => 20196, 16751 => 28316, 16752 => 29705,
+16753 => 27060, 16754 => 30827, 16755 => 39311, 16756 => 30041, 16757 => 21016,
+16758 => 30244, 16759 => 27969, 16760 => 26611, 16761 => 20845, 16762 => 40857,
+16763 => 32843, 16764 => 21657, 16765 => 31548, 16766 => 31423, 16929 => 38534,
+16930 => 22404, 16931 => 25314, 16932 => 38471, 16933 => 27004, 16934 => 23044,
+16935 => 25602, 16936 => 31699, 16937 => 28431, 16938 => 38475, 16939 => 33446,
+16940 => 21346, 16941 => 39045, 16942 => 24208, 16943 => 28809, 16944 => 25523,
+16945 => 21348, 16946 => 34383, 16947 => 40065, 16948 => 40595, 16949 => 30860,
+16950 => 38706, 16951 => 36335, 16952 => 36162, 16953 => 40575, 16954 => 28510,
+16955 => 31108, 16956 => 24405, 16957 => 38470, 16958 => 25134, 16959 => 39540,
+16960 => 21525, 16961 => 38109, 16962 => 20387, 16963 => 26053, 16964 => 23653,
+16965 => 23649, 16966 => 32533, 16967 => 34385, 16968 => 27695, 16969 => 24459,
+16970 => 29575, 16971 => 28388, 16972 => 32511, 16973 => 23782, 16974 => 25371,
+16975 => 23402, 16976 => 28390, 16977 => 21365, 16978 => 20081, 16979 => 25504,
+16980 => 30053, 16981 => 25249, 16982 => 36718, 16983 => 20262, 16984 => 20177,
+16985 => 27814, 16986 => 32438, 16987 => 35770, 16988 => 33821, 16989 => 34746,
+16990 => 32599, 16991 => 36923, 16992 => 38179, 16993 => 31657, 16994 => 39585,
+16995 => 35064, 16996 => 33853, 16997 => 27931, 16998 => 39558, 16999 => 32476,
+17000 => 22920, 17001 => 40635, 17002 => 29595, 17003 => 30721, 17004 => 34434,
+17005 => 39532, 17006 => 39554, 17007 => 22043, 17008 => 21527, 17009 => 22475,
+17010 => 20080, 17011 => 40614, 17012 => 21334, 17013 => 36808, 17014 => 33033,
+17015 => 30610, 17016 => 39314, 17017 => 34542, 17018 => 28385, 17019 => 34067,
+17020 => 26364, 17021 => 24930, 17022 => 28459, 17185 => 35881, 17186 => 33426,
+17187 => 33579, 17188 => 30450, 17189 => 27667, 17190 => 24537, 17191 => 33725,
+17192 => 29483, 17193 => 33541, 17194 => 38170, 17195 => 27611, 17196 => 30683,
+17197 => 38086, 17198 => 21359, 17199 => 33538, 17200 => 20882, 17201 => 24125,
+17202 => 35980, 17203 => 36152, 17204 => 20040, 17205 => 29611, 17206 => 26522,
+17207 => 26757, 17208 => 37238, 17209 => 38665, 17210 => 29028, 17211 => 27809,
+17212 => 30473, 17213 => 23186, 17214 => 38209, 17215 => 27599, 17216 => 32654,
+17217 => 26151, 17218 => 23504, 17219 => 22969, 17220 => 23194, 17221 => 38376,
+17222 => 38391, 17223 => 20204, 17224 => 33804, 17225 => 33945, 17226 => 27308,
+17227 => 30431, 17228 => 38192, 17229 => 29467, 17230 => 26790, 17231 => 23391,
+17232 => 30511, 17233 => 37274, 17234 => 38753, 17235 => 31964, 17236 => 36855,
+17237 => 35868, 17238 => 24357, 17239 => 31859, 17240 => 31192, 17241 => 35269,
+17242 => 27852, 17243 => 34588, 17244 => 23494, 17245 => 24130, 17246 => 26825,
+17247 => 30496, 17248 => 32501, 17249 => 20885, 17250 => 20813, 17251 => 21193,
+17252 => 23081, 17253 => 32517, 17254 => 38754, 17255 => 33495, 17256 => 25551,
+17257 => 30596, 17258 => 34256, 17259 => 31186, 17260 => 28218, 17261 => 24217,
+17262 => 22937, 17263 => 34065, 17264 => 28781, 17265 => 27665, 17266 => 25279,
+17267 => 30399, 17268 => 25935, 17269 => 24751, 17270 => 38397, 17271 => 26126,
+17272 => 34719, 17273 => 40483, 17274 => 38125, 17275 => 21517, 17276 => 21629,
+17277 => 35884, 17278 => 25720, 17441 => 25721, 17442 => 34321, 17443 => 27169,
+17444 => 33180, 17445 => 30952, 17446 => 25705, 17447 => 39764, 17448 => 25273,
+17449 => 26411, 17450 => 33707, 17451 => 22696, 17452 => 40664, 17453 => 27819,
+17454 => 28448, 17455 => 23518, 17456 => 38476, 17457 => 35851, 17458 => 29279,
+17459 => 26576, 17460 => 25287, 17461 => 29281, 17462 => 20137, 17463 => 22982,
+17464 => 27597, 17465 => 22675, 17466 => 26286, 17467 => 24149, 17468 => 21215,
+17469 => 24917, 17470 => 26408, 17471 => 30446, 17472 => 30566, 17473 => 29287,
+17474 => 31302, 17475 => 25343, 17476 => 21738, 17477 => 21584, 17478 => 38048,
+17479 => 37027, 17480 => 23068, 17481 => 32435, 17482 => 27670, 17483 => 20035,
+17484 => 22902, 17485 => 32784, 17486 => 22856, 17487 => 21335, 17488 => 30007,
+17489 => 38590, 17490 => 22218, 17491 => 25376, 17492 => 33041, 17493 => 24700,
+17494 => 38393, 17495 => 28118, 17496 => 21602, 17497 => 39297, 17498 => 20869,
+17499 => 23273, 17500 => 33021, 17501 => 22958, 17502 => 38675, 17503 => 20522,
+17504 => 27877, 17505 => 23612, 17506 => 25311, 17507 => 20320, 17508 => 21311,
+17509 => 33147, 17510 => 36870, 17511 => 28346, 17512 => 34091, 17513 => 25288,
+17514 => 24180, 17515 => 30910, 17516 => 25781, 17517 => 25467, 17518 => 24565,
+17519 => 23064, 17520 => 37247, 17521 => 40479, 17522 => 23615, 17523 => 25423,
+17524 => 32834, 17525 => 23421, 17526 => 21870, 17527 => 38218, 17528 => 38221,
+17529 => 28037, 17530 => 24744, 17531 => 26592, 17532 => 29406, 17533 => 20957,
+17534 => 23425, 17697 => 25319, 17698 => 27870, 17699 => 29275, 17700 => 25197,
+17701 => 38062, 17702 => 32445, 17703 => 33043, 17704 => 27987, 17705 => 20892,
+17706 => 24324, 17707 => 22900, 17708 => 21162, 17709 => 24594, 17710 => 22899,
+17711 => 26262, 17712 => 34384, 17713 => 30111, 17714 => 25386, 17715 => 25062,
+17716 => 31983, 17717 => 35834, 17718 => 21734, 17719 => 27431, 17720 => 40485,
+17721 => 27572, 17722 => 34261, 17723 => 21589, 17724 => 20598, 17725 => 27812,
+17726 => 21866, 17727 => 36276, 17728 => 29228, 17729 => 24085, 17730 => 24597,
+17731 => 29750, 17732 => 25293, 17733 => 25490, 17734 => 29260, 17735 => 24472,
+17736 => 28227, 17737 => 27966, 17738 => 25856, 17739 => 28504, 17740 => 30424,
+17741 => 30928, 17742 => 30460, 17743 => 30036, 17744 => 21028, 17745 => 21467,
+17746 => 20051, 17747 => 24222, 17748 => 26049, 17749 => 32810, 17750 => 32982,
+17751 => 25243, 17752 => 21638, 17753 => 21032, 17754 => 28846, 17755 => 34957,
+17756 => 36305, 17757 => 27873, 17758 => 21624, 17759 => 32986, 17760 => 22521,
+17761 => 35060, 17762 => 36180, 17763 => 38506, 17764 => 37197, 17765 => 20329,
+17766 => 27803, 17767 => 21943, 17768 => 30406, 17769 => 30768, 17770 => 25256,
+17771 => 28921, 17772 => 28558, 17773 => 24429, 17774 => 34028, 17775 => 26842,
+17776 => 30844, 17777 => 31735, 17778 => 33192, 17779 => 26379, 17780 => 40527,
+17781 => 25447, 17782 => 30896, 17783 => 22383, 17784 => 30738, 17785 => 38713,
+17786 => 25209, 17787 => 25259, 17788 => 21128, 17789 => 29749, 17790 => 27607,
+17953 => 21860, 17954 => 33086, 17955 => 30130, 17956 => 30382, 17957 => 21305,
+17958 => 30174, 17959 => 20731, 17960 => 23617, 17961 => 35692, 17962 => 31687,
+17963 => 20559, 17964 => 29255, 17965 => 39575, 17966 => 39128, 17967 => 28418,
+17968 => 29922, 17969 => 31080, 17970 => 25735, 17971 => 30629, 17972 => 25340,
+17973 => 39057, 17974 => 36139, 17975 => 21697, 17976 => 32856, 17977 => 20050,
+17978 => 22378, 17979 => 33529, 17980 => 33805, 17981 => 24179, 17982 => 20973,
+17983 => 29942, 17984 => 35780, 17985 => 23631, 17986 => 22369, 17987 => 27900,
+17988 => 39047, 17989 => 23110, 17990 => 30772, 17991 => 39748, 17992 => 36843,
+17993 => 31893, 17994 => 21078, 17995 => 25169, 17996 => 38138, 17997 => 20166,
+17998 => 33670, 17999 => 33889, 18000 => 33769, 18001 => 33970, 18002 => 22484,
+18003 => 26420, 18004 => 22275, 18005 => 26222, 18006 => 28006, 18007 => 35889,
+18008 => 26333, 18009 => 28689, 18010 => 26399, 18011 => 27450, 18012 => 26646,
+18013 => 25114, 18014 => 22971, 18015 => 19971, 18016 => 20932, 18017 => 28422,
+18018 => 26578, 18019 => 27791, 18020 => 20854, 18021 => 26827, 18022 => 22855,
+18023 => 27495, 18024 => 30054, 18025 => 23822, 18026 => 33040, 18027 => 40784,
+18028 => 26071, 18029 => 31048, 18030 => 31041, 18031 => 39569, 18032 => 36215,
+18033 => 23682, 18034 => 20062, 18035 => 20225, 18036 => 21551, 18037 => 22865,
+18038 => 30732, 18039 => 22120, 18040 => 27668, 18041 => 36804, 18042 => 24323,
+18043 => 27773, 18044 => 27875, 18045 => 35755, 18046 => 25488, 18209 => 24688,
+18210 => 27965, 18211 => 29301, 18212 => 25190, 18213 => 38030, 18214 => 38085,
+18215 => 21315, 18216 => 36801, 18217 => 31614, 18218 => 20191, 18219 => 35878,
+18220 => 20094, 18221 => 40660, 18222 => 38065, 18223 => 38067, 18224 => 21069,
+18225 => 28508, 18226 => 36963, 18227 => 27973, 18228 => 35892, 18229 => 22545,
+18230 => 23884, 18231 => 27424, 18232 => 27465, 18233 => 26538, 18234 => 21595,
+18235 => 33108, 18236 => 32652, 18237 => 22681, 18238 => 34103, 18239 => 24378,
+18240 => 25250, 18241 => 27207, 18242 => 38201, 18243 => 25970, 18244 => 24708,
+18245 => 26725, 18246 => 30631, 18247 => 20052, 18248 => 20392, 18249 => 24039,
+18250 => 38808, 18251 => 25772, 18252 => 32728, 18253 => 23789, 18254 => 20431,
+18255 => 31373, 18256 => 20999, 18257 => 33540, 18258 => 19988, 18259 => 24623,
+18260 => 31363, 18261 => 38054, 18262 => 20405, 18263 => 20146, 18264 => 31206,
+18265 => 29748, 18266 => 21220, 18267 => 33465, 18268 => 25810, 18269 => 31165,
+18270 => 23517, 18271 => 27777, 18272 => 38738, 18273 => 36731, 18274 => 27682,
+18275 => 20542, 18276 => 21375, 18277 => 28165, 18278 => 25806, 18279 => 26228,
+18280 => 27696, 18281 => 24773, 18282 => 39031, 18283 => 35831, 18284 => 24198,
+18285 => 29756, 18286 => 31351, 18287 => 31179, 18288 => 19992, 18289 => 37041,
+18290 => 29699, 18291 => 27714, 18292 => 22234, 18293 => 37195, 18294 => 27845,
+18295 => 36235, 18296 => 21306, 18297 => 34502, 18298 => 26354, 18299 => 36527,
+18300 => 23624, 18301 => 39537, 18302 => 28192, 18465 => 21462, 18466 => 23094,
+18467 => 40843, 18468 => 36259, 18469 => 21435, 18470 => 22280, 18471 => 39079,
+18472 => 26435, 18473 => 37275, 18474 => 27849, 18475 => 20840, 18476 => 30154,
+18477 => 25331, 18478 => 29356, 18479 => 21048, 18480 => 21149, 18481 => 32570,
+18482 => 28820, 18483 => 30264, 18484 => 21364, 18485 => 40522, 18486 => 27063,
+18487 => 30830, 18488 => 38592, 18489 => 35033, 18490 => 32676, 18491 => 28982,
+18492 => 29123, 18493 => 20873, 18494 => 26579, 18495 => 29924, 18496 => 22756,
+18497 => 25880, 18498 => 22199, 18499 => 35753, 18500 => 39286, 18501 => 25200,
+18502 => 32469, 18503 => 24825, 18504 => 28909, 18505 => 22764, 18506 => 20161,
+18507 => 20154, 18508 => 24525, 18509 => 38887, 18510 => 20219, 18511 => 35748,
+18512 => 20995, 18513 => 22922, 18514 => 32427, 18515 => 25172, 18516 => 20173,
+18517 => 26085, 18518 => 25102, 18519 => 33592, 18520 => 33993, 18521 => 33635,
+18522 => 34701, 18523 => 29076, 18524 => 28342, 18525 => 23481, 18526 => 32466,
+18527 => 20887, 18528 => 25545, 18529 => 26580, 18530 => 32905, 18531 => 33593,
+18532 => 34837, 18533 => 20754, 18534 => 23418, 18535 => 22914, 18536 => 36785,
+18537 => 20083, 18538 => 27741, 18539 => 20837, 18540 => 35109, 18541 => 36719,
+18542 => 38446, 18543 => 34122, 18544 => 29790, 18545 => 38160, 18546 => 38384,
+18547 => 28070, 18548 => 33509, 18549 => 24369, 18550 => 25746, 18551 => 27922,
+18552 => 33832, 18553 => 33134, 18554 => 40131, 18555 => 22622, 18556 => 36187,
+18557 => 19977, 18558 => 21441, 18721 => 20254, 18722 => 25955, 18723 => 26705,
+18724 => 21971, 18725 => 20007, 18726 => 25620, 18727 => 39578, 18728 => 25195,
+18729 => 23234, 18730 => 29791, 18731 => 33394, 18732 => 28073, 18733 => 26862,
+18734 => 20711, 18735 => 33678, 18736 => 30722, 18737 => 26432, 18738 => 21049,
+18739 => 27801, 18740 => 32433, 18741 => 20667, 18742 => 21861, 18743 => 29022,
+18744 => 31579, 18745 => 26194, 18746 => 29642, 18747 => 33515, 18748 => 26441,
+18749 => 23665, 18750 => 21024, 18751 => 29053, 18752 => 34923, 18753 => 38378,
+18754 => 38485, 18755 => 25797, 18756 => 36193, 18757 => 33203, 18758 => 21892,
+18759 => 27733, 18760 => 25159, 18761 => 32558, 18762 => 22674, 18763 => 20260,
+18764 => 21830, 18765 => 36175, 18766 => 26188, 18767 => 19978, 18768 => 23578,
+18769 => 35059, 18770 => 26786, 18771 => 25422, 18772 => 31245, 18773 => 28903,
+18774 => 33421, 18775 => 21242, 18776 => 38902, 18777 => 23569, 18778 => 21736,
+18779 => 37045, 18780 => 32461, 18781 => 22882, 18782 => 36170, 18783 => 34503,
+18784 => 33292, 18785 => 33293, 18786 => 36198, 18787 => 25668, 18788 => 23556,
+18789 => 24913, 18790 => 28041, 18791 => 31038, 18792 => 35774, 18793 => 30775,
+18794 => 30003, 18795 => 21627, 18796 => 20280, 18797 => 36523, 18798 => 28145,
+18799 => 23072, 18800 => 32453, 18801 => 31070, 18802 => 27784, 18803 => 23457,
+18804 => 23158, 18805 => 29978, 18806 => 32958, 18807 => 24910, 18808 => 28183,
+18809 => 22768, 18810 => 29983, 18811 => 29989, 18812 => 29298, 18813 => 21319,
+18814 => 32499, 18977 => 30465, 18978 => 30427, 18979 => 21097, 18980 => 32988,
+18981 => 22307, 18982 => 24072, 18983 => 22833, 18984 => 29422, 18985 => 26045,
+18986 => 28287, 18987 => 35799, 18988 => 23608, 18989 => 34417, 18990 => 21313,
+18991 => 30707, 18992 => 25342, 18993 => 26102, 18994 => 20160, 18995 => 39135,
+18996 => 34432, 18997 => 23454, 18998 => 35782, 18999 => 21490, 19000 => 30690,
+19001 => 20351, 19002 => 23630, 19003 => 39542, 19004 => 22987, 19005 => 24335,
+19006 => 31034, 19007 => 22763, 19008 => 19990, 19009 => 26623, 19010 => 20107,
+19011 => 25325, 19012 => 35475, 19013 => 36893, 19014 => 21183, 19015 => 26159,
+19016 => 21980, 19017 => 22124, 19018 => 36866, 19019 => 20181, 19020 => 20365,
+19021 => 37322, 19022 => 39280, 19023 => 27663, 19024 => 24066, 19025 => 24643,
+19026 => 23460, 19027 => 35270, 19028 => 35797, 19029 => 25910, 19030 => 25163,
+19031 => 39318, 19032 => 23432, 19033 => 23551, 19034 => 25480, 19035 => 21806,
+19036 => 21463, 19037 => 30246, 19038 => 20861, 19039 => 34092, 19040 => 26530,
+19041 => 26803, 19042 => 27530, 19043 => 25234, 19044 => 36755, 19045 => 21460,
+19046 => 33298, 19047 => 28113, 19048 => 30095, 19049 => 20070, 19050 => 36174,
+19051 => 23408, 19052 => 29087, 19053 => 34223, 19054 => 26257, 19055 => 26329,
+19056 => 32626, 19057 => 34560, 19058 => 40653, 19059 => 40736, 19060 => 23646,
+19061 => 26415, 19062 => 36848, 19063 => 26641, 19064 => 26463, 19065 => 25101,
+19066 => 31446, 19067 => 22661, 19068 => 24246, 19069 => 25968, 19070 => 28465,
+19233 => 24661, 19234 => 21047, 19235 => 32781, 19236 => 25684, 19237 => 34928,
+19238 => 29993, 19239 => 24069, 19240 => 26643, 19241 => 25332, 19242 => 38684,
+19243 => 21452, 19244 => 29245, 19245 => 35841, 19246 => 27700, 19247 => 30561,
+19248 => 31246, 19249 => 21550, 19250 => 30636, 19251 => 39034, 19252 => 33308,
+19253 => 35828, 19254 => 30805, 19255 => 26388, 19256 => 28865, 19257 => 26031,
+19258 => 25749, 19259 => 22070, 19260 => 24605, 19261 => 31169, 19262 => 21496,
+19263 => 19997, 19264 => 27515, 19265 => 32902, 19266 => 23546, 19267 => 21987,
+19268 => 22235, 19269 => 20282, 19270 => 20284, 19271 => 39282, 19272 => 24051,
+19273 => 26494, 19274 => 32824, 19275 => 24578, 19276 => 39042, 19277 => 36865,
+19278 => 23435, 19279 => 35772, 19280 => 35829, 19281 => 25628, 19282 => 33368,
+19283 => 25822, 19284 => 22013, 19285 => 33487, 19286 => 37221, 19287 => 20439,
+19288 => 32032, 19289 => 36895, 19290 => 31903, 19291 => 20723, 19292 => 22609,
+19293 => 28335, 19294 => 23487, 19295 => 35785, 19296 => 32899, 19297 => 37240,
+19298 => 33948, 19299 => 31639, 19300 => 34429, 19301 => 38539, 19302 => 38543,
+19303 => 32485, 19304 => 39635, 19305 => 30862, 19306 => 23681, 19307 => 31319,
+19308 => 36930, 19309 => 38567, 19310 => 31071, 19311 => 23385, 19312 => 25439,
+19313 => 31499, 19314 => 34001, 19315 => 26797, 19316 => 21766, 19317 => 32553,
+19318 => 29712, 19319 => 32034, 19320 => 38145, 19321 => 25152, 19322 => 22604,
+19323 => 20182, 19324 => 23427, 19325 => 22905, 19326 => 22612, 19489 => 29549,
+19490 => 25374, 19491 => 36427, 19492 => 36367, 19493 => 32974, 19494 => 33492,
+19495 => 25260, 19496 => 21488, 19497 => 27888, 19498 => 37214, 19499 => 22826,
+19500 => 24577, 19501 => 27760, 19502 => 22349, 19503 => 25674, 19504 => 36138,
+19505 => 30251, 19506 => 28393, 19507 => 22363, 19508 => 27264, 19509 => 30192,
+19510 => 28525, 19511 => 35885, 19512 => 35848, 19513 => 22374, 19514 => 27631,
+19515 => 34962, 19516 => 30899, 19517 => 25506, 19518 => 21497, 19519 => 28845,
+19520 => 27748, 19521 => 22616, 19522 => 25642, 19523 => 22530, 19524 => 26848,
+19525 => 33179, 19526 => 21776, 19527 => 31958, 19528 => 20504, 19529 => 36538,
+19530 => 28108, 19531 => 36255, 19532 => 28907, 19533 => 25487, 19534 => 28059,
+19535 => 28372, 19536 => 32486, 19537 => 33796, 19538 => 26691, 19539 => 36867,
+19540 => 28120, 19541 => 38518, 19542 => 35752, 19543 => 22871, 19544 => 29305,
+19545 => 34276, 19546 => 33150, 19547 => 30140, 19548 => 35466, 19549 => 26799,
+19550 => 21076, 19551 => 36386, 19552 => 38161, 19553 => 25552, 19554 => 39064,
+19555 => 36420, 19556 => 21884, 19557 => 20307, 19558 => 26367, 19559 => 22159,
+19560 => 24789, 19561 => 28053, 19562 => 21059, 19563 => 23625, 19564 => 22825,
+19565 => 28155, 19566 => 22635, 19567 => 30000, 19568 => 29980, 19569 => 24684,
+19570 => 33300, 19571 => 33094, 19572 => 25361, 19573 => 26465, 19574 => 36834,
+19575 => 30522, 19576 => 36339, 19577 => 36148, 19578 => 38081, 19579 => 24086,
+19580 => 21381, 19581 => 21548, 19582 => 28867, 19745 => 27712, 19746 => 24311,
+19747 => 20572, 19748 => 20141, 19749 => 24237, 19750 => 25402, 19751 => 33351,
+19752 => 36890, 19753 => 26704, 19754 => 37230, 19755 => 30643, 19756 => 21516,
+19757 => 38108, 19758 => 24420, 19759 => 31461, 19760 => 26742, 19761 => 25413,
+19762 => 31570, 19763 => 32479, 19764 => 30171, 19765 => 20599, 19766 => 25237,
+19767 => 22836, 19768 => 36879, 19769 => 20984, 19770 => 31171, 19771 => 31361,
+19772 => 22270, 19773 => 24466, 19774 => 36884, 19775 => 28034, 19776 => 23648,
+19777 => 22303, 19778 => 21520, 19779 => 20820, 19780 => 28237, 19781 => 22242,
+19782 => 25512, 19783 => 39059, 19784 => 33151, 19785 => 34581, 19786 => 35114,
+19787 => 36864, 19788 => 21534, 19789 => 23663, 19790 => 33216, 19791 => 25302,
+19792 => 25176, 19793 => 33073, 19794 => 40501, 19795 => 38464, 19796 => 39534,
+19797 => 39548, 19798 => 26925, 19799 => 22949, 19800 => 25299, 19801 => 21822,
+19802 => 25366, 19803 => 21703, 19804 => 34521, 19805 => 27964, 19806 => 23043,
+19807 => 29926, 19808 => 34972, 19809 => 27498, 19810 => 22806, 19811 => 35916,
+19812 => 24367, 19813 => 28286, 19814 => 29609, 19815 => 39037, 19816 => 20024,
+19817 => 28919, 19818 => 23436, 19819 => 30871, 19820 => 25405, 19821 => 26202,
+19822 => 30358, 19823 => 24779, 19824 => 23451, 19825 => 23113, 19826 => 19975,
+19827 => 33109, 19828 => 27754, 19829 => 29579, 19830 => 20129, 19831 => 26505,
+19832 => 32593, 19833 => 24448, 19834 => 26106, 19835 => 26395, 19836 => 24536,
+19837 => 22916, 19838 => 23041, 20001 => 24013, 20002 => 24494, 20003 => 21361,
+20004 => 38886, 20005 => 36829, 20006 => 26693, 20007 => 22260, 20008 => 21807,
+20009 => 24799, 20010 => 20026, 20011 => 28493, 20012 => 32500, 20013 => 33479,
+20014 => 33806, 20015 => 22996, 20016 => 20255, 20017 => 20266, 20018 => 23614,
+20019 => 32428, 20020 => 26410, 20021 => 34074, 20022 => 21619, 20023 => 30031,
+20024 => 32963, 20025 => 21890, 20026 => 39759, 20027 => 20301, 20028 => 28205,
+20029 => 35859, 20030 => 23561, 20031 => 24944, 20032 => 21355, 20033 => 30239,
+20034 => 28201, 20035 => 34442, 20036 => 25991, 20037 => 38395, 20038 => 32441,
+20039 => 21563, 20040 => 31283, 20041 => 32010, 20042 => 38382, 20043 => 21985,
+20044 => 32705, 20045 => 29934, 20046 => 25373, 20047 => 34583, 20048 => 28065,
+20049 => 31389, 20050 => 25105, 20051 => 26017, 20052 => 21351, 20053 => 25569,
+20054 => 27779, 20055 => 24043, 20056 => 21596, 20057 => 38056, 20058 => 20044,
+20059 => 27745, 20060 => 35820, 20061 => 23627, 20062 => 26080, 20063 => 33436,
+20064 => 26791, 20065 => 21566, 20066 => 21556, 20067 => 27595, 20068 => 27494,
+20069 => 20116, 20070 => 25410, 20071 => 21320, 20072 => 33310, 20073 => 20237,
+20074 => 20398, 20075 => 22366, 20076 => 25098, 20077 => 38654, 20078 => 26212,
+20079 => 29289, 20080 => 21247, 20081 => 21153, 20082 => 24735, 20083 => 35823,
+20084 => 26132, 20085 => 29081, 20086 => 26512, 20087 => 35199, 20088 => 30802,
+20089 => 30717, 20090 => 26224, 20091 => 22075, 20092 => 21560, 20093 => 38177,
+20094 => 29306, 20257 => 31232, 20258 => 24687, 20259 => 24076, 20260 => 24713,
+20261 => 33181, 20262 => 22805, 20263 => 24796, 20264 => 29060, 20265 => 28911,
+20266 => 28330, 20267 => 27728, 20268 => 29312, 20269 => 27268, 20270 => 34989,
+20271 => 24109, 20272 => 20064, 20273 => 23219, 20274 => 21916, 20275 => 38115,
+20276 => 27927, 20277 => 31995, 20278 => 38553, 20279 => 25103, 20280 => 32454,
+20281 => 30606, 20282 => 34430, 20283 => 21283, 20284 => 38686, 20285 => 36758,
+20286 => 26247, 20287 => 23777, 20288 => 20384, 20289 => 29421, 20290 => 19979,
+20291 => 21414, 20292 => 22799, 20293 => 21523, 20294 => 25472, 20295 => 38184,
+20296 => 20808, 20297 => 20185, 20298 => 40092, 20299 => 32420, 20300 => 21688,
+20301 => 36132, 20302 => 34900, 20303 => 33335, 20304 => 38386, 20305 => 28046,
+20306 => 24358, 20307 => 23244, 20308 => 26174, 20309 => 38505, 20310 => 29616,
+20311 => 29486, 20312 => 21439, 20313 => 33146, 20314 => 39301, 20315 => 32673,
+20316 => 23466, 20317 => 38519, 20318 => 38480, 20319 => 32447, 20320 => 30456,
+20321 => 21410, 20322 => 38262, 20323 => 39321, 20324 => 31665, 20325 => 35140,
+20326 => 28248, 20327 => 20065, 20328 => 32724, 20329 => 31077, 20330 => 35814,
+20331 => 24819, 20332 => 21709, 20333 => 20139, 20334 => 39033, 20335 => 24055,
+20336 => 27233, 20337 => 20687, 20338 => 21521, 20339 => 35937, 20340 => 33831,
+20341 => 30813, 20342 => 38660, 20343 => 21066, 20344 => 21742, 20345 => 22179,
+20346 => 38144, 20347 => 28040, 20348 => 23477, 20349 => 28102, 20350 => 26195,
+20513 => 23567, 20514 => 23389, 20515 => 26657, 20516 => 32918, 20517 => 21880,
+20518 => 31505, 20519 => 25928, 20520 => 26964, 20521 => 20123, 20522 => 27463,
+20523 => 34638, 20524 => 38795, 20525 => 21327, 20526 => 25375, 20527 => 25658,
+20528 => 37034, 20529 => 26012, 20530 => 32961, 20531 => 35856, 20532 => 20889,
+20533 => 26800, 20534 => 21368, 20535 => 34809, 20536 => 25032, 20537 => 27844,
+20538 => 27899, 20539 => 35874, 20540 => 23633, 20541 => 34218, 20542 => 33455,
+20543 => 38156, 20544 => 27427, 20545 => 36763, 20546 => 26032, 20547 => 24571,
+20548 => 24515, 20549 => 20449, 20550 => 34885, 20551 => 26143, 20552 => 33125,
+20553 => 29481, 20554 => 24826, 20555 => 20852, 20556 => 21009, 20557 => 22411,
+20558 => 24418, 20559 => 37026, 20560 => 34892, 20561 => 37266, 20562 => 24184,
+20563 => 26447, 20564 => 24615, 20565 => 22995, 20566 => 20804, 20567 => 20982,
+20568 => 33016, 20569 => 21256, 20570 => 27769, 20571 => 38596, 20572 => 29066,
+20573 => 20241, 20574 => 20462, 20575 => 32670, 20576 => 26429, 20577 => 21957,
+20578 => 38152, 20579 => 31168, 20580 => 34966, 20581 => 32483, 20582 => 22687,
+20583 => 25100, 20584 => 38656, 20585 => 34394, 20586 => 22040, 20587 => 39035,
+20588 => 24464, 20589 => 35768, 20590 => 33988, 20591 => 37207, 20592 => 21465,
+20593 => 26093, 20594 => 24207, 20595 => 30044, 20596 => 24676, 20597 => 32110,
+20598 => 23167, 20599 => 32490, 20600 => 32493, 20601 => 36713, 20602 => 21927,
+20603 => 23459, 20604 => 24748, 20605 => 26059, 20606 => 29572, 20769 => 36873,
+20770 => 30307, 20771 => 30505, 20772 => 32474, 20773 => 38772, 20774 => 34203,
+20775 => 23398, 20776 => 31348, 20777 => 38634, 20778 => 34880, 20779 => 21195,
+20780 => 29071, 20781 => 24490, 20782 => 26092, 20783 => 35810, 20784 => 23547,
+20785 => 39535, 20786 => 24033, 20787 => 27529, 20788 => 27739, 20789 => 35757,
+20790 => 35759, 20791 => 36874, 20792 => 36805, 20793 => 21387, 20794 => 25276,
+20795 => 40486, 20796 => 40493, 20797 => 21568, 20798 => 20011, 20799 => 33469,
+20800 => 29273, 20801 => 34460, 20802 => 23830, 20803 => 34905, 20804 => 28079,
+20805 => 38597, 20806 => 21713, 20807 => 20122, 20808 => 35766, 20809 => 28937,
+20810 => 21693, 20811 => 38409, 20812 => 28895, 20813 => 28153, 20814 => 30416,
+20815 => 20005, 20816 => 30740, 20817 => 34578, 20818 => 23721, 20819 => 24310,
+20820 => 35328, 20821 => 39068, 20822 => 38414, 20823 => 28814, 20824 => 27839,
+20825 => 22852, 20826 => 25513, 20827 => 30524, 20828 => 34893, 20829 => 28436,
+20830 => 33395, 20831 => 22576, 20832 => 29141, 20833 => 21388, 20834 => 30746,
+20835 => 38593, 20836 => 21761, 20837 => 24422, 20838 => 28976, 20839 => 23476,
+20840 => 35866, 20841 => 39564, 20842 => 27523, 20843 => 22830, 20844 => 40495,
+20845 => 31207, 20846 => 26472, 20847 => 25196, 20848 => 20335, 20849 => 30113,
+20850 => 32650, 20851 => 27915, 20852 => 38451, 20853 => 27687, 20854 => 20208,
+20855 => 30162, 20856 => 20859, 20857 => 26679, 20858 => 28478, 20859 => 36992,
+20860 => 33136, 20861 => 22934, 20862 => 29814, 21025 => 25671, 21026 => 23591,
+21027 => 36965, 21028 => 31377, 21029 => 35875, 21030 => 23002, 21031 => 21676,
+21032 => 33280, 21033 => 33647, 21034 => 35201, 21035 => 32768, 21036 => 26928,
+21037 => 22094, 21038 => 32822, 21039 => 29239, 21040 => 37326, 21041 => 20918,
+21042 => 20063, 21043 => 39029, 21044 => 25494, 21045 => 19994, 21046 => 21494,
+21047 => 26355, 21048 => 33099, 21049 => 22812, 21050 => 28082, 21051 => 19968,
+21052 => 22777, 21053 => 21307, 21054 => 25558, 21055 => 38129, 21056 => 20381,
+21057 => 20234, 21058 => 34915, 21059 => 39056, 21060 => 22839, 21061 => 36951,
+21062 => 31227, 21063 => 20202, 21064 => 33008, 21065 => 30097, 21066 => 27778,
+21067 => 23452, 21068 => 23016, 21069 => 24413, 21070 => 26885, 21071 => 34433,
+21072 => 20506, 21073 => 24050, 21074 => 20057, 21075 => 30691, 21076 => 20197,
+21077 => 33402, 21078 => 25233, 21079 => 26131, 21080 => 37009, 21081 => 23673,
+21082 => 20159, 21083 => 24441, 21084 => 33222, 21085 => 36920, 21086 => 32900,
+21087 => 30123, 21088 => 20134, 21089 => 35028, 21090 => 24847, 21091 => 27589,
+21092 => 24518, 21093 => 20041, 21094 => 30410, 21095 => 28322, 21096 => 35811,
+21097 => 35758, 21098 => 35850, 21099 => 35793, 21100 => 24322, 21101 => 32764,
+21102 => 32716, 21103 => 32462, 21104 => 33589, 21105 => 33643, 21106 => 22240,
+21107 => 27575, 21108 => 38899, 21109 => 38452, 21110 => 23035, 21111 => 21535,
+21112 => 38134, 21113 => 28139, 21114 => 23493, 21115 => 39278, 21116 => 23609,
+21117 => 24341, 21118 => 38544, 21281 => 21360, 21282 => 33521, 21283 => 27185,
+21284 => 23156, 21285 => 40560, 21286 => 24212, 21287 => 32552, 21288 => 33721,
+21289 => 33828, 21290 => 33829, 21291 => 33639, 21292 => 34631, 21293 => 36814,
+21294 => 36194, 21295 => 30408, 21296 => 24433, 21297 => 39062, 21298 => 30828,
+21299 => 26144, 21300 => 21727, 21301 => 25317, 21302 => 20323, 21303 => 33219,
+21304 => 30152, 21305 => 24248, 21306 => 38605, 21307 => 36362, 21308 => 34553,
+21309 => 21647, 21310 => 27891, 21311 => 28044, 21312 => 27704, 21313 => 24703,
+21314 => 21191, 21315 => 29992, 21316 => 24189, 21317 => 20248, 21318 => 24736,
+21319 => 24551, 21320 => 23588, 21321 => 30001, 21322 => 37038, 21323 => 38080,
+21324 => 29369, 21325 => 27833, 21326 => 28216, 21327 => 37193, 21328 => 26377,
+21329 => 21451, 21330 => 21491, 21331 => 20305, 21332 => 37321, 21333 => 35825,
+21334 => 21448, 21335 => 24188, 21336 => 36802, 21337 => 28132, 21338 => 20110,
+21339 => 30402, 21340 => 27014, 21341 => 34398, 21342 => 24858, 21343 => 33286,
+21344 => 20313, 21345 => 20446, 21346 => 36926, 21347 => 40060, 21348 => 24841,
+21349 => 28189, 21350 => 28180, 21351 => 38533, 21352 => 20104, 21353 => 23089,
+21354 => 38632, 21355 => 19982, 21356 => 23679, 21357 => 31161, 21358 => 23431,
+21359 => 35821, 21360 => 32701, 21361 => 29577, 21362 => 22495, 21363 => 33419,
+21364 => 37057, 21365 => 21505, 21366 => 36935, 21367 => 21947, 21368 => 23786,
+21369 => 24481, 21370 => 24840, 21371 => 27442, 21372 => 29425, 21373 => 32946,
+21374 => 35465, 21537 => 28020, 21538 => 23507, 21539 => 35029, 21540 => 39044,
+21541 => 35947, 21542 => 39533, 21543 => 40499, 21544 => 28170, 21545 => 20900,
+21546 => 20803, 21547 => 22435, 21548 => 34945, 21549 => 21407, 21550 => 25588,
+21551 => 36757, 21552 => 22253, 21553 => 21592, 21554 => 22278, 21555 => 29503,
+21556 => 28304, 21557 => 32536, 21558 => 36828, 21559 => 33489, 21560 => 24895,
+21561 => 24616, 21562 => 38498, 21563 => 26352, 21564 => 32422, 21565 => 36234,
+21566 => 36291, 21567 => 38053, 21568 => 23731, 21569 => 31908, 21570 => 26376,
+21571 => 24742, 21572 => 38405, 21573 => 32792, 21574 => 20113, 21575 => 37095,
+21576 => 21248, 21577 => 38504, 21578 => 20801, 21579 => 36816, 21580 => 34164,
+21581 => 37213, 21582 => 26197, 21583 => 38901, 21584 => 23381, 21585 => 21277,
+21586 => 30776, 21587 => 26434, 21588 => 26685, 21589 => 21705, 21590 => 28798,
+21591 => 23472, 21592 => 36733, 21593 => 20877, 21594 => 22312, 21595 => 21681,
+21596 => 25874, 21597 => 26242, 21598 => 36190, 21599 => 36163, 21600 => 33039,
+21601 => 33900, 21602 => 36973, 21603 => 31967, 21604 => 20991, 21605 => 34299,
+21606 => 26531, 21607 => 26089, 21608 => 28577, 21609 => 34468, 21610 => 36481,
+21611 => 22122, 21612 => 36896, 21613 => 30338, 21614 => 28790, 21615 => 29157,
+21616 => 36131, 21617 => 25321, 21618 => 21017, 21619 => 27901, 21620 => 36156,
+21621 => 24590, 21622 => 22686, 21623 => 24974, 21624 => 26366, 21625 => 36192,
+21626 => 25166, 21627 => 21939, 21628 => 28195, 21629 => 26413, 21630 => 36711,
+21793 => 38113, 21794 => 38392, 21795 => 30504, 21796 => 26629, 21797 => 27048,
+21798 => 21643, 21799 => 20045, 21800 => 28856, 21801 => 35784, 21802 => 25688,
+21803 => 25995, 21804 => 23429, 21805 => 31364, 21806 => 20538, 21807 => 23528,
+21808 => 30651, 21809 => 27617, 21810 => 35449, 21811 => 31896, 21812 => 27838,
+21813 => 30415, 21814 => 26025, 21815 => 36759, 21816 => 23853, 21817 => 23637,
+21818 => 34360, 21819 => 26632, 21820 => 21344, 21821 => 25112, 21822 => 31449,
+21823 => 28251, 21824 => 32509, 21825 => 27167, 21826 => 31456, 21827 => 24432,
+21828 => 28467, 21829 => 24352, 21830 => 25484, 21831 => 28072, 21832 => 26454,
+21833 => 19976, 21834 => 24080, 21835 => 36134, 21836 => 20183, 21837 => 32960,
+21838 => 30260, 21839 => 38556, 21840 => 25307, 21841 => 26157, 21842 => 25214,
+21843 => 27836, 21844 => 36213, 21845 => 29031, 21846 => 32617, 21847 => 20806,
+21848 => 32903, 21849 => 21484, 21850 => 36974, 21851 => 25240, 21852 => 21746,
+21853 => 34544, 21854 => 36761, 21855 => 32773, 21856 => 38167, 21857 => 34071,
+21858 => 36825, 21859 => 27993, 21860 => 29645, 21861 => 26015, 21862 => 30495,
+21863 => 29956, 21864 => 30759, 21865 => 33275, 21866 => 36126, 21867 => 38024,
+21868 => 20390, 21869 => 26517, 21870 => 30137, 21871 => 35786, 21872 => 38663,
+21873 => 25391, 21874 => 38215, 21875 => 38453, 21876 => 33976, 21877 => 25379,
+21878 => 30529, 21879 => 24449, 21880 => 29424, 21881 => 20105, 21882 => 24596,
+21883 => 25972, 21884 => 25327, 21885 => 27491, 21886 => 25919, 22049 => 24103,
+22050 => 30151, 22051 => 37073, 22052 => 35777, 22053 => 33437, 22054 => 26525,
+22055 => 25903, 22056 => 21553, 22057 => 34584, 22058 => 30693, 22059 => 32930,
+22060 => 33026, 22061 => 27713, 22062 => 20043, 22063 => 32455, 22064 => 32844,
+22065 => 30452, 22066 => 26893, 22067 => 27542, 22068 => 25191, 22069 => 20540,
+22070 => 20356, 22071 => 22336, 22072 => 25351, 22073 => 27490, 22074 => 36286,
+22075 => 21482, 22076 => 26088, 22077 => 32440, 22078 => 24535, 22079 => 25370,
+22080 => 25527, 22081 => 33267, 22082 => 33268, 22083 => 32622, 22084 => 24092,
+22085 => 23769, 22086 => 21046, 22087 => 26234, 22088 => 31209, 22089 => 31258,
+22090 => 36136, 22091 => 28825, 22092 => 30164, 22093 => 28382, 22094 => 27835,
+22095 => 31378, 22096 => 20013, 22097 => 30405, 22098 => 24544, 22099 => 38047,
+22100 => 34935, 22101 => 32456, 22102 => 31181, 22103 => 32959, 22104 => 37325,
+22105 => 20210, 22106 => 20247, 22107 => 33311, 22108 => 21608, 22109 => 24030,
+22110 => 27954, 22111 => 35788, 22112 => 31909, 22113 => 36724, 22114 => 32920,
+22115 => 24090, 22116 => 21650, 22117 => 30385, 22118 => 23449, 22119 => 26172,
+22120 => 39588, 22121 => 29664, 22122 => 26666, 22123 => 34523, 22124 => 26417,
+22125 => 29482, 22126 => 35832, 22127 => 35803, 22128 => 36880, 22129 => 31481,
+22130 => 28891, 22131 => 29038, 22132 => 25284, 22133 => 30633, 22134 => 22065,
+22135 => 20027, 22136 => 33879, 22137 => 26609, 22138 => 21161, 22139 => 34496,
+22140 => 36142, 22141 => 38136, 22142 => 31569, 22305 => 20303, 22306 => 27880,
+22307 => 31069, 22308 => 39547, 22309 => 25235, 22310 => 29226, 22311 => 25341,
+22312 => 19987, 22313 => 30742, 22314 => 36716, 22315 => 25776, 22316 => 36186,
+22317 => 31686, 22318 => 26729, 22319 => 24196, 22320 => 35013, 22321 => 22918,
+22322 => 25758, 22323 => 22766, 22324 => 29366, 22325 => 26894, 22326 => 38181,
+22327 => 36861, 22328 => 36184, 22329 => 22368, 22330 => 32512, 22331 => 35846,
+22332 => 20934, 22333 => 25417, 22334 => 25305, 22335 => 21331, 22336 => 26700,
+22337 => 29730, 22338 => 33537, 22339 => 37196, 22340 => 21828, 22341 => 30528,
+22342 => 28796, 22343 => 27978, 22344 => 20857, 22345 => 21672, 22346 => 36164,
+22347 => 23039, 22348 => 28363, 22349 => 28100, 22350 => 23388, 22351 => 32043,
+22352 => 20180, 22353 => 31869, 22354 => 28371, 22355 => 23376, 22356 => 33258,
+22357 => 28173, 22358 => 23383, 22359 => 39683, 22360 => 26837, 22361 => 36394,
+22362 => 23447, 22363 => 32508, 22364 => 24635, 22365 => 32437, 22366 => 37049,
+22367 => 36208, 22368 => 22863, 22369 => 25549, 22370 => 31199, 22371 => 36275,
+22372 => 21330, 22373 => 26063, 22374 => 31062, 22375 => 35781, 22376 => 38459,
+22377 => 32452, 22378 => 38075, 22379 => 32386, 22380 => 22068, 22381 => 37257,
+22382 => 26368, 22383 => 32618, 22384 => 23562, 22385 => 36981, 22386 => 26152,
+22387 => 24038, 22388 => 20304, 22389 => 26590, 22390 => 20570, 22391 => 20316,
+22392 => 22352, 22393 => 24231, 22561 => 20109, 22562 => 19980, 22563 => 20800,
+22564 => 19984, 22565 => 24319, 22566 => 21317, 22567 => 19989, 22568 => 20120,
+22569 => 19998, 22570 => 39730, 22571 => 23404, 22572 => 22121, 22573 => 20008,
+22574 => 31162, 22575 => 20031, 22576 => 21269, 22577 => 20039, 22578 => 22829,
+22579 => 29243, 22580 => 21358, 22581 => 27664, 22582 => 22239, 22583 => 32996,
+22584 => 39319, 22585 => 27603, 22586 => 30590, 22587 => 40727, 22588 => 20022,
+22589 => 20127, 22590 => 40720, 22591 => 20060, 22592 => 20073, 22593 => 20115,
+22594 => 33416, 22595 => 23387, 22596 => 21868, 22597 => 22031, 22598 => 20164,
+22599 => 21389, 22600 => 21405, 22601 => 21411, 22602 => 21413, 22603 => 21422,
+22604 => 38757, 22605 => 36189, 22606 => 21274, 22607 => 21493, 22608 => 21286,
+22609 => 21294, 22610 => 21310, 22611 => 36188, 22612 => 21350, 22613 => 21347,
+22614 => 20994, 22615 => 21000, 22616 => 21006, 22617 => 21037, 22618 => 21043,
+22619 => 21055, 22620 => 21056, 22621 => 21068, 22622 => 21086, 22623 => 21089,
+22624 => 21084, 22625 => 33967, 22626 => 21117, 22627 => 21122, 22628 => 21121,
+22629 => 21136, 22630 => 21139, 22631 => 20866, 22632 => 32596, 22633 => 20155,
+22634 => 20163, 22635 => 20169, 22636 => 20162, 22637 => 20200, 22638 => 20193,
+22639 => 20203, 22640 => 20190, 22641 => 20251, 22642 => 20211, 22643 => 20258,
+22644 => 20324, 22645 => 20213, 22646 => 20261, 22647 => 20263, 22648 => 20233,
+22649 => 20267, 22650 => 20318, 22651 => 20327, 22652 => 25912, 22653 => 20314,
+22654 => 20317, 22817 => 20319, 22818 => 20311, 22819 => 20274, 22820 => 20285,
+22821 => 20342, 22822 => 20340, 22823 => 20369, 22824 => 20361, 22825 => 20355,
+22826 => 20367, 22827 => 20350, 22828 => 20347, 22829 => 20394, 22830 => 20348,
+22831 => 20396, 22832 => 20372, 22833 => 20454, 22834 => 20456, 22835 => 20458,
+22836 => 20421, 22837 => 20442, 22838 => 20451, 22839 => 20444, 22840 => 20433,
+22841 => 20447, 22842 => 20472, 22843 => 20521, 22844 => 20556, 22845 => 20467,
+22846 => 20524, 22847 => 20495, 22848 => 20526, 22849 => 20525, 22850 => 20478,
+22851 => 20508, 22852 => 20492, 22853 => 20517, 22854 => 20520, 22855 => 20606,
+22856 => 20547, 22857 => 20565, 22858 => 20552, 22859 => 20558, 22860 => 20588,
+22861 => 20603, 22862 => 20645, 22863 => 20647, 22864 => 20649, 22865 => 20666,
+22866 => 20694, 22867 => 20742, 22868 => 20717, 22869 => 20716, 22870 => 20710,
+22871 => 20718, 22872 => 20743, 22873 => 20747, 22874 => 20189, 22875 => 27709,
+22876 => 20312, 22877 => 20325, 22878 => 20430, 22879 => 40864, 22880 => 27718,
+22881 => 31860, 22882 => 20846, 22883 => 24061, 22884 => 40649, 22885 => 39320,
+22886 => 20865, 22887 => 22804, 22888 => 21241, 22889 => 21261, 22890 => 35335,
+22891 => 21264, 22892 => 20971, 22893 => 22809, 22894 => 20821, 22895 => 20128,
+22896 => 20822, 22897 => 20147, 22898 => 34926, 22899 => 34980, 22900 => 20149,
+22901 => 33044, 22902 => 35026, 22903 => 31104, 22904 => 23348, 22905 => 34819,
+22906 => 32696, 22907 => 20907, 22908 => 20913, 22909 => 20925, 22910 => 20924,
+23073 => 20935, 23074 => 20886, 23075 => 20898, 23076 => 20901, 23077 => 35744,
+23078 => 35750, 23079 => 35751, 23080 => 35754, 23081 => 35764, 23082 => 35765,
+23083 => 35767, 23084 => 35778, 23085 => 35779, 23086 => 35787, 23087 => 35791,
+23088 => 35790, 23089 => 35794, 23090 => 35795, 23091 => 35796, 23092 => 35798,
+23093 => 35800, 23094 => 35801, 23095 => 35804, 23096 => 35807, 23097 => 35808,
+23098 => 35812, 23099 => 35816, 23100 => 35817, 23101 => 35822, 23102 => 35824,
+23103 => 35827, 23104 => 35830, 23105 => 35833, 23106 => 35836, 23107 => 35839,
+23108 => 35840, 23109 => 35842, 23110 => 35844, 23111 => 35847, 23112 => 35852,
+23113 => 35855, 23114 => 35857, 23115 => 35858, 23116 => 35860, 23117 => 35861,
+23118 => 35862, 23119 => 35865, 23120 => 35867, 23121 => 35864, 23122 => 35869,
+23123 => 35871, 23124 => 35872, 23125 => 35873, 23126 => 35877, 23127 => 35879,
+23128 => 35882, 23129 => 35883, 23130 => 35886, 23131 => 35887, 23132 => 35890,
+23133 => 35891, 23134 => 35893, 23135 => 35894, 23136 => 21353, 23137 => 21370,
+23138 => 38429, 23139 => 38434, 23140 => 38433, 23141 => 38449, 23142 => 38442,
+23143 => 38461, 23144 => 38460, 23145 => 38466, 23146 => 38473, 23147 => 38484,
+23148 => 38495, 23149 => 38503, 23150 => 38508, 23151 => 38514, 23152 => 38516,
+23153 => 38536, 23154 => 38541, 23155 => 38551, 23156 => 38576, 23157 => 37015,
+23158 => 37019, 23159 => 37021, 23160 => 37017, 23161 => 37036, 23162 => 37025,
+23163 => 37044, 23164 => 37043, 23165 => 37046, 23166 => 37050, 23329 => 37048,
+23330 => 37040, 23331 => 37071, 23332 => 37061, 23333 => 37054, 23334 => 37072,
+23335 => 37060, 23336 => 37063, 23337 => 37075, 23338 => 37094, 23339 => 37090,
+23340 => 37084, 23341 => 37079, 23342 => 37083, 23343 => 37099, 23344 => 37103,
+23345 => 37118, 23346 => 37124, 23347 => 37154, 23348 => 37150, 23349 => 37155,
+23350 => 37169, 23351 => 37167, 23352 => 37177, 23353 => 37187, 23354 => 37190,
+23355 => 21005, 23356 => 22850, 23357 => 21154, 23358 => 21164, 23359 => 21165,
+23360 => 21182, 23361 => 21759, 23362 => 21200, 23363 => 21206, 23364 => 21232,
+23365 => 21471, 23366 => 29166, 23367 => 30669, 23368 => 24308, 23369 => 20981,
+23370 => 20988, 23371 => 39727, 23372 => 21430, 23373 => 24321, 23374 => 30042,
+23375 => 24047, 23376 => 22348, 23377 => 22441, 23378 => 22433, 23379 => 22654,
+23380 => 22716, 23381 => 22725, 23382 => 22737, 23383 => 22313, 23384 => 22316,
+23385 => 22314, 23386 => 22323, 23387 => 22329, 23388 => 22318, 23389 => 22319,
+23390 => 22364, 23391 => 22331, 23392 => 22338, 23393 => 22377, 23394 => 22405,
+23395 => 22379, 23396 => 22406, 23397 => 22396, 23398 => 22395, 23399 => 22376,
+23400 => 22381, 23401 => 22390, 23402 => 22387, 23403 => 22445, 23404 => 22436,
+23405 => 22412, 23406 => 22450, 23407 => 22479, 23408 => 22439, 23409 => 22452,
+23410 => 22419, 23411 => 22432, 23412 => 22485, 23413 => 22488, 23414 => 22490,
+23415 => 22489, 23416 => 22482, 23417 => 22456, 23418 => 22516, 23419 => 22511,
+23420 => 22520, 23421 => 22500, 23422 => 22493, 23585 => 22539, 23586 => 22541,
+23587 => 22525, 23588 => 22509, 23589 => 22528, 23590 => 22558, 23591 => 22553,
+23592 => 22596, 23593 => 22560, 23594 => 22629, 23595 => 22636, 23596 => 22657,
+23597 => 22665, 23598 => 22682, 23599 => 22656, 23600 => 39336, 23601 => 40729,
+23602 => 25087, 23603 => 33401, 23604 => 33405, 23605 => 33407, 23606 => 33423,
+23607 => 33418, 23608 => 33448, 23609 => 33412, 23610 => 33422, 23611 => 33425,
+23612 => 33431, 23613 => 33433, 23614 => 33451, 23615 => 33464, 23616 => 33470,
+23617 => 33456, 23618 => 33480, 23619 => 33482, 23620 => 33507, 23621 => 33432,
+23622 => 33463, 23623 => 33454, 23624 => 33483, 23625 => 33484, 23626 => 33473,
+23627 => 33449, 23628 => 33460, 23629 => 33441, 23630 => 33450, 23631 => 33439,
+23632 => 33476, 23633 => 33486, 23634 => 33444, 23635 => 33505, 23636 => 33545,
+23637 => 33527, 23638 => 33508, 23639 => 33551, 23640 => 33543, 23641 => 33500,
+23642 => 33524, 23643 => 33490, 23644 => 33496, 23645 => 33548, 23646 => 33531,
+23647 => 33491, 23648 => 33553, 23649 => 33562, 23650 => 33542, 23651 => 33556,
+23652 => 33557, 23653 => 33504, 23654 => 33493, 23655 => 33564, 23656 => 33617,
+23657 => 33627, 23658 => 33628, 23659 => 33544, 23660 => 33682, 23661 => 33596,
+23662 => 33588, 23663 => 33585, 23664 => 33691, 23665 => 33630, 23666 => 33583,
+23667 => 33615, 23668 => 33607, 23669 => 33603, 23670 => 33631, 23671 => 33600,
+23672 => 33559, 23673 => 33632, 23674 => 33581, 23675 => 33594, 23676 => 33587,
+23677 => 33638, 23678 => 33637, 23841 => 33640, 23842 => 33563, 23843 => 33641,
+23844 => 33644, 23845 => 33642, 23846 => 33645, 23847 => 33646, 23848 => 33712,
+23849 => 33656, 23850 => 33715, 23851 => 33716, 23852 => 33696, 23853 => 33706,
+23854 => 33683, 23855 => 33692, 23856 => 33669, 23857 => 33660, 23858 => 33718,
+23859 => 33705, 23860 => 33661, 23861 => 33720, 23862 => 33659, 23863 => 33688,
+23864 => 33694, 23865 => 33704, 23866 => 33722, 23867 => 33724, 23868 => 33729,
+23869 => 33793, 23870 => 33765, 23871 => 33752, 23872 => 22535, 23873 => 33816,
+23874 => 33803, 23875 => 33757, 23876 => 33789, 23877 => 33750, 23878 => 33820,
+23879 => 33848, 23880 => 33809, 23881 => 33798, 23882 => 33748, 23883 => 33759,
+23884 => 33807, 23885 => 33795, 23886 => 33784, 23887 => 33785, 23888 => 33770,
+23889 => 33733, 23890 => 33728, 23891 => 33830, 23892 => 33776, 23893 => 33761,
+23894 => 33884, 23895 => 33873, 23896 => 33882, 23897 => 33881, 23898 => 33907,
+23899 => 33927, 23900 => 33928, 23901 => 33914, 23902 => 33929, 23903 => 33912,
+23904 => 33852, 23905 => 33862, 23906 => 33897, 23907 => 33910, 23908 => 33932,
+23909 => 33934, 23910 => 33841, 23911 => 33901, 23912 => 33985, 23913 => 33997,
+23914 => 34000, 23915 => 34022, 23916 => 33981, 23917 => 34003, 23918 => 33994,
+23919 => 33983, 23920 => 33978, 23921 => 34016, 23922 => 33953, 23923 => 33977,
+23924 => 33972, 23925 => 33943, 23926 => 34021, 23927 => 34019, 23928 => 34060,
+23929 => 29965, 23930 => 34104, 23931 => 34032, 23932 => 34105, 23933 => 34079,
+23934 => 34106, 24097 => 34134, 24098 => 34107, 24099 => 34047, 24100 => 34044,
+24101 => 34137, 24102 => 34120, 24103 => 34152, 24104 => 34148, 24105 => 34142,
+24106 => 34170, 24107 => 30626, 24108 => 34115, 24109 => 34162, 24110 => 34171,
+24111 => 34212, 24112 => 34216, 24113 => 34183, 24114 => 34191, 24115 => 34169,
+24116 => 34222, 24117 => 34204, 24118 => 34181, 24119 => 34233, 24120 => 34231,
+24121 => 34224, 24122 => 34259, 24123 => 34241, 24124 => 34268, 24125 => 34303,
+24126 => 34343, 24127 => 34309, 24128 => 34345, 24129 => 34326, 24130 => 34364,
+24131 => 24318, 24132 => 24328, 24133 => 22844, 24134 => 22849, 24135 => 32823,
+24136 => 22869, 24137 => 22874, 24138 => 22872, 24139 => 21263, 24140 => 23586,
+24141 => 23589, 24142 => 23596, 24143 => 23604, 24144 => 25164, 24145 => 25194,
+24146 => 25247, 24147 => 25275, 24148 => 25290, 24149 => 25306, 24150 => 25303,
+24151 => 25326, 24152 => 25378, 24153 => 25334, 24154 => 25401, 24155 => 25419,
+24156 => 25411, 24157 => 25517, 24158 => 25590, 24159 => 25457, 24160 => 25466,
+24161 => 25486, 24162 => 25524, 24163 => 25453, 24164 => 25516, 24165 => 25482,
+24166 => 25449, 24167 => 25518, 24168 => 25532, 24169 => 25586, 24170 => 25592,
+24171 => 25568, 24172 => 25599, 24173 => 25540, 24174 => 25566, 24175 => 25550,
+24176 => 25682, 24177 => 25542, 24178 => 25534, 24179 => 25669, 24180 => 25665,
+24181 => 25611, 24182 => 25627, 24183 => 25632, 24184 => 25612, 24185 => 25638,
+24186 => 25633, 24187 => 25694, 24188 => 25732, 24189 => 25709, 24190 => 25750,
+24353 => 25722, 24354 => 25783, 24355 => 25784, 24356 => 25753, 24357 => 25786,
+24358 => 25792, 24359 => 25808, 24360 => 25815, 24361 => 25828, 24362 => 25826,
+24363 => 25865, 24364 => 25893, 24365 => 25902, 24366 => 24331, 24367 => 24530,
+24368 => 29977, 24369 => 24337, 24370 => 21343, 24371 => 21489, 24372 => 21501,
+24373 => 21481, 24374 => 21480, 24375 => 21499, 24376 => 21522, 24377 => 21526,
+24378 => 21510, 24379 => 21579, 24380 => 21586, 24381 => 21587, 24382 => 21588,
+24383 => 21590, 24384 => 21571, 24385 => 21537, 24386 => 21591, 24387 => 21593,
+24388 => 21539, 24389 => 21554, 24390 => 21634, 24391 => 21652, 24392 => 21623,
+24393 => 21617, 24394 => 21604, 24395 => 21658, 24396 => 21659, 24397 => 21636,
+24398 => 21622, 24399 => 21606, 24400 => 21661, 24401 => 21712, 24402 => 21677,
+24403 => 21698, 24404 => 21684, 24405 => 21714, 24406 => 21671, 24407 => 21670,
+24408 => 21715, 24409 => 21716, 24410 => 21618, 24411 => 21667, 24412 => 21717,
+24413 => 21691, 24414 => 21695, 24415 => 21708, 24416 => 21721, 24417 => 21722,
+24418 => 21724, 24419 => 21673, 24420 => 21674, 24421 => 21668, 24422 => 21725,
+24423 => 21711, 24424 => 21726, 24425 => 21787, 24426 => 21735, 24427 => 21792,
+24428 => 21757, 24429 => 21780, 24430 => 21747, 24431 => 21794, 24432 => 21795,
+24433 => 21775, 24434 => 21777, 24435 => 21799, 24436 => 21802, 24437 => 21863,
+24438 => 21903, 24439 => 21941, 24440 => 21833, 24441 => 21869, 24442 => 21825,
+24443 => 21845, 24444 => 21823, 24445 => 21840, 24446 => 21820, 24609 => 21815,
+24610 => 21846, 24611 => 21877, 24612 => 21878, 24613 => 21879, 24614 => 21811,
+24615 => 21808, 24616 => 21852, 24617 => 21899, 24618 => 21970, 24619 => 21891,
+24620 => 21937, 24621 => 21945, 24622 => 21896, 24623 => 21889, 24624 => 21919,
+24625 => 21886, 24626 => 21974, 24627 => 21905, 24628 => 21883, 24629 => 21983,
+24630 => 21949, 24631 => 21950, 24632 => 21908, 24633 => 21913, 24634 => 21994,
+24635 => 22007, 24636 => 21961, 24637 => 22047, 24638 => 21969, 24639 => 21995,
+24640 => 21996, 24641 => 21972, 24642 => 21990, 24643 => 21981, 24644 => 21956,
+24645 => 21999, 24646 => 21989, 24647 => 22002, 24648 => 22003, 24649 => 21964,
+24650 => 21965, 24651 => 21992, 24652 => 22005, 24653 => 21988, 24654 => 36756,
+24655 => 22046, 24656 => 22024, 24657 => 22028, 24658 => 22017, 24659 => 22052,
+24660 => 22051, 24661 => 22014, 24662 => 22016, 24663 => 22055, 24664 => 22061,
+24665 => 22104, 24666 => 22073, 24667 => 22103, 24668 => 22060, 24669 => 22093,
+24670 => 22114, 24671 => 22105, 24672 => 22108, 24673 => 22092, 24674 => 22100,
+24675 => 22150, 24676 => 22116, 24677 => 22129, 24678 => 22123, 24679 => 22139,
+24680 => 22140, 24681 => 22149, 24682 => 22163, 24683 => 22191, 24684 => 22228,
+24685 => 22231, 24686 => 22237, 24687 => 22241, 24688 => 22261, 24689 => 22251,
+24690 => 22265, 24691 => 22271, 24692 => 22276, 24693 => 22282, 24694 => 22281,
+24695 => 22300, 24696 => 24079, 24697 => 24089, 24698 => 24084, 24699 => 24081,
+24700 => 24113, 24701 => 24123, 24702 => 24124, 24865 => 24119, 24866 => 24132,
+24867 => 24148, 24868 => 24155, 24869 => 24158, 24870 => 24161, 24871 => 23692,
+24872 => 23674, 24873 => 23693, 24874 => 23696, 24875 => 23702, 24876 => 23688,
+24877 => 23704, 24878 => 23705, 24879 => 23697, 24880 => 23706, 24881 => 23708,
+24882 => 23733, 24883 => 23714, 24884 => 23741, 24885 => 23724, 24886 => 23723,
+24887 => 23729, 24888 => 23715, 24889 => 23745, 24890 => 23735, 24891 => 23748,
+24892 => 23762, 24893 => 23780, 24894 => 23755, 24895 => 23781, 24896 => 23810,
+24897 => 23811, 24898 => 23847, 24899 => 23846, 24900 => 23854, 24901 => 23844,
+24902 => 23838, 24903 => 23814, 24904 => 23835, 24905 => 23896, 24906 => 23870,
+24907 => 23860, 24908 => 23869, 24909 => 23916, 24910 => 23899, 24911 => 23919,
+24912 => 23901, 24913 => 23915, 24914 => 23883, 24915 => 23882, 24916 => 23913,
+24917 => 23924, 24918 => 23938, 24919 => 23961, 24920 => 23965, 24921 => 35955,
+24922 => 23991, 24923 => 24005, 24924 => 24435, 24925 => 24439, 24926 => 24450,
+24927 => 24455, 24928 => 24457, 24929 => 24460, 24930 => 24469, 24931 => 24473,
+24932 => 24476, 24933 => 24488, 24934 => 24493, 24935 => 24501, 24936 => 24508,
+24937 => 34914, 24938 => 24417, 24939 => 29357, 24940 => 29360, 24941 => 29364,
+24942 => 29367, 24943 => 29368, 24944 => 29379, 24945 => 29377, 24946 => 29390,
+24947 => 29389, 24948 => 29394, 24949 => 29416, 24950 => 29423, 24951 => 29417,
+24952 => 29426, 24953 => 29428, 24954 => 29431, 24955 => 29441, 24956 => 29427,
+24957 => 29443, 24958 => 29434, 25121 => 29435, 25122 => 29463, 25123 => 29459,
+25124 => 29473, 25125 => 29450, 25126 => 29470, 25127 => 29469, 25128 => 29461,
+25129 => 29474, 25130 => 29497, 25131 => 29477, 25132 => 29484, 25133 => 29496,
+25134 => 29489, 25135 => 29520, 25136 => 29517, 25137 => 29527, 25138 => 29536,
+25139 => 29548, 25140 => 29551, 25141 => 29566, 25142 => 33307, 25143 => 22821,
+25144 => 39143, 25145 => 22820, 25146 => 22786, 25147 => 39267, 25148 => 39271,
+25149 => 39272, 25150 => 39273, 25151 => 39274, 25152 => 39275, 25153 => 39276,
+25154 => 39284, 25155 => 39287, 25156 => 39293, 25157 => 39296, 25158 => 39300,
+25159 => 39303, 25160 => 39306, 25161 => 39309, 25162 => 39312, 25163 => 39313,
+25164 => 39315, 25165 => 39316, 25166 => 39317, 25167 => 24192, 25168 => 24209,
+25169 => 24203, 25170 => 24214, 25171 => 24229, 25172 => 24224, 25173 => 24249,
+25174 => 24245, 25175 => 24254, 25176 => 24243, 25177 => 36179, 25178 => 24274,
+25179 => 24273, 25180 => 24283, 25181 => 24296, 25182 => 24298, 25183 => 33210,
+25184 => 24516, 25185 => 24521, 25186 => 24534, 25187 => 24527, 25188 => 24579,
+25189 => 24558, 25190 => 24580, 25191 => 24545, 25192 => 24548, 25193 => 24574,
+25194 => 24581, 25195 => 24582, 25196 => 24554, 25197 => 24557, 25198 => 24568,
+25199 => 24601, 25200 => 24629, 25201 => 24614, 25202 => 24603, 25203 => 24591,
+25204 => 24589, 25205 => 24617, 25206 => 24619, 25207 => 24586, 25208 => 24639,
+25209 => 24609, 25210 => 24696, 25211 => 24697, 25212 => 24699, 25213 => 24698,
+25214 => 24642, 25377 => 24682, 25378 => 24701, 25379 => 24726, 25380 => 24730,
+25381 => 24749, 25382 => 24733, 25383 => 24707, 25384 => 24722, 25385 => 24716,
+25386 => 24731, 25387 => 24812, 25388 => 24763, 25389 => 24753, 25390 => 24797,
+25391 => 24792, 25392 => 24774, 25393 => 24794, 25394 => 24756, 25395 => 24864,
+25396 => 24870, 25397 => 24853, 25398 => 24867, 25399 => 24820, 25400 => 24832,
+25401 => 24846, 25402 => 24875, 25403 => 24906, 25404 => 24949, 25405 => 25004,
+25406 => 24980, 25407 => 24999, 25408 => 25015, 25409 => 25044, 25410 => 25077,
+25411 => 24541, 25412 => 38579, 25413 => 38377, 25414 => 38379, 25415 => 38385,
+25416 => 38387, 25417 => 38389, 25418 => 38390, 25419 => 38396, 25420 => 38398,
+25421 => 38403, 25422 => 38404, 25423 => 38406, 25424 => 38408, 25425 => 38410,
+25426 => 38411, 25427 => 38412, 25428 => 38413, 25429 => 38415, 25430 => 38418,
+25431 => 38421, 25432 => 38422, 25433 => 38423, 25434 => 38425, 25435 => 38426,
+25436 => 20012, 25437 => 29247, 25438 => 25109, 25439 => 27701, 25440 => 27732,
+25441 => 27740, 25442 => 27722, 25443 => 27811, 25444 => 27781, 25445 => 27792,
+25446 => 27796, 25447 => 27788, 25448 => 27752, 25449 => 27753, 25450 => 27764,
+25451 => 27766, 25452 => 27782, 25453 => 27817, 25454 => 27856, 25455 => 27860,
+25456 => 27821, 25457 => 27895, 25458 => 27896, 25459 => 27889, 25460 => 27863,
+25461 => 27826, 25462 => 27872, 25463 => 27862, 25464 => 27898, 25465 => 27883,
+25466 => 27886, 25467 => 27825, 25468 => 27859, 25469 => 27887, 25470 => 27902,
+25633 => 27961, 25634 => 27943, 25635 => 27916, 25636 => 27971, 25637 => 27976,
+25638 => 27911, 25639 => 27908, 25640 => 27929, 25641 => 27918, 25642 => 27947,
+25643 => 27981, 25644 => 27950, 25645 => 27957, 25646 => 27930, 25647 => 27983,
+25648 => 27986, 25649 => 27988, 25650 => 27955, 25651 => 28049, 25652 => 28015,
+25653 => 28062, 25654 => 28064, 25655 => 27998, 25656 => 28051, 25657 => 28052,
+25658 => 27996, 25659 => 28000, 25660 => 28028, 25661 => 28003, 25662 => 28186,
+25663 => 28103, 25664 => 28101, 25665 => 28126, 25666 => 28174, 25667 => 28095,
+25668 => 28128, 25669 => 28177, 25670 => 28134, 25671 => 28125, 25672 => 28121,
+25673 => 28182, 25674 => 28075, 25675 => 28172, 25676 => 28078, 25677 => 28203,
+25678 => 28270, 25679 => 28238, 25680 => 28267, 25681 => 28338, 25682 => 28255,
+25683 => 28294, 25684 => 28243, 25685 => 28244, 25686 => 28210, 25687 => 28197,
+25688 => 28228, 25689 => 28383, 25690 => 28337, 25691 => 28312, 25692 => 28384,
+25693 => 28461, 25694 => 28386, 25695 => 28325, 25696 => 28327, 25697 => 28349,
+25698 => 28347, 25699 => 28343, 25700 => 28375, 25701 => 28340, 25702 => 28367,
+25703 => 28303, 25704 => 28354, 25705 => 28319, 25706 => 28514, 25707 => 28486,
+25708 => 28487, 25709 => 28452, 25710 => 28437, 25711 => 28409, 25712 => 28463,
+25713 => 28470, 25714 => 28491, 25715 => 28532, 25716 => 28458, 25717 => 28425,
+25718 => 28457, 25719 => 28553, 25720 => 28557, 25721 => 28556, 25722 => 28536,
+25723 => 28530, 25724 => 28540, 25725 => 28538, 25726 => 28625, 25889 => 28617,
+25890 => 28583, 25891 => 28601, 25892 => 28598, 25893 => 28610, 25894 => 28641,
+25895 => 28654, 25896 => 28638, 25897 => 28640, 25898 => 28655, 25899 => 28698,
+25900 => 28707, 25901 => 28699, 25902 => 28729, 25903 => 28725, 25904 => 28751,
+25905 => 28766, 25906 => 23424, 25907 => 23428, 25908 => 23445, 25909 => 23443,
+25910 => 23461, 25911 => 23480, 25912 => 29999, 25913 => 39582, 25914 => 25652,
+25915 => 23524, 25916 => 23534, 25917 => 35120, 25918 => 23536, 25919 => 36423,
+25920 => 35591, 25921 => 36790, 25922 => 36819, 25923 => 36821, 25924 => 36837,
+25925 => 36846, 25926 => 36836, 25927 => 36841, 25928 => 36838, 25929 => 36851,
+25930 => 36840, 25931 => 36869, 25932 => 36868, 25933 => 36875, 25934 => 36902,
+25935 => 36881, 25936 => 36877, 25937 => 36886, 25938 => 36897, 25939 => 36917,
+25940 => 36918, 25941 => 36909, 25942 => 36911, 25943 => 36932, 25944 => 36945,
+25945 => 36946, 25946 => 36944, 25947 => 36968, 25948 => 36952, 25949 => 36962,
+25950 => 36955, 25951 => 26297, 25952 => 36980, 25953 => 36989, 25954 => 36994,
+25955 => 37000, 25956 => 36995, 25957 => 37003, 25958 => 24400, 25959 => 24407,
+25960 => 24406, 25961 => 24408, 25962 => 23611, 25963 => 21675, 25964 => 23632,
+25965 => 23641, 25966 => 23409, 25967 => 23651, 25968 => 23654, 25969 => 32700,
+25970 => 24362, 25971 => 24361, 25972 => 24365, 25973 => 33396, 25974 => 24380,
+25975 => 39739, 25976 => 23662, 25977 => 22913, 25978 => 22915, 25979 => 22925,
+25980 => 22953, 25981 => 22954, 25982 => 22947, 26145 => 22935, 26146 => 22986,
+26147 => 22955, 26148 => 22942, 26149 => 22948, 26150 => 22994, 26151 => 22962,
+26152 => 22959, 26153 => 22999, 26154 => 22974, 26155 => 23045, 26156 => 23046,
+26157 => 23005, 26158 => 23048, 26159 => 23011, 26160 => 23000, 26161 => 23033,
+26162 => 23052, 26163 => 23049, 26164 => 23090, 26165 => 23092, 26166 => 23057,
+26167 => 23075, 26168 => 23059, 26169 => 23104, 26170 => 23143, 26171 => 23114,
+26172 => 23125, 26173 => 23100, 26174 => 23138, 26175 => 23157, 26176 => 33004,
+26177 => 23210, 26178 => 23195, 26179 => 23159, 26180 => 23162, 26181 => 23230,
+26182 => 23275, 26183 => 23218, 26184 => 23250, 26185 => 23252, 26186 => 23224,
+26187 => 23264, 26188 => 23267, 26189 => 23281, 26190 => 23254, 26191 => 23270,
+26192 => 23256, 26193 => 23260, 26194 => 23305, 26195 => 23319, 26196 => 23318,
+26197 => 23346, 26198 => 23351, 26199 => 23360, 26200 => 23573, 26201 => 23580,
+26202 => 23386, 26203 => 23397, 26204 => 23411, 26205 => 23377, 26206 => 23379,
+26207 => 23394, 26208 => 39541, 26209 => 39543, 26210 => 39544, 26211 => 39546,
+26212 => 39551, 26213 => 39549, 26214 => 39552, 26215 => 39553, 26216 => 39557,
+26217 => 39560, 26218 => 39562, 26219 => 39568, 26220 => 39570, 26221 => 39571,
+26222 => 39574, 26223 => 39576, 26224 => 39579, 26225 => 39580, 26226 => 39581,
+26227 => 39583, 26228 => 39584, 26229 => 39586, 26230 => 39587, 26231 => 39589,
+26232 => 39591, 26233 => 32415, 26234 => 32417, 26235 => 32419, 26236 => 32421,
+26237 => 32424, 26238 => 32425, 26401 => 32429, 26402 => 32432, 26403 => 32446,
+26404 => 32448, 26405 => 32449, 26406 => 32450, 26407 => 32457, 26408 => 32459,
+26409 => 32460, 26410 => 32464, 26411 => 32468, 26412 => 32471, 26413 => 32475,
+26414 => 32480, 26415 => 32481, 26416 => 32488, 26417 => 32491, 26418 => 32494,
+26419 => 32495, 26420 => 32497, 26421 => 32498, 26422 => 32525, 26423 => 32502,
+26424 => 32506, 26425 => 32507, 26426 => 32510, 26427 => 32513, 26428 => 32514,
+26429 => 32515, 26430 => 32519, 26431 => 32520, 26432 => 32523, 26433 => 32524,
+26434 => 32527, 26435 => 32529, 26436 => 32530, 26437 => 32535, 26438 => 32537,
+26439 => 32540, 26440 => 32539, 26441 => 32543, 26442 => 32545, 26443 => 32546,
+26444 => 32547, 26445 => 32548, 26446 => 32549, 26447 => 32550, 26448 => 32551,
+26449 => 32554, 26450 => 32555, 26451 => 32556, 26452 => 32557, 26453 => 32559,
+26454 => 32560, 26455 => 32561, 26456 => 32562, 26457 => 32563, 26458 => 32565,
+26459 => 24186, 26460 => 30079, 26461 => 24027, 26462 => 30014, 26463 => 37013,
+26464 => 29582, 26465 => 29585, 26466 => 29614, 26467 => 29602, 26468 => 29599,
+26469 => 29647, 26470 => 29634, 26471 => 29649, 26472 => 29623, 26473 => 29619,
+26474 => 29632, 26475 => 29641, 26476 => 29640, 26477 => 29669, 26478 => 29657,
+26479 => 39036, 26480 => 29706, 26481 => 29673, 26482 => 29671, 26483 => 29662,
+26484 => 29626, 26485 => 29682, 26486 => 29711, 26487 => 29738, 26488 => 29787,
+26489 => 29734, 26490 => 29733, 26491 => 29736, 26492 => 29744, 26493 => 29742,
+26494 => 29740, 26657 => 29723, 26658 => 29722, 26659 => 29761, 26660 => 29788,
+26661 => 29783, 26662 => 29781, 26663 => 29785, 26664 => 29815, 26665 => 29805,
+26666 => 29822, 26667 => 29852, 26668 => 29838, 26669 => 29824, 26670 => 29825,
+26671 => 29831, 26672 => 29835, 26673 => 29854, 26674 => 29864, 26675 => 29865,
+26676 => 29840, 26677 => 29863, 26678 => 29906, 26679 => 29882, 26680 => 38890,
+26681 => 38891, 26682 => 38892, 26683 => 26444, 26684 => 26451, 26685 => 26462,
+26686 => 26440, 26687 => 26473, 26688 => 26533, 26689 => 26503, 26690 => 26474,
+26691 => 26483, 26692 => 26520, 26693 => 26535, 26694 => 26485, 26695 => 26536,
+26696 => 26526, 26697 => 26541, 26698 => 26507, 26699 => 26487, 26700 => 26492,
+26701 => 26608, 26702 => 26633, 26703 => 26584, 26704 => 26634, 26705 => 26601,
+26706 => 26544, 26707 => 26636, 26708 => 26585, 26709 => 26549, 26710 => 26586,
+26711 => 26547, 26712 => 26589, 26713 => 26624, 26714 => 26563, 26715 => 26552,
+26716 => 26594, 26717 => 26638, 26718 => 26561, 26719 => 26621, 26720 => 26674,
+26721 => 26675, 26722 => 26720, 26723 => 26721, 26724 => 26702, 26725 => 26722,
+26726 => 26692, 26727 => 26724, 26728 => 26755, 26729 => 26653, 26730 => 26709,
+26731 => 26726, 26732 => 26689, 26733 => 26727, 26734 => 26688, 26735 => 26686,
+26736 => 26698, 26737 => 26697, 26738 => 26665, 26739 => 26805, 26740 => 26767,
+26741 => 26740, 26742 => 26743, 26743 => 26771, 26744 => 26731, 26745 => 26818,
+26746 => 26990, 26747 => 26876, 26748 => 26911, 26749 => 26912, 26750 => 26873,
+26913 => 26916, 26914 => 26864, 26915 => 26891, 26916 => 26881, 26917 => 26967,
+26918 => 26851, 26919 => 26896, 26920 => 26993, 26921 => 26937, 26922 => 26976,
+26923 => 26946, 26924 => 26973, 26925 => 27012, 26926 => 26987, 26927 => 27008,
+26928 => 27032, 26929 => 27000, 26930 => 26932, 26931 => 27084, 26932 => 27015,
+26933 => 27016, 26934 => 27086, 26935 => 27017, 26936 => 26982, 26937 => 26979,
+26938 => 27001, 26939 => 27035, 26940 => 27047, 26941 => 27067, 26942 => 27051,
+26943 => 27053, 26944 => 27092, 26945 => 27057, 26946 => 27073, 26947 => 27082,
+26948 => 27103, 26949 => 27029, 26950 => 27104, 26951 => 27021, 26952 => 27135,
+26953 => 27183, 26954 => 27117, 26955 => 27159, 26956 => 27160, 26957 => 27237,
+26958 => 27122, 26959 => 27204, 26960 => 27198, 26961 => 27296, 26962 => 27216,
+26963 => 27227, 26964 => 27189, 26965 => 27278, 26966 => 27257, 26967 => 27197,
+26968 => 27176, 26969 => 27224, 26970 => 27260, 26971 => 27281, 26972 => 27280,
+26973 => 27305, 26974 => 27287, 26975 => 27307, 26976 => 29495, 26977 => 29522,
+26978 => 27521, 26979 => 27522, 26980 => 27527, 26981 => 27524, 26982 => 27538,
+26983 => 27539, 26984 => 27533, 26985 => 27546, 26986 => 27547, 26987 => 27553,
+26988 => 27562, 26989 => 36715, 26990 => 36717, 26991 => 36721, 26992 => 36722,
+26993 => 36723, 26994 => 36725, 26995 => 36726, 26996 => 36728, 26997 => 36727,
+26998 => 36729, 26999 => 36730, 27000 => 36732, 27001 => 36734, 27002 => 36737,
+27003 => 36738, 27004 => 36740, 27005 => 36743, 27006 => 36747, 27169 => 36749,
+27170 => 36750, 27171 => 36751, 27172 => 36760, 27173 => 36762, 27174 => 36558,
+27175 => 25099, 27176 => 25111, 27177 => 25115, 27178 => 25119, 27179 => 25122,
+27180 => 25121, 27181 => 25125, 27182 => 25124, 27183 => 25132, 27184 => 33255,
+27185 => 29935, 27186 => 29940, 27187 => 29951, 27188 => 29967, 27189 => 29969,
+27190 => 29971, 27191 => 25908, 27192 => 26094, 27193 => 26095, 27194 => 26096,
+27195 => 26122, 27196 => 26137, 27197 => 26482, 27198 => 26115, 27199 => 26133,
+27200 => 26112, 27201 => 28805, 27202 => 26359, 27203 => 26141, 27204 => 26164,
+27205 => 26161, 27206 => 26166, 27207 => 26165, 27208 => 32774, 27209 => 26207,
+27210 => 26196, 27211 => 26177, 27212 => 26191, 27213 => 26198, 27214 => 26209,
+27215 => 26199, 27216 => 26231, 27217 => 26244, 27218 => 26252, 27219 => 26279,
+27220 => 26269, 27221 => 26302, 27222 => 26331, 27223 => 26332, 27224 => 26342,
+27225 => 26345, 27226 => 36146, 27227 => 36147, 27228 => 36150, 27229 => 36155,
+27230 => 36157, 27231 => 36160, 27232 => 36165, 27233 => 36166, 27234 => 36168,
+27235 => 36169, 27236 => 36167, 27237 => 36173, 27238 => 36181, 27239 => 36185,
+27240 => 35271, 27241 => 35274, 27242 => 35275, 27243 => 35276, 27244 => 35278,
+27245 => 35279, 27246 => 35280, 27247 => 35281, 27248 => 29294, 27249 => 29343,
+27250 => 29277, 27251 => 29286, 27252 => 29295, 27253 => 29310, 27254 => 29311,
+27255 => 29316, 27256 => 29323, 27257 => 29325, 27258 => 29327, 27259 => 29330,
+27260 => 25352, 27261 => 25394, 27262 => 25520, 27425 => 25663, 27426 => 25816,
+27427 => 32772, 27428 => 27626, 27429 => 27635, 27430 => 27645, 27431 => 27637,
+27432 => 27641, 27433 => 27653, 27434 => 27655, 27435 => 27654, 27436 => 27661,
+27437 => 27669, 27438 => 27672, 27439 => 27673, 27440 => 27674, 27441 => 27681,
+27442 => 27689, 27443 => 27684, 27444 => 27690, 27445 => 27698, 27446 => 25909,
+27447 => 25941, 27448 => 25963, 27449 => 29261, 27450 => 29266, 27451 => 29270,
+27452 => 29232, 27453 => 34402, 27454 => 21014, 27455 => 32927, 27456 => 32924,
+27457 => 32915, 27458 => 32956, 27459 => 26378, 27460 => 32957, 27461 => 32945,
+27462 => 32939, 27463 => 32941, 27464 => 32948, 27465 => 32951, 27466 => 32999,
+27467 => 33000, 27468 => 33001, 27469 => 33002, 27470 => 32987, 27471 => 32962,
+27472 => 32964, 27473 => 32985, 27474 => 32973, 27475 => 32983, 27476 => 26384,
+27477 => 32989, 27478 => 33003, 27479 => 33009, 27480 => 33012, 27481 => 33005,
+27482 => 33037, 27483 => 33038, 27484 => 33010, 27485 => 33020, 27486 => 26389,
+27487 => 33042, 27488 => 35930, 27489 => 33078, 27490 => 33054, 27491 => 33068,
+27492 => 33048, 27493 => 33074, 27494 => 33096, 27495 => 33100, 27496 => 33107,
+27497 => 33140, 27498 => 33113, 27499 => 33114, 27500 => 33137, 27501 => 33120,
+27502 => 33129, 27503 => 33148, 27504 => 33149, 27505 => 33133, 27506 => 33127,
+27507 => 22605, 27508 => 23221, 27509 => 33160, 27510 => 33154, 27511 => 33169,
+27512 => 28373, 27513 => 33187, 27514 => 33194, 27515 => 33228, 27516 => 26406,
+27517 => 33226, 27518 => 33211, 27681 => 33217, 27682 => 33190, 27683 => 27428,
+27684 => 27447, 27685 => 27449, 27686 => 27459, 27687 => 27462, 27688 => 27481,
+27689 => 39121, 27690 => 39122, 27691 => 39123, 27692 => 39125, 27693 => 39129,
+27694 => 39130, 27695 => 27571, 27696 => 24384, 27697 => 27586, 27698 => 35315,
+27699 => 26000, 27700 => 40785, 27701 => 26003, 27702 => 26044, 27703 => 26054,
+27704 => 26052, 27705 => 26051, 27706 => 26060, 27707 => 26062, 27708 => 26066,
+27709 => 26070, 27710 => 28800, 27711 => 28828, 27712 => 28822, 27713 => 28829,
+27714 => 28859, 27715 => 28864, 27716 => 28855, 27717 => 28843, 27718 => 28849,
+27719 => 28904, 27720 => 28874, 27721 => 28944, 27722 => 28947, 27723 => 28950,
+27724 => 28975, 27725 => 28977, 27726 => 29043, 27727 => 29020, 27728 => 29032,
+27729 => 28997, 27730 => 29042, 27731 => 29002, 27732 => 29048, 27733 => 29050,
+27734 => 29080, 27735 => 29107, 27736 => 29109, 27737 => 29096, 27738 => 29088,
+27739 => 29152, 27740 => 29140, 27741 => 29159, 27742 => 29177, 27743 => 29213,
+27744 => 29224, 27745 => 28780, 27746 => 28952, 27747 => 29030, 27748 => 29113,
+27749 => 25150, 27750 => 25149, 27751 => 25155, 27752 => 25160, 27753 => 25161,
+27754 => 31035, 27755 => 31040, 27756 => 31046, 27757 => 31049, 27758 => 31067,
+27759 => 31068, 27760 => 31059, 27761 => 31066, 27762 => 31074, 27763 => 31063,
+27764 => 31072, 27765 => 31087, 27766 => 31079, 27767 => 31098, 27768 => 31109,
+27769 => 31114, 27770 => 31130, 27771 => 31143, 27772 => 31155, 27773 => 24529,
+27774 => 24528, 27937 => 24636, 27938 => 24669, 27939 => 24666, 27940 => 24679,
+27941 => 24641, 27942 => 24665, 27943 => 24675, 27944 => 24747, 27945 => 24838,
+27946 => 24845, 27947 => 24925, 27948 => 25001, 27949 => 24989, 27950 => 25035,
+27951 => 25041, 27952 => 25094, 27953 => 32896, 27954 => 32895, 27955 => 27795,
+27956 => 27894, 27957 => 28156, 27958 => 30710, 27959 => 30712, 27960 => 30720,
+27961 => 30729, 27962 => 30743, 27963 => 30744, 27964 => 30737, 27965 => 26027,
+27966 => 30765, 27967 => 30748, 27968 => 30749, 27969 => 30777, 27970 => 30778,
+27971 => 30779, 27972 => 30751, 27973 => 30780, 27974 => 30757, 27975 => 30764,
+27976 => 30755, 27977 => 30761, 27978 => 30798, 27979 => 30829, 27980 => 30806,
+27981 => 30807, 27982 => 30758, 27983 => 30800, 27984 => 30791, 27985 => 30796,
+27986 => 30826, 27987 => 30875, 27988 => 30867, 27989 => 30874, 27990 => 30855,
+27991 => 30876, 27992 => 30881, 27993 => 30883, 27994 => 30898, 27995 => 30905,
+27996 => 30885, 27997 => 30932, 27998 => 30937, 27999 => 30921, 28000 => 30956,
+28001 => 30962, 28002 => 30981, 28003 => 30964, 28004 => 30995, 28005 => 31012,
+28006 => 31006, 28007 => 31028, 28008 => 40859, 28009 => 40697, 28010 => 40699,
+28011 => 40700, 28012 => 30449, 28013 => 30468, 28014 => 30477, 28015 => 30457,
+28016 => 30471, 28017 => 30472, 28018 => 30490, 28019 => 30498, 28020 => 30489,
+28021 => 30509, 28022 => 30502, 28023 => 30517, 28024 => 30520, 28025 => 30544,
+28026 => 30545, 28027 => 30535, 28028 => 30531, 28029 => 30554, 28030 => 30568,
+28193 => 30562, 28194 => 30565, 28195 => 30591, 28196 => 30605, 28197 => 30589,
+28198 => 30592, 28199 => 30604, 28200 => 30609, 28201 => 30623, 28202 => 30624,
+28203 => 30640, 28204 => 30645, 28205 => 30653, 28206 => 30010, 28207 => 30016,
+28208 => 30030, 28209 => 30027, 28210 => 30024, 28211 => 30043, 28212 => 30066,
+28213 => 30073, 28214 => 30083, 28215 => 32600, 28216 => 32609, 28217 => 32607,
+28218 => 35400, 28219 => 32616, 28220 => 32628, 28221 => 32625, 28222 => 32633,
+28223 => 32641, 28224 => 32638, 28225 => 30413, 28226 => 30437, 28227 => 34866,
+28228 => 38021, 28229 => 38022, 28230 => 38023, 28231 => 38027, 28232 => 38026,
+28233 => 38028, 28234 => 38029, 28235 => 38031, 28236 => 38032, 28237 => 38036,
+28238 => 38039, 28239 => 38037, 28240 => 38042, 28241 => 38043, 28242 => 38044,
+28243 => 38051, 28244 => 38052, 28245 => 38059, 28246 => 38058, 28247 => 38061,
+28248 => 38060, 28249 => 38063, 28250 => 38064, 28251 => 38066, 28252 => 38068,
+28253 => 38070, 28254 => 38071, 28255 => 38072, 28256 => 38073, 28257 => 38074,
+28258 => 38076, 28259 => 38077, 28260 => 38079, 28261 => 38084, 28262 => 38088,
+28263 => 38089, 28264 => 38090, 28265 => 38091, 28266 => 38092, 28267 => 38093,
+28268 => 38094, 28269 => 38096, 28270 => 38097, 28271 => 38098, 28272 => 38101,
+28273 => 38102, 28274 => 38103, 28275 => 38105, 28276 => 38104, 28277 => 38107,
+28278 => 38110, 28279 => 38111, 28280 => 38112, 28281 => 38114, 28282 => 38116,
+28283 => 38117, 28284 => 38119, 28285 => 38120, 28286 => 38122, 28449 => 38121,
+28450 => 38123, 28451 => 38126, 28452 => 38127, 28453 => 38131, 28454 => 38132,
+28455 => 38133, 28456 => 38135, 28457 => 38137, 28458 => 38140, 28459 => 38141,
+28460 => 38143, 28461 => 38147, 28462 => 38146, 28463 => 38150, 28464 => 38151,
+28465 => 38153, 28466 => 38154, 28467 => 38157, 28468 => 38158, 28469 => 38159,
+28470 => 38162, 28471 => 38163, 28472 => 38164, 28473 => 38165, 28474 => 38166,
+28475 => 38168, 28476 => 38171, 28477 => 38173, 28478 => 38174, 28479 => 38175,
+28480 => 38178, 28481 => 38186, 28482 => 38187, 28483 => 38185, 28484 => 38188,
+28485 => 38193, 28486 => 38194, 28487 => 38196, 28488 => 38198, 28489 => 38199,
+28490 => 38200, 28491 => 38204, 28492 => 38206, 28493 => 38207, 28494 => 38210,
+28495 => 38197, 28496 => 38212, 28497 => 38213, 28498 => 38214, 28499 => 38217,
+28500 => 38220, 28501 => 38222, 28502 => 38223, 28503 => 38226, 28504 => 38227,
+28505 => 38228, 28506 => 38230, 28507 => 38231, 28508 => 38232, 28509 => 38233,
+28510 => 38235, 28511 => 38238, 28512 => 38239, 28513 => 38237, 28514 => 38241,
+28515 => 38242, 28516 => 38244, 28517 => 38245, 28518 => 38246, 28519 => 38247,
+28520 => 38248, 28521 => 38249, 28522 => 38250, 28523 => 38251, 28524 => 38252,
+28525 => 38255, 28526 => 38257, 28527 => 38258, 28528 => 38259, 28529 => 38202,
+28530 => 30695, 28531 => 30700, 28532 => 38601, 28533 => 31189, 28534 => 31213,
+28535 => 31203, 28536 => 31211, 28537 => 31238, 28538 => 23879, 28539 => 31235,
+28540 => 31234, 28541 => 31262, 28542 => 31252, 28705 => 31289, 28706 => 31287,
+28707 => 31313, 28708 => 40655, 28709 => 39333, 28710 => 31344, 28711 => 30344,
+28712 => 30350, 28713 => 30355, 28714 => 30361, 28715 => 30372, 28716 => 29918,
+28717 => 29920, 28718 => 29996, 28719 => 40480, 28720 => 40482, 28721 => 40488,
+28722 => 40489, 28723 => 40490, 28724 => 40491, 28725 => 40492, 28726 => 40498,
+28727 => 40497, 28728 => 40502, 28729 => 40504, 28730 => 40503, 28731 => 40505,
+28732 => 40506, 28733 => 40510, 28734 => 40513, 28735 => 40514, 28736 => 40516,
+28737 => 40518, 28738 => 40519, 28739 => 40520, 28740 => 40521, 28741 => 40523,
+28742 => 40524, 28743 => 40526, 28744 => 40529, 28745 => 40533, 28746 => 40535,
+28747 => 40538, 28748 => 40539, 28749 => 40540, 28750 => 40542, 28751 => 40547,
+28752 => 40550, 28753 => 40551, 28754 => 40552, 28755 => 40553, 28756 => 40554,
+28757 => 40555, 28758 => 40556, 28759 => 40561, 28760 => 40557, 28761 => 40563,
+28762 => 30098, 28763 => 30100, 28764 => 30102, 28765 => 30112, 28766 => 30109,
+28767 => 30124, 28768 => 30115, 28769 => 30131, 28770 => 30132, 28771 => 30136,
+28772 => 30148, 28773 => 30129, 28774 => 30128, 28775 => 30147, 28776 => 30146,
+28777 => 30166, 28778 => 30157, 28779 => 30179, 28780 => 30184, 28781 => 30182,
+28782 => 30180, 28783 => 30187, 28784 => 30183, 28785 => 30211, 28786 => 30193,
+28787 => 30204, 28788 => 30207, 28789 => 30224, 28790 => 30208, 28791 => 30213,
+28792 => 30220, 28793 => 30231, 28794 => 30218, 28795 => 30245, 28796 => 30232,
+28797 => 30229, 28798 => 30233, 28961 => 30235, 28962 => 30268, 28963 => 30242,
+28964 => 30240, 28965 => 30272, 28966 => 30253, 28967 => 30256, 28968 => 30271,
+28969 => 30261, 28970 => 30275, 28971 => 30270, 28972 => 30259, 28973 => 30285,
+28974 => 30302, 28975 => 30292, 28976 => 30300, 28977 => 30294, 28978 => 30315,
+28979 => 30319, 28980 => 32714, 28981 => 31462, 28982 => 31352, 28983 => 31353,
+28984 => 31360, 28985 => 31366, 28986 => 31368, 28987 => 31381, 28988 => 31398,
+28989 => 31392, 28990 => 31404, 28991 => 31400, 28992 => 31405, 28993 => 31411,
+28994 => 34916, 28995 => 34921, 28996 => 34930, 28997 => 34941, 28998 => 34943,
+28999 => 34946, 29000 => 34978, 29001 => 35014, 29002 => 34999, 29003 => 35004,
+29004 => 35017, 29005 => 35042, 29006 => 35022, 29007 => 35043, 29008 => 35045,
+29009 => 35057, 29010 => 35098, 29011 => 35068, 29012 => 35048, 29013 => 35070,
+29014 => 35056, 29015 => 35105, 29016 => 35097, 29017 => 35091, 29018 => 35099,
+29019 => 35082, 29020 => 35124, 29021 => 35115, 29022 => 35126, 29023 => 35137,
+29024 => 35174, 29025 => 35195, 29026 => 30091, 29027 => 32997, 29028 => 30386,
+29029 => 30388, 29030 => 30684, 29031 => 32786, 29032 => 32788, 29033 => 32790,
+29034 => 32796, 29035 => 32800, 29036 => 32802, 29037 => 32805, 29038 => 32806,
+29039 => 32807, 29040 => 32809, 29041 => 32808, 29042 => 32817, 29043 => 32779,
+29044 => 32821, 29045 => 32835, 29046 => 32838, 29047 => 32845, 29048 => 32850,
+29049 => 32873, 29050 => 32881, 29051 => 35203, 29052 => 39032, 29053 => 39040,
+29054 => 39043, 29217 => 39049, 29218 => 39052, 29219 => 39053, 29220 => 39055,
+29221 => 39060, 29222 => 39066, 29223 => 39067, 29224 => 39070, 29225 => 39071,
+29226 => 39073, 29227 => 39074, 29228 => 39077, 29229 => 39078, 29230 => 34381,
+29231 => 34388, 29232 => 34412, 29233 => 34414, 29234 => 34431, 29235 => 34426,
+29236 => 34428, 29237 => 34427, 29238 => 34472, 29239 => 34445, 29240 => 34443,
+29241 => 34476, 29242 => 34461, 29243 => 34471, 29244 => 34467, 29245 => 34474,
+29246 => 34451, 29247 => 34473, 29248 => 34486, 29249 => 34500, 29250 => 34485,
+29251 => 34510, 29252 => 34480, 29253 => 34490, 29254 => 34481, 29255 => 34479,
+29256 => 34505, 29257 => 34511, 29258 => 34484, 29259 => 34537, 29260 => 34545,
+29261 => 34546, 29262 => 34541, 29263 => 34547, 29264 => 34512, 29265 => 34579,
+29266 => 34526, 29267 => 34548, 29268 => 34527, 29269 => 34520, 29270 => 34513,
+29271 => 34563, 29272 => 34567, 29273 => 34552, 29274 => 34568, 29275 => 34570,
+29276 => 34573, 29277 => 34569, 29278 => 34595, 29279 => 34619, 29280 => 34590,
+29281 => 34597, 29282 => 34606, 29283 => 34586, 29284 => 34622, 29285 => 34632,
+29286 => 34612, 29287 => 34609, 29288 => 34601, 29289 => 34615, 29290 => 34623,
+29291 => 34690, 29292 => 34594, 29293 => 34685, 29294 => 34686, 29295 => 34683,
+29296 => 34656, 29297 => 34672, 29298 => 34636, 29299 => 34670, 29300 => 34699,
+29301 => 34643, 29302 => 34659, 29303 => 34684, 29304 => 34660, 29305 => 34649,
+29306 => 34661, 29307 => 34707, 29308 => 34735, 29309 => 34728, 29310 => 34770,
+29473 => 34758, 29474 => 34696, 29475 => 34693, 29476 => 34733, 29477 => 34711,
+29478 => 34691, 29479 => 34731, 29480 => 34789, 29481 => 34732, 29482 => 34741,
+29483 => 34739, 29484 => 34763, 29485 => 34771, 29486 => 34749, 29487 => 34769,
+29488 => 34752, 29489 => 34762, 29490 => 34779, 29491 => 34794, 29492 => 34784,
+29493 => 34798, 29494 => 34838, 29495 => 34835, 29496 => 34814, 29497 => 34826,
+29498 => 34843, 29499 => 34849, 29500 => 34873, 29501 => 34876, 29502 => 32566,
+29503 => 32578, 29504 => 32580, 29505 => 32581, 29506 => 33296, 29507 => 31482,
+29508 => 31485, 29509 => 31496, 29510 => 31491, 29511 => 31492, 29512 => 31509,
+29513 => 31498, 29514 => 31531, 29515 => 31503, 29516 => 31559, 29517 => 31544,
+29518 => 31530, 29519 => 31513, 29520 => 31534, 29521 => 31537, 29522 => 31520,
+29523 => 31525, 29524 => 31524, 29525 => 31539, 29526 => 31550, 29527 => 31518,
+29528 => 31576, 29529 => 31578, 29530 => 31557, 29531 => 31605, 29532 => 31564,
+29533 => 31581, 29534 => 31584, 29535 => 31598, 29536 => 31611, 29537 => 31586,
+29538 => 31602, 29539 => 31601, 29540 => 31632, 29541 => 31654, 29542 => 31655,
+29543 => 31672, 29544 => 31660, 29545 => 31645, 29546 => 31656, 29547 => 31621,
+29548 => 31658, 29549 => 31644, 29550 => 31650, 29551 => 31659, 29552 => 31668,
+29553 => 31697, 29554 => 31681, 29555 => 31692, 29556 => 31709, 29557 => 31706,
+29558 => 31717, 29559 => 31718, 29560 => 31722, 29561 => 31756, 29562 => 31742,
+29563 => 31740, 29564 => 31759, 29565 => 31766, 29566 => 31755, 29729 => 31775,
+29730 => 31786, 29731 => 31782, 29732 => 31800, 29733 => 31809, 29734 => 31808,
+29735 => 33278, 29736 => 33281, 29737 => 33282, 29738 => 33284, 29739 => 33260,
+29740 => 34884, 29741 => 33313, 29742 => 33314, 29743 => 33315, 29744 => 33325,
+29745 => 33327, 29746 => 33320, 29747 => 33323, 29748 => 33336, 29749 => 33339,
+29750 => 33331, 29751 => 33332, 29752 => 33342, 29753 => 33348, 29754 => 33353,
+29755 => 33355, 29756 => 33359, 29757 => 33370, 29758 => 33375, 29759 => 33384,
+29760 => 34942, 29761 => 34949, 29762 => 34952, 29763 => 35032, 29764 => 35039,
+29765 => 35166, 29766 => 32669, 29767 => 32671, 29768 => 32679, 29769 => 32687,
+29770 => 32688, 29771 => 32690, 29772 => 31868, 29773 => 25929, 29774 => 31889,
+29775 => 31901, 29776 => 31900, 29777 => 31902, 29778 => 31906, 29779 => 31922,
+29780 => 31932, 29781 => 31933, 29782 => 31937, 29783 => 31943, 29784 => 31948,
+29785 => 31949, 29786 => 31944, 29787 => 31941, 29788 => 31959, 29789 => 31976,
+29790 => 33390, 29791 => 26280, 29792 => 32703, 29793 => 32718, 29794 => 32725,
+29795 => 32741, 29796 => 32737, 29797 => 32742, 29798 => 32745, 29799 => 32750,
+29800 => 32755, 29801 => 31992, 29802 => 32119, 29803 => 32166, 29804 => 32174,
+29805 => 32327, 29806 => 32411, 29807 => 40632, 29808 => 40628, 29809 => 36211,
+29810 => 36228, 29811 => 36244, 29812 => 36241, 29813 => 36273, 29814 => 36199,
+29815 => 36205, 29816 => 35911, 29817 => 35913, 29818 => 37194, 29819 => 37200,
+29820 => 37198, 29821 => 37199, 29822 => 37220, 29985 => 37218, 29986 => 37217,
+29987 => 37232, 29988 => 37225, 29989 => 37231, 29990 => 37245, 29991 => 37246,
+29992 => 37234, 29993 => 37236, 29994 => 37241, 29995 => 37260, 29996 => 37253,
+29997 => 37264, 29998 => 37261, 29999 => 37265, 30000 => 37282, 30001 => 37283,
+30002 => 37290, 30003 => 37293, 30004 => 37294, 30005 => 37295, 30006 => 37301,
+30007 => 37300, 30008 => 37306, 30009 => 35925, 30010 => 40574, 30011 => 36280,
+30012 => 36331, 30013 => 36357, 30014 => 36441, 30015 => 36457, 30016 => 36277,
+30017 => 36287, 30018 => 36284, 30019 => 36282, 30020 => 36292, 30021 => 36310,
+30022 => 36311, 30023 => 36314, 30024 => 36318, 30025 => 36302, 30026 => 36303,
+30027 => 36315, 30028 => 36294, 30029 => 36332, 30030 => 36343, 30031 => 36344,
+30032 => 36323, 30033 => 36345, 30034 => 36347, 30035 => 36324, 30036 => 36361,
+30037 => 36349, 30038 => 36372, 30039 => 36381, 30040 => 36383, 30041 => 36396,
+30042 => 36398, 30043 => 36387, 30044 => 36399, 30045 => 36410, 30046 => 36416,
+30047 => 36409, 30048 => 36405, 30049 => 36413, 30050 => 36401, 30051 => 36425,
+30052 => 36417, 30053 => 36418, 30054 => 36433, 30055 => 36434, 30056 => 36426,
+30057 => 36464, 30058 => 36470, 30059 => 36476, 30060 => 36463, 30061 => 36468,
+30062 => 36485, 30063 => 36495, 30064 => 36500, 30065 => 36496, 30066 => 36508,
+30067 => 36510, 30068 => 35960, 30069 => 35970, 30070 => 35978, 30071 => 35973,
+30072 => 35992, 30073 => 35988, 30074 => 26011, 30075 => 35286, 30076 => 35294,
+30077 => 35290, 30078 => 35292, 30241 => 35301, 30242 => 35307, 30243 => 35311,
+30244 => 35390, 30245 => 35622, 30246 => 38739, 30247 => 38633, 30248 => 38643,
+30249 => 38639, 30250 => 38662, 30251 => 38657, 30252 => 38664, 30253 => 38671,
+30254 => 38670, 30255 => 38698, 30256 => 38701, 30257 => 38704, 30258 => 38718,
+30259 => 40832, 30260 => 40835, 30261 => 40837, 30262 => 40838, 30263 => 40839,
+30264 => 40840, 30265 => 40841, 30266 => 40842, 30267 => 40844, 30268 => 40702,
+30269 => 40715, 30270 => 40717, 30271 => 38585, 30272 => 38588, 30273 => 38589,
+30274 => 38606, 30275 => 38610, 30276 => 30655, 30277 => 38624, 30278 => 37518,
+30279 => 37550, 30280 => 37576, 30281 => 37694, 30282 => 37738, 30283 => 37834,
+30284 => 37775, 30285 => 37950, 30286 => 37995, 30287 => 40063, 30288 => 40066,
+30289 => 40069, 30290 => 40070, 30291 => 40071, 30292 => 40072, 30293 => 31267,
+30294 => 40075, 30295 => 40078, 30296 => 40080, 30297 => 40081, 30298 => 40082,
+30299 => 40084, 30300 => 40085, 30301 => 40090, 30302 => 40091, 30303 => 40094,
+30304 => 40095, 30305 => 40096, 30306 => 40097, 30307 => 40098, 30308 => 40099,
+30309 => 40101, 30310 => 40102, 30311 => 40103, 30312 => 40104, 30313 => 40105,
+30314 => 40107, 30315 => 40109, 30316 => 40110, 30317 => 40112, 30318 => 40113,
+30319 => 40114, 30320 => 40115, 30321 => 40116, 30322 => 40117, 30323 => 40118,
+30324 => 40119, 30325 => 40122, 30326 => 40123, 30327 => 40124, 30328 => 40125,
+30329 => 40132, 30330 => 40133, 30331 => 40134, 30332 => 40135, 30333 => 40138,
+30334 => 40139, 30497 => 40140, 30498 => 40141, 30499 => 40142, 30500 => 40143,
+30501 => 40144, 30502 => 40147, 30503 => 40148, 30504 => 40149, 30505 => 40151,
+30506 => 40152, 30507 => 40153, 30508 => 40156, 30509 => 40157, 30510 => 40159,
+30511 => 40162, 30512 => 38780, 30513 => 38789, 30514 => 38801, 30515 => 38802,
+30516 => 38804, 30517 => 38831, 30518 => 38827, 30519 => 38819, 30520 => 38834,
+30521 => 38836, 30522 => 39601, 30523 => 39600, 30524 => 39607, 30525 => 40536,
+30526 => 39606, 30527 => 39610, 30528 => 39612, 30529 => 39617, 30530 => 39616,
+30531 => 39621, 30532 => 39618, 30533 => 39627, 30534 => 39628, 30535 => 39633,
+30536 => 39749, 30537 => 39747, 30538 => 39751, 30539 => 39753, 30540 => 39752,
+30541 => 39757, 30542 => 39761, 30543 => 39144, 30544 => 39181, 30545 => 39214,
+30546 => 39253, 30547 => 39252, 30548 => 39647, 30549 => 39649, 30550 => 39654,
+30551 => 39663, 30552 => 39659, 30553 => 39675, 30554 => 39661, 30555 => 39673,
+30556 => 39688, 30557 => 39695, 30558 => 39699, 30559 => 39711, 30560 => 39715,
+30561 => 40637, 30562 => 40638, 30563 => 32315, 30564 => 40578, 30565 => 40583,
+30566 => 40584, 30567 => 40587, 30568 => 40594, 30569 => 37846, 30570 => 40605,
+30571 => 40607, 30572 => 40667, 30573 => 40668, 30574 => 40669, 30575 => 40672,
+30576 => 40671, 30577 => 40674, 30578 => 40681, 30579 => 40679, 30580 => 40677,
+30581 => 40682, 30582 => 40687, 30583 => 40738, 30584 => 40748, 30585 => 40751,
+30586 => 40761, 30587 => 40759, 30588 => 40765, 30589 => 40766, 30590 => 40772,
+0 => 0 );
+
+ function gb2utf8($gb) {
+ if( !trim($gb) ) return $gb;
+ $utf8='';
+ while($gb) {
+ if( ord(substr($gb,0,1)) > 127 ) {
+ $t=substr($gb,0,2);
+ $gb=substr($gb,2);
+ $utf8 .= $this->u2utf8($this->codetable[hexdec(bin2hex($t))-0x8080]);
+ }
+ else {
+ $t=substr($gb,0,1);
+ $gb=substr($gb,1);
+ $utf8 .= $this->u2utf8($t);
+ }
+ }
+ return $utf8;
+ }
+
+ function u2utf8($c) {
+ $str='';
+ if ($c < 0x80) {
+ $str.=$c;
+ }
+ else if ($c < 0x800) {
+ $str.=chr(0xC0 | $c>>6);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ else if ($c < 0x10000) {
+ $str.=chr(0xE0 | $c>>12);
+ $str.=chr(0x80 | $c>>6 & 0x3F);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ else if ($c < 0x200000) {
+ $str.=chr(0xF0 | $c>>18);
+ $str.=chr(0x80 | $c>>12 & 0x3F);
+ $str.=chr(0x80 | $c>>6 & 0x3F);
+ $str.=chr(0x80 | $c & 0x3F);
+ }
+ return $str;
+ }
+
+} // END Class
+
+?>
diff --git a/includes/jpgraph/jpgraph_gradient.php b/includes/jpgraph/jpgraph_gradient.php
new file mode 100644
index 0000000..69be051
--- /dev/null
+++ b/includes/jpgraph/jpgraph_gradient.php
@@ -0,0 +1,322 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_GRADIENT.PHP
+// Description: Create a color gradient
+// Created: 2003-02-01
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_gradient.php,v 1.1.2.7 2003/10/25 19:14:45 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+
+
+//===================================================
+// CLASS Gradient
+// Description: Handles gradient fills. This is to be
+// considered a "friend" class of Class Image.
+//===================================================
+class Gradient {
+ var $img=null;
+ var $numcolors=100;
+//---------------
+// CONSTRUCTOR
+ function Gradient(&$img) {
+ $this->img = $img;
+ }
+
+
+ function SetNumColors($aNum) {
+ $this->numcolors=$aNum;
+ }
+//---------------
+// PUBLIC METHODS
+ // Produce a gradient filled rectangle with a smooth transition between
+ // two colors.
+ // ($xl,$yt) Top left corner
+ // ($xr,$yb) Bottom right
+ // $from_color Starting color in gradient
+ // $to_color End color in the gradient
+ // $style Which way is the gradient oriented?
+ function FilledRectangle($xl,$yt,$xr,$yb,$from_color,$to_color,$style=1) {
+ switch( $style ) {
+ case GRAD_VER:
+ $steps = abs($xr-$xl);
+ $delta = $xr>=$xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for( $i=0, $x=$xl; $i < $steps; ++$i ) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yt,$x,$yb);
+ $x += $delta;
+ }
+ break;
+
+ case GRAD_HOR:
+ $steps = abs($yb-$yt);
+ $delta = $yb>=$yt ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($i=0,$y=$yt; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ break;
+
+ case GRAD_MIDHOR:
+ $steps = abs($yb-$yt)/2;
+ $delta = $yb >= $yt ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($y=$yt, $i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ --$i;
+ if( abs($yb-$yt) % 2 == 1 ) --$steps;
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ $this->img->Line($xl,$y,$xr,$y);
+ break;
+
+ case GRAD_MIDVER:
+ $steps = round(abs($xr-$xl)/2);
+ $delta = $xr>=$xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ --$i;
+ if( abs($xr-$xl) % 2 == 1 ) --$steps;
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $this->img->Line($x,$yb,$x,$yt);
+ break;
+
+ case GRAD_WIDE_MIDVER:
+ $diff = round(abs($xr-$xl));
+ $steps = floor(abs($diff)/3);
+ $firststep = $diff - 2*$steps ;
+ $delta = $xr >= $xl ? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $firststep; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ --$i;
+ $this->img->current_color = $colors[$i];
+ for($j=0; $j< $steps; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+ case GRAD_WIDE_MIDHOR:
+ $diff = round(abs($yb-$yt));
+ $steps = floor(abs($diff)/3);
+ $firststep = $diff - 2*$steps ;
+ $delta = $yb >= $yt? 1 : -1;
+ $this->GetColArray($from_color,$to_color,$firststep,$colors,$this->numcolors);
+ for($y=$yt, $i=0; $i < $firststep; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ --$i;
+ $this->img->current_color = $colors[$i];
+ for($j=0; $j < $steps; ++$j) {
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ for($j=0; $j < $steps; ++$j, --$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($xl,$y,$xr,$y);
+ $y += $delta;
+ }
+ break;
+
+ case GRAD_LEFT_REFLECTION:
+ $steps1 = round(0.3*abs($xr-$xl));
+ $delta = $xr>=$xl ? 1 : -1;
+
+ $this->GetColArray($from_color.':1.3',$to_color,$steps1,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps1; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps2 = max(1,round(0.08*abs($xr-$xl)));
+ $this->img->SetColor($to_color);
+ for($j=0; $j< $steps2; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps = abs($xr-$xl)-$steps1-$steps2;
+ $this->GetColArray($to_color,$from_color,$steps,$colors,$this->numcolors);
+ for($i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+ case GRAD_RIGHT_REFLECTION:
+ $steps1 = round(0.7*abs($xr-$xl));
+ $delta = $xr>=$xl ? 1 : -1;
+
+ $this->GetColArray($from_color,$to_color,$steps1,$colors,$this->numcolors);
+ for($x=$xl, $i=0; $i < $steps1; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps2 = max(1,round(0.08*abs($xr-$xl)));
+ $this->img->SetColor($to_color);
+ for($j=0; $j< $steps2; ++$j) {
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ $steps = abs($xr-$xl)-$steps1-$steps2;
+ $this->GetColArray($to_color,$from_color.':1.3',$steps,$colors,$this->numcolors);
+ for($i=0; $i < $steps; ++$i) {
+ $this->img->current_color = $colors[$i];
+ $this->img->Line($x,$yb,$x,$yt);
+ $x += $delta;
+ }
+ break;
+
+
+ case GRAD_CENTER:
+ $steps = floor(min(($yb-$yt)+1,($xr-$xl)+1)/2);
+ $this->GetColArray($from_color,$to_color,$steps,$colors,$this->numcolors);
+ $dx = ($xr-$xl)/2;
+ $dy = ($yb-$yt)/2;
+ $x=$xl;$y=$yt;$x2=$xr;$y2=$yb;
+ for($x=$xl, $i=0; $x < $xl+$dx && $y < $yt+$dy ; ++$x, ++$y, --$x2, --$y2, ++$i) {
+ assert( $i < count($colors));
+ $this->img->current_color = $colors[$i];
+ $this->img->Rectangle($x,$y,$x2,$y2);
+ }
+ $this->img->Line($x,$y,$x2,$y2);
+ break;
+
+ default:
+ die("JpGraph Error: Unknown gradient style (=$style).");
+ break;
+ }
+ }
+
+ // Fill a special case of a polygon with a flat bottom
+ // with a gradient. Can be used for filled line plots.
+ // Please note that this is NOT a generic gradient polygon fill
+ // routine. It assumes that the bottom is flat (like a drawing
+ // of a mountain)
+ function FilledFlatPolygon($pts,$from_color,$to_color) {
+ if( count($pts) == 0 ) return;
+
+ $maxy=$pts[1];
+ $miny=$pts[1];
+ $n = count($pts) ;
+ for( $i=0, $idx=0; $i < $n; $i += 2) {
+ $x = round($pts[$i]);
+ $y = round($pts[$i+1]);
+ $miny = min($miny,$y);
+ $maxy = max($maxy,$y);
+ }
+
+ $colors = array();
+ $this->GetColArray($from_color,$to_color,abs($maxy-$miny)+1,$colors,$this->numcolors);
+ for($i=$miny, $idx=0; $i <= $maxy; ++$i ) {
+ $colmap[$i] = $colors[$idx++];
+ }
+
+ $n = count($pts)/2 ;
+ $idx = 0 ;
+ while( $idx < $n-1 ) {
+ $p1 = array(round($pts[$idx*2]),round($pts[$idx*2+1]));
+ $p2 = array(round($pts[++$idx*2]),round($pts[$idx*2+1]));
+
+ // Find the largest rectangle we can fill
+ $y = max($p1[1],$p2[1]) ;
+ for($yy=$maxy; $yy > $y; --$yy) {
+ $this->img->current_color = $colmap[$yy];
+ $this->img->Line($p1[0],$yy,$p2[0]-1,$yy);
+ }
+
+ if( $p1[1] == $p2[1] ) continue;
+
+ // Fill the rest using lines (slow...)
+ $slope = ($p2[0]-$p1[0])/($p1[1]-$p2[1]);
+ $x1 = $p1[0];
+ $x2 = $p2[0]-1;
+ $start = $y;
+ if( $p1[1] > $p2[1] ) {
+ while( $y >= $p2[1] ) {
+ $x1=$slope*($start-$y)+$p1[0];
+ $this->img->current_color = $colmap[$y];
+ $this->img->Line($x1,$y,$x2,$y);
+ --$y;
+ }
+ }
+ else {
+ while( $y >= $p1[1] ) {
+ $x2=$p2[0]+$slope*($start-$y);
+ $this->img->current_color = $colmap[$y];
+ $this->img->Line($x1,$y,$x2,$y);
+ --$y;
+ }
+ }
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+ // Add to the image color map the necessary colors to do the transition
+ // between the two colors using $numcolors intermediate colors
+ function GetColArray($from_color,$to_color,$arr_size,&$colors,$numcols=100) {
+ if( $arr_size==0 ) return;
+ // If color is given as text get it's corresponding r,g,b values
+ $from_color = $this->img->rgb->Color($from_color);
+ $to_color = $this->img->rgb->Color($to_color);
+
+ $rdelta=($to_color[0]-$from_color[0])/$numcols;
+ $gdelta=($to_color[1]-$from_color[1])/$numcols;
+ $bdelta=($to_color[2]-$from_color[2])/$numcols;
+ $colorsperstep = $numcols/$arr_size;
+ $prevcolnum = -1;
+ $from_alpha = $from_color[3];
+ $to_alpha = $to_color[3];
+ $adelta = ( $to_alpha - $from_alpha ) / $numcols ;
+ for ($i=0; $i < $arr_size; ++$i) {
+ $colnum = floor($colorsperstep*$i);
+ if ( $colnum == $prevcolnum )
+ $colors[$i] = $colidx;
+ else {
+ $r = floor($from_color[0] + $colnum*$rdelta);
+ $g = floor($from_color[1] + $colnum*$gdelta);
+ $b = floor($from_color[2] + $colnum*$bdelta);
+ $alpha = $from_alpha + $colnum*$adelta;
+ $colidx = $this->img->rgb->Allocate(sprintf("#%02x%02x%02x",$r,$g,$b),$alpha);
+ $colors[$i] = $colidx;
+ }
+ $prevcolnum = $colnum;
+ }
+ }
+} // Class
+
+?>
diff --git a/includes/jpgraph/jpgraph_imgtrans.php b/includes/jpgraph/jpgraph_imgtrans.php
new file mode 100644
index 0000000..f982e87
--- /dev/null
+++ b/includes/jpgraph/jpgraph_imgtrans.php
@@ -0,0 +1,230 @@
+<?php
+//=======================================================================
+// File: JPGRAPH_IMGTRANS.PHP
+// Description: Extension for JpGraph to do some simple img transformations
+// Created: 2003-09-06
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_imgtrans.php,v 1.1.2.2 2003/10/12 19:06:46 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+// Defines for 3D skew directions
+DEFINE('SKEW3D_UP',0);
+DEFINE('SKEW3D_DOWN',1);
+DEFINE('SKEW3D_LEFT',2);
+DEFINE('SKEW3D_RIGHT',3);
+
+//------------------------------------------------------------------------
+// Class ImgTrans
+// Perform some simple image transformations.
+//------------------------------------------------------------------------
+class ImgTrans {
+ var $gdImg=null;
+
+ function ImgTrans($aGdImg) {
+ // Constructor
+ $this->gdImg = $aGdImg;
+ }
+
+ // --------------------------------------------------------------------
+ // _TransVert3D() and _TransHor3D() are helper methods to
+ // Skew3D().
+ // --------------------------------------------------------------------
+ function _TransVert3D($aGdImg,$aHorizon=100,$aSkewDist=120,$aDir=SKEW3D_DOWN,$aMinSize=true,$aFillColor='#FFFFFF',$aQuality=false,$aBorder=false,$aHorizonPos=0.5) {
+
+
+ // Parameter check
+ if( $aHorizonPos < 0 || $aHorizonPos > 1.0 ) {
+ JpGraphError::Raise("Value for image transformation out of bounds.\nVanishing point on horizon must be specified as a value between 0 and 1.");
+ }
+
+ $w = imagesx($aGdImg);
+ $h = imagesy($aGdImg);
+
+ // Create new image
+ $ww = $w;
+ if( $aMinSize )
+ $hh = ceil($h * $aHorizon / ($aSkewDist+$h));
+ else
+ $hh = $h;
+
+ $newgdh = imagecreatetruecolor($ww,$hh);
+ $crgb = new RGB( $newgdh );
+ $fillColor = $crgb->Allocate($aFillColor);
+ imagefilledrectangle($newgdh,0,0,$ww-1,$hh-1,$fillColor);
+
+ if( $aBorder ) {
+ $colidx = $crgb->Allocate($aBorder);
+ imagerectangle($newgdh,0,0,$ww-1,$hh-1,$colidx);
+ }
+
+ $mid = round($w * $aHorizonPos);
+
+ $last=$h;
+ for($y=0; $y < $h; ++$y) {
+
+ $yp = $h-$y-1;
+ $yt = floor($yp * $aHorizon / ($aSkewDist + $yp));
+
+ if( !$aQuality ) {
+ if( $last <= $yt ) continue ;
+ $last = $yt;
+ }
+
+ for($x=0; $x < $w; ++$x) {
+ $xt = ($x-$mid) * $aSkewDist / ($aSkewDist + $yp);
+ if( $aDir == SKEW3D_UP )
+ $rgb = imagecolorat($aGdImg,$x,$h-$y-1);
+ else
+ $rgb = imagecolorat($aGdImg,$x,$y);
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ $colidx = imagecolorallocate($newgdh,$r,$g,$b);
+ $xt = round($xt+$mid);
+ if( $aDir == SKEW3D_UP ) {
+ $syt = $yt;
+ }
+ else {
+ $syt = $hh-$yt-1;
+ }
+
+ if( !empty($set[$yt]) ) {
+ $nrgb = imagecolorat($newgdh,$xt,$syt);
+ $nr = ($nrgb >> 16) & 0xFF;
+ $ng = ($nrgb >> 8) & 0xFF;
+ $nb = $nrgb & 0xFF;
+ $colidx = imagecolorallocate($newgdh,floor(($r+$nr)/2),
+ floor(($g+$ng)/2),floor(($b+$nb)/2));
+ }
+
+ imagesetpixel($newgdh,$xt,$syt,$colidx);
+ }
+
+ $set[$yt] = true;
+ }
+
+ return $newgdh;
+ }
+
+ // --------------------------------------------------------------------
+ // _TransVert3D() and _TransHor3D() are helper methods to
+ // Skew3D().
+ // --------------------------------------------------------------------
+ function _TransHor3D($aGdImg,$aHorizon=100,$aSkewDist=120,$aDir=SKEW3D_LEFT,$aMinSize=true,$aFillColor='#FFFFFF',$aQuality=false,$aBorder=false,$aHorizonPos=0.5) {
+
+ $w = imagesx($aGdImg);
+ $h = imagesy($aGdImg);
+
+ // Create new image
+ $hh = $h;
+ if( $aMinSize )
+ $ww = ceil($w * $aHorizon / ($aSkewDist+$w));
+ else
+ $ww = $w;
+
+ $newgdh = imagecreatetruecolor($ww,$hh);
+ $crgb = new RGB( $newgdh );
+ $fillColor = $crgb->Allocate($aFillColor);
+ imagefilledrectangle($newgdh,0,0,$ww-1,$hh-1,$fillColor);
+
+ if( $aBorder ) {
+ $colidx = $crgb->Allocate($aBorder);
+ imagerectangle($newgdh,0,0,$ww-1,$hh-1,$colidx);
+ }
+
+ $mid = round($h * $aHorizonPos);
+
+ $last = -1;
+ for($x=0; $x < $w-1; ++$x) {
+ $xt = floor($x * $aHorizon / ($aSkewDist + $x));
+ if( !$aQuality ) {
+ if( $last >= $xt ) continue ;
+ $last = $xt;
+ }
+
+ for($y=0; $y < $h; ++$y) {
+ $yp = $h-$y-1;
+ $yt = ($yp-$mid) * $aSkewDist / ($aSkewDist + $x);
+
+ if( $aDir == SKEW3D_RIGHT )
+ $rgb = imagecolorat($aGdImg,$w-$x-1,$y);
+ else
+ $rgb = imagecolorat($aGdImg,$x,$y);
+ $r = ($rgb >> 16) & 0xFF;
+ $g = ($rgb >> 8) & 0xFF;
+ $b = $rgb & 0xFF;
+ $colidx = imagecolorallocate($newgdh,$r,$g,$b);
+ $yt = floor($hh-$yt-$mid-1);
+ if( $aDir == SKEW3D_RIGHT ) {
+ $sxt = $ww-$xt-1;
+ }
+ else
+ $sxt = $xt ;
+
+ if( !empty($set[$xt]) ) {
+ $nrgb = imagecolorat($newgdh,$sxt,$yt);
+ $nr = ($nrgb >> 16) & 0xFF;
+ $ng = ($nrgb >> 8) & 0xFF;
+ $nb = $nrgb & 0xFF;
+ $colidx = imagecolorallocate($newgdh,floor(($r+$nr)/2),
+ floor(($g+$ng)/2),floor(($b+$nb)/2));
+ }
+ imagesetpixel($newgdh,$sxt,$yt,$colidx);
+ }
+
+ $set[$xt] = true;
+ }
+
+ return $newgdh;
+ }
+
+ // --------------------------------------------------------------------
+ // Skew image for the apperance of a 3D effect
+ // This transforms an image into a 3D-skewed version
+ // of the image. The transformation is specified by giving the height
+ // of the artificial horizon and specifying a "skew" factor which
+ // is the distance on the horizon line between the point of
+ // convergence and perspective line.
+ //
+ // The function returns the GD handle of the transformed image
+ // leaving the original image untouched.
+ //
+ // Parameters:
+ // * $aGdImg, GD handle to the image to be transformed
+ // * $aHorizon, Distance to the horizon
+ // * $aSkewDist, Distance from the horizon point of convergence
+ // on the horizon line to the perspective points. A larger
+ // value will fore-shorten the image more
+ // * $aDir, parameter specifies type of convergence. This of this
+ // as the walls in a room you are looking at. This specifies if the
+ // image should be applied on the left,right,top or bottom walls.
+ // * $aMinSize, true=make the new image just as big as needed,
+ // false = keep the image the same size as the original image
+ // * $aFillColor, Background fill color in the image
+ // * $aHiQuality, true=performa some interpolation that improves
+ // the image quality but at the expense of performace. Enabling
+ // high quality will have a dramatic effect on the time it takes
+ // to transform an image.
+ // * $aBorder, if set to anything besides false this will draw a
+ // a border of the speciied color around the image
+ // --------------------------------------------------------------------
+ function Skew3D($aHorizon=120,$aSkewDist=150,$aDir=SKEW3D_DOWN,$aHiQuality=false,$aMinSize=true,$aFillColor='#FFFFFF',$aBorder=false) {
+ return $this->_Skew3D($this->gdImg,$aHorizon,$aSkewDist,$aDir,$aHiQuality,
+ $aMinSize,$aFillColor,$aBorder);
+ }
+
+ function _Skew3D($aGdImg,$aHorizon=120,$aSkewDist=150,$aDir=SKEW3D_DOWN,$aHiQuality=false,$aMinSize=true,$aFillColor='#FFFFFF',$aBorder=false) {
+ if( $aDir == SKEW3D_DOWN || $aDir == SKEW3D_UP )
+ return $this->_TransVert3D($aGdImg,$aHorizon,$aSkewDist,$aDir,$aMinSize,$aFillColor,$aHiQuality,$aBorder);
+ else
+ return $this->_TransHor3D($aGdImg,$aHorizon,$aSkewDist,$aDir,$aMinSize,$aFillColor,$aHiQuality,$aBorder);
+
+ }
+
+}
+
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_line.php b/includes/jpgraph/jpgraph_line.php
new file mode 100644
index 0000000..e2f8e3a
--- /dev/null
+++ b/includes/jpgraph/jpgraph_line.php
@@ -0,0 +1,444 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_LINE.PHP
+// Description: Line plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_line.php,v 1.48.2.3 2003/08/23 22:01:56 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+// constants for the (filled) area
+DEFINE("LP_AREA_FILLED", true);
+DEFINE("LP_AREA_NOT_FILLED", false);
+DEFINE("LP_AREA_BORDER",false);
+DEFINE("LP_AREA_NO_BORDER",true);
+
+//===================================================
+// CLASS LinePlot
+// Description:
+//===================================================
+class LinePlot extends Plot{
+ var $filled=false;
+ var $fill_color='blue';
+ var $mark=null;
+ var $step_style=false, $center=false;
+ var $line_style=1; // Default to solid
+ var $filledAreas = array(); // array of arrays(with min,max,col,filled in them)
+ var $barcenter=false; // When we mix line and bar. Should we center the line in the bar.
+ var $fillFromMin = false ;
+ var $fillgrad=false,$fillgrad_fromcolor='navy',$fillgrad_tocolor='silver',$fillgrad_numcolors=100;
+
+//---------------
+// CONSTRUCTOR
+ function LinePlot(&$datay,$datax=false) {
+ $this->Plot($datay,$datax);
+ $this->mark = new PlotMark();
+ }
+//---------------
+// PUBLIC METHODS
+
+ // Set style, filled or open
+ function SetFilled($aFlag=true) {
+ JpGraphError::Raise('LinePlot::SetFilled() is deprecated. Use SetFillColor()');
+ }
+
+ function SetBarCenter($aFlag=true) {
+ $this->barcenter=$aFlag;
+ }
+
+ function SetStyle($aStyle) {
+ $this->line_style=$aStyle;
+ }
+
+ function SetStepStyle($aFlag=true) {
+ $this->step_style = $aFlag;
+ }
+
+ function SetColor($aColor) {
+ parent::SetColor($aColor);
+ }
+
+ function SetFillFromYMin($f=true) {
+ $this->fillFromMin = $f ;
+ }
+
+ function SetFillColor($aColor,$aFilled=true) {
+ $this->fill_color=$aColor;
+ $this->filled=$aFilled;
+ }
+
+ function SetFillGradient($aFromColor,$aToColor,$aNumColors=100,$aFilled=true) {
+ $this->fillgrad_fromcolor = $aFromColor;
+ $this->fillgrad_tocolor = $aToColor;
+ $this->fillgrad_numcolors = $aNumColors;
+ $this->filled = $aFilled;
+ $this->fillgrad = true;
+ }
+
+ function Legend(&$graph) {
+ if( $this->legend!="" ) {
+ if( $this->filled ) {
+ $graph->legend->Add($this->legend,
+ $this->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ } else {
+ $graph->legend->Add($this->legend,
+ $this->color,$this->mark,$this->line_style,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+ }
+
+ function AddArea($aMin=0,$aMax=0,$aFilled=LP_AREA_NOT_FILLED,$aColor="gray9",$aBorder=LP_AREA_BORDER) {
+ if($aMin > $aMax) {
+ // swap
+ $tmp = $aMin;
+ $aMin = $aMax;
+ $aMax = $tmp;
+ }
+ $this->filledAreas[] = array($aMin,$aMax,$aColor,$aFilled,$aBorder);
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+
+ // If another plot type have already adjusted the
+ // offset we don't touch it.
+ // (We check for empty in case the scale is a log scale
+ // and hence doesn't contain any xlabel_offset)
+ if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
+ $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
+ if( $this->center ) {
+ ++$this->numpoints;
+ $a=0.5; $b=0.5;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ //$graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $numpoints=count($this->coords[0]);
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$numpoints )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $this->barcenter )
+ $textadj = 0.5-$xscale->text_scale_off;
+ else
+ $textadj = 0;
+
+ // Find the first numeric data point
+ $startpoint=0;
+ while( $startpoint < $numpoints && !is_numeric($this->coords[0][$startpoint]) )
+ ++$startpoint;
+
+ // Bail out if no data points
+ if( $startpoint == $numpoints )
+ return;
+
+ if( $exist_x )
+ $xs=$this->coords[1][$startpoint];
+ else
+ $xs= $textadj+$startpoint;
+
+ $img->SetStartPoint($xscale->Translate($xs),
+ $yscale->Translate($this->coords[0][$startpoint]));
+
+
+ if( $this->filled ) {
+ $cord[] = $xscale->Translate($xs);
+ $min = $yscale->GetMinVal();
+ if( $min > 0 || $this->fillFromMin )
+ $cord[] = $yscale->Translate($min);
+ else
+ $cord[] = $yscale->Translate(0);
+ }
+ $xt = $xscale->Translate($xs);
+ $yt = $yscale->Translate($this->coords[0][$startpoint]);
+ $cord[] = $xt;
+ $cord[] = $yt;
+ $yt_old = $yt;
+
+ $this->value->Stroke($img,$this->coords[0][$startpoint],$xt,$yt);
+
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+ $img->SetLineStyle($this->line_style);
+ for( $pnts=$startpoint+1; $pnts<$numpoints; ++$pnts) {
+
+ if( $exist_x ) $x=$this->coords[1][$pnts];
+ else $x=$pnts+$textadj;
+ $xt = $xscale->Translate($x);
+ $yt = $yscale->Translate($this->coords[0][$pnts]);
+
+ $y=$this->coords[0][$pnts];
+ if( $this->step_style && is_numeric($y) ) {
+ $img->StyleLineTo($xt,$yt_old);
+ $img->StyleLineTo($xt,$yt);
+
+ $cord[] = $xt;
+ $cord[] = $yt_old;
+
+ $cord[] = $xt;
+ $cord[] = $yt;
+
+ }
+ else {
+ if( is_numeric($y) || (is_string($y) && $y != "-") ) {
+ $tmp1=$this->coords[0][$pnts];
+ $tmp2=$this->coords[0][$pnts-1];
+ if( is_numeric($tmp1) && (is_numeric($tmp2) || $tmp2=="-" ) ) {
+ $img->StyleLineTo($xt,$yt);
+ }
+ else {
+ $img->SetStartPoint($xt,$yt);
+ }
+ if( is_numeric($tmp1) &&
+ (is_numeric($tmp2) || $tmp2=="-" || ($this->filled && $tmp2=='') ) ) {
+ $cord[] = $xt;
+ $cord[] = $yt;
+ }
+ }
+ }
+ $yt_old = $yt;
+
+ $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
+ }
+
+ if( $this->filled ) {
+ $cord[] = $xt;
+ if( $min > 0 || $this->fillFromMin )
+ $cord[] = $yscale->Translate($min);
+ else
+ $cord[] = $yscale->Translate(0);
+ if( $this->fillgrad ) {
+ $img->SetLineWeight(1);
+ $grad = new Gradient($img);
+ $grad->SetNumColors($this->fillgrad_numcolors);
+ $grad->FilledFlatPolygon($cord,$this->fillgrad_fromcolor,$this->fillgrad_tocolor);
+ $img->SetLineWeight($this->weight);
+ }
+ else {
+ $img->SetColor($this->fill_color);
+ $img->FilledPolygon($cord);
+ }
+ if( $this->line_weight > 0 ) {
+ $img->SetColor($this->color);
+ $img->Polygon($cord);
+ }
+ }
+
+ if(!empty($this->filledAreas)) {
+
+ $minY = $yscale->Translate($yscale->GetMinVal());
+ $factor = ($this->step_style ? 4 : 2);
+
+ for($i = 0; $i < sizeof($this->filledAreas); ++$i) {
+ // go through all filled area elements ordered by insertion
+ // fill polygon array
+ $areaCoords[] = $cord[$this->filledAreas[$i][0] * $factor];
+ $areaCoords[] = $minY;
+
+ $areaCoords =
+ array_merge($areaCoords,
+ array_slice($cord,
+ $this->filledAreas[$i][0] * $factor,
+ ($this->filledAreas[$i][1] - $this->filledAreas[$i][0] + ($this->step_style ? 0 : 1)) * $factor));
+ $areaCoords[] = $areaCoords[sizeof($areaCoords)-2]; // last x
+ $areaCoords[] = $minY; // last y
+
+ if($this->filledAreas[$i][3]) {
+ $img->SetColor($this->filledAreas[$i][2]);
+ $img->FilledPolygon($areaCoords);
+ $img->SetColor($this->color);
+ }
+ // Check if we should draw the frame.
+ // If not we still re-draw the line since it might have been
+ // partially overwritten by the filled area and it doesn't look
+ // very good.
+ // TODO: The behaviour is undefined if the line does not have
+ // any line at the position of the area.
+ if( $this->filledAreas[$i][4] )
+ $img->Polygon($areaCoords);
+ else
+ $img->Polygon($cord);
+
+ $areaCoords = array();
+ }
+ }
+
+ if( $this->mark->type == -1 || $this->mark->show == false )
+ return;
+
+ for( $pnts=0; $pnts<$numpoints; ++$pnts) {
+
+ if( $exist_x ) $x=$this->coords[1][$pnts];
+ else $x=$pnts+$textadj;
+ $xt = $xscale->Translate($x);
+ $yt = $yscale->Translate($this->coords[0][$pnts]);
+
+ if( is_numeric($this->coords[0][$pnts]) ) {
+ if( !empty($this->csimtargets[$pnts]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$pnts]);
+ $this->mark->SetCSIMAlt($this->csimalts[$pnts]);
+ }
+ if( $exist_x )
+ $x=$this->coords[1][$pnts];
+ else
+ $x=$pnts;
+ $this->mark->SetCSIMAltVal($this->coords[0][$pnts],$x);
+ $this->mark->Stroke($img,$xt,$yt);
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ $this->StrokeDataValue($img,$this->coords[0][$pnts],$xt,$yt);
+ }
+ }
+
+
+ }
+} // Class
+
+
+//===================================================
+// CLASS AccLinePlot
+// Description:
+//===================================================
+class AccLinePlot extends Plot {
+ var $plots=null,$nbrplots=0,$numpoints=0;
+//---------------
+// CONSTRUCTOR
+ function AccLinePlot($plots) {
+ $this->plots = $plots;
+ $this->nbrplots = count($plots);
+ $this->numpoints = $plots[0]->numpoints;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Legend(&$graph) {
+ foreach( $this->plots as $p )
+ $p->DoLegend($graph);
+ }
+
+ function Max() {
+ list($xmax) = $this->plots[0]->Max();
+ $nmax=0;
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x) = $this->plots[$i]->Max();
+ $xmax = Max($xmax,$x);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for line $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots max y-value since that
+ // would in most cases give to large y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymax[$i] = $y;
+ }
+ $ymax = max($ymax);
+ return array($xmax,$ymax);
+ }
+
+ function Min() {
+ $nmax=0;
+ list($xmin,$ysetmin) = $this->plots[0]->Min();
+ for($i=0; $i<count($this->plots); ++$i) {
+ $n = count($this->plots[$i]->coords[0]);
+ $nmax = max($nmax,$n);
+ list($x,$y) = $this->plots[$i]->Min();
+ $xmin = Min($xmin,$x);
+ $ysetmin = Min($y,$ysetmin);
+ }
+ for( $i = 0; $i < $nmax; $i++ ) {
+ // Get y-value for line $i by adding the
+ // individual bars from all the plots added.
+ // It would be wrong to just add the
+ // individual plots min y-value since that
+ // would in most cases give to small y-value.
+ $y=$this->plots[0]->coords[0][$i];
+ for( $j = 1; $j < $this->nbrplots; $j++ ) {
+ $y += $this->plots[ $j ]->coords[0][$i];
+ }
+ $ymin[$i] = $y;
+ }
+ $ymin = Min($ysetmin,Min($ymin));
+ return array($xmin,$ymin);
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+
+ // If another plot type have already adjusted the
+ // offset we don't touch it.
+ // (We check for empty in case the scale is a log scale
+ // and hence doesn't contain any xlabel_offset)
+
+ if( empty($graph->xaxis->scale->ticks->xlabel_offset) ||
+ $graph->xaxis->scale->ticks->xlabel_offset == 0 ) {
+ if( $this->center ) {
+ ++$this->numpoints;
+ $a=0.5; $b=0.5;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ $graph->xaxis->scale->ticks->SupressMinorTickMarks();
+ }
+
+ }
+
+ // To avoid duplicate of line drawing code here we just
+ // change the y-values for each plot and then restore it
+ // after we have made the stroke. We must do this copy since
+ // it wouldn't be possible to create an acc line plot
+ // with the same graphs, i.e AccLinePlot(array($pl,$pl,$pl));
+ // since this method would have a side effect.
+ function Stroke(&$img,&$xscale,&$yscale) {
+ $img->SetLineWeight($this->weight);
+ $this->numpoints = count($this->plots[0]->coords[0]);
+ // Allocate array
+ $coords[$this->nbrplots][$this->numpoints]=0;
+ for($i=0; $i<$this->numpoints; $i++) {
+ $coords[0][$i]=$this->plots[0]->coords[0][$i];
+ $accy=$coords[0][$i];
+ for($j=1; $j<$this->nbrplots; ++$j ) {
+ $coords[$j][$i] = $this->plots[$j]->coords[0][$i]+$accy;
+ $accy = $coords[$j][$i];
+ }
+ }
+ for($j=$this->nbrplots-1; $j>=0; --$j) {
+ $p=$this->plots[$j];
+ for( $i=0; $i<$this->numpoints; ++$i) {
+ $tmp[$i]=$p->coords[0][$i];
+ $p->coords[0][$i]=$coords[$j][$i];
+ }
+ $p->Stroke($img,$xscale,$yscale);
+ for( $i=0; $i<$this->numpoints; ++$i)
+ $p->coords[0][$i]=$tmp[$i];
+ $p->coords[0][]=$tmp;
+ }
+ }
+} // Class
+
+
+/* EOF */
+?>
diff --git a/includes/jpgraph/jpgraph_log.php b/includes/jpgraph/jpgraph_log.php
new file mode 100644
index 0000000..35338bf
--- /dev/null
+++ b/includes/jpgraph/jpgraph_log.php
@@ -0,0 +1,248 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_LOG.PHP
+// Description: Log scale plot extension for JpGraph
+// Created: 2001-01-08
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_log.php,v 1.15.2.4 2003/09/27 00:24:59 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+DEFINE('LOGLABELS_PLAIN',0);
+DEFINE('LOGLABELS_MAGNITUDE',1);
+
+//===================================================
+// CLASS LogScale
+// Description: Logarithmic scale between world and screen
+//===================================================
+class LogScale extends LinearScale {
+//---------------
+// CONSTRUCTOR
+
+ // Log scale is specified using the log of min and max
+ function LogScale($min,$max,$type="y") {
+ $this->LinearScale($min,$max,$type);
+ $this->ticks = new LogTicks();
+ $this->name = 'log';
+ }
+
+//----------------
+// PUBLIC METHODS
+
+ // Translate between world and screen
+ function Translate($a) {
+ if( !is_numeric($a) ) {
+ if( $a != '' && $a != '-' )
+ JpGraphError::Raise('Your data contains non-numeric values.');
+ }
+ if( $a < 0 ) {
+ JpGraphError::Raise("Negative data values can not be used in a log scale.");
+ exit(1);
+ }
+ if( $a==0 ) $a=1;
+ $a=log10($a);
+ return ceil($this->off + ($a*1.0 - $this->scale[0]) * $this->scale_factor);
+ }
+
+ // Relative translate (don't include offset) usefull when we just want
+ // to know the relative position (in pixels) on the axis
+ function RelTranslate($a) {
+ if( $a==0 ) $a=1;
+ $a=log10($a);
+ return round(($a*1.0 - $this->scale[0]) * $this->scale_factor);
+ }
+
+ // Use bcpow() for increased precision
+ function GetMinVal() {
+ if( function_exists("bcpow") )
+ return round(bcpow(10,$this->scale[0],15),14);
+ else
+ return round(pow(10,$this->scale[0]));
+ }
+
+ function GetMaxVal() {
+ if( function_exists("bcpow") )
+ return round(bcpow(10,$this->scale[1],15),14);
+ else
+ return round(pow(10,$this->scale[1]));
+ }
+
+ // Logarithmic autoscaling is much simplier since we just
+ // set the min and max to logs of the min and max values.
+ // Note that for log autoscale the "maxstep" the fourth argument
+ // isn't used. This is just included to give the method the same
+ // signature as the linear counterpart.
+ function AutoScale(&$img,$min,$max,$dummy) {
+ if( $min==0 ) $min=1;
+
+ if( $max <= 0 ) {
+ JpGraphError::Raise('Scale error for logarithmic scale. You have a problem with your data values. The max value must be greater than 0. It is mathematically impossible to have 0 in a logarithmic scale.');
+ }
+ $smin = floor(log10($min));
+ $smax = ceil(log10($max));
+ $this->Update($img,$smin,$smax);
+ }
+//---------------
+// PRIVATE METHODS
+} // Class
+
+//===================================================
+// CLASS LogTicks
+// Description:
+//===================================================
+class LogTicks extends Ticks{
+ var $label_logtype=LOGLABELS_MAGNITUDE;
+//---------------
+// CONSTRUCTOR
+ function LogTicks() {
+ }
+//---------------
+// PUBLIC METHODS
+ function IsSpecified() {
+ return true;
+ }
+
+ function SetLabelLogType($aType) {
+ $this->label_logtype = $aType;
+ }
+
+ // For log scale it's meaningless to speak about a major step
+ // We just return -1 to make the framework happy (specifically
+ // StrokeLabels() )
+ function GetMajor() {
+ return -1;
+ }
+
+ function SetTextLabelStart($aStart) {
+ JpGraphError::Raise('Specifying tick interval for a logarithmic scale is undefined. Remove any calls to SetTextLabelStart() or SetTextTickInterval() on the logarithmic scale.');
+ }
+
+ function SetXLabelOffset($dummy) {
+ // For log scales we dont care about XLabel offset
+ }
+
+ // Draw ticks on image "img" using scale "scale". The axis absolute
+ // position in the image is specified in pos, i.e. for an x-axis
+ // it specifies the absolute y-coord and for Y-ticks it specified the
+ // absolute x-position.
+ function Stroke(&$img,&$scale,$pos) {
+ $start = $scale->GetMinVal();
+ $limit = $scale->GetMaxVal();
+ $nextMajor = 10*$start;
+ $step = $nextMajor / 10.0;
+
+
+ $img->SetLineWeight($this->weight);
+
+ if( $scale->type == "y" ) {
+ // member direction specified if the ticks should be on
+ // left or right side.
+ $a=$pos + $this->direction*$this->GetMinTickAbsSize();
+ $a2=$pos + $this->direction*$this->GetMajTickAbsSize();
+
+ $count=1;
+ $this->maj_ticks_pos[0]=$scale->Translate($start);
+ $this->maj_ticklabels_pos[0]=$scale->Translate($start);
+ if( $this->supress_first )
+ $this->maj_ticks_label[0]="";
+ else {
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[0]=call_user_func($f,$start);
+ }
+ elseif( $this->label_logtype == LOGLABELS_PLAIN )
+ $this->maj_ticks_label[0]=$start;
+ else
+ $this->maj_ticks_label[0]='10^'.round(log10($start));
+ }
+ $i=1;
+ for($y=$start; $y<=$limit; $y+=$step,++$count ) {
+ $ys=$scale->Translate($y);
+ $this->ticks_pos[]=$ys;
+ $this->ticklabels_pos[]=$ys;
+ if( $count % 10 == 0 ) {
+ if( $this->majcolor!="" ) {
+ $img->PushColor($this->majcolor);
+ $img->Line($pos,$ys,$a2,$ys);
+ $img->PopColor();
+ }
+ else
+ $img->Line($pos,$ys,$a2,$ys);
+
+ $this->maj_ticks_pos[$i]=$ys;
+ $this->maj_ticklabels_pos[$i]=$ys;
+
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[$i]=call_user_func($f,$nextMajor);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[$i]=$nextMajor;
+ else
+ $this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
+ ++$i;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else {
+ if( $this->mincolor!="" ) $img->PushColor($this->mincolor);
+ $img->Line($pos,$ys,$a,$ys);
+ if( $this->mincolor!="" ) $img->PopCOlor();
+ }
+ }
+ }
+ else {
+ $a=$pos - $this->direction*$this->GetMinTickAbsSize();
+ $a2=$pos - $this->direction*$this->GetMajTickAbsSize();
+ $count=1;
+ $this->maj_ticks_pos[0]=$scale->Translate($start);
+ $this->maj_ticklabels_pos[0]=$scale->Translate($start);
+ if( $this->supress_first )
+ $this->maj_ticks_label[0]="";
+ else {
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[0]=call_user_func($f,$start);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[0]=$start;
+ else
+ $this->maj_ticks_label[0]='10^'.round(log10($start));
+ }
+ $i=1;
+ for($x=$start; $x<=$limit; $x+=$step,++$count ) {
+ $xs=$scale->Translate($x);
+ $this->ticks_pos[]=$xs;
+ $this->ticklabels_pos[]=$xs;
+ if( $count % 10 == 0 ) {
+ $img->Line($xs,$pos,$xs,$a2);
+ $this->maj_ticks_pos[$i]=$xs;
+ $this->maj_ticklabels_pos[$i]=$xs;
+
+ if( $this->label_formfunc != '' ) {
+ $f = $this->label_formfunc;
+ $this->maj_ticks_label[$i]=call_user_func($f,$nextMajor);
+ }
+ elseif( $this->label_logtype == 0 )
+ $this->maj_ticks_label[$i]=$nextMajor;
+ else
+ $this->maj_ticks_label[$i]='10^'.round(log10($nextMajor));
+ ++$i;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else
+ $img->Line($xs,$pos,$xs,$a);
+ }
+ }
+ return true;
+ }
+} // Class
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_pie.php b/includes/jpgraph/jpgraph_pie.php
new file mode 100644
index 0000000..cb92285
--- /dev/null
+++ b/includes/jpgraph/jpgraph_pie.php
@@ -0,0 +1,971 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_PIE.PHP
+// Description: Pie plot extension for JpGraph
+// Created: 2001-02-14
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_pie.php,v 1.49.2.14 2003/11/04 19:47:37 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002,2003 Johan Persson
+//========================================================================
+*/
+
+
+// Defines for PiePlot::SetLabelType()
+DEFINE("PIE_VALUE_ABS",1);
+DEFINE("PIE_VALUE_PER",0);
+DEFINE("PIE_VALUE_PERCENTAGE",0);
+DEFINE("PIE_VALUE_ADJPERCENTAGE",2);
+DEFINE("PIE_VALUE_ADJPER",2);
+
+//===================================================
+// CLASS PiePlot
+// Description: Draws a pie plot
+//===================================================
+class PiePlot {
+ var $posx=0.5,$posy=0.5;
+ var $radius=0.3;
+ var $explode_radius=array(),$explode_all=false,$explode_r=20;
+ var $labels=null, $legends=null;
+ var $csimtargets=null; // Array of targets for CSIM
+ var $csimareas=''; // Generated CSIM text
+ var $csimalts=null; // ALT tags for corresponding target
+ var $data=null;
+ var $title;
+ var $startangle=0;
+ var $weight=1, $color="black";
+ var $legend_margin=6,$show_labels=true;
+ var $themearr = array(
+ "earth" => array(136,34,40,45,46,62,63,134,74,10,120,136,141,168,180,77,209,218,346,395,89,430),
+ "pastel" => array(27,415,128,59,66,79,105,110,42,147,152,230,236,240,331,337,405,38),
+ "water" => array(8,370,24,40,335,56,213,237,268,14,326,387,10,388),
+ "sand" => array(27,168,34,170,19,50,65,72,131,209,46,393));
+ var $theme="earth";
+ var $setslicecolors=array();
+ var $labeltype=0; // Default to percentage
+ var $pie_border=true,$pie_interior_border=true;
+ var $value;
+ var $ishadowcolor='',$ishadowdrop=4;
+ var $ilabelposadj=1;
+ var $legendcsimtargets = array();
+ var $legendcsimalts = array();
+ var $adjusted_data = array();
+
+//---------------
+// CONSTRUCTOR
+ function PiePlot($data) {
+ $this->data = array_reverse($data);
+ $this->title = new Text("");
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->value = new DisplayValue();
+ $this->value->Show();
+ $this->value->SetFormat('%.1f%%');
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetCenter($x,$y=0.5) {
+ $this->posx = $x;
+ $this->posy = $y;
+ }
+
+ function SetColor($aColor) {
+ $this->color = $aColor;
+ }
+
+ function SetSliceColors($aColors) {
+ $this->setslicecolors = $aColors;
+ }
+
+ function SetShadow($aColor='darkgray',$aDropWidth=4) {
+ $this->ishadowcolor = $aColor;
+ $this->ishadowdrop = $aDropWidth;
+ }
+
+ function SetCSIMTargets($targets,$alts=null) {
+ $this->csimtargets=array_reverse($targets);
+ if( is_array($alts) )
+ $this->csimalts=array_reverse($alts);
+ }
+
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
+ //Slice number, ellipse centre (x,y), height, width, start angle, end angle
+ while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
+ while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
+
+ $sa = 2*M_PI - $sa;
+ $ea = 2*M_PI - $ea;
+
+ //add coordinates of the centre to the map
+ $coords = "$xc, $yc";
+
+ //add coordinates of the first point on the arc to the map
+ $xp = floor(($radius*cos($ea))+$xc);
+ $yp = floor($yc-$radius*sin($ea));
+ $coords.= ", $xp, $yp";
+
+ //add coordinates every 0.2 radians
+ $a=$ea+0.2;
+ while ($a<$sa) {
+ $xp = floor($radius*cos($a)+$xc);
+ $yp = floor($yc-$radius*sin($a));
+ $coords.= ", $xp, $yp";
+ $a += 0.2;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($radius*cos($sa)+$xc);
+ $yp = floor($yc-$radius*sin($sa));
+ $coords.= ", $xp, $yp";
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+
+ function SetTheme($aTheme) {
+ if( in_array($aTheme,array_keys($this->themearr)) )
+ $this->theme = $aTheme;
+ else
+ JpGraphError::Raise("PiePLot::SetTheme() Unknown theme: $aTheme");
+ }
+
+ function ExplodeSlice($e,$radius=20) {
+ if( ! is_integer($e) )
+ JpGraphError::Raise('Argument to PiePlot::ExplodeSlice() must be an integer');
+ $this->explode_radius[$e]=$radius;
+ }
+
+ function ExplodeAll($radius=20) {
+ $this->explode_all=true;
+ $this->explode_r = $radius;
+ }
+
+ function Explode($aExplodeArr) {
+ if( !is_array($aExplodeArr) ) {
+ JpGraphError::Raise("Argument to PiePlot::Explode() must be an array with integer distances.");
+ }
+ $this->explode_radius = $aExplodeArr;
+ }
+
+ function SetStartAngle($aStart) {
+ if( $aStart < 0 || $aStart > 360 ) {
+ JpGraphError::Raise('Slice start angle must be between 0 and 360 degrees.');
+ }
+ $this->startangle = 360-$aStart;
+ $this->startangle *= M_PI/180;
+ }
+
+ function SetFont($family,$style=FS_NORMAL,$size=10) {
+ JpGraphError::Raise('PiePlot::SetFont() is deprecated. Use PiePlot->value->SetFont() instead.');
+ }
+
+ // Size in percentage
+ function SetSize($aSize) {
+ if( ($aSize>0 && $aSize<=0.5) || ($aSize>10 && $aSize<1000) )
+ $this->radius = $aSize;
+ else
+ JpGraphError::Raise("PiePlot::SetSize() Radius for pie must either be specified as a fraction
+ [0, 0.5] of the size of the image or as an absolute size in pixels
+ in the range [10, 1000]");
+ }
+
+ function SetFontColor($aColor) {
+ JpGraphError::Raise('PiePlot::SetFontColor() is deprecated. Use PiePlot->value->SetColor() instead.');
+ }
+
+ // Set label arrays
+ function SetLegends($aLegend) {
+ $this->legends = $aLegend;
+ }
+
+ // Set text labels for slices
+ function SetLabels($aLabels,$aLblPosAdj="auto") {
+ $this->labels = array_reverse($aLabels);
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+ function SetLabelPos($aLblPosAdj) {
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+ // Should we display actual value or percentage?
+ function SetLabelType($t) {
+ if( $t < 0 || $t > 2 )
+ JpGraphError::Raise("PiePlot::SetLabelType() Type for pie plots must be 0 or 1 (not $t).");
+ $this->labeltype=$t;
+ }
+
+ function SetValueType($aType) {
+ $this->SetLabelType($aType);
+ }
+
+ // Should the circle around a pie plot be displayed
+ function ShowBorder($exterior=true,$interior=true) {
+ $this->pie_border = $exterior;
+ $this->pie_interior_border = $interior;
+ }
+
+ // Setup the legends
+ function Legend(&$graph) {
+ $colors = array_keys($graph->img->rgb->rgb_table);
+ sort($colors);
+ $ta=$this->themearr[$this->theme];
+ $n = count($this->data);
+
+ if( $this->setslicecolors==null ) {
+ $numcolors=count($ta);
+ if( get_class($this)==='pieplot3d' ) {
+ $ta = array_reverse(array_slice($ta,0,$n));
+ }
+ }
+ else {
+ $this->setslicecolors = array_slice($this->setslicecolors,0,$n);
+ $numcolors=count($this->setslicecolors);
+ if( $graph->pieaa && get_class($this)==='pieplot' ) {
+ $this->setslicecolors = array_reverse($this->setslicecolors);
+ }
+ }
+
+ $sum=0;
+ for($i=0; $i < $n; ++$i)
+ $sum += $this->data[$i];
+
+ // Bail out with error if the sum is 0
+ if( $sum==0 )
+ JpGraphError::Raise("Illegal pie plot. Sum of all data is zero for Pie!");
+
+ // Make sure we don't plot more values than data points
+ // (in case the user added more legends than data points)
+ $n = min(count($this->legends),count($this->data));
+ if( $this->legends != "" ) {
+ $this->legends = array_reverse(array_slice($this->legends,0,$n));
+ }
+ for( $i=$n-1; $i >= 0; --$i ) {
+ $l = $this->legends[$i];
+ // Replace possible format with actual values
+ if( $this->labeltype==0 ) {
+ $l = sprintf($l,100*$this->data[$i]/$sum);
+ $alt = sprintf($this->csimalts[$i],$this->data[$i]);
+
+ }
+ elseif( $this->labeltype == 1) {
+ $l = sprintf($l,$this->data[$i]);
+ $alt = sprintf($this->csimalts[$i],$this->data[$i]);
+
+ }
+ else {
+ $l = sprintf($l,$this->adjusted_data[$i]);
+ $alt = sprintf($this->csimalts[$i],$this->adjusted_data[$i]);
+ }
+
+
+ if( $this->setslicecolors==null ) {
+ $graph->legend->Add($l,$colors[$ta[$i%$numcolors]],"",0,
+ $this->csimtargets[$i],$alt);
+ }
+ else {
+ $graph->legend->Add($l,$this->setslicecolors[$i%$numcolors],"",0,
+ $this->csimtargets[$i],$alt);
+ }
+ }
+ }
+
+ // Adjust the rounded percetage value so that the sum of
+ // of the pie slices are always 100%
+ // Using the Hare/Niemeyer method
+ function AdjPercentage($aData,$aPrec=0) {
+ $mul=100;
+ if( $aPrec > 0 && $aPrec < 3 ) {
+ if( $aPrec == 1 )
+ $mul=1000;
+ else
+ $mul=10000;
+ }
+
+ $tmp = array();
+ $result = array();
+ $quote_sum=0;
+ $n = count($aData) ;
+ for( $i=0, $sum=0; $i < $n; ++$i )
+ $sum+=$aData[$i];
+ foreach($aData as $index => $value) {
+ $tmp_percentage=$value/$sum*$mul;
+ $result[$index]=floor($tmp_percentage);
+ $tmp[$index]=$tmp_percentage-$result[$index];
+ $quote_sum+=$result[$index];
+ }
+ if( $quote_sum == $mul) {
+ if( $mul > 100 ) {
+ $tmp = $mul / 100;
+ for( $i=0; $i < $n; ++$i ) {
+ $result[$i] /= $tmp ;
+ }
+ }
+ return $result;
+ }
+ arsort($tmp,SORT_NUMERIC);
+ reset($tmp);
+ for($i=0; $i < $mul-$quote_sum; $i++)
+ {
+ $result[key($tmp)]++;
+ next($tmp);
+ }
+ if( $mul > 100 ) {
+ $tmp = $mul / 100;
+ for( $i=0; $i < $n; ++$i ) {
+ $result[$i] /= $tmp ;
+ }
+ }
+ return $result;
+ }
+
+
+ function Stroke(&$img,$aaoption=0) {
+ // aaoption is used to handle antialias
+ // aaoption == 0 a normal pie
+ // aaoption == 1 just the body
+ // aaoption == 2 just the values
+
+ // Explode scaling. If anti anti alias we scale the image
+ // twice and we also need to scale the exploding distance
+ $expscale = $aaoption === 1 ? 2 : 1;
+
+ if( $this->labeltype == 2 ) {
+ // Adjust the data so that it will add up to 100%
+ $this->adjusted_data = $this->AdjPercentage($this->data);
+ }
+
+ $colors = array_keys($img->rgb->rgb_table);
+ sort($colors);
+ $ta=$this->themearr[$this->theme];
+ $n = count($this->data);
+
+ if( $this->setslicecolors==null ) {
+ $numcolors=count($ta);
+ }
+ else {
+ $this->setslicecolors = array_reverse(array_slice($this->setslicecolors,0,$n));
+ $numcolors=count($this->setslicecolors);
+ $tt = array_slice($this->setslicecolors,$n % $numcolors);
+ $tt2 = array_slice($this->setslicecolors,0,$n % $numcolors);
+ $tt2 = array_merge($tt, $tt2);
+ $this->setslicecolors = $tt + $tt2;
+ }
+
+ // Draw the slices
+ $sum=0;
+ for($i=0; $i < $n; ++$i)
+ $sum += $this->data[$i];
+
+ // Bail out with error if the sum is 0
+ if( $sum==0 )
+ JpGraphError::Raise("Sum of all data is 0 for Pie.");
+
+ // Set up the pie-circle
+ if( $this->radius <= 1 )
+ $radius = floor($this->radius*min($img->width,$img->height));
+ else {
+ $radius = $aaoption === 1 ? $this->radius*2 : $this->radius;
+ }
+
+ if( $this->posx <= 1 && $this->posx > 0 )
+ $xc = round($this->posx*$img->width);
+ else
+ $xc = $this->posx ;
+
+ if( $this->posy <= 1 && $this->posy > 0 )
+ $yc = round($this->posy*$img->height);
+ else
+ $yc = $this->posy ;
+
+ $n = count($this->data);
+
+ if( $this->explode_all )
+ for($i=0; $i < $n; ++$i)
+ $this->explode_radius[$i]=$this->explode_r;
+
+ if( $this->ishadowcolor != "" && $aaoption !== 2) {
+ $accsum=0;
+ $angle2 = $this->startangle;
+ $img->SetColor($this->ishadowcolor);
+ for($i=0; $sum > 0 && $i < $n; ++$i) {
+ $j = $n-$i-1;
+ $d = $this->data[$i];
+ $angle1 = $angle2;
+ $accsum += $d;
+ $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
+ if( empty($this->explode_radius[$j]) )
+ $this->explode_radius[$j]=0;
+
+ $la = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
+
+ $xcm = $xc + $this->explode_radius[$j]*cos($la)*$expscale;
+ $ycm = $yc - $this->explode_radius[$j]*sin($la)*$expscale;
+
+ $xcm += $this->ishadowdrop*$expscale;
+ $ycm += $this->ishadowdrop*$expscale;
+
+ $img->CakeSlice($xcm,$ycm,$radius,$radius,
+ $angle1*180/M_PI,$angle2*180/M_PI,$this->ishadowcolor);
+
+ }
+ }
+
+ $accsum=0;
+ $angle2 = $this->startangle;
+ $img->SetColor($this->color);
+ for($i=0; $sum>0 && $i < $n; ++$i) {
+ $j = $n-$i-1;
+ if( empty($this->explode_radius[$j]) )
+ $this->explode_radius[$j]=0;
+ $d = $this->data[$i];
+ $angle1 = $angle2;
+ $accsum += $d;
+ $angle2 = $this->startangle+2*M_PI*$accsum/$sum;
+ $this->la[$i] = 2*M_PI - (abs($angle2-$angle1)/2.0+$angle1);
+
+ if( $d == 0 ) continue;
+
+ if( $this->setslicecolors==null )
+ $slicecolor=$colors[$ta[$i%$numcolors]];
+ else
+ $slicecolor=$this->setslicecolors[$i%$numcolors];
+
+ if( $this->pie_interior_border && $aaoption===0 )
+ $img->SetColor($this->color);
+ else
+ $img->SetColor($slicecolor);
+
+ $arccolor = $this->pie_border && $aaoption===0 ? $this->color : "";
+
+ $xcm = $xc + $this->explode_radius[$j]*cos($this->la[$i])*$expscale;
+ $ycm = $yc - $this->explode_radius[$j]*sin($this->la[$i])*$expscale;
+
+ if( $aaoption !== 2 ) {
+ $img->CakeSlice($xcm,$ycm,$radius-1,$radius-1,
+ $angle1*180/M_PI,$angle2*180/M_PI,$slicecolor,$arccolor);
+ }
+
+ if( $this->csimtargets && $aaoption !== 1 )
+ $this->AddSliceToCSIM($i,$xcm,$ycm,$radius,$angle1,$angle2);
+ }
+
+ // Format the titles for each slice
+ for( $i=0; $i < $n; ++$i) {
+ if( $this->labeltype==0 ) {
+ if( $sum != 0 )
+ $l = 100.0*$this->data[$i]/$sum;
+ else
+ $l = 0.0;
+ }
+ elseif( $this->labeltype==1 ) {
+ $l = $this->data[$i]*1.0;
+ }
+ else {
+ $l = $this->adjusted_data[$i];
+ }
+ if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
+ $this->labels[$i]=sprintf($this->labels[$i],$l);
+ else
+ $this->labels[$i]=$l;
+ }
+
+ if( $this->value->show && $aaoption !== 1 ) {
+ $this->StrokeAllLabels($img,$xc,$yc,$radius);
+ }
+
+ // Adjust title position
+ if( $aaoption !== 1 ) {
+ $this->title->Pos($xc,
+ $yc-$this->title->GetFontHeight($img)-$radius-$this->title->margin,
+ "center","bottom");
+ $this->title->Stroke($img);
+ }
+
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ function StrokeAllLabels($img,$xc,$yc,$radius) {
+ $n = count($this->labels);
+ for($i=0; $i < $n; ++$i) {
+ $this->StrokeLabel($this->labels[$i],$img,$xc,$yc,
+ $this->la[$i],
+ $radius + $this->explode_radius[$n-$i-1]);
+ }
+ }
+
+ // Position the labels of each slice
+ function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
+
+ // Default value
+ if( $this->ilabelposadj === 'auto' )
+ $this->ilabelposadj = 0.65;
+
+ // We position the values diferently depending on if they are inside
+ // or outside the pie
+ if( $this->ilabelposadj < 1.0 ) {
+
+ $this->value->SetAlign('center','center');
+ $this->value->margin = 0;
+
+ $xt=round($this->ilabelposadj*$r*cos($a)+$xc);
+ $yt=round($yc-$this->ilabelposadj*$r*sin($a));
+
+ $this->value->Stroke($img,$label,$xt,$yt);
+ }
+ else {
+
+ $this->value->halign = "left";
+ $this->value->valign = "top";
+ $this->value->margin = 0;
+
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
+ $h=$img->GetTextHeight($label);
+ // For numeric values the format of the display value
+ // must be taken into account
+ if( is_numeric($label) ) {
+ if( $label > 0 )
+ $w=$img->GetTextWidth(sprintf($this->value->format,$label));
+ else
+ $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
+ }
+ else
+ $w=$img->GetTextWidth($label);
+
+ if( $this->ilabelposadj > 1.0 && $this->ilabelposadj < 5.0) {
+ $r *= $this->ilabelposadj;
+ }
+
+ $r += $img->GetFontHeight()/1.5;
+ $xt=round($r*cos($a)+$xc);
+ $yt=round($yc-$r*sin($a));
+
+ while( $a < 0 ) $a += 2*M_PI;
+ while( $a > 2*M_PI ) $a -= 2*M_PI;
+
+ if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
+
+ if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
+
+ $this->value->Stroke($img,$label,$xt-$dx*$w,$yt-$dy*$h);
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS PiePlotC
+// Description: Same as a normal pie plot but with a
+// filled circle in the center
+//===================================================
+class PiePlotC extends PiePlot {
+ var $imidsize=0.5; // Fraction of total width
+ var $imidcolor='white';
+ var $midtitle='';
+ var $middlecsimtarget="",$middlecsimalt="";
+
+ function PiePlotC($data,$aCenterTitle='') {
+ parent::PiePlot($data);
+ $this->midtitle = new Text();
+ $this->midtitle->ParagraphAlign('center');
+ }
+
+ function SetMid($aTitle,$aColor='white',$aSize=0.5) {
+ $this->midtitle->Set($aTitle);
+ $this->imidsize = $aSize ;
+ $this->imidcolor = $aColor ;
+ }
+
+ function SetMidTitle($aTitle) {
+ $this->midtitle->Set($aTitle);
+ }
+
+ function SetMidSize($aSize) {
+ $this->imidsize = $aSize ;
+ }
+
+ function SetMidColor($aColor) {
+ $this->imidcolor = $aColor ;
+ }
+
+ function SetMidCSIM($aTarget,$aAlt) {
+ $this->middlecsimtarget = $aTarget;
+ $this->middlecsimalt = $aAlt;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$radius,$sa,$ea) {
+ //Slice number, ellipse centre (x,y), radius, start angle, end angle
+ while( $sa > 2*M_PI ) $sa = $sa - 2*M_PI;
+ while( $ea > 2*M_PI ) $ea = $ea - 2*M_PI;
+
+ $sa = 2*M_PI - $sa;
+ $ea = 2*M_PI - $ea;
+
+ // Add inner circle first point
+ $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
+ $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
+ $coords = "$xp, $yp";
+
+ //add coordinates every 0.25 radians
+ $a=$ea+0.25;
+ while ($a < $sa) {
+ $xp = floor(($this->imidsize*$radius*cos($a)+$xc));
+ $yp = floor($yc-($this->imidsize*$radius*sin($a)));
+ $coords.= ", $xp, $yp";
+ $a += 0.25;
+ }
+
+ // Make sure we end at the last point
+ $xp = floor(($this->imidsize*$radius*cos($sa)+$xc));
+ $yp = floor($yc-($this->imidsize*$radius*sin($sa)));
+ $coords.= ", $xp, $yp";
+
+ // Straight line to outer circle
+ $xp = floor($radius*cos($sa)+$xc);
+ $yp = floor($yc-$radius*sin($sa));
+ $coords.= ", $xp, $yp";
+
+ //add coordinates every 0.25 radians
+ $a=$sa - 0.25;
+ while ($a > $ea) {
+ $xp = floor($radius*cos($a)+$xc);
+ $yp = floor($yc-$radius*sin($a));
+ $coords.= ", $xp, $yp";
+ $a -= 0.25;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($radius*cos($ea)+$xc);
+ $yp = floor($yc-$radius*sin($ea));
+ $coords.= ", $xp, $yp";
+
+ // Close the arc
+ $xp = floor(($this->imidsize*$radius*cos($ea))+$xc);
+ $yp = floor($yc-($this->imidsize*$radius*sin($ea)));
+ $coords .= ", $xp, $yp";
+
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".
+ $this->csimtargets[$i]."\"";
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+
+ function Stroke($img,$aaoption=0) {
+
+ // Stroke the pie but don't stroke values
+ $tmp = $this->value->show;
+ $this->value->show = false;
+ parent::Stroke($img,$aaoption);
+ $this->value->show = $tmp;
+
+ $xc = round($this->posx*$img->width);
+ $yc = round($this->posy*$img->height);
+
+ $radius = floor($this->radius * min($img->width,$img->height)) ;
+
+
+ if( $this->imidsize > 0 && $aaoption !== 2 ) {
+
+ if( $this->ishadowcolor != "" ) {
+ $img->SetColor($this->ishadowcolor);
+ $img->FilledCircle($xc+$this->ishadowdrop,$yc+$this->ishadowdrop,
+ round($radius*$this->imidsize));
+ }
+
+ $img->SetColor($this->imidcolor);
+ $img->FilledCircle($xc,$yc,round($radius*$this->imidsize));
+
+ if( $this->pie_border && $aaoption === 0 ) {
+ $img->SetColor($this->color);
+ $img->Circle($xc,$yc,round($radius*$this->imidsize));
+ }
+
+ if( !empty($this->middlecsimtarget) )
+ $this->AddMiddleCSIM($xc,$yc,round($radius*$this->imidsize));
+
+ }
+
+ if( $this->value->show && $aaoption !== 1) {
+ $this->StrokeAllLabels($img,$xc,$yc,$radius);
+ $this->midtitle->Pos($xc,$yc,'center','center');
+ $this->midtitle->Stroke($img);
+ }
+
+ }
+
+ function AddMiddleCSIM($xc,$yc,$r) {
+ $this->csimareas .= "<area shape=\"circle\" coords=\"$xc,$yc,$r\" href=\"".
+ $this->middlecsimtarget."\"";
+ if( !empty($this->middlecsimalt) ) {
+ $tmp = $this->middlecsimalt;
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+
+ function StrokeLabel($label,$img,$xc,$yc,$a,$r) {
+
+ if( $this->ilabelposadj === 'auto' )
+ $this->ilabelposadj = (1-$this->imidsize)/2+$this->imidsize;
+
+ parent::StrokeLabel($label,$img,$xc,$yc,$a,$r);
+
+ }
+
+}
+
+
+//===================================================
+// CLASS PieGraph
+// Description:
+//===================================================
+class PieGraph extends Graph {
+ var $posx, $posy, $radius;
+ var $legends=array();
+ var $plots=array();
+ var $pieaa = false ;
+//---------------
+// CONSTRUCTOR
+ function PieGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
+ $this->Graph($width,$height,$cachedName,$timeout,$inline);
+ $this->posx=$width/2;
+ $this->posy=$height/2;
+ $this->SetColor(array(255,255,255));
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Add($aObj) {
+
+ if( is_array($aObj) && count($aObj) > 0 )
+ $cl = get_class($aObj[0]);
+ else
+ $cl = get_class($aObj);
+
+ if( $cl == 'text' )
+ $this->AddText($aObj);
+ elseif( $cl == 'iconplot' )
+ $this->AddIcon($aObj);
+ else
+ $this->plots[] = $aObj;
+ }
+
+ function SetAntiAliasing($aFlg=true) {
+ $this->pieaa = $aFlg;
+ }
+
+ function SetColor($c) {
+ $this->SetMarginColor($c);
+ }
+
+
+ function DisplayCSIMAreas() {
+ $csim="";
+ foreach($this->plots as $p ) {
+ $csim .= $p->GetCSIMareas();
+ }
+ //$csim.= $this->legend->GetCSIMareas();
+ if (preg_match_all("/area shape=\"(\w+)\" coords=\"([0-9\, ]+)\"/", $csim, $coords)) {
+ $this->img->SetColor($this->csimcolor);
+ for ($i=0; $i<count($coords[0]); $i++) {
+ if ($coords[1][$i]=="poly") {
+ preg_match_all('/\s*([0-9]+)\s*,\s*([0-9]+)\s*,*/',$coords[2][$i],$pts);
+ $this->img->SetStartPoint($pts[1][count($pts[0])-1],$pts[2][count($pts[0])-1]);
+ for ($j=0; $j<count($pts[0]); $j++) {
+ $this->img->LineTo($pts[1][$j],$pts[2][$j]);
+ }
+ } else if ($coords[1][$i]=="rect") {
+ $pts = preg_split('/,/', $coords[2][$i]);
+ $this->img->SetStartPoint($pts[0],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[1]);
+ $this->img->LineTo($pts[2],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[3]);
+ $this->img->LineTo($pts[0],$pts[1]);
+
+ }
+ }
+ }
+ }
+
+ // Method description
+ function Stroke($aStrokeFileName="") {
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+
+
+ $n = count($this->plots);
+
+ if( $this->pieaa ) {
+
+ if( !$_csim ) {
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ }
+
+
+ $w = $this->img->width;
+ $h = $this->img->height;
+ $oldimg = $this->img->img;
+
+ $this->img->CreateImgCanvas(2*$w,2*$h);
+
+ $this->img->SetColor( $this->margin_color );
+ $this->img->FilledRectangle(0,0,2*$w-1,2*$h-1);
+
+ // Make all icons *2 i size since we will be scaling down the
+ // imahe to do the anti aliasing
+ $ni = count($this->iIcons);
+ for($i=0; $i < $ni; ++$i) {
+ $this->iIcons[$i]->iScale *= 2 ;
+ }
+ $this->StrokeIcons();
+
+ for($i=0; $i < $n; ++$i) {
+ if( $this->plots[$i]->posx > 1 )
+ $this->plots[$i]->posx *= 2 ;
+ if( $this->plots[$i]->posy > 1 )
+ $this->plots[$i]->posy *= 2 ;
+
+ $this->plots[$i]->Stroke($this->img,1);
+
+ if( $this->plots[$i]->posx > 1 )
+ $this->plots[$i]->posx /= 2 ;
+ if( $this->plots[$i]->posy > 1 )
+ $this->plots[$i]->posy /= 2 ;
+ }
+
+ $indent = $this->doframe ? ($this->frame_weight + ($this->doshadow ? $this->shadow_width : 0 )) : 0 ;
+ $indent += $this->framebevel ? $this->framebeveldepth + 1 : 0 ;
+ $this->img->CopyCanvasH($oldimg,$this->img->img,$indent,$indent,$indent,$indent,
+ $w-2*$indent,$h-2*$indent,2*($w-$indent),2*($h-$indent));
+
+ $this->img->img = $oldimg ;
+ $this->img->width = $w ;
+ $this->img->height = $h ;
+
+ for($i=0; $i < $n; ++$i) {
+ $this->plots[$i]->Stroke($this->img,2); // Stroke labels
+ $this->plots[$i]->Legend($this);
+ }
+
+ }
+ else {
+
+ if( !$_csim ) {
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ }
+
+ $this->StrokeIcons();
+
+ for($i=0; $i < $n; ++$i) {
+ $this->plots[$i]->Stroke($this->img);
+ $this->plots[$i]->Legend($this);
+ }
+ }
+
+
+ $this->legend->Stroke($this->img);
+ $this->footer->Stroke($this->img);
+
+ if( !$_csim ) {
+ $this->StrokeTitles();
+
+ // Stroke texts
+ if( $this->texts != null ) {
+ $n = count($this->texts);
+ for($i=0; $i < $n; ++$i ) {
+ $this->texts[$i]->Stroke($this->img);
+ }
+ }
+
+ if( _JPG_DEBUG ) {
+ $this->DisplayCSIMAreas();
+ }
+
+ // Should we do any final image transformation
+ if( $this->iImgTrans ) {
+ if( !class_exists('ImgTrans') ) {
+ require_once('jpgraph_imgtrans.php');
+ //JpGraphError::Raise('In order to use image transformation you must include the file jpgraph_imgtrans.php in your script.');
+ }
+
+ $tform = new ImgTrans($this->img->img);
+ $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
+ $this->iImgTransDirection,$this->iImgTransHighQ,
+ $this->iImgTransMinSize,$this->iImgTransFillColor,
+ $this->iImgTransBorder);
+ }
+
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+} // Class
+
+/* EOF */
+?>
diff --git a/includes/jpgraph/jpgraph_pie3d.php b/includes/jpgraph/jpgraph_pie3d.php
new file mode 100644
index 0000000..9894888
--- /dev/null
+++ b/includes/jpgraph/jpgraph_pie3d.php
@@ -0,0 +1,895 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_PIE3D.PHP
+// Description: 3D Pie plot extension for JpGraph
+// Created: 2001-03-24
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_pie3d.php,v 1.46.2.3 2003/08/15 11:07:07 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS PiePlot3D
+// Description: Plots a 3D pie with a specified projection
+// angle between 20 and 70 degrees.
+//===================================================
+class PiePlot3D extends PiePlot {
+ var $labelhintcolor="red",$showlabelhint=true,$labelmargin=0.30;
+ var $angle=50;
+ var $edgecolor="", $edgeweight=1;
+ var $iThickness=false;
+
+//---------------
+// CONSTRUCTOR
+ function PiePlot3d(&$data) {
+ $this->radius = 0.5;
+ $this->data = $data;
+ $this->title = new Text("");
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->value = new DisplayValue();
+ $this->value->Show();
+ $this->value->SetFormat('%.0f%%');
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // Set label arrays
+ function SetLegends($aLegend) {
+ $this->legends = array_reverse($aLegend);
+ }
+
+ function SetSliceColors($aColors) {
+ $this->setslicecolors = $aColors;
+ }
+
+ function Legend(&$aGraph) {
+ parent::Legend($aGraph);
+ $aGraph->legend->txtcol = array_reverse($aGraph->legend->txtcol);
+ }
+
+ function SetCSIMTargets($targets,$alts=null) {
+ $this->csimtargets = $targets;
+ $this->csimalts = $alts;
+ }
+
+ // Should the slices be separated by a line? If color is specified as "" no line
+ // will be used to separate pie slices.
+ function SetEdge($aColor,$aWeight=1) {
+ $this->edgecolor = $aColor;
+ $this->edgeweight = $aWeight;
+ }
+
+ // Specify projection angle for 3D in degrees
+ // Must be between 20 and 70 degrees
+ function SetAngle($a) {
+ if( $a<5 || $a>90 )
+ JpGraphError::Raise("PiePlot3D::SetAngle() 3D Pie projection angle must be between 5 and 85 degrees.");
+ else
+ $this->angle = $a;
+ }
+
+ function AddSliceToCSIM($i,$xc,$yc,$height,$width,$thick,$sa,$ea) { //Slice number, ellipse centre (x,y), height, width, start angle, end angle
+
+ $sa *= M_PI/180;
+ $ea *= M_PI/180;
+
+ //add coordinates of the centre to the map
+ $coords = "$xc, $yc";
+
+ //add coordinates of the first point on the arc to the map
+ $xp = floor($width*cos($sa)/2+$xc);
+ $yp = floor($yc-$height*sin($sa)/2);
+ $coords.= ", $xp, $yp";
+
+ //If on the front half, add the thickness offset
+ if ($sa >= M_PI && $sa <= 2*M_PI*1.01) {
+ $yp = floor($yp+$thick);
+ $coords.= ", $xp, $yp";
+ }
+
+ //add coordinates every 0.2 radians
+ $a=$sa+0.2;
+ while ($a<$ea) {
+ $xp = floor($width*cos($a)/2+$xc);
+ if ($a >= M_PI && $a <= 2*M_PI*1.01) {
+ $yp = floor($yc-($height*sin($a)/2)+$thick);
+ } else {
+ $yp = floor($yc-$height*sin($a)/2);
+ }
+ $coords.= ", $xp, $yp";
+ $a += 0.2;
+ }
+
+ //Add the last point on the arc
+ $xp = floor($width*cos($ea)/2+$xc);
+ $yp = floor($yc-$height*sin($ea)/2);
+
+
+ if ($ea >= M_PI && $ea <= 2*M_PI*1.01) {
+ $coords.= ", $xp, ".floor($yp+$thick);
+ }
+ $coords.= ", $xp, $yp";
+ $alt='';
+ if( !empty($this->csimalts[$i]) ) {
+ $tmp=sprintf($this->csimalts[$i],$this->data[$i]);
+ $alt="alt=\"$tmp\" title=\"$tmp\"";
+ }
+ if( !empty($this->csimtargets[$i]) )
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtargets[$i]."\" $alt>\n";
+ }
+
+ function SetLabels($aLabels,$aLblPosAdj="auto") {
+ $this->labels = $aLabels;
+ $this->ilabelposadj=$aLblPosAdj;
+ }
+
+
+ // Distance from the pie to the labels
+ function SetLabelMargin($m) {
+ assert($m>0 && $m<1);
+ $this->labelmargin=$m;
+ }
+
+ // Show a thin line from the pie to the label for a specific slice
+ function ShowLabelHint($f=true) {
+ $this->showlabelhint=$f;
+ }
+
+ // Set color of hint line to label for each slice
+ function SetLabelHintColor($c) {
+ $this->labelhintcolor=$c;
+ }
+
+ function SetHeight($aHeight) {
+ $this->iThickness = $aHeight;
+ }
+
+
+// Normalize Angle between 0-360
+ function NormAngle($a) {
+ // Normalize anle to 0 to 2M_PI
+ //
+ if( $a > 0 ) {
+ while($a > 360) $a -= 360;
+ }
+ else {
+ while($a < 0) $a += 360;
+ }
+ if( $a < 0 )
+ $a = 360 + $a;
+
+ if( $a == 360 ) $a=0;
+ return $a;
+ }
+
+
+
+// Draw one 3D pie slice at position ($xc,$yc) with height $z
+ function Pie3DSlice($img,$xc,$yc,$w,$h,$sa,$ea,$z,$fillcolor,$shadow=0.65) {
+
+ // Due to the way the 3D Pie algorithm works we are
+ // guaranteed that any slice we get into this method
+ // belongs to either the left or right side of the
+ // pie ellipse. Hence, no slice will cross 90 or 270
+ // point.
+ if( ($sa < 90 && $ea > 90) || ( ($sa > 90 && $sa < 270) && $ea > 270) ) {
+ JpGraphError::Raise('Internal assertion failed. Pie3D::Pie3DSlice');
+ exit(1);
+ }
+
+ $p[] = array();
+
+ // Setup pre-calculated values
+ $rsa = $sa/180*M_PI; // to Rad
+ $rea = $ea/180*M_PI; // to Rad
+ $sinsa = sin($rsa);
+ $cossa = cos($rsa);
+ $sinea = sin($rea);
+ $cosea = cos($rea);
+
+ // p[] is the points for the overall slice and
+ // pt[] is the points for the top pie
+
+ // Angular step when approximating the arc with a polygon train.
+ $step = 0.05;
+
+ if( $sa >= 270 ) {
+ if( $ea > 360 || ($ea > 0 && $ea <= 90) ) {
+ if( $ea > 0 && $ea <= 90 ) {
+ // Adjust angle to simplify conditions in loops
+ $rea += 2*M_PI;
+ }
+
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa);
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ for( $a=$rsa; $a < 2*M_PI; $a += $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w;
+ $p[] = $z+$yc;
+ $p[] = $xc+$w;
+ $p[] = $yc;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ for( $a=2*M_PI+$step; $a < $rea; $a += $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ else {
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa);
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ $rea = $rea == 0.0 ? 2*M_PI : $rea;
+ for( $a=$rsa; $a < $rea; $a += $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w*$cosea;
+ $p[] = $z+$yc-$h*$sinea;
+ $p[] = $xc+$w*$cosea;
+ $p[] = $yc-$h*$sinea;
+ $p[] = $xc;
+ $p[] = $yc;
+ }
+ }
+ elseif( $sa >= 180 ) {
+ $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a>$rsa; $a -= $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z+$yc-$h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ $p[] = $xc+$w*$cossa;
+ $p[] = $z+$yc-$h*$sinsa;
+ $p[] = $xc+$w*$cossa;
+ $p[] = $yc-$h*$sinsa;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ }
+ elseif( $sa >= 90 ) {
+ if( $ea > 180 ) {
+ $p = array($xc,$yc,$xc,$yc+$z,$xc+$w*$cosea,$z+$yc-$h*$sinea);
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a > M_PI; $a -= $step ) {
+ $tca = cos($a);
+ $tsa = sin($a);
+ $p[] = $xc+$w*$tca;
+ $p[] = $z + $yc - $h*$tsa;
+ $pt[] = $xc+$w*$tca;
+ $pt[] = $yc-$h*$tsa;
+ }
+
+ $p[] = $xc-$w;
+ $p[] = $z+$yc;
+ $p[] = $xc-$w;
+ $p[] = $yc;
+ $p[] = $xc;
+ $p[] = $yc;
+
+ $pt[] = $xc-$w;
+ $pt[] = $z+$yc;
+ $pt[] = $xc-$w;
+ $pt[] = $yc;
+
+ for( $a=M_PI-$step; $a > $rsa; $a -= $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ else { // $sa >= 90 && $ea <= 180
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cosea,$z+$yc-$h*$sinea,
+ $xc+$w*$cosea,$yc-$h*$sinea,
+ $xc,$yc);
+
+ $pt = array($xc,$yc,$xc+$w*$cosea,$yc-$h*$sinea);
+
+ for( $a=$rea; $a>$rsa; $a -= $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cossa;
+ $pt[] = $yc-$h*$sinsa;
+ $pt[] = $xc;
+ $pt[] = $yc;
+
+ }
+ }
+ else { // sa > 0 && ea < 90
+
+ $p = array($xc,$yc,$xc,$yc+$z,
+ $xc+$w*$cossa,$z+$yc-$h*$sinsa,
+ $xc+$w*$cossa,$yc-$h*$sinsa,
+ $xc,$yc);
+
+ $pt = array($xc,$yc,$xc+$w*$cossa,$yc-$h*$sinsa);
+
+ for( $a=$rsa; $a < $rea; $a += $step ) {
+ $pt[] = $xc + $w*cos($a);
+ $pt[] = $yc - $h*sin($a);
+ }
+
+ $pt[] = $xc+$w*$cosea;
+ $pt[] = $yc-$h*$sinea;
+ $pt[] = $xc;
+ $pt[] = $yc;
+ }
+
+ $img->PushColor($fillcolor.":".$shadow);
+ $img->FilledPolygon($p);
+ $img->PopColor();
+
+ $img->PushColor($fillcolor);
+ $img->FilledPolygon($pt);
+ $img->PopColor();
+ }
+
+// Draw a 3D Pie
+ function Pie3D($aaoption,$img,$data,$colors,$xc,$yc,$d,$angle,$z,
+ $shadow=0.65,$startangle=0,$edgecolor="",$edgeweight=1) {
+
+ //---------------------------------------------------------------------------
+ // As usual the algorithm get more complicated than I originally
+ // envisioned. I believe that this is as simple as it is possible
+ // to do it with the features I want. It's a good exercise to start
+ // thinking on how to do this to convince your self that all this
+ // is really needed for the general case.
+ //
+ // The algorithm two draw 3D pies without "real 3D" is done in
+ // two steps.
+ // First imagine the pie cut in half through a thought line between
+ // 12'a clock and 6'a clock. It now easy to imagine that we can plot
+ // the individual slices for each half by starting with the topmost
+ // pie slice and continue down to 6'a clock.
+ //
+ // In the algortithm this is done in three principal steps
+ // Step 1. Do the knife cut to ensure by splitting slices that extends
+ // over the cut line. This is done by splitting the original slices into
+ // upto 3 subslices.
+ // Step 2. Find the top slice for each half
+ // Step 3. Draw the slices from top to bottom
+ //
+ // The thing that slightly complicates this scheme with all the
+ // angle comparisons below is that we can have an arbitrary start
+ // angle so we must take into account the different equivalence classes.
+ // For the same reason we must walk through the angle array in a
+ // modulo fashion.
+ //
+ // Limitations of algorithm:
+ // * A small exploded slice which crosses the 270 degree point
+ // will get slightly nagged close to the center due to the fact that
+ // we print the slices in Z-order and that the slice left part
+ // get printed first and might get slightly nagged by a larger
+ // slice on the right side just before the right part of the small
+ // slice. Not a major problem though.
+ //---------------------------------------------------------------------------
+
+
+ // Determine the height of the ellippse which gives an
+ // indication of the inclination angle
+ $h = ($angle/90.0)*$d;
+ $sum = 0;
+ for($i=0; $i<count($data); ++$i ) {
+ $sum += $data[$i];
+ }
+
+ // Special optimization
+ if( $sum==0 ) return;
+
+ if( $this->labeltype == 2 ) {
+ $this->adjusted_data = $this->AdjPercentage($data);
+ }
+
+ // Setup the start
+ $accsum = 0;
+ $a = $startangle;
+ $a = $this->NormAngle($a);
+
+ //
+ // Step 1 . Split all slices that crosses 90 or 270
+ //
+ $idx=0;
+ $adjexplode=array();
+ $numcolors = count($colors);
+ for($i=0; $i<count($data); ++$i, ++$idx ) {
+ $da = $data[$i]/$sum * 360;
+
+ if( empty($this->explode_radius[$i]) )
+ $this->explode_radius[$i]=0;
+
+ $expscale=1;
+ if( $aaoption == 1 )
+ $expscale=2;
+
+ $la = $a + $da/2;
+ $explode = array( $xc + $this->explode_radius[$i]*cos($la*M_PI/180)*$expscale,
+ $yc - $this->explode_radius[$i]*sin($la*M_PI/180) * ($h/$d) *$expscale );
+ $adjexplode[$idx] = $explode;
+ $labeldata[$i] = array($la,$explode[0],$explode[1]);
+ $originalangles[$i] = array($a,$a+$da);
+
+ $ne = $this->NormAngle($a+$da);
+ if( $da <= 180 ) {
+ // If the slice size is <= 90 it can at maximum cut across
+ // one boundary (either 90 or 270) where it needs to be split
+ $split=-1; // no split
+ if( ($da<=90 && ($a <= 90 && $ne > 90)) ||
+ (($da <= 180 && $da >90) && (($a < 90 || $a >= 270) && $ne > 90)) ) {
+ $split = 90;
+ }
+ elseif( ($da<=90 && ($a <= 270 && $ne > 270)) ||
+ (($da<=180 && $da>90) && ($a >= 90 && $a < 270 && ($a+$da) > 270 )) ) {
+ $split = 270;
+ }
+ if( $split > 0 ) { // split in two
+ $angles[$idx] = array($a,$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ $angles[++$idx] = array($split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ else { // no split
+ $angles[$idx] = array($a,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ }
+ else {
+ // da>180
+ // Slice may, depending on position, cross one or two
+ // bonudaries
+
+ if( $a < 90 )
+ $split = 90;
+ elseif( $a <= 270 )
+ $split = 270;
+ else
+ $split = 90;
+
+ $angles[$idx] = array($a,$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ //if( $a+$da > 360-$split ) {
+ // For slices larger than 270 degrees we might cross
+ // another boundary as well. This means that we must
+ // split the slice further. The comparison gets a little
+ // bit complicated since we must take into accound that
+ // a pie might have a startangle >0 and hence a slice might
+ // wrap around the 0 angle.
+ // Three cases:
+ // a) Slice starts before 90 and hence gets a split=90, but
+ // we must also check if we need to split at 270
+ // b) Slice starts after 90 but before 270 and slices
+ // crosses 90 (after a wrap around of 0)
+ // c) If start is > 270 (hence the firstr split is at 90)
+ // and the slice is so large that it goes all the way
+ // around 270.
+ if( ($a < 90 && ($a+$da > 270)) ||
+ ($a > 90 && $a<=270 && ($a+$da>360+90) ) ||
+ ($a > 270 && $this->NormAngle($a+$da)>270) ) {
+ $angles[++$idx] = array($split,360-$split);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ $angles[++$idx] = array(360-$split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ else {
+ // Just a simple split to the previous decided
+ // angle.
+ $angles[++$idx] = array($split,$ne);
+ $adjcolors[$idx] = $colors[$i % $numcolors];
+ $adjexplode[$idx] = $explode;
+ }
+ }
+ $a += $da;
+ $a = $this->NormAngle($a);
+ }
+
+ // Total number of slices
+ $n = count($angles);
+
+ for($i=0; $i<$n; ++$i) {
+ list($dbgs,$dbge) = $angles[$i];
+ }
+
+ //
+ // Step 2. Find start index (first pie that starts in upper left quadrant)
+ //
+ $minval = $angles[0][0];
+ $min = 0;
+ for( $i=0; $i<$n; ++$i ) {
+ if( $angles[$i][0] < $minval ) {
+ $minval = $angles[$i][0];
+ $min = $i;
+ }
+ }
+ $j = $min;
+ $cnt = 0;
+ while( $angles[$j][1] <= 90 ) {
+ $j++;
+ if( $j>=$n) {
+ $j=0;
+ }
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal error (#1). Trying to wrap twice when looking for start index");
+ }
+ ++$cnt;
+ }
+ $start = $j;
+
+ //
+ // Step 3. Print slices in z-order
+ //
+ $cnt = 0;
+
+ // First stroke all the slices between 90 and 270 (left half circle)
+ // counterclockwise
+
+ while( $angles[$j][0] < 270 && $aaoption !== 2 ) {
+
+ list($x,$y) = $adjexplode[$j];
+
+ $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
+ $z,$adjcolors[$j],$shadow);
+
+ $last = array($x,$y,$j);
+
+ $j++;
+ if( $j >= $n ) $j=0;
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
+ }
+ ++$cnt;
+ }
+
+ $slice_left = $n-$cnt;
+ $j=$start-1;
+ if($j<0) $j=$n-1;
+ $cnt = 0;
+
+ // The stroke all slices from 90 to -90 (right half circle)
+ // clockwise
+ while( $cnt < $slice_left && $aaoption !== 2 ) {
+
+ list($x,$y) = $adjexplode[$j];
+
+ $this->Pie3DSlice($img,$x,$y,$d,$h,$angles[$j][0],$angles[$j][1],
+ $z,$adjcolors[$j],$shadow);
+ $j--;
+ if( $cnt > $n ) {
+ JpGraphError::Raise("Pie3D Internal Error: Z-Sorting algorithm for 3D Pies is not working properly (2). Trying to wrap twice while stroking.");
+ }
+ if($j<0) $j=$n-1;
+ $cnt++;
+ }
+
+ // Now do a special thing. Stroke the last slice on the left
+ // halfcircle one more time. This is needed in the case where
+ // the slice close to 270 have been exploded. In that case the
+ // part of the slice close to the center of the pie might be
+ // slightly nagged.
+ if( $aaoption !== 2 )
+ $this->Pie3DSlice($img,$last[0],$last[1],$d,$h,$angles[$last[2]][0],
+ $angles[$last[2]][1],$z,$adjcolors[$last[2]],$shadow);
+
+
+ if( $aaoption !== 1 ) {
+ // Now print possible labels and add csim
+ $img->SetFont($this->value->ff,$this->value->fs);
+ $margin = $img->GetFontHeight()/2;
+ for($i=0; $i < count($data); ++$i ) {
+ $la = $labeldata[$i][0];
+ $x = $labeldata[$i][1] + cos($la*M_PI/180)*($d+$margin);
+ $y = $labeldata[$i][2] - sin($la*M_PI/180)*($h+$margin);
+ if( $la > 180 && $la < 360 ) $y += $z;
+ if( $this->labeltype == 0 ) {
+ if( $sum > 0 )
+ $l = 100*$data[$i]/$sum;
+ else
+ $l = 0;
+ }
+ elseif( $this->labeltype == 1 ) {
+ $l = $data[$i];
+ }
+ else {
+ $l = $this->adjusted_data[$i];
+ }
+ if( isset($this->labels[$i]) && is_string($this->labels[$i]) )
+ $l=sprintf($this->labels[$i],$l);
+
+ $this->StrokeLabels($l,$img,$labeldata[$i][0]*M_PI/180,$x,$y,$z);
+
+ $this->AddSliceToCSIM($i,$labeldata[$i][1],$labeldata[$i][2],$h*2,$d*2,$z,
+ $originalangles[$i][0],$originalangles[$i][1]);
+ }
+ }
+
+ //
+ // Finally add potential lines in pie
+ //
+
+ if( $edgecolor=="" || $aaoption !== 0 ) return;
+
+ $accsum = 0;
+ $a = $startangle;
+ $a = $this->NormAngle($a);
+
+ $a *= M_PI/180.0;
+
+ $idx=0;
+ $img->PushColor($edgecolor);
+ $img->SetLineWeight($edgeweight);
+
+ $fulledge = true;
+ for($i=0; $i < count($data) && $fulledge; ++$i ) {
+ if( empty($this->explode_radius[$i]) )
+ $this->explode_radius[$i]=0;
+ if( $this->explode_radius[$i] > 0 ) {
+ $fulledge = false;
+ }
+ }
+
+
+ for($i=0; $i < count($data); ++$i, ++$idx ) {
+
+ $da = $data[$i]/$sum * 2*M_PI;
+ $this->StrokeFullSliceFrame($img,$xc,$yc,$a,$a+$da,$d,$h,$z,$edgecolor,
+ $this->explode_radius[$i],$fulledge);
+ $a += $da;
+ }
+ $img->PopColor();
+ }
+
+ function StrokeFullSliceFrame($img,$xc,$yc,$sa,$ea,$w,$h,$z,$edgecolor,$exploderadius,$fulledge) {
+ $step = 0.02;
+
+ if( $exploderadius > 0 ) {
+ $la = ($sa+$ea)/2;
+ $xc += $exploderadius*cos($la);
+ $yc -= $exploderadius*sin($la) * ($h/$w) ;
+
+ }
+
+ $p = array($xc,$yc,$xc+$w*cos($sa),$yc-$h*sin($sa));
+
+ for($a=$sa; $a < $ea; $a += $step ) {
+ $p[] = $xc + $w*cos($a);
+ $p[] = $yc - $h*sin($a);
+ }
+
+ $p[] = $xc+$w*cos($ea);
+ $p[] = $yc-$h*sin($ea);
+ $p[] = $xc;
+ $p[] = $yc;
+
+ $img->SetColor($edgecolor);
+ $img->Polygon($p);
+
+ // Unfortunately we can't really draw the full edge around the whole of
+ // of the slice if any of the slices are exploded. The reason is that
+ // this algorithm is to simply. There are cases where the edges will
+ // "overwrite" other slices when they have been exploded.
+ // Doing the full, proper 3D hidden lines stiff is actually quite
+ // tricky. So for exploded pies we only draw the top edge. Not perfect
+ // but the "real" solution is much more complicated.
+ if( $fulledge && !( $sa > 0 && $sa < M_PI && $ea < M_PI) ) {
+
+ if($sa < M_PI && $ea > M_PI)
+ $sa = M_PI;
+
+ if($sa < 2*M_PI && (($ea >= 2*M_PI) || ($ea > 0 && $ea < $sa ) ) )
+ $ea = 2*M_PI;
+
+ if( $sa >= M_PI && $ea <= 2*M_PI ) {
+ $p = array($xc + $w*cos($sa),$yc - $h*sin($sa),
+ $xc + $w*cos($sa),$z + $yc - $h*sin($sa));
+
+ for($a=$sa+$step; $a < $ea; $a += $step ) {
+ $p[] = $xc + $w*cos($a);
+ $p[] = $z + $yc - $h*sin($a);
+ }
+ $p[] = $xc + $w*cos($ea);
+ $p[] = $z + $yc - $h*sin($ea);
+ $p[] = $xc + $w*cos($ea);
+ $p[] = $yc - $h*sin($ea);
+ $img->SetColor($edgecolor);
+ $img->Polygon($p);
+ }
+ }
+ }
+
+ function Stroke($img,$aaoption=0) {
+ $n = count($this->data);
+
+ // If user hasn't set the colors use the theme array
+ if( $this->setslicecolors==null ) {
+ $colors = array_keys($img->rgb->rgb_table);
+ sort($colors);
+ $idx_a=$this->themearr[$this->theme];
+ $ca = array();
+ $m = count($idx_a);
+ for($i=0; $i < $m; ++$i)
+ $ca[$i] = $colors[$idx_a[$i]];
+ $ca = array_reverse(array_slice($ca,0,$n));
+ }
+ else {
+ $ca = $this->setslicecolors;
+ }
+
+
+ if( $this->posx <= 1 && $this->posx > 0 )
+ $xc = round($this->posx*$img->width);
+ else
+ $xc = $this->posx ;
+
+ if( $this->posy <= 1 && $this->posy > 0 )
+ $yc = round($this->posy*$img->height);
+ else
+ $yc = $this->posy ;
+
+ if( $this->radius <= 1 ) {
+ $width = floor($this->radius*min($img->width,$img->height));
+ // Make sure that the pie doesn't overflow the image border
+ // The 0.9 factor is simply an extra margin to leave some space
+ // between the pie an the border of the image.
+ $width = min($width,min($xc*0.9,($yc*90/$this->angle-$width/4)*0.9));
+ }
+ else {
+ $width = $this->radius * ($aaoption === 1 ? 2 : 1 ) ;
+ }
+
+ // Add a sanity check for width
+ if( $width < 1 ) {
+ JpGraphError::Raise("Width for 3D Pie is 0. Specify a size > 0");
+ exit();
+ }
+
+ // Establish a thickness. By default the thickness is a fifth of the
+ // pie slice width (=pie radius) but since the perspective depends
+ // on the inclination angle we use some heuristics to make the edge
+ // slightly thicker the less the angle.
+
+ // Has user specified an absolute thickness? In that case use
+ // that instead
+
+ if( $this->iThickness ) {
+ $thick = $this->iThickness;
+ $thick *= ($aaoption === 1 ? 2 : 1 );
+ }
+ else
+ $thick = $width/12;
+ $a = $this->angle;
+ if( $a <= 30 ) $thick *= 1.6;
+ elseif( $a <= 40 ) $thick *= 1.4;
+ elseif( $a <= 50 ) $thick *= 1.2;
+ elseif( $a <= 60 ) $thick *= 1.0;
+ elseif( $a <= 70 ) $thick *= 0.8;
+ elseif( $a <= 80 ) $thick *= 0.7;
+ else $thick *= 0.6;
+
+ $thick = floor($thick);
+
+ if( $this->explode_all )
+ for($i=0; $i < $n; ++$i)
+ $this->explode_radius[$i]=$this->explode_r;
+
+ $this->Pie3D($aaoption,$img,$this->data, $ca, $xc, $yc, $width, $this->angle,
+ $thick, 0.65, $this->startangle, $this->edgecolor, $this->edgeweight);
+
+ // Adjust title position
+ if( $aaoption != 1 ) {
+ $this->title->Pos($xc,$yc-$this->title->GetFontHeight($img)-$width/2-$this->title->margin, "center","bottom");
+ $this->title->Stroke($img);
+ }
+ }
+
+//---------------
+// PRIVATE METHODS
+
+ // Position the labels of each slice
+ function StrokeLabels($label,$img,$a,$xp,$yp,$z) {
+ $this->value->halign="left";
+ $this->value->valign="top";
+ $this->value->margin=0;
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $img->SetFont($this->value->ff,$this->value->fs,$this->value->fsize);
+ $h=$img->GetTextHeight($label);
+ // For numeric values the format of the display value
+ // must be taken into account
+ if( is_numeric($label) ) {
+ if( $label >= 0 )
+ $w=$img->GetTextWidth(sprintf($this->value->format,$label));
+ else
+ $w=$img->GetTextWidth(sprintf($this->value->negformat,$label));
+ }
+ else
+ $w=$img->GetTextWidth($label);
+ while( $a > 2*M_PI ) $a -= 2*M_PI;
+ if( $a>=7*M_PI/4 || $a <= M_PI/4 ) $dx=0;
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dx=($a-M_PI/4)*2/M_PI;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dx=1;
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dx=(1-($a-M_PI*5/4)*2/M_PI);
+
+ if( $a>=7*M_PI/4 ) $dy=(($a-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $a<=M_PI/4 ) $dy=(1-$a*2/M_PI);
+ if( $a>=M_PI/4 && $a <= 3*M_PI/4 ) $dy=1;
+ if( $a>=3*M_PI/4 && $a <= 5*M_PI/4 ) $dy=(1-($a-3*M_PI/4)*2/M_PI);
+ if( $a>=5*M_PI/4 && $a <= 7*M_PI/4 ) $dy=0;
+
+ $x = round($xp-$dx*$w);
+ $y = round($yp-$dy*$h);
+
+ /*
+ // Mark anchor point for debugging
+ $img->SetColor('red');
+ $img->Line($xp-10,$yp,$xp+10,$yp);
+ $img->Line($xp,$yp-10,$xp,$yp+10);
+ */
+
+ $this->value->Stroke($img,$label,$x,$y);
+ }
+} // Class
+
+/* EOF */
+?>
diff --git a/includes/jpgraph/jpgraph_plotmark.inc b/includes/jpgraph/jpgraph_plotmark.inc
new file mode 100644
index 0000000..2fd138b
--- /dev/null
+++ b/includes/jpgraph/jpgraph_plotmark.inc
@@ -0,0 +1,480 @@
+<?php
+//=======================================================================
+// File: JPGRAPH_PLOTMARK.PHP
+// Description: Class file. Handles plotmarks
+// Created: 2003-03-21
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_plotmark.inc,v 1.9.2.5 2003/10/25 20:29:48 aditus Exp $
+//
+// License: This code is released under QPL 1.0
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+
+
+//========================================================================
+// CLASS ImgData
+// Description: Base class for all image data classes that contains the
+// real image data.
+//========================================================================
+class ImgData {
+ var $name = ''; // Each subclass gives a name
+ var $an = array(); // Data array names
+ var $colors = array(); // Available colors
+ var $index = array(); // Index for colors
+ var $maxidx = 0 ; // Max color index
+ var $anchor_x=0.5, $anchor_y=0.5 ; // Where is the center of the image
+ // Create a GD image from the data and return a GD handle
+ function GetImg($aMark,$aIdx) {
+ $n = $this->an[$aMark];
+ if( is_string($aIdx) ) {
+ if( !in_array($aIdx,$this->colors) ) {
+ JpGraphError::Raise('This marker "'.($this->name).'" does not exist in color: '.$aIdx);
+ die();
+ }
+ $idx = $this->index[$aIdx];
+ }
+ elseif( !is_integer($aIdx) ||
+ (is_integer($aIdx) && $aIdx > $this->maxidx ) ) {
+ JpGraphError::Raise('Mark color index too large for marker "'.($this->name).'"');
+ }
+ else
+ $idx = $aIdx ;
+ return Image::CreateFromString(base64_decode($this->{$n}[$idx][1]));
+ }
+ function GetAnchor() {
+ return array($this->anchor_x,$this->anchor_y);
+ }
+}
+
+
+// Keep a global flag cache to reduce memory usage
+$_gFlagCache=array(
+ 1 => null,
+ 2 => null,
+ 3 => null,
+ 4 => null,
+);
+// Only supposed to b called as statics
+class FlagCache {
+ function GetFlagImgByName($aSize,$aName) {
+ global $_gFlagCache;
+ require_once('jpgraph_flags.php');
+ if( $_gFlagCache[$aSize] === null ) {
+ $_gFlagCache[$aSize] =& new FlagImages($aSize);
+ }
+ $f =& $_gFlagCache[$aSize];
+ $idx = $f->GetIdxByName($aName,$aFullName);
+ return $f->GetImgByIdx($idx);
+ }
+}
+
+//===================================================
+// CLASS PlotMark
+// Description: Handles the plot marks in graphs
+//===================================================
+class PlotMark {
+ var $title, $show=true;
+ var $type,$weight=1;
+ var $color="black", $width=4, $fill_color="blue";
+ var $yvalue,$xvalue='',$csimtarget,$csimalt,$csimareas;
+ var $iFormatCallback="";
+ var $iFormatCallback2="";
+ var $markimg='',$iScale=1.0;
+ var $oldfilename='',$iFileName='';
+ var $imgdata_balls = null;
+ var $imgdata_diamonds = null;
+ var $imgdata_squares = null;
+ var $imgdata_bevels = null;
+ var $imgdata_stars = null;
+ var $imgdata_pushpins = null;
+
+//--------------
+// CONSTRUCTOR
+ function PlotMark() {
+ $this->title = new Text();
+ $this->title->Hide();
+ $this->csimareas = '';
+ $this->csimalt = '';
+ $this->type=-1;
+ }
+//---------------
+// PUBLIC METHODS
+ function SetType($aType,$aFileName='',$aScale=1.0) {
+ $this->type = $aType;
+ if( $aType == MARK_IMG && $aFileName=='' ) {
+ JpGraphError::Raise('A filename must be specified if you set the mark type to MARK_IMG.');
+ }
+ $this->iFileName = $aFileName;
+ $this->iScale = $aScale;
+ }
+
+ function SetCallback($aFunc) {
+ $this->iFormatCallback = $aFunc;
+ }
+
+ function SetCallbackYX($aFunc) {
+ $this->iFormatCallback2 = $aFunc;
+ }
+
+ function GetType() {
+ return $this->type;
+ }
+
+ function SetColor($aColor) {
+ $this->color=$aColor;
+ }
+
+ function SetFillColor($aFillColor) {
+ $this->fill_color = $aFillColor;
+ }
+
+ function SetWeight($aWeight) {
+ $this->weight = $aWeight;
+ }
+
+ // Synonym for SetWidth()
+ function SetSize($aWidth) {
+ $this->width=$aWidth;
+ }
+
+ function SetWidth($aWidth) {
+ $this->width=$aWidth;
+ }
+
+ function SetDefaultWidth() {
+ switch( $this->type ) {
+ case MARK_CIRCLE:
+ case MARK_FILLEDCIRCLE:
+ $this->width=4;
+ break;
+ default:
+ $this->width=7;
+ }
+ }
+
+ function GetWidth() {
+ return $this->width;
+ }
+
+ function Hide($aHide=true) {
+ $this->show = !$aHide;
+ }
+
+ function Show($aShow=true) {
+ $this->show = $aShow;
+ }
+
+ function SetCSIMAltVal($aY,$aX='') {
+ $this->yvalue=$aY;
+ $this->xvalue=$aX;
+ }
+
+ function SetCSIMTarget($aTarget) {
+ $this->csimtarget=$aTarget;
+ }
+
+ function SetCSIMAlt($aAlt) {
+ $this->csimalt=$aAlt;
+ }
+
+ function GetCSIMAreas(){
+ return $this->csimareas;
+ }
+
+ function AddCSIMPoly($aPts) {
+ $coords = round($aPts[0]).", ".round($aPts[1]);
+ $n = count($aPts)/2;
+ for( $i=1; $i < $n; ++$i){
+ $coords .= ", ".round($aPts[2*$i]).", ".round($aPts[2*$i+1]);
+ }
+ $this->csimareas="";
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas .= "<area shape=\"poly\" coords=\"$coords\" href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ function AddCSIMCircle($x,$y,$r) {
+ $x = round($x); $y=round($y); $r=round($r);
+ $this->csimareas="";
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas .= "<area shape=\"circle\" coords=\"$x,$y,$r\" href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+ }
+
+ function Stroke($img,$x,$y) {
+ if( !$this->show ) return;
+
+ if( $this->iFormatCallback != '' || $this->iFormatCallback2 != '' ) {
+
+ if( $this->iFormatCallback != '' ) {
+ $f = $this->iFormatCallback;
+ list($width,$color,$fcolor) = call_user_func($f,$this->yvalue);
+ $filename = $this->iFileName;
+ $imgscale = $this->iScale;
+ }
+ else {
+ $f = $this->iFormatCallback2;
+ list($width,$color,$fcolor,$filename,$imgscale) = call_user_func($f,$this->yvalue,$this->xvalue);
+ if( $filename=="" ) $filename = $this->iFileName;
+ if( $imgscale=="" ) $imgscale = $this->iScale;
+ }
+
+ if( $width=="" ) $width = $this->width;
+ if( $color=="" ) $color = $this->color;
+ if( $fcolor=="" ) $fcolor = $this->fill_color;
+
+ }
+ else {
+ $fcolor = $this->fill_color;
+ $color = $this->color;
+ $width = $this->width;
+ $filename = $this->iFileName;
+ $imgscale = $this->iScale;
+ }
+
+ if( $this->type == MARK_IMG ||
+ ($this->type >= MARK_FLAG1 && $this->type <= MARK_FLAG4 ) ||
+ $this->type >= MARK_IMG_PUSHPIN ) {
+
+ // Note: For the builtin images we use the "filename" parameter
+ // to denote the color
+ $anchor_x = 0.5;
+ $anchor_y = 0.5;
+ switch( $this->type ) {
+ case MARK_FLAG1:
+ case MARK_FLAG2:
+ case MARK_FLAG3:
+ case MARK_FLAG4:
+ $this->markimg = FlagCache::GetFlagImgByName($this->type-MARK_FLAG1+1,$filename);
+ break;
+
+ case MARK_IMG :
+ // Load an image and use that as a marker
+ // Small optimization, if we have already read an image don't
+ // waste time reading it again.
+ if( $this->markimg == '' || !($this->oldfilename === $filename) ) {
+ $this->markimg = Graph::LoadBkgImage('',$filename);
+ $this->oldfilename = $filename ;
+ }
+ break;
+
+ case MARK_IMG_PUSHPIN:
+ case MARK_IMG_SPUSHPIN:
+ case MARK_IMG_LPUSHPIN:
+ if( $this->imgdata_pushpins == null ) {
+ require_once 'imgdata_pushpins.inc';
+ $this->imgdata_pushpins = new ImgData_PushPins();
+ }
+ $this->markimg = $this->imgdata_pushpins->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_pushpins->GetAnchor();
+ break;
+
+ case MARK_IMG_SQUARE:
+ if( $this->imgdata_squares == null ) {
+ require_once 'imgdata_squares.inc';
+ $this->imgdata_squares = new ImgData_Squares();
+ }
+ $this->markimg = $this->imgdata_squares->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_squares->GetAnchor();
+ break;
+
+ case MARK_IMG_STAR:
+ if( $this->imgdata_stars == null ) {
+ require_once 'imgdata_stars.inc';
+ $this->imgdata_stars = new ImgData_Stars();
+ }
+ $this->markimg = $this->imgdata_stars->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_stars->GetAnchor();
+ break;
+
+ case MARK_IMG_BEVEL:
+ if( $this->imgdata_bevels == null ) {
+ require_once 'imgdata_bevels.inc';
+ $this->imgdata_bevels = new ImgData_Bevels();
+ }
+ $this->markimg = $this->imgdata_bevels->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_bevels->GetAnchor();
+ break;
+
+ case MARK_IMG_DIAMOND:
+ if( $this->imgdata_diamonds == null ) {
+ require_once 'imgdata_diamonds.inc';
+ $this->imgdata_diamonds = new ImgData_Diamonds();
+ }
+ $this->markimg = $this->imgdata_diamonds->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_diamonds->GetAnchor();
+ break;
+
+ case MARK_IMG_BALL:
+ case MARK_IMG_SBALL:
+ case MARK_IMG_MBALL:
+ case MARK_IMG_LBALL:
+ if( $this->imgdata_balls == null ) {
+ require_once 'imgdata_balls.inc';
+ $this->imgdata_balls = new ImgData_Balls();
+ }
+ $this->markimg = $this->imgdata_balls->GetImg($this->type,$filename);
+ list($anchor_x,$anchor_y) = $this->imgdata_balls->GetAnchor();
+ break;
+ }
+
+ $w = $img->GetWidth($this->markimg);
+ $h = $img->GetHeight($this->markimg);
+
+ $dw = round($imgscale * $w );
+ $dh = round($imgscale * $h );
+
+ $dx = round($x-$dw*$anchor_x);
+ $dy = round($y-$dh*$anchor_y);
+
+ $this->width = max($dx,$dy);
+
+ $img->Copy($this->markimg,$dx,$dy,0,0,$dw,$dh,$w,$h);
+ if( !empty($this->csimtarget) ) {
+ $this->csimareas = "<area shape=\"rect\" coords=\"".
+ $dx.','.$dy.','.round($dx+$dw).','.round($dy+$dh).'" '.
+ "href=\"".$this->csimtarget."\"";
+ if( !empty($this->csimalt) ) {
+ $tmp=sprintf($this->csimalt,$this->yvalue,$this->xvalue);
+ $this->csimareas .= " alt=\"$tmp\" title=\"$tmp\"";
+ }
+ $this->csimareas .= ">\n";
+ }
+
+ // Stroke title
+ $this->title->Align("center","top");
+ $this->title->Stroke($img,$x,$y+round($dh/2));
+ return;
+ }
+
+ $weight = $this->weight;
+ $dx=round($width/2,0);
+ $dy=round($width/2,0);
+ $pts=0;
+
+ switch( $this->type ) {
+ case MARK_SQUARE:
+ $c[]=$x-$dx;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y-$dy;
+ $pts=5;
+ break;
+ case MARK_UTRIANGLE:
+ ++$dx;++$dy;
+ $c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $c[]=$x;$c[]=$y-0.87*$dy;
+ $c[]=$x+$dx;$c[]=$y+0.87*$dy;
+ $c[]=$x-$dx;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $pts=4;
+ break;
+ case MARK_DTRIANGLE:
+ ++$dx;++$dy;
+ $c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $c[]=$x-$dx;$c[]=$y-0.87*$dy;
+ $c[]=$x+$dx;$c[]=$y-0.87*$dy;
+ $c[]=$x;$c[]=$y+0.87*$dy; // tan(60)/2*$dx
+ $pts=4;
+ break;
+ case MARK_DIAMOND:
+ $c[]=$x;$c[]=$y+$dy;
+ $c[]=$x-$dx;$c[]=$y;
+ $c[]=$x;$c[]=$y-$dy;
+ $c[]=$x+$dx;$c[]=$y;
+ $c[]=$x;$c[]=$y+$dy;
+ $pts=5;
+ break;
+ case MARK_LEFTTRIANGLE:
+ $c[]=$x;$c[]=$y;
+ $c[]=$x;$c[]=$y+2*$dy;
+ $c[]=$x+$dx*2;$c[]=$y;
+ $c[]=$x;$c[]=$y;
+ $pts=4;
+ break;
+ case MARK_RIGHTTRIANGLE:
+ $c[]=$x-$dx*2;$c[]=$y;
+ $c[]=$x;$c[]=$y+2*$dy;
+ $c[]=$x;$c[]=$y;
+ $c[]=$x-$dx*2;$c[]=$y;
+ $pts=4;
+ break;
+ case MARK_FLASH:
+ $dy *= 2;
+ $c[]=$x+$dx/2; $c[]=$y-$dy;
+ $c[]=$x-$dx+$dx/2; $c[]=$y+$dy*0.7-$dy;
+ $c[]=$x+$dx/2; $c[]=$y+$dy*1.3-$dy;
+ $c[]=$x-$dx+$dx/2; $c[]=$y+2*$dy-$dy;
+ $img->SetLineWeight($weight);
+ $img->SetColor($color);
+ $img->Polygon($c);
+ $img->SetLineWeight(1);
+ $this->AddCSIMPoly($c);
+ break;
+ }
+
+ if( $pts>0 ) {
+ $this->AddCSIMPoly($c);
+ $img->SetLineWeight($weight);
+ $img->SetColor($fcolor);
+ $img->FilledPolygon($c);
+ $img->SetColor($color);
+ $img->Polygon($c);
+ $img->SetLineWeight(1);
+ }
+ elseif( $this->type==MARK_CIRCLE ) {
+ $img->SetColor($color);
+ $img->Circle($x,$y,$width);
+ $this->AddCSIMCircle($x,$y,$width);
+ }
+ elseif( $this->type==MARK_FILLEDCIRCLE ) {
+ $img->SetColor($fcolor);
+ $img->FilledCircle($x,$y,$width);
+ $img->SetColor($color);
+ $img->Circle($x,$y,$width);
+ $this->AddCSIMCircle($x,$y,$width);
+ }
+ elseif( $this->type==MARK_CROSS ) {
+ // Oversize by a pixel to match the X
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x,$y+$dy+1,$x,$y-$dy-1);
+ $img->Line($x-$dx-1,$y,$x+$dx+1,$y);
+ $this->AddCSIMCircle($x,$y,$dx);
+ }
+ elseif( $this->type==MARK_X ) {
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
+ $img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
+ $this->AddCSIMCircle($x,$y,$dx+$dy);
+ }
+ elseif( $this->type==MARK_STAR ) {
+ $img->SetColor($color);
+ $img->SetLineWeight($weight);
+ $img->Line($x+$dx,$y+$dy,$x-$dx,$y-$dy);
+ $img->Line($x-$dx,$y+$dy,$x+$dx,$y-$dy);
+ // Oversize by a pixel to match the X
+ $img->Line($x,$y+$dy+1,$x,$y-$dy-1);
+ $img->Line($x-$dx-1,$y,$x+$dx+1,$y);
+ $this->AddCSIMCircle($x,$y,$dx+$dy);
+ }
+
+ // Stroke title
+ $this->title->Align("center","center");
+ $this->title->Stroke($img,$x,$y);
+ }
+} // Class
+
+
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_polar.php b/includes/jpgraph/jpgraph_polar.php
new file mode 100644
index 0000000..6a319a1
--- /dev/null
+++ b/includes/jpgraph/jpgraph_polar.php
@@ -0,0 +1,834 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_POLAR.PHP
+// Description: Polar plot extension for JpGraph
+// Created: 2003-02-02
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_polar.php,v 1.2.2.2 2003/09/12 20:18:16 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+require_once "jpgraph_log.php";
+
+
+DEFINE('POLAR_360',1);
+DEFINE('POLAR_180',2);
+
+//
+// Note. Don't attempt to make sense of this code.
+// In order not to have to be able to inherit the scaling code
+// from the main graph package we have had to make some "tricks" since
+// the original scaling and axis was not designed to do what is
+// required here.
+// There were two option. 1: Re-implement everything and get a clean design
+// and 2: do some "small" trickery and be able to inherit most of
+// the functionlity from the main graph package.
+// We choose 2: here in order to save some time.
+//
+
+//--------------------------------------------------------------------------
+// class PolarPlot
+//--------------------------------------------------------------------------
+class PolarPlot {
+ var $numpoints=0;
+ var $iColor='navy',$iFillColor='';
+ var $iLineWeight=1;
+ var $coord=null;
+ var $legendcsimtarget='';
+ var $legendcsimalt='';
+ var $legend="";
+ var $csimtargets=array(); // Array of targets for CSIM
+ var $csimareas=""; // Resultant CSIM area tags
+ var $csimalts=null; // ALT:s for corresponding target
+ var $line_style='solid',$mark;
+
+ function PolarPlot($aData) {
+ $n = count($aData);
+ if( $n & 1 ) {
+ JpGraphError::Raise('Polar plots must have an even number of data point. Each data point is a tuple (angle,radius).');
+ }
+ $this->numpoints = $n/2;
+ $this->coord = $aData;
+ $this->mark = new PlotMark();
+ }
+
+ function SetWeight($aWeight) {
+ $this->iLineWeight = $aWeight;
+ }
+
+ function SetColor($aColor){
+ $this->iColor = $aColor;
+ }
+
+ function SetFillColor($aColor){
+ $this->iFillColor = $aColor;
+ }
+
+ function Max() {
+ $m = $this->coord[1];
+ $i=1;
+ while( $i < $this->numpoints ) {
+ $m = max($m,$this->coord[2*$i+1]);
+ ++$i;
+ }
+ return $m;
+ }
+ // Set href targets for CSIM
+ function SetCSIMTargets($aTargets,$aAlts=null) {
+ $this->csimtargets=$aTargets;
+ $this->csimalts=$aAlts;
+ }
+
+ // Get all created areas
+ function GetCSIMareas() {
+ return $this->csimareas;
+ }
+
+ function SetLegend($aLegend,$aCSIM="",$aCSIMAlt="") {
+ $this->legend = $aLegend;
+ $this->legendcsimtarget = $aCSIM;
+ $this->legendcsimalt = $aCSIMAlt;
+ }
+
+ // Private methods
+
+ function Legend(&$aGraph) {
+ $color = $this->iColor ;
+ if( $this->legend != "" ) {
+ if( $this->iFillColor!='' ) {
+ $color = $this->iFillColor;
+ $aGraph->legend->Add($this->legend,$color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ else {
+ $aGraph->legend->Add($this->legend,$color,$this->mark,$this->line_style,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+ }
+
+ function Stroke($img,$scale) {
+
+ $i=0;
+ $p=array();
+ $this->csimareas='';
+ while($i < $this->numpoints) {
+ list($x1,$y1) = $scale->PTranslate($this->coord[2*$i],$this->coord[2*$i+1]);
+ $p[2*$i] = $x1;
+ $p[2*$i+1] = $y1;
+
+ if( isset($this->csimtargets[$i]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$i]);
+ $this->mark->SetCSIMAlt($this->csimalts[$i]);
+ $this->mark->SetCSIMAltVal($this->coord[2*$i], $this->coord[2*$i+1]);
+ $this->mark->Stroke($img,$x1,$y1);
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ }
+ else
+ $this->mark->Stroke($img,$x1,$y1);
+
+ ++$i;
+ }
+
+ if( $this->iFillColor != '' ) {
+ $img->SetColor($this->iFillColor);
+ $img->FilledPolygon($p);
+ }
+ $img->SetLineWeight($this->iLineWeight);
+ $img->SetColor($this->iColor);
+ $img->Polygon($p,$this->iFillColor!='');
+ }
+}
+
+//--------------------------------------------------------------------------
+// class PolarAxis
+//--------------------------------------------------------------------------
+class PolarAxis extends Axis {
+ var $angle_step=15,$angle_color='lightgray',$angle_label_color='black';
+ var $angle_fontfam=FF_FONT1,$angle_fontstyle=FS_NORMAL,$angle_fontsize=10;
+ var $angle_fontcolor = 'navy';
+ var $gridminor_color='lightgray',$gridmajor_color='lightgray';
+ var $show_minor_grid = false, $show_major_grid = true ;
+ var $show_angle_mark=true, $show_angle_grid=true, $show_angle_label=true;
+ var $angle_tick_len=3, $angle_tick_len2=3, $angle_tick_color='black';
+ var $show_angle_tick=true;
+ var $radius_tick_color='black';
+
+ function PolarAxis(&$img,&$aScale) {
+ parent::Axis($img,$aScale);
+ }
+
+ function ShowAngleDegreeMark($aFlg=true) {
+ $this->show_angle_mark = $aFlg;
+ }
+
+ function SetAngleStep($aStep) {
+ $this->angle_step=$aStep;
+ }
+
+ function HideTicks($aFlg=true,$aAngleFlg=true) {
+ parent::HideTicks($aFlg,$aFlg);
+ $this->show_angle_tick = !$aAngleFlg;
+ }
+
+ function ShowAngleLabel($aFlg=true) {
+ $this->show_angle_label = $aFlg;
+ }
+
+ function ShowGrid($aMajor=true,$aMinor=false,$aAngle=true) {
+ $this->show_minor_grid = $aMinor;
+ $this->show_major_grid = $aMajor;
+ $this->show_angle_grid = $aAngle ;
+ }
+
+ function SetAngleFont($aFontFam,$aFontStyle=FS_NORMAL,$aFontSize=10) {
+ $this->angle_fontfam = $aFontFam;
+ $this->angle_fontstyle = $aFontStyle;
+ $this->angle_fontsize = $aFontSize;
+ }
+
+ function SetColor($aColor,$aRadColor='',$aAngleColor='') {
+ if( $aAngleColor == '' )
+ $aAngleColor=$aColor;
+ parent::SetColor($aColor,$aRadColor);
+ $this->angle_fontcolor = $aAngleColor;
+ }
+
+ function SetGridColor($aMajorColor,$aMinorColor='',$aAngleColor='') {
+ if( $aMinorColor == '' )
+ $aMinorColor = $aMajorColor;
+ if( $aAngleColor == '' )
+ $aAngleColor = $aMajorColor;
+
+ $this->gridminor_color = $aMinorColor;
+ $this->gridmajor_color = $aMajorColor;
+ $this->angle_color = $aAngleColor;
+ }
+
+ function SetTickColors($aRadColor,$aAngleColor='') {
+ $this->radius_tick_color = $aRadColor;
+ $this->angle_tick_color = $aAngleColor;
+ }
+
+ // Private methods
+ function StrokeGrid($pos) {
+ $x = round($this->img->left_margin + $this->img->plotwidth/2);
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+
+ // Stroke the minor arcs
+ $pmin = array();
+ $p = $this->scale->ticks->ticks_pos;
+ $n = count($p);
+ $i = 0;
+ $this->img->SetColor($this->gridminor_color);
+ while( $i < $n ) {
+ $r = $p[$i]-$x+1;
+ $pmin[]=$r;
+ if( $this->show_minor_grid ) {
+ $this->img->Circle($x,$pos,$r);
+ }
+ $i++;
+ }
+
+ $limit = max($this->img->plotwidth,$this->img->plotheight)*1.4 ;
+ while( $r < $limit ) {
+ $off = $r;
+ $i=1;
+ $r = $off + round($p[$i]-$x+1);
+ while( $r < $limit && $i < $n ) {
+ $r = $off+$p[$i]-$x;
+ $pmin[]=$r;
+ if( $this->show_minor_grid ) {
+ $this->img->Circle($x,$pos,$r);
+ }
+ $i++;
+ }
+ }
+
+ // Stroke the major arcs
+ if( $this->show_major_grid ) {
+ // First determine how many minor step on
+ // every major step. We have recorded the minor radius
+ // in pmin and use these values. This is done in order
+ // to avoid rounding errors if we were to recalculate the
+ // different major radius.
+ $pmaj = $this->scale->ticks->maj_ticks_pos;
+ $p = $this->scale->ticks->ticks_pos;
+ if( $this->scale->name == 'lin' ) {
+ $step=round(($pmaj[1] - $pmaj[0])/($p[1] - $p[0]));
+ }
+ else {
+ $step=9;
+ }
+ $n = round(count($pmin)/$step);
+ $i = 0;
+ $this->img->SetColor($this->gridmajor_color);
+ $limit = max($this->img->plotwidth,$this->img->plotheight)*1.4 ;
+ $off = $r;
+ $i=0;
+ $r = $pmin[$i*$step];
+ while( $r < $limit && $i < $n ) {
+ $r = $pmin[$i*$step];
+ $this->img->Circle($x,$pos,$r);
+ $i++;
+ }
+ }
+
+ // Draw angles
+ if( $this->show_angle_grid ) {
+ $this->img->SetColor($this->angle_color);
+ $d = max($this->img->plotheight,$this->img->plotwidth)*1.4 ;
+ $a = 0;
+ $p = $this->scale->ticks->ticks_pos;
+ $start_radius = $p[1]-$x;
+ while( $a < 360 ) {
+ if( $a == 90 || $a == 270 ) {
+ // Make sure there are no rounding problem with
+ // exactly vertical lines
+ $this->img->Line($x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$start_radius*sin($a/180*M_PI),
+ $x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$d*sin($a/180*M_PI));
+
+ }
+ else {
+ $this->img->Line($x+$start_radius*cos($a/180*M_PI)+1,
+ $pos-$start_radius*sin($a/180*M_PI),
+ $x+$d*cos($a/180*M_PI),
+ $pos-$d*sin($a/180*M_PI));
+ }
+ $a += $this->angle_step;
+ }
+ }
+ }
+
+ function StrokeAngleLabels($pos,$type) {
+
+ if( !$this->show_angle_label )
+ return;
+
+ $x0 = round($this->img->left_margin+$this->img->plotwidth/2)+1;
+
+ $d = max($this->img->plotwidth,$this->img->plotheight)*1.42;
+ $a = $this->angle_step;
+ $t = new Text();
+ $t->SetColor($this->angle_fontcolor);
+ $t->SetFont($this->angle_fontfam,$this->angle_fontstyle,$this->angle_fontsize);
+ $xright = $this->img->width - $this->img->right_margin;
+ $ytop = $this->img->top_margin;
+ $xleft = $this->img->left_margin;
+ $ybottom = $this->img->height - $this->img->bottom_margin;
+ $ha = 'left';
+ $va = 'center';
+ $w = $this->img->plotwidth/2;
+ $h = $this->img->plotheight/2;
+ $xt = $x0; $yt = $pos;
+ $margin=5;
+
+ $tl = $this->angle_tick_len ; // Outer len
+ $tl2 = $this->angle_tick_len2 ; // Interior len
+
+ $this->img->SetColor($this->angle_tick_color);
+ $rot90 = $this->img->a == 90 ;
+
+ if( $type == POLAR_360 ) {
+ $ca1 = atan($h/$w)/M_PI*180;
+ $ca2 = 180-$ca1;
+ $ca3 = $ca1+180;
+ $ca4 = 360-$ca1;
+ $end = 360;
+ while( $a < $end ) {
+ $ca = cos($a/180*M_PI);
+ $sa = sin($a/180*M_PI);
+ $x = $d*$ca;
+ $y = $d*$sa;
+ $xt=1000;$yt=1000;
+ if( $a <= $ca1 || $a >= $ca4 ) {
+ $yt = $pos - $w * $y/$x;
+ $xt = $xright + $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'top';
+ }
+ else {
+ $ha = 'left';
+ $va = 'center';
+ }
+ $x1=$xright-$tl2; $x2=$xright+$tl;
+ $y1=$y2=$yt;
+ }
+ elseif( $a > $ca1 && $a < $ca2 ) {
+ $xt = $x0 + $h * $x/$y;
+ $yt = $ytop - $margin;
+ if( $rot90 ) {
+ $ha = 'left';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ $y1=$ytop+$tl2;$y2=$ytop-$tl;
+ $x1=$x2=$xt;
+ }
+ elseif( $a >= $ca2 && $a <= $ca3 ) {
+ $yt = $pos + $w * $y/$x;
+ $xt = $xleft - $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ else {
+ $ha = 'right';
+ $va = 'center';
+ }
+ $x1=$xleft+$tl2;$x2=$xleft-$tl;
+ $y1=$y2=$yt;
+ }
+ else {
+ $xt = $x0 - $h * $x/$y;
+ $yt = $ybottom + $margin;
+ if( $rot90 ) {
+ $ha = 'right';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'top';
+ }
+ $y1=$ybottom-$tl2;$y2=$ybottom+$tl;
+ $x1=$x2=$xt;
+ }
+ if( $a != 0 && $a != 180 ) {
+ $t->Align($ha,$va);
+ if( $this->show_angle_mark )
+ $a .= '°';
+ $t->Set($a);
+ $t->Stroke($this->img,$xt,$yt);
+ if( $this->show_angle_tick )
+ $this->img->Line($x1,$y1,$x2,$y2);
+ }
+ $a += $this->angle_step;
+ }
+ }
+ else {
+ // POLAR_HALF
+ $ca1 = atan($h/$w*2)/M_PI*180;
+ $ca2 = 180-$ca1;
+ $end = 180;
+ while( $a < $end ) {
+ $ca = cos($a/180*M_PI);
+ $sa = sin($a/180*M_PI);
+ $x = $d*$ca;
+ $y = $d*$sa;
+ if( $a <= $ca1 ) {
+ $yt = $pos - $w * $y/$x;
+ $xt = $xright + $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'top';
+ }
+ else {
+ $ha = 'left';
+ $va = 'center';
+ }
+ $x1=$xright-$tl2; $x2=$xright+$tl;
+ $y1=$y2=$yt;
+ }
+ elseif( $a > $ca1 && $a < $ca2 ) {
+ $xt = $x0 + 2*$h * $x/$y;
+ $yt = $ytop - $margin;
+ if( $rot90 ) {
+ $ha = 'left';
+ $va = 'center';
+ }
+ else {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ $y1=$ytop+$tl2;$y2=$ytop-$tl;
+ $x1=$x2=$xt;
+ }
+ elseif( $a >= $ca2 ) {
+ $yt = $pos + $w * $y/$x;
+ $xt = $xleft - $margin;
+ if( $rot90 ) {
+ $ha = 'center';
+ $va = 'bottom';
+ }
+ else {
+ $ha = 'right';
+ $va = 'center';
+ }
+ $x1=$xleft+$tl2;$x2=$xleft-$tl;
+ $y1=$y2=$yt;
+ }
+ $t->Align($ha,$va);
+ if( $this->show_angle_mark )
+ $a .= '°';
+ $t->Set($a);
+ $t->Stroke($this->img,$xt,$yt);
+ if( $this->show_angle_tick )
+ $this->img->Line($x1,$y1,$x2,$y2);
+ $a += $this->angle_step;
+ }
+ }
+ }
+
+ function Stroke($pos) {
+
+ $this->img->SetLineWeight($this->weight);
+ $this->img->SetColor($this->color);
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ if( !$this->hide_line )
+ $this->img->FilledRectangle($this->img->left_margin,$pos,
+ $this->img->width-$this->img->right_margin,$pos+$this->weight-1);
+ $y=$pos+$this->img->GetFontHeight()+$this->title_margin+$this->title->margin;
+ if( $this->title_adjust=="high" )
+ $this->title->Pos($this->img->width-$this->img->right_margin,$y,"right","top");
+ elseif( $this->title_adjust=="middle" || $this->title_adjust=="center" )
+ $this->title->Pos(($this->img->width-$this->img->left_margin-
+ $this->img->right_margin)/2+$this->img->left_margin,
+ $y,"center","top");
+ elseif($this->title_adjust=="low")
+ $this->title->Pos($this->img->left_margin,$y,"left","top");
+ else {
+ JpGraphError::Raise('Unknown alignment specified for X-axis title. ('.
+ $this->title_adjust.')');
+ }
+
+ $this->StrokeLabels($pos,false);
+ $this->img->SetColor($this->radius_tick_color);
+ $this->scale->ticks->Stroke($this->img,$this->scale,$pos);
+
+ //
+ // Mirror the positions for the left side of the scale
+ //
+ $mid = 2*($this->img->left_margin+$this->img->plotwidth/2);
+ $n = count($this->scale->ticks->ticks_pos);
+ $i=0;
+ while( $i < $n ) {
+ $this->scale->ticks->ticks_pos[$i] =
+ $mid-$this->scale->ticks->ticks_pos[$i] ;
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticks_pos);
+ $i=0;
+ while( $i < $n ) {
+ $this->scale->ticks->maj_ticks_pos[$i] =
+ $mid-$this->scale->ticks->maj_ticks_pos[$i] ;
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticklabels_pos);
+ $i=1;
+ while( $i < $n ) {
+ $this->scale->ticks->maj_ticklabels_pos[$i] =
+ $mid-$this->scale->ticks->maj_ticklabels_pos[$i] ;
+ ++$i;
+ }
+
+ // Draw the left side of the scale
+ $n = count($this->scale->ticks->ticks_pos);
+ $yu = $pos - $this->scale->ticks->direction*$this->scale->ticks->GetMinTickAbsSize();
+
+
+ // Minor ticks
+ $i=1;
+ while( $i < $n/2 ) {
+ $x = round($this->scale->ticks->ticks_pos[$i]) ;
+ $this->img->Line($x,$pos,$x,$yu);
+ ++$i;
+ }
+
+ $n = count($this->scale->ticks->maj_ticks_pos);
+ $yu = $pos - $this->scale->ticks->direction*$this->scale->ticks->GetMajTickAbsSize();
+
+
+ // Major ticks
+ $i=1;
+ while( $i < $n/2 ) {
+ $x = round($this->scale->ticks->maj_ticks_pos[$i]) ;
+ $this->img->Line($x,$pos,$x,$yu);
+ ++$i;
+ }
+
+ $this->StrokeLabels($pos,false);
+ $this->title->Stroke($this->img);
+ }
+}
+
+class PolarScale extends LinearScale {
+ var $graph;
+ function PolarScale($aMax=0,&$graph) {
+ parent::LinearScale(0,$aMax,'x');
+ $this->graph = &$graph;
+ }
+
+ function _Translate($v) {
+ return parent::Translate($v);
+ }
+
+ function PTranslate($aAngle,$aRad) {
+
+ $m = $this->scale[1];
+ $w = $this->graph->img->plotwidth/2;
+ $aRad = $aRad/$m*$w;
+
+ $x = cos( $aAngle/180 * M_PI ) * $aRad;
+ $y = sin( $aAngle/180 * M_PI ) * $aRad;
+
+ $x += $this->_Translate(0);
+
+ if( $this->graph->iType == POLAR_360 ) {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight/2) - $y;
+ }
+ else {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight) - $y;
+ }
+ return array($x,$y);
+ }
+}
+
+class PolarLogScale extends LogScale {
+ var $graph;
+ function PolarLogScale($aMax=1,&$graph) {
+ parent::LogScale(0,$aMax,'x');
+ $this->graph = &$graph;
+ $this->ticks->SetLabelLogType(LOGLABELS_MAGNITUDE);
+
+ }
+
+ function PTranslate($aAngle,$aRad) {
+
+ if( $aRad == 0 )
+ $aRad = 1;
+ $aRad = log10($aRad);
+ $m = $this->scale[1];
+ $w = $this->graph->img->plotwidth/2;
+ $aRad = $aRad/$m*$w;
+
+ $x = cos( $aAngle/180 * M_PI ) * $aRad;
+ $y = sin( $aAngle/180 * M_PI ) * $aRad;
+
+ $x += $w+$this->graph->img->left_margin;//$this->_Translate(0);
+ if( $this->graph->iType == POLAR_360 ) {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight/2) - $y;
+ }
+ else {
+ $y = ($this->graph->img->top_margin + $this->graph->img->plotheight) - $y;
+ }
+ return array($x,$y);
+ }
+}
+
+class PolarGraph extends Graph {
+ var $scale;
+ var $iType=POLAR_360;
+ var $axis;
+
+ function PolarGraph($aWidth=300,$aHeight=200,$aCachedName="",$aTimeOut=0,$aInline=true) {
+ parent::Graph($aWidth,$aHeight,$aCachedName,$aTimeOut,$aInline) ;
+ $this->SetDensity(TICKD_DENSE);
+ $this->SetBox();
+ $this->SetMarginColor('white');
+ }
+
+ function SetDensity($aDense) {
+ $this->SetTickDensity(TICKD_NORMAL,$aDense);
+ }
+
+ function Set90AndMargin($lm=0,$rm=0,$tm=0,$bm=0) {
+ $adj = ($this->img->height - $this->img->width)/2;
+ $this->SetAngle(90);
+ $this->img->SetMargin($lm-$adj,$rm-$adj,$tm+$adj,$bm+$adj);
+ $this->img->SetCenter(floor($this->img->width/2),floor($this->img->height/2));
+ $this->axis->SetLabelAlign('right','center');
+ //JpGraphError::Raise('Set90AndMargin() is not supported for polar graphs.');
+ }
+
+ function SetScale($aScale,$rmax=0) {
+ if( $aScale == 'lin' )
+ $this->scale = new PolarScale($rmax,$this);
+ elseif( $aScale == 'log' ) {
+ $this->scale = new PolarLogScale($rmax,$this);
+ }
+ else {
+ JpGraphError::Raise('Unknown scale type for polar graph. Must be "lin" or "log"');
+ }
+
+ $this->scale->Init($this->img);
+ $this->axis = new PolarAxis($this->img,$this->scale);
+ $this->SetMargin(40,40,50,40);
+ }
+
+ function SetType($aType) {
+ $this->iType = $aType;
+ }
+
+ function SetPlotSize($w,$h) {
+ $this->SetMargin(($this->img->width-$w)/2,($this->img->width-$w)/2,
+ ($this->img->height-$h)/2,($this->img->height-$h)/2);
+ }
+
+ // Private methods
+ function GetPlotsMax() {
+ $n = count($this->plots);
+ $m = $this->plots[0]->Max();
+ $i=1;
+ while($i < $n) {
+ $m = max($this->plots[$i]->Max());
+ ++$i;
+ }
+ return $m;
+ }
+
+ function Stroke($aStrokeFileName="") {
+
+ // Start by adjusting the margin so that potential titles will fit.
+ $this->AdjustMarginsForTitles();
+
+ // If the filename is the predefined value = '_csim_special_'
+ // we assume that the call to stroke only needs to do enough
+ // to correctly generate the CSIM maps.
+ // We use this variable to skip things we don't strictly need
+ // to do to generate the image map to improve performance
+ // a best we can. Therefor you will see a lot of tests !$_csim in the
+ // code below.
+ $_csim = ($aStrokeFileName===_CSIM_SPECIALFILE);
+
+ // We need to know if we have stroked the plot in the
+ // GetCSIMareas. Otherwise the CSIM hasn't been generated
+ // and in the case of GetCSIM called before stroke to generate
+ // CSIM without storing an image to disk GetCSIM must call Stroke.
+ $this->iHasStroked = true;
+
+ //Check if we should autoscale axis
+ if( !$this->scale->IsSpecified() && count($this->plots)>0 ) {
+ $max = $this->GetPlotsMax();
+ $t1 = $this->img->plotwidth;
+ $this->img->plotwidth /= 2;
+ $t2 = $this->img->left_margin;
+ $this->img->left_margin += $this->img->plotwidth+1;
+ $this->scale->AutoScale($this->img,0,$max,
+ $this->img->plotwidth/$this->xtick_factor/2);
+ $this->img->plotwidth = $t1;
+ $this->img->left_margin = $t2;
+ }
+ else {
+ // The tick calculation will use the user suplied min/max values to determine
+ // the ticks. If auto_ticks is false the exact user specifed min and max
+ // values will be used for the scale.
+ // If auto_ticks is true then the scale might be slightly adjusted
+ // so that the min and max values falls on an even major step.
+ //$min = 0;
+ $max = $this->scale->scale[1];
+ $t1 = $this->img->plotwidth;
+ $this->img->plotwidth /= 2;
+ $t2 = $this->img->left_margin;
+ $this->img->left_margin += $this->img->plotwidth+1;
+ $this->scale->AutoScale($this->img,0,$max,
+ $this->img->plotwidth/$this->xtick_factor/2);
+ $this->img->plotwidth = $t1;
+ $this->img->left_margin = $t2;
+ }
+
+ if( $this->iType == POLAR_180 )
+ $pos = $this->img->height - $this->img->bottom_margin;
+ else
+ $pos = $this->img->plotheight/2 + $this->img->top_margin;
+
+
+ if( !$_csim ) {
+ $this->StrokePlotArea();
+ }
+
+ $this->iDoClipping = true;
+
+ if( $this->iDoClipping ) {
+ $oldimage = $this->img->CloneCanvasH();
+ }
+
+ if( !$_csim ) {
+ $this->axis->StrokeGrid($pos);
+ }
+
+ // Stroke all plots for Y1 axis
+ for($i=0; $i < count($this->plots); ++$i) {
+ $this->plots[$i]->Stroke($this->img,$this->scale);
+ }
+
+
+ if( $this->iDoClipping ) {
+ // Clipping only supports graphs at 0 and 90 degrees
+ if( $this->img->a == 0 ) {
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->left_margin,$this->img->top_margin,
+ $this->img->plotwidth+1,$this->img->plotheight+1);
+ }
+ elseif( $this->img->a == 90 ) {
+ $adj = round(($this->img->height - $this->img->width)/2);
+ $this->img->CopyCanvasH($oldimage,$this->img->img,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->bottom_margin-$adj,$this->img->left_margin+$adj,
+ $this->img->plotheight,$this->img->plotwidth);
+ }
+ $this->img->Destroy();
+ $this->img->SetCanvasH($oldimage);
+ }
+
+ if( !$_csim ) {
+ $this->axis->Stroke($pos);
+ $this->axis->StrokeAngleLabels($pos,$this->iType);
+ }
+
+ if( !$_csim ) {
+ $this->StrokePlotBox();
+ $this->footer->Stroke($this->img);
+
+ // The titles and legends never gets rotated so make sure
+ // that the angle is 0 before stroking them
+ $aa = $this->img->SetAngle(0);
+ $this->StrokeTitles();
+ }
+
+ for($i=0; $i < count($this->plots) ; ++$i ) {
+ $this->plots[$i]->Legend($this);
+ }
+
+ $this->legend->Stroke($this->img);
+
+ if( !$_csim ) {
+
+ $this->StrokeTexts();
+ $this->img->SetAngle($aa);
+
+ // Draw an outline around the image map
+ if(_JPG_DEBUG)
+ $this->DisplayClientSideaImageMapAreas();
+
+ // Adjust the appearance of the image
+ $this->AdjustSaturationBrightnessContrast();
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+ }
+}
+
+
+
+?>
diff --git a/includes/jpgraph/jpgraph_radar.php b/includes/jpgraph/jpgraph_radar.php
new file mode 100644
index 0000000..07ece96
--- /dev/null
+++ b/includes/jpgraph/jpgraph_radar.php
@@ -0,0 +1,600 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_RADAR.PHP
+// Description: Radar plot extension for JpGraph
+// Created: 2001-02-04
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_radar.php,v 1.7.2.2 2003/10/07 02:40:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+class RadarLogTicks extends Ticks {
+//---------------
+// CONSTRUCTOR
+ function RadarLogTicks() {
+ }
+//---------------
+// PUBLIC METHODS
+
+ // TODO: Add Argument grid
+ function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
+ $start = $aScale->GetMinVal();
+ $limit = $aScale->GetMaxVal();
+ $nextMajor = 10*$start;
+ $step = $nextMajor / 10.0;
+ $count=1;
+
+ $ticklen_maj=5;
+ $dx_maj=round(sin($aAxisAngle)*$ticklen_maj);
+ $dy_maj=round(cos($aAxisAngle)*$ticklen_maj);
+ $ticklen_min=3;
+ $dx_min=round(sin($aAxisAngle)*$ticklen_min);
+ $dy_min=round(cos($aAxisAngle)*$ticklen_min);
+
+ $aMajPos=array();
+ $aMajLabel=array();
+
+ if( $this->supress_first )
+ $aMajLabel[]="";
+ else
+ $aMajLabel[]=$start;
+ $yr=$aScale->RelTranslate($start);
+ $xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($yr*sin($aAxisAngle));
+ $aMajPos[]=$xt+2*$dx_maj;
+ $aMajPos[]=$yt-$aImg->GetFontheight()/2;
+ $grid[]=$xt;
+ $grid[]=$yt;
+
+ $aImg->SetLineWeight($this->weight);
+
+ for($y=$start; $y<=$limit; $y+=$step,++$count ) {
+ $yr=$aScale->RelTranslate($y);
+ $xt=round($yr*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($yr*sin($aAxisAngle));
+ if( $count % 10 == 0 ) {
+ $grid[]=$xt;
+ $grid[]=$yt;
+ $aMajPos[]=$xt+2*$dx_maj;
+ $aMajPos[]=$yt-$aImg->GetFontheight()/2;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
+ $aImg->Line($xt+$dx_maj,$yt+$dy_maj,$xt-$dx_maj,$yt-$dy_maj);
+ if( $this->majcolor!="" ) $aImg->PopColor();
+ }
+ $aMajLabel[]=$nextMajor;
+ $nextMajor *= 10;
+ $step *= 10;
+ $count=1;
+ }
+ else
+ if( !$this->supress_minor_tickmarks ) {
+ if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
+ $aImg->Line($xt+$dx_min,$yt+$dy_min,$xt-$dx_min,$yt-$dy_min);
+ if( $this->mincolor!="" ) $aImg->PopColor();
+ }
+ }
+ }
+}
+
+class RadarLinearTicks extends LinearTicks {
+//---------------
+// CONSTRUCTOR
+ function RadarLinearTicks() {
+ // Empty
+ }
+
+//---------------
+// PUBLIC METHODS
+
+ // TODO: Add argument grid
+ function Stroke(&$aImg,&$grid,$aPos,$aAxisAngle,&$aScale,&$aMajPos,&$aMajLabel) {
+ // Prepare to draw linear ticks
+ $maj_step_abs = abs($aScale->scale_factor*$this->major_step);
+ $min_step_abs = abs($aScale->scale_factor*$this->minor_step);
+ $nbrmaj = floor(($aScale->world_abs_size)/$maj_step_abs);
+ $nbrmin = floor(($aScale->world_abs_size)/$min_step_abs);
+ $skip = round($nbrmin/$nbrmaj); // Don't draw minor ontop of major
+
+ // Draw major ticks
+ $ticklen2=$this->major_abs_size;
+ $dx=round(sin($aAxisAngle)*$ticklen2);
+ $dy=round(cos($aAxisAngle)*$ticklen2);
+ $label=$aScale->scale[0]+$this->major_step;
+
+ $aImg->SetLineWeight($this->weight);
+
+ for($i=1; $i<=$nbrmaj; ++$i) {
+ $xt=round($i*$maj_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($i*$maj_step_abs*sin($aAxisAngle));
+ $aMajLabel[]=$label;
+ $label += $this->major_step;
+ $grid[]=$xt;
+ $grid[]=$yt;
+ $aMajPos[($i-1)*2]=$xt+2*$dx;
+ $aMajPos[($i-1)*2+1]=$yt-$aImg->GetFontheight()/2;
+ if( !$this->supress_tickmarks ) {
+ if( $this->majcolor!="" ) $aImg->PushColor($this->majcolor);
+ $aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
+ if( $this->majcolor!="" ) $aImg->PopColor();
+ }
+ }
+
+ // Draw minor ticks
+ $ticklen2=$this->minor_abs_size;
+ $dx=round(sin($aAxisAngle)*$ticklen2);
+ $dy=round(cos($aAxisAngle)*$ticklen2);
+ if( !$this->supress_tickmarks && !$this->supress_minor_tickmarks) {
+ if( $this->mincolor!="" ) $aImg->PushColor($this->mincolor);
+ for($i=1; $i<=$nbrmin; ++$i) {
+ if( ($i % $skip) == 0 ) continue;
+ $xt=round($i*$min_step_abs*cos($aAxisAngle))+$aScale->scale_abs[0];
+ $yt=$aPos-round($i*$min_step_abs*sin($aAxisAngle));
+ $aImg->Line($xt+$dx,$yt+$dy,$xt-$dx,$yt-$dy);
+ }
+ if( $this->mincolor!="" ) $aImg->PopColor();
+ }
+ }
+}
+
+
+
+//===================================================
+// CLASS RadarAxis
+// Description: Implements axis for the spider graph
+//===================================================
+class RadarAxis extends Axis {
+ var $title_color="navy";
+ var $title=null;
+//---------------
+// CONSTRUCTOR
+ function RadarAxis(&$img,&$aScale,$color=array(0,0,0)) {
+ parent::Axis($img,$aScale,$color);
+ $this->len=$img->plotheight;
+ $this->title = new Text();
+ $this->title->SetFont(FF_FONT1,FS_BOLD);
+ $this->color = array(0,0,0);
+ }
+//---------------
+// PUBLIC METHODS
+ function SetTickLabels($l) {
+ $this->ticks_label = $l;
+ }
+
+
+ // Stroke the axis
+ // $pos = Vertical position of axis
+ // $aAxisAngle = Axis angle
+ // $grid = Returns an array with positions used to draw the grid
+ // $lf = Label flag, TRUE if the axis should have labels
+ function Stroke($pos,$aAxisAngle,&$grid,$title,$lf) {
+ $this->img->SetColor($this->color);
+
+ // Determine end points for the axis
+ $x=round($this->scale->world_abs_size*cos($aAxisAngle)+$this->scale->scale_abs[0]);
+ $y=round($pos-$this->scale->world_abs_size*sin($aAxisAngle));
+
+ // Draw axis
+ $this->img->SetColor($this->color);
+ $this->img->SetLineWeight($this->weight);
+ if( !$this->hide )
+ $this->img->Line($this->scale->scale_abs[0],$pos,$x,$y);
+
+ $this->scale->ticks->Stroke($this->img,$grid,$pos,$aAxisAngle,$this->scale,$majpos,$majlabel);
+
+ // Draw labels
+ if( $lf && !$this->hide ) {
+ $this->img->SetFont($this->font_family,$this->font_style,$this->font_size);
+ $this->img->SetTextAlign("left","top");
+ $this->img->SetColor($this->color);
+
+ // majpos contsins (x,y) coordinates for labels
+ for($i=0; $i<count($majpos)/2; ++$i) {
+ if( $this->ticks_label != null )
+ $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$this->ticks_label[$i]);
+ else
+ $this->img->StrokeText($majpos[$i*2],$majpos[$i*2+1],$majlabel[$i]);
+ }
+ }
+ $this->_StrokeAxisTitle($pos,$aAxisAngle,$title);
+ }
+//---------------
+// PRIVATE METHODS
+
+ function _StrokeAxisTitle($pos,$aAxisAngle,$title) {
+ $this->title->Set($title);
+ $marg=6+$this->title->margin;
+ $xt=round(($this->scale->world_abs_size+$marg)*cos($aAxisAngle)+$this->scale->scale_abs[0]);
+ $yt=round($pos-($this->scale->world_abs_size+$marg)*sin($aAxisAngle));
+
+ // Position the axis title.
+ // dx, dy is the offset from the top left corner of the bounding box that sorrounds the text
+ // that intersects with the extension of the corresponding axis. The code looks a little
+ // bit messy but this is really the only way of having a reasonable position of the
+ // axis titles.
+ $h=$this->img->GetFontHeight();
+ $w=$this->img->GetTextWidth($title);
+ while( $aAxisAngle > 2*M_PI ) $aAxisAngle -= 2*M_PI;
+ if( $aAxisAngle>=7*M_PI/4 || $aAxisAngle <= M_PI/4 ) $dx=0;
+ if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dx=($aAxisAngle-M_PI/4)*2/M_PI;
+ if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dx=1;
+ if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dx=(1-($aAxisAngle-M_PI*5/4)*2/M_PI);
+
+ if( $aAxisAngle>=7*M_PI/4 ) $dy=(($aAxisAngle-M_PI)-3*M_PI/4)*2/M_PI;
+ if( $aAxisAngle<=M_PI/4 ) $dy=(1-$aAxisAngle*2/M_PI);
+ if( $aAxisAngle>=M_PI/4 && $aAxisAngle <= 3*M_PI/4 ) $dy=1;
+ if( $aAxisAngle>=3*M_PI/4 && $aAxisAngle <= 5*M_PI/4 ) $dy=(1-($aAxisAngle-3*M_PI/4)*2/M_PI);
+ if( $aAxisAngle>=5*M_PI/4 && $aAxisAngle <= 7*M_PI/4 ) $dy=0;
+
+ if( !$this->hide )
+ $this->title->Stroke($this->img,$xt-$dx*$w,$yt-$dy*$h,$title);
+ }
+
+
+} // Class
+
+
+//===================================================
+// CLASS RadarGrid
+// Description: Draws grid for the spider graph
+//===================================================
+class RadarGrid extends Grid {
+//------------
+// CONSTRUCTOR
+ function RadarGrid() {
+ }
+
+//----------------
+// PRIVATE METHODS
+ function Stroke(&$img,&$grid) {
+ if( !$this->show ) return;
+ $nbrticks = count($grid[0])/2;
+ $nbrpnts = count($grid);
+ $img->SetColor($this->grid_color);
+ $img->SetLineWeight($this->weight);
+ for($i=0; $i<$nbrticks; ++$i) {
+ for($j=0; $j<$nbrpnts; ++$j) {
+ $pnts[$j*2]=$grid[$j][$i*2];
+ $pnts[$j*2+1]=$grid[$j][$i*2+1];
+ }
+ for($k=0; $k<$nbrpnts; ++$k ){
+ $l=($k+1)%$nbrpnts;
+ if( $this->type == "solid" )
+ $img->Line($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1]);
+ elseif( $this->type == "dotted" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],1,6);
+ elseif( $this->type == "dashed" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],2,4);
+ elseif( $this->type == "longdashed" )
+ $img->DashedLine($pnts[$k*2],$pnts[$k*2+1],$pnts[$l*2],$pnts[$l*2+1],8,6);
+ }
+ $pnts=array();
+ }
+ }
+} // Class
+
+
+//===================================================
+// CLASS RadarPlot
+// Description: Plot a spiderplot
+//===================================================
+class RadarPlot {
+ var $data=array();
+ var $fill=false, $fill_color=array(200,170,180);
+ var $color=array(0,0,0);
+ var $legend="";
+ var $weight=1;
+//---------------
+// CONSTRUCTOR
+ function RadarPlot($data) {
+ $this->data = $data;
+ }
+
+//---------------
+// PUBLIC METHODS
+ function Min() {
+ return Min($this->data);
+ }
+
+ function Max() {
+ return Max($this->data);
+ }
+
+ function SetLegend($legend) {
+ $this->legend=$legend;
+ }
+
+
+ function SetLineWeight($w) {
+ $this->weight=$w;
+ }
+
+ function SetFillColor($aColor) {
+ $this->fill_color = $aColor;
+ $this->fill = true;
+ }
+
+ function SetFill($f=true) {
+ $this->fill = $f;
+ }
+
+ function SetColor($aColor,$aFillColor=false) {
+ $this->color = $aColor;
+ if( $aFillColor ) {
+ $this->SetFillColor($aFillColor);
+ $this->fill = true;
+ }
+ }
+
+ function GetCSIMareas() {
+ JpGraphError::Raise("Client side image maps not supported for RadarPlots.");
+ }
+
+ function Stroke(&$img, $pos, &$scale, $startangle) {
+ $nbrpnts = count($this->data);
+ $astep=2*M_PI/$nbrpnts;
+ $a=$startangle;
+
+ // Rotate each point to the correct axis-angle
+ // TODO: Update for LogScale
+ for($i=0; $i<$nbrpnts; ++$i) {
+ //$c=$this->data[$i];
+ $cs=$scale->RelTranslate($this->data[$i]);
+ $x=round($cs*cos($a)+$scale->scale_abs[0]);
+ $y=round($pos-$cs*sin($a));
+ /*
+ $c=log10($c);
+ $x=round(($c-$scale->scale[0])*$scale->scale_factor*cos($a)+$scale->scale_abs[0]);
+ $y=round($pos-($c-$scale->scale[0])*$scale->scale_factor*sin($a));
+ */
+ $pnts[$i*2]=$x;
+ $pnts[$i*2+1]=$y;
+ $a += $astep;
+ }
+ if( $this->fill ) {
+ $img->SetColor($this->fill_color);
+ $img->FilledPolygon($pnts);
+ }
+ $img->SetLineWeight($this->weight);
+ $img->SetColor($this->color);
+ $pnts[]=$pnts[0];
+ $pnts[]=$pnts[1];
+ $img->Polygon($pnts);
+ }
+
+//---------------
+// PRIVATE METHODS
+ function GetCount() {
+ return count($this->data);
+ }
+
+ function Legend(&$graph) {
+ if( $this->legend=="" ) return;
+ if( $this->fill )
+ $graph->legend->Add($this->legend,$this->fill_color);
+ else
+ $graph->legend->Add($this->legend,$this->color);
+ }
+
+} // Class
+
+//===================================================
+// CLASS RadarGraph
+// Description: Main container for a spider graph
+//===================================================
+class RadarGraph extends Graph {
+ var $posx;
+ var $posy;
+ var $len;
+ var $plots=null, $axis_title=null;
+ var $grid,$axis=null;
+//---------------
+// CONSTRUCTOR
+ function RadarGraph($width=300,$height=200,$cachedName="",$timeout=0,$inline=1) {
+ $this->Graph($width,$height,$cachedName,$timeout,$inline);
+ $this->posx=$width/2;
+ $this->posy=$height/2;
+ $this->len=min($width,$height)*0.35;
+ $this->SetColor(array(255,255,255));
+ $this->SetTickDensity(TICKD_NORMAL);
+ $this->SetScale("lin");
+ $this->SetGridDepth(DEPTH_FRONT);
+
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SupressTickMarks($f=true) {
+ if( ERR_DEPRECATED )
+ JpGraphError::Raise('RadarGraph::SupressTickMarks() is deprecated. Use HideTickMarks() instead.');
+ $this->axis->scale->ticks->SupressTickMarks($f);
+ }
+
+ function HideTickMarks($aFlag=true) {
+ $this->axis->scale->ticks->SupressTickMarks($aFlag);
+ }
+
+ function ShowMinorTickmarks($aFlag=true) {
+ $this->yscale->ticks->SupressMinorTickMarks(!$aFlag);
+ }
+
+ function SetScale($axtype,$ymin=1,$ymax=1) {
+ if( $axtype != "lin" && $axtype != "log" ) {
+ JpGraphError::Raise("Illegal scale for spiderplot ($axtype). Must be \"lin\" or \"log\"");
+ }
+ if( $axtype=="lin" ) {
+ $this->yscale = & new LinearScale($ymin,$ymax);
+ $this->yscale->ticks = & new RadarLinearTicks();
+ $this->yscale->ticks->SupressMinorTickMarks();
+ }
+ elseif( $axtype=="log" ) {
+ $this->yscale = & new LogScale($ymin,$ymax);
+ $this->yscale->ticks = & new RadarLogTicks();
+ }
+
+ $this->axis = & new RadarAxis($this->img,$this->yscale);
+ $this->grid = & new RadarGrid();
+ }
+
+ function SetSize($aSize) {
+ if( $aSize<0.1 || $aSize>1 )
+ JpGraphError::Raise("Radar Plot size must be between 0.1 and 1. (Your value=$s)");
+ $this->len=min($this->img->width,$this->img->height)*$aSize/2;
+ }
+
+ function SetPlotSize($aSize) {
+ $this->SetSize($aSize);
+ }
+
+ function SetTickDensity($densy=TICKD_NORMAL) {
+ $this->ytick_factor=25;
+ switch( $densy ) {
+ case TICKD_DENSE:
+ $this->ytick_factor=12;
+ break;
+ case TICKD_NORMAL:
+ $this->ytick_factor=25;
+ break;
+ case TICKD_SPARSE:
+ $this->ytick_factor=40;
+ break;
+ case TICKD_VERYSPARSE:
+ $this->ytick_factor=70;
+ break;
+ default:
+ JpGraphError::Raise("RadarPlot Unsupported Tick density: $densy");
+ }
+ }
+
+ function SetPos($px,$py=0.5) {
+ $this->SetCenter($px,$py);
+ }
+
+ function SetCenter($px,$py=0.5) {
+ assert($px > 0 && $py > 0 );
+ $this->posx=$this->img->width*$px;
+ $this->posy=$this->img->height*$py;
+ }
+
+ function SetColor($c) {
+ $this->SetMarginColor($c);
+ }
+
+ function SetTitles($title) {
+ $this->axis_title = $title;
+ }
+
+ function Add(&$splot) {
+ $this->plots[]=$splot;
+ }
+
+ function GetPlotsYMinMax() {
+ $min=$this->plots[0]->Min();
+ $max=$this->plots[0]->Max();
+ foreach( $this->plots as $p ) {
+ $max=max($max,$p->Max());
+ $min=min($min,$p->Min());
+ }
+ if( $min < 0 )
+ JpGraphError::Raise("Minimum data $min (Radar plots only makes sence to use when all data points > 0)");
+ return array($min,$max);
+ }
+
+ // Stroke the Radar graph
+ function Stroke($aStrokeFileName="") {
+ // Set Y-scale
+ if( !$this->yscale->IsSpecified() && count($this->plots)>0 ) {
+ list($min,$max) = $this->GetPlotsYMinMax();
+ $this->yscale->AutoScale($this->img,0,$max,$this->len/$this->ytick_factor);
+ }
+ // Set start position end length of scale (in absolute pixels)
+ $this->yscale->SetConstants($this->posx,$this->len);
+
+ // We need as many axis as there are data points
+ $nbrpnts=$this->plots[0]->GetCount();
+
+ // If we have no titles just number the axis 1,2,3,...
+ if( $this->axis_title==null ) {
+ for($i=0; $i<$nbrpnts; ++$i )
+ $this->axis_title[$i] = $i+1;
+ }
+ elseif(count($this->axis_title)<$nbrpnts)
+ JpGraphError::Raise("Number of titles does not match number of points in plot.");
+ for($i=0; $i<count($this->plots); ++$i )
+ if( $nbrpnts != $this->plots[$i]->GetCount() )
+ JpGraphError::Raise("Each spider plot must have the same number of data points.");
+
+ if( $this->background_image != "" ) {
+ $this->StrokeFrameBackground();
+ }
+ else {
+ $this->StrokeFrame();
+ }
+ $astep=2*M_PI/$nbrpnts;
+
+ // Prepare legends
+ for($i=0; $i<count($this->plots); ++$i)
+ $this->plots[$i]->Legend($this);
+ $this->legend->Stroke($this->img);
+ $this->footer->Stroke($this->img);
+
+ if( $this->grid_depth == DEPTH_BACK ) {
+ // Draw axis and grid
+ for( $i=0,$a=M_PI/2; $i < $nbrpnts; ++$i, $a += $astep ) {
+ $this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
+ }
+ }
+
+ // Plot points
+ $a=M_PI/2;
+ for($i=0; $i<count($this->plots); ++$i )
+ $this->plots[$i]->Stroke($this->img, $this->posy, $this->yscale, $a);
+
+ if( $this->grid_depth != DEPTH_BACK ) {
+ // Draw axis and grid
+ for( $i=0,$a=M_PI/2; $i < $nbrpnts; ++$i, $a += $astep ) {
+ $this->axis->Stroke($this->posy,$a,$grid[$i],$this->axis_title[$i],$i==0);
+ }
+ }
+ $this->grid->Stroke($this->img,$grid);
+ $this->StrokeTitles();
+
+ // Stroke texts
+ if( $this->texts != null ) {
+ foreach( $this->texts as $t)
+ $t->Stroke($this->img);
+ }
+
+ // Should we do any final image transformation
+ if( $this->iImgTrans ) {
+ if( !class_exists('ImgTrans') ) {
+ require_once('jpgraph_imgtrans.php');
+ }
+
+ $tform = new ImgTrans($this->img->img);
+ $this->img->img = $tform->Skew3D($this->iImgTransHorizon,$this->iImgTransSkewDist,
+ $this->iImgTransDirection,$this->iImgTransHighQ,
+ $this->iImgTransMinSize,$this->iImgTransFillColor,
+ $this->iImgTransBorder);
+ }
+
+ // If the filename is given as the special "__handle"
+ // then the image handler is returned and the image is NOT
+ // streamed back
+ if( $aStrokeFileName == _IMG_HANDLER ) {
+ return $this->img->img;
+ }
+ else {
+ // Finally stream the generated picture
+ $this->cache->PutAndStream($this->img,$this->cache_name,$this->inline,
+ $aStrokeFileName);
+ }
+ }
+} // Class
+
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_regstat.php b/includes/jpgraph/jpgraph_regstat.php
new file mode 100644
index 0000000..99989da
--- /dev/null
+++ b/includes/jpgraph/jpgraph_regstat.php
@@ -0,0 +1,105 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_REGSTAT.PHP
+// Description: Regression and statistical analysis helper classes
+// Created: 2002-12-01
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_regstat.php,v 1.2 2003/03/08 11:29:21 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2002 Johan Persson
+//========================================================================
+*/
+
+//------------------------------------------------------------------------
+// CLASS Spline
+// Create a new data array from an existing data array but with more points.
+// The new points are interpolated using a cubic spline algorithm
+//------------------------------------------------------------------------
+class Spline {
+ // 3:rd degree polynom approximation
+
+ var $xdata,$ydata; // Data vectors
+ var $y2; // 2:nd derivate of ydata
+ var $n=0;
+
+ function Spline($xdata,$ydata) {
+ $this->y2 = array();
+ $this->xdata = $xdata;
+ $this->ydata = $ydata;
+
+ $n = count($ydata);
+ $this->n = $n;
+
+ // Natural spline 2:derivate == 0 at endpoints
+ $this->y2[0] = 0.0;
+ $this->y2[$n-1] = 0.0;
+ $delta[0] = 0.0;
+
+ // Calculate 2:nd derivate
+ for($i=1; $i < $n-1; ++$i) {
+ $d = ($xdata[$i+1]-$xdata[$i-1]);
+ if( $d == 0 ) {
+ JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
+ }
+ $s = ($xdata[$i]-$xdata[$i-1])/$d;
+ $p = $s*$this->y2[$i-1]+2.0;
+ $this->y2[$i] = ($s-1.0)/$p;
+ $delta[$i] = ($ydata[$i+1]-$ydata[$i])/($xdata[$i+1]-$xdata[$i]) -
+ ($ydata[$i]-$ydata[$i-1])/($xdata[$i]-$xdata[$i-1]);
+ $delta[$i] = (6.0*$delta[$i]/($xdata[$i+1]-$xdata[$i-1])-$s*$delta[$i-1])/$p;
+ }
+
+ // Backward substitution
+ for( $j=$n-2; $j >= 0; --$j ) {
+ $this->y2[$j] = $this->y2[$j]*$this->y2[$j+1] + $delta[$j];
+ }
+ }
+
+ // Return the two new data vectors
+ function Get($num=50) {
+ $n = $this->n ;
+ $step = ($this->xdata[$n-1]-$this->xdata[0]) / ($num-1);
+ $xnew=array();
+ $ynew=array();
+ $xnew[0] = $this->xdata[0];
+ $ynew[0] = $this->ydata[0];
+ for( $j=1; $j < $num; ++$j ) {
+ $xnew[$j] = $xnew[0]+$j*$step;
+ $ynew[$j] = $this->Interpolate($xnew[$j]);
+ }
+ return array($xnew,$ynew);
+ }
+
+ // Return a single interpolated Y-value from an x value
+ function Interpolate($xpoint) {
+
+ $max = $this->n-1;
+ $min = 0;
+
+ // Binary search to find interval
+ while( $max-$min > 1 ) {
+ $k = ($max+$min) / 2;
+ if( $this->xdata[$k] > $xpoint )
+ $max=$k;
+ else
+ $min=$k;
+ }
+
+ // Each interval is interpolated by a 3:degree polynom function
+ $h = $this->xdata[$max]-$this->xdata[$min];
+
+ if( $h == 0 ) {
+ JpGraphError::Raise('Invalid input data for spline. Two or more consecutive input X-values are equal. Each input X-value must differ since from a mathematical point of view it must be a one-to-one mapping, i.e. each X-value must correspond to exactly one Y-value.');
+ }
+
+
+ $a = ($this->xdata[$max]-$xpoint)/$h;
+ $b = ($xpoint-$this->xdata[$min])/$h;
+ return $a*$this->ydata[$min]+$b*$this->ydata[$max]+
+ (($a*$a*$a-$a)*$this->y2[$min]+($b*$b*$b-$b)*$this->y2[$max])*($h*$h)/6.0;
+ }
+}
+
+// EOF
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_scatter.php b/includes/jpgraph/jpgraph_scatter.php
new file mode 100644
index 0000000..4f3a8c3
--- /dev/null
+++ b/includes/jpgraph/jpgraph_scatter.php
@@ -0,0 +1,222 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_SCATTER.PHP
+// Description: Scatter (and impuls) plot extension for JpGraph
+// Created: 2001-02-11
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_scatter.php,v 1.31.2.1 2003/08/15 15:27:40 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2001,2002 Johan Persson
+//========================================================================
+*/
+
+
+//===================================================
+// CLASS FieldArrow
+// Description: Draw an arrow at (x,y) with angle a
+//===================================================
+class FieldArrow {
+ var $iSize=10; // Length in pixels for arrow
+ var $iArrowSize = 2;
+ var $iColor='black';
+ var $isizespec = array(
+ array(2,1),array(3,2),array(4,3),array(6,4),array(7,4),array(8,5),array(10,6),array(12,7),array(16,8),array(20,10));
+ function FieldArrow() {
+ }
+
+ function SetSize($aSize,$aArrowSize=2) {
+ $this->iSize = $aSize;
+ $this->iArrowSize = $aArrowSize;
+ }
+
+ function SetColor($aColor) {
+ $this->iColor = $aColor;
+ }
+
+ function Stroke($aImg,$x,$y,$a) {
+ $old_origin = $aImg->SetCenter($x,$y);
+ $old_a = $aImg->SetAngle(-$a);
+
+ $dx = round($this->iSize/2);
+ $c = array($x-$dx,$y,$x+$dx,$y);
+ $x += $dx;
+
+ list($dx,$dy) = $this->isizespec[$this->iArrowSize];
+ $ca = array($x,$y,$x-$dx,$y-$dy,$x-$dx,$y+$dy,$x,$y);
+
+ $aImg->SetColor($this->iColor);
+ $aImg->Polygon($c);
+ $aImg->FilledPolygon($ca);
+
+ $aImg->SetCenter($old_origin[0],$old_origin[1]);
+ $aImg->SetAngle($old_a);
+ }
+}
+
+//===================================================
+// CLASS FieldPlot
+// Description: Render a field plot
+//===================================================
+class FieldPlot extends Plot {
+ var $iAngles;
+ var $iCallback='';
+ function FieldPlot($datay,$datax,$angles) {
+ if( (count($datax) != count($datay)) )
+ JpGraphError::Raise("Fieldplots must have equal number of X and Y points.");
+ if( (count($datax) != count($angles)) )
+ JpGraphError::Raise("Fieldplots must have an angle specified for each X and Y points.");
+
+ $this->iAngles = $angles;
+
+ $this->Plot($datay,$datax);
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(15);
+
+ $this->arrow = new FieldArrow();
+ }
+
+ function SetCallback($aFunc) {
+ $this->iCallback = $aFunc;
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ // Remeber base color and size
+ $bc = $this->arrow->iColor;
+ $bs = $this->arrow->iSize;
+ $bas = $this->arrow->iArrowSize;
+
+ for( $i=0; $i<$this->numpoints; ++$i ) {
+ // Skip null values
+ if( $this->coords[0][$i]==="" )
+ continue;
+
+ $f = $this->iCallback;
+ if( $f != "" ) {
+ list($cc,$cs,$cas) = call_user_func($f,$this->coords[1][$i],$this->coords[0][$i],$this->iAngles[$i]);
+ // Fall back on global data if the callback isn't set
+ if( $cc == "" ) $cc = $bc;
+ if( $cs == "" ) $cs = $bs;
+ if( $cas == "" ) $cas = $bas;
+ //echo "f=$f, cc=$cc, cs=$cs, cas=$cas<br>";
+ $this->arrow->SetColor($cc);
+ $this->arrow->SetSize($cs,$cas);
+ }
+
+ $xt = $xscale->Translate($this->coords[1][$i]);
+ $yt = $yscale->Translate($this->coords[0][$i]);
+
+ $this->arrow->Stroke($img,$xt,$yt,$this->iAngles[$i]);
+ $this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
+ }
+ }
+
+ // Framework function
+ function Legend(&$aGraph) {
+ if( $this->legend != "" ) {
+ $aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+}
+
+//===================================================
+// CLASS ScatterPlot
+// Description: Render X and Y plots
+//===================================================
+class ScatterPlot extends Plot {
+ var $impuls = false;
+ var $linkpoints = false, $linkpointweight=1, $linkpointcolor="black";
+//---------------
+// CONSTRUCTOR
+ function ScatterPlot($datay,$datax=false) {
+ if( (count($datax) != count($datay)) && is_array($datax))
+ JpGraphError::Raise("Scatterplot must have equal number of X and Y points.");
+ $this->Plot($datay,$datax);
+ $this->mark = new PlotMark();
+ $this->mark->SetType(MARK_SQUARE);
+ $this->mark->SetColor($this->color);
+ $this->value->SetAlign('center','center');
+ $this->value->SetMargin(0);
+ }
+
+//---------------
+// PUBLIC METHODS
+ function SetImpuls($f=true) {
+ $this->impuls = $f;
+ }
+
+ // Combine the scatter plot points with a line
+ function SetLinkPoints($aFlag=true,$aColor="black",$aWeight=1) {
+ $this->linkpoints=$aFlag;
+ $this->linkpointcolor=$aColor;
+ $this->linkpointweight=$aWeight;
+ }
+
+ function Stroke(&$img,&$xscale,&$yscale) {
+
+ $ymin=$yscale->scale_abs[0];
+ if( $yscale->scale[0] < 0 )
+ $yzero=$yscale->Translate(0);
+ else
+ $yzero=$yscale->scale_abs[0];
+
+ $this->csimareas = '';
+ for( $i=0; $i<$this->numpoints; ++$i ) {
+
+ // Skip null values
+ if( $this->coords[0][$i]==="" )
+ continue;
+
+ if( isset($this->coords[1]) )
+ $xt = $xscale->Translate($this->coords[1][$i]);
+ else
+ $xt = $xscale->Translate($i);
+ $yt = $yscale->Translate($this->coords[0][$i]);
+
+
+ if( $this->linkpoints && isset($yt_old) ) {
+ $img->SetColor($this->linkpointcolor);
+ $img->SetLineWeight($this->linkpointweight);
+ $img->Line($xt_old,$yt_old,$xt,$yt);
+ }
+
+ if( $this->impuls ) {
+ $img->SetColor($this->color);
+ $img->SetLineWeight($this->weight);
+ $img->Line($xt,$yzero,$xt,$yt);
+ }
+
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->mark->SetCSIMTarget($this->csimtargets[$i]);
+ $this->mark->SetCSIMAlt($this->csimalts[$i]);
+ }
+
+ if( isset($this->coords[1]) ) {
+ $this->mark->SetCSIMAltVal($this->coords[0][$i],$this->coords[1][$i]);
+ }
+ else {
+ $this->mark->SetCSIMAltVal($this->coords[0][$i],$i);
+ }
+
+ $this->mark->Stroke($img,$xt,$yt);
+
+ $this->csimareas .= $this->mark->GetCSIMAreas();
+ $this->value->Stroke($img,$this->coords[0][$i],$xt,$yt);
+
+ $xt_old = $xt;
+ $yt_old = $yt;
+ }
+ }
+
+ // Framework function
+ function Legend(&$aGraph) {
+ if( $this->legend != "" ) {
+ $aGraph->legend->Add($this->legend,$this->mark->fill_color,$this->mark,0,
+ $this->legendcsimtarget,$this->legendcsimalt);
+ }
+ }
+} // Class
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/jpgraph/jpgraph_stock.php b/includes/jpgraph/jpgraph_stock.php
new file mode 100644
index 0000000..142a21a
--- /dev/null
+++ b/includes/jpgraph/jpgraph_stock.php
@@ -0,0 +1,183 @@
+<?php
+/*=======================================================================
+// File: JPGRAPH_STOCK.PHP
+// Description: Stock plot extension for JpGraph
+// Created: 2003-01-27
+// Author: Johan Persson (johanp@aditus.nu)
+// Ver: $Id: jpgraph_stock.php,v 1.5.2.1 2003/11/24 15:39:11 aditus Exp $
+//
+// License: This code is released under QPL
+// Copyright (C) 2003 Johan Persson
+//========================================================================
+*/
+
+//===================================================
+// CLASS StockPlot
+//===================================================
+class StockPlot extends Plot {
+ var $iWidth=9;
+ var $iEndLines=1;
+ var $iStockColor1='white',$iStockColor2='darkred',$iStockColor3='darkred';
+ var $iTupleSize = 4;
+//---------------
+// CONSTRUCTOR
+ function StockPlot(&$datay,$datax=false) {
+ if( count($datay) % $this->iTupleSize ) {
+ JpGraphError::Raise('Data values for Stock charts must contain an even multiple of '.$this->iTupleSize.' data points.');
+ }
+ $this->Plot($datay,$datax);
+ $this->numpoints /= $this->iTupleSize;
+ }
+//---------------
+// PUBLIC METHODS
+
+ function SetColor($aColor,$aColor1='white',$aColor2='darkred',$aColor3='darkred') {
+ $this->color = $aColor;
+ $this->iStockColor1 = $aColor1;
+ $this->iStockColor2 = $aColor2;
+ $this->iStockColor3 = $aColor3;
+ }
+
+ function SetWidth($aWidth) {
+ // Make sure it's odd
+ $this->iWidth = 2*floor($aWidth/2)+1;
+ }
+
+ function HideEndLines($aHide=true) {
+ $this->iEndLines = !$aHide;
+ }
+
+ // Gets called before any axis are stroked
+ function PreStrokeAdjust(&$graph) {
+ if( $this->center ) {
+ $a=0.5; $b=0.5;
+ $this->numpoints++;
+ } else {
+ $a=0; $b=0;
+ }
+ $graph->xaxis->scale->ticks->SetXLabelOffset($a);
+ $graph->SetTextScaleOff($b);
+ }
+
+ // Method description
+ function Stroke($img,$xscale,$yscale) {
+ $n=$this->numpoints;
+ if( $this->center ) $n--;
+ if( isset($this->coords[1]) ) {
+ if( count($this->coords[1])!=$n )
+ JpGraphError::Raise("Number of X and Y points are not equal. Number of X-points:".count($this->coords[1])." Number of Y-points:$numpoints");
+ else
+ $exist_x = true;
+ }
+ else
+ $exist_x = false;
+
+ if( $exist_x )
+ $xs=$this->coords[1][0];
+ else
+ $xs=0;
+
+ $ts = $this->iTupleSize;
+ $this->csimareas = '';
+ for( $i=0; $i<$n; ++$i) {
+
+ //If value is NULL, then don't draw a bar at all
+ if ($this->coords[0][$i] === null) continue;
+
+ if( $exist_x ) $x=$this->coords[1][$i];
+ else $x=$i;
+ $xt = $xscale->Translate($x);
+
+ $neg = $this->coords[0][$i*$ts] > $this->coords[0][$i*$ts+1] ;
+ $yopen = $yscale->Translate($this->coords[0][$i*$ts]);
+ $yclose = $yscale->Translate($this->coords[0][$i*$ts+1]);
+ $ymin = $yscale->Translate($this->coords[0][$i*$ts+2]);
+ $ymax = $yscale->Translate($this->coords[0][$i*$ts+3]);
+
+ $dx = floor($this->iWidth/2);
+ $xl = $xt - $dx;
+ $xr = $xt + $dx;
+
+ if( $neg )
+ $img->SetColor($this->iStockColor3);
+ else
+ $img->SetColor($this->iStockColor1);
+ $img->FilledRectangle($xl,$yopen,$xr,$yclose);
+ $img->SetLineWeight($this->weight);
+ if( $neg )
+ $img->SetColor($this->iStockColor2);
+ else
+ $img->SetColor($this->color);
+
+ $img->Rectangle($xl,$yopen,$xr,$yclose);
+
+ if( $yopen < $yclose ) {
+ $ytop = $yopen ;
+ $ybottom = $yclose ;
+ }
+ else {
+ $ytop = $yclose ;
+ $ybottom = $yopen ;
+ }
+ $img->SetColor($this->color);
+ $img->Line($xt,$ytop,$xt,$ymax);
+ $img->Line($xt,$ybottom,$xt,$ymin);
+
+ if( $this->iEndLines ) {
+ $img->Line($xl,$ymax,$xr,$ymax);
+ $img->Line($xl,$ymin,$xr,$ymin);
+ }
+
+ // A chance for subclasses to add things to the bar
+ // for data point i
+ $this->ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg);
+
+ // Setup image maps
+ if( !empty($this->csimtargets[$i]) ) {
+ $this->csimareas.= '<area shape="rect" coords="'.
+ round($xl).','.round($ytop).','.
+ round($xr).','.round($ybottom).'" ';
+ $this->csimareas .= ' href="'.$this->csimtargets[$i].'"';
+ if( !empty($this->csimalts[$i]) ) {
+ $sval=$this->csimalts[$i];
+ $this->csimareas .= " alt=\"$sval\" title=\"$sval\" ";
+ }
+ $this->csimareas.= ">\n";
+ }
+ }
+ return true;
+ }
+
+ // A hook for subclasses to modify the plot
+ function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {}
+
+} // Class
+
+//===================================================
+// CLASS BoxPlot
+//===================================================
+class BoxPlot extends StockPlot {
+ var $iPColor='black',$iNColor='white';
+ function BoxPlot($datay,$datax=false) {
+ $this->iTupleSize=5;
+ parent::StockPlot($datay,$datax);
+ }
+
+ function SetMedianColor($aPos,$aNeg) {
+ $this->iPColor = $aPos;
+ $this->iNColor = $aNeg;
+ }
+
+ function ModBox($img,$xscale,$yscale,$i,$xl,$xr,$neg) {
+ if( $neg )
+ $img->SetColor($this->iNColor);
+ else
+ $img->SetColor($this->iPColor);
+
+ $y = $yscale->Translate($this->coords[0][$i*5+4]);
+ $img->Line($xl,$y,$xr,$y);
+ }
+}
+
+/* EOF */
+?> \ No newline at end of file
diff --git a/includes/ui.body b/includes/ui.body
new file mode 100644
index 0000000..8840c03
--- /dev/null
+++ b/includes/ui.body
@@ -0,0 +1,148 @@
+<?
+
+/* IF YOU CAN READ THIS SEND A SCREENSHOT TO plasm@roo.me.uk; PHP/APACHE IS
+ CAUSING THIS ANNOYING PRINT-OUT CRAP EVERY SO OFTEN! :-( */
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once 'common.auth';
+require_once 'common.version';
+require_once 'ui.css';
+require_once 'ui.newitems';
+
+function bodyFooter_invoke()
+{
+ global $GLSAMVersion, $GLSAMTag, $recentCounter;
+ recentItems_invoke(false);
+
+ ?>
+
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td><img src="images/icons/flag.png" align="top" alt="">&nbsp;GLSAMaker <? echo($GLSAMVersion."<b>$GLSAMTag</b>"); ?> [ <i><? echo(authGetStatus()); ?></i> ]</td><td align="right" valign="middle"><? echo(date('D F j, G:i a T Y')); ?>&nbsp;</td></tr>
+ </table>
+ <br>
+<? generateKonqBreak(); ?>
+ <form action="frame-view.php" method="get">
+ <table width="100%" border="0" cellspacing="0" cellpadding="3" class="body_rootTable">
+ <tr><td>
+<? /* <img src="images/icons/queue.<? generateImageExt(); ?>" align="top" alt="">&nbsp;<i><? if($recentCounter == 0) echo ("No"); else echo("$recentCounter"); ?> new announcement<? if($recentCounter <> 1) echo("s"); ?> in the past week</i> */ ?>
+ <a target="_top" href="frame-archive.php" class="nav">
+ <img src="images/icons/bugs.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Archive
+ </a>
+ <font color="gray"><b>::</b></font>
+ <a target="_top" href="frame-recent.php" class="nav">
+ <img src="images/icons/queue.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Repository
+ </a>
+ <font color="gray"><b>::</b></font>
+ <a target="_top" href="frame-new.php" class="nav">
+ <img src="images/icons/new.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;File an announcement
+ </a>
+ <font color="gray"><b>::</b></font>
+ <a target="_top" href="frame-request.php" class="nav">
+ <img src="images/icons/view_text.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;File a request
+ </a>
+ <font color="gray"><b>::</b></font>
+ <a target="_top" href="frame-search.php" class="nav">
+ <img src="images/icons/query.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Search
+ </a>
+ <font color="gray"><b>::</b></font>
+ <a target="_top" href="wiki/" class="nav">
+ <img src="images/icons/reporting.<? generateImageExt(); ?>" border="0" align="middle" alt="">&nbsp;Wiki
+ </a>
+ </td>
+ <td align="right">Jump to <acronym title="Gentoo Linux Security Advisory">GLSA</acronym>: ID <input align="bottom" type="text" maxlength="9" size="9" name="id" class="grayinput">&nbsp;</td></tr>
+ </table>
+ </form>
+ <br>
+<?
+}
+
+function bodyHeader_invoke($showAnnouncements = false)
+{
+ recentItems_invoke($showAnnouncements);
+ if($showAnnouncements) echo("\n\n\t<br>");
+ bodyFooter_generateCSSSwitcher();
+
+ ?>
+
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td>Copyright <?echo(date('Y'));?> Gentoo Foundation, Inc.</td><td align="right">Any bugs, comments or suggestions should go to <a href="mailto:plasmaroo@gentoo.org" class="black">plasmaroo</a>.<? /* <a href="http://validator.w3.org/check/referer" class="black">Validate</a>. */ ?>&nbsp;</td></tr>
+ </table>
+
+<?
+}
+
+function generateKonqBreak($break = "\n")
+{
+ if(substr_count($_SERVER['HTTP_USER_AGENT'],'Konqueror/') != 0) echo("<br>\n");
+ echo($break);
+}
+
+function generateHelpIcon($text)
+{
+ echo '<img src="images/icons/help.'.generateImageExt(true).'" class="help" width="16" height="16" alt="" title="'.$text.'">';
+}
+
+function generateInfo($text)
+{
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable"><tr><td width="120" valign="top"><img src="images/icons/resolved.'.generateImageExt(true).'" align="top" alt="">&nbsp;<b>Information: </b></td><td>'.$text.'</td></tr></table><br>';
+}
+
+function generateButton($text, $extra = '', $style = 'grayinput')
+{
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Lynx') != 0)
+ {
+ if($extra)
+ return '<input type="submit"'.$extra.'>';
+ return '<input type="submit" value="'.strip_tags($text).'">';
+ }
+ else
+ {
+ if($style)
+ return '<button class="'.$style.'"'.$extra.'>'.$text.'</button>';
+ return '<button'.$extra.'>'.$text.'</button>';
+ }
+}
+
+function generateImageExt($noecho = false, $failover = 'gif', $normal = 'png')
+{
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Opera 7') != 0){ if(!$noecho) echo $normal; return $normal; }
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'MSIE') != 0){ if(!$noecho) echo $failover; return $failover; }
+ if(substr_count($_SERVER['HTTP_USER_AGENT'], 'Netscape/4.') != 0){ if(!$noecho) echo $failover; return $failover; }
+ if(!$noecho) echo $normal; return $normal;
+}
+
+function generateWarning($text)
+{
+ global $plainOutput;
+ if($plainOutput)
+ echo($text."\n");
+ else
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable"><tr><td width="120" valign="top"><img src="images/icons/warning.'.generateImageExt(true).'" align="top" alt="">&nbsp;<b>Warning: </b></td><td>'.$text.'</td></tr></table><br>';
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/ui.css b/includes/ui.css
new file mode 100644
index 0000000..5c152e2
--- /dev/null
+++ b/includes/ui.css
@@ -0,0 +1,60 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+$CSSBodySheet = 'body.css';
+$CSSDefaultSheet = 'pinstripe.css';
+$CSSSheets = array('pinstripe.css');
+
+function bodyFooter_generateCSSHeader()
+{
+ global $CSSBodySheet, $CSSDefaultSheet, $CSSSheets;
+ foreach($CSSSheets as $CSSSheet)
+ {
+ echo("\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"css/$CSSSheet\"");
+ if(count($CSSSheets) > 1)
+ echo(' title="'.ucfirst(str_replace('.css', '', $CSSSheet)).'"');
+ echo('>');
+ }
+ echo("\n\t<link rel=\"stylesheet\" type=\"text/css\" href=\"css/$CSSBodySheet\">");
+}
+
+function bodyFooter_generateCSSSwitcher()
+{
+ global $CSSSheets;
+ if(count($CSSSheets) > 1)
+ {
+?>
+ <table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">
+ <tr><td align="right"><b>Switch CSS:</b> <? foreach($CSSSheets as $CSSSheet) { echo '<a href="#" onclick="setActiveStyleSheet(\''.ucfirst(str_replace('.css', '', $CSSSheet)).'\')">'.ucfirst(str_replace('.css', '', $CSSSheet)).'</a>&nbsp;'; }?></td></tr>
+ </table>
+
+ <br>
+<? }
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/ui.css.js b/includes/ui.css.js
new file mode 100644
index 0000000..962ec56
--- /dev/null
+++ b/includes/ui.css.js
@@ -0,0 +1,66 @@
+// From: http://www.alistapart.com/stories/alternate
+
+function setActiveStyleSheet(title) {
+ var i, a, main;
+ for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
+ if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
+ a.disabled = true;
+ if(a.getAttribute("title") == title) a.disabled = false;
+ }
+ }
+}
+
+function getActiveStyleSheet() {
+ var i, a;
+ for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
+ if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title") && !a.disabled) return a.getAttribute("title");
+ }
+ return null;
+}
+
+function getPreferredStyleSheet() {
+ var i, a;
+ for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
+ if(a.getAttribute("rel").indexOf("style") != -1
+ && a.getAttribute("rel").indexOf("alt") == -1
+ && a.getAttribute("title")
+ ) return a.getAttribute("title");
+ }
+ return null;
+}
+
+function createCookie(name,value,days) {
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime()+(days*24*60*60*1000));
+ var expires = "; expires="+date.toGMTString();
+ }
+ else expires = "";
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function readCookie(name) {
+ var nameEQ = name + "=";
+ var ca = document.cookie.split(';');
+ for(var i=0;i < ca.length;i++) {
+ var c = ca[i];
+ while (c.charAt(0)==' ') c = c.substring(1,c.length);
+ if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
+ }
+ return null;
+}
+
+window.onload = function(e) {
+ var cookie = readCookie("style");
+ var title = cookie ? cookie : getPreferredStyleSheet();
+ setActiveStyleSheet(title);
+}
+
+window.onunload = function(e) {
+ var title = getActiveStyleSheet();
+ createCookie("style", title, 365);
+}
+
+var cookie = readCookie("style");
+var title = cookie ? cookie : getPreferredStyleSheet();
+setActiveStyleSheet(title);
diff --git a/includes/ui.init b/includes/ui.init
new file mode 100644
index 0000000..2f7d39a
--- /dev/null
+++ b/includes/ui.init
@@ -0,0 +1,49 @@
+<?
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once 'ui.css';
+
+/*! This file contains initBegin() and initEnd() which doesn't do much other
+ than provide a set of standardized headers... */
+
+function initBegin($titleAdd = "")
+{
+ ?>
+<html>
+<head>
+ <title>Gentoo Security<? if($titleAdd != "") echo(" - $titleAdd"); ?></title><? bodyFooter_generateCSSHeader(); ?>
+<?/* <script src="includes/ui.css.js"></script>*/ ?>
+</head>
+
+<body background="images/bg-grid.gif">
+<?
+}
+
+function initEnd()
+{?>
+ </body>
+ </html>
+<?}
+
+?>
diff --git a/includes/ui.newitems b/includes/ui.newitems
new file mode 100644
index 0000000..70ba507
--- /dev/null
+++ b/includes/ui.newitems
@@ -0,0 +1,66 @@
+<?
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2003 Tim Yamin < plasmaroo > *
+ * < plasmaroo@plasmaroo.squirrelserver.co.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once 'io.filegrepper';
+require_once 'xml.glsaparser';
+
+function recentItems_invoke($showAnnouncements) {
+ global $recentCounter;
+ $recentItems = fileGrepper_parseTree();
+
+ if($showAnnouncements)
+ {
+ echo '<table width="100%" border="0" cellspacing="0" cellpadding="4" class="body_rootTable">';
+ for ($i = 0; $i < 4 && $i < count($recentItems); $i++)
+ {
+ echo '<tr><td width="175">';
+ if($i == 0) echo '<b>Latest announcements:</b>';
+
+ echo '</td><td align="center" width="135"><a href="frame-view.php?id='.$recentItems[$i][0].'">GLSA '.$recentItems[$i][0].'</a></td>';
+ echo '<td>'.date('Y-m-d H:i:s', $recentItems[$i][1]);
+ if(sscanf($recentItems[$i][0], '%04d%02d-%d', $ID1, $ID2, $ID3) == 3)
+ {
+ $Parser = new GLSAParser();
+ $Parser->GLSAParse(fileGrepper_getGLSAText($ID1, $ID2, $ID3), true);
+ echo ' :: <i>'.$Parser->GLSAShortSummary.'...</i>';
+ }
+ echo '</td></tr>';
+ }
+ echo '</table>';
+ }
+
+ $recentCounter = 0;
+ for ($i = 0; $i < count($recentItems); $i++) {
+
+ // If not older than one week ...
+ if(time() - $recentItems[$i][1] <= 604800) $recentCounter++;
+
+ }
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/includes/xml.glsaparser b/includes/xml.glsaparser
new file mode 100644
index 0000000..acae724
--- /dev/null
+++ b/includes/xml.glsaparser
@@ -0,0 +1,1228 @@
+<?php
+
+/***************************************************************************
+ * *
+ * Copyright (C) 2004 Tim Yamin < plasmaroo@gentoo.org > *
+ * < plasm@roo.me.uk > *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ * *
+ ***************************************************************************/
+
+require_once 'io.filegrepper';
+
+function reformatString($string, $XMLRewrap = false, $wrapTo = 71)
+{
+ $newLine = "\n";
+ if($XMLRewrap != 1)
+ {
+ // We should only really decode if we're processing into text; otherwise don't to preserve
+ // quoting inside tag attributes...
+ $string = html_entity_decode($string);
+
+ // Remove whitespace between tags
+ $string = preg_replace(array('/\s*<p>\s*/', '/\s*<\/p>\s*</'), array('<p>', '</p><'), $string);
+
+ // Remove the first <P>
+ $string = preg_replace('/<p>/', '', $string, 1);
+
+ // Give lists some space
+ $string = str_replace('</p><ol>', '</p><br><ol>', $string);
+ $string = str_replace('</p><ul>', '</p><br><ul>', $string);
+
+ // Parse <OL> or <UL> tags
+ if($XMLRewrap != 2)
+ {
+ preg_match_all('/(<ol>.*?<\/ol>|<ul>.*?<\/ul>)/s', $string, $OLs, PREG_SET_ORDER);
+ foreach($OLs as $OL)
+ {
+ if(substr_count($OL[0], '<ul>') > 0)
+ {
+ $padLength = 2;
+ $nS = '* ';
+ $OL[0] = preg_replace('/ *<ul> */', '', $OL[0]);
+ $OL[0] = str_replace('</ul>', '', $OL[0]);
+ }
+ else
+ {
+ $padLength = strlen(substr_count($OL[0], '<li>'))+2;
+ $nS = '$i. ';
+ $OL[0] = preg_replace('/ *<ol> */', '', $OL[0]);
+ $OL[0] = str_replace('</ol>', '', $OL[0]);
+ }
+
+ // Parse each <LI> tag
+ $LIs = explode('<li>', $OL[0]);
+
+ $LICounter = 1;
+ $ListOutput = '';
+
+ // Count average length
+ $avgItemLength = -1;
+ if(count($LIs) >= 10)
+ {
+ $LICounter = array();
+ foreach($LIs as $LI)
+ {
+ $LICounter[] = strlen($LI);
+ }
+
+ $avgItemLength = max($LICounter);
+ }
+
+ foreach($LIs as $LI)
+ {
+ if(trim($LI) != '')
+ {
+ if($LICounter == 1)
+ $ListOutput .= $newLine;
+
+ // This is where we do the decision for adding \n or not
+ if($avgItemLength < 0 || $avgItemLength > $wrapTo-$padLength)
+ $ListOutput .= $newLine;
+
+ $ListOutput .= padWordWrap(str_replace('$i', $LICounter++, $nS).trim(str_replace('</li>', '', $LI)), str_repeat(' ', $padLength), $wrapTo-$padLength).$newLine;
+ }
+ }
+
+ if($nS != '* ')
+ $string = preg_replace('/<ol>.*?<\/ol>/s', $ListOutput, $string, 1);
+ else
+ $string = preg_replace('/<ul>.*?<\/ul>/s', $ListOutput, $string, 1);
+ }
+ }
+ // Replace each <P> with a "\n\n" and remove </P>'s
+ $string = str_replace('<br>', $newLine.$newLine, $string);
+ $string = str_replace('<p>', $newLine.$newLine, $string);
+
+ // Remove unneeded tags
+ if($XMLRewrap != 2)
+ $string = preg_replace(array('/<b>(.*?)<\/b>/s', '/<uri link=(\'|")(.*?)(\'|")>.*?<\/uri>/s', '/<\/?(i|e|p|u|uri).*?>/s'), array('*\1*', '\2', ''), $string);
+ else
+ $string = str_replace('</p>', '', $string);
+ }
+ $string = preg_replace(array('/<li> /s', '/ +<\/li>/s'), array('<li>'.$newLine, $newLine.'</li>'), $string);
+
+ // Split up the <pre>'s; wordwrap non-pre elements
+ $StringArray = preg_split('/(<\/?code>)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+ foreach ($StringArray as $Key => $String)
+ {
+ // Since we recode entities be sure to decode first...
+ $String = html_entity_decode($String);
+
+ if(!preg_match('/<\/?code.*>/', $String))
+ {
+ if(preg_match('/<code.*?>/', $StringArray[$Key-1]) && !$XMLRewrap)
+ $OString .= $newLine.preg_replace('/^(\S.*)$/m', ' \1', $String);
+ else if(preg_match('/<code.*?>/', $StringArray[$Key-1]) && $XMLRewrap)
+ $OString .= $newLine.'<code>'.htmlspecialchars(preg_replace('/^ /m', '', $String)).'</code>';
+ else if($wrapTo > 0)
+ {
+ if($XMLRewrap)
+ {
+ $String = str_replace(array('<p> ', ' </p>'), array('<p>'.$newLine, $newLine.'</p>'), $String);
+ $OString .= str_replace('&quot;', '"', wordwrap($String, $wrapTo, $newLine, true));
+ }
+ else
+ $OString .= wordwrap($String, $wrapTo, $newLine, true);
+ }
+ else
+ {
+ if($XMLRewrap)
+ $OString .= str_replace('&quot;', '"', $String);
+ else
+ $OString .= $String;
+ }
+ }
+ }
+
+ if(!$XMLRewrap)
+ {
+ // Remove excessive newlines
+ $OString = preg_replace('/'.$newLine.$newLine.$newLine.'+/s', $newLine.$newLine, $OString);
+
+ // Trim and return
+ return trim($OString, ' '.$newLine);
+ }
+
+ // We're doing an XML rewrap; untangle things otherwise and return.
+ return trim(preg_replace(array('/ *<p> *(\w+)/', '/ *<\/p> */', '/(<\/\w+>)\s*(<\/?\w+>)/', '/(<\w+>)\s+(<\w+>)/', '/(.+)<\/p>/', '/<uri link=&quot;(.+)&quot;>/s', '/<\/(.+)><p>/s'), array('<p>'.$newLine.'\1', '</p>', '\1'.$newLine.'\2', '\1'.$newLine.'\2', '\1'.$newLine.'</p>', '<uri link="\1">', '</\1>'.$newLine.'<p>'), $OString));
+}
+
+/*! Replace excessive whitespace with one whitespace and trim the
+ result.
+*/
+
+function npadOutput($string)
+{
+ return trim(preg_replace('/ +/', ' ', $string), "\n");
+}
+
+/*! Perform newline formatting on a string - the string is trimmed
+ and newlines are replaced with a newline and the requested padding
+ string.
+
+ @param string The input string.
+ @param pad String to pad on at newlines.
+ @param length Line length to wrap to.
+ @return The processed input string.
+
+*/
+
+function padWordWrap($string, $pad = '', $length = 75)
+{
+ $newLine = "\n";
+ $string = wordwrap($string, $length, $newLine, true);
+ $string = str_replace($newLine, $newLine.$pad, $string);
+ $string = preg_replace('/'.$newLine.$pad."(\r?)\n/", $newLine.$newLine, $string);
+ return $string;
+}
+
+function padXML($string, $pad = '')
+{
+ $newLine = "\n";
+ return str_replace($newLine, $newLine.$pad, $string);
+}
+
+/*! Turn double newlines [ = one blank line ] to separate paragraph
+ structures, but preserve other structures such as "<code>" tags. */
+
+function paragraphifyFromPlain($string)
+{
+ /* Split up code tags */
+ $StringArray = preg_split('/(<\/?code>|<\/?ol>|<\/?ul>)/', $string, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+ $fn = '';
+ $newLine = "\n";
+
+ foreach ($StringArray as $Key => $String)
+ {
+ if(!preg_match('/<\/?(code|ol|ul)>/', $String))
+ {
+ if($StringArray[$Key-1] == '<code>')
+ $OString .= $newLine.'<code>'.htmlspecialchars(rtrim($String)).'</code>';
+ else if($StringArray[$Key-1] == '<ol>')
+ $OString .= $newLine.'<ol>'.rtrim($String).'</ol>';
+ else if($StringArray[$Key-1] == '<ul>')
+ $OString .= $newLine.'<ul>'.rtrim($String).'</ul>';
+ else
+ {
+ $String = preg_replace("/(\r\n|\n|\r)/", $newLine, $String);
+ $Paragraphs = preg_split('/'.$newLine.$newLine.'+/', htmlspecialchars($String, ENT_NOQUOTES), -1, PREG_SPLIT_NO_EMPTY);
+ foreach ($Paragraphs as $Paragraph)
+ {
+ $OString .= $fn.'<p>'.$newLine.trim(npadOutput($Paragraph)).$newLine.'</p>'; $fn = $newLine;
+ }
+ }
+ }
+ }
+
+ return preg_replace(array('/&lt;(\/?(b|e|i|u|uri).*?)&gt;/s', '/<uri link=&quot;(.+)&quot;>/s'), array('<\1>', '<uri link="\1">'), $OString);
+}
+
+/*! Strip un-necessary '<P>' tags to preserve layout structures - this
+ function first checks whether there is only one <P> tag - if so, it
+ is stripped and the string is returned. If the <P> tag is not at the
+ end, it is replaced by <br> and the opening <P> tag is removed.
+
+ If a </P> tag is found to be remaining on the end of the string, it is
+ removed along with the corresponding <P> tag.
+
+ @param string The input string.
+ @return The processed input string.
+
+*/
+
+function stripParagraphTags($string)
+{
+ if(strpos(ltrim($string), '<p>') == 0 || (substr_count($string, '<p>') == 1))
+ {
+ $string = preg_replace('/<p>(.+)<\/p>\s*<pre/', '\1<br><pre', $string);
+ if(substr_count($string, '<p>') > 0)
+ $string = substr_replace($string, '', strpos($string, '<p>'), 3);
+ $string = preg_replace('/<\/p>\s*<p>(.+)<\/p>\s*<pre/', '</p><br><br>\1<pre', $string, 1);
+ if(substr_count($string, '</p>') == 2)
+ {
+ $string = substr_replace($string, '<br><br>', strpos($string, '</p>'), 4);
+ }
+ else if(substr_count($string, '</p>') > 2)
+ {
+ $string = substr_replace($string, '<br>', strpos($string, '</p>'), 4);
+ }
+ else if(substr_count($string, '<p>') == 0 && (substr_count($string, '<ol>') > 0))
+ {
+ $string = substr_replace($string, '<br><br>', strpos($string, '</p>'), 4);
+ }
+ else if(substr_count($string, '</p>') > 0)
+ {
+ $string = substr_replace($string, '', strpos($string, '</p>'), 4);
+ }
+ }
+ if(strrpos(rtrim($string), '</p>') == strlen(rtrim($string))-4)
+ {
+ $pos = strlen($string) - (strpos(strrev($string), strrev('<p>')) + strlen('<p>'));
+ $string = substr_replace($string, '', $pos, 3);
+ $pos = strlen($string) - (strpos(strrev($string), strrev('</p>')) + strlen('</p>'));
+ $string = substr_replace($string, '', $pos, 4);
+ }
+ $string = str_replace('</ol>', '</ol><br>', $string);
+
+ // >> PHP5. This will match the whole string and return -8.
+ // Change as needed.
+
+ if(strrpos(rtrim($string), '</ol><br>') == strlen(rtrim($string))-4)
+ $string = substr_replace($string, '', strlen(rtrim($string))-4, 4);
+ return $string;
+}
+
+function rangeInt($int, $range)
+{
+ if($int >= $range)
+ return $int;
+ return $range;
+}
+
+function padLinks($string)
+{
+ return preg_replace("/(?<!<uri link=\")((http|ftp)+(s)?:\/\/[^,<>\s]+)/i", "<uri link=\"\\0\">\\0</uri>", $string);
+}
+
+function padHTML($string, $spell = false, $speller = false)
+{
+ $string = __spell__($string, $spell, &$speller);
+
+ $string = str_replace('<code>', '<pre style="text-align: left;">', $string);
+ $string = str_replace('</code>', '</pre>', $string);
+
+ $string = str_replace('<uri link', '<a href', $string);
+ $string = str_replace('</uri>', '</a>', $string);
+
+ $string = str_replace('<i>', '<font color="blue">', $string);
+ $string = str_replace('</i>', '</font>', $string);
+
+ $string = str_replace('<e>', '<i>', $string);
+ $string = str_replace('</e>', '</i>', $string);
+
+ return $string;
+}
+
+/*! Generate a RFC 2822 timestamp for use in metadata...
+
+ @return The timestamp.
+
+*/
+
+function genMetadataTimestamp()
+{
+ return date('r');
+}
+
+/*! Turn 'yes' or 'no' into binary 1 and 0; and vice-versa.
+
+ @param input The input data.
+ @return The result.
+
+*/
+function SBMod($input)
+{
+ switch($input)
+ {
+ case 0:
+ return 'no';
+ case 1:
+ return 'yes';
+ case 'no':
+ return 0;
+ case 'yes':
+ return 1;
+ }
+}
+
+/*! This function turns a version expression, such as one
+ which is used in GLSAs into a human-readable set of
+ operators.
+
+ @param VXP The input expression.
+ @return A human readable version of the VXP.
+
+*/
+function VXPToText($VXP)
+{
+ switch( $VXP )
+ {
+ case 'eq':
+ return '==';
+ case 'ge':
+ return '&gt;=';
+ case 'le':
+ return '&lt;=';
+ case 'gt':
+ return '&gt;';
+ case 'lt':
+ return '&lt;';
+ case 'rlt':
+ return '*&lt;';
+ case 'rle':
+ return '*&lt;=';
+ case 'rgt':
+ return '*&gt;';
+ case 'rge':
+ return '*&gt;=';
+ }
+}
+
+class GLSAParser {
+
+ var $XMLParser;
+
+ var $GLSAID;
+ var $GLSASeverity;
+
+ var $GLSAShortSummary;
+ var $GLSASynopsis;
+ var $GLSADate;
+
+ var $GLSABugs = array();
+ var $GLSAMetadata = array();
+ var $GLSAPackages = array();
+ var $GLSAReferences = array();
+
+ var $GLSAAccess;
+ var $GLSARevision;
+
+ var $GLSABackground = '';
+ var $GLSADescription = '';
+ var $GLSAImpact = '';
+
+ var $GLSAProduct = '';
+ var $GLSAProductType = '';
+
+ var $GLSAResolution = '';
+ var $GLSAWorkaround = '';
+
+ var $__inMetadata__;
+ var $__inMetadataCounter__ = -1;
+ var $__inMetadataNRST__;
+ var $__inMetadataParentArray__;
+
+ var $__inPackage__;
+ var $__inPreformatted__;
+ var $__inReferences__;
+ var $__inVersion__;
+
+ var $__tagMetadata__;
+ var $__tagMinimal__;
+
+ var $__boolNewline__;
+
+ function GLSAParse($GLSAData, $tagMinimal = false, $tagAddMetadata = false)
+ {
+ $this->XMLParser = xml_parser_create('UTF-8');
+
+ xml_set_object($this->XMLParser, $this);
+ xml_set_character_data_handler($this->XMLParser, '__readContent__');
+ xml_set_element_handler($this->XMLParser, '__tagOpen__', '__tagClose__');
+ xml_parser_set_option($this->XMLParser, XML_OPTION_CASE_FOLDING, false);
+
+ if($tagMinimal)
+ $this->__tagMetadata__ = $tagAddMetadata;
+ else
+ $this->__tagMetadata__ = true;
+ $this->__tagMinimal__ = $tagMinimal;
+
+ xml_parse($this->XMLParser, $GLSAData, true);
+ xml_parser_free($this->XMLParser);
+
+ $this->GLSAAccess = rtrim($this->GLSAAccess);
+ $this->GLSADate = rtrim($this->GLSADate);
+ $this->GLSAProduct = rtrim($this->GLSAProduct);
+ $this->GLSARevision = rtrim($this->GLSARevision);
+ $this->GLSAShortSummary = rtrim(htmlspecialchars($this->GLSAShortSummary, ENT_NOQUOTES));
+ $this->GLSASynopsis = trim(htmlspecialchars($this->GLSASynopsis, ENT_NOQUOTES));
+ }
+
+ function __iterateMetadata__ (&$MetadataSet, $iLevel)
+ {
+ $newLine = "\n";
+ foreach($MetadataSet as $GLSAMetadataItem)
+ {
+ $string .= str_repeat(' ', $iLevel).'<metadata tag="'.$GLSAMetadataItem['tag'].'"'.rtrim($GLSAMetadataItem['revision'] ? ' revision="'.$GLSAMetadataItem['revision'].'"' : '').rtrim($GLSAMetadataItem['author'] ? ' author="'.$GLSAMetadataItem['author'].'"' : '').rtrim($GLSAMetadataItem['timestamp'] ? ' timestamp="'.$GLSAMetadataItem['timestamp'].'"' : '').rtrim($GLSAMetadataItem['flag'] ? ' flag="'.$GLSAMetadataItem['flag'].'"' : '').'>'.$newLine;
+ if($GLSAMetadataItem['cdata'])
+ $string .= str_repeat(' ', $iLevel+2).htmlspecialchars(ltrim($GLSAMetadataItem['cdata'])).$newLine;
+ if(is_array($GLSAMetadataItem['data']) && count($GLSAMetadataItem['data']) > 0)
+ $string .= $this->__iterateMetadata__(&$GLSAMetadataItem['data'], $iLevel+2);
+ $string .= str_repeat(' ', $iLevel).'</metadata>'.$newLine;
+ }
+ return $string;
+ }
+
+ function __readContent__($parser, $data)
+ {
+ if($this->GLSADate && $this->GLSAShortSummary && $this->__tagMinimal__ && !$this->__tagMetadata__)
+ return;
+ if (isset($this->__inPreformatted__))
+ {
+ if($this->__inTag__ != 'code')
+ {
+ if($this->__inTag__ != 'uri')
+ {
+ $data = padLinks($data);
+ $this->__inPreformatted__ .= npadOutput(htmlspecialchars($data), ENT_NOQUOTES);
+ } else
+ {
+ // Strip XML newline indentation whitespace
+ $this->__inPreformatted__ .= npadOutput(preg_replace('/^ +/', '', htmlspecialchars($data)), ENT_NOQUOTES);
+ }
+ }
+ else
+ {
+ /* rtrim is broken; it strips spaces before the end of the string for some reason... */
+ /* Using workaround... */
+ $this->__inPreformatted__ .= $data;
+ }
+ }
+ elseif (isset($this->__inReferences__))
+ {
+ $key = end(array_keys($this->GLSAReferences));
+ if($key != '0') $this->GLSAReferences[$key] .= rtrim($data);
+ }
+ else
+ {
+ switch ($this->__inTag__)
+ {
+ case 'access':
+ $this->GLSAAccess .= npadOutput($data);
+ break;
+ case 'announced':
+ $this->GLSADate .= npadOutput($data);
+ break;
+ case 'bug':
+ if(trim($data) != '') $this->GLSABugs[] = trim($data);
+ break;
+ case 'product':
+ $this->GLSAProduct .= npadOutput($data);
+ break;
+ case 'metadata':
+ if(!is_array($this->__inMetadata__) || trim($data) == '')
+ break;
+ $this->__inMetadata__['cdata'] .= $data;
+ break;
+ case 'revised':
+ $this->GLSARevision .= npadOutput($data);
+ break;
+ case 'summary':
+ $this->GLSAShortSummary .= npadOutput($data);
+ break;
+ case 'synopsis':
+ $this->GLSASynopsis .= npadOutput($data);
+ break;
+ case 'title':
+ $this->GLSAShortSummary .= npadOutput($data);
+ break;
+ case 'unaffected':
+ if(trim($data) != '') $this->GLSAPackages[count($this->GLSAPackages)-1]['unaffected'][] = array($this->__inVersion__ => $data);
+ break;
+ case 'vulnerable':
+ if(trim($data) != '') $this->GLSAPackages[count($this->GLSAPackages)-1]['vulnerable'][] = array($this->__inVersion__ => $data);
+ break;
+ }
+ }
+ }
+
+ function __tagOpen__($parser, $tag, $attributes)
+ {
+ $this->__inTagPrevious__ = $this->__inTag__;
+ $this->__inTag__ = $tag;
+
+ if($this->GLSADate && $this->GLSAShortSummary && $this->__tagMinimal__ && !$this->__tagMetadata__
+ || ($this->__tagMinimal__ && $this->__tagMetadata__ && $tag != 'metadata'))
+ return;
+
+ if(isset($this->__inPreformatted__))
+ {
+ if($tag == 'uri')
+ {
+ $this->__inPreformatted__ .= '<uri link="'.htmlspecialchars($attributes['link'], ENT_NOQUOTES).'">';
+ }
+ else
+ $this->__inPreformatted__ .= '<'.$tag.'>';
+ return;
+ }
+
+ switch($tag)
+ {
+ case 'affected':
+ $this->__inVersion__ = &$this->GLSAAffected;
+ break;
+ case 'background':
+ $this->__inPreformatted__ = &$this->GLSABackground;
+ break;
+ case 'description':
+ $this->__inPreformatted__ = &$this->GLSADescription;
+ break;
+ case 'impact':
+ $this->GLSASeverity = $attributes['type'];
+ $this->__inPreformatted__ = &$this->GLSAImpact;
+ break;
+ case 'glsa':
+ if(ereg('^[a-fA-F0-9]{32}$', $attributes['id']))
+ $this->GLSAID = $attributes['id'];
+ else
+ {
+ $__array__ = explode('-', $attributes['id']);
+ $this->GLSAID = sprintf('%04d-%02d', $__array__[0], $__array__[1]);
+ }
+ $this->GLSASeverity = $attributes['severity'];
+ break;
+ case 'metadata':
+ if(isset($this->__inMetadata__) == 0 && $this->__inMetadataCounter__ == -1)
+ {
+ $this->GLSAMetadata[] = array('data' => array(), 'revision' => $attributes['revision'], 'tag' => $attributes['tag'], 'author' => $attributes['author'], 'rootnode' => '', 'timestamp' => $attributes['timestamp'], 'flag' => $attributes['flag']);
+ end($this->GLSAMetadata);
+
+ $this->__inMetadata__ = &$this->GLSAMetadata[key($this->GLSAMetadata)];
+ $this->__inMetadataParentArray__[$this->__inMetadataCounter__++] = &$this->GLSAMetadata[key($this->GLSAMetadata)];
+ }
+ else
+ {
+ $this->__inMetadata__['data'][] = array('data' => array(), 'revision' => $attributes['revision'], 'tag' => $attributes['tag'], 'author' => $attributes['author'], 'timestamp' => $attributes['timestamp'], 'flag' => $attributes['flag']);
+ end($this->__inMetadata__['data']);
+ $this->__inMetadataParentArray__[$this->__inMetadataCounter__] = &$this->__inMetadata__['data'][key($this->__inMetadata__['data'])];
+ $this->__inMetadata__ = &$this->__inMetadata__['data'][key($this->__inMetadata__['data'])];
+ $this->__inMetadataCounter__+=1;
+ }
+ break;
+ case 'package':
+ $this->GLSAPackages[] = array('arch' => $attributes['arch']);
+ $this->__inPackage__ = &$this->GLSAPackages[count($this->GLSAPackages)-1];
+
+ $this->__inPackage__['auto'] = $attributes['auto'];
+ $this->__inPackage__['name'] = $attributes['name'];
+ $this->__inPackage__['unaffected'] = array();
+ $this->__inPackage__['vulnerable'] = array();
+ break;
+ case 'product':
+ $this->GLSAProductType = $attributes['type'];
+ break;
+ case 'resolution':
+ $this->__inPreformatted__ = &$this->GLSAResolution;
+ break;
+ case 'references':
+ $this->__inReferences__ = true;
+ break;
+ case 'workaround':
+ $this->__inPreformatted__ = &$this->GLSAWorkaround;
+ break;
+ case 'unaffected':
+ if($attributes['name'] != '')
+ {
+ array_push($this->GLSAPackages, array('arch' => $this->__inPackage__['arch']));
+ $myPackage = &$this->GLSAPackages[count($this->GLSAPackages)-1];
+
+ $myPackage['auto'] = $this->__inPackage__['auto'];
+ $myPackage['name'] = $attributes['name'];
+ $myPackage['unaffected'] = array();
+ $myPackage['vulnerable'] = array();
+ break;
+ }
+ $this->__inVersion__ = $attributes['range'];
+ break;
+ case 'uri':
+ if($this->__inReferences__)
+ $this->GLSAReferences[$attributes['link']] = '';
+ break;
+ case 'vulnerable':
+ if($attributes['name'] != '')
+ {
+ array_push($this->GLSAPackages, array('arch' => $this->__inPackage__['arch']));
+ $myPackage = &$this->GLSAPackages[count($this->GLSAPackages)-1];
+
+ $myPackage['auto'] = $this->__inPackage__['auto'];
+ $myPackage['name'] = $attributes['name'];
+ $myPackage['unaffected'] = array();
+ $myPackage['vulnerable'] = array();
+ break;
+ }
+ $this->__inVersion__ = $attributes['range'];
+ break;
+ }
+ }
+
+ /*! This function is called internally by the PHP XML Parser. It
+ processes the given tag and if any actions are required for that
+ tag, they are applied.
+ */
+ function __tagClose__($parser, $tag)
+ {
+ if($tag == 'metadata')
+ {
+ if(isset($this->__inMetadata__['rootnode']) && !$this->__inMetadataNRST__)
+ {
+ $this->__inMetadataCounter__ = -1;
+ unset($this->__inMetadata__);
+ }
+ else
+ {
+ if($this->__inMetadataCounter__ > 1)
+ $this->__inMetadataNRST__ = true;
+ else
+ $this->__inMetadataNRST__ = false;
+ if($this->__inMetadataParentArray__[$this->__inMetadataCounter__-1]['tag'] != '')
+ {
+ $this->__inMetadata__ = &$this->__inMetadataParentArray__[$this->__inMetadataCounter__-2];
+ $this->__inMetadataCounter__--;
+ }
+// echo("-*-".$this->__inMetadataCounter__."@".$this->__inMetadataParentArray__[$this->__inMetadataCounter__]['tag']."--\n");
+ }
+ }
+
+ if($tag == 'references')
+ unset($this->__inReferences__);
+
+ if($tag == 'description' || $tag == 'background' || $tag == 'impact' || $tag == 'workaround' || $tag == 'resolution')
+ unset($this->__inPreformatted__);
+
+ if(isset($this->__inPreformatted__))
+ {
+ $this->__inPreformatted__ .= '</'.$tag.'>';
+ if($tag == 'uri')
+ {
+// $this->__inPreformatted__ .= ' ';
+ if(isset($this->__inTagPrevious__))
+ $this->__inTag__ = $this->__inTagPrevious__;
+ }
+ }
+ }
+
+ function GLSAToXML($reviseDates = true)
+ {
+ $newLine = "\n";
+
+ $string = '<?xml version="1.0" encoding="utf-8"?>'.$newLine;
+ $string .= '<?xml-stylesheet href="/xsl/glsa.xsl" type="text/xsl"?>'.$newLine;
+ $string .= '<?xml-stylesheet href="/xsl/guide.xsl" type="text/xsl"?>'.$newLine;
+ $string .= '<!DOCTYPE glsa SYSTEM "http://www.gentoo.org/dtd/glsa.dtd">'.$newLine;
+
+ $string .= $newLine.'<glsa id="'.$this->GLSAID.'">'.$newLine;
+ $string .= ' <title>'.htmlspecialchars($this->GLSAShortSummary).'</title>'.$newLine;
+
+ if($this->GLSASynopsis)
+ {
+ $string .= ' <synopsis>'.$newLine;
+ $string .= ' '.padWordWrap(htmlspecialchars($this->GLSASynopsis, ENT_NOQUOTES), ' ').$newLine;
+ $string .= ' </synopsis>'.$newLine;
+ } else
+ $string .= ' <synopsis/>'.$newLine;
+
+ $string .= ' <product type="'.$this->GLSAProductType.'">'.$this->GLSAProduct.'</product>'.$newLine;
+ $string .= ' <announced>';
+ $string .= $reviseDates === 'reset' ? date('F d, Y') : $this->GLSADate;
+ $string .= '</announced>'.$newLine;
+ $string .= ' <revised>';
+ if($reviseDates === 'reset')
+ $string .= date('F d, Y').': 01';
+ else if($reviseDates)
+ {
+ $temp = explode(':', $this->GLSARevision);
+ count($temp) < 2 ? $temp = 1 : $temp = $temp[1];
+ $string .= date('F d, Y').': '.padNumber(((int) $temp)+1);
+ }
+ else
+ $string .= $this->GLSARevision;
+ $string .= '</revised>'.$newLine;
+
+ foreach( $this->GLSABugs as $GLSABug ){ if(is_numeric($GLSABug)) { $string .= ' <bug>'.$GLSABug.'</bug>'.$newLine; } }
+ if($this->GLSAAccess)
+ $string .= ' <access>'.htmlspecialchars($this->GLSAAccess).'</access>'.$newLine;
+
+ $string .= ' <affected>'.$newLine;
+ foreach( $this->GLSAPackages as $GLSAPackage )
+ {
+ $string .= ' <package name="'.$GLSAPackage['name'].'" auto="'.$GLSAPackage['auto'].'" arch="'.$GLSAPackage['arch'].'">'.$newLine;
+ if(is_array($GLSAPackage['unaffected']))
+ {
+ foreach( $GLSAPackage['unaffected'] as $GLSAUnaffected )
+ {
+ $string .= ' <unaffected range="'.key($GLSAUnaffected).'">'.$GLSAUnaffected[key($GLSAUnaffected)].'</unaffected>'.$newLine;
+ }
+ }
+ else
+ generateWarning('No unaffected packages found for '.$GLSAPackage['name'].'! If this is intended; ignore this warning...');
+ if(is_array($GLSAPackage['vulnerable']))
+ {
+ foreach( $GLSAPackage['vulnerable'] as $GLSAVulnerable )
+ {
+ $string .= ' <vulnerable range="'.key($GLSAVulnerable).'">'.$GLSAVulnerable[key($GLSAVulnerable)].'</vulnerable>'.$newLine;
+ }
+ }
+ else
+ generateWarning('No unaffected packages found for '.$GLSAPackage['name'].'!');
+ $string .= ' </package>'.$newLine;
+ }
+ $string .= ' </affected>'.$newLine;
+
+ if($this->GLSABackground)
+ {
+ $string .= ' <background>'.$newLine;
+ $string .= padXML(' '.reformatString($this->GLSABackground, true), ' ').$newLine;
+ $string .= ' </background>'.$newLine;
+ }
+
+ if($this->GLSADescription)
+ {
+ $string .= ' <description>'.$newLine;
+ $string .= padXML(' '.reformatString($this->GLSADescription, true), ' ').$newLine;
+ $string .= ' </description>'.$newLine;
+ } else
+ $string .= ' <description/>'.$newLine;
+
+ if($this->GLSAImpact)
+ {
+ $string .= ' <impact type="'.$this->GLSASeverity.'">'.$newLine;
+ $string .= padXML(' '.reformatString($this->GLSAImpact, true), ' ').$newLine;
+ $string .= ' </impact>'.$newLine;
+ } else
+ $string .= ' <impact type="'.$this->GLSASeverity.'"/>'.$newLine;
+
+ if($this->GLSAWorkaround)
+ {
+ $string .= ' <workaround>'.$newLine;
+ $string .= padXML(' '.reformatString($this->GLSAWorkaround, true), ' ').$newLine;
+ $string .= ' </workaround>'.$newLine;
+ } else
+ $string .= ' <workaround/>'.$newLine;
+
+ if($this->GLSAResolution)
+ {
+ $string .= ' <resolution>'.$newLine;
+ $string .= padXML(' '.reformatString($this->GLSAResolution, true), ' ').$newLine;
+ $string .= ' </resolution>'.$newLine;
+ } else
+ $string .= ' <resolution/>'.$newLine;
+
+ if(count($this->GLSAReferences) > 0)
+ {
+ $string .= ' <references>'.$newLine;
+ foreach( $this->GLSAReferences as $GLSARef => $GLSAReference ) { $string .= ' <uri link="'.htmlspecialchars($GLSARef).'">'.htmlspecialchars(trim($GLSAReference)).'</uri>'.$newLine; }
+ $string .= ' </references>'.$newLine;
+ } else
+ $string .= ' <references/>'.$newLine;
+
+ if(count($this->GLSAMetadata) > 0)
+ $string .= $this->__iterateMetadata__ (&$this->GLSAMetadata, 2);
+
+ $string .= '</glsa>'.$newLine;
+ return str_replace("\r", '', $string);
+ }
+
+ function GLSAToText($noBanner = false, $updateMode = 0)
+ {
+ $newLine = "\n";
+ $wrapLevel = 71; # Wrap at 71 characters
+
+ if(!$noBanner)
+ {
+ if($updateMode == 1)
+ $titleTag = 'UPDATE: ';
+ if($updateMode == 2)
+ $titleTag = 'ERRATA: ';
+
+ $string .= 'To: gentoo-announce@gentoo.org'.$newLine;
+ $string .= 'CC: '.'bugtraq@securityfocus.com, full-disclosure@lists.grok.org.uk, security-alerts@linuxsecurity.com'.$newLine;
+ $string .= 'Subject: '.rtrim(html_entity_decode($titleTag.'[ GLSA '.$this->GLSAID.' ] '.$this->GLSAShortSummary)).$newLine.$newLine;
+ }
+
+ $GLSARevision = explode(':', $this->GLSARevision);
+ if(count($GLSARevision) < 2)
+ {
+ $GLSARevisionC = 1;
+ $GLSARevisionString = ':01';
+ } else {
+ $GLSARevisionString = ':'.str_replace(' ', '', $GLSARevision[1]);
+ $GLSARevisionC = $GLSARevision[1];
+ }
+ if($GLSARevisionString == ':01')
+ $GLSARevisionString = '';
+
+ if($updateMode == 1)
+ $titleTag = ' [UPDATE] ';
+ if($updateMode == 2)
+ $titleTag = ' [ERRATA UPDATE] ';
+
+ $string .= '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'.$newLine;
+ $string .= 'Gentoo Linux Security Advisory'.$titleTag.str_repeat(' ', $wrapLevel-30-strlen($titleTag)-strlen('GLSA '.$this->GLSAID.$GLSARevisionString)).'GLSA '.$this->GLSAID.$GLSARevisionString.$newLine;
+ $string .= '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'.$newLine;
+ $string .= ' http://security.gentoo.org/'.$newLine;
+ $string .= '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'.$newLine;
+ $string .= $newLine;
+ $string .= ' Severity: '.ucfirst($this->GLSASeverity).$newLine;
+ $string .= ' Title: '.rtrim(wordwrap(html_entity_decode($this->GLSAShortSummary).$newLine, $wrapLevel-11, $newLine.' ')).$newLine;
+ $string .= ' Date: '.$this->GLSADate.$newLine;
+ if($GLSARevisionC > 1)
+ $string .= ' Updated: '.$GLSARevision[0].$newLine;
+ if(count($this->GLSABugs) > 0)
+ $string .= ' Bugs: #'.implode(', #', $this->GLSABugs).$newLine;
+ $string .= ' ID: '.$pool.$this->GLSAID.$GLSARevisionString.$newLine;
+ $string .= $newLine;
+ $string .= '- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -'.$newLine;
+ $string .= $newLine;
+ if($updateMode == 1)
+ $string .= 'Update'.$newLine.'======'.$newLine.$newLine.wordwrap('***'.$newLine.'Insert a summary of the update here and remove any sections which have not been changed (keep Availability, Concerns and License in all cases).', $wrapLevel).$newLine.'***'.$newLine.$newLine.'The updated sections appear below.'.$newLine.$newLine;
+ if($updateMode == 2)
+ $string .= 'Errata'.$newLine.'======'.$newLine.$newLine.wordwrap('***'.$newLine.'Insert a summary of the errata changes here and remove any sections which have not been changed (keep Availability, Concerns and License in all cases).', $wrapLevel).$newLine.'***'.$newLine.$newLine.'The corrected sections appear below.'.$newLine.$newLine;
+ $string .= 'Synopsis'.$newLine.'========'.$newLine.$newLine;
+ $string .= wordwrap(html_entity_decode($this->GLSASynopsis), $wrapLevel);
+ $string .= $newLine.$newLine;
+ if($this->GLSABackground != '')
+ {
+ $string .= 'Background'.$newLine.'=========='.$newLine.$newLine;
+ $string .= reformatString($this->GLSABackground, false, $wrapLevel);
+ $string .= $newLine.$newLine;
+ }
+ if(count($this->GLSAPackages) > 0)
+ {
+ $string .= 'Affected packages'.$newLine.'================='.$newLine.$newLine;
+ foreach ($this->GLSAPackages as $Package)
+ {
+ if(strlen($Package['name']) > $longestPackageName)
+ $longestPackageName = strlen($Package['name']);
+ foreach ($Package['vulnerable'] as $VersionArray)
+ {
+ foreach ($VersionArray as $VXP => $Version)
+ {
+ if(strlen(html_entity_decode(VXPToText($VXP)).' '.$Version) > $longestVulnerableVersion)
+ $longestVulnerableVersion = strlen(html_entity_decode(VXPToText($VXP)).' '.$Version);
+ }
+ }
+ if(isset($Package['unaffected']))
+ {
+ foreach ($Package['unaffected'] as $VersionArray)
+ {
+ foreach ($VersionArray as $VXP => $Version)
+ {
+ if(strlen(html_entity_decode(VXPToText($VXP)).' '.$Version) > $longestUnaffectedVersion)
+ $longestUnaffectedVersion = strlen(html_entity_decode(VXPToText($VXP)).' '.$Version);
+ }
+ }
+ }
+ }
+
+ if($longestPackageName < 7)
+ $longestPackageName = 7;
+ if($longestVulnerableVersion < 10)
+ $longestVulnerableVersion = 10;
+ if($longestUnaffectedVersion < 13)
+ $longestUnaffectedVersion = 13;
+
+ $padLevel = 4;
+ $longestPackageName += 2;
+ if($longestPackageName+$longestVulnerableVersion+$longestUnaffectedVersion > $wrapLevel-12 || $this->GLSAProduct == 'Kernel')
+ {
+ $trimPackageNames = true;
+ $longestPackageName = 0;
+ foreach ($this->GLSAPackages as $Package)
+ {
+ $packageName = explode('/', $Package['name']);
+ if(strlen($packageName[1]) > $longestPackageName)
+ $longestPackageName = strlen($packageName[1]) + 2;
+ }
+ }
+
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ if($this->GLSAProduct == 'Kernel')
+ {
+ $longestPackageName += 2;
+ if(floor($longestUnaffectedVersion/2) < ceil($longestUnaffectedVersion/2))
+ $Cstring = ' ';
+
+ $string .= str_repeat(' ', $padLevel).' Kernel '.@str_repeat(' ', $longestPackageName-7).'/'.str_repeat(' ', $pad=ceil((($longestUnaffectedVersion-6)/2))).'Unaffected'.str_repeat(' ', $pad).'/'.str_repeat(' ', $wrapLevel-$padLevel-$longestPackageName-$pad-$pad-21).$Cstring.'Remerge'.$newLine;
+ }
+ else
+ {
+ if(floor($longestUnaffectedVersion/2) < ceil($longestUnaffectedVersion/1.5))
+ $Cstring = ' ';
+
+ $string .= str_repeat(' ', $padLevel).' Package '.@str_repeat(' ', $longestPackageName-8).'/'.str_repeat(' ', $pad=ceil((($longestVulnerableVersion-6)/2))).'Vulnerable'.str_repeat(' ', $pad).'/'.str_repeat(' ', $wrapLevel-$padLevel-$longestPackageName-$pad-$pad-25).$Cstring.'Unaffected'.$newLine;
+ }
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+
+ $i = 1;
+ $PackageAutos = 0;
+ $PackageVulns = 0;
+
+ $PackageLast = '';
+ $PackageSame = true;
+ foreach ($this->GLSAPackages as $Package)
+ {
+ $iSet = false;
+ if($PackageLast != '' && $PackageLast != $Package['name'] && $PackageSame)
+ $PackageSame = false;
+ $PackageLast = $Package['name'];
+ foreach ($Package['vulnerable'] as $VersionArray)
+ {
+ $ii = 1;
+ foreach ($VersionArray as $VXP => $Version)
+ {
+ if($trimPackageNames)
+ {
+ $packageName = explode('/', $Package['name']);
+ $package = $packageName[1];
+ }
+ else
+ $package = &$Package['name'];
+
+ $UnaffectedVersion = @$Package['unaffected'][key($Package['unaffected'])];
+ if(!$UnaffectedVersionVXP = @key($UnaffectedVersion))
+ {
+ $UnaffectedVersionVersion = '';
+ if(count($Package['unaffected']) == 0)
+ generateWarning('No unaffected packages found for '.$package.'! If this is intended; ignore this warning...');
+ }
+ else
+ $UnaffectedVersionVersion = $UnaffectedVersion[$UnaffectedVersionVXP];
+
+ if(!$iSet)
+ {
+ $string .= str_repeat(' ', $padLevel-strlen($i)-1).$i.' ';
+ $iSet = true;
+ } else
+ $string .= str_repeat(' ', $padLevel+1);
+ if($this->GLSAProduct != 'Kernel')
+ {
+ $string .= $package.str_repeat(' ', $longestPackageName-strlen($package)+1);
+ $string .= str_repeat(' ', $pad+5-floor(strlen(html_entity_decode(VXPToText($VXP)).' '.$Version)/2));
+ $string .= html_entity_decode(VXPToText($VXP)).' '.$Version.str_repeat(' ', $pad+5-(ceil(strlen(html_entity_decode(VXPToText($VXP)).' '.$Version)/2)));
+ }
+ else
+ $string .= $package.' '.@str_repeat('.', $longestPackageName-strlen($package)-1);
+
+ if(floor(strlen($Version)/2) > ceil(strlen($Version)/2) && $trimPackageNames)
+ $Ccstring = '';
+ else if($trimPackageNames || isset($Cstring))
+ $Ccstring = ' ';
+
+ if(count($Package['unaffected']) == 0)
+ {
+ $PackageVulns++;
+ if($this->GLSAProduct == 'Kernel')
+ {
+ $string .= str_repeat('.', $pad+6-floor(strlen(' Vulnerable! ')/2)).' Vulnerable! '.str_repeat('.', $pad+5-ceil(strlen(' Vulnerable! ')/2));
+ $string .= str_repeat('.', $wrapLevel-7-$padLevel-$longestPackageName-$longestUnaffectedVersion).$newLine;
+ }
+ else
+ {
+ $string .= @str_repeat(' ', $wrapLevel-$padLevel-$longestPackageName-$pad-$pad-14-strlen('Vulnerable')).'Vulnerable!'.$newLine;
+ }
+ }
+ if(isset($Package['unaffected']))
+ {
+ foreach($Package['unaffected'] as $UnaffectedVersion)
+ {
+ foreach($UnaffectedVersion as $UnaffectedVersionVXP => $UnaffectedVersionVersion)
+ {
+ if($this->GLSAProduct == 'Kernel')
+ {
+ if($ii++ != 1)
+ $string .= @str_repeat(' ', $padLevel+1).@str_repeat('.', $longestPackageName);
+ $string .= @str_repeat('.', $pad+5-floor(strlen(html_entity_decode(VXPToText($UnaffectedVersionVXP)).' '.$UnaffectedVersionVersion)/2));
+ }
+ else
+ {
+ if($ii++ == 1)
+ $string .= @str_repeat(' ', $wrapLevel-$padLevel-$longestPackageName-$pad-$pad-14-strlen(html_entity_decode(VXPToText($UnaffectedVersionVXP)).' '.$UnaffectedVersionVersion));
+ else
+ $string .= @str_repeat(' ', $wrapLevel-2-strlen(html_entity_decode(VXPToText($UnaffectedVersionVXP)).' '.$UnaffectedVersionVersion));
+ }
+ $string .= $Ccstring.html_entity_decode(VXPToText($UnaffectedVersionVXP)).' '.$UnaffectedVersionVersion;
+ if($this->GLSAProduct == 'Kernel')
+ {
+ $string .= ' '.@str_repeat('.', $pad+5-ceil(strlen(html_entity_decode(VXPToText($UnaffectedVersionVXP)).' '.$UnaffectedVersionVersion)/2));
+ $string .= @str_repeat('.', $wrapLevel-12-$padLevel-$longestPackageName-$longestUnaffectedVersion);
+ if($Package['auto'] == 'no')
+ $string .= ' YES';
+ else
+ $string .= '....';
+ }
+ $string .= $newLine;
+ }
+ }
+ }
+ }
+ }
+ if($Package['auto'] == 'no')
+ $PackageAutos++;
+ if($Package['arch'] != '*')
+ $PackageArchs[$i] = array($Package['name'], strtoupper($Package['arch']));
+ else
+ $HaveUniversals++;
+ $i++;
+ }
+ if(isset($PackageArchs))
+ {
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ foreach($PackageArchs as $Item => $Data)
+ {
+ $string .= str_repeat(' ', $padLevel+1);
+ $Data[1] = str_replace(',', ' ', $Data[1]);
+ $Data[1] = str_replace('X86', 'x86', $Data[1]);
+
+ if($PackageSame)
+ $string .= padWordWrap('# Package '.$Item.' only applies to '.preg_replace(array('/ /', '/(.+), (.+)/'), array(', ', '\1 and \2'), $Data[1]).' users.', str_repeat(' ', $padLevel+3), $wrapLevel-$padLevel-2).$newLine;
+ else
+ $string .= padWordWrap('# Package '.$Item.' ['.$Data[0].'] only applies to '.preg_replace(array('/ /', '/(.+), (.+)/'), array(', ', '\1 and \2'), $Data[1]).' users.', str_repeat(' ', $padLevel+3), $wrapLevel-$padLevel-2).$newLine;
+ }
+ if($HaveUniversals)
+ {
+ $string .= $newLine.str_repeat(' ', $padLevel+1);
+ $string .= padWordWrap('NOTE: Any packages listed without architecture tags apply to all architectures...', str_repeat(' ', $padLevel-strlen($i)+8), $wrapLevel-$padLevel-1).$newLine;
+ }
+ }
+ if($PackageVulns > 0)
+ {
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ $string .= str_repeat(' ', $padLevel);
+ if($this->GLSAProduct == 'Kernel')
+ {
+ $string .= ' NOTE: Some kernels are still vulnerable. Users should migrate to'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' another kernel if one is available or seek another'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' solution such as patching their existing kernel.';
+ } else {
+ $string .= ' NOTE: Certain packages are still vulnerable. Users should migrate'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' to another package if one is available or wait for the'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' existing packages to be marked stable by their'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' architecture maintainers.';
+ }
+ $string .= $newLine;
+ if($i < 3 && $PackageAutos > 0)
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ }
+ if($PackageAutos > 0)
+ {
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ $string .= str_repeat(' ', $padLevel);
+ if($this->GLSAProduct == 'Kernel')
+ {
+ $string .= ' NOTE: Packages marked with "Remerge" as "YES" require a re-merge'.$newLine;
+ $string .= str_repeat(' ', $padLevel).' even though Portage does not indicate a newer version!';
+ }
+ else
+ $string .= ' NOTE: Packages marked with asterisks require manual intervention!';
+ $string .= $newLine;
+ if($i < 2)
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ }
+ if($i > 2)
+ {
+ $string .= str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ $string .= str_repeat(' ', $padLevel).' '.($i-1).' affected packages';
+ if($i-1 == $HaveUniversals)
+ $string .= ' on all of their supported architectures.';
+ else if($i-1 == count($PackageArchs))
+ $string .= '; please see the notes above...';
+ else if($i-1 < count($PackageArchs))
+ $string .= '; '.($i-1-count($PackageArchs)).' on all of their supported architectures.';
+ $string .= $newLine.str_repeat(' ', $padLevel).str_repeat('-', $wrapLevel-$padLevel).$newLine;
+ }
+ $string .= $newLine;
+ }
+ $string .= 'Description'.$newLine.'==========='.$newLine.$newLine;
+ $string .= reformatString($this->GLSADescription, false, $wrapLevel);
+ $string .= $newLine.$newLine;
+ $string .= 'Impact'.$newLine.'======'.$newLine.$newLine;
+ $string .= reformatString($this->GLSAImpact, false, $wrapLevel);
+ $string .= $newLine.$newLine;
+ $string .= 'Workaround'.$newLine.'=========='.$newLine.$newLine;
+ $string .= reformatString($this->GLSAWorkaround, false, $wrapLevel);
+ $string .= $newLine.$newLine;
+ $string .= 'Resolution'.$newLine.'=========='.$newLine.$newLine;
+ $string .= reformatString($this->GLSAResolution, false, $wrapLevel);
+ $string .= $newLine.$newLine;
+ if(count($this->GLSAReferences) > 0)
+ {
+ $string .= 'References'.$newLine.'=========='.$newLine.$newLine;
+ $i = 1;
+ foreach ($this->GLSAReferences as $URI => $Text)
+ {
+ $string .= ' [ '.$i.' ] '.rtrim($Text).$newLine;
+ $string .= ' '.str_repeat(' ', strlen($i)).' '.$URI.$newLine;
+ $i++;
+ }
+ $string .= $newLine;
+ }
+ if(strlen($this->GLSAID) == 9) // YYYYMM-NN
+ {
+ $string .= 'Availability'.$newLine.'============'.$newLine.$newLine;
+ $string .= 'This GLSA and any updates to it are available for viewing at'.$newLine;
+ $string .= 'the Gentoo Security Website:'.$newLine.$newLine.' http://security.gentoo.org/glsa/glsa-'.$this->GLSAID.'.xml'.$newLine.$newLine;
+ }
+ $string .= 'Concerns?'.$newLine.'========='.$newLine.$newLine;
+ $string .= 'Security is a primary focus of Gentoo Linux and ensuring the'.$newLine;
+ $string .= 'confidentiality and security of our users machines is of utmost'.$newLine;
+ $string .= 'importance to us. Any security concerns should be addressed to'.$newLine;
+ $string .= 'security@gentoo.org or alternatively, you may file a bug at'.$newLine;
+ $string .= 'http://bugs.gentoo.org.'.$newLine;
+ $string .= $newLine.'License'.$newLine.'======='.$newLine.$newLine.'Copyright '.date('Y').' Gentoo Foundation, Inc; referenced text'.$newLine.'belongs to its owner(s).'.$newLine.$newLine.'The contents of this document are licensed under the'.$newLine.'Creative Commons - Attribution / Share Alike license.'.$newLine.$newLine.'http://creativecommons.org/licenses/by-sa/2.5'.$newLine;
+
+ return $string;
+ }
+
+ /*! This function performs the logical search comparison
+ for the metadata searcher.
+
+ @param needle The expression to look for.
+ @param haystack The expression to look in.
+ @return A boolean result.
+
+ */
+ function __searchMetadata__matchCriteria($needle, $haystack)
+ {
+ if($needle === $haystack || $needle === NULL)
+ return true;
+ return false;
+ }
+
+ function &searchMetadata(&$MetadataSet, $maxIteration = NULL, $matchTag = NULL,
+ $matchAuthor = NULL, $matchRevision = NULL, $matchCData = NULL,
+ $matchData = NULL, $iterationLevel = 0)
+ {
+ // We have to use a classic $i iteration here since foreach doesn't return
+ // pointers...
+ for ($i = 0; is_array($MetadataSet[$i]); $i++)
+ {
+ if($this->__searchMetadata__matchCriteria($matchTag, $MetadataSet[$i]['tag'])
+ && $this->__searchMetadata__matchCriteria($matchAuthor, $MetadataSet[$i]['author'])
+ && $this->__searchMetadata__matchCriteria($matchRevision, $MetadataSet[$i]['revision'])
+ && $this->__searchMetadata__matchCriteria($matchCData, $MetadataSet[$i]['cdata'])
+ && $this->__searchMetadata__matchCriteria($matchData, $MetadataSet[$i]['data']))
+ return $MetadataSet[$i];
+ if(is_array($MetadataSet[$i]['data']) && count($MetadataSet[$i]['data']) > 0
+ && $maxIteration > $iterationLevel)
+ {
+ if(is_array($result = $this->searchMetadata($MetadataSet[$i], $maxIteration,
+ $matchTag, $matchAuthor, $matchRevision, $matchCData,
+ $matchData, $iterationLevel+1)))
+ return $result;
+ }
+ }
+ return false;
+ }
+}
+
+// Local Variables: ***
+// truncate-lines:true ***
+// End: ***
+
+?>
diff --git a/index.php b/index.php
new file mode 120000
index 0000000..cfb95d1
--- /dev/null
+++ b/index.php
@@ -0,0 +1 @@
+frame-recent.php \ No newline at end of file
diff --git a/spell.data b/spell.data
new file mode 100644
index 0000000..2ea732f
--- /dev/null
+++ b/spell.data
@@ -0,0 +1,8 @@
+personal_ws-1.1 en 2
+GLSA
+Gentoo
+gentoo
+cvename
+creativecommons
+playlist
+uninstall