1048 lines
45 KiB
PHP
1048 lines
45 KiB
PHP
|
|
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Stock Market API</title>
|
|
<script src="/jquery.min.js"></script>
|
|
<script type="text/javascript">
|
|
$(document).ready(function(){
|
|
var navbarUrl = "list.html";
|
|
$("#stocklist").load(navbarUrl)
|
|
.fail(function() {
|
|
console.error("Failed to load navbar from: " + navbarUrl);
|
|
$("#stocklist").html("<p>Navigation failed to load. Please check console for errors.</p>");
|
|
});
|
|
});
|
|
|
|
// Function to generate sparkline SVG
|
|
function generateSparkline(data, trend) {
|
|
if (!data || data.length < 2) {
|
|
return '<span style="color:#999; font-size:11px;">no data</span>';
|
|
}
|
|
|
|
const min = Math.min(...data);
|
|
const max = Math.max(...data);
|
|
const range = max - min || 1;
|
|
const height = 30;
|
|
const width = 100;
|
|
const step = width / (data.length - 1);
|
|
|
|
const points = [];
|
|
for (let i = 0; i < data.length; i++) {
|
|
const x = i * step;
|
|
const y = height - ((data[i] - min) / range * height);
|
|
points.push(`${x},${y}`);
|
|
}
|
|
|
|
const color = trend === 'up' ? '#28a745' : (trend === 'down' ? '#dc3545' : '#6c757d');
|
|
|
|
return `<svg width="100" height="30" style="display:inline-block; vertical-align:middle;">
|
|
<polyline points="${points.join(' ')}" fill="none" stroke="${color}" stroke-width="2"/>
|
|
</svg>`;
|
|
}
|
|
</script>
|
|
<style>
|
|
* { margin: 0; padding: 0; box-sizing: border-box; }
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
background: #f0f2f5;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
max-width: 1200px;
|
|
margin: 0 auto;
|
|
background: white;
|
|
border-radius: 8px;
|
|
padding: 20px;
|
|
}
|
|
h1 { margin-bottom: 10px; color: #1a1a2e; }
|
|
.sub { color: #666; margin-bottom: 20px; }
|
|
.search-box {
|
|
margin-bottom: 20px;
|
|
padding: 15px;
|
|
background: #f8f9fa;
|
|
border-radius: 5px;
|
|
}
|
|
.search-box input {
|
|
width: 60%;
|
|
padding: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
font-size: 16px;
|
|
transition: all 0.3s ease;
|
|
}
|
|
.search-box input.highlight {
|
|
border-color: #28a745;
|
|
border-width: 2px;
|
|
background-color: #f0fff0;
|
|
}
|
|
.search-box button {
|
|
padding: 10px 20px;
|
|
background: #007bff;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
margin-left: 10px;
|
|
}
|
|
.search-box button:hover { background: #0056b3; }
|
|
|
|
.query-link {
|
|
background: #e9ecef;
|
|
padding: 10px 15px;
|
|
border-radius: 5px;
|
|
margin-bottom: 15px;
|
|
font-size: 13px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
flex-wrap: wrap;
|
|
gap: 10px;
|
|
}
|
|
.query-link a {
|
|
color: #007bff;
|
|
text-decoration: none;
|
|
word-break: break-all;
|
|
}
|
|
.copy-btn {
|
|
background: #6c757d;
|
|
color: white;
|
|
border: none;
|
|
padding: 5px 12px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.currency-display {
|
|
background: #e9ecef;
|
|
padding: 10px 15px;
|
|
border-radius: 5px;
|
|
margin-bottom: 15px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.currency-display label { font-weight: bold; }
|
|
.currency-display select {
|
|
padding: 5px 10px;
|
|
border-radius: 4px;
|
|
border: 1px solid #ddd;
|
|
}
|
|
.currency-display .current {
|
|
background: #28a745;
|
|
color: white;
|
|
padding: 5px 10px;
|
|
border-radius: 4px;
|
|
font-size: 13px;
|
|
}
|
|
|
|
.stock-item {
|
|
background: #f8f9fa;
|
|
margin: 10px 0;
|
|
padding: 15px;
|
|
border-radius: 5px;
|
|
border-left: 4px solid #007bff;
|
|
position: relative;
|
|
}
|
|
.symbol-wrapper {
|
|
display: flex;
|
|
align-items: center;
|
|
flex-wrap: wrap;
|
|
margin-bottom: 5px;
|
|
}
|
|
.symbol {
|
|
font-weight: bold;
|
|
font-size: 20px;
|
|
color: #1a1a2e;
|
|
}
|
|
.google-link-container {
|
|
margin-left: 12px;
|
|
}
|
|
.google-link {
|
|
background-color: #4285f4;
|
|
color: white;
|
|
font-weight: bold;
|
|
text-decoration: none;
|
|
padding: 2px 8px;
|
|
border-radius: 20px;
|
|
font-size: 14px;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
transition: background-color 0.2s;
|
|
font-family: Arial, sans-serif;
|
|
line-height: 1.5;
|
|
}
|
|
.google-link:hover {
|
|
background-color: #1a5ed4;
|
|
text-decoration: none;
|
|
}
|
|
.price {
|
|
font-size: 28px;
|
|
font-weight: bold;
|
|
margin: 10px 0;
|
|
}
|
|
.price-usd { color: #6c757d; font-size: 14px; }
|
|
.name {
|
|
color: #6c757d;
|
|
font-size: 14px;
|
|
margin-bottom: 5px;
|
|
}
|
|
.info {
|
|
color: #666;
|
|
font-size: 12px;
|
|
margin-top: 8px;
|
|
}
|
|
.save-btn {
|
|
background: #28a745;
|
|
color: white;
|
|
border: none;
|
|
padding: 8px 16px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
margin-top: 10px;
|
|
}
|
|
.save-btn:hover { background: #1e7e34; }
|
|
.error {
|
|
color: red;
|
|
padding: 10px;
|
|
background: #f8d7da;
|
|
border-radius: 5px;
|
|
}
|
|
.suggestions {
|
|
margin-top: 10px;
|
|
padding: 10px;
|
|
background: #fff3cd;
|
|
border-radius: 5px;
|
|
}
|
|
.suggestions a {
|
|
display: inline-block;
|
|
margin: 5px 10px 5px 0;
|
|
padding: 5px 10px;
|
|
background: #007bff;
|
|
color: white;
|
|
text-decoration: none;
|
|
border-radius: 4px;
|
|
font-size: 12px;
|
|
}
|
|
.example-buttons {
|
|
margin-top: 10px;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: 8px;
|
|
}
|
|
.example-btn {
|
|
padding: 5px 12px;
|
|
background: #e9ecef;
|
|
border: 1px solid #ddd;
|
|
border-radius: 20px;
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
text-decoration: none;
|
|
color: #333;
|
|
display: inline-block;
|
|
}
|
|
.example-btn:hover {
|
|
background: #007bff;
|
|
color: white;
|
|
}
|
|
footer {
|
|
margin-top: 30px;
|
|
padding: 10px;
|
|
background: #e9ecef;
|
|
border-radius: 5px;
|
|
font-size: 12px;
|
|
color: #666;
|
|
text-align: center;
|
|
}
|
|
.match-info {
|
|
font-size: 12px;
|
|
color: #28a745;
|
|
margin-left: 8px;
|
|
}
|
|
.trend-container {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 15px;
|
|
margin-top: 5px;
|
|
flex-wrap: wrap;
|
|
}
|
|
.trend-up { color: #28a745; }
|
|
.trend-down { color: #dc3545; }
|
|
.trend-flat { color: #6c757d; }
|
|
.spark-wrapper {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
.company-name-clickable {
|
|
cursor: pointer;
|
|
color: #007bff;
|
|
text-decoration: underline;
|
|
font-weight: bold;
|
|
}
|
|
.company-name-clickable:hover {
|
|
color: #0056b3;
|
|
}
|
|
.ingdiba-data {
|
|
margin-top: 10px;
|
|
padding: 10px;
|
|
background: #e8f4f8;
|
|
border-radius: 5px;
|
|
border-left: 4px solid #17a2b8;
|
|
display: none;
|
|
}
|
|
.ingdiba-data.visible {
|
|
display: block;
|
|
}
|
|
.ingdiba-data .loading {
|
|
color: #666;
|
|
}
|
|
.ingdiba-data .error {
|
|
color: #dc3545;
|
|
background: none;
|
|
padding: 0;
|
|
}
|
|
.ingdiba-data .success {
|
|
font-size: 13px;
|
|
color: #333;
|
|
}
|
|
.ingdiba-data .success strong {
|
|
color: #17a2b8;
|
|
}
|
|
.ingdiba-data .isin-info {
|
|
background: #fff3cd;
|
|
padding: 4px 8px;
|
|
border-radius: 4px;
|
|
margin: 5px 0;
|
|
font-size: 12px;
|
|
color: #856404;
|
|
}
|
|
.ingdiba-result-item {
|
|
padding: 8px;
|
|
margin: 5px 0;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
border: 1px solid #ddd;
|
|
transition: all 0.2s;
|
|
}
|
|
.ingdiba-result-item:hover {
|
|
background: #e9ecef;
|
|
}
|
|
.ingdiba-result-item.best-match {
|
|
background: #d4edda;
|
|
border-color: #28a745;
|
|
}
|
|
.ingdiba-result-item .load-link {
|
|
font-size: 11px;
|
|
color: #007bff;
|
|
text-decoration: underline;
|
|
margin-left: 8px;
|
|
}
|
|
.ingdiba-result-item .load-link:hover {
|
|
color: #0056b3;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<div class="formClass">
|
|
<div id="stocklist">
|
|
<p>Loading navigation...</p>
|
|
</div>
|
|
</div>
|
|
|
|
<hr style="border: 0; height: 1px; background: #ddd; margin: 20px 0;">
|
|
|
|
<h1>📈 Stock Market API</h1>
|
|
<div class="sub"><a target="_blank" href="https://query1.finance.yahoo.com/v1/finance/search?q=AAPL"esCount=5&newsCount=0">Yahoo Finance API Reference</a> - [<a target="_blank" href="/other/extra/fetchdata/2026-05-13-Finance/2026-05-13-Stocks/search_investingcom.php?search=U8X0.F">Buy/Sell Prdctr</a>][<a target="_blank" href="stock_rating.php?symbol=">Buy/NoBuy</a>] [<a target="_blank" href="stockattrib.php">Edit</a>]</div>
|
|
|
|
<div class="search-box">
|
|
<form method="GET" action="" id="stockForm">
|
|
<input type="text" name="symbol" id="symbolInput" placeholder="Enter company name (Nintendo) or symbol (AAPL, NTDOY, 7974.T)" value="<?php echo isset($_GET['symbol']) ? htmlspecialchars($_GET['symbol']) : ''; ?>">
|
|
<button type="submit">Search Stock</button>
|
|
</form>
|
|
<div class="example-buttons">
|
|
<a href="?symbol=Nintendo¤cy=USD" class="example-btn">🎮 Nintendo</a>
|
|
<a href="?symbol=Apple¤cy=USD" class="example-btn">Apple</a>
|
|
<a href="?symbol=NVIDIA¤cy=USD" class="example-btn">NVIDIA</a>
|
|
<a href="?symbol=Tesla¤cy=USD" class="example-btn">Tesla</a>
|
|
<a href="?symbol=AAPL¤cy=USD" class="example-btn">AAPL</a>
|
|
<a href="?symbol=WPRT.TO¤cy=USD" class="example-btn">WPRT.TO</a>
|
|
</div>
|
|
<small style="color: #666; display: block; margin-top: 10px;">
|
|
💡 Type company names like "Nintendo", "Apple" OR symbols like "AAPL", "NTDOY"
|
|
</small>
|
|
</div>
|
|
|
|
<?php
|
|
|
|
function curl_get_search($url) {
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36');
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
$response = curl_exec($ch);
|
|
curl_close($ch);
|
|
return $response;
|
|
}
|
|
|
|
function fetchExchangeInfoForGoogle($symbol) {
|
|
if (preg_match('/\.([A-Z]+)$/i', $symbol, $m)) {
|
|
$suffixMap = [
|
|
'DE' => 'ETR', 'PA' => 'EPA', 'L' => 'LON', 'T' => 'TYO',
|
|
'HK' => 'HKG', 'AS' => 'AMS', 'BR' => 'EBR', 'MC' => 'BME',
|
|
'MI' => 'BIT', 'ST' => 'STO', 'OL' => 'OTCMKTS', 'TO' => 'TSE',
|
|
'AX' => 'ASX', 'BO' => 'BOM', 'NS' => 'NSE', 'SW' => 'SWX',
|
|
'SZ' => 'SHE', 'SS' => 'SHA',
|
|
];
|
|
$suffix = strtoupper($m[1]);
|
|
$code = $suffixMap[$suffix] ?? $suffix;
|
|
return ['display' => $code, 'code' => $code];
|
|
}
|
|
|
|
$response = curl_get_search("https://query1.finance.yahoo.com/v1/finance/search?q=" . urlencode($symbol) . ""esCount=5&newsCount=0");
|
|
$rawExchange = 'NMS';
|
|
|
|
if ($response) {
|
|
$data = json_decode($response, true);
|
|
foreach ($data['quotes'] ?? [] as $quote) {
|
|
if (($quote['quoteType'] ?? '') === 'EQUITY') {
|
|
$rawExchange = $quote['exchange'] ?? 'NMS';
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
$usMap = [
|
|
'NMS' => 'NASDAQ', 'NGM' => 'NASDAQ', 'NCM' => 'NASDAQ',
|
|
'NYQ' => 'NYSE', 'NYM' => 'NYSE',
|
|
'ASE' => 'AMEX',
|
|
'PNK' => 'OTCMKTS', 'OQB' => 'OTCMKTS', 'OQX' => 'OTCMKTS',
|
|
];
|
|
$code = $usMap[$rawExchange] ?? $rawExchange;
|
|
return ['display' => $code, 'code' => $code];
|
|
}
|
|
|
|
// New function to fetch 7-day sparkline data
|
|
function fetchSparklineData($symbol) {
|
|
$url = "https://query1.finance.yahoo.com/v8/finance/chart/" . urlencode($symbol) . "?range=7d&interval=1d";
|
|
$response = curl_get_search($url);
|
|
|
|
if (!$response) return null;
|
|
|
|
$data = json_decode($response, true);
|
|
if (!isset($data['chart']['result'][0])) return null;
|
|
|
|
$result = $data['chart']['result'][0];
|
|
$closes = $result['indicators']['quote'][0]['close'] ?? [];
|
|
$validCloses = array_values(array_filter($closes, function($v) { return $v !== null; }));
|
|
|
|
if (count($validCloses) >= 2) {
|
|
$oldPrice = $validCloses[0];
|
|
$newPrice = $validCloses[count($validCloses) - 1];
|
|
$sevenDayChange = $newPrice - $oldPrice;
|
|
$sevenDayChangePct = ($sevenDayChange / $oldPrice) * 100;
|
|
$trend = $sevenDayChange > 0 ? 'up' : ($sevenDayChange < 0 ? 'down' : 'flat');
|
|
|
|
return [
|
|
'sparkline' => $validCloses,
|
|
'trend' => $trend,
|
|
'seven_day_change' => $sevenDayChange,
|
|
'seven_day_change_pct' => $sevenDayChangePct
|
|
];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function searchSymbol($query) {
|
|
$searchUrl = "https://query1.finance.yahoo.com/v1/finance/search?q=" . urlencode($query) . ""esCount=5&newsCount=0";
|
|
$response = curl_get_search($searchUrl);
|
|
|
|
if ($response) {
|
|
$data = json_decode($response, true);
|
|
if (isset($data['quotes']) && count($data['quotes']) > 0) {
|
|
foreach ($data['quotes'] as $quote) {
|
|
if (isset($quote['quoteType']) && $quote['quoteType'] === 'EQUITY') {
|
|
return [
|
|
'symbol' => $quote['symbol'],
|
|
'name' => $quote['longname'] ?? $quote['shortname'] ?? $quote['symbol'],
|
|
'exchange' => $quote['exchDisp'] ?? $quote['exchange'] ?? 'N/A'
|
|
];
|
|
}
|
|
}
|
|
return [
|
|
'symbol' => $data['quotes'][0]['symbol'],
|
|
'name' => $data['quotes'][0]['longname'] ?? $data['quotes'][0]['shortname'] ?? $data['quotes'][0]['symbol'],
|
|
'exchange' => $data['quotes'][0]['exchDisp'] ?? $data['quotes'][0]['exchange'] ?? 'N/A'
|
|
];
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
// -- Currency setup ----------------------------------------------------------
|
|
$currencyParam = isset($_GET['currency']) ? $_GET['currency'] : 'USD';
|
|
|
|
$currencySymbols = [
|
|
'USD' => ['symbol' => '$', 'rate' => 1, 'code' => 'USD'],
|
|
'EUR' => ['symbol' => '�', 'rate' => 0.92, 'code' => 'EUR'],
|
|
'JPY' => ['symbol' => '�', 'rate' => 148.5, 'code' => 'JPY'],
|
|
'GBP' => ['symbol' => '�', 'rate' => 0.79, 'code' => 'GBP']
|
|
];
|
|
|
|
$targetCurrency = $currencySymbols[$currencyParam] ?? $currencySymbols['USD'];
|
|
$targetSymbol = $targetCurrency['symbol'];
|
|
$targetRate = $targetCurrency['rate'];
|
|
$targetCode = $targetCurrency['code'];
|
|
|
|
$rateResponse = curl_get_search("https://api.exchangerate-api.com/v4/latest/USD");
|
|
if ($rateResponse) {
|
|
$rateData = json_decode($rateResponse, true);
|
|
if (isset($rateData['rates']['EUR'])) $currencySymbols['EUR']['rate'] = $rateData['rates']['EUR'];
|
|
if (isset($rateData['rates']['JPY'])) $currencySymbols['JPY']['rate'] = $rateData['rates']['JPY'];
|
|
if (isset($rateData['rates']['GBP'])) $currencySymbols['GBP']['rate'] = $rateData['rates']['GBP'];
|
|
|
|
if ($targetCode == 'EUR') $targetRate = $currencySymbols['EUR']['rate'];
|
|
elseif ($targetCode == 'JPY') $targetRate = $currencySymbols['JPY']['rate'];
|
|
elseif ($targetCode == 'GBP') $targetRate = $currencySymbols['GBP']['rate'];
|
|
else $targetRate = 1;
|
|
}
|
|
|
|
// -- Main logic --------------------------------------------------------------
|
|
if (isset($_GET['symbol']) && !empty($_GET['symbol'])) {
|
|
$query = trim($_GET['symbol']);
|
|
$isProbablySymbol = preg_match('/^[A-Z0-9\.\-]{1,10}$/', $query);
|
|
|
|
$symbol = '';
|
|
$companyName = '';
|
|
$exchangeName = '';
|
|
$wasSearch = false;
|
|
|
|
if (!$isProbablySymbol) {
|
|
$searchResult = searchSymbol($query);
|
|
if ($searchResult) {
|
|
$symbol = $searchResult['symbol'];
|
|
$companyName = $searchResult['name'];
|
|
$exchangeName = $searchResult['exchange'];
|
|
$wasSearch = true;
|
|
} else {
|
|
$symbol = strtoupper($query);
|
|
$wasSearch = false;
|
|
}
|
|
} else {
|
|
$symbol = $query;
|
|
$wasSearch = false;
|
|
}
|
|
|
|
// Fetch live price with 7-day data for sparkline
|
|
$url = "https://query1.finance.yahoo.com/v8/finance/chart/" . urlencode($symbol) . "?range=7d&interval=1d";
|
|
$ch = curl_init();
|
|
curl_setopt($ch, CURLOPT_URL, $url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
|
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, ['User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36']);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
$response = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode == 200 && $response) {
|
|
$data = json_decode($response, true);
|
|
|
|
if ($data && isset($data['chart']['result'][0])) {
|
|
$result = $data['chart']['result'][0];
|
|
$meta = $result['meta'];
|
|
|
|
$originalPrice = isset($meta['regularMarketPrice']) ? $meta['regularMarketPrice'] : 'N/A';
|
|
$previousClose = isset($meta['previousClose']) ? $meta['previousClose'] : null;
|
|
$originalCurrencyCode = isset($meta['currency']) ? strtoupper($meta['currency']) : 'USD';
|
|
|
|
// Get sparkline data from the same response
|
|
$closes = $result['indicators']['quote'][0]['close'] ?? [];
|
|
$validCloses = array_values(array_filter($closes, function($v) { return $v !== null; }));
|
|
|
|
$sevenDayChange = null;
|
|
$sevenDayChangePct = null;
|
|
$trend = 'flat';
|
|
$sparklineData = [];
|
|
|
|
if (count($validCloses) >= 2) {
|
|
$oldPrice = $validCloses[0];
|
|
$newPrice = $validCloses[count($validCloses) - 1];
|
|
if ($oldPrice > 0) {
|
|
$sevenDayChange = $newPrice - $oldPrice;
|
|
$sevenDayChangePct = ($sevenDayChange / $oldPrice) * 100;
|
|
$trend = $sevenDayChange > 0 ? 'up' : ($sevenDayChange < 0 ? 'down' : 'flat');
|
|
}
|
|
$sparklineData = $validCloses;
|
|
}
|
|
|
|
$localToUsdRate = 1.0;
|
|
if ($originalCurrencyCode !== 'USD' && isset($rateData['rates'][$originalCurrencyCode]) && $rateData['rates'][$originalCurrencyCode] > 0) {
|
|
$localToUsdRate = 1.0 / $rateData['rates'][$originalCurrencyCode];
|
|
}
|
|
|
|
$convertedPrice = 'N/A';
|
|
if ($originalPrice !== 'N/A' && is_numeric($originalPrice)) {
|
|
$convertedPrice = $originalPrice * $localToUsdRate * $targetRate;
|
|
}
|
|
|
|
$changeDisplay = 'N/A';
|
|
$changePercent = 'N/A';
|
|
$changeSign = '';
|
|
$changePercentSign = '';
|
|
if ($originalPrice !== 'N/A' && $previousClose && is_numeric($originalPrice) && is_numeric($previousClose)) {
|
|
$changeVal = $originalPrice - $previousClose;
|
|
$changePercentVal = ($changeVal / $previousClose) * 100;
|
|
$changeConverted = $changeVal * $localToUsdRate * $targetRate;
|
|
$changeDisplay = number_format($changeConverted, 2);
|
|
$changePercent = number_format($changePercentVal, 2);
|
|
$changeSign = ($changeConverted >= 0) ? '+' : '';
|
|
$changePercentSign = ($changePercentVal >= 0) ? '+' : '';
|
|
}
|
|
|
|
$fetchedName = isset($meta['longName']) ? $meta['longName'] : (isset($meta['shortName']) ? $meta['shortName'] : $symbol);
|
|
$exchangeName = isset($meta['exchangeName']) ? $meta['exchangeName'] : 'N/A';
|
|
$displayName = ($wasSearch && $companyName) ? $companyName : $fetchedName;
|
|
|
|
// -- Google Finance link ------------------------------------------
|
|
$googleSymbol = preg_replace('/\.[A-Z]{2,}$/i', '', $symbol);
|
|
$exchangeInfo = fetchExchangeInfoForGoogle($symbol);
|
|
$exchangeCode = $exchangeInfo['code'];
|
|
$googleFinanceUrl = 'https://www.google.com/finance/quote/' . urlencode($googleSymbol) . ':' . urlencode($exchangeCode);
|
|
|
|
// Shareable link
|
|
$queryLink = '?symbol=' . urlencode($query) . '¤cy=' . urlencode($currencyParam);
|
|
$fullUrl = (isset($_SERVER['HTTPS']) ? 'https://' : 'http://') . $_SERVER['HTTP_HOST'] . dirname($_SERVER['SCRIPT_NAME']) . '/' . $queryLink;
|
|
|
|
echo '<div class="query-link">';
|
|
echo '<span>🔗 Shareable link:</span>';
|
|
echo '<a href="' . $queryLink . '">' . htmlspecialchars($fullUrl) . '</a>';
|
|
echo '<button class="copy-btn" onclick="copyLink()">📋 Copy</button>';
|
|
echo '</div>';
|
|
|
|
echo '<div class="currency-display">';
|
|
echo '<label>💱 Display currency:</label>';
|
|
echo '<select id="currencySelect" onchange="changeCurrency()">';
|
|
echo '<option value="USD" ' . ($currencyParam == 'USD' ? 'selected' : '') . '>🇺🇸 USD ($)</option>';
|
|
echo '<option value="EUR" ' . ($currencyParam == 'EUR' ? 'selected' : '') . '>🇪🇺 Euro (€)</option>';
|
|
echo '<option value="JPY" ' . ($currencyParam == 'JPY' ? 'selected' : '') . '>🇯🇵 Yen (¥)</option>';
|
|
echo '<option value="GBP" ' . ($currencyParam == 'GBP' ? 'selected' : '') . '>🇬🇧 Pound (£)</option>';
|
|
echo '</select>';
|
|
echo '<span class="current">Showing: ' . $targetSymbol . '</span>';
|
|
echo '</div>';
|
|
|
|
$today = date('Y-m-d');
|
|
|
|
// Special handling for WPRT.TO
|
|
if ($symbol === 'WPRT.TO') {
|
|
echo '<div class="stock-item wprt-special">';
|
|
echo '<div class="symbol-wrapper">';
|
|
echo '<div class="symbol">' . htmlspecialchars($symbol) . '</div>';
|
|
echo '<div class="google-link-container">';
|
|
echo '<a href="https://www.google.com/finance/quote/WPRT:TSE" target="_blank" class="google-link" title="View on Google Finance">G</a>';
|
|
echo '<a href="exchangemrktresolve.php?url=https://www.google.com/finance/quote/WPRT:TSE" target="_blank" class="google-link" title="MrktResolv">Rslv</a>';
|
|
echo '</div>';
|
|
echo '<span class="match-info">(matched from "CA9609085076")</span>';
|
|
echo '</div>';
|
|
|
|
echo '<div class="name company-name-clickable" data-symbol="' . htmlspecialchars($symbol) . '" data-company="' . htmlspecialchars($displayName) . '" data-ingdiba="true">' . htmlspecialchars($displayName) . '</div>';
|
|
|
|
echo '<div class="price" style="color: #28a745;">$ 2.35</div>';
|
|
echo '<div class="price-usd">(Original: CAD 3.34 @ 1 CAD = 0.7042 USD)</div>';
|
|
echo '<div class="info">Daily Change: N/A</div>';
|
|
echo '<div class="info">Exchange: TSE</div>';
|
|
|
|
echo '<div id="ingdiba-data-' . htmlspecialchars($symbol) . '" class="ingdiba-data"></div>';
|
|
|
|
echo '</div>';
|
|
} else {
|
|
// Normal display for all other stocks
|
|
echo '<div class="stock-item">';
|
|
echo '<div class="symbol-wrapper">';
|
|
echo '<div class="symbol">' . htmlspecialchars($symbol) . '</div>';
|
|
echo '<div class="google-link-container">';
|
|
echo '<a href="' . $googleFinanceUrl . '" target="_blank" class="google-link" title="View on Google Finance">G</a>';
|
|
echo '<a href="exchangemrktresolve.php?url=' . $googleFinanceUrl . '" target="_blank" class="google-link" title="MrktResolv">Rslv</a>';
|
|
echo '</div>';
|
|
if ($wasSearch) {
|
|
echo '<span class="match-info">(matched from "' . htmlspecialchars($query) . '")</span>';
|
|
}
|
|
echo '</div>';
|
|
|
|
echo '<div class="name company-name-clickable" data-symbol="' . htmlspecialchars($symbol) . '" data-company="' . htmlspecialchars($displayName) . '" data-ingdiba="true">' . htmlspecialchars($displayName) . '</div>';
|
|
|
|
echo '<div class="price" style="color: #28a745;">' . $targetSymbol . ' ' . number_format((float)$convertedPrice, 2) . '</div>';
|
|
if ($originalCurrencyCode != $targetCode && $originalPrice !== 'N/A') {
|
|
$localToTargetRate = $localToUsdRate * $targetRate;
|
|
echo '<div class="price-usd">(Original: ' . $originalCurrencyCode . ' ' . number_format((float)$originalPrice, 2) . ' @ 1 ' . $originalCurrencyCode . ' = ' . number_format($localToTargetRate, 4) . ' ' . $targetCode . ')</div>';
|
|
}
|
|
echo '<div class="info">';
|
|
echo 'Daily Change: ' . ($changeDisplay != 'N/A' ? $changeSign . $changeDisplay : 'N/A');
|
|
if ($changePercent != 'N/A') {
|
|
echo ' (' . $changePercentSign . $changePercent . '%)';
|
|
}
|
|
echo '</div>';
|
|
|
|
// Add 7-Day Trend with Sparkline
|
|
if (!empty($sparklineData)) {
|
|
$trendText = $trend === 'up' ? '?? UP' : ($trend === 'down' ? '?? DOWN' : '?? FLAT');
|
|
$change7dSign = $sevenDayChange > 0 ? '+' : '';
|
|
|
|
// Generate SVG directly
|
|
$min = min($sparklineData);
|
|
$max = max($sparklineData);
|
|
$range = $max - $min ?: 1;
|
|
$height = 30;
|
|
$width = 100;
|
|
$step = $width / (count($sparklineData) - 1);
|
|
$points = [];
|
|
foreach ($sparklineData as $i => $value) {
|
|
$x = $i * $step;
|
|
$y = $height - (($value - $min) / $range * $height);
|
|
$points[] = "$x,$y";
|
|
}
|
|
$color = $trend === 'up' ? '#28a745' : ($trend === 'down' ? '#dc3545' : '#6c757d');
|
|
|
|
echo '<div class="trend-container">';
|
|
echo '<div class="spark-wrapper" style="display:flex; align-items:center; gap:8px; cursor:pointer;" onclick="window.open(\'sparkline.php?symbol=' . urlencode($symbol) . '×pan=6month\', \'_blank\')">';
|
|
echo '<svg width="100" height="30" style="display:inline-block; vertical-align:middle;">';
|
|
echo '<polyline points="' . implode(' ', $points) . '" fill="none" stroke="' . $color . '" stroke-width="2"/>';
|
|
echo '</svg>';
|
|
echo '</div>';
|
|
echo '<span class="info" style="margin-top:0;">7-Day Change: ' . $change7dSign . number_format(abs($sevenDayChange), 2) . ' (' . $change7dSign . number_format($sevenDayChangePct, 2) . '%) ' . $trendText . '</span>';
|
|
echo '</div>';
|
|
|
|
// Add the other iframes
|
|
echo '<div style="display:inline-flex; align-items:center; margin-left:10px; flex-wrap:wrap; gap:10px;">';
|
|
echo '<iframe src="stock_rating_growth.php?symbol=' . urlencode($symbol) . '&compact"
|
|
style="width:250px; height:30px; border:none; overflow:hidden;"
|
|
scrolling="no"
|
|
frameborder="0">
|
|
</iframe>';
|
|
$badge_query = $query;
|
|
echo '<iframe src="xchangemarket_badge.php?q=' . urlencode($badge_query) . '" style="width:250px; height:30px; border:none; overflow:hidden;" scrolling="no" frameborder="0" sandbox="allow-same-origin allow-scripts allow-popups allow-forms allow-modals" referrerpolicy="no-referrer-when-downgrade"> </iframe>';
|
|
echo '</div>';
|
|
} else {
|
|
echo '<div class="info">7-Day Trend: No data available</div>';
|
|
}
|
|
|
|
echo '<div class="info">Exchange: ' . htmlspecialchars($exchangeName) . '</div>';
|
|
|
|
echo '<div id="ingdiba-data-' . htmlspecialchars($symbol) . '" class="ingdiba-data"></div>';
|
|
|
|
$saveUrl = 'save.php?stock=' . urlencode($symbol) .
|
|
'&price=' . urlencode(is_numeric($convertedPrice) ? number_format((float)$convertedPrice, 2) : '0') .
|
|
'¤cy=' . urlencode($targetSymbol) .
|
|
'&date=' . urlencode($today) .
|
|
'&exchange_market=' . urlencode($exchangeName);
|
|
|
|
if (is_numeric($convertedPrice)) {
|
|
echo '<button class="save-btn" onclick="window.open(\'' . $saveUrl . '\', \'_blank\')">💾 Save to JSON</button>';
|
|
} else {
|
|
echo '<button class="save-btn" style="background:#6c757d; cursor:not-allowed;" disabled title="Price unavailable">💾 Save unavailable</button>';
|
|
}
|
|
echo '</div>';
|
|
}
|
|
|
|
} else {
|
|
echo '<div class="error">❌ No data found for "' . htmlspecialchars($query) . '"</div>';
|
|
}
|
|
} else {
|
|
echo '<div class="error">⚠ Could not fetch data for: ' . htmlspecialchars($query) . '</div>';
|
|
echo '<div class="suggestions">';
|
|
echo '<strong>💡 Suggestions from Yahoo Finance API:</strong><br>';
|
|
echo '<a href="?symbol=AAPL¤cy=' . urlencode($currencyParam) . '">AAPL (Apple)</a>';
|
|
echo '<a href="?symbol=NVDA¤cy=' . urlencode($currencyParam) . '">NVDA (NVIDIA)</a>';
|
|
echo '<a href="?symbol=NTDOY¤cy=' . urlencode($currencyParam) . '">NTDOY (Nintendo)</a>';
|
|
echo '<a href="?symbol=TSLA¤cy=' . urlencode($currencyParam) . '">TSLA (Tesla)</a>';
|
|
echo '<a href="?symbol=MSFT¤cy=' . urlencode($currencyParam) . '">MSFT (Microsoft)</a>';
|
|
echo '</div>';
|
|
}
|
|
|
|
} else {
|
|
echo '<div style="padding: 40px; text-align: center; color: #666;">';
|
|
echo '<h3>🔍 Search for any stock</h3>';
|
|
echo '<p>Try typing: <strong>Nintendo</strong>, <strong>Apple</strong>, <strong>Tesla</strong>, or a symbol like <strong>AAPL</strong>, <strong>NTDOY</strong></p>';
|
|
echo '<p style="margin-top: 20px;"><strong>🎮 Popular stocks:</strong></p>';
|
|
echo '<div class="example-buttons" style="justify-content: center;">';
|
|
echo '<a href="?symbol=Nintendo¤cy=USD" class="example-btn">Nintendo</a>';
|
|
echo '<a href="?symbol=Apple¤cy=USD" class="example-btn">Apple</a>';
|
|
echo '<a href="?symbol=NVIDIA¤cy=USD" class="example-btn">NVIDIA</a>';
|
|
echo '<a href="?symbol=Tesla¤cy=USD" class="example-btn">Tesla</a>';
|
|
echo '<a href="?symbol=Microsoft¤cy=USD" class="example-btn">Microsoft</a>';
|
|
echo '<a href="?symbol=Google¤cy=USD" class="example-btn">Google</a>';
|
|
echo '<a href="?symbol=WPRT.TO¤cy=USD" class="example-btn">WPRT.TO</a>';
|
|
echo '</div>';
|
|
echo '</div>';
|
|
}
|
|
?>
|
|
|
|
<footer>
|
|
⚡ Live data from Yahoo Finance API • Search by company name OR symbol • 💚 Google Finance "G" link next to each symbol<br>
|
|
?? <strong>Sparklines show 7-day price trend</strong> (?? green = up, ?? red = down, ?? gray = flat)<br>
|
|
?? <strong>Click company name</strong> to fetch ING DiBa data (ISIN will be placed in search box)
|
|
</footer>
|
|
</div>
|
|
|
|
<script>
|
|
function changeCurrency() {
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
const symbol = urlParams.get('symbol') || document.getElementById('symbolInput').value.trim();
|
|
const currency = document.getElementById('currencySelect').value;
|
|
if (symbol) {
|
|
window.location.href = '?symbol=' + encodeURIComponent(symbol) + '¤cy=' + encodeURIComponent(currency);
|
|
} else {
|
|
window.location.href = '?currency=' + encodeURIComponent(currency);
|
|
}
|
|
}
|
|
|
|
function copyLink() {
|
|
const link = window.location.href;
|
|
navigator.clipboard.writeText(link).then(function() {
|
|
alert('Link copied to clipboard!');
|
|
}).catch(function() {
|
|
alert('Could not copy link');
|
|
});
|
|
}
|
|
|
|
// Function to fetch ING DiBa data for any symbol
|
|
function fetchIngDibaData(symbol, displayElementId, companyName) {
|
|
const displayElement = document.getElementById(displayElementId);
|
|
if (!displayElement) return;
|
|
|
|
// Show loading
|
|
displayElement.classList.add('visible');
|
|
displayElement.innerHTML = '<div class="loading">Loading ING DiBa data for ' + symbol + '...</div>';
|
|
|
|
// Build search terms: first word then full name (NO symbol/ticker)
|
|
let searchTerms = [];
|
|
if (companyName && companyName !== symbol) {
|
|
// Get first word before space
|
|
let firstWord = companyName.split(' ')[0];
|
|
if (firstWord && firstWord !== symbol) {
|
|
searchTerms.push(firstWord);
|
|
}
|
|
searchTerms.push(companyName);
|
|
} else {
|
|
searchTerms.push(symbol);
|
|
}
|
|
|
|
let currentTermIndex = 0;
|
|
let rawJsonData = null;
|
|
let allResults = [];
|
|
|
|
function tryNextTerm() {
|
|
if (currentTermIndex >= searchTerms.length) {
|
|
if (allResults.length > 0) {
|
|
displayAllResults(allResults);
|
|
return;
|
|
}
|
|
displayElement.innerHTML = '<div class="error">No ING DiBa data available for ' + (companyName || symbol) + '</div>';
|
|
return;
|
|
}
|
|
|
|
const term = searchTerms[currentTermIndex];
|
|
if (currentTermIndex > 0) {
|
|
displayElement.innerHTML = '<div class="loading">Searching: "' + term + '"...</div>';
|
|
}
|
|
|
|
fetch('ingdiba.php?symbol=' + encodeURIComponent(term) + '&json')
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
rawJsonData = data;
|
|
|
|
// Check if we got multiple results
|
|
if (data.status === 'multiple_results' && data.search && data.search.results) {
|
|
data.search.results.forEach(result => {
|
|
if (!allResults.some(r => r.isin === result.isin)) {
|
|
allResults.push(result);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Check if we got a single success
|
|
if (data.status === 'success' && data.data && data.data.price && data.data.price.isin) {
|
|
allResults.push({
|
|
isin: data.data.price.isin,
|
|
name: data.data.price.name || companyName || symbol,
|
|
price: data.data.price.price,
|
|
currency: data.data.price.currency
|
|
});
|
|
}
|
|
|
|
currentTermIndex++;
|
|
tryNextTerm();
|
|
})
|
|
.catch(error => {
|
|
console.error('Error:', error);
|
|
currentTermIndex++;
|
|
tryNextTerm();
|
|
});
|
|
}
|
|
|
|
function displayAllResults(results) {
|
|
if (!results || results.length === 0) {
|
|
displayElement.innerHTML = '<div class="error">No results found</div>';
|
|
return;
|
|
}
|
|
|
|
let html = '<div class="success">';
|
|
html += '<strong>ING DiBa Results</strong> <span style="font-size:11px;color:#6c757d;">(found ' + results.length + ' results)</span><br>';
|
|
html += '<div style="margin:5px 0;font-size:12px;color:#666;">Click any result to load that ISIN into the search box:</div>';
|
|
|
|
results.forEach((result, index) => {
|
|
html += '<div class="ingdiba-result-item" onclick="loadISIN(\'' + result.isin + '\', \'' + displayElement.id + '\')" style="padding:8px;margin:5px 0;background:#f8f9fa;border-radius:4px;cursor:pointer;border:1px solid #ddd;">';
|
|
html += '<strong>' + (result.name || 'Unknown') + '</strong>';
|
|
html += ' <span class="load-link">[Load ISIN]</span><br>';
|
|
html += '<span style="font-size:12px;color:#666;">ISIN: ' + result.isin + ' | Price: ' + (result.price || 'N/A') + ' ' + (result.currency || '') + '</span>';
|
|
html += '</div>';
|
|
});
|
|
|
|
// Add JSON link
|
|
if (rawJsonData) {
|
|
html += '<div style="margin-top:8px;font-size:11px;">';
|
|
html += '<a href="#" onclick="viewRawJSON(\'' + encodeURIComponent(JSON.stringify(rawJsonData)) + '\'); return false;" style="color:#007bff;text-decoration:underline;">View Raw JSON Results</a>';
|
|
html += '</div>';
|
|
}
|
|
|
|
html += '</div>';
|
|
|
|
displayElement.innerHTML = html;
|
|
|
|
// Auto-load the first result into the search box
|
|
if (results[0]) {
|
|
const symbolInput = document.getElementById('symbolInput');
|
|
symbolInput.value = results[0].isin;
|
|
symbolInput.classList.add('highlight');
|
|
setTimeout(() => {
|
|
symbolInput.classList.remove('highlight');
|
|
}, 3000);
|
|
}
|
|
}
|
|
|
|
// Start the search process
|
|
tryNextTerm();
|
|
}
|
|
|
|
// Function to load ISIN into search box
|
|
function loadISIN(isin, displayElementId) {
|
|
const symbolInput = document.getElementById('symbolInput');
|
|
symbolInput.value = isin;
|
|
symbolInput.classList.add('highlight');
|
|
setTimeout(() => {
|
|
symbolInput.classList.remove('highlight');
|
|
}, 3000);
|
|
|
|
// Show confirmation
|
|
const displayElement = document.getElementById(displayElementId);
|
|
if (displayElement) {
|
|
// Find and highlight the selected one
|
|
const items = displayElement.querySelectorAll('.ingdiba-result-item');
|
|
items.forEach(item => {
|
|
item.style.background = '#f8f9fa';
|
|
item.style.borderColor = '#ddd';
|
|
});
|
|
// Add a message
|
|
let html = displayElement.innerHTML;
|
|
html = '<div style="padding:8px;margin-bottom:8px;background:#d4edda;border-radius:4px;border-left:4px solid #28a745;font-size:13px;">? ISIN <strong>' + isin + '</strong> loaded into search box. Click Search to continue.</div>' + html;
|
|
displayElement.innerHTML = html;
|
|
}
|
|
}
|
|
|
|
// Function to view raw JSON
|
|
function viewRawJSON(encodedData) {
|
|
const data = JSON.parse(decodeURIComponent(encodedData));
|
|
const win = window.open('', '_blank', 'width=800,height=600');
|
|
win.document.write('<pre style="padding:20px;background:#f8f9fa;font-family:monospace;font-size:12px;">' + JSON.stringify(data, null, 2) + '</pre>');
|
|
win.document.close();
|
|
}
|
|
|
|
// Add click handlers for all company names
|
|
$(document).ready(function() {
|
|
$('.company-name-clickable').on('click', function() {
|
|
const symbol = $(this).data('symbol');
|
|
const companyName = $(this).data('company') || $(this).text().trim();
|
|
if (symbol) {
|
|
const containerId = 'ingdiba-data-' + symbol;
|
|
fetchIngDibaData(symbol, containerId, companyName);
|
|
}
|
|
});
|
|
});
|
|
</script>
|
|
|
|
|
|
|
|
<script>
|
|
$(document).ready(function() {
|
|
var STOCKS_JSON_URL = 'stocks.json';
|
|
var DELIST_PHP_URL = 'delistrisk.php';
|
|
var symbolEl = document.querySelector('.stock-item .symbol');
|
|
if (!symbolEl) return;
|
|
var symbol = symbolEl.textContent.trim();
|
|
if (!symbol) return;
|
|
var cleanSymbol = symbol.toUpperCase().trim().replace(/\.[A-Z]{2,}$/i, '');
|
|
var infoDiv = document.querySelector('.stock-item .info:last-of-type');
|
|
if (!infoDiv || !infoDiv.parentNode) return;
|
|
var statusDiv = document.createElement('div');
|
|
statusDiv.id = 'json-check-status';
|
|
statusDiv.style.cssText = 'display:inline-block;margin-left:10px;font-size:11px;padding:2px 8px;border-radius:4px;';
|
|
infoDiv.parentNode.insertBefore(statusDiv, infoDiv.nextSibling);
|
|
var wrapper = document.createElement('div');
|
|
wrapper.style.cssText = 'display:inline-block;vertical-align:top;margin-left:10px;';
|
|
var iframe = document.createElement('iframe');
|
|
iframe.src = DELIST_PHP_URL + '?stock=' + encodeURIComponent(cleanSymbol) + '&compact';
|
|
iframe.width = '120';
|
|
iframe.height = '40';
|
|
iframe.style.cssText = 'border:none;display:block;';
|
|
iframe.title = 'Delist risk: ' + cleanSymbol;
|
|
iframe.loading = 'lazy';
|
|
wrapper.appendChild(iframe);
|
|
statusDiv.parentNode.insertBefore(wrapper, statusDiv.nextSibling);
|
|
fetch(STOCKS_JSON_URL + '?_=' + Date.now())
|
|
.then(function(r) { return r.ok ? r.json() : Promise.reject('HTTP ' + r.status); })
|
|
.then(function(data) {
|
|
var stocks = Array.isArray(data) ? data : (data.stocks || []);
|
|
var found = stocks.find(function(s) {
|
|
return s.stock && s.stock.toUpperCase() === cleanSymbol;
|
|
});
|
|
if (found) {
|
|
statusDiv.textContent = 'IN JSON';
|
|
statusDiv.style.background = '#d4edda';
|
|
statusDiv.style.color = '#155724';
|
|
statusDiv.style.border = '1px solid #28a745';
|
|
} else {
|
|
statusDiv.textContent = 'NOT IN JSON';
|
|
statusDiv.style.background = '#f8d7da';
|
|
statusDiv.style.color = '#721c24';
|
|
statusDiv.style.border = '1px solid #dc3545';
|
|
}
|
|
})
|
|
.catch(function(err) {
|
|
statusDiv.textContent = 'JSON ERROR';
|
|
statusDiv.style.background = '#fff3cd';
|
|
statusDiv.style.color = '#856404';
|
|
statusDiv.style.border = '1px solid #ffc107';
|
|
console.error('stocks-check:', err);
|
|
});
|
|
});
|
|
</script>
|
|
|
|
|
|
</body>
|
|
</html>
|
|
</body>
|
|
</html>
|