27 Apr

son of multiselect

There have been a few fixes and upgrades to the multiselect code since the last announcement.

For those of you new to the idea, the multiselect is a tool for providing a more friendly version of HTML’s <select multiple>, using checkboxes, instead of the clumsy CTRL+click method that was there before-hand.

To make sure of backwards-compatibility, the script uses what has become known as “Unobtrusive JavaScript” – ie; if you don’t have JavaScript enabled, it will make little difference to the end result.

here is the example.

The new code adds the following functions:

  • The all, none, reset links may be turned off by changing the show_toplinks variable in the source to false.
  • The script does not force you to use ‘[]‘ symbols. This is good news for JSP developers, and other users of non-PHP languages.
  • You may now mark individual <option>s as disabled.

As usual, instructions for use are:

  • Get your multiple select working properly in plain HTML first.
  • Link to the external script.

Thank you to all the commenters on the previous version.

21 Apr

IE7 "::before" jiggle workaround

Look at this page, in either Firefox, or IE5+. The curves are created using some handy pieces of CSS; ::before, ::after, and content().

Thanks to Dean’s wonderful project, IE7, that code works in IE5+, which meant that we could supply our own clients with curves that didn’t involve tortuous HTML.

However, if you refresh the example page a few times in IE (especially IE5), you will notice there is a time-lag between the page loading, and the curve showing. While this is acceptable for a small demo, it is not acceptable for a published site!

The problem is that the page, while loading, will position everything as if the curves’ space is not used, and then, after the IE7 script has done its thing, it will “jump” everything into the right place.

It can be very disconcerting to have that happen on every page that you visit in a site – to have the page jump around every time you load a new document.

So, I set about finding a solution.

The solution was relatively simple to find, once I knew what I was looking for – I needed to find a piece of CSS that IE5 understood, in order to “reserve” the space for the curves, and then override that CSS when the IE7 code had been loaded and run.

Using the example page as context, the hack would run like this:

 p.test { margin:30px 0  }
 body:first-child p.test { margin:0 }

That’s it, basically.

A CSS-compliant browser will apply the second line, as it is more specific than the first line. IE5+, though, does not understand the second line, so applies the first line. That has the effect of reserving the space needed for the curves, above and below the p.test.

When the IE7 script runs, it learns to understand the second line, so applies that, removing the reserved space. It also learns to apply the ::before and ::after code, thus placing the curves into the reserved area.

An example of that code in a live site can be seen here.

18 Apr

using javascript folds in vim

I discovered a fantastic tool in vim recently – folding.

Now that I’ve learned to use it, I find that I’m addicted! I’ve installed Linux on a few machines in the last few weeks, and each time I do it, I don’t feel complete until I activate it.

To turn on folding for JavaScript, place the following line in your ~/.vimrc.

let javaScript_fold=1

To show you an example of how it appears, I’m working on the following page: borders.

The JavaScript for that page is getting pretty massive, so folding lets me navigate the file easily. Here’s how the script looks upon opening it with folding inactive:

/* coded by Kae - kae@verens.com */ {
/*
 I'd appreciate any feedback.
 You have the right to include this in your sites.
 Please retain this notice.
 This source has elements coded by Dean Edwards - please read his notices before copying
*/
}
function getCss(){
 var els,i;
 getCss_done=1;
 // get external stylesheets
 els=document.getElementsByTagName('link');
 for(i=0;i<els.length;++i){
  if(els[i].type=='text/css' && els[i].href){
   getCss_done=0;
   getCss_leftToDo++;
   var x=new XMLHttpRequest();
   x.open('GET',els[i].href,true);
   x.onreadystatechange=function(){
    if(x.readyState==4 && (x.status==200 || x.status==304) ){
     getCss_text+=x.responseText;
     getCss_leftToDo--;
     getCss_parseCssText();
     if(!getCss_leftToDo)getCss_done=1;
    }
   }
   x.send(null);
  }
 }
}

Reasonable, you probably think. But, what if you’re looking for a section of that file and it’s 970 lines long? That’s how long that particular script is so far. The default folding macros supplied by vim turn the above into this:

/* coded by Kae - kae@verens.com */ {
/*
 I'd appreciate any feedback.
 You have the right to include this in your sites.
 Please retain this notice.
 This source has elements coded by Dean Edwards - please read his notices before copying
*/
}
function getCss(){--------------------------------------------------------------------------------------------------------------------------------------------------------
function getCss_parseCssText(){-------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius(){--------------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_waitForCss(){---------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_generateCurves(){-----------------------------------------------------------------------------------------------------------------------------------
function sqrt(num){-------------------------------------------------------------------------------------------------------------------------------------------------------
function drawRect(el,x,y,w,h,bc,bi,op){-----------------------------------------------------------------------------------------------------------------------------------

Isn’t that great!? Now, to view any particular function, I don’t need to go searching for it – I can just close the one I’m working on (zc), use my arrow keys to go to the function I want, then open that with zo.

But, that’s not good enough… The borderRadius_generateCurves() function is 471 lines long, for example.

So, I took a look inside the vim macro file for JavaScript (/usr/share/vim/vim63/syntax/javascript.vim).

The language used by the syntax manager is damned ugly looking, so it was a real trial trying to do what I wanted, without screwing everything up. Eventually, I found it. Here’s the code that does the default folding:

if exists("javaScript_fold")
    syn match   javaScriptFunction      "\<function\>"
    syn region  javaScriptFunctionFold  start="\<function\>.*[^};]$" end="^\z1}.*$" transparent fold keepend

    syn sync match javaScriptSync       grouphere javaScriptFunctionFold "\<function\>"
    syn sync match javaScriptSync       grouphere NONE "^}"

    setlocal foldmethod=syntax
    setlocal foldtext=getline(v:foldstart)
else
    syn keyword javaScriptFunction      function
    syn match   javaScriptBraces           "[{}]"
endif

syn sync fromstart
syn sync maxlines=100

What I wanted, was to fold at every loop, comment, conditional, and function. It was actually quite simple in the end – just replace the above with this:

syn region myFold start="{" end="}" transparent fold
syn sync fromstart
set foldmethod=syntax
set foldtext=getline(v:foldstart)
syn sync maxlines=100

Now, the entire file was visible when I opened it:

/* coded by Kae - kae@verens.com */ {-------------------------------------------------------------------------------------------------------------------------------------
function getCss(){--------------------------------------------------------------------------------------------------------------------------------------------------------
function getCss_parseCssText(){-------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius(){--------------------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_waitForCss(){---------------------------------------------------------------------------------------------------------------------------------------
function borderRadius_generateCurves(){-----------------------------------------------------------------------------------------------------------------------------------
function sqrt(num){-------------------------------------------------------------------------------------------------------------------------------------------------------
function drawRect(el,x,y,w,h,bc,bi,op){-----------------------------------------------------------------------------------------------------------------------------------
/* global variables */ {--------------------------------------------------------------------------------------------------------------------------------------------------
/* load xmlhttprequest compatibility layer, if required */ {--------------------------------------------------------------------------------------------------------------
/* attach the onload event */ {-------------------------------------------------------------------------------------------------------------------------------------------

/* the following code was not written by Kae Verens. licenses have been kept */ {-----------------------------------------------------------------------------------------

When I open a function, it doesn’t automatically expand the whole thing now, making it a bit more readable. I can expand it out as I need, and keep the bits I’m not interested in hidden:

function borderRadius_generateCurves(){
 /* common variables */ {-------------------------------------------------------------------------------------------------------------------------------------------------
 for(;i<getCss_rules.length;++i){
  rules=getCss_rules[i][1];
  /* fix undefined css */ {-----------------------------------------------------------------------------------------------------------------------------------------------
  /* common variables */ {------------------------------------------------------------------------------------------------------------------------------------------------
  if(parseInt(tr[0]) || parseInt(br[0]) || parseInt(bl[0]) || parseInt(tl[0])){
   /* common variables */ {-----------------------------------------------------------------------------------------------------------------------------------------------
   for(;j<els.length;++j){ /* draw border and background */
    /* setup border array and common variables */ {-----------------------------------------------------------------------------------------------------------------------
    /* create background element */ {-------------------------------------------------------------------------------------------------------------------------------------
    /* generate curved borders */ {
     if(tr0){ // top right corner-----------------------------------------------------------------------------------------------------------------------------------------
     if(br0){ // bottom right corner
      var irx=tppBR0-tppWR,iry=tppBR1-tppWB;
      var irx2=irx*irx,tmp2=tppHeight-tppBR1;
      for(var cx=tppBR0-1;cx>-1;--cx){
       var cx2=cx*cx,ind=tppWidth-tppBR0+cx;
       var h=(cx<irx)?sqrt(iry*iry*(1-cx2/irx2)):0;
       b_arr[ind][2]=tmp2+h;
       b_arr[ind][3]=tmp2+sqrt(tppBR1*tppBR1*(1-cx2/(tppBR0*tppBR0)));
      }
     }
     if(bl0){ // bottom left corner---------------------------------------------------------------------------------------------------------------------------------------
     if(tl0){ // top left corner------------------------------------------------------------------------------------------------------------------------------------------
    }
    /* fill in the background */ {----------------------------------------------------------------------------------------------------------------------------------------
    /* draw borders */ {--------------------------------------------------------------------------------------------------------------------------------------------------
    theEl.parentNode.insertBefore(bgw,theEl);
   }
   for(j=0;j<els.length;j++){ // remove the original border and adjust the padding accordingly ---------------------------------------------------------------------------
  }
 }
 /* show timer */ {-------------------------------------------------------------------------------------------------------------------------------------------------------
}

Note that I’ve managed to contract comments as well. You do that by using the { and } symbols:

/* coded by Kae - kae@verens.com */ {
/*
 I'd appreciate any feedback.
 You have the right to include this in your sites.
 Please retain this notice.
 This source has elements coded by Dean Edwards - please read his notices before copying
*/
}

That trick can also be used to close switches as well:

   switch(parts2[0]){
    case 'background': {--------------------------------------------------------------------------------------------------------------------------------------------------
    case 'border': {------------------------------------------------------------------------------------------------------------------------------------------------------
    case 'border-color': {------------------------------------------------------------------------------------------------------------------------------------------------
    case 'border-radius': {-----------------------------------------------------------------------------------------------------------------------------------------------
    case 'border-top-right-radius': {
     m=(parts3L>1)?parts3[0]+' '+parts3[1]:parts3[0]+' '+parts3[0];
     getCss_rules[a][1]['border-top-right-radius']=m;
     break; }
    case 'border-top-left-radius': {--------------------------------------------------------------------------------------------------------------------------------------
    case 'border-bottom-right-radius': {----------------------------------------------------------------------------------------------------------------------------------
    case 'border-bottom-left-radius': {-----------------------------------------------------------------------------------------------------------------------------------
    case 'border-style': {------------------------------------------------------------------------------------------------------------------------------------------------
    case 'border-width': {------------------------------------------------------------------------------------------------------------------------------------------------
    default: {------------------------------------------------------------------------------------------------------------------------------------------------------------
   }

Well, that’s the longest thing I’ve written in a while… I hope it’s useful to someone!

13 Apr

curved corners using CSS3's border-radius, and a little JavaScript

I created a hack recently which generated corner curves using a custom background image.

When I showed it to the in-house designers here, they commented that it was too slow, and there was an annoying few seconds where the entire background of the affected areas was blank.

So, I thought about it, and came up with a different method – check this out. It’s a pure JavaScript method, which generates the curves a pixel at a time.

This works in IE6, Firefox1.0.2, and KDE3.4. It may work in others, but those are the browsers I tested it in.

The script has the following good points:

  • No messy CSS hacks. Just write your border-radius code as normal.
  • No long delay as a custom background is fetched.

The script improves over Firefox’s own rendering in that it:

  • is compatible with the W3C’s CSS3 standard for border-radius, which indicates that a corner may be elliptical, and not just circular.
  • doesn’t leak images! At the moment, Firefox’s rendering of border curves breaks if you have a background image. I’ve tried fixing it myself, but it will take a bit of work – a lot of work, actually.

Of course, it’s not all good news. The script has the following failings (noted so far).

  • Curves that use a background image render slowly in IE6, because IE6 does not seem to check its local cache for CSS-called images.
  • The border style used at the moment is solid for all sides. While this may be okay for most people, it’s annoying to me.

My plan for this script is to get the border styles working properly with dashed, double, and dotted styles, then rewrite the Firefox border-rendering code using the algorithms I’ve developed. It should hopefully shut a few bugs down from Bugzilla.

12 Apr

intermittent XMLHTTPRequest bug, and its solution

I was tearing out my hair for the last half an hour, trying to track down the reason why an undesired behaviour was happening in IE6 on one machine, but not on another (both machines were identical – XP, SP2, etc).

After I discovered the cause, I had to hit myself – it was a bug in my own programming that I’d already described to a co-worker as something to watch out for.

The symptom was that I have a certain piece of data which is requested from the server, which, in some cases, was not being retrieved. The strange thing was that there was never any error, and a refresh of the page would sometimes work.

The cause of it was the order in which the various steps of the XMLHTTPRequest process is done.

You must follow the following steps in order:

  • Create the XMLHTTPRequest object (obvious):
    var req=new HTMLHttpRequest();
  • Assign the onreadystatechange function:
    req.onreadystatechange=function(){ /* stuff */ }
  • Send the request:
    req.send(null);

Yes, that seems obvious. But if you do come across the situation where seemingly perfect code is just not retrieving the data you are looking for, then make sure that your process is in the above order.

This error will occur with higher frequency, the higher the bandwidth of the computer. The problem is that the request is being satisfied before the computer has parsed the code which handles the retrieved data.

05 Apr

shockingly unethical behaviour by BlueFish!

I’m on the mailing list for KDEWebDev, but usually, I just skim through it, ignoring most mails.

Today, though, the subject of one of the mails intrigued me:

Blue fish scum!!!

Good lord! That’s incredible language for a leader of the open source community to use about a rival application.

I read further, and it seems the anger is warrented.

Look at the voting box at the bottom of the page. Looks fine, right? Look at the source of it!

It does not allow you to vote any lower than 8! Even when you think you’re voting for a low number, you are actually voting for a different number.

Here is a sample:

 <option value="9">3</option>
 <option value="8">2</option>

If you can’t see what’s wrong with that, then you should not be working in anything that involves a modicum of ethics.

As a VIM user, this revelation will not affect my own usage of the product, but I hope some of you readers out there think twice before deciding to help out in that particular project.

04 Apr

wedding rings

Myself and Bronwyn will be getting married in October (on the 31st).

I did some gimping to develop some images to describe what I think the rings will end up looking like.

Bronwyn’s is a fairly simple one to work out, for those of you that know anything about Celtic history:

My own is a bit more modern: