Skip navigation.

My comments on ...

... everything bothering me

Posts tagged with "JavaScript"

Safari and HTMLElement.prototype

, ,

As you all know, Safari does not support HTMLElement, which makes it hard to add stuff to all newly created HTML-Elements, Nodes, whatever you might call it.

Fortunately, it supports the semi-standard __proto__ property. Exploiting another "bug", that all HTMLElement(s) are produced from the same prototype, I came up with a neat solution that even works in Safari 1.3 and you can replace functions such as "getAttribute" (go figure!).

var w = window;
if(!w.HTMLElement&&(typeof document.createElement)=="function" // check basics
   &&(var t=document.createElement('a').__proto__) // '__proto__' supported?
   &&t==document.createElement('p').__proto__){ // all HTMLElements the same?
   w.HTMLElement={}; // prevent people from constructing
   w.HTMLElement.prototype=t;
}

This piece uses perfect object detection, i.e. every browser that works this way will be fixed (i.e. all supporting Safaris, maybe KHTML?), while leaving browsers that work differently untouched.

Remember: You saw it here first!

PS: Best used inside a closure, e.g. (function(){ [your code here] })().

n-Dimensional Arrays

function nD(){
   var A,tempA; //final and temp array
   var dimension=arguments.length, i; //iteration vars
   while(dimension--){
      tempA = Array(arguments[dimension]);
      i = tempA.length;
      while(i--)
         tempA[i]=A;
      A = tempA;
   }
   return A;
};

function check(A){
   return (""+A).split(",").length;
};

var test = nD(3,3,3); //create 3x3x3 'cube'
alert(test);
alert(check(test))); //check 'volume'


Or the short version:
l='length';

function nD(){
   var a,b,c=arguments,d=c[l],i;
   while(d--){
      i = (b = Array(c[d]))[l];
      while(i--)
         b[i]=a;
      a = b;
   }
   return a;
};

function check(a){
   return (""+a).split(",")[l];
};

alert(nD(3,3,3));
alert(check(nD(3,3,3)));

Wrapping IE's DOM functions

, ,

Many people are irritated by the fact that IE's DOM functions (which are really COM objects) don't support the "call" and "apply" methods. Transfering those methods from a function object to a COM object throws errors. People invented a weird hack involving the Function() constructor to overcome this issue, but I think I know better. I don't know if I'm the first one to come up with it (probably not), but I found a "standard" way to make an arbitrary DOM function into a real Ecmascript function. As a very easy test case, type this javascript url into IE:
javascript:alert(Function.prototype.call.call(document.createElement,document,'p'))
This will produce a new paragraph (

) Element. You can change the string to any valid value. And now the annotated minimised version of the wrapDOMf function:

function wrapDOMf (scope,func)
{
    if(!(scope && typeof func == "string" && scope[func])) return; //ensure proper usage; no type checking

    var storage = arguments.callee; //using wrapDOMf as temporary storage
    storage.orig = scope[func];
    scope[func] = function WRAP()
            {
                return Function.prototype.apply.call(arguments.callee.$original,this,arguments);
            };
    scope[func].$original = storage.orig;
    delete storage.orig; //get rid of any unnecessary references, optional
}
It shouldn't leak memory. If it should turn out to do that, I'll have to sort that out. The minimised version I intend to use is the following (but I don't know about leak-status):
function wrapDOMf (s,f) //scope, function
{
    if(!(s && typeof f == "string" && s[f])) return;

    var m = s[f]; //using local var instead of property
    s[f] = function WRAP()
            {
                return Function.prototype.apply.call(arguments.callee.$original,this,arguments);
            };
    s[f].$original = m;
}
Why I delete the variable at the end [long version only] ? Because that's not where my imagination stopped. I wanted to use a closure to add functionality. We'll want to manipulate the input and output of the function, don't we? I assume we want to change the output most of the cases. Thus, my primary function will do that and only that. The change in the name reflects the new usage: It could be useful for all functions now, not just DOM ones.
function wrapFunc (scope,func,hijack)
{
    if(!(scope && typeof func == "string" && scope[func])) return; //ensure proper usage
    if(!(hijack && hijack instanceof Function)) hijack = function(x){return x}; //if no hijack exists, f(x)=x

    var storage = arguments.callee;
    storage.orig = scope[func];
    scope[func] = function WRAP()
            {
                var obj = Function.prototype.apply.call(arguments.callee.$original,this,arguments)
                return hijack.call(this,obj,arguments); // 'hijack' is passed the output object and all the arguments
            };
    scope[func].$original = storage.orig;
    delete storage.orig; //now mandatory, since we retain closure
}
The one for input manipulation:
function wrapFuncBefore (scope,func,hijack)
{
    if(!(scope && typeof func == "string" && scope[func])) return; //ensure proper usage
    if(!(hijack && hijack instanceof Function)) hijack = function(){return [this,arguments]};

    var storage = arguments.callee;
    storage.orig = scope[func];
    scope[func] = function WRAP()
            {
                var temp = hijack.apply(this,arguments); //I want destructuring assignment
                var that = temp[0];
                var args = temp[1];
                return Function.prototype.apply.call(arguments.callee.$original,that,args);
            };
    scope[func].$original = storage.orig;
    delete storage.orig; //now mandatory, since we retain closure
}

The "classic example", take 2

, , ,

if(!document.getElementById)void()
else
{
   //continue
}

The "classic example"

, ,

Inspired by hallvors' Capability Detection and a follow-up post from Crisp, I tried to improve on the "classic example"...

if (!document.getElementById)
{
   (function()
    {
     var obj = {};
     if (document.all)
     {
       obj = document.all;
     }
     else if (document.layers)
     {
       obj = document.layers;
     }
     document.getElementById = function(id){return obj[id]};
    }
   )();
}

edit: I also thought about really fixing this, as to change the output from "undefined" to "null" in the case that there is no element with that Id (according to spec), but I figured if someone is stupid enough to check explicitly against null or undefined, instead of normal object detection, then he earned the burn. I really can't imagine a scenario where this "feature" of the function is mandatory.

If there ever occurred such a scenario, however... replace

document.getElementById = function(id){return obj[id]};

by

document.getElementById = function(id){return obj[id] ? obj[id] : null};

or, if there are browsers out there that return "undefined" instead of "null" (natively), you might want to add this instead (update 2007/08/28: Don't use, it's dirty)

if (!document.getElementById(null) && document.getElementById(null) !== null)
{
   (function()
    {
     var obj = document;
     var func = 'getElementById';
     var key = '_';
     while (obj[key+func]) {key+=key};
     obj[key+func] = obj[func];
     obj[func] = function(id){return obj[key+func](id) || null}
    }
   )();
}

The better way of Capability Detection, Part 2

, ,

I haven't found time earlier to write this down, so I've made a note with the code attached and do it now :wink:

In execution of my occupation, last week (or so) I wrote a post "nagging at the nagger". This is round 2, he has given response (he basically says it's not worth the hassle).

I've removed the "finally" part and shortened the most important part down from two lines to one (thereby obscuring it a bit, *sigh*).

Based on a suggestion from Crisp, I wrapped it around an anonymous function, to prevent the variables from being declared in global scope. I've put the function into the condition (as opposed to the other way round), because the program then can tell right away that this function does not need to be executed, as opposed to executing the function and then evaluating the condition.

if (!window.XMLHttpRequest)
{
    (function()
     {
      var temp;
      var types = [
          'MSXML2.XMLHTTP.6.0',
          'MSXML2.XMLHTTP.3.0'
      ];

      for (var i = 0; i < types.length; i++)
      {
          try
          {
              void((temp = function(){return new ActiveXObject(types[i])})());
          }
          catch {temp = null}
          if(temp) break;
      }
      window.XMLHttpRequest = temp || function(){return undefined};
     }
    )();
}


Now, let's have a look at the most tricky part:

void(temp = function(){return new ActiveXObject(types[i])}());

// reformatted looks like this:

void(                                            /* void the result, to protect browsers
                                                    that return/throw "false" or similar */
     (temp = function()                          /* assign a function to the variable... */
            {
               return new ActiveXObject(types[i])
            })()                                 /* and then call it by inserting parenthe-
                                                  ses after those enclosing the function */
     );


Interestingly, I haven't yet tested it, though. Funny thing, that.

The better way of Capability Detection

, ,

In response to Crisp's post about "capability detection" (and his comment system, that ate most of my messages...):

The goal was to optimize the code provided so the "if" statements were not part of the function anymore.

The code wrapped around the "finally" construct might stand alone, and I'm sure the code could be otherwise shortened, but I think this should work all in all...

if (!window.XMLHttpRequest)
{
     var temp;
     var types = [
         'MSXML2.XMLHTTP.6.0',
         'MSXML2.XMLHTTP.3.0'
     ];
 
     for (var i = 0; i < types.length; i++)
     {
         try
         {
             temp = function(){return new ActiveXObject(types[i])};
             void(temp());
         }
         catch(e) {temp = null;}
         finally {if(temp) break;}
     }
     window.XMLHttpRequest = temp || function(){return undefined};
}
December 2008
M T W T F S S
November 2008January 2009
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31