In shoppingcart.js, the other scripts are loaded through a function:
function addJS(name){
if(window.addEventListener){
var el=newEl('script');el.src='/admin/j/'+name;
document.getElementsByTagName('head')[0].appendChild(el);
}else document.write('');
}
addJS('shoppingcart_cartdisplay.js');
in the above, newEl() is just a shortcut to document.createElement()
shoppingcart_init() is defined in shoppingcart_cartdisplay.js.
IE runs the above fine, but Firefox has trouble with it. It looks like that Firefox replaces shoppingcart_init with a pointer to the actual function. This only seems to work if the function has either been defined already, or is in the local file (when you set something equal to something else, the something else must exist so its value can be read – this is different in IE, it seems).
The solution is to create a pseudofunction which will then call the initialisation function. The internal code of the function will not be parsed until it is called, so it doesn’t matter that the initialisation function does not exist yet.
First, a short description of it, for all you people that didn’t read the post:
This hack is a javascript & php script which you simply link to in your <head> section as you would any other external javascript. From then on, you write plain CSS. If you want to give an element a curved corner, then simply specify it in the CSS!
As for native ability – as far as I know, there is only one single browser that can do curved borders.
That browser is, of course, Firefox; the greatest browser in the world.
Unfortunately, not even that great god of a browser gets it all right:
In the example image (which is rendered using Firefox’s native rendering engine – try for your self (Mozilla browsers only)), you can see that the background image is leaking.
I’m currently compiling a build of Mozilla to see if I can get working on a fix for that (unlikely, as my C++ is woeful, but I will try…).
Anyway – look in the example with your own browser and you will see that your own browser can now display it properly.
So, how was it done?
It was a three-step process:
get the css and parse it.
for each element which has a border-radius, remove the border from the element, correcting the size of the element by adding the missing border widths to the elements, and giving the element a custom background image which fakes the border.
create the image
It’s surprising (and there’s probably a universal law that explains it), but the shorter a problem’s description, the more difficult it usually is. Of the three items above, the easiest was the second.
Grabbing the CSS
I spent a week researching how to grab CSS straight from the source before the browser parses it. I gave up after a good read through the W3C’s DOM specs turned up nothing.
Suddenly, the answer was clear: XMLHttpRequest! Why not just grab the actual source, instead of trying to access it through some non-existant DOM interface.
Actually, once I thought of the solution, it was pretty simple… it’s just that I spent so long trying to figure it out that I was damned frustrated.
Parsing the CSS and fixing the elements
Nyeh – read the source and figure it out for yourself 🙂 It’s pretty self-explanatory.
Dean Edwards, as usually, was a life saver. His cssQuery code made it a cinch to grab arrays of each affected element.
The background image
I’m not really a graphical coder, so writing the background painter involved a bit of thought for me.
Basically, the solution, though, was pretty simple again, after I’d worked it out.
You need to paint two images – the first one is a mask, and the second is the border. The mask is for figuring out what should be transparent and what should not.
First, you build the mask. This involves painting a rectangle in a certain colour (eg; black), and then painting the border in another color (white). If the element is supposed to have a background image or colour, then you need to fill in the space between the borders.
Then, you paint the real image. This involves first painting a rectangle in the element’s background colour (if specified), then tiling the background (again if specified).
Once the two images are done, you just go through the mask, a pixel at a time, and whenever a “transparent” indicator (black, in this example) is found, the corresponding pixel in the real image is painted “clear”.
Conclusion
As far as I know, this is a pretty foolproof method – if JavaScript is not enabled, then the browser simply relies on its own rendering capabilities.
There are improvements that could be made – the script has not been tested with multiple colours, and I haven’t coded any border styles (dashed, dotted, etc) into it.
If there is interest in the script, then I may improve on it.
In short: this is a method for applying rounded borders with one single line of CSS. No messy tables, no messy :before{content()} stuff – just plain old border-radius.
I had an idea last week – a border can easily be faked by giving an element a background which has a border painted into it…
This set off a chain of thoughts, which basically ended with this:
if all existing browsers have trouble rendering rounded borders (yes, even firefox), then why not just remove them completely from the CSS and render them as a background image in PHP?
In other words, scan the CSS, grab any CSS relating to rounded borders, and render the borders as a background image.
This thought was electric! Why hadn’t I thought of it before? Why hadn’t anyone else thought of it?
Before you get bored, here’s the example. Note that the source brings in two external items – the CSS (which is very plain), and the script for rendering it.
Here’s the source. Just link it into your page and then add some border-radius magic to your CSS. It should work straight out.
The script grabs external CSS using XMLHttpRequest – there does not seem to be an ECMAScript method to extract un-parsed CSS (and I’ve scoured the DOM standards documentation!).
A major advantage this method has over previous methods is that you can have a background image, which is clipped properly by the server-side script. Other methods involve adding images to the corners, which, if background images are involved, mean very careful planning to make sure the corners don’t clash.
The original link tracker code was built as a “can it be done?” project, when I was discussing Donncha’s link tracker method with Donncha and Stewie (blog? – damn those pseudonyms).
I lost my system drive during the week so thought I’d take the time to install gentoo, as I tend to prefer installing things from source, and… …and… you know – I’m not going to attempt to justify it – I just felt like it.
So anyway – everything installed fine, even if it took forever to install the system in the first place. Everything, that is, except the Netgear WG511v2 that I use for my wireless network.
As mentioned in an earlier post, where I installed the card in Fedora, the version I’m using is the Chinese one, with the Marvell chipset.
Check the version of ndiswrapper that Gentoo will install:
emerge -p ndiswrapper
If the version is greater than, or equal to 1.1 (as of today, the version installed by portage is 0.12-r3 – not good enough), then do this:
emerge ndiswrapper
Otherwise, you’ll have to go to the ndiswrapper site, download the newset version, unzip it, enter the directory, then sudo -c "make && make install" to install it.
If you go ahead with the emerge anyway (yeah – ignore me… I’m just the person trying to give help here 🙂 ), then you will get weird errors like “unknown symbol ntoskernel” in the log when you try to install the module.
So, ndiswrapper is installed; time to install the card driver. Type the following into the console.
wget http://opencurve.org/~sunny/linux/files/WG511v2.tar.gz
tar xzf WG511v2.tar.gz
cd "Windows XP"
sudo -c "ndiswrapper -i WG511v2.INF && ndiswrapper -l"
The last line installs the driver, and then displays all installed ndiswrapper drivers:
wg511v2 driver present, hardware present
If your console doesn’t say “hardware present”, then plug your card in and type sudo -c "ndiswrapper -l" to check again. If it fails again, or the wg511v2 bit doesn’t appear at all, then go join the ndiswrapper mailinglist and help out.
So now, type su to gain root rights (too many commands to bother with su -c), and type the following:
modprobe ndiswrapper
iwconfig
If your wlan0 interface rears its pretty little face, then all is well. If not, see above about the mailing list.
iwlist wlan0 scan
this gives me the following output:
wlan0 Scan completed :
Cell 01 - Address: 00:0F:B5:0F:D0:91
ESSID:"NETGEAR"
Protocol:IEEE 802.11b
Mode:Managed
Frequency:2.462 GHz (Channel 11)
Quality:0/100 Signal level:-41 dBm Noise level:-256 dBm
Encryption key:off
Bit Rate:1 Mb/s
Bit Rate:2 Mb/s
Bit Rate:5.5 Mb/s
Bit Rate:11 Mb/s
Bit Rate:6 Mb/s
Bit Rate:9 Mb/s
Bit Rate:12 Mb/s
Bit Rate:18 Mb/s
Bit Rate:24 Mb/s
Bit Rate:36 Mb/s
Bit Rate:48 Mb/s
Bit Rate:54 Mb/s
Extra:bcn_int=100
Extra:atim=0
That gives us info such as the AP‘s ESSID, the channel it broadcasts on, and its MAC address (the address that looks like an IPv6 number).
IMPORTANT My system had a kernel panic a moments after the following step. Make sure to save whatever work you’re doing before proceeding. The panic only happened once, so it may be related to something different (I have a naughty habit of massively multi-tasking).
iwconfig wlan0 channel 11 essid "NETGEAR" ap "00:0f:b5:0f:d0:91"
We’re almost there. Now you need to bring the card up. Change the numbers below as appropriate:
ifconfig wlan0 192.168.1.106
ifconfig wlan0 up
route add default gw 192.168.1.254 dev wlan0
I’m not sure what the Gentoo equivalent of rc.local is yet, but when I find it, the two code blocks above will be going into it, so the network interface is brought up immediately upon boot. (I don’t know how to do it any other way 🙂 )
There was a bit of chat on the #linux chatroom about how Donncha’s linktracker used a method which could be misread as an attempt to spam google.
So, we had a bit of a brainstorm, and Stewie suggested that I try using XMLHTTPRequest to track the link before it is followed.
…which I did.
On the example page, I have three links – two external, and one internal. I guessed that it’s silly to record internal links, when your web logs already do that, so I deliberately told the script to ignore internal links.
The script automatically searches the document for links, and attaches an ‘onclick’ event which calls the link tracker before the link is followed.
Donncha says he’ll be using it in his WPMU WordPress enhancement. I’ll be writing it into a plugin probably tonight, if someone else doesn’t do it first 🙂
update 2005-09-23: noticed this fantastic example of how to avoid all the AJAX malarkey to do this trick. I’m surprised I didn’t think of it myself! I’m blinded by science, I suppose – applying new tricks to everything, when a simple gimmick will do the trick even better. Well done, Martin!
Through the encouragement of “Helen” (full name, location, website etc – unknown), I’ve been prodded into reviving an old chestnut of mine called the Collapsible Menu.
The idea of this script was that it takes menus that you create in the form of unordered lists, then it dynamically converts them into a collapsible menu.
And, with no extra work, you have a collapsible menu (example).
Yes yes, I know its not new – there are many people that do that these days. But back in the day, I believe I was one of the first to do it.
Anyway – the request I had from Helen was to change the script so it was possible to remove the [+] links and instead use the entire parent link as the opener/closer.
After much tardiness on my part (been busy and sick, in turns), I’ve done that requested work.
To use the parent link as the opener/closer, you need to override a variable. After the tag which includes the JavaScript, place this line:
That’s about it really. But, before I submit this article, here’s a nice goody – A while back, Nathan Young took the script and made a bookmarklet out of it. Try it out! Bookmark the ul collapser, go to this page, then click on the bookmark in your bookmarks list. Violin!
This one had me scratching my head for a few minutes.
Look at the code below, and tell me what’s wrong with it.
<div id='testdiv'>test</div>
<script type='text/javascript'>
testdiv=document.getElementById('testdiv');
testdiv.appendChild(document.createTextNode('. all is well'));
</script>
There is nothing wrong with the above, in sane browsers, but IE is not known for its sanity.
Trying the above in IE will result in an “object does not support this method” error.
The reason for this seems to be that IE creates a global variable for each id that it comes across. In this case, that means that testdiv in the JavaScript above is predefined as the <div> in the HTML section.
IE’s error message system is hopelessly useless. The error applies to the ‘=‘ in the line, and not the getElementById, which is what I was scratching my head about.
So what’s the solution? There are two workarounds:
use a unique variable name.
pay attention to scope.
The unique variable name approach is not ideal, as it may make less readable in your code than you’d like, so the solution is to tell the browser that the variable name you’re using applies just to that local scope.
<div id='testdiv'>test</div>
<script type='text/javascript'>
var testdiv=document.getElementById('testdiv');
testdiv.appendChild(document.createTextNode('. all is well'));
</script>
The above should work. Surprising, how simple a workaround can be…
I learn what’s going on outside the computer world by reading google’s news site. This morning, I found that they’ve improved the interface for the page.
You can now rearrange the news categories how you want, increase/decrease the number of articles reported per category, and even remove categories you find completely boring. For example, I no longer have to hear about how some crappy tennis or football “star” is doing in some tournament somewhere.
Now, they just need to find some way if providing localised news… There is a “US” section, but every other country is lumped under “World”.
It would be nice if they could spell “customised” right as well, but I’m not greedy…
Thanks to all the people who have tested and used the multiselect before. I’ve taken the improvements, suggestions, and bug-fixes and am now happy with a new version of it.
Multiselect takes a normal <select multiple="multiple"> and converts it dynamically into a more user-friendly interface.
An example of the new and improved version can be found here.
Points of note
you can now “select all” and “select none”
a new “reset” link brings the select box back to how it was originally
the script works even if you have more than one multiselect box
caveats: the name of the <select> must end in ‘[]‘. While this is specifically against W3C rules, there does not seem to be any alternative method to pass on multiple values for one variable.