Wednesday, December 24, 2008

Quick! way to get repeated characters

want 8 spaces?


(new Array(8)).join(' ')

it's quick not only as in fewer keystrokes/less code, but is also blazingly fast to execute compared to a for-loop implementation.

Sunday, December 14, 2008

Fireworks CS4 filesize export bug

Fireworks' export wizard seems to be plagued by filesize bugs. I thought they squashed them in CS3, but CS4 seems to have re-introduced or made a new one: it reports an exported file's size as Kilobytes = bytes/1000 instead of bytes/1024.

I just exported a file, noting that FW tells me it will be 754K, when in fact, it is 737K (754,807 bytes).

Tuesday, October 21, 2008

IE6 + AlphaImageLoader + hover

the 'filter' nomenclature is unwieldy, so these lines are going to be long, you'll need to scroll. The gist is - ORDER MATTERS! - and the order is opposite from what you'd expect.


/* order matters! */
.carousel a:hover .playbutton{background:transparent url(/css/i/x.gif);filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/css/i/icons/playbutton_thumb_hover.png", sizingMethod="crop");}
.carousel .playbutton{background:transparent url(/css/i/x.gif);filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/css/i/icons/playbutton_thumb_normal.png", sizingMethod="crop");}
a:hover .playbutton{background:transparent url(/css/i/x.gif);filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/css/i/icons/playbutton_hover.png", sizingMethod="crop");}
.playbutton{background:transparent url(/css/i/x.gif);filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src="/css/i/icons/playbutton_normal.png", sizingMethod="crop");}

Tuesday, October 7, 2008

Asynchronously loading javascript

Asynchronous allows the page to keep loading--it's non-blocking--which can be nice. But also has gotchyas. With traditional, sequential, synchronously loaded javascript, you can be sure that something is defined if you've defined it in a preceeding script block. Not so with asynch. loaded js.

Safari 2

evidently, form.submit() bypasses any onsubmit handler that may be in play, whether it was added via form.onsubmit = function(){} or by using addEventListener.

Wednesday, September 24, 2008

Events


//will break if element.on[event] is modified directly elsewhere
//will break if the markup itself contains inline event listener functions ( <element onevent="function body"> )
//is incompatible with prototype library (Prototype library should not be used for ANY project.)

// using EVENT namespace ... could be different if needed, could possibly be Element.prototype ... could possibly be a jQuery extension/plugin (or incorporated into jQuery core?)

EVENT = {};

EVENT.elements = [];

EVENT.addListener = function(el, type, f, prepend){
    
    if(!el.eventListenerList){
        el.eventListenerList = [];
    }
    
    if(!el.eventListenerList[type]){
        el.eventListenerList[type] = [];
    }
    
    if(prepend){
        el.eventListenerList[type].splice(0,0,f);
    } else {
        el.eventListenerList[type].push(f);
    }
    
    if(!el['on'+type]){
        el['on'+type] = function(e){
            for(var i in el.eventListenerList[type]){
                el.eventListenerList[type][i](e);
            }
        };
    }
};

EVENT.removeListener = function(el, f){
    
    if(!el.eventListenerList){
        return;
    }
    
    //note: "var i in obj" is incompatible with icky prototype library
    var list = el.eventListenerList;
    for(var i in list){
        for(var j in list[i]){
            if( list[i][j] == f ){
                //remove it.
                el.eventListenerList[i].splice(j,1);
                return;
            }
        }
    }
    
};

EVENT.hasListener = function(el, f){

    if(!el.eventListenerList){
        return false;
    }
    
    //note: "var i in obj" is incompatible with icky prototype library
    var list = el.eventListenerList;
    for(var i in list){
        for(var j in list[i]){
            if( list[i][j] == f ){
                return true;
            }
        }
    }
    
    return false;
};

Wednesday, September 10, 2008

svg boundingbox of rotated ellipse

Found out last night that safari has a bug in its svg implementation (ok, probably lost most of you right there “who cares about svg?” … but there’s a possibility that this could be useful for flash/actionscript too)

Turns out that if you rotate an ellipse, the getBBox method returns a rather generous bounding box, because it’s using an implicit rectangle wrapped around the ellipse to get the bounding box.

You can see below what the browser bug is and what the correct box is. The red—buggy—implementation is created by the browser basing its calculations on the blue (implicit) rectangle that surrounds the ellipse.

The correct result is the box in green.

I created a javascript workaround for this bug.

It uses calculus. This is the first time I’ve used calculus in programming something that didn’t exist solely to demonstrate calculus.

I first tried to find a solution posted somewhere on the net, but only found a post where someone says it’s solved in the latest version, then reverses himself and says, ‘oh, no, actually it’s firefox that fixed its version of the same bug.’

If someone happens to find the bug # or a posted solution, that’d be cool.


// Trig functions (Math shortcut aliases)
var sin = Math.sin,
    cos = Math.cos,
    tan = Math.tan,
    cot = function(a){return 1/tan(a);},
    sqrt= Math.sqrt,
    acos= Math.acos,

    // our instance of an ellipse .. will become something more like this._shape (if this.type == ELLIPSE)
    ellipse = $('e').getElementsByTagName('ellipse')[0],

    // the properties of our instance of an ellipse
    a = ellipse.getAttribute('rx')*1, //x-radius (major, minor, who knows)
    b = ellipse.getAttribute('ry')*1, //y-radius (major, minor, who cares)
    cx = ellipse.getAttribute('cx')*1, //center's x-coord .. not currently used (assume the ellipse is centered at the origin)
    cy = ellipse.getAttribute('cy')*1, //center's y-coord .. not currently used (assume the ellipse is centered at the origin)
    matrix = ellipse.getCTM(), //the transform matrix
    angle = acos( matrix.a ),

    // the x coordinates where the ROTATED xmax and ymax occur on the ellipse BEFORE ROTATION.
    // these "magical" formulas represent the recombined derivative of the ellipse function
    x1 = (-1*(a*a)*tan(angle)) / sqrt(a*a*tan(angle)*tan(angle) + (b*b)),
    x2 = (   (a*a)*cot(angle)) / sqrt(a*a*cot(angle)*cot(angle) + (b*b)),

    // the actual xmax and ymax, reflecting the above, rotated into position (AFTER ROTATION).
    xmax = (cos(angle) * x2) + (sin(angle) * f(x2)),
    // (using negative-angle because positive screen y-axis is the same as 'normal maths' negative y-axis)
    ymax = (sin(-angle) * x1) + (cos(-angle) * f(x1));
///

// the f(x) that defines the ellipse (in general form)
function f(x){
    return b * Math.sqrt(1-((x*x)/(a*a)));
};

//~ console.log(ellipse.getAttribute('transform'));
//~ log(matrix.a, matrix.b, matrix.c, matrix.d, matrix.e, matrix.f);

// browser's idea of where the bbox is... (red box)
var box = ellipse.parentNode.getBBox();
re = $("re");
re.setAttribute("x", box.x);
re.setAttribute("y", box.y);
re.setAttribute("width", box.width);
re.setAttribute("height", box.height);
re.setAttribute("stroke", "red");

// my idea of where the bbox is... (green box)
re = $("re2");
re.setAttribute("x", -Math.abs(xmax));
re.setAttribute("y", -Math.abs(ymax));
re.setAttribute("width", Math.abs(xmax)*2);
re.setAttribute("height", Math.abs(ymax)*2);
re.setAttribute("stroke","#090");