//-----------------------------------------------------------------------------
// $Id: tiobe.js,v 1.0 2006/12/01 11:40:08 jong Exp $
// $Name:  $
// COPYRIGHT 2006, TIOBE Software B.V.  --  All rights reserved.
//-----------------------------------------------------------------------------

// Add div src loadtime support.
addEventHandler( window, 'load', loadDivSources );
addEventHandler( window, 'load', setThumbnailImages );
addEventHandler( window, 'load', setTextOverImages );
addEventHandler( window, 'load', setDivTextShadows );

///////////////////////
// Various functions //
/////////////////////////////////////////////////////////////////////////////

// Returns if the given variable is defined (true) or not (false)
function isDefined(item) {
  return (typeof item != 'undefined');
}

// Returns if the given argument is an array (true) or not (false)
function isArray() {
  if (typeof arguments[0] == 'object') {
    var criterion = arguments[0].constructor.toString().match(/array/i);
    return (criterion != null);
  }
  return false;
}

// return true if user-agent says this browser is Internet Explorer
function isIE() {
  return ( navigator.userAgent.indexOf("MSIE") >= 0 );
}

// Returns a string of random characters in the range [a..Z0..9]
// of the given length.
//
function getRandomString(len) {
  var chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
  var str = '';
  for(var i=0;i<len;i++) {
    str += chars.charAt( Math.round( Math.random()*(chars.length) ) );
  }
  return str;
}

// Add a trim() method into the string object
if( !String.prototype.trim ) {
  String.prototype.trim = function() {
    var sInString = this.replace( /^\s+/g, "" );// strip leading
    return sInString.replace( /\s+$/g, "" );// strip trailing
  }
}

// Add an startsWith method into the string object
if( !String.prototype.startsWith ) {
  String.prototype.startsWith = function(prefix) {
    return (this.indexOf(prefix) === 0);
  };
}

// Add an endsWith method into the string object
if( !String.prototype.endsWith ) {
  String.prototype.endsWith = function(suffix) {
    var startPos = this.length - suffix.length;
    if( startPos < 0 ) {
      return false;
    }
    return (this.lastIndexOf(suffix, startPos) == startPos);
  };
}

// Add a contains method into the string object
if( !String.prototype.contains ) {
  String.prototype.contains = function(test) {
    return this.indexOf( test ) >= 0;
  };
}


// Add one or more event handlers to an event generator, like onLoad.
// For example addEvent(body, 'load', somefunction) will add the
// given function to the onLoad handler of the body element.
//
// Returns true when the event handler could be added to object,
// false otherwise.
//
function addEventHandler(obj, evType, fn){
 if (obj.addEventListener){
   obj.addEventListener(evType, fn, false);
   return true;
 } else if (obj.attachEvent){
   var r = obj.attachEvent("on"+evType, fn);
   return r;
 } else {
   return false;
 }
}

// Shows an exception e in an alert box.
// e            Caught exception (catch(e))
// [moreText]   Set if more text should be added in the alert box.
function showException(e,moreText) {
  if( window.hideExceptions ) return;
  var txt = 'Exception:\n\n';
  for(var attr in e) {
    try {
        if( attr == 'stack' ) { continue; }
        if( isFunction(e[attr]) ) { continue; }
        txt += attr + ': ' + e[attr] + '\n';
    } catch(ignored) {
        // some fields are priviliged
    }
  }
  alert(txt + (moreText ? moreText : ''));
}

// Opens the given url in a new window of given width and height.
// Returns a reference to the opened window.
function openWindow(name, left, top, width, height, url) {
  return window.open( url ? url : '',
                      name ? name : "newWindow",
                        "left="+left+",top="+top+",width="+width+",height="+height+","+
                        "resizable=yes,menubar=no,toolbar=no,location=no,status=no,"+
                        "scrollbars=yes,directories=no"
                    );
}


// Returns the height of the current content window
function getFrameSize() {
  var frameWidth = 100;
  var frameHeight = 100;
  if (self.innerWidth) {
  	frameWidth = self.innerWidth;
  	frameHeight = self.innerHeight;
  } else
  if (document.documentElement && document.documentElement.clientWidth) {
  	frameWidth = document.documentElement.clientWidth;
  	frameHeight = document.documentElement.clientHeight;
  } else
  if (document.body) {
  	frameWidth = document.body.clientWidth;
  	frameHeight = document.body.clientHeight;
  }
  return { 'width' : frameWidth, 'height' : frameHeight };
}

//
function getHeight () {
  var x,y;

  if (self.innerHeight) {
    // all except Explorer
    x = self.innerWidth;
    y = self.innerHeight;
  }
  else if (document.documentElement && document.documentElement.clientHeight) {
    // Explorer 6 Strict Mode
    x = document.documentElement.clientWidth;
    y = document.documentElement.clientHeight;
  }
  else if (document.body) {
    // other Explorers
    x = document.body.clientWidth;
    y = document.body.clientHeight;
  }
  return y;
}

function getWidth () {
  var x,y;

  if (self.innerHeight) {
    // all except Explorer
    x = self.innerWidth;
    y = self.innerHeight;
  }
  else if (document.documentElement && document.documentElement.clientHeight) {
    // Explorer 6 Strict Mode
    x = document.documentElement.clientWidth;
    y = document.documentElement.clientHeight;
  }
  else if (document.body) {
    // other Explorers
    x = document.body.clientWidth;
    y = document.body.clientHeight;
  }
  return x;
}

// Returns boundaries of window as map with left, top, width, height.
function getWindowBounds() {
  var bounds = {};
  bounds.left = window.screenX ? window.screenX : window.screenLeft;
  bounds.top  = window.screenY ? window.screenY : window.screenTop;
  bounds.width= getWidth();
  bounds.height= getHeight();
  return bounds;
}

//
///////////////////////////////////////////////////////////////////////////////////

function setHtml(target, html, concatenate) {
  if( !target ) { alert('setHtml: no target set!'); return; }
  if( !html   ) html = '';

  html = replaceCodes( html );

  // add to DOM before running inner scripts
  if( concatenate ) {
    target.innerHTML += html;
  } else {
    target.innerHTML = html;
  }

  //
  setThumbnailImages( target );
  setTextOverImages( target );
  setDivTextShadows( target );
  setDivAnchors( target );
  runScripts( html );
}

// Replaces {codes} it finds in the text with the value as retrieved from
// the getCodeValue(code) call.
// The html with changes is returned. If nothing changed the original html
// will be returned.
function replaceCodes(html) {
  var result = '';
  var blockPos = 0;
  var pos;
  for(pos = 0; pos >= 0; pos = html.indexOf( '{', pos ) ) {
    var end = html.indexOf( '}', pos );
    if( end < pos + 30 ) {
      var code = html.substring( pos+1, end );
      if( code.indexOf(' ') < 0 && code.indexOf('\n') < 0 && code.indexOf('\t') < 0 ) {
        var value = getCodeValue( code );
        if( !value ) value = '{' + code + '}';

        result += html.substring( blockPos, pos ) + value;
        blockPos = end+1;
        pos = end+1;
      } else {
        pos++;
      }
    } else {
      pos++;
    }
  }
  if( blockPos > 0 ) {
    result += html.substring( blockPos );
    return result;
  } else {
    return html;
  }
}

// Returns the value from from the 'codeReplacements' map (if it exists)
// for the given code key.
//
// Some values are built in:
//   year      : current year [yyyy]
//   month     : current month [1..12]
//   monthName : current month ['January',...,'December']
//   day       : current day [1..31]
//   weekday   : current day ['Sunday',..,'Saturday']
//   date      : current date [d mmm yyyy]
//   time      : current time [hh:mm:ss]
//
function getCodeValue(code) {
  switch( code ) {
  case 'year':     return new Date().getFullYear();
  case 'month':    return new Date().getMonth()+1;
  case 'monthName':return ['January','February','March','April','May',
                           'June','July','August','September','October',
                           'November','December'] [new Date().getMonth()];
  case 'day':      return new Date().getDate()+1;
  case 'weekday':  return ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'][new Date().getDay()];
  case 'date':     var today = new Date();
                   return (today.getDate() +1) + ' ' +
                          getCodeValue('monthName') +
                          (today.getFullYear());
  case 'time':     var now = new Date();
                   return (now.getHours()  < 10 ? '0' : '') + now.getHours()   + ':' +
                          (now.getMinutes()< 10 ? '0' : '') + now.getMinutes() + ':' +
                          (now.getSeconds()< 10 ? '0' : '') + now.getSeconds() + ':';
  default:
    if( typeof(codeReplacements) != 'undefined' ) {
      return codeReplacements[code];
    } else {
      return null; // not found
    }
  }
}

// Finds all A elements which have a '@id' in the href, where id is the id
// of a div. (simple '@' is interpreted as '@maincontent') and adds event
// handlers to load the result in the given div when clicked.
//
function setDivAnchors(node) {
  if( !node ) {
    setDivAchnors( document.body );
  } else {
    if( node.nodeName == 'A' && node.attributes.href && node.attributes.href.value ) {
      var href = node.attributes.href.value;

      var at = href.indexOf('@');
      if( at > 0 && href.toLowerCase().indexOf('mailto:') < 0 ) {
        var url = href.substring( 0, at );
        var divId = href.substring( at+1 );
        if( divId == '' ) divId = 'maincontent';

        if( divId.length < 30 ) { // this url probably does not refer to an id if id >= 30 chars
          var oldOnClick = node.onclick;
          node.onclick = function(event) {
            if( oldOnClick ) oldOnClick( event );
            loadContent( divId, url );
            return false;
          };
          node.href = url;
        }
      }
    }

    // recurse
    if( node.childNodes ) {
      var nrChildren = node.childNodes.length;
      for(var i=0; i<nrChildren; i++) {
        var child = node.childNodes.item( i );
        if( child ) setDivAnchors( child );
      }
    }
  }
}


// Keep a reference to the thumbnail window, so it can be reused.
var thumbnailWindow = null;
var thumbnailWindowIndex = 1;

// Finds all IMG elements of class "Thumbnail" and containing "src" attribute,
// adds an image with the "<src>_small" (extension will be moved to end),
// and adds a click handler that will open <src> in a new window that is large
// enough to show the image without scaling.
//
function setThumbnailImages(node) {
  if( !node ) {
    setThumbnailImages( document.body );
  } else {

    // img class=thumbnail
    if( node.nodeName == 'IMG' && node.className.toLowerCase() == 'thumbnail' && node.attributes.src && node.attributes.src.value ) {
      var src = node.attributes.src.value;
      var imageTitle = node.title;
      var srcSmall;

      /*
      var hasWidth = (node.attributes.width && node.attributes.width.value);
      var hasHeight= (node.attributes.height && node.attributes.height.value);
      if( hasWidth == 'null' ) hasWidth = null; // ?? happened with IE6
      if( hasHeight == 'null' ) hasHeight = null;
      hasWidth=false;hasHeight=false;

      if( hasWidth || hasHeight ) {
        alert('static size: '+node.attributes.width.value+" x "+node.attributes.height.value);
        srcSmall = src;
      } else {
        var dot = src.lastIndexOf('.');
        if( dot < 0 ) dot = src.length;
        srcSmall = src.substring( 0, dot ) + '_small' + src.substring( dot );
      }
      */
      var dot = src.lastIndexOf('.');
      if( dot < 0 ) dot = src.length;
      srcSmall = src.substring( 0, dot ) + '_small' + src.substring( dot );

      node.style.cursor = 'pointer';
      node.title = 'Click picture to enlarge';
      node.border = 0;
      node.style.padding = '0px';
      //if( hasWidth ) node.style.width = node.attributes.width.value + 'px';
      //if( hasHeight ) node.style.height = node.attributes.height.value + 'px';

      //
      var align = (node.attributes.align ? node.attributes.align.value : null);
      var tnOnLoad = function() {
        var w = node.width;
        var h = node.height;
        var div = document.createElement( 'DIV' );

        div.className = node.className;
        div.style.width = w + 'px';
        div.style.height = h + 'px';
        div.style.marginLeft = 'auto';
        div.style.marginRight= 'auto';
        node.className = null;

        node.parentNode.replaceChild( /*newChild=*/div, /*oldChild=*/node );
        div.appendChild( node );
      };
      if( align == 'center' || align == 'middle' ) {
        // set in timeout because IE won't update the 'complete'
        // state of the image until this thread has finished.
        setTimeout( function() {
          if( node.complete ) {
            tnOnLoad();
          } else {
            addEventHandler( node, 'load', tnOnLoad );
          }
        }, 5 );
      }

      // change img to actual thumbnail
      node.src = srcSmall;
      node.style.visibility = 'visible';

      // When user clicks on thumbnail, open the full image in a new window
      addEventHandler( node, 'click', function() {
        logLink( src );
        var bufferedImage = new Image();
        bufferedImage.onload = function() {
          // browser window bounds
          var winBounds = getWindowBounds();

          var width = bufferedImage.width;
          var height= bufferedImage.height;
          var left = winBounds.left + (winBounds.width - width) / 2;
          var top  = winBounds.top  + (winBounds.height - height ) / 2;

          var wwidth = width;
          var wheight= height;

          // center thumbnail window over browser
          if( isIE() ) {
            wwidth += 18;
            wheight+= 1;
          } else {
            wwidth += 1;
            wheight+= 1;
          }

          // Create new thumbnail window. If a window with that name
          // already exists, it will be reused.
          var thumbnailWindow = openWindow( 'ThumbnailWindow' + (thumbnailWindowIndex++), left, top, wwidth, wheight );
          var img = thumbnailWindow.document.getElementById( 'image' );

          // If thumbnail window was created before, reuse it.
          if( !img ) {
            img = thumbnailWindow.document.createElement( 'IMG' );
            thumbnailWindow.document.body.appendChild( img );
          }

          // Create image to fill thumbnail window
          img.id = 'image';
          img.style.position = 'absolute';
          img.style.left = '0px';
          img.style.top = '0px';
          img.style.width = width + 'px';
          img.style.height = height + 'px';
          img.onclick = function() { thumbnailWindow.close(); }
          img.title = 'Click to close';
          if( imageTitle ) thumbnailWindow.document.title = imageTitle;

          img.style.padding = '0px';
          img.style.margin = '0px';
          img.src = bufferedImage.src;

          thumbnailWindow.document.body.style.overflow = 'auto';
          thumbnailWindow.focus();
        };
        bufferedImage.src = src;
      });
    }

    // recurse
    if( node.childNodes ) {
      var nrChildren = node.childNodes.length;
      for(var i=0; i<nrChildren; i++) {
        var child = node.childNodes.item( i );
        if( child ) setThumbnailImages( child );
      }
    }
  }
}


// Finds all IMG elements containing a 'text' attribute.
//
// text          The text to show over the image, like "1.2.3"
// [textColor]   Color of the text.
// textPos       Position to place text. four arguments, being
//               left, top, right, bottom. The text font size
//               is determined by the difference between top
//               and bottom.
//
// Adds the given text over the image.
//
// Because of browser bugs, don't use 'align' or 'float'
// attribute in the img tag. If you require align/float,
// add a DIV around the image and float it instead.
//
function setTextOverImages(node) {
  if( !node ) {
    setTextOverImages( document.body );
  } else {

    // img
    if( node.nodeName == 'IMG' && node.attributes['text'] && node.attributes['textPos'] ) {
      var img     = node;
      var text    = img.attributes['text'].value;
      var color   = ( node.attributes['textColor'] ? node.attributes['textColor'].value : null );
      var vpos    = node.attributes['textPos'].value;
      var pos     = vpos.split(',');
      while( pos.length < 4 ) pos[pos.length]='0';
      var left    = pos[0];
      var top     = pos[1];
      var right   = pos[2];
      var bottom  = pos[3];

      var container = document.createElement( 'DIV' );
      var textDiv = document.createElement( 'DIV' );

      container.style.position = 'relative';

      textDiv.style.position = 'absolute';
      textDiv.style.left  = left + 'px';
      textDiv.style.width = (right-left) + 'px';
      textDiv.style.top   = top + 'px';
      textDiv.style.cursor= 'default';

      var sstyle = 'font-size:' + (bottom-top) + 'px;' + (color ? 'color:' + color + ';' : '');
      textDiv.innerHTML = '<center style="' + sstyle + '"><b>' + text + '</b></center>';

      img.parentNode.replaceChild( /*newChild=*/container, /*oldChild=*/img );
      container.appendChild( img );
      container.appendChild( textDiv );
    }

    // recurse
    if( node.childNodes ) {
      var nrChildren = node.childNodes.length;
      for(var i=0; i<nrChildren; i++) {
        var child = node.childNodes.item( i );
        if( child ) setTextOverImages( child );
      }
    }
  }
}

// Finds all DIV elements containing a 'textShadow' attribute.
// This attribute can contain a number of colors to be used
// for the shadow of the text in the DIV. Colors are seperated
// by commas.
//
function setDivTextShadows(node) {
  if( !node ) {
    setDivTextShadows( document.body );
  } else {

    // img
    if( node.nodeName == 'DIV' && node.attributes['textShadow'] ) {
      var div = node;
      if( div.style.position == '' ) div.style.position = 'absolute';

      var children = div.childNodes;
      var content = null;
      for(var i=0; i<children.length; i++) {
        var child = children.item( i );
        if( child.firstChild || (child.nodeValue && child.nodeValue.trim() != '') ) {
          content = child;
          break;
        }
      }

      if( content != null ) {
        div.removeChild( content );

        var colors = node.attributes['textShadow'].value.split(',');
        var nrColors = colors.length;

        for(var i=0; i<=nrColors; i++) {
          var shadow = document.createElement( 'DIV' );
          if( i < nrColors ) {
            shadow.style.color = colors[i];
          }
          shadow.style.position = 'absolute';
          shadow.style.left = (nrColors-i) + 'px';
          shadow.style.top  = (nrColors-i) + 'px';
          shadow.appendChild( content.cloneNode(/*deep=*/true) );

          div.appendChild( shadow );
        }
      }
    }

    // recurse
    if( node.childNodes ) {
      var nrChildren = node.childNodes.length;
      for(var i=0; i<nrChildren; i++) {
        var child = node.childNodes.item( i );
        if( child ) setDivTextShadows( child );
      }
    }
  }
}

///////////////////////////////////////////////
// Inline http-requests Calling and handling //
/////////////////////////////////////////////////////////////////////////////

// Run the scripts in the given text, between <script ...> and </script> by
// calling eval(..) for each block of script in the given text.
function runScripts(text) {
  try {
    var scripts = text.replace(/[\n\r]+/g, ' /*n*/ ') // javascript regex has no s flag
                      .match(/<script[^>]*>(.*?)<\/script>/g);
    if( scripts ) {
      for(var si=0; scripts && si<scripts.length; si++) {
        var script = scripts[si];
        if( script.indexOf('redirect.js') >= 0 ) continue;
        
        var src = script.match( /<script[^>]*src="([^"]+)"/ );
        if( src ) {
          src = src[1];
          script = document.createElement( 'SCRIPT' );
          //script.setAttribute( 'SRC', src ); // this doesn't work in IE6
          script.src = src; 
          var head = document.getElementsByTagName('head')[0];
          head.appendChild( script );
        } else {
          script = script.replace( /^<script[^>]*>|<\/script>$/g, '' )
                         .replace(/ \/\*n\*\/ /g, '\n') // put back newlines
                   ;
          window.eval( script ); // eval in window context (doesn't work in IE?)
        }
      }
    }
  } catch(e) { alert(e); }
}

// Add text to attr src & href of elements.
//
// textToAdd     Text to add after 'href=' and 'src='.
// text          Text to add the text to.
// Returns changed text
//
function addPrefixToHtmlLinks(text, textToAdd ) {
  for(var ai=0; ai<3; ai++) {
    var attr;
    if( ai == 0 ) attr = 'src';
    if( ai == 1 ) attr = 'href';
    if( ai == 2 ) attr = 'action';
    var pos = -1;

    while( (pos = text.toLowerCase().indexOf( attr, pos+1)) > 0 ) {
      pos+=attr.length-1;
      var c;
      do {
        c = text.charAt(++pos);
      } while(c==' ' || c == '\t' || c == '=');
      if( c == '\'' || c == '\"' ) {
        var end = text.indexOf( c, pos+1 );
        if( end > 0 ) {
          pos++;
          var url = text.substring( pos, end );
          if( url.toLowerCase().indexOf( ':' ) < 0 && url.indexOf( '/' ) != 0 && url.indexOf('#') != 0 ) {
              text = text.substring( 0, pos ) + textToAdd + text.substring( pos );
          }
        }
      }
    }
  }
  return text;
}

// Check each div element for a src attribute and if it exists load the
// requested file and put it into the div.
//
// [onlyIfNoContent] When set, will only load data in the DIVs that are empty
// [node]            Start point in DOM, or null/undefined to start in root.
//
function loadDivSources(onlyIfNoContent, node) {
  if( !node ) {
    loadDivSources( onlyIfNoContent, document.body );
  } else {

    // div? div.src? empty div? then load data
    if( node.nodeName == 'DIV' ) {
      if( node.attributes.src && (!onlyIfNoContent || node.childNodes.length==0) ) {
        if( document.location.href.indexOf( 'file:' ) >= 0 ) {
          alert('div src is not supported on local file system');
          return;
        }
        if( node.attributes && node.attributes.src ) {
          var url = node.attributes.src.value;

          if( url.indexOf(',') >= 0 ) {
            var urls = url.split(',');
            loadContent( node, urls );
          } else {
            loadContent( node, url );
          }
        }
      }
    }

    // recurse
    var nrChildren = node.childNodes ? node.childNodes.length : 0;
    for(var i=0; i<nrChildren; i++) {
      var child = node.childNodes.item( i );
      if( child ) loadDivSources( onlyIfNoContent, child );
    }
  }
}

// Load contents of given url (which should be from the same site as the
// current page and not local file system) into the target for which the
// id or reference is given. If target element does not have an innerHTML
// field, its parent will be tried until root. If no element for id or
// reference could be found, the errorCallback will be called, if any
// provided.
//
// Returns true when the http request could be performed (but the request
// itself, which is asynchronous, might still fail, hence the errorCallback),
// false otherwise.
//
//
// idOrRef          Id of an element that supports the innerHTML attribute,
//                  like DIV or SPAN.
// [url]            Location of data to load. This location should be on
//                  the same site as the originating page and not on the
//                  local file system. If not provided, the 'src' attribute
//                  of the node will be used. If this parameter is an array
//                  the contents of all urls will be concatenated in the
//                  order of the URLs.
// [callback]       Optional function that, when given, only downloads the
//                  requested data but not yet loads it in target. Instead,
//                  this callback will be called with the target node when
//                  loading has finished succesfully. The second argument
//                  holds the data. Set target.innerHTML=data to set the
//                  data as html in the target. If omitted this callback
//                  will be implemented internally as follows:
//                    function(target,url,data) {
//                      target.innerHTML = data;
//                      runScripts( data );
//                    }
// [errorCallback]  Optional function that will be called with four arguments,
//                  being idOrRef, url, a number (-1 if no target found, otherwise
//                  the http error number) and a description string of the error.
//                  If omitted, no feedback will be given on an error.
// [postvars]       Optional argument. If omitted the url will be called using
//                  a GET http request. If a url-encoded (a=b&c=d&...) string
//                  is provided a POST http request will be performed.
// [username]       Optional authentication. If username is provided, the
//                  password should be provided as well.
// [password]       Optional authentication. If password is provided, the
//                  username should be provided as well.
// [headers]        Optional http header items map. For example:
//                    var headers = { "Referer" : "http://www.tiobe.com", ... }
//
function loadContent(idOrRef, url, callback, errorCallback, postvars, username, password, headers) {
  var target;
  var isId = ( typeof idOrRef == 'string' );

  if( isId ) {
    target = document.getElementById( idOrRef );
  } else {
    target = idOrRef;
  }

  while( target && !isDefined(target.innerHTML) ) target = target.parentNode;

  // if no url provided, get it from target src
  if( target && !url ) {
    if( target.src ) {
      url = target.src;
    } else
    if( target.attributes['src'] ) {
      url = target.attributes['src'].value;
    }
    if( !url && errorCallback ) {
      errorCallback( -1, 'no resource to load found (no url and no src)' );
      return false;
    }
  }


  // perform http request to load requested data into target
  if( target ) {
    var loadedData = {};

    // Called when a url was loaded
    var onLoadedData = function(newUrl, data) {

      // Make all sources (src,href) relative to page url
      var srcPrefix = newUrl.substring( 0, newUrl.lastIndexOf('/')+1 );
      data = addPrefixToHtmlLinks( data, srcPrefix );

      // Added some code to support multiple data loads
      // added to a single div. The loads are asynchronous
      // so can finish in any order.

      // Find the index of the loaded part
      var urlIndex=0;
      if( urls.length > 1 ) {
        for(urlIndex=0; urlIndex<urls.length; urlIndex++) {
          if( newUrl.indexOf(urls[urlIndex]) >= 0 ) break;
        }
      }
      loadedData[urlIndex] = data;

      // Check if all parts have loaded
      for(var dataIndex = 0; dataIndex < urls.length; dataIndex++) {
        if( !loadedData[dataIndex] ) return;
      }

      // Combine all loaded parts
      var allData = '';
      var len = (urls.length ? urls.length : 1);
      for(var dataIndex=0; dataIndex<len; dataIndex++) {
        allData += loadedData[dataIndex];
      }

      // Finished
      data = allData;

      //
      if( callback ) {
        callback( target, url, data );
      } else {
        //target.innerHTML = data; // add to DOM before running inner scripts
        runScripts( data );
        setHtml( target, data );
      }
    };

    // Called on a load failure
    var onLoadError = function(url, errorId, errorText) {
      if( errorCallback ) {
        errorCallback( idOrRef, url, errorId, errorText );
      }
    };

    //
    target.url = url;
    var urls;

    if( typeof url == 'string' ) {
      urls = [ url ];
    } else {
      urls = url;
    }

    // perform the downloads
    for(var i=0; i<urls.length; i++) {
      var base = (window.urlBase && urls[i].indexOf('://')<0 ? urlBase : '');
      performHttpRequest( base + urls[i], onLoadedData, postvars, onLoadError, username, password, headers);
    }

  } else {

    if( errorCallback ) {
      errorCallback( idOrRef, url, -1, isId ? 'Unknown id given to load url in'
                                            : 'Provided object does not support innerHTML');
      return false;
    }
  }
  return true;
}

var noAJAX = false;

// Send an http request and send the returned data as a single string to the
// given callback method. If postvars are given a 'post' will be performed,
// otherwise a 'get' is performed.
//
// Example of use:
//   performHttpRequest( 'somefile.html', function(html) {
//     alert('received html:\n' + html);
//   });
//
// url              Url (without arguments). Note that url should be on the
//                  same server as the calling page is from and does not work
//                  on the local file system.
// callback         function to call when a successful reply was received. The
//                  function will be called with the url and text result of the
//                  http request.
// [postvars]       Optional argument. If omitted the url will be called using
//                  a GET http request. If a url-encoded (a=b&c=d&...) string
//                  is provided a POST http request will be performed.
// [errorCallback]  Optional argument. Will be called if the request fails.
//                  The provided function will be called with the url, status
//                  number and status text.
//                  If omitted, no feedback will be given on an error.
// [username]       Optional authentication. If username is provided, the
//                  password should be provided as well.
// [password]       Optional authentication. If password is provided, the
//                  username should be provided as well.
// [headers]        Optional http header items map. For example:
//                    var headers = { "Referer" : "http://www.tiobe.com", ... }
//
function performHttpRequest(url, callback, postvars, errorCallback, username, password, headers) {
  var req = false;

  // Get the HttpRequest object which is either built in
  // the browser or via an ActiveX object (IE < V7)

  // native XMLHttpRequest object
  if(window.XMLHttpRequest) {
    try {
      req = new XMLHttpRequest();
    } catch(e) {
      req = false;
    }
    // IE ActiveX object
  } else if(window.ActiveXObject) {
    try {
      req = new ActiveXObject("Msxml2.XMLHTTP");
    } catch(e) {
      try {
        req = new ActiveXObject("Microsoft.XMLHTTP");
      } catch(e) {
        req = false;
      }
    }
  }
  //*TESTING!*/req = null;

  //
  if(req) {
    // this function will be called for ever step in the request
    req.onreadystatechange =
      function() {
        try {
          // readyState:    0 = unitialized
          //                1 = loading
          //                2 = loaded
          //                3 = interactive
          //                4 = complete
          // status values: 200 = ok
          //                ...other http response values, like 404 for 'not found'
          // statusText:    Message describing status
          // responseText:  Returned data as a string
          // responseXML:   Returned data as a DOM-accessible XML tree
          //
          if (req.readyState == 4 ) {
            if( req.status == 200) {
                callback( url, req.responseText );
            } else {
                if( errorCallback ) {
                    errorCallback( url, req.status, req.statusText );
                }
            }
          }
        } catch (e) {
            if( errorCallback ) {
                errorCallback( url, 0, e );
            }
        }
      };

    // set http headers, if provided
    if( headers ) {
      for(var key in headers) {
        req.setRequestHeader( key, headers[key] );
      }
    }

    // If no caching is required, make the url unique
    if( window.noCache && noCache ) {
      url += (url.indexOf('?') > 0 ? '&' : '?' ) + '&random=' + getRandomString( 10 );
    }

    // perform the POST or GET
    if( postvars ) {
        req.open("POST", url, /*async=*/true, username, password);
        if( !headers || !headers['Content-Type'] ) {
            req.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        }
        req.send(postvars);
    } else {
        req.open("GET", url, /*async=*/true, username, password );
        req.send( null );
    }

  } else {
    //if( errorCallback ) {
    //    errorCallback( url, 0, "Your browser does not support XMLHttpRequest" );
    //}

    // No AJAX support in browser? Fall back to
    // loading data in IFRAMEs.
    var iframe = document.createElement( 'iframe' );
    addEventHandler( iframe, 'load', function() {
      var html = iframe.contentWindow.document.body.innerHTML;
      iframe.parentNode.removeChild( iframe );

      callback( url, html );
    });
    iframe.style.display = 'none';
    document.body.appendChild( iframe );

    var reqUrl = url;
    if( window.noCache && noCache ) {
      reqUrl += (reqUrl.indexOf('?') > 0 ? '&' : '?' ) + '&random=' + getRandomString( 10 );
    }
    reqUrl += (reqUrl.indexOf('?') > 0 ? '&' : '?' ) + 'standalone';
    iframe.contentWindow.location.href = reqUrl;
  }
}


/////////////////////////////////////////////////////////////////////////////
