mirror of
https://github.com/pfsense/pfsense.git
synced 2025-10-26 11:38:35 +00:00
Sync my recent stuff to HEAD. Easy Rule, Log parsing tweaks, Firewall Log filter, Firewall Log Summary graphs. Also add ProtoChart.
This commit is contained in:
parent
a87afa7cb8
commit
c0b6fddeec
@ -5,7 +5,8 @@
|
||||
part of pfSesne by Scott Ullrich
|
||||
originally based on m0n0wall (http://m0n0.ch/wall)
|
||||
|
||||
Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
|
||||
Copyright (C) 2003-2009 Manuel Kasper <mk@neon1.net>,
|
||||
Jim Pingle <myfirstname>@<mylastname>.org
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
@ -51,13 +52,34 @@ if($_GET['getrulenum'] or $_POST['getrulenum']) {
|
||||
exit;
|
||||
}
|
||||
|
||||
if($_GET['dnsip'] or $_POST['dnsip']) {
|
||||
if($_GET['dnsip'])
|
||||
$dnsip = $_GET['dnsip'];
|
||||
if($_POST['dnsip'])
|
||||
$dnsip = $_POST['dnsip'];
|
||||
$host = get_reverse_dns($dnsip);
|
||||
if ($host == $ip) {
|
||||
$host = "No PTR Record";
|
||||
}
|
||||
echo "IP: {$dnsip}\nHost: {$host}";
|
||||
exit;
|
||||
}
|
||||
|
||||
$filtertext = "";
|
||||
if($_GET['filtertext'] or $_POST['filtertext']) {
|
||||
if($_GET['filtertext'])
|
||||
$filtertext = $_GET['filtertext'];
|
||||
if($_POST['filtertext'])
|
||||
$filtertext = $_POST['filtertext'];
|
||||
}
|
||||
|
||||
$filter_logfile = "{$g['varlog_path']}/filter.log";
|
||||
|
||||
$nentries = $config['syslog']['nentries'];
|
||||
if (!$nentries)
|
||||
$nentries = 50;
|
||||
|
||||
if ($_POST['clear'])
|
||||
if ($_POST['clear'])
|
||||
clear_log_file($filter_logfile);
|
||||
|
||||
$pgtitle = array("Status","System logs","Firewall");
|
||||
@ -88,12 +110,20 @@ include("head.inc");
|
||||
<td>
|
||||
<div id="mainarea">
|
||||
<table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0">
|
||||
<tr><td colspan="6" align="left">
|
||||
Normal View | <a href="diag_logs_filter_dynamic.php">Dynamic View</a> | <a href="diag_logs_filter_summary.php">Summary View</a><br/><br/>
|
||||
</td></tr>
|
||||
<?php if (!isset($config['syslog']['rawfilter'])):
|
||||
$filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100);
|
||||
$filterlog = conv_log_filter($filter_logfile, $nentries, $nentries + 100, $filtertext);
|
||||
?>
|
||||
<tr>
|
||||
<td colspan="6" class="listtopic">
|
||||
Last <?php echo $nentries;?> firewall log entries (<a href='/diag_logs_filter_dynamic.php'>switch</a> to dynamic view)</td>
|
||||
<?php if (!$filtertext) { ?>
|
||||
Last <?php echo count($filterlog);?> firewall log entries.
|
||||
<?php } else { ?>
|
||||
<?php echo count($filterlog);?> matched log entries.
|
||||
<?php } ?>
|
||||
(Max <?php echo $nentries;?>)
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="10%" class="listhdrr">Act</td>
|
||||
@ -111,8 +141,23 @@ include("head.inc");
|
||||
<?php if ($filterent['count']) echo $filterent['count'];?></td>
|
||||
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['time']);?></td>
|
||||
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['interface']);?></td>
|
||||
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['src']);?></td>
|
||||
<td class="listr" nowrap><?php echo htmlspecialchars($filterent['dst']);?></td>
|
||||
<?php
|
||||
$int = strtolower($filterent['interface']);
|
||||
$proto = strtolower($filterent['proto']);
|
||||
|
||||
$srcstr = $filterent['srcip'] . get_port_with_service($filterent['srcport'], $proto);
|
||||
$dststr = $filterent['dstip'] . get_port_with_service($filterent['dstport'], $proto);
|
||||
?>
|
||||
<td class="listr" nowrap>
|
||||
<a href="diag_dns.php?host=<?php echo $filterent['srcip']; ?>" title="Reverse Resolve with DNS"><img border="0" src="/themes/nervecenter/images/icons/icon_log.gif"></a>
|
||||
<a href="easyrule.php?<?php echo "action=block&int={$int}&src={$filterent['srcip']}"; ?>" title="Easy Rule: Add to Block List" onclick="return confirm('Do you really want to add this BLOCK rule?\n\nEasy Rule is still experimental.\nContinue at risk of your own peril.\nBackups are also nice.')"><img border="0" src="/themes/nervecenter/images/icons/icon_block_add.gif"></a>
|
||||
<?php echo $srcstr;?>
|
||||
</td>
|
||||
<td class="listr" nowrap>
|
||||
<a href="diag_dns.php?host=<?php echo $filterent['dstip']; ?>" title="Reverse Resolve with DNS"><img border="0" src="/themes/nervecenter/images/icons/icon_log.gif"></a>
|
||||
<a href="easyrule.php?<?php echo "action=pass&int={$int}&proto={$proto}&src={$filterent['srcip']}&dst={$filterent['dstip']}&dstport={$filterent['dstport']}"; ?>" title="Easy Rule: Pass this traffic" onclick="return confirm('Do you really want to add this PASS rule?\n\nEasy Rule is still experimental.\nContinue at risk of your own peril.\nBackups are also nice.')"><img border="0" src="/themes/nervecenter/images/icons/icon_pass_add.gif"></a>
|
||||
<?php echo $dststr;?>
|
||||
</td>
|
||||
<?php
|
||||
if ($filterent['proto'] == "TCP")
|
||||
$filterent['proto'] .= ":{$filterent['tcpflags']}";
|
||||
@ -124,11 +169,27 @@ include("head.inc");
|
||||
<td colspan="2" class="listtopic">
|
||||
Last <?php echo $nentries;?> firewall log entries</td>
|
||||
</tr>
|
||||
<?php dump_clog($filter_logfile, $nentries); ?>
|
||||
<?php
|
||||
if($filtertext)
|
||||
dump_clog($filter_logfile, $nentries, true, array("$filtertext"));
|
||||
else
|
||||
dump_clog($filter_logfile, $nentries);
|
||||
?>
|
||||
<?php endif; ?>
|
||||
<tr><td><br /><form action="diag_logs_filter.php" method="post">
|
||||
<input name="clear" type="submit" class="formbtn" value="Clear log" /></td></tr>
|
||||
</form>
|
||||
<tr>
|
||||
<td align="left" valign="top" colspan="3">
|
||||
<form id="clearform" name="clearform" action="diag_logs_filter.php" method="post" style="margin-top: 14px;">
|
||||
<input id="submit" name="clear" type="submit" class="formbtn" value="<?=gettext("Clear log");?>" />
|
||||
</form>
|
||||
</td>
|
||||
<td align="right" valign="top" colspan="3">
|
||||
<form id="filterform" name="filterform" action="diag_logs_filter.php" method="post" style="margin-top: 14px;">
|
||||
<input id="filtertext" name="filtertext" value="<?=gettext($filtertext);?>" />
|
||||
<input id="filtersubmit" name="filtersubmit" type="submit" class="formbtn" value="<?=gettext("Filter");?>" />
|
||||
</form>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
@ -108,8 +108,11 @@ include("head.inc");
|
||||
<tr>
|
||||
<td>
|
||||
<div id="mainarea">
|
||||
<div class="tabcont">
|
||||
<a href="diag_logs_filter.php">Normal View</a> | Dynamic View | <a href="diag_logs_filter_summary.php">Summary View</a>
|
||||
</div>
|
||||
<div class="listtopic">
|
||||
Last <?php echo $nentries; ?> records; (Switch to <a href="diag_logs_filter.php">regular</a> view) Pause:<input valign="middle" type="checkbox" onClick="javascript:toggle_pause();">
|
||||
Last <?php echo $nentries; ?> records; Pause:<input valign="middle" type="checkbox" onClick="javascript:toggle_pause();">
|
||||
</div>
|
||||
<div id="log">
|
||||
<div class="log-header">
|
||||
|
||||
227
usr/local/www/diag_logs_filter_summary.php
Normal file
227
usr/local/www/diag_logs_filter_summary.php
Normal file
@ -0,0 +1,227 @@
|
||||
<?php
|
||||
/*
|
||||
diag_logs_filter_summary.php
|
||||
|
||||
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
$pgtitle = "Diagnostics: System logs: Firewall Log Summary";
|
||||
require_once("guiconfig.inc");
|
||||
include_once("includes/log.inc.php");
|
||||
|
||||
$filter_logfile = "{$g['varlog_path']}/filter.log";
|
||||
$lines = 5000;
|
||||
$entriesperblock = 5;
|
||||
|
||||
$filterlog = conv_clog_filter($filter_logfile, $lines, $lines);
|
||||
$gotlines = count($filterlog);
|
||||
|
||||
$descr = array();
|
||||
$descr['actions'] = "Actions";
|
||||
$descr['ints'] = "Interfaces";
|
||||
$descr['protos'] = "Protocols";
|
||||
$descr['srcips'] = "Source IPs";
|
||||
$descr['dstips'] = "Destination IPs";
|
||||
$descr['srcports'] = "Source Ports";
|
||||
$descr['dstports'] = "Destination Ports";
|
||||
|
||||
$summary['actions'] = array();
|
||||
$summary['ints'] = array();
|
||||
$summary['protos'] = array();
|
||||
$summary['srcips'] = array();
|
||||
$summary['dstips'] = array();
|
||||
$summary['srcports'] = array();
|
||||
$summary['dstports'] = array();
|
||||
|
||||
$totals = array();
|
||||
|
||||
function cmp($a, $b) {
|
||||
if ($a == $b) {
|
||||
return 0;
|
||||
}
|
||||
return ($a < $b) ? 1 : -1;
|
||||
}
|
||||
|
||||
function stat_block($summary, $stat, $num) {
|
||||
global $gotlines, $descr;
|
||||
uasort($summary[$stat] , 'cmp');
|
||||
print '<table width="200px" cellpadding="3" cellspacing="0" border="1">';
|
||||
print "<tr><th colspan='2'>{$descr[$stat]} data</th></tr>";
|
||||
$k = array_keys($summary[$stat]);
|
||||
$total = 0;
|
||||
$numentries = 0;
|
||||
for ($i=0; $i < $num; $i++) {
|
||||
if ($k[$i]) {
|
||||
$total += $summary[$stat][$k[$i]];
|
||||
$numentries++;
|
||||
$outstr = $k[$i];
|
||||
if (is_ipaddr($outstr)) {
|
||||
$outstr = "<a href=\"diag_dns.php?host={$outstr}\" title=\"Reverse Resolve with DNS\"><img border=\"0\" src=\"/themes/nervecenter/images/icons/icon_log.gif\"></a> {$outstr}";
|
||||
} elseif (substr_count($outstr, '/') == 1) {
|
||||
list($proto, $port) = explode('/', $outstr);
|
||||
$service = getservbyport($port, strtolower($proto));
|
||||
if ($service)
|
||||
$outstr .= ": {$service}";
|
||||
}
|
||||
print "<tr><td>{$outstr}</td><td width='50px' align='right'>{$summary[$stat][$k[$i]]}</td></tr>\n";
|
||||
}
|
||||
}
|
||||
$leftover = $gotlines - $total;
|
||||
if ($leftover > 0) {
|
||||
print "<tr><td>Other</td><td width='50px' align='right'>{$leftover}</td></tr>\n";
|
||||
}
|
||||
print '</table>';
|
||||
}
|
||||
|
||||
function pie_block($summary, $stat, $num) {
|
||||
global $gotlines, $descr;
|
||||
uasort($summary[$stat] , 'cmp');
|
||||
$k = array_keys($summary[$stat]);
|
||||
$total = 0;
|
||||
$numentries = 0;
|
||||
print "\n<script language=\"javascript\" type=\"text/javascript\">\n";
|
||||
print "function lblfmt(lbl) {\n";
|
||||
print " return '<font size=\"-2\">' + lbl + '</font>'\n";
|
||||
print "}\n";
|
||||
for ($i=0; $i < $num; $i++) {
|
||||
if ($k[$i]) {
|
||||
$total += $summary[$stat][$k[$i]];
|
||||
$numentries++;
|
||||
print "var d{$i} = [];\n";
|
||||
print "d{$i}.push([1, {$summary[$stat][$k[$i]]}]);\n";
|
||||
}
|
||||
}
|
||||
$leftover = $gotlines - $total;
|
||||
if ($leftover > 0) {
|
||||
print "var d{$num} = [];\n";
|
||||
print "d{$num}.push([1, {$leftover}]);\n";
|
||||
}
|
||||
|
||||
print "Event.observe(window, 'load', function() {\n";
|
||||
print " new Proto.Chart($('piechart{$stat}'), \n";
|
||||
print " [\n";
|
||||
for ($i=0; $i < $num; $i++) {
|
||||
if ($k[$i]) {
|
||||
print " { data: d{$i}, label: \"{$k[$i]}\"}";
|
||||
if (!(($i == ($numentries - 1)) && ($leftover <= 0)))
|
||||
print ",\n";
|
||||
else
|
||||
print "\n";
|
||||
}
|
||||
}
|
||||
if ($leftover > 0)
|
||||
print " { data: d{$i}, label: \"Other\"}\n";
|
||||
print " ],\n";
|
||||
print " {\n";
|
||||
print " pies: {show: true, autoScale: true},\n";
|
||||
print " legend: {show: true, labelFormatter: lblfmt}\n";
|
||||
print " });\n";
|
||||
print "});\n";
|
||||
|
||||
print "</script>";
|
||||
print "<table cellpadding=\"3\" cellspacing=\"0\" border=\"0\">";
|
||||
print "<tr><th><font size=\"+1\">{$descr[$stat]}</font></th></tr>";
|
||||
print "<tr><td><div id=\"piechart{$stat}\" style=\"width:450px;height:300px\"></div>\n";
|
||||
print "</table>";
|
||||
}
|
||||
|
||||
foreach ($filterlog as $fe) {
|
||||
$summary['actions'][$fe['act']]++;
|
||||
$summary['ints'][$fe['interface']]++;
|
||||
$summary['protos'][$fe['proto']]++;
|
||||
$summary['srcips'][$fe['srcip']]++;
|
||||
$summary['dstips'][$fe['dstip']]++;
|
||||
if ($fe['srcport'])
|
||||
$summary['srcports'][$fe['proto'].'/'.$fe['srcport']]++;
|
||||
else
|
||||
$summary['srcports'][$fe['srcport']]++;
|
||||
if ($fe['dstport'])
|
||||
$summary['dstports'][$fe['proto'].'/'.$fe['dstport']]++;
|
||||
else
|
||||
$summary['dstports'][$fe['dstport']]++;
|
||||
}
|
||||
|
||||
include("head.inc"); ?>
|
||||
<body link="#000000" vlink="#000000" alink="#000000">
|
||||
<script src="/javascript/filter_log.js" type="text/javascript"></script>
|
||||
<script language="javascript" type="text/javascript" src="/protochart/prototype.js"></script>
|
||||
<script language="javascript" type="text/javascript" src="/protochart/ProtoChart.js"></script>
|
||||
<!--[if IE]>
|
||||
<script language="javascript" type="text/javascript" src="/protochart/excanvas.js">
|
||||
</script>
|
||||
<![endif]-->
|
||||
|
||||
<? include("fbegin.inc"); ?>
|
||||
<p class="pgtitle"><?=$pgtitle?></p>
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0">
|
||||
<tr><td>
|
||||
<?php
|
||||
$tab_array = array();
|
||||
$tab_array[] = array("System", false, "diag_logs.php");
|
||||
$tab_array[] = array("Firewall", true, "diag_logs_filter.php");
|
||||
$tab_array[] = array("DHCP", false, "diag_logs_dhcp.php");
|
||||
$tab_array[] = array("Portal Auth", false, "diag_logs_auth.php");
|
||||
$tab_array[] = array("IPSEC VPN", false, "diag_logs_ipsec.php");
|
||||
$tab_array[] = array("PPTP VPN", false, "diag_logs_vpn.php");
|
||||
$tab_array[] = array("Load Balancer", false, "diag_logs_slbd.php");
|
||||
$tab_array[] = array("OpenVPN", false, "diag_logs_openvpn.php");
|
||||
$tab_array[] = array("OpenNTPD", false, "diag_logs_ntpd.php");
|
||||
$tab_array[] = array("Settings", false, "diag_logs_settings.php");
|
||||
display_top_tabs($tab_array);
|
||||
?>
|
||||
</td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="mainarea">
|
||||
<table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0" align="center">
|
||||
<tr><td colspan="6" align="left">
|
||||
<a href="diag_logs_filter.php">Normal View</a> | <a href="diag_logs_filter_dynamic.php">Dynamic View</a> | Summary View<br/><br/>
|
||||
</td></tr>
|
||||
<tr><td align="center">
|
||||
|
||||
This is a firewall log summary, of the last <?php echo $gotlines; ?> lines of the firewall log (Max <?php echo $lines; ?>).<br />
|
||||
NOTE: IE8 users must enable compatibility view.
|
||||
|
||||
<?php pie_block($summary, 'actions' , $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'actions' , $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'ints' , $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'ints' , $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'protos' , $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'protos' , $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'srcips' , $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'srcips' , $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'dstips' , $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'dstips' , $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'srcports', $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'srcports', $entriesperblock); ?><br /><br />
|
||||
<?php pie_block($summary, 'dstports', $entriesperblock); ?><br /><br />
|
||||
<?php stat_block($summary, 'dstports', $entriesperblock); ?><br /><br />
|
||||
|
||||
</td></tr></table>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<?php include("fend.inc"); ?>
|
||||
260
usr/local/www/easyrule.inc
Normal file
260
usr/local/www/easyrule.inc
Normal file
@ -0,0 +1,260 @@
|
||||
<?php
|
||||
/*
|
||||
easyrule.inc.php
|
||||
|
||||
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
|
||||
Sponsored By Anathematic @ pfSense Forums
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
$blockaliasname = 'EasyRuleBlockHosts';
|
||||
$specialsrcdst = explode(" ", "any wanip lanip lan pptp pppoe");
|
||||
|
||||
function easyrule_find_rule_interface($int) {
|
||||
global $config;
|
||||
/* Borrowed from firewall_rules.php */
|
||||
$iflist = array("lan" => "LAN", "wan" => "WAN");
|
||||
|
||||
for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
|
||||
$iflist['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
|
||||
}
|
||||
|
||||
if ($config['pptpd']['mode'] == "server")
|
||||
$iflist['pptp'] = "PPTP VPN";
|
||||
|
||||
if ($config['pppoe']['mode'] == "server")
|
||||
$iflist['pppoe'] = "PPPoE VPN";
|
||||
|
||||
/* add ipsec interfaces */
|
||||
if (isset($config['ipsec']['enable']) || isset($config['ipsec']['mobileclients']['enable'])){
|
||||
$iflist["enc0"] = "IPSEC";
|
||||
}
|
||||
|
||||
for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) {
|
||||
$iflist['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];
|
||||
}
|
||||
|
||||
if (isset($iflist[$int]))
|
||||
return $int;
|
||||
|
||||
foreach ($iflist as $if => $ifd) {
|
||||
if (strtolower($int) == strtolower($ifd))
|
||||
return $if;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function easyrule_block_rule_exists($int = 'wan') {
|
||||
global $blockaliasname, $config;
|
||||
/* No rules, we we know it doesn't exist */
|
||||
if (!is_array($config['filter']['rule'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Search through the rules for one referencing our alias */
|
||||
foreach ($config['filter']['rule'] as $rule)
|
||||
if ($rule['source']['address'] == $blockaliasname . strtoupper($int) && ($rule['interface'] == $int))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function easyrule_block_rule_create($int = 'wan') {
|
||||
global $blockaliasname, $config;
|
||||
/* If the alias doesn't exist, exit.
|
||||
* Can't create an empty alias, and we don't know a host */
|
||||
if (easyrule_block_alias_getid($int) === false)
|
||||
return false;
|
||||
|
||||
/* If the rule already exists, no need to do it again */
|
||||
if (easyrule_block_rule_exists($int))
|
||||
return true;
|
||||
|
||||
/* No rules, start a new array */
|
||||
if (!is_array($config['filter']['rule'])) {
|
||||
$config['filter']['rule'] = array();
|
||||
}
|
||||
|
||||
filter_rules_sort();
|
||||
$a_filter = &$config['filter']['rule'];
|
||||
|
||||
/* Make up a new rule */
|
||||
$filterent = array();
|
||||
$filterent['type'] = 'block';
|
||||
$filterent['interface'] = $int;
|
||||
$filterent['source']['address'] = $blockaliasname . strtoupper($int);
|
||||
$filterent['destination']['any'] = '';
|
||||
$filterent['descr'] = "Easy Rule: Blocked from Firewall Log View";
|
||||
|
||||
$a_filter[] = $filterent;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function easyrule_block_alias_getid($int = 'wan') {
|
||||
global $blockaliasname, $config;
|
||||
if (!is_array($config['aliases']))
|
||||
return false;
|
||||
|
||||
/* Hunt down an alias with the name we want, return its id */
|
||||
foreach ($config['aliases']['alias'] as $aliasid => $alias)
|
||||
if ($alias['name'] == $blockaliasname . strtoupper($int))
|
||||
return $aliasid;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function easyrule_block_alias_add($host, $int = 'wan') {
|
||||
global $blockaliasname, $config;
|
||||
/* If the host isn't a valid IP address, bail */
|
||||
if (!is_ipaddr($host))
|
||||
return false;
|
||||
|
||||
/* If there are no aliases, start an array */
|
||||
if (!is_array($config['aliases']['alias']))
|
||||
$config['aliases']['alias'] = array();
|
||||
|
||||
aliases_sort();
|
||||
$a_aliases = &$config['aliases']['alias'];
|
||||
|
||||
/* Try to get the ID if the alias already exists */
|
||||
$id = easyrule_block_alias_getid($int);
|
||||
if ($id === false)
|
||||
unset($id);
|
||||
|
||||
$alias = array();
|
||||
|
||||
if (isset($id) && $a_aliases[$id]) {
|
||||
/* Make sure this IP isn't already in the list. */
|
||||
if (in_array($host.'/32', explode(" ", $a_aliases[$id]['address'])))
|
||||
return true;
|
||||
/* Since the alias already exists, just add to it. */
|
||||
$alias['name'] = $a_aliases[$id]['name'];
|
||||
$alias['type'] = $a_aliases[$id]['type'];
|
||||
$alias['descr'] = $a_aliases[$id]['descr'];
|
||||
|
||||
$alias['address'] = $a_aliases[$id]['address'] . ' ' . $host . '/32';
|
||||
$alias['detail'] = $a_aliases[$id]['detail'] . 'Entry added ' . date('r') . '||';
|
||||
} else {
|
||||
/* Create a new alias with all the proper information */
|
||||
$alias['name'] = $blockaliasname . strtoupper($int);
|
||||
$alias['type'] = 'network';
|
||||
$alias['descr'] = mb_convert_encoding("Hosts blocked from Firewall Log view","HTML-ENTITIES","auto");
|
||||
|
||||
$alias['address'] = $host . '/32';
|
||||
$alias['detail'] = 'Entry added ' . date('r') . '||';
|
||||
}
|
||||
|
||||
/* Replace the old alias if needed, otherwise tack it on the end */
|
||||
if (isset($id) && $a_aliases[$id])
|
||||
$a_aliases[$id] = $alias;
|
||||
else
|
||||
$a_aliases[] = $alias;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function easyrule_block_host_add($host, $int = 'wan') {
|
||||
global $retval;
|
||||
/* Bail if the supplied host is not a valid IP address */
|
||||
if (!is_ipaddr($host))
|
||||
return false;
|
||||
|
||||
/* Flag whether or not we need to reload the filter */
|
||||
$dirty = false;
|
||||
|
||||
/* Attempt to add this host to the alias */
|
||||
if (easyrule_block_alias_add($host, $int)) {
|
||||
$dirty = true;
|
||||
} else {
|
||||
/* Couldn't add the alias, or adding the host failed. */
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Attempt to add the firewall rule if it doesn't exist.
|
||||
* Failing to add the rule isn't necessarily an error, it may
|
||||
* have been modified by the user in some way. Adding to the
|
||||
* Alias is what's important.
|
||||
*/
|
||||
if (!easyrule_block_rule_exists($int)) {
|
||||
if (easyrule_block_rule_create($int)) {
|
||||
$dirty = true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* If needed, write the config and reload the filter */
|
||||
if ($dirty) {
|
||||
write_config();
|
||||
config_lock();
|
||||
$retval = filter_configure();
|
||||
config_unlock();
|
||||
header("Location: firewall_aliases.php");
|
||||
exit;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function easyrule_pass_rule_add($int, $proto, $srchost, $dsthost, $dstport) {
|
||||
global $config;
|
||||
|
||||
/* No rules, start a new array */
|
||||
if (!is_array($config['filter']['rule'])) {
|
||||
$config['filter']['rule'] = array();
|
||||
}
|
||||
|
||||
filter_rules_sort();
|
||||
$a_filter = &$config['filter']['rule'];
|
||||
|
||||
/* Make up a new rule */
|
||||
$filterent = array();
|
||||
$filterent['type'] = 'pass';
|
||||
$filterent['interface'] = $int;
|
||||
$filterent['descr'] = "Easy Rule: Passed from Firewall Log View";
|
||||
|
||||
if ($proto != "any")
|
||||
$filterent['protocol'] = $proto;
|
||||
else
|
||||
unset($filterent['protocol']);
|
||||
|
||||
/* Default to only allow echo requests, since that's what most people want and
|
||||
* it should be a safe choice. */
|
||||
if ($proto == "icmp")
|
||||
$filterent['icmptype'] = 'echoreq';
|
||||
|
||||
pconfig_to_address($filterent['source'], $srchost, 32);
|
||||
pconfig_to_address($filterent['destination'], $dsthost, 32, '', $dstport, $dstport);
|
||||
|
||||
$a_filter[] = $filterent;
|
||||
|
||||
write_config();
|
||||
config_lock();
|
||||
$retval = filter_configure();
|
||||
config_unlock();
|
||||
header("Location: firewall_rules.php?if={$int}");
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
132
usr/local/www/easyrule.php
Normal file
132
usr/local/www/easyrule.php
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
/*
|
||||
easyrule.php
|
||||
|
||||
Copyright (C) 2009 Jim Pingle (jpingle@gmail.com)
|
||||
Sponsored By Anathematic @ pfSense Forums
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
||||
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
|
||||
AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
|
||||
OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
$pgtitle = "Status : EasyRule";
|
||||
require_once("guiconfig.inc");
|
||||
require_once("easyrule.inc");
|
||||
$retval = 0;
|
||||
$message = "";
|
||||
|
||||
if ($_GET && isset($_GET['action'])) {
|
||||
switch ($_GET['action']) {
|
||||
case 'block':
|
||||
/* Check that we have a valid host */
|
||||
if (isset($_GET['src']) && isset($_GET['int'])) {
|
||||
if (!is_ipaddr($_GET['src'])) {
|
||||
$message .= "Tried to block invalid IP: " . htmlspecialchars($_GET['src']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
$_GET['int'] = easyrule_find_rule_interface($_GET['int']);
|
||||
if ($_GET['int'] === false) {
|
||||
$message .= "Invalid interface for block rule: " . htmlspecialchars($_GET['int']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if (easyrule_block_host_add($_GET['src'], $_GET['int'])) {
|
||||
/* shouldn't get here, the function will redirect */
|
||||
$message .= "Host added successfully" . "<br/>";
|
||||
} else {
|
||||
$message .= "Failed to create block rule, alias, or add host." . "<br/>";
|
||||
}
|
||||
} else {
|
||||
$message .= "Tried to block but had no host IP or interface<br/>";
|
||||
}
|
||||
break;
|
||||
case 'pass':
|
||||
/* Check for valid int, srchost, dsthost, dstport, and proto */
|
||||
if (isset($_GET['int']) && isset($_GET['proto']) && isset($_GET['src']) && isset($_GET['dst'])) {
|
||||
$_GET['int'] = easyrule_find_rule_interface($_GET['int']);
|
||||
if ($_GET['int'] === false) {
|
||||
$message .= "Invalid interface for pass rule: " . htmlspecialchars($_GET['int']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if (getprotobyname($_GET['proto']) == -1) {
|
||||
$message .= "Invalid protocol for pass rule: " . htmlspecialchars($_GET['proto']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if (!is_ipaddr($_GET['src'])) {
|
||||
$message .= "Tried to pass invalid source IP: " . htmlspecialchars($_GET['src']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if (!is_ipaddr($_GET['dst'])) {
|
||||
$message .= "Tried to pass invalid destination IP: " . htmlspecialchars($_GET['dst']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if (($_GET['proto'] != 'icmp') && !isset($_GET['dstport'])) {
|
||||
$message .= "Missing destination port: " . htmlspecialchars($_GET['dstport']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
if ($_GET['proto'] == 'icmp') {
|
||||
$_GET['dstport'] = 0;
|
||||
}
|
||||
if (!is_numeric($_GET['dstport']) || ($_GET['dstport'] < 0) || ($_GET['dstport'] > 65536)) {
|
||||
$message .= "Tried to pass invalid destination port: " . htmlspecialchars($_GET['dstport']) . "<br/>";
|
||||
break;
|
||||
}
|
||||
/* Should have valid input... */
|
||||
if (easyrule_pass_rule_add($_GET['int'], $_GET['proto'], $_GET['src'], $_GET['dst'], $_GET['dstport'])) {
|
||||
/* Shouldn't get here, the function should redirect. */
|
||||
$message .= "Successfully added pass rule!" . "<br/>";
|
||||
} else {
|
||||
$message .= "Failed to add pass rule." . "<br/>";
|
||||
}
|
||||
} else {
|
||||
$message = "Missing parameters for pass rule";
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(stristr($retval, "error") == true)
|
||||
$message = $retval;
|
||||
|
||||
include("head.inc"); ?>
|
||||
<body link="#000000" vlink="#000000" alink="#000000">
|
||||
<? include("fbegin.inc"); ?>
|
||||
<p class="pgtitle"><?=$pgtitle?></p>
|
||||
<table width="100%" border="0" cellpadding="0" cellspacing="0">
|
||||
<tr>
|
||||
<td>
|
||||
<?php if ($input_errors) print_input_errors($input_errors); ?>
|
||||
|
||||
<?php if ($message) { ?>
|
||||
<br/>
|
||||
Message: <?php echo $message; ?>
|
||||
<br/>
|
||||
<? } else { ?>
|
||||
This is the Easy Rule status page, mainly used to display errors when adding rules.
|
||||
If you are seeing this, there apparently was not an error, and you navigated to the
|
||||
page directly without telling it what to do.<br/><br/>
|
||||
This page is meant to be called from the block/pass buttons on the Firewall Logs page, <a href="http://192.168.56.101/diag_logs_filter.php">Status > System Logs,
|
||||
Firewall Tab</a>.
|
||||
<br />
|
||||
<? } ?>
|
||||
</td></tr></table>
|
||||
<?php include("fend.inc"); ?>
|
||||
@ -1,7 +1,7 @@
|
||||
<?php
|
||||
/* $Id$ */
|
||||
/*
|
||||
log.inc.php
|
||||
filter_log.inc
|
||||
part of pfSesne by Scott Ullrich
|
||||
originally based on m0n0wall (http://m0n0.ch/wall)
|
||||
|
||||
@ -31,13 +31,16 @@
|
||||
*/
|
||||
|
||||
/* format filter logs */
|
||||
function conv_log_filter($logfile, $nentries, $tail = 50) {
|
||||
function conv_log_filter($logfile, $nentries, $tail = 50, $filtertext = "") {
|
||||
global $config, $g;
|
||||
|
||||
/* Make sure this is a number before using it in a system call */
|
||||
if (!(is_numeric($tail)))
|
||||
return;
|
||||
|
||||
if ($filtertext)
|
||||
$tail = 5000;
|
||||
|
||||
/* FreeBSD 8 splits pf log lines into two lines, so we need to at least
|
||||
* tail twice as many, plus some extra to account for unparseable lines */
|
||||
$tail = $tail * 2 + 50;
|
||||
@ -45,7 +48,7 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
|
||||
/* Always do a reverse tail, to be sure we're grabbing the 'end' of the log. */
|
||||
$logarr = "";
|
||||
|
||||
if(isset($config['system']['usefifolog']))
|
||||
if(isset($config['system']['usefifolog']))
|
||||
exec("/usr/sbin/fifolog_reader {$logfile} | /usr/bin/tail -r -n {$tail}", $logarr);
|
||||
else
|
||||
exec("/usr/sbin/clog {$logfile} | grep -v \"CLOG\" | grep -v \"\033\" | /usr/bin/tail -r -n {$tail}", $logarr);
|
||||
@ -60,7 +63,7 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
|
||||
break;
|
||||
|
||||
$flent = parse_filter_line($logent);
|
||||
if ($flent != "") {
|
||||
if (($flent != "") && (match_filter_line($flent, $filtertext))) {
|
||||
$counter++;
|
||||
$filterlog[] = $flent;
|
||||
}
|
||||
@ -69,6 +72,13 @@ function conv_log_filter($logfile, $nentries, $tail = 50) {
|
||||
return isset($config['syslog']['reverse']) ? $filterlog : array_reverse($filterlog);
|
||||
}
|
||||
|
||||
function match_filter_line($flent, $filtertext = "") {
|
||||
if (!$filtertext)
|
||||
return true;
|
||||
$filtertext = str_replace(' ', '\s+', $filtertext);
|
||||
return preg_match("/{$filtertext}/i", implode(" ", array_values($flent)));
|
||||
}
|
||||
|
||||
function collapse_filter_lines($logarr) {
|
||||
$lastline = "";
|
||||
$collapsed = array();
|
||||
@ -95,8 +105,17 @@ function parse_filter_line($line) {
|
||||
|
||||
list($all, $flent['time'], $host, $rule, $flent['act'], $flent['realint'], $details, $src, $dst, $leftovers) = $log_split;
|
||||
|
||||
$flent['src'] = convert_port_period_to_colon($src);
|
||||
$flent['dst'] = convert_port_period_to_colon($dst);
|
||||
list($flent['srcip'], $flent['srcport']) = parse_ipport($src);
|
||||
list($flent['dstip'], $flent['dstport']) = parse_ipport($dst);
|
||||
|
||||
$flent['src'] = $flent['srcip'];
|
||||
$flent['dst'] = $flent['dstip'];
|
||||
|
||||
if ($flent['srcport'])
|
||||
$flent['src'] .= ':' . $flent['srcport'];
|
||||
if ($flent['dstport'])
|
||||
$flent['dst'] .= ':' . $flent['dstport'];
|
||||
|
||||
$flent['interface'] = convert_log_interface_to_friendly_interface_name($flent['realint']);
|
||||
|
||||
$tmp = split("/", $rule);
|
||||
@ -104,7 +123,7 @@ function parse_filter_line($line) {
|
||||
|
||||
$proto = array(" ", "(?)");
|
||||
/* Attempt to determine the protocol, based on several possible patterns.
|
||||
* The value returned by strpos() must be strictly checkeded against the
|
||||
* The value returned by strpos() must be strictly checkeded against the
|
||||
* boolean FALSE because it could return a valid answer of 0 upon success. */
|
||||
if (!(strpos($details, 'proto ') === FALSE)) {
|
||||
preg_match("/.*\sproto\s(.*)\s\(/", $details, $proto);
|
||||
@ -152,26 +171,49 @@ function convert_log_interface_to_friendly_interface_name($int) {
|
||||
return $int;
|
||||
}
|
||||
|
||||
function convert_port_period_to_colon($addr) {
|
||||
function parse_ipport($addr) {
|
||||
$addr = rtrim($addr, ":");
|
||||
$port = '';
|
||||
if (substr_count($addr, '.') > 1) {
|
||||
/* IPv4 - Change the port delimiter to : */
|
||||
/* IPv4 */
|
||||
$addr_split = split("\.", $addr);
|
||||
if($addr_split[4] == "") {
|
||||
$newvar = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}";
|
||||
$newvar = rtrim($newvar, ":");
|
||||
} else {
|
||||
$ip = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}";
|
||||
|
||||
if ($ip == "...")
|
||||
return array($addr, '');
|
||||
|
||||
if($addr_split[4] != "") {
|
||||
$port_split = split("\:", $addr_split[4]);
|
||||
$newvar = "{$addr_split[0]}.{$addr_split[1]}.{$addr_split[2]}.{$addr_split[3]}:{$port_split[0]}";
|
||||
$newvar = rtrim($newvar, ":");
|
||||
$port = $port_split[0];
|
||||
}
|
||||
if($newvar == "...")
|
||||
return $addr;
|
||||
return $newvar;
|
||||
} else {
|
||||
/* IPv6 - Leave it alone */
|
||||
/* IPv6 */
|
||||
$addr = split(" ", $addr);
|
||||
return rtrim($addr[0], ":");
|
||||
$addr = rtrim($addr[0], ":");
|
||||
$addr_split = split("\.", $addr);
|
||||
if (count($addr_split) > 1) {
|
||||
$ip = $addr_split[0];
|
||||
$port = $addr_split[1];
|
||||
} else {
|
||||
$ip = $addr;
|
||||
}
|
||||
}
|
||||
|
||||
return array($ip, $port);
|
||||
}
|
||||
|
||||
function get_port_with_service($port, $proto) {
|
||||
if (!$port)
|
||||
return '';
|
||||
|
||||
$service = getservbyport($port, $proto);
|
||||
$portstr = "";
|
||||
if ($service) {
|
||||
$portstr = "<span title=\"Service {$port}/{$proto}: {$service}\">" . htmlspecialchars($port) . "</span>";
|
||||
} else {
|
||||
$portstr = htmlspecialchars($port);
|
||||
}
|
||||
return ':' . $portstr;
|
||||
}
|
||||
|
||||
function find_rule_by_number($rulenum, $type="rules") {
|
||||
|
||||
@ -63,7 +63,11 @@ while(!feof($log)) {
|
||||
proto - Protocol (e.g. TCP, UDP, ICMP, etc)
|
||||
tcpflags - TCP flags/control bits
|
||||
src - Source address with port
|
||||
srcip - Source IP
|
||||
srcport - Source Port
|
||||
dst - Destination address with port
|
||||
dstip - Destination IP
|
||||
dstport - Destination Port
|
||||
*/
|
||||
if ($flent != "")
|
||||
echo "{$flent['time']} {$flent['act']} {$flent['realint']} {$flent['proto']} {$flent['src']} {$flent['dst']}\n";
|
||||
|
||||
2653
usr/local/www/protochart/ProtoChart.js
Normal file
2653
usr/local/www/protochart/ProtoChart.js
Normal file
File diff suppressed because it is too large
Load Diff
19
usr/local/www/protochart/excanvas-compressed.js
Normal file
19
usr/local/www/protochart/excanvas-compressed.js
Normal file
@ -0,0 +1,19 @@
|
||||
if(!window.CanvasRenderingContext2D){(function(){var I=Math,i=I.round,L=I.sin,M=I.cos,m=10,A=m/2,Q={init:function(a){var b=a||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var c=this;b.attachEvent("onreadystatechange",function(){c.r(b)})}},r:function(a){if(a.readyState=="complete"){if(!a.namespaces["s"]){a.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var b=a.createStyleSheet();b.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}";
|
||||
var c=a.getElementsByTagName("canvas");for(var d=0;d<c.length;d++){if(!c[d].getContext){this.initElement(c[d])}}}},q:function(a){var b=a.outerHTML,c=a.ownerDocument.createElement(b);if(b.slice(-2)!="/>"){var d="/"+a.tagName,e;while((e=a.nextSibling)&&e.tagName!=d){e.removeNode()}if(e){e.removeNode()}}a.parentNode.replaceChild(c,a);return c},initElement:function(a){a=this.q(a);a.getContext=function(){if(this.l){return this.l}return this.l=new K(this)};a.attachEvent("onpropertychange",V);a.attachEvent("onresize",
|
||||
W);var b=a.attributes;if(b.width&&b.width.specified){a.style.width=b.width.nodeValue+"px"}else{a.width=a.clientWidth}if(b.height&&b.height.specified){a.style.height=b.height.nodeValue+"px"}else{a.height=a.clientHeight}return a}};function V(a){var b=a.srcElement;switch(a.propertyName){case "width":b.style.width=b.attributes.width.nodeValue+"px";b.getContext().clearRect();break;case "height":b.style.height=b.attributes.height.nodeValue+"px";b.getContext().clearRect();break}}function W(a){var b=a.srcElement;
|
||||
if(b.firstChild){b.firstChild.style.width=b.clientWidth+"px";b.firstChild.style.height=b.clientHeight+"px"}}Q.init();var R=[];for(var E=0;E<16;E++){for(var F=0;F<16;F++){R[E*16+F]=E.toString(16)+F.toString(16)}}function J(){return[[1,0,0],[0,1,0],[0,0,1]]}function G(a,b){var c=J();for(var d=0;d<3;d++){for(var e=0;e<3;e++){var g=0;for(var h=0;h<3;h++){g+=a[d][h]*b[h][e]}c[d][e]=g}}return c}function N(a,b){b.fillStyle=a.fillStyle;b.lineCap=a.lineCap;b.lineJoin=a.lineJoin;b.lineWidth=a.lineWidth;b.miterLimit=
|
||||
a.miterLimit;b.shadowBlur=a.shadowBlur;b.shadowColor=a.shadowColor;b.shadowOffsetX=a.shadowOffsetX;b.shadowOffsetY=a.shadowOffsetY;b.strokeStyle=a.strokeStyle;b.d=a.d;b.e=a.e}function O(a){var b,c=1;a=String(a);if(a.substring(0,3)=="rgb"){var d=a.indexOf("(",3),e=a.indexOf(")",d+1),g=a.substring(d+1,e).split(",");b="#";for(var h=0;h<3;h++){b+=R[Number(g[h])]}if(g.length==4&&a.substr(3,1)=="a"){c=g[3]}}else{b=a}return[b,c]}function S(a){switch(a){case "butt":return"flat";case "round":return"round";
|
||||
case "square":default:return"square"}}function K(a){this.a=J();this.m=[];this.k=[];this.c=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=m*1;this.globalAlpha=1;this.canvas=a;var b=a.ownerDocument.createElement("div");b.style.width=a.clientWidth+"px";b.style.height=a.clientHeight+"px";b.style.overflow="hidden";b.style.position="absolute";a.appendChild(b);this.j=b;this.d=1;this.e=1}var j=K.prototype;j.clearRect=function(){this.j.innerHTML=
|
||||
"";this.c=[]};j.beginPath=function(){this.c=[]};j.moveTo=function(a,b){this.c.push({type:"moveTo",x:a,y:b});this.f=a;this.g=b};j.lineTo=function(a,b){this.c.push({type:"lineTo",x:a,y:b});this.f=a;this.g=b};j.bezierCurveTo=function(a,b,c,d,e,g){this.c.push({type:"bezierCurveTo",cp1x:a,cp1y:b,cp2x:c,cp2y:d,x:e,y:g});this.f=e;this.g=g};j.quadraticCurveTo=function(a,b,c,d){var e=this.f+0.6666666666666666*(a-this.f),g=this.g+0.6666666666666666*(b-this.g),h=e+(c-this.f)/3,l=g+(d-this.g)/3;this.bezierCurveTo(e,
|
||||
g,h,l,c,d)};j.arc=function(a,b,c,d,e,g){c*=m;var h=g?"at":"wa",l=a+M(d)*c-A,n=b+L(d)*c-A,o=a+M(e)*c-A,f=b+L(e)*c-A;if(l==o&&!g){l+=0.125}this.c.push({type:h,x:a,y:b,radius:c,xStart:l,yStart:n,xEnd:o,yEnd:f})};j.rect=function(a,b,c,d){this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath()};j.strokeRect=function(a,b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.stroke()};j.fillRect=function(a,
|
||||
b,c,d){this.beginPath();this.moveTo(a,b);this.lineTo(a+c,b);this.lineTo(a+c,b+d);this.lineTo(a,b+d);this.closePath();this.fill()};j.createLinearGradient=function(a,b,c,d){var e=new H("gradient");return e};j.createRadialGradient=function(a,b,c,d,e,g){var h=new H("gradientradial");h.n=c;h.o=g;h.i.x=a;h.i.y=b;return h};j.drawImage=function(a,b){var c,d,e,g,h,l,n,o,f=a.runtimeStyle.width,k=a.runtimeStyle.height;a.runtimeStyle.width="auto";a.runtimeStyle.height="auto";var q=a.width,r=a.height;a.runtimeStyle.width=
|
||||
f;a.runtimeStyle.height=k;if(arguments.length==3){c=arguments[1];d=arguments[2];h=(l=0);n=(e=q);o=(g=r)}else if(arguments.length==5){c=arguments[1];d=arguments[2];e=arguments[3];g=arguments[4];h=(l=0);n=q;o=r}else if(arguments.length==9){h=arguments[1];l=arguments[2];n=arguments[3];o=arguments[4];c=arguments[5];d=arguments[6];e=arguments[7];g=arguments[8]}else{throw"Invalid number of arguments";}var s=this.b(c,d),t=[],v=10,w=10;t.push(" <g_vml_:group",' coordsize="',m*v,",",m*w,'"',' coordorigin="0,0"',
|
||||
' style="width:',v,";height:",w,";position:absolute;");if(this.a[0][0]!=1||this.a[0][1]){var x=[];x.push("M11='",this.a[0][0],"',","M12='",this.a[1][0],"',","M21='",this.a[0][1],"',","M22='",this.a[1][1],"',","Dx='",i(s.x/m),"',","Dy='",i(s.y/m),"'");var p=s,y=this.b(c+e,d),z=this.b(c,d+g),B=this.b(c+e,d+g);p.x=Math.max(p.x,y.x,z.x,B.x);p.y=Math.max(p.y,y.y,z.y,B.y);t.push("padding:0 ",i(p.x/m),"px ",i(p.y/m),"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",x.join(""),", sizingmethod='clip');")}else{t.push("top:",
|
||||
i(s.y/m),"px;left:",i(s.x/m),"px;")}t.push(' ">','<g_vml_:image src="',a.src,'"',' style="width:',m*e,";"," height:",m*g,';"',' cropleft="',h/q,'"',' croptop="',l/r,'"',' cropright="',(q-h-n)/q,'"',' cropbottom="',(r-l-o)/r,'"'," />","</g_vml_:group>");this.j.insertAdjacentHTML("BeforeEnd",t.join(""))};j.stroke=function(a){var b=[],c=O(a?this.fillStyle:this.strokeStyle),d=c[0],e=c[1]*this.globalAlpha,g=10,h=10;b.push("<g_vml_:shape",' fillcolor="',d,'"',' filled="',Boolean(a),'"',' style="position:absolute;width:',
|
||||
g,";height:",h,';"',' coordorigin="0 0" coordsize="',m*g," ",m*h,'"',' stroked="',!a,'"',' strokeweight="',this.lineWidth,'"',' strokecolor="',d,'"',' path="');var l={x:null,y:null},n={x:null,y:null};for(var o=0;o<this.c.length;o++){var f=this.c[o];if(f.type=="moveTo"){b.push(" m ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="lineTo"){b.push(" l ");var k=this.b(f.x,f.y);b.push(i(k.x),",",i(k.y))}else if(f.type=="close"){b.push(" x ")}else if(f.type=="bezierCurveTo"){b.push(" c ");
|
||||
var k=this.b(f.x,f.y),q=this.b(f.cp1x,f.cp1y),r=this.b(f.cp2x,f.cp2y);b.push(i(q.x),",",i(q.y),",",i(r.x),",",i(r.y),",",i(k.x),",",i(k.y))}else if(f.type=="at"||f.type=="wa"){b.push(" ",f.type," ");var k=this.b(f.x,f.y),s=this.b(f.xStart,f.yStart),t=this.b(f.xEnd,f.yEnd);b.push(i(k.x-this.d*f.radius),",",i(k.y-this.e*f.radius)," ",i(k.x+this.d*f.radius),",",i(k.y+this.e*f.radius)," ",i(s.x),",",i(s.y)," ",i(t.x),",",i(t.y))}if(k){if(l.x==null||k.x<l.x){l.x=k.x}if(n.x==null||k.x>n.x){n.x=k.x}if(l.y==
|
||||
null||k.y<l.y){l.y=k.y}if(n.y==null||k.y>n.y){n.y=k.y}}}b.push(' ">');if(typeof this.fillStyle=="object"){var v={x:"50%",y:"50%"},w=n.x-l.x,x=n.y-l.y,p=w>x?w:x;v.x=i(this.fillStyle.i.x/w*100+50)+"%";v.y=i(this.fillStyle.i.y/x*100+50)+"%";var y=[];if(this.fillStyle.p=="gradientradial"){var z=this.fillStyle.n/p*100,B=this.fillStyle.o/p*100-z}else{var z=0,B=100}var C={offset:null,color:null},D={offset:null,color:null};this.fillStyle.h.sort(function(T,U){return T.offset-U.offset});for(var o=0;o<this.fillStyle.h.length;o++){var u=
|
||||
this.fillStyle.h[o];y.push(u.offset*B+z,"% ",u.color,",");if(u.offset>C.offset||C.offset==null){C.offset=u.offset;C.color=u.color}if(u.offset<D.offset||D.offset==null){D.offset=u.offset;D.color=u.color}}y.pop();b.push("<g_vml_:fill",' color="',D.color,'"',' color2="',C.color,'"',' type="',this.fillStyle.p,'"',' focusposition="',v.x,", ",v.y,'"',' colors="',y.join(""),'"',' opacity="',e,'" />')}else if(a){b.push('<g_vml_:fill color="',d,'" opacity="',e,'" />')}else{b.push("<g_vml_:stroke",' opacity="',
|
||||
e,'"',' joinstyle="',this.lineJoin,'"',' miterlimit="',this.miterLimit,'"',' endcap="',S(this.lineCap),'"',' weight="',this.lineWidth,'px"',' color="',d,'" />')}b.push("</g_vml_:shape>");this.j.insertAdjacentHTML("beforeEnd",b.join(""));this.c=[]};j.fill=function(){this.stroke(true)};j.closePath=function(){this.c.push({type:"close"})};j.b=function(a,b){return{x:m*(a*this.a[0][0]+b*this.a[1][0]+this.a[2][0])-A,y:m*(a*this.a[0][1]+b*this.a[1][1]+this.a[2][1])-A}};j.save=function(){var a={};N(this,a);
|
||||
this.k.push(a);this.m.push(this.a);this.a=G(J(),this.a)};j.restore=function(){N(this.k.pop(),this);this.a=this.m.pop()};j.translate=function(a,b){var c=[[1,0,0],[0,1,0],[a,b,1]];this.a=G(c,this.a)};j.rotate=function(a){var b=M(a),c=L(a),d=[[b,c,0],[-c,b,0],[0,0,1]];this.a=G(d,this.a)};j.scale=function(a,b){this.d*=a;this.e*=b;var c=[[a,0,0],[0,b,0],[0,0,1]];this.a=G(c,this.a)};j.clip=function(){};j.arcTo=function(){};j.createPattern=function(){return new P};function H(a){this.p=a;this.n=0;this.o=
|
||||
0;this.h=[];this.i={x:0,y:0}}H.prototype.addColorStop=function(a,b){b=O(b);this.h.push({offset:1-a,color:b})};function P(){}G_vmlCanvasManager=Q;CanvasRenderingContext2D=K;CanvasGradient=H;CanvasPattern=P})()};
|
||||
785
usr/local/www/protochart/excanvas.js
Normal file
785
usr/local/www/protochart/excanvas.js
Normal file
@ -0,0 +1,785 @@
|
||||
// Copyright 2006 Google Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
// Known Issues:
|
||||
//
|
||||
// * Patterns are not implemented.
|
||||
// * Radial gradient are not implemented. The VML version of these look very
|
||||
// different from the canvas one.
|
||||
// * Clipping paths are not implemented.
|
||||
// * Coordsize. The width and height attribute have higher priority than the
|
||||
// width and height style values which isn't correct.
|
||||
// * Painting mode isn't implemented.
|
||||
// * Canvas width/height should is using content-box by default. IE in
|
||||
// Quirks mode will draw the canvas using border-box. Either change your
|
||||
// doctype to HTML5
|
||||
// (http://www.whatwg.org/specs/web-apps/current-work/#the-doctype)
|
||||
// or use Box Sizing Behavior from WebFX
|
||||
// (http://webfx.eae.net/dhtml/boxsizing/boxsizing.html)
|
||||
// * Optimize. There is always room for speed improvements.
|
||||
|
||||
// only add this code if we do not already have a canvas implementation
|
||||
if (!window.CanvasRenderingContext2D) {
|
||||
|
||||
(function () {
|
||||
|
||||
// alias some functions to make (compiled) code shorter
|
||||
var m = Math;
|
||||
var mr = m.round;
|
||||
var ms = m.sin;
|
||||
var mc = m.cos;
|
||||
|
||||
// this is used for sub pixel precision
|
||||
var Z = 10;
|
||||
var Z2 = Z / 2;
|
||||
|
||||
var G_vmlCanvasManager_ = {
|
||||
init: function (opt_doc) {
|
||||
var doc = opt_doc || document;
|
||||
if (/MSIE/.test(navigator.userAgent) && !window.opera) {
|
||||
var self = this;
|
||||
doc.attachEvent("onreadystatechange", function () {
|
||||
self.init_(doc);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
init_: function (doc) {
|
||||
if (doc.readyState == "complete") {
|
||||
// create xmlns
|
||||
if (!doc.namespaces["g_vml_"]) {
|
||||
doc.namespaces.add("g_vml_", "urn:schemas-microsoft-com:vml");
|
||||
}
|
||||
|
||||
// setup default css
|
||||
var ss = doc.createStyleSheet();
|
||||
ss.cssText = "canvas{display:inline-block;overflow:hidden;" +
|
||||
// default size is 300x150 in Gecko and Opera
|
||||
"text-align:left;width:300px;height:150px}" +
|
||||
"g_vml_\\:*{behavior:url(#default#VML)}";
|
||||
|
||||
// find all canvas elements
|
||||
var els = doc.getElementsByTagName("canvas");
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
if (!els[i].getContext) {
|
||||
this.initElement(els[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
fixElement_: function (el) {
|
||||
// in IE before version 5.5 we would need to add HTML: to the tag name
|
||||
// but we do not care about IE before version 6
|
||||
var outerHTML = el.outerHTML;
|
||||
|
||||
var newEl = el.ownerDocument.createElement(outerHTML);
|
||||
// if the tag is still open IE has created the children as siblings and
|
||||
// it has also created a tag with the name "/FOO"
|
||||
if (outerHTML.slice(-2) != "/>") {
|
||||
var tagName = "/" + el.tagName;
|
||||
var ns;
|
||||
// remove content
|
||||
while ((ns = el.nextSibling) && ns.tagName != tagName) {
|
||||
ns.removeNode();
|
||||
}
|
||||
// remove the incorrect closing tag
|
||||
if (ns) {
|
||||
ns.removeNode();
|
||||
}
|
||||
}
|
||||
el.parentNode.replaceChild(newEl, el);
|
||||
return newEl;
|
||||
},
|
||||
|
||||
/**
|
||||
* Public initializes a canvas element so that it can be used as canvas
|
||||
* element from now on. This is called automatically before the page is
|
||||
* loaded but if you are creating elements using createElement you need to
|
||||
* make sure this is called on the element.
|
||||
* @param {HTMLElement} el The canvas element to initialize.
|
||||
* @return {HTMLElement} the element that was created.
|
||||
*/
|
||||
initElement: function (el) {
|
||||
el = this.fixElement_(el);
|
||||
el.getContext = function () {
|
||||
if (this.context_) {
|
||||
return this.context_;
|
||||
}
|
||||
return this.context_ = new CanvasRenderingContext2D_(this);
|
||||
};
|
||||
|
||||
// do not use inline function because that will leak memory
|
||||
el.attachEvent('onpropertychange', onPropertyChange);
|
||||
el.attachEvent('onresize', onResize);
|
||||
|
||||
var attrs = el.attributes;
|
||||
if (attrs.width && attrs.width.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setWidth_(attrs.width.nodeValue);
|
||||
el.style.width = attrs.width.nodeValue + "px";
|
||||
} else {
|
||||
el.width = el.clientWidth;
|
||||
}
|
||||
if (attrs.height && attrs.height.specified) {
|
||||
// TODO: use runtimeStyle and coordsize
|
||||
// el.getContext().setHeight_(attrs.height.nodeValue);
|
||||
el.style.height = attrs.height.nodeValue + "px";
|
||||
} else {
|
||||
el.height = el.clientHeight;
|
||||
}
|
||||
//el.getContext().setCoordsize_()
|
||||
return el;
|
||||
}
|
||||
};
|
||||
|
||||
function onPropertyChange(e) {
|
||||
var el = e.srcElement;
|
||||
|
||||
switch (e.propertyName) {
|
||||
case 'width':
|
||||
el.style.width = el.attributes.width.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
case 'height':
|
||||
el.style.height = el.attributes.height.nodeValue + "px";
|
||||
el.getContext().clearRect();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function onResize(e) {
|
||||
var el = e.srcElement;
|
||||
if (el.firstChild) {
|
||||
el.firstChild.style.width = el.clientWidth + 'px';
|
||||
el.firstChild.style.height = el.clientHeight + 'px';
|
||||
}
|
||||
}
|
||||
|
||||
G_vmlCanvasManager_.init();
|
||||
|
||||
// precompute "00" to "FF"
|
||||
var dec2hex = [];
|
||||
for (var i = 0; i < 16; i++) {
|
||||
for (var j = 0; j < 16; j++) {
|
||||
dec2hex[i * 16 + j] = i.toString(16) + j.toString(16);
|
||||
}
|
||||
}
|
||||
|
||||
function createMatrixIdentity() {
|
||||
return [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
}
|
||||
|
||||
function matrixMultiply(m1, m2) {
|
||||
var result = createMatrixIdentity();
|
||||
|
||||
for (var x = 0; x < 3; x++) {
|
||||
for (var y = 0; y < 3; y++) {
|
||||
var sum = 0;
|
||||
|
||||
for (var z = 0; z < 3; z++) {
|
||||
sum += m1[x][z] * m2[z][y];
|
||||
}
|
||||
|
||||
result[x][y] = sum;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function copyState(o1, o2) {
|
||||
o2.fillStyle = o1.fillStyle;
|
||||
o2.lineCap = o1.lineCap;
|
||||
o2.lineJoin = o1.lineJoin;
|
||||
o2.lineWidth = o1.lineWidth;
|
||||
o2.miterLimit = o1.miterLimit;
|
||||
o2.shadowBlur = o1.shadowBlur;
|
||||
o2.shadowColor = o1.shadowColor;
|
||||
o2.shadowOffsetX = o1.shadowOffsetX;
|
||||
o2.shadowOffsetY = o1.shadowOffsetY;
|
||||
o2.strokeStyle = o1.strokeStyle;
|
||||
o2.arcScaleX_ = o1.arcScaleX_;
|
||||
o2.arcScaleY_ = o1.arcScaleY_;
|
||||
}
|
||||
|
||||
function processStyle(styleString) {
|
||||
var str, alpha = 1;
|
||||
|
||||
styleString = String(styleString);
|
||||
if (styleString.substring(0, 3) == "rgb") {
|
||||
var start = styleString.indexOf("(", 3);
|
||||
var end = styleString.indexOf(")", start + 1);
|
||||
var guts = styleString.substring(start + 1, end).split(",");
|
||||
|
||||
str = "#";
|
||||
for (var i = 0; i < 3; i++) {
|
||||
str += dec2hex[Number(guts[i])];
|
||||
}
|
||||
|
||||
if ((guts.length == 4) && (styleString.substr(3, 1) == "a")) {
|
||||
alpha = guts[3];
|
||||
}
|
||||
} else {
|
||||
str = styleString;
|
||||
}
|
||||
|
||||
return [str, alpha];
|
||||
}
|
||||
|
||||
function processLineCap(lineCap) {
|
||||
switch (lineCap) {
|
||||
case "butt":
|
||||
return "flat";
|
||||
case "round":
|
||||
return "round";
|
||||
case "square":
|
||||
default:
|
||||
return "square";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class implements CanvasRenderingContext2D interface as described by
|
||||
* the WHATWG.
|
||||
* @param {HTMLElement} surfaceElement The element that the 2D context should
|
||||
* be associated with
|
||||
*/
|
||||
function CanvasRenderingContext2D_(surfaceElement) {
|
||||
this.m_ = createMatrixIdentity();
|
||||
|
||||
this.mStack_ = [];
|
||||
this.aStack_ = [];
|
||||
this.currentPath_ = [];
|
||||
|
||||
// Canvas context properties
|
||||
this.strokeStyle = "#000";
|
||||
this.fillStyle = "#000";
|
||||
|
||||
this.lineWidth = 1;
|
||||
this.lineJoin = "miter";
|
||||
this.lineCap = "butt";
|
||||
this.miterLimit = Z * 1;
|
||||
this.globalAlpha = 1;
|
||||
this.canvas = surfaceElement;
|
||||
|
||||
var el = surfaceElement.ownerDocument.createElement('div');
|
||||
el.style.width = surfaceElement.clientWidth + 'px';
|
||||
el.style.height = surfaceElement.clientHeight + 'px';
|
||||
el.style.overflow = 'hidden';
|
||||
el.style.position = 'absolute';
|
||||
surfaceElement.appendChild(el);
|
||||
|
||||
this.element_ = el;
|
||||
this.arcScaleX_ = 1;
|
||||
this.arcScaleY_ = 1;
|
||||
}
|
||||
|
||||
var contextPrototype = CanvasRenderingContext2D_.prototype;
|
||||
contextPrototype.clearRect = function() {
|
||||
this.element_.innerHTML = "";
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.beginPath = function() {
|
||||
// TODO: Branch current matrix so that save/restore has no effect
|
||||
// as per safari docs.
|
||||
|
||||
this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.moveTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "moveTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.lineTo = function(aX, aY) {
|
||||
this.currentPath_.push({type: "lineTo", x: aX, y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.bezierCurveTo = function(aCP1x, aCP1y,
|
||||
aCP2x, aCP2y,
|
||||
aX, aY) {
|
||||
this.currentPath_.push({type: "bezierCurveTo",
|
||||
cp1x: aCP1x,
|
||||
cp1y: aCP1y,
|
||||
cp2x: aCP2x,
|
||||
cp2y: aCP2y,
|
||||
x: aX,
|
||||
y: aY});
|
||||
this.currentX_ = aX;
|
||||
this.currentY_ = aY;
|
||||
};
|
||||
|
||||
contextPrototype.quadraticCurveTo = function(aCPx, aCPy, aX, aY) {
|
||||
// the following is lifted almost directly from
|
||||
// http://developer.mozilla.org/en/docs/Canvas_tutorial:Drawing_shapes
|
||||
var cp1x = this.currentX_ + 2.0 / 3.0 * (aCPx - this.currentX_);
|
||||
var cp1y = this.currentY_ + 2.0 / 3.0 * (aCPy - this.currentY_);
|
||||
var cp2x = cp1x + (aX - this.currentX_) / 3.0;
|
||||
var cp2y = cp1y + (aY - this.currentY_) / 3.0;
|
||||
this.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, aX, aY);
|
||||
};
|
||||
|
||||
contextPrototype.arc = function(aX, aY, aRadius,
|
||||
aStartAngle, aEndAngle, aClockwise) {
|
||||
aRadius *= Z;
|
||||
var arcType = aClockwise ? "at" : "wa";
|
||||
|
||||
var xStart = aX + (mc(aStartAngle) * aRadius) - Z2;
|
||||
var yStart = aY + (ms(aStartAngle) * aRadius) - Z2;
|
||||
|
||||
var xEnd = aX + (mc(aEndAngle) * aRadius) - Z2;
|
||||
var yEnd = aY + (ms(aEndAngle) * aRadius) - Z2;
|
||||
|
||||
// IE won't render arches drawn counter clockwise if xStart == xEnd.
|
||||
if (xStart == xEnd && !aClockwise) {
|
||||
xStart += 0.125; // Offset xStart by 1/80 of a pixel. Use something
|
||||
// that can be represented in binary
|
||||
}
|
||||
|
||||
this.currentPath_.push({type: arcType,
|
||||
x: aX,
|
||||
y: aY,
|
||||
radius: aRadius,
|
||||
xStart: xStart,
|
||||
yStart: yStart,
|
||||
xEnd: xEnd,
|
||||
yEnd: yEnd});
|
||||
|
||||
};
|
||||
|
||||
contextPrototype.rect = function(aX, aY, aWidth, aHeight) {
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
};
|
||||
|
||||
contextPrototype.strokeRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.stroke();
|
||||
};
|
||||
|
||||
contextPrototype.fillRect = function(aX, aY, aWidth, aHeight) {
|
||||
// Will destroy any existing path (same as FF behaviour)
|
||||
this.beginPath();
|
||||
this.moveTo(aX, aY);
|
||||
this.lineTo(aX + aWidth, aY);
|
||||
this.lineTo(aX + aWidth, aY + aHeight);
|
||||
this.lineTo(aX, aY + aHeight);
|
||||
this.closePath();
|
||||
this.fill();
|
||||
};
|
||||
|
||||
contextPrototype.createLinearGradient = function(aX0, aY0, aX1, aY1) {
|
||||
var gradient = new CanvasGradient_("gradient");
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.createRadialGradient = function(aX0, aY0,
|
||||
aR0, aX1,
|
||||
aY1, aR1) {
|
||||
var gradient = new CanvasGradient_("gradientradial");
|
||||
gradient.radius1_ = aR0;
|
||||
gradient.radius2_ = aR1;
|
||||
gradient.focus_.x = aX0;
|
||||
gradient.focus_.y = aY0;
|
||||
return gradient;
|
||||
};
|
||||
|
||||
contextPrototype.drawImage = function (image, var_args) {
|
||||
var dx, dy, dw, dh, sx, sy, sw, sh;
|
||||
|
||||
// to find the original width we overide the width and height
|
||||
var oldRuntimeWidth = image.runtimeStyle.width;
|
||||
var oldRuntimeHeight = image.runtimeStyle.height;
|
||||
image.runtimeStyle.width = 'auto';
|
||||
image.runtimeStyle.height = 'auto';
|
||||
|
||||
// get the original size
|
||||
var w = image.width;
|
||||
var h = image.height;
|
||||
|
||||
// and remove overides
|
||||
image.runtimeStyle.width = oldRuntimeWidth;
|
||||
image.runtimeStyle.height = oldRuntimeHeight;
|
||||
|
||||
if (arguments.length == 3) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
sx = sy = 0;
|
||||
sw = dw = w;
|
||||
sh = dh = h;
|
||||
} else if (arguments.length == 5) {
|
||||
dx = arguments[1];
|
||||
dy = arguments[2];
|
||||
dw = arguments[3];
|
||||
dh = arguments[4];
|
||||
sx = sy = 0;
|
||||
sw = w;
|
||||
sh = h;
|
||||
} else if (arguments.length == 9) {
|
||||
sx = arguments[1];
|
||||
sy = arguments[2];
|
||||
sw = arguments[3];
|
||||
sh = arguments[4];
|
||||
dx = arguments[5];
|
||||
dy = arguments[6];
|
||||
dw = arguments[7];
|
||||
dh = arguments[8];
|
||||
} else {
|
||||
throw "Invalid number of arguments";
|
||||
}
|
||||
|
||||
var d = this.getCoords_(dx, dy);
|
||||
|
||||
var w2 = sw / 2;
|
||||
var h2 = sh / 2;
|
||||
|
||||
var vmlStr = [];
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
// For some reason that I've now forgotten, using divs didn't work
|
||||
vmlStr.push(' <g_vml_:group',
|
||||
' coordsize="', Z * W, ',', Z * H, '"',
|
||||
' coordorigin="0,0"' ,
|
||||
' style="width:', W, ';height:', H, ';position:absolute;');
|
||||
|
||||
// If filters are necessary (rotation exists), create them
|
||||
// filters are bog-slow, so only create them if abbsolutely necessary
|
||||
// The following check doesn't account for skews (which don't exist
|
||||
// in the canvas spec (yet) anyway.
|
||||
|
||||
if (this.m_[0][0] != 1 || this.m_[0][1]) {
|
||||
var filter = [];
|
||||
|
||||
// Note the 12/21 reversal
|
||||
filter.push("M11='", this.m_[0][0], "',",
|
||||
"M12='", this.m_[1][0], "',",
|
||||
"M21='", this.m_[0][1], "',",
|
||||
"M22='", this.m_[1][1], "',",
|
||||
"Dx='", mr(d.x / Z), "',",
|
||||
"Dy='", mr(d.y / Z), "'");
|
||||
|
||||
// Bounding box calculation (need to minimize displayed area so that
|
||||
// filters don't waste time on unused pixels.
|
||||
var max = d;
|
||||
var c2 = this.getCoords_(dx + dw, dy);
|
||||
var c3 = this.getCoords_(dx, dy + dh);
|
||||
var c4 = this.getCoords_(dx + dw, dy + dh);
|
||||
|
||||
max.x = Math.max(max.x, c2.x, c3.x, c4.x);
|
||||
max.y = Math.max(max.y, c2.y, c3.y, c4.y);
|
||||
|
||||
vmlStr.push("padding:0 ", mr(max.x / Z), "px ", mr(max.y / Z),
|
||||
"px 0;filter:progid:DXImageTransform.Microsoft.Matrix(",
|
||||
filter.join(""), ", sizingmethod='clip');");
|
||||
} else {
|
||||
vmlStr.push("top:", mr(d.y / Z), "px;left:", mr(d.x / Z), "px;");
|
||||
}
|
||||
|
||||
vmlStr.push(' ">' ,
|
||||
'<g_vml_:image src="', image.src, '"',
|
||||
' style="width:', Z * dw, ';',
|
||||
' height:', Z * dh, ';"',
|
||||
' cropleft="', sx / w, '"',
|
||||
' croptop="', sy / h, '"',
|
||||
' cropright="', (w - sx - sw) / w, '"',
|
||||
' cropbottom="', (h - sy - sh) / h, '"',
|
||||
' />',
|
||||
'</g_vml_:group>');
|
||||
|
||||
this.element_.insertAdjacentHTML("BeforeEnd",
|
||||
vmlStr.join(""));
|
||||
};
|
||||
|
||||
contextPrototype.stroke = function(aFill) {
|
||||
var lineStr = [];
|
||||
var lineOpen = false;
|
||||
var a = processStyle(aFill ? this.fillStyle : this.strokeStyle);
|
||||
var color = a[0];
|
||||
var opacity = a[1] * this.globalAlpha;
|
||||
|
||||
var W = 10;
|
||||
var H = 10;
|
||||
|
||||
lineStr.push('<g_vml_:shape',
|
||||
' fillcolor="', color, '"',
|
||||
' filled="', Boolean(aFill), '"',
|
||||
' style="position:absolute;width:', W, ';height:', H, ';"',
|
||||
' coordorigin="0 0" coordsize="', Z * W, ' ', Z * H, '"',
|
||||
' stroked="', !aFill, '"',
|
||||
' strokeweight="', this.lineWidth, '"',
|
||||
' strokecolor="', color, '"',
|
||||
' path="');
|
||||
|
||||
var newSeq = false;
|
||||
var min = {x: null, y: null};
|
||||
var max = {x: null, y: null};
|
||||
|
||||
for (var i = 0; i < this.currentPath_.length; i++) {
|
||||
var p = this.currentPath_[i];
|
||||
|
||||
if (p.type == "moveTo") {
|
||||
lineStr.push(" m ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "lineTo") {
|
||||
lineStr.push(" l ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
lineStr.push(mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "close") {
|
||||
lineStr.push(" x ");
|
||||
} else if (p.type == "bezierCurveTo") {
|
||||
lineStr.push(" c ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var c1 = this.getCoords_(p.cp1x, p.cp1y);
|
||||
var c2 = this.getCoords_(p.cp2x, p.cp2y);
|
||||
lineStr.push(mr(c1.x), ",", mr(c1.y), ",",
|
||||
mr(c2.x), ",", mr(c2.y), ",",
|
||||
mr(c.x), ",", mr(c.y));
|
||||
} else if (p.type == "at" || p.type == "wa") {
|
||||
lineStr.push(" ", p.type, " ");
|
||||
var c = this.getCoords_(p.x, p.y);
|
||||
var cStart = this.getCoords_(p.xStart, p.yStart);
|
||||
var cEnd = this.getCoords_(p.xEnd, p.yEnd);
|
||||
|
||||
lineStr.push(mr(c.x - this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y - this.arcScaleY_ * p.radius), " ",
|
||||
mr(c.x + this.arcScaleX_ * p.radius), ",",
|
||||
mr(c.y + this.arcScaleY_ * p.radius), " ",
|
||||
mr(cStart.x), ",", mr(cStart.y), " ",
|
||||
mr(cEnd.x), ",", mr(cEnd.y));
|
||||
}
|
||||
|
||||
|
||||
// TODO: Following is broken for curves due to
|
||||
// move to proper paths.
|
||||
|
||||
// Figure out dimensions so we can do gradient fills
|
||||
// properly
|
||||
if(c) {
|
||||
if (min.x == null || c.x < min.x) {
|
||||
min.x = c.x;
|
||||
}
|
||||
if (max.x == null || c.x > max.x) {
|
||||
max.x = c.x;
|
||||
}
|
||||
if (min.y == null || c.y < min.y) {
|
||||
min.y = c.y;
|
||||
}
|
||||
if (max.y == null || c.y > max.y) {
|
||||
max.y = c.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
lineStr.push(' ">');
|
||||
|
||||
if (typeof this.fillStyle == "object") {
|
||||
var focus = {x: "50%", y: "50%"};
|
||||
var width = (max.x - min.x);
|
||||
var height = (max.y - min.y);
|
||||
var dimension = (width > height) ? width : height;
|
||||
|
||||
focus.x = mr((this.fillStyle.focus_.x / width) * 100 + 50) + "%";
|
||||
focus.y = mr((this.fillStyle.focus_.y / height) * 100 + 50) + "%";
|
||||
|
||||
var colors = [];
|
||||
|
||||
// inside radius (%)
|
||||
if (this.fillStyle.type_ == "gradientradial") {
|
||||
var inside = (this.fillStyle.radius1_ / dimension * 100);
|
||||
|
||||
// percentage that outside radius exceeds inside radius
|
||||
var expansion = (this.fillStyle.radius2_ / dimension * 100) - inside;
|
||||
} else {
|
||||
var inside = 0;
|
||||
var expansion = 100;
|
||||
}
|
||||
|
||||
var insidecolor = {offset: null, color: null};
|
||||
var outsidecolor = {offset: null, color: null};
|
||||
|
||||
// We need to sort 'colors' by percentage, from 0 > 100 otherwise ie
|
||||
// won't interpret it correctly
|
||||
this.fillStyle.colors_.sort(function (cs1, cs2) {
|
||||
return cs1.offset - cs2.offset;
|
||||
});
|
||||
|
||||
for (var i = 0; i < this.fillStyle.colors_.length; i++) {
|
||||
var fs = this.fillStyle.colors_[i];
|
||||
|
||||
colors.push( (fs.offset * expansion) + inside, "% ", fs.color, ",");
|
||||
|
||||
if (fs.offset > insidecolor.offset || insidecolor.offset == null) {
|
||||
insidecolor.offset = fs.offset;
|
||||
insidecolor.color = fs.color;
|
||||
}
|
||||
|
||||
if (fs.offset < outsidecolor.offset || outsidecolor.offset == null) {
|
||||
outsidecolor.offset = fs.offset;
|
||||
outsidecolor.color = fs.color;
|
||||
}
|
||||
}
|
||||
colors.pop();
|
||||
|
||||
lineStr.push('<g_vml_:fill',
|
||||
' color="', outsidecolor.color, '"',
|
||||
' color2="', insidecolor.color, '"',
|
||||
' type="', this.fillStyle.type_, '"',
|
||||
' focusposition="', focus.x, ', ', focus.y, '"',
|
||||
' colors="', colors.join(""), '"',
|
||||
' opacity="', opacity, '" />');
|
||||
} else if (aFill) {
|
||||
lineStr.push('<g_vml_:fill color="', color, '" opacity="', opacity, '" />');
|
||||
} else {
|
||||
lineStr.push(
|
||||
'<g_vml_:stroke',
|
||||
' opacity="', opacity,'"',
|
||||
' joinstyle="', this.lineJoin, '"',
|
||||
' miterlimit="', this.miterLimit, '"',
|
||||
' endcap="', processLineCap(this.lineCap) ,'"',
|
||||
' weight="', this.lineWidth, 'px"',
|
||||
' color="', color,'" />'
|
||||
);
|
||||
}
|
||||
|
||||
lineStr.push("</g_vml_:shape>");
|
||||
|
||||
this.element_.insertAdjacentHTML("beforeEnd", lineStr.join(""));
|
||||
|
||||
//this.currentPath_ = [];
|
||||
};
|
||||
|
||||
contextPrototype.fill = function() {
|
||||
this.stroke(true);
|
||||
};
|
||||
|
||||
contextPrototype.closePath = function() {
|
||||
this.currentPath_.push({type: "close"});
|
||||
};
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
contextPrototype.getCoords_ = function(aX, aY) {
|
||||
return {
|
||||
x: Z * (aX * this.m_[0][0] + aY * this.m_[1][0] + this.m_[2][0]) - Z2,
|
||||
y: Z * (aX * this.m_[0][1] + aY * this.m_[1][1] + this.m_[2][1]) - Z2
|
||||
}
|
||||
};
|
||||
|
||||
contextPrototype.save = function() {
|
||||
var o = {};
|
||||
copyState(this, o);
|
||||
this.aStack_.push(o);
|
||||
this.mStack_.push(this.m_);
|
||||
this.m_ = matrixMultiply(createMatrixIdentity(), this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.restore = function() {
|
||||
copyState(this.aStack_.pop(), this);
|
||||
this.m_ = this.mStack_.pop();
|
||||
};
|
||||
|
||||
contextPrototype.translate = function(aX, aY) {
|
||||
var m1 = [
|
||||
[1, 0, 0],
|
||||
[0, 1, 0],
|
||||
[aX, aY, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.rotate = function(aRot) {
|
||||
var c = mc(aRot);
|
||||
var s = ms(aRot);
|
||||
|
||||
var m1 = [
|
||||
[c, s, 0],
|
||||
[-s, c, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
contextPrototype.scale = function(aX, aY) {
|
||||
this.arcScaleX_ *= aX;
|
||||
this.arcScaleY_ *= aY;
|
||||
var m1 = [
|
||||
[aX, 0, 0],
|
||||
[0, aY, 0],
|
||||
[0, 0, 1]
|
||||
];
|
||||
|
||||
this.m_ = matrixMultiply(m1, this.m_);
|
||||
};
|
||||
|
||||
/******** STUBS ********/
|
||||
contextPrototype.clip = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.arcTo = function() {
|
||||
// TODO: Implement
|
||||
};
|
||||
|
||||
contextPrototype.createPattern = function() {
|
||||
return new CanvasPattern_;
|
||||
};
|
||||
|
||||
// Gradient / Pattern Stubs
|
||||
function CanvasGradient_(aType) {
|
||||
this.type_ = aType;
|
||||
this.radius1_ = 0;
|
||||
this.radius2_ = 0;
|
||||
this.colors_ = [];
|
||||
this.focus_ = {x: 0, y: 0};
|
||||
}
|
||||
|
||||
CanvasGradient_.prototype.addColorStop = function(aOffset, aColor) {
|
||||
aColor = processStyle(aColor);
|
||||
this.colors_.push({offset: 1-aOffset, color: aColor});
|
||||
};
|
||||
|
||||
function CanvasPattern_() {}
|
||||
|
||||
// set up externs
|
||||
G_vmlCanvasManager = G_vmlCanvasManager_;
|
||||
CanvasRenderingContext2D = CanvasRenderingContext2D_;
|
||||
CanvasGradient = CanvasGradient_;
|
||||
CanvasPattern = CanvasPattern_;
|
||||
|
||||
})();
|
||||
|
||||
} // if
|
||||
BIN
usr/local/www/themes/nervecenter/images/icons/icon_block_add.gif
Normal file
BIN
usr/local/www/themes/nervecenter/images/icons/icon_block_add.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 B |
BIN
usr/local/www/themes/nervecenter/images/icons/icon_pass_add.gif
Normal file
BIN
usr/local/www/themes/nervecenter/images/icons/icon_pass_add.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 183 B |
Loading…
Reference in New Issue
Block a user