Introduction
Before I begin, you should know that this method utilizes PHP, JavaScript, xhtml, and CSS. You should have at least an intermediate-level understanding of the said technologies before using this implementation method. Also, before you begin, it is wise to download and install an up-to-date version of the php_browscap.ini file on your server. Without going into much detail, you can find information on the issue here and here. And here's an up-to-date php_browscap.ini.
I have almost never found a need to provide an alternative <input type="file">, but today I found one. The question I had is, "Why do people need to know the path on their own computer to the file they've chosen? Don't they only need to know the file name?" The answer is simple: All they need is the file name as to provide a double-check. So, the problem arose, "Well, how do I then hide the text-box (or label, as is the case in Apple Safari) and only display the file name?"
The Investigation
I searched the web for possibilities. I found a work-around that replaced the file input field with a text box and image, while the input field still remained—although transparent and working. That didn't quite solve my problem as the whole 'file path' thing still remained. I found, using JavaScript, a way to extract the file name and insert that into a by-side label (<div> in this case), but that still didn't solve the 'file path' issue.
So, I did a little case study in Internet Explorer, Opera, Firefox, and Safari (using their default themes, of course, and under Windows Vista). What I did is captured a screen-shot of each browser with the file input field without any CSS. Then I checked their inferred measurements using each client's respective developer tool/DOM browser. What I extracted is the width
and height
dimensions, as well as the left
and top
positions of each browser's file input field's 'browse' button. That information could easily allow me to use CSS absolute positioning inside a relatively-positioned element to hide the text-box of the file input field.
The problem was the inconsistent implementation of the <input type="file"> between each Web client. Internet Explorer, in my mind, has the more politically correct implementation, whereas Firefox, Opera, and particularly Safari mangle what I would picture the perfect implementation to be. In Safari's case, they did away with the text-box altogether and included a not-to-spec text label that only includes the file name as well as an icon representing their inferred file type of the chosen file. That was particularly shoddy in my mind, although their reasoning behind the endeavor is dually noted and understood (an attempt to avoid potential security flaws).
For the three aforementioned browsers, other than Internet Explorer, the text displayed on the 'browse' button was also different, as was the type-face used to display the information. That provides more problems: the width
, height
, left
position, and top
position would differ between the clients respectively and that change could not programmatically be accounted for. Sure, I could use CSS to augment the type-face and font-size, but that doesn't change the fact that word(s) chosen to represent the word "browse" differed, once again.
So, my only alternative was to dynamically write CSS dependent on each browser, which brings me to my point and the implementation I'm about to provide.
The Solution
A long while back—years, if I remember correctly—I wrote a PHP code class that can determine—using PHP's built-in browser functionality and the php_browscap.ini file—the user agent being used on the client side. I had never found a use for it until now: the dynamic creation of browser-specific CSS, not using JavaScript (as JavaScript is sometimes disabled by over-paranoid users). Note that the JavaScript to be used should be safely ignored by such individuals' browsers and merely only provides access to the label we're going to use to display the file to be uploaded to the user.
The xhtml
So, now we get to the code. Here's a look at the base HTML that we'll use to show the <input type="file">:
<form action="/path/to/action/file.php" enctype="multipart/form-data" method="post">
<div class="fileInputContainer">
<input class="fileInput" id="fileInput" name="fileInput" type="file" />
</div>
<div class="fileInputLabel" id="fileInputLabel"></div>
</form>
As you can see, the xhtml is fairly simple in design. Remember: all forms that are to submit files should have an enctype="multipart/form-data" declaration and the method should be set to post
, not get
.
The PHP Code Class ( HYPONIQS_Browser_Lite )
Now that we have the basic skeleton of the xhtml, we have to decide which browser the user is using and how we're going to display the element in question based on that browser. That's where the server-side PHP code class comes in. Here's a look at that class in its simplistic entirety. A few more browsers are provided than we're going to use, but they are involved so that this class can be used inside third-party development environments (namely Adobe Dreamweaver and ActiveState Komodo) that I use regularly. Anyway, the code:
<?php
// Browser_Lite.php
class HYPONIQS_Browser_Lite
{
private $browserTag;
public $browserInfo;
public $isDreamweaver = false;
public $isFirefox = false;
public $isGecko = false;
public $isIE = false;
public $isKomodo = false;
public $isMozilla = false;
public $isNavigator = false;
public $isOpera = false;
public $isSafari = false;
public $isSeaMonkey = false;
public $isUnknown = false;
public $platform;
public $version;
public $versionMajor;
public $versionMinor;
public function __construct()
{
$this->browserTag = $_SERVER['HTTP_USER_AGENT'];
// we must suppress any errors here (if the browscap php.ini
// directive is not set, it throws an E_WARNING)
$this->browserInfo = @get_browser();
$this->getPlatform();
$this->getUA();
$this->getUAVersion();
}
public function getPlatform()
{
// There is a problem, here: this relies on an up-to-date
// php_browscap.ini file on the server-side. This fails
// if the php_browscap.ini is not up-to-date or if the
// path to the php_browscap.ini file is not set in the
// server's php.ini file.
//
// So, as a precaution, we're going to make sure that the
// $this->browserInfo initialized properly. If so, we can
// get the OS the user is on.
if (is_object($this->browserInfo)) {
$this->platform = $this->browserInfo->platform;
// if not, we'll specify an unknown OS
} else {
$this->platform = 'Unknown Operating System';
}
return true;
}
public function getUA()
{
// this represents the class variable $browserTag to minimize code
$bTag = $this->browserTag;
// Internet Explorer uses the Microsoft HTML (MSHTML) rendering engine
if (strstr($bTag, 'MSIE')) {
$this->isIE = true;
// Opera uses their own rendering engine (unnamed)
} elseif (strstr($bTag, 'Opera')) {
$this->isOpera = true;
// Safari uses the KHTML rendering engine
} elseif (strstr($bTag, 'Safari') && !strstr($bTag, 'Dreamweaver')) {
$this->isSafari = true;
// this is just for GP (General Practice)
// check if Adobe Dreamweaver's internal browser
// could be used with something like "$isContentRenderable = false";
//
// Dreamweaver also uses Apple's KHTML rendering engine
} elseif (strstr($bTag, 'MMHttp') || strstr($bTag, "Dreamweaver")) {
$this->isDreamweaver = true;
// all of the following browsers use the Gecko rendering engine
} elseif (strstr($bTag, 'Mozilla') && (!$this->isIE &&
!$this->isOpera &&
!$this->isSafari))
{
$this->isGecko = true;
// check if Netscape Navigator
if (strstr($bTag, 'Navigator')) {
$this->isNavigator = true;
// this is just for GP (General Practice)
// check if ActiveState Komodo's internal browser
// could be used with something like "$isContentRenderable = false";
} elseif (strstr($bTag, 'Komodo')) {
$this->isKomodo = true;
// check if Firefox
} elseif (strstr($bTag, 'Firefox') && (!$this->isNavigator &&
!$this->isKomodo))
{
$this->isFirefox = true;
// check if SeaMonkey
} elseif (strstr($bTag, 'SeaMonkey')) {
$this->isSeaMonkey = true;
// if all else fails, it's the Mozilla Web Browser (father of Firefox)
} else {
$this->isMozilla = true;
}
// no known browser support
} else {
$this->isUnknown = true;
}
return true;
}
public function getUAStrings()
{
if ($this->isIE) {
$engine = 'MSHTML';
$browser = 'Internet Explorer';
} elseif ($this->isOpera) {
$engine = 'Internal (unknown)';
$browser = 'Opera';
} elseif ($this->isSafari) {
$engine = 'KHTML';
$browser = 'Safari';
} elseif ($this->isDreamweaver) {
$engine = 'Adobe; KHTML';
$browser = 'Dreamweaver';
} elseif ($this->isGecko) {
$engine = 'Gecko';
if ($this->isFirefox) {
$browser = 'Mozilla Firefox';
} elseif ($this->isNavigator) {
$browser = 'Netscape Navigator';
} elseif ($this->isKomodo) {
$browser = 'ActiveState Komodo';
} elseif ($this->isSeaMonkey) {
$browser = 'Mozilla SeaMonkey';
} elseif ($this->isMozilla) {
$browser = 'Mozilla';
}
} else {
$engine = 'Unknown';
$browser = 'Unknown or unsupported';
}
return array($browser, $engine);
}
public function getUAVersion()
{
$bTag = $this->browserTag;
$expression = '';
if ($this->isIE) {
$expression = '/MSIE ([\w\d.]*);/';
} elseif ($this->isOpera) {
$expression = '/Opera\/([\w\d.]*)/ ';
} elseif ($this->isSafari) {
$expression = '/Version\/([\w\d.]*)/ ';
} elseif ($this->isDreamweaver) {
if (strstr($bTag, 'Dreamweaver')) {
$expression = '/Dreamweaver\/([\w\d.]*)/ ';
} else {
$expression = '/Version:([\w\d.]*)/ ';
}
} elseif ($this->isGecko) {
if ($this->isFirefox) {
$expression = '/Firefox\/([\w\d.]*)/i';
} elseif ($this->isNavigator) {
$expression = '/Navigator\/([\w\d.]*)/i';
} elseif ($this->isKomodo) {
$expression = '/Komodo\/([\w\d.]*)/i';
} elseif ($this->isMozilla) {
$expression = '/Mozilla\/([\w\d.]*)/i';
} elseif ($this->isSeaMonkey) {
$expression = '/SeaMonkey\/([\w\d.]*)/i';
}
} else {
$this->version = 0;
$this->versionMajor = 0;
$this->versionMinor = 0;
return true;
}
if (preg_match($expression, $bTag)) {
preg_match($expression, $bTag, $matches);
$subVersions = explode('.', $matches[1]);
$this->version = $matches[1];
$this->versionMajor = (isset($subVersions[0])) ? (int) $subVersions[0] : 0;
$this->versionMinor = (isset($subVersions[1])) ? (int) $subVersions[1] : 0;
}
return true;
}
public function __toString()
{
list ($browser, $engine) = $this->getUAStrings();
$html = '<pre>' . "\n";
$html .= str_replace('/', ': ', $this->browserTag) . "\n\n";
$html .= 'Browser: ' . $browser . "\n";
$html .= 'Browser Version: ' . $this->version . "\n";
$html .= 'Version Major: ' . $this->versionMajor . "\n";
$html .= 'Version Minor: ' . $this->versionMinor . "\n";
$html .= 'Rendering Engine: ' . $engine . "\n";
$html .= 'Operating System: ' . $this->platform . "\n";
$html .= '</pre>' . "\n";
return $html;
}
}
?>
Utilizing this class is simply limited to:
<?php
// include the Browser_Lite.php file
require_once 'Browser_Lite.php';
// instantiate the HYPONIQS_Browser_Lite class
$browser = new HYPONIQS_Browser_Lite();
// check if browser is Internet Explorer
if ($browser->isIE) {
// do something here for Internet Explorer
print 'I am Internet Explorer.';
}
?>
The CSS
Now that we have the PHP code class and the xhtml, here's the CSS we'll be using before augmenting it server-side with PHP:
<style charset="utf-8" media="screen" type="text/css">
div.fileInputLabel {
float: left;
margin-left: 5px;
padding-top: 2px;
}
div.fileInputContainer {
position: relative;
overflow: hidden;
float: left;
/**
* Later, we'll use the PHP code class to dynamically add the
* width and height of the file input container
*/
}
div.fileInputContainer input.fileInput {
position: absolute;
/**
* Later, we'll use the PHP code class to dynamically add the
* left and top position to the file input
*/
}
</style>
The PHP Code Class, xhtml, and CSS Together
Unfortunately, all the "bones"—so to speak—of this tiny application do not have any real grounds separately. We need to put them together for the whole thing to work. A few questions we have first, though.
First off, "What is the width
and height
of the 'browse' button in each user agent?" The answer is shockingly simple. Through my own investigation, on Windows Vista, that is, the width
and height
of the 'browse' button is as follows (provided the user agent is using the default UI theme):
- Internet Explorer
width
: 79pxheight
: 18px
- FireFox
width
: 73pxheight
: 22px
- Opera
width
: 72pxheight
: 20px
- Safari
width
: 92pxheight
: 20px
Of course, under other operating systems, such as Apple Mac OSX, previous versions of Microsoft Windows, and any Linux/Unix distribution, these dimensions are subject to change. It is likely, however, that they will not be much different.
The second question we have is, "What are the coordinates for the top-left corner of the 'browse' button from the file input element's bounding box?"
- Internet Explorer
left
: 156pxtop
: 1px
- FireFox
left
: 150pxtop
: 1px
- Opera
left
: 133pxtop
: 1px
- Safari
left
: 1pxtop
: 1px
As you can see, if there's anything these browsers have in common, it is their top
coordinate (this would be on the Y vertices of a plane). Otherwise, each browser has its own rendition of how a file input element should be displayed. That's what we're after.
So, now that we know the dimensions, I'll tell you how this is all aligned, logically speaking. For the most part, the visible area of the file input element will be the button's with by its height
. So, we'll use CSS to position the top-left corner of the button with the top-left corner of the containing <div> element. We'll also set the containing <div> element to be equivalent in width
and height
to the button. This will safely hide all the rest.
There is one other thing I should mention. In Safari, there is an additional 2 pixel margin surrounding the entire <input type="file"> element. Why this is I am uncertain, but it is there. Because of this, we have to account for that additional margin on top
and on the left
of the element. That's why the object is positioned up and back 3 pixels instead of simply 1.
So, here's a look at the PHP at work:
<?php
// include the Browser_Lite.php file
require_once 'Browser_Lite.php';
// instantiate the HYPONIQS_Browser_Lite class
$browser = new HYPONIQS_Browser_Lite();
// We'll use this variable to decide if the browser being used
// is one we're supporting
$supported = false;
// make sure the browser is one we're supporting
if (
// This example uses Windows Vista; we must make sure the user
// is on Windows Vista
$browser->platform == 'WinVista' &&
// Now we just have to make sure that they are on one of the
// supported browsers
($browser->isIE ||
$browser->isFirefox ||
$browser->isOpera ||
$browser->isSafari)
)
{
$supported = true;
}
// check if browser is Internet Explorer
if ($browser->isIE) {
$width = '79px';
$height = '18px';
$top = '-1px';
$left = '-156px';
// check if browser is Firefox
} elseif ($browser->isFirefox) {
$width = '73px';
$height = '22px';
$top = '-1px';
$left = '-150px';
// check if browser is Opera
} elseif ($browser->isOpera) {
$width = '72px';
$height = '20px';
$top = '-1px';
$left = '-133px';
// check if browser is Safari
} elseif ($browser->isSafari) {
$width = '92px';
$height = '20px';
$top = '-3px';
$left = '-3px';
}
?>
That wasn't too bad, was it? Now, augmenting the CSS is a snap.
So, to put it ALL together, we wind up with this:
<?php
// include the Browser_Lite.php file
require_once 'Browser_Lite.php';
// instantiate the HYPONIQS_Browser_Lite class
$browser = new HYPONIQS_Browser_Lite();
// We'll use this variable to decide if the browser being used
// is one we're supporting
$supported = false;
// make sure the browser is one we're supporting
if (
// This example uses Windows Vista; we must make sure the user
// is on Windows Vista
$browser->platform == 'WinVista' &&
// Now we just have to make sure that they are on one of the
// supported browsers
($browser->isIE ||
$browser->isFirefox ||
$browser->isOpera ||
$browser->isSafari)
)
{
$supported = true;
}
// check if browser is Internet Explorer
if ($browser->isIE) {
$width = '79px';
$height = '18px';
$top = '-1px';
$left = '-156px';
// check if browser is Firefox
} elseif ($browser->isFirefox) {
$width = '73px';
$height = '22px';
$top = '-1px';
$left = '-150px';
// check if browser is Opera
} elseif ($browser->isOpera) {
$width = '72px';
$height = '20px';
$top = '-1px';
$left = '-133px';
// check if browser is Safari
} elseif ($browser->isSafari) {
$width = '92px';
$height = '20px';
$top = '-3px';
$left = '-3px';
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Input Type File Experiment</title>
<?php
// Now, we're only including this CSS if the browser being used is supported!
if ($supported) :
?>
<style charset="utf-8" media="screen" type="text/css">
div.fileInputLabel {
float: left;
margin-left: 5px;
padding-top: 2px;
}
div.fileInputContainer {
position: relative;
overflow: hidden;
float: left;
width: <?php echo $width; ?>;
height: <?php echo $height; ?>;
}
div.fileInputContainer input.fileInput {
position: absolute;
left: <?php echo $left; ?>;
top: <?php echo $top; ?>;
}
</style>
<?php
// This is so that we still get a pretty display between the label and the input element
else :
?>
<style charset="utf-8" media="screen" type="text/css">
div.fileInputLabel {
float: left;
margin-left: 5px;
padding-top: 2px;
}
div.fileInputContainer {
position: relative;
float: left;
}
div.fileInputContainer input.fileInput {
}
</style>
<?php
endif;
?>
</head>
<body>
<form action="/path/to/action/file.php" enctype="multipart/form-data" method="post">
<div class="fileInputContainer">
<input class="fileInput" id="fileInput" name="fileInput" type="file" />
</div>
<div class="fileInputLabel" id="fileInputLabel"></div>
</form>
</body>
</html>
Didn't I Mention JavaScript?
Now that we have all that stuff out of the way, there's just one more thing we're lacking: the JavaScript.
I found, not too long ago, a simple JavaScript framework for easily manipulating the DOM. That framework is jQuery: The "Write Less, Do More" JavaScript Library. If you're unfamiliar with it, I suggest you take a look-see. It solves many common issues with cross-browser compatibility and makes writing DOM manipulation JavaScript tasks a snap. I highly recommend this application as I have found it quite useful.
Unfortunately, everything in the following code strictly depends on the inclusion of jQuery in your application. You must have a fundamental understanding of the use of that framework in order to completely understand the following. That said, however, jQuery is lightning-fast to learn and comprehend. If you have any JavaScript experience whatsoever, you should be able to catch on pretty quick.
So, on that note, let me explain what we're going to do. Logically, it is fair to assume that we're finally going to solve the initial problem: "How do I hide the text-box (or label, as is the case in Apple Safari) and only display the file name?" The solution, in its entirety, lacks in complication. First, we get the entire (albeit misguided) file path of the selected file. Closely following, we extract simply the file name and then, finally, display that in the label we created using the xhtml markup <div class="fileInputLabel" id="fileInputLabel"></div>.
jQuery makes this task relatively painless. We'll simply utilize its built-in methods for extracting the value of the file input element and transfer only the file name to the label. And, without further ado, here's the code:
<script charset="utf-8" src="jQuery-1.3.2.js" type="text/javascript"></script>
<script charset="utf-8" type="text/javascript">
// attach the init function to the WINDOW object
/**
* This function attaches an event handler to the file input element's
* onchange event.
*/
this.init = function ()
{
$('#fileInput').change(fileInputChanged);
};
/**
* This function handles the onchange event of the file input element
*/
fileInputChanged = function ()
{
// get the file path of the selected file by way of the
// file input element's value property
var path = $('#fileInput').val();
// create a RegEx object for searching for backslashes in
// the path
var regEx = /\\/g;
// replace all occurrences of backslashes with forward slashes
// (this makes coding easier)
path = path.replace(regEx, '/');
// get the character index number of the last occurrence of
// a forward slash (directory delimiter)
var lastIndexOffset = path.lastIndexOf('/') + 1;
// the length of the final file name string
var length = path.length - lastIndexOffset;
// get the file name by extracting only the text from the last
// occurrence of a forward slash to the end of the path string
var fileName = path.substr(lastIndexOffset, length);
// finally, make the file name appear in the label we created
$('#fileInputLabel').text(fileName);
};
// When the DOCUMENT object has finished loading (all images,
// stylesheets, scripts, and markup, as well as all other external,
// included files), initialize this script.
$(document).ready(
function ()
{
init();
}
);
</script>
Wrapping Things Up
To close the application, here's a look at the completed code:
<?php
// include the Browser_Lite.php file
require_once 'Browser_Lite.php';
// instantiate the HYPONIQS_Browser_Lite class
$browser = new HYPONIQS_Browser_Lite();
// We'll use this variable to decide if the browser being used
// is one we're supporting
$supported = false;
// make sure the browser is one we're supporting
if (
// This example uses Windows Vista; we must make sure the user
// is on Windows Vista
$browser->platform == 'WinVista' &&
// Now we just have to make sure that they are on one of the
// supported browsers
($browser->isIE ||
$browser->isFirefox ||
$browser->isOpera ||
$browser->isSafari)
)
{
$supported = true;
}
// check if browser is Internet Explorer
if ($browser->isIE) {
$width = '79px';
$height = '18px';
$top = '-1px';
$left = '-156px';
// check if browser is Firefox
} elseif ($browser->isFirefox) {
$width = '73px';
$height = '22px';
$top = '-1px';
$left = '-150px';
// check if browser is Opera
} elseif ($browser->isOpera) {
$width = '72px';
$height = '20px';
$top = '-1px';
$left = '-133px';
// check if browser is Safari
} elseif ($browser->isSafari) {
$width = '92px';
$height = '20px';
$top = '-3px';
$left = '-3px';
}
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Input Type File Experiment</title><script charset="utf-8" src="jQuery-1.3.2.js" type="text/javascript"></script>
<script charset="utf-8" type="text/javascript">
// this script will be safely ignored if the user has JavaScript
// disabled or if the browser doesn't support JavaScript
//
// attach the init function to the WINDOW object
/**
* This function attaches an event handler to the file input element's
* onchange event.
*/
this.init = function ()
{
$('#fileInput').change(fileInputChanged);
};
/**
* This function handles the onchange event of the file input element
*/
fileInputChanged = function ()
{
// get the file path of the selected file by way of the
// file input element's value property
var path = $('#fileInput').val();
// create a RegEx object for searching for backslashes in
// the path
var regEx = /\\/g;
// replace all occurrences of backslashes with forward slashes
// (this makes coding easier)
path = path.replace(regEx, '/');
// get the character index number of the last occurrence of
// a forward slash (directory delimiter)
var lastIndexOffset = path.lastIndexOf('/') + 1;
// the length of the final file name string
var length = path.length - lastIndexOffset;
// get the file name by extracting only the text from the last
// occurrence of a forward slash to the end of the path string
var fileName = path.substr(lastIndexOffset, length);
// finally, make the file name appear in the label we created
$('#fileInputLabel').text(fileName);
};
// When the DOCUMENT object has finished loading (all images,
// stylesheets, scripts, and markup, as well as all other external,
// included files), initialize this script.
$(document).ready(
function ()
{
init();
}
);
</script>
<?php
// Now, we're only including this CSS and script if the browser being used is supported!
if ($supported) :
?> <style charset="utf-8" media="screen" type="text/css">
div.fileInputLabel {
float: left;
margin-left: 5px;
padding-top: 2px;
}
div.fileInputContainer {
position: relative;
overflow: hidden;
float: left;
width: <?php echo $width; ?>;
height: <?php echo $height; ?>;
}
div.fileInputContainer input.fileInput {
position: absolute;
left: <?php echo $left; ?>;
top: <?php echo $top; ?>;
}
</style>
<?php
// This is so that we still get a pretty display between the label and the input element
else :
?>
<style charset="utf-8" media="screen" type="text/css">
div.fileInputLabel {
float: left;
margin-left: 5px;
padding-top: 2px;
}
div.fileInputContainer {
position: relative;
float: left;
}
div.fileInputContainer input.fileInput {
}
</style>
<?php
endif;
?>
</head>
<body>
<form action="/path/to/action/file.php" enctype="multipart/form-data" method="post">
<div class="fileInputContainer">
<input class="fileInput" id="fileInput" name="fileInput" type="file" />
</div>
<div class="fileInputLabel" id="fileInputLabel"></div>
</form>
</body>
</html>
In Conclusion
Just for peace of mind, I'd like to once again assure you that this solution, as it stands, is meant only to support the most recent versions of Microsoft Internet Explorer, Apple Safari, Mozilla FireFox, and Opera under the Microsoft Windows Vista operating system. If you'd like to support other browsers and/or operating systems, it would take much more investigation into the behaviors of each browser on each operating system. I am not doing that here as this is little more than a starting point for the average developer. I would imagine that supporting a much wider base would require many more hours of investigation than I have already involved myself in.
All that said, I hope this 'solution', as it were, gives you inspiration to pursue a much more complete application based on this one.
I hold no license on this application, although I do ask that you accredit me with the original idea as well as link back to me if you use my code.
No comments:
Post a Comment