 /****************************************************************
  *                                                              *
  *  curvyCorners                                                *
  *  ------------                                                *
  *                                                              *
  *  This script generates rounded corners for your divs.        *
  *                                                              *
  *  Version 1.2.10                                              *
  *  Copyright (c) 2008 Cameron Cooke                            *
  *  By: Cameron Cooke and Tim Hutchison.                        *
  *                                                              *
  *                                                              *
  *  Website: http://www.curvycorners.net                        *
  *  Email:   web@cameroncooke.com                               *
  *  Discuss: http://groups.google.com/group/curvycorners        *
  *                                                              *
  *  Changes:                                                    *
  *                                                              *
  *  29/10:08: Background position bug fix.                      *
  *            (Fix by Dustin Jorge)                             *
  *  11/08/08: Modified to work as a div replacement             *
  *            This should work to replace any existing DIV      *
  *            in existing HTML and not cause reflow issues      *
  *            rounded borders will auto round based on the      *
  *            CSS declarations (see notes below)*               *
  *            (by Terry Riegel riegel@clearimageonline.com)     *
  *                                                              *
  *                                                              *
  *  This library is free software; you can redistribute         *
  *  it and/or modify it under the terms of the GNU              *
  *  Lesser General Public License as published by the           *
  *  Free Software Foundation; either version 2.1 of the         *
  *  License, or (at your option) any later version.             *
  *                                                              *
  *  This library is distributed in the hope that it will        *
  *  be useful, but WITHOUT ANY WARRANTY; without even the       *
  *  implied warranty of MERCHANTABILITY or FITNESS FOR A        *
  *  PARTICULAR PURPOSE. See the GNU Lesser General Public       *
  *  License for more details.                                   *
  *                                                              *
  *  You should have received a copy of the GNU Lesser           *
  *  General Public License along with this library;             *
  *  Inc., 59 Temple Place, Suite 330, Boston,                   *
  *  MA 02111-1307 USA                                           *
  *                                                              *
  ****************************************************************/

/*
Will now autoMagically apply borders via the CSS declarations
Safari, Mozilla, and Chrome all support rounded borders via

-webkit-border-radius, and -moz-border-radius

So instead of reinventing the wheel we will let these browsers render
their borders natively. Firefox for Windows renders non-antialiased
borders so they look a bit ugly. Since this script breaks with Google's
Chrome we will let it render its "ugly" borders as well. So if we let
FireFox, Safari, and Chrome render their borders natively, then we only have
to support IE for rounded borders. Fortunately IE will read CSS properties
that it doesn't understand (both Firefox and Safari discard them)
So we will add yet another CSS declaration that IE can find and apply
the borders to (IEborderRadius)

So to make curvycorners work with any major browser simply add the following
CSS declarations and it should be good to go...

.round { -webkit-border-radius: 25px;
         -moz-border-radius: 25px;
         IEborderRadius: 25px;}

*/



  // Browser detection
  var isIE      = navigator.userAgent.toLowerCase().indexOf("msie") > -1;
  var isMoz     = document.implementation && document.implementation.createDocument;
  var isSafari  = ((navigator.userAgent.toLowerCase().indexOf('safari')!=-1)&&(navigator.userAgent.toLowerCase().indexOf('mac')!=-1))?true:false;
  var BackCompat= document.compatMode.indexOf("BackCompat") > -1;
  /*
  Usage:

  newCornersObj = new curvyCorners(settingsObj, "classNameStr");
  newCornersObj = new curvyCorners(settingsObj, divObj1[, divObj2[, divObj3[, . . . [, divObjN]]]]);
  */


   function styleit()
   {
    if (isIE)
    {
     for(var t = 0; t < document.styleSheets.length; t++)
     {
      for(var i = 0; i < document.styleSheets[t].rules.length; i++)
      {
       var allR = document.styleSheets[t].rules[i].style.IEborderRadius    || 0;
       var tR   = document.styleSheets[t].rules[i].style.IEborderRadiusTR  || allR;
       var tL   = document.styleSheets[t].rules[i].style.IEborderRadiusTL  || allR;
       var bR   = document.styleSheets[t].rules[i].style.IEborderRadiusBR  || allR;
       var bL   = document.styleSheets[t].rules[i].style.IEborderRadiusBL  || allR;

// alert(allR+'|'+tR+'|'+tL+'|'+bR+'|'+bL);

       if (allR || tR || tR || bR || bL)
       {
        var r=document.styleSheets[t].rules[i].style.IEborderRadius;
        var s=document.styleSheets[t].rules[i].selectorText;
        var settings = {tl: { radius: 5 }, tr: { radius: 5 }, bl: { radius: 5 }, br: { radius: 5 }, antiAlias: true   , autoPad: true     , validTags: ["div"]}
        settings.tl.radius=replace(tL,'px','');
        settings.tr.radius=replace(tR,'px','');
        settings.bl.radius=replace(bL,'px','');
        settings.br.radius=replace(bR,'px','');
        var myBoxObject = new curvyCorners(settings, replace(s,'.','') );
        myBoxObject.applyCornersToAll();
       }
      }
     }
    } else if(isMoz)
    {



     for(var t = 0; t < document.styleSheets.length; t++)
     {

//alert(t);

      for(var i = 0; i < document.styleSheets[t].cssRules.length; i++)
      {

//alert(t+'|'+i);


//alert(document.styleSheets[t].cssRules[i].style.MozBorderRadius);
//alert(document.styleSheets[t].cssRules[i].cssText);





       if (document.styleSheets[t].cssRules[i].style.mozBorderRadiusTopleft)
       {
        var r=document.styleSheets[t].rules[i].style.IEborderRadius;
        var s=document.styleSheets[t].rules[i].selectorText;
        var settings = {tl: { radius: 5 }, tr: { radius: 5 }, bl: { radius: 5 }, br: { radius: 5 }, antiAlias: true   , autoPad: true     , validTags: ["div"]}
        settings.tl.radius=replace(r,'px','');
        settings.tr.radius=replace(r,'px','');
        settings.bl.radius=replace(r,'px','');
        settings.br.radius=replace(r,'px','');
        var myBoxObject = new curvyCorners(settings, "myBox");
        myBoxObject.applyCornersToAll();
       }
      }
     }





    }
   }




  function curvyCorners()
  {
      // Check parameters
      if(typeof(arguments[0]) != "object") throw newCurvyError("First parameter of curvyCorners() must be an object.");
      if(typeof(arguments[1]) != "object" && typeof(arguments[1]) != "string") throw newCurvyError("Second parameter of curvyCorners() must be an object or a class name.");

      // Get object(s)
      if(typeof(arguments[1]) == "string")
      {
          // Get elements by class name
          var startIndex = 0;
          var boxCol = getElementsByClass(arguments[1]);
      }
      else
      {
          // Get objects
          var startIndex = 1;
          var boxCol = arguments;
      }

      // Create return collection/object
      var curvyCornersCol = new Array();

      // Create array of html elements that can have rounded corners
      if(arguments[0].validTags)
        var validElements = arguments[0].validTags;
      else
        var validElements = ["div"]; // Default

      // Loop through each argument
      for(var i = startIndex, j = boxCol.length; i < j; i++)
      {
          // Current element tag name
          var currentTag = boxCol[i].tagName.toLowerCase();


          if(inArray(validElements, currentTag) !== false)
          {
             if (!(boxCol[i].style.IEborderRadius=='set'))
              {boxCol[i].style.IEborderRadius='set'; curvyCornersCol[curvyCornersCol.length] = new curvyObject(arguments[0], boxCol[i]);}
          }
      }

      this.objects = curvyCornersCol;

      // Applys the curvyCorners to all objects
      this.applyCornersToAll = function()
      {
          for(var x = 0, k = this.objects.length; x < k; x++)
          {

              this.objects[x].applyCorners();
          }
      }
  }




function right(t,x) { return t.substr(t.length-x) ; }

function left(t,x) { return t.substr(0,x) ; }

function reverse(o)
{
  var s = "";
  var i = o.length;
  while (i>0) {
   s += o.substring(i-1,i);
   i--; }
  return s;
}

function replace(a,b,c) {return a.replace(b,c) ; }

function replaceall(text, strA, strB) 
{ while ( text.indexOf(strA) != -1)
  { text = text.replace(strA,strB); }
  return text; }

function middle(t,a,b) {return t.substring(a-1,b);}

function chopleft(t,a)
{ var b='';
  if (t.search(a)<0)
  {b=t;}
  else
  {b=t.substring(t.search(a));}
  return b;
}

function chopright(t,a)
{ var b='';
  if (t.search(a)<0)
  {b=t;}
  else
  {b=t.substring(0,t.search(a));}
  return b;
}





function ifTop(obj,val) {
 if (obj.settings.tl || obj.settings.tr) {
   return val;
 } else {
   return 0;
 }
}

function ifBot(obj,val) {
 if (obj.settings.bl || obj.settings.br) {
   return val;
 } else {
   return 0;
 }
}




  // curvyCorners object (can be called directly)
  function curvyObject()
  {



      // Setup Globals
      this.box              = arguments[1];
      this.settings         = arguments[0];
      this.topContainer     = null;
      this.bottomContainer  = null;
      this.masterCorners    = new Array();
      this.contentDIV       = null;

      // Get box formatting details
      var boxHeight       = get_style(this.box, "height", "height");
      var boxWidth        = get_style(this.box, "width", "width");
      var borderWidth     = get_style(this.box, "borderTopWidth", "border-top-width");
      var borderWidthB    = get_style(this.box, "borderBottomWidth", "border-bottom-width");
      var borderColour    = get_style(this.box, "borderTopColor", "border-top-color");
      var borderColourB   = get_style(this.box, "borderBottomColor", "border-bottom-color");
      var boxColour       = get_style(this.box, "backgroundColor", "background-color");
      var backgroundImage = get_style(this.box, "backgroundImage", "background-image");
      var boxPosition     = get_style(this.box, "position", "position");
      var boxPadding      = get_style(this.box, "paddingTop", "padding-top");
      var topPadding      = get_style(this.box, "paddingTop", "padding-top");
      var bottomPadding   = get_style(this.box, "paddingBottom", "padding-Bottom");
      var leftPadding     = get_style(this.box, "paddingLeft", "padding-Left");
      var rightPadding    = get_style(this.box, "paddingRight", "padding-Right");


      var topMaxRadius    = Math.max(this.settings.tl ? this.settings.tl.radius : 0, this.settings.tr ? this.settings.tr.radius : 0);
      var botMaxRadius    = Math.max(this.settings.bl ? this.settings.bl.radius : 0, this.settings.br ? this.settings.br.radius : 0);


      // Set formatting propertes
      this.boxHeight       = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight));
      this.boxWidth        = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth));
      this.borderWidth     = parseInt(((borderWidth != "" && borderWidth.indexOf("px") !== -1)? borderWidth.slice(0, borderWidth.indexOf("px")) : 0));
      this.borderWidthB    = parseInt(((borderWidthB != "" && borderWidthB.indexOf("px") !== -1)? borderWidthB.slice(0, borderWidthB.indexOf("px")) : 0));
      this.boxColour       = format_colour(boxColour);
      this.boxPadding      = parseInt(((boxPadding != "" && boxPadding.indexOf("px") !== -1)? boxPadding.slice(0, boxPadding.indexOf("px")) : 0));
      this.topPadding      = parseInt(((topPadding != "" && topPadding.indexOf("px") !== -1)? topPadding.slice(0, topPadding.indexOf("px")) : 0));
      this.bottomPadding   = parseInt(((bottomPadding != "" && bottomPadding.indexOf("px") !== -1)? bottomPadding.slice(0, bottomPadding.indexOf("px")) : 0));
      this.leftPadding     = parseInt(((leftPadding != "" && leftPadding.indexOf("px") !== -1)? leftPadding.slice(0, leftPadding.indexOf("px")) : 0));
      this.rightPadding    = parseInt(((rightPadding != "" && rightPadding.indexOf("px") !== -1)? rightPadding.slice(0, rightPadding.indexOf("px")) : 0));
      this.borderColour    = format_colour(borderColour);
      this.borderColourB   = format_colour(borderColourB);
      this.borderString    = this.borderWidth + "px" + " solid " + this.borderColour;
      this.borderStringB   = this.borderWidthB + "px" + " solid " + this.borderColourB;
      this.backgroundImage = ((backgroundImage != "none")? backgroundImage : "");
      this.boxContent      = this.box.innerHTML;


//alert(this.settings.tl.radius+'|'+this.borderWidth);
//alert(this.settings.tr.radius+'|'+this.borderWidth);
//alert(this.settings.bl.radius+'|'+this.borderWidthB);
//alert(this.settings.br.radius+'|'+this.borderWidthB);

//if (this.settings.tl.radius<=this.borderWidth)  this.settings.tl.radius=this.borderWidth+3;
//if (this.settings.tr.radius<=this.borderWidth)  this.settings.tr.radius=this.borderWidth+3;
//if (this.settings.bl.radius<=this.borderWidthB) this.settings.bl.radius=this.borderWidthB+3;
//if (this.settings.br.radius<=this.borderWidthB) this.settings.br.radius=this.borderWidthB+3;

//alert(this.settings.tl.radius+'|'+this.borderWidth);
//alert(this.settings.tr.radius+'|'+this.borderWidth);
//alert(this.settings.bl.radius+'|'+this.borderWidthB);
//alert(this.settings.br.radius+'|'+this.borderWidthB);


      if(BackCompat)
       {this.box.style.width =parseInt(this.boxWidth)+'px';} else
       {this.box.style.width =parseInt(this.boxWidth+this.leftPadding+this.rightPadding)+'px';}

      if(isIE)
       {if(BackCompat)
        {this.box.style.height=parseInt(this.boxHeight - topMaxRadius - botMaxRadius)+'px';
         this.box.parentNode.style.height=parseInt(this.boxHeight + ifTop(this,this.borderWidth) + ifBot(this,this.borderWidthB))+'px';} else
        {this.box.style.height=parseInt(this.boxHeight + ifTop(this,this.borderWidth) + ifBot(this,this.borderWidthB) - topMaxRadius - botMaxRadius)+'px';
         this.box.parentNode.style.height=parseInt(this.boxHeight + ifTop(this,this.borderWidth) + ifBot(this,this.borderWidthB))+'px';}
       } else {
        this.box.style.height=parseInt(this.boxHeight + this.topPadding + this.bottomPadding + ifTop(this,this.borderWidth) + ifBot(this,this.borderWidthB) - topMaxRadius - botMaxRadius)+'px';
        this.box.parentNode.style.height=parseInt(this.boxHeight + this.topPadding + this.bottomPadding + ifTop(this,this.borderWidth) + ifBot(this,this.borderWidthB))+'px';
       }


      var boxWidth         = get_style(this.box, "width", "width");
      this.boxWidth        = parseInt(((boxWidth != "" && boxWidth != "auto" && boxWidth.indexOf("%") == -1)? boxWidth.substring(0, boxWidth.indexOf("px")) : this.box.scrollWidth));
      var boxHeight        = get_style(this.box, "height", "height");
      this.boxHeight       = parseInt(((boxHeight != "" && boxHeight != "auto" && boxHeight.indexOf("%") == -1)? boxHeight.substring(0, boxHeight.indexOf("px")) : this.box.scrollHeight));



      // Make box relative if not already absolute and remove any padding
      if(boxPosition != "absolute") this.box.style.position = "relative";
      this.box.style.padding = "0px";

      // If IE and height and width are not set, we need to set width so that we get positioning
      if(isIE && boxWidth == "auto" && boxHeight == "auto") this.box.style.width = "100%";

      // Resize box so that it stays to the orignal height


      // Remove content if box is using autoPad
      if(this.settings.autoPad == true && this.boxPadding > 0)
        this.box.innerHTML = "";

      /*
      This method creates the corners and
      applies them to the div element.
      */
      this.applyCorners = function()
      {
          /*
          Create top and bottom containers.
          These will be used as a parent for the corners and bars.
          */
          for(var t = 0; t < 2; t++)
          {
              switch(t)
              {
                  // Top
                  case 0:

                      // Only build top bar if a top corner is to be draw
                      if(this.settings.tl || this.settings.tr)
                      {
                          var newMainContainer = document.createElement("DIV");
                          newMainContainer.style.width    = this.boxWidth+"px";
                          newMainContainer.style.fontSize = "1px";
                          newMainContainer.style.overflow = "hidden";
                          newMainContainer.style.position = "absolute";
                          newMainContainer.style.paddingLeft  = this.borderWidth + "px";
                          newMainContainer.style.paddingRight = this.borderWidth + "px";
                          newMainContainer.style.height = topMaxRadius + "px";
                          newMainContainer.style.top    = 0 - topMaxRadius + "px";
                          newMainContainer.style.left   = 0 - this.borderWidth + "px";
                          this.topContainer = this.box.appendChild(newMainContainer);
                      }
                      break;

                  // Bottom
                  case 1:

                      // Only build bottom bar if a bottom corner is to be draw
                      if(this.settings.bl || this.settings.br)
                      {
                          var newMainContainer = document.createElement("DIV");
                          newMainContainer.style.width    = this.boxWidth+"px";
                          newMainContainer.style.fontSize = "1px";
                          newMainContainer.style.overflow = "hidden";
                          newMainContainer.style.position = "absolute";
                          newMainContainer.style.paddingLeft  = this.borderWidthB + "px";
                          newMainContainer.style.paddingRight = this.borderWidthB + "px";
                          newMainContainer.style.height  =  botMaxRadius + "px";
                          newMainContainer.style.bottom  =  0 - botMaxRadius + "px";
                          newMainContainer.style.left    =  0 - this.borderWidthB + "px";
                          this.bottomContainer = this.box.appendChild(newMainContainer);
                      }
                      break;
              }
          }




          // Turn off current borders


          if(this.topContainer) this.box.style.borderTopWidth = "0px";
          if(this.bottomContainer) this.box.style.borderBottomWidth = "0px";



this.box.style.borderLeftWidth = "0";
this.box.style.borderRightWidth = "0";
if (!isIE)  this.box.style.backgroundColor = 'none';
this.box.style.backgroundImage = 'none';


          // Create array of available corners
          var corners = ["tr", "tl", "br", "bl"];

          /*
          Loop for each corner
          */
          for(var i in corners)
          {
              // FIX for prototype lib
              if(i > -1 < 4)
              {
                  // Get current corner type from array
                  var cc = corners[i];
                  // Has the user requested the currentCorner be round?
                  if(!this.settings[cc] || ((cc=='bl' || cc=='br') && this.borderWidthB==0)  || ((cc=='tl' || cc=='tr') && this.borderWidth==0)  )
                  {
                      // No
                      if(((cc == "tr" || cc == "tl") && this.topContainer != null) || ((cc == "br" || cc == "bl") && this.bottomContainer != null))
                      {
                          // We need to create a filler div to fill the space upto the next horzontal corner.
                          var newCorner = document.createElement("DIV");

                          // Setup corners properties
                          newCorner.style.position = "relative";
                          newCorner.style.fontSize = "1px";
                          newCorner.style.overflow = "hidden";

                          // Add background image?
                          if(this.backgroundImage == "")
                            newCorner.style.backgroundColor = this.boxColour;
                          else
                            newCorner.style.backgroundImage = this.backgroundImage;

                          switch(cc)
                          {
                              case "tl":
                                  newCorner.style.height      = topMaxRadius - this.borderWidth + "px";
                                  newCorner.style.marginRight = this.settings.tr.radius - (this.borderWidth*2) + "px";
                                  newCorner.style.borderLeft  = this.borderString;
                                  newCorner.style.borderTop   = this.borderString;
                                  newCorner.style.left        = -this.borderWidth + "px";
                                  break;

                              case "tr":
                                  newCorner.style.height      = topMaxRadius - this.borderWidth + "px";
                                  newCorner.style.marginLeft  = this.settings.tl.radius - (this.borderWidth*2) + "px";
                                  newCorner.style.borderRight = this.borderString;
                                  newCorner.style.borderTop   = this.borderString;
                                  newCorner.style.backgroundPosition  = "-" + (topMaxRadius + this.borderWidth) + "px 0px";
                                  newCorner.style.left        = this.borderWidth + "px";
                                  break;

                              case "bl":
                                  newCorner.style.height       = botMaxRadius - this.borderWidthB + "px";
                                  newCorner.style.marginRight  = this.settings.br.radius - (this.borderWidthB*2) + "px";
                                  newCorner.style.borderLeft   = this.borderStringB;
                                  newCorner.style.borderBottom = this.borderStringB;
                                  newCorner.style.left         = -this.borderWidthB + "px";
                                  newCorner.style.backgroundPosition = "-" + (this.borderWidthB) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidthB)) + "px";
                                  break;

                              case "br":
                                  newCorner.style.height       = botMaxRadius - this.borderWidthB + "px";
                                  newCorner.style.marginLeft   = this.settings.bl.radius - (this.borderWidthB*2) + "px";
                                  newCorner.style.borderRight  = this.borderStringB;
                                  newCorner.style.borderBottom = this.borderStringB;
                                  newCorner.style.left         = this.borderWidthB + "px"
                                  newCorner.style.backgroundPosition = "-" + (botMaxRadius + this.borderWidthB) + "px -" + (this.boxHeight + (botMaxRadius + this.borderWidthB)) + "px";
                                  break;
                          }
                      }
                  }
                  else
                  {
                      /*
                      PERFORMANCE NOTE:

                      If more than one corner is requested and a corner has been already
                      created for the same radius then that corner will be used as a master and cloned.
                      The pixel bars will then be repositioned to form the new corner type.
                      All new corners start as a bottom right corner.
                      */
//                    if(this.masterCorners[this.settings[cc].radius])
                      if(1==0)
                      {
                          // Create clone of the master corner
                          var newCorner = this.masterCorners[this.settings[cc].radius].cloneNode(true);
                      }
                      else
                      {




// Code to apply correct color to top or bottom
if(cc == "tr" || cc == "tl")
{
 var bwidth=this.borderWidth;
 var bcolor=this.borderColour;
} else {
 var bwidth=this.borderWidthB;
 var bcolor=this.borderColourB;
}
                          // Yes, we need to create a new corner
                          var newCorner = document.createElement("DIV");
                          newCorner.style.height = this.settings[cc].radius + "px";
                          newCorner.style.width  = this.settings[cc].radius + "px";
                          newCorner.style.position = "absolute";
                          newCorner.style.fontSize = "1px";
                          newCorner.style.overflow = "hidden";

                          // THE FOLLOWING BLOCK OF CODE CREATES A ROUNDED CORNER
                          // ---------------------------------------------------- TOP

                          // Get border radius
                          var borderRadius = parseInt(this.settings[cc].radius - this.borderWidth);

                          // Cycle the x-axis
                          for(var intx = 0, j = this.settings[cc].radius; intx < j; intx++)
                          {

                              // Calculate the value of y1 which identifies the pixels inside the border
                              if((intx +1) >= borderRadius)
                                var y1 = -1;
                              else
                                var y1 = (Math.floor(Math.sqrt(Math.pow(borderRadius, 2) - Math.pow((intx+1), 2))) - 1);

                              // Only calculate y2 and y3 if there is a border defined

                              if(borderRadius != j)
                              {
                                  if((intx) >= borderRadius)
                                    var y2 = -1;
                                  else
                                    var y2 = Math.ceil(Math.sqrt(Math.pow(borderRadius,2) - Math.pow(intx, 2)));

                                  if((intx+1) >= j)
                                    var y3 = -1;
                                  else
                                    var y3 = (Math.floor(Math.sqrt(Math.pow(j ,2) - Math.pow((intx+1), 2))) - 1);
                              }

                              // Calculate y4
                              if((intx) >= j)
                                var y4 = -1;
                              else
                                var y4 = Math.ceil(Math.sqrt(Math.pow(j ,2) - Math.pow(intx, 2)));

                              // Draw bar on inside of the border with foreground colour
                              if(y1 > -1) this.drawPixel(intx, 0, this.boxColour, 100, (y1+1), newCorner, -1, this.settings[cc].radius);

                              // Only draw border/foreground antialiased pixels and border if there is a border defined
                              if(borderRadius != j)
                              {
                                  // Cycle the y-axis
                                  for(var inty = (y1 + 1); inty < y2; inty++)
                                  {
                                      // Draw anti-alias pixels
                                      if(this.settings.antiAlias)
                                      {
                                          // For each of the pixels that need anti aliasing between the foreground and border colour draw single pixel divs
                                          if(this.backgroundImage != "")
                                          {
                                              var borderFract = (pixelFraction(intx, inty, borderRadius) * 100);
  
                                              if(borderFract < 30)
                                              {
  										                                        this.drawPixel(intx, inty, bcolor, 100, 1, newCorner, 0, this.settings[cc].radius);
                                              }
  									                                     else
                                              {
  									                                         this.drawPixel(intx, inty, bcolor, 100, 1, newCorner, -1, this.settings[cc].radius);
                                              }
                                          }
                                          else
                                          {
                                              var pixelcolour = BlendColour(this.boxColour, bcolor, pixelFraction(intx, inty, borderRadius));
                                              this.drawPixel(intx, inty, pixelcolour, 100, 1, newCorner, 0, this.settings[cc].radius, cc);
                                          }
                                      }
                                  }

                                  // Draw bar for the border
                                  if(this.settings.antiAlias)
                                  {
                                      if(y3 >= y2)
                                      {
                                         if (y2 == -1) y2 = 0;
                                         this.drawPixel(intx, y2, bcolor, 100, (y3 - y2 + 1), newCorner, 0, 0);
                                      }
                                  }
                                  else
                                  {
                                      if(y3 >= y1)
                                      {
                                          this.drawPixel(intx, (y1 + 1), bcolor, 100, (y3 - y1), newCorner, 0, 0);
                                      }
                                  }

                                  // Set the colour for the outside curve
                                  var outsideColour = bcolor;
                              }
                              else
                              {
                                  // Set the colour for the outside curve
                                  var outsideColour = this.boxColour;
                                  var y3 = y1;
                              }

                              // Draw aa pixels?
                              if(this.settings.antiAlias)
                              {
                                  // Cycle the y-axis and draw the anti aliased pixels on the outside of the curve
                                  for(var inty = (y3 + 1); inty < y4; inty++)
                                  {
                                      // For each of the pixels that need anti aliasing between the foreground/border colour & background draw single pixel divs
                                      this.drawPixel(intx, inty, outsideColour, (pixelFraction(intx, inty , j) * 100), 1, newCorner, ((this.borderWidth > 0)? 0 : -1), this.settings[cc].radius);
                                  }
                              }
                          }




                          // END OF CORNER CREATION
                          // ---------------------------------------------------- END


                          // We now need to store the current corner in the masterConers array
                          this.masterCorners[this.settings[cc].radius] = newCorner.cloneNode(true);
                      }




                      /*
                      Now we have a new corner we need to reposition all the pixels unless
                      the current corner is the bottom right.
                      */
                      if(cc != "br")
                      {
                          // Loop through all children (pixel bars)
                          for(var t = 0, k = newCorner.childNodes.length; t < k; t++)
                          {
                              // Get current pixel bar
                              var pixelBar = newCorner.childNodes[t];

                              // Get current top and left properties
                              var pixelBarTop    = parseInt(pixelBar.style.top.substring(0, pixelBar.style.top.indexOf("px")));
                              var pixelBarLeft   = parseInt(pixelBar.style.left.substring(0, pixelBar.style.left.indexOf("px")));
                              var pixelBarHeight = parseInt(pixelBar.style.height.substring(0, pixelBar.style.height.indexOf("px")));

                              // Reposition pixels
                              if(cc == "tl" || cc == "bl"){
                                  pixelBar.style.left = this.settings[cc].radius -pixelBarLeft -1 + "px"; // Left
                              }
                              if(cc == "tr" || cc == "tl"){
                                  pixelBar.style.top =  this.settings[cc].radius -pixelBarHeight -pixelBarTop + "px"; // Top
                              }

                              switch(cc)
                              {
                                  case "tr":
                                      pixelBar.style.backgroundPosition  = "-" + Math.abs((this.boxWidth - this.settings[cc].radius + this.borderWidth) + pixelBarLeft) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px";
                                      break;

                                  case "tl":
                                      pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1)  - this.borderWidth) + "px -" + Math.abs(this.settings[cc].radius -pixelBarHeight -pixelBarTop - this.borderWidth) + "px";
                                      break;

                                  case "bl":
                                      pixelBar.style.backgroundPosition = "-" + Math.abs((this.settings[cc].radius -pixelBarLeft -1) - this.borderWidth) + "px -" + Math.abs((this.boxHeight + this.settings[cc].radius + pixelBarTop) -this.borderWidth) + "px";
                                      break;
                              }
                          }
                      }
                  }

                  if(newCorner)
                  {
                      // Position the container
                      switch(cc)
                      {

                          case "tl":
                            if(newCorner.style.position == "absolute") newCorner.style.top  = "0px";
                            if(newCorner.style.position == "absolute") newCorner.style.left = "0px";
                            if(this.topContainer) this.topContainer.appendChild(newCorner);
                            break;

                          case "tr":
                            if(newCorner.style.position == "absolute") newCorner.style.top  = "0px";
                            if (BackCompat)
                             {if(newCorner.style.position == "absolute") newCorner.style.right = "-1px";} else
                             {if(newCorner.style.position == "absolute") newCorner.style.right = "0px";}
                            if(this.topContainer) this.topContainer.appendChild(newCorner);
                            break;

                          case "bl":
                            if(newCorner.style.position == "absolute") newCorner.style.bottom  = "0px";
                            if(newCorner.style.position == "absolute") newCorner.style.left = "0px";
                            if(this.bottomContainer) this.bottomContainer.appendChild(newCorner);
                            break;

                          case "br":
                            if(newCorner.style.position == "absolute") newCorner.style.bottom   = "0px";
                            if (BackCompat)
                             {if(newCorner.style.position == "absolute") newCorner.style.right = "-1px";} else
                             {if(newCorner.style.position == "absolute") newCorner.style.right = "0px";}
                            if(this.bottomContainer) this.bottomContainer.appendChild(newCorner);
                            break;
                      }
                  }





              }

          }


if (!(this.box.parentNode.tagName=='TD')) this.box.style.top = topMaxRadius + 'px'; else this.box.style.top = parseInt((topMaxRadius-botMaxRadius)/2)+'px';
this.box.style.borderLeftWidth = this.borderWidth+"px";
this.box.style.borderRightWidth = this.borderWidth+"px";
this.box.style.backgroundColor = this.boxColour;
this.box.style.backgroundImage = this.backgroundImage;
this.box.style.backgroundPosition = '0 ' + ifTop(this,this.borderWidth) + 'px';





          /*
          The last thing to do is draw the rest of the filler DIVs.
          We only need to create a filler DIVs when two corners have
          diffrent radiuses in either the top or bottom container.
          */

          // Find out which corner has the bigger radius and get the difference amount
          var radiusDiff = new Array();
          radiusDiff["t"] = Math.abs(this.settings.tl.radius - this.settings.tr.radius)
          radiusDiff["b"] = Math.abs(this.settings.bl.radius - this.settings.br.radius);

          for(z in radiusDiff)
          {
              // FIX for prototype lib
              if(z == "t" || z == "b")
              {
                  if(radiusDiff[z])
                  {
                      // Get the type of corner that is the smaller one
                      var smallerCornerType = ((this.settings[z + "l"].radius < this.settings[z + "r"].radius)? z +"l" : z +"r");

                      // First we need to create a DIV for the space under the smaller corner
                      var newFiller = document.createElement("DIV");
                      newFiller.style.height = radiusDiff[z] + "px";
                      newFiller.style.width  =  this.settings[smallerCornerType].radius+ "px"
                      newFiller.style.position = "absolute";
                      newFiller.style.fontSize = "1px";
                      newFiller.style.overflow = "hidden";
                      newFiller.style.backgroundColor = this.boxColour;
                      //newFiller.style.backgroundColor = get_random_color();

                      // Position filler
                      switch(smallerCornerType)
                      {
                          case "tl":
                              newFiller.style.bottom = "0px";
                              newFiller.style.left   = "0px";
                              newFiller.style.borderLeft = this.borderString;
                              this.topContainer.appendChild(newFiller);
                              break;

                          case "tr":
                              newFiller.style.bottom = "0px";
                              newFiller.style.right  = "0px";
                              newFiller.style.borderRight = this.borderString;
                              this.topContainer.appendChild(newFiller);
                              break;

                          case "bl":
                              newFiller.style.top    = "0px";
                              newFiller.style.left   = "0px";
                              newFiller.style.borderLeft = this.borderStringB;
                              this.bottomContainer.appendChild(newFiller);
                              break;

                          case "br":
                              newFiller.style.top    = "0px";
                              newFiller.style.right  = "0px";
                              newFiller.style.borderRight = this.borderStringB;
                              this.bottomContainer.appendChild(newFiller);
                              break;
                      }
                  }






                  // Create the bar to fill the gap between each corner horizontally
                  var newFillerBar = document.createElement("DIV");
                  newFillerBar.style.position = "relative";
                  newFillerBar.style.fontSize = "1px";
                  newFillerBar.style.overflow = "hidden";
                  newFillerBar.style.backgroundColor = this.boxColour;
                  newFillerBar.style.backgroundImage = this.backgroundImage;

                  switch(z)
                  {
                      case "t":
                          // Top Bar
                          if(this.topContainer)
                          {
                              // Edit by Asger Hallas: Check if settings.xx.radius is not false
                              if(this.settings.tl.radius && this.settings.tr.radius)
                              {
                                  if (BackCompat)
                                   {newFillerBar.style.height      = 100 + topMaxRadius + "px";} else
                                   {newFillerBar.style.height      = 100 + topMaxRadius - this.borderWidth + "px";}



                                  newFillerBar.style.marginLeft  = this.settings.tl.radius - this.borderWidth + "px";
                                  newFillerBar.style.marginRight = this.settings.tr.radius - this.borderWidth + "px";

                                  newFillerBar.style.borderTop   = this.borderString;




                                  if(this.backgroundImage != "")
                                    newFillerBar.style.backgroundPosition  = "-" + parseInt(topMaxRadius - this.borderWidth) + "px 0px";





                                  this.topContainer.appendChild(newFillerBar);
                              }

                              // Repos the boxes background image
                              this.box.style.backgroundPosition      = "0px -" + parseInt(topMaxRadius - this.borderWidth) + "px";




                          }
                          break;

                      case "b":
                          if(this.bottomContainer)
                          {
                              // Edit by Asger Hallas: Check if settings.xx.radius is not false
                              if(this.settings.bl.radius && this.settings.br.radius)
                              {
                                  // Bottom Bar
                                  if (BackCompat)
                                   {newFillerBar.style.height     = botMaxRadius + "px";} else
                                   {newFillerBar.style.height     = botMaxRadius - this.borderWidth + "px";}
                                  newFillerBar.style.marginLeft   = this.settings.bl.radius - this.borderWidth + "px";
                                  newFillerBar.style.marginRight  = this.settings.br.radius - this.borderWidth + "px";
                                  newFillerBar.style.borderBottom = this.borderStringB;

                                  if(this.backgroundImage != "")
                                    newFillerBar.style.backgroundPosition  = "-" + (botMaxRadius - this.borderWidth) + "px -" + (this.boxHeight + (topMaxRadius - this.borderWidth)) + "px";

                                  this.bottomContainer.appendChild(newFillerBar);
                              }
                          }
                          break;
                  }





















              }
          }

          /*
          AutoPad! apply padding if set.
          */
          if(this.settings.autoPad == true && this.boxPadding > 0)
          {




              // Create content container
              var contentContainer = document.createElement("DIV");

              // Set contentContainer's properties
              contentContainer.style.position = "relative";
              contentContainer.innerHTML      = this.boxContent;
              contentContainer.className      = "autoPadDiv";

              // Get padding amounts
              var topPadding = Math.abs(ifTop(this,this.borderWidth)  + this.boxPadding);
              var botPadding = Math.abs(ifBot(this,this.borderWidthB) + this.boxPadding);

              // Apply top padding
              if(topMaxRadius < this.boxPadding)
                {contentContainer.style.paddingTop = parseInt(topPadding - topMaxRadius) + "px";} else
                {contentContainer.style.paddingTop = "0";
                 contentContainer.style.top = parseInt(topPadding - topMaxRadius)+"px";}

              // Apply Bottom padding
              if(botMaxRadius < this.boxPadding)
                {contentContainer.style.paddingBottom = parseInt(botPadding - botMaxRadius) + "px";} else
                {contentContainer.style.paddingBottom = "0";}

              // Apply left and right padding
              contentContainer.style.paddingLeft = this.leftPadding + "px";
              contentContainer.style.paddingRight = this.rightPadding + "px";




              // Append contentContainer
              this.contentDIV = this.box.appendChild(contentContainer);



          }





      }








































      /*
      This function draws the pixels
      */
      this.drawPixel = function(intx, inty, colour, transAmount, height, newCorner, image, cornerRadius)
      {
          // Create pixel
          var pixel = document.createElement("DIV");
          pixel.style.height   = height + "px";
          pixel.style.width    = "1px";
          pixel.style.position = "absolute";
          pixel.style.fontSize = "1px";
          pixel.style.overflow = "hidden";

          // Max Top Radius
          var topMaxRadius = Math.max(this.settings["tr"].radius, this.settings["tl"].radius);

          // Dont apply background image to border pixels
          if(image == -1 && this.backgroundImage != "")
          {
              pixel.style.backgroundImage = this.backgroundImage;
			           pixel.style.backgroundPosition  = "-" + (this.boxWidth - (cornerRadius - intx) + this.borderWidth) + "px -" + ((this.boxHeight + topMaxRadius + inty) -this.borderWidth) + "px";
		        }
          else
          {

// alert(colour);

              pixel.style.backgroundColor = colour;
          }

          // Set opacity if the transparency is anything other than 100
          if (transAmount != 100)
            setOpacity(pixel, transAmount);

          // Set the pixels position
          pixel.style.top = inty + "px";
          pixel.style.left = intx + "px";

          newCorner.appendChild(pixel);
      }
  }

  // ------------- UTILITY FUNCTIONS

  // Inserts a element after another
  function insertAfter(parent, node, referenceNode)
  {
	     parent.insertBefore(node, referenceNode.nextSibling);
  }

  /*
  Blends the two colours by the fraction
  returns the resulting colour as a string in the format "#FFFFFF"
  */
  function BlendColour(Col1, Col2, Col1Fraction)
  {
      var red1 = parseInt(Col1.substr(1,2),16);
      var green1 = parseInt(Col1.substr(3,2),16);
      var blue1 = parseInt(Col1.substr(5,2),16);
      var red2 = parseInt(Col2.substr(1,2),16);
      var green2 = parseInt(Col2.substr(3,2),16);
      var blue2 = parseInt(Col2.substr(5,2),16);

      if(Col1Fraction > 1 || Col1Fraction < 0) Col1Fraction = 1;

      var endRed = Math.round((red1 * Col1Fraction) + (red2 * (1 - Col1Fraction)));
      if(endRed > 255) endRed = 255;
      if(endRed < 0) endRed = 0;

      var endGreen = Math.round((green1 * Col1Fraction) + (green2 * (1 - Col1Fraction)));
      if(endGreen > 255) endGreen = 255;
      if(endGreen < 0) endGreen = 0;

      var endBlue = Math.round((blue1 * Col1Fraction) + (blue2 * (1 - Col1Fraction)));
      if(endBlue > 255) endBlue = 255;
      if(endBlue < 0) endBlue = 0;

      return "#" + IntToHex(endRed)+ IntToHex(endGreen)+ IntToHex(endBlue);
  }

  /*
  Converts a number to hexadecimal format
  */
  function IntToHex(strNum)
  {
      base = strNum / 16;
      rem = strNum % 16;
      base = base - (rem / 16);
      baseS = MakeHex(base);
      remS = MakeHex(rem);

      return baseS + '' + remS;
  }


  /*
  gets the hex bits of a number
  */
  function MakeHex(x)
  {
      if((x >= 0) && (x <= 9))
      {
          return x;
      }
      else
      {
          switch(x)
          {
              case 10: return "A";
              case 11: return "B";
              case 12: return "C";
              case 13: return "D";
              case 14: return "E";
              case 15: return "F";
          }
      }
  }


  /*
  For a pixel cut by the line determines the fraction of the pixel on the 'inside' of the
  line.  Returns a number between 0 and 1
  */
  function pixelFraction(x, y, r)
  {
      var pixelfraction = 0;

      /*
      determine the co-ordinates of the two points on the perimeter of the pixel that the
      circle crosses
      */
      var xvalues = new Array(1);
      var yvalues = new Array(1);
      var point = 0;
      var whatsides = "";

      // x + 0 = Left
      var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x,2)));

      if ((intersect >= y) && (intersect < (y+1)))
      {
          whatsides = "Left";
          xvalues[point] = 0;
          yvalues[point] = intersect - y;
          point =  point + 1;
      }
      // y + 1 = Top
      var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y+1,2)));

      if ((intersect >= x) && (intersect < (x+1)))
      {
          whatsides = whatsides + "Top";
          xvalues[point] = intersect - x;
          yvalues[point] = 1;
          point = point + 1;
      }
      // x + 1 = Right
      var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(x+1,2)));

      if ((intersect >= y) && (intersect < (y+1)))
      {
          whatsides = whatsides + "Right";
          xvalues[point] = 1;
          yvalues[point] = intersect - y;
          point =  point + 1;
      }
      // y + 0 = Bottom
      var intersect = Math.sqrt((Math.pow(r,2) - Math.pow(y,2)));

      if ((intersect >= x) && (intersect < (x+1)))
      {
          whatsides = whatsides + "Bottom";
          xvalues[point] = intersect - x;
          yvalues[point] = 0;
      }

      /*
      depending on which sides of the perimeter of the pixel the circle crosses calculate the
      fraction of the pixel inside the circle
      */
      switch (whatsides)
      {
              case "LeftRight":
              pixelfraction = Math.min(yvalues[0],yvalues[1]) + ((Math.max(yvalues[0],yvalues[1]) - Math.min(yvalues[0],yvalues[1]))/2);
              break;

              case "TopRight":
              pixelfraction = 1-(((1-xvalues[0])*(1-yvalues[1]))/2);
              break;

              case "TopBottom":
              pixelfraction = Math.min(xvalues[0],xvalues[1]) + ((Math.max(xvalues[0],xvalues[1]) - Math.min(xvalues[0],xvalues[1]))/2);
              break;

              case "LeftBottom":
              pixelfraction = (yvalues[0]*xvalues[1])/2;
              break;

              default:
              pixelfraction = 1;
      }

      return pixelfraction;
  }


  // This function converts CSS rgb(x, x, x) to hexadecimal
  function rgb2Hex(rgbColour)
  {
      try{

          // Get array of RGB values
          var rgbArray = rgb2Array(rgbColour);

          // Get RGB values
          var red   = parseInt(rgbArray[0]);
          var green = parseInt(rgbArray[1]);
          var blue  = parseInt(rgbArray[2]);

          // Build hex colour code
          var hexColour = "#" + IntToHex(red) + IntToHex(green) + IntToHex(blue);
      }
      catch(e){

          alert("There was an error converting the RGB value to Hexadecimal in function rgb2Hex");
      }

      return hexColour;
  }

  // Returns an array of rbg values
  function rgb2Array(rgbColour)
  {
      // Remove rgb()
      var rgbValues = rgbColour.substring(4, rgbColour.indexOf(")"));

      // Split RGB into array
      var rgbArray = rgbValues.split(", ");

      return rgbArray;
  }

  /*
  Function by Simon Willison from sitepoint.com
  Modified by Cameron Cooke adding Safari's rgba support
  */
  function setOpacity(obj, opacity)
  {
      opacity = (opacity == 100)?99.999:opacity;

      if(isSafari && obj.tagName != "IFRAME")
      {
          // Get array of RGB values
          var rgbArray = rgb2Array(obj.style.backgroundColor);

          // Get RGB values
          var red   = parseInt(rgbArray[0]);
          var green = parseInt(rgbArray[1]);
          var blue  = parseInt(rgbArray[2]);

          // Safari using RGBA support
          obj.style.backgroundColor = "rgba(" + red + ", " + green + ", " + blue + ", " + opacity/100 + ")";
      }
      else if(typeof(obj.style.opacity) != "undefined")
      {
          // W3C
          obj.style.opacity = opacity/100;
      }
      else if(typeof(obj.style.MozOpacity) != "undefined")
      {
          // Older Mozilla
          obj.style.MozOpacity = opacity/100;
      }
      else if(typeof(obj.style.filter) != "undefined")
      {
          // IE
          obj.style.filter = "alpha(opacity:" + opacity + ")";
      }
      else if(typeof(obj.style.KHTMLOpacity) != "undefined")
      {
          // Older KHTML Based Browsers
          obj.style.KHTMLOpacity = opacity/100;
      }
  }

  /*
  Returns index if the passed value is found in the
  array otherwise returns false.
  */
  function inArray(array, value)
  {
      for(var i = 0; i < array.length; i++){

          // Matches identical (===), not just similar (==).
          if (array[i] === value) return i;
      }

      return false;
  }

  /*
  Returns true if the passed value is found as a key
  in the array otherwise returns false.
  */
  function inArrayKey(array, value)
  {
      for(key in array){

          // Matches identical (===), not just similar (==).
          if(key === value) return true;
      }

      return false;
  }

  // Cross browser add event wrapper
  function addEvent(elm, evType, fn, useCapture) {
	  if (elm.addEventListener) {
		  elm.addEventListener(evType, fn, useCapture);
		  return true;
	  }
	  else if (elm.attachEvent) {
		  var r = elm.attachEvent('on' + evType, fn);
		  return r;
	  }
	  else {
		  elm['on' + evType] = fn;
	  }
  }

  // Cross browser remove event wrapper
  function removeEvent(obj, evType, fn, useCapture){
    if (obj.removeEventListener){
      obj.removeEventListener(evType, fn, useCapture);
      return true;
    } else if (obj.detachEvent){
      var r = obj.detachEvent("on"+evType, fn);
      return r;
    } else {
      alert("Handler could not be removed");
    }
  }

  // Formats colours
  function format_colour(colour)
  {
      var returnColour = "#ffffff";

      // Make sure colour is set and not transparent
      if(colour != "" && colour != "transparent")
      {
          // RGB Value?
          if(colour.substr(0, 3) == "rgb")
          {
              // Get HEX aquiv.
              returnColour = rgb2Hex(colour);
          }
          else if(colour.length == 4)
          {
              // 3 chr colour code add remainder
              returnColour = "#" + colour.substring(1, 2) + colour.substring(1, 2) + colour.substring(2, 3) + colour.substring(2, 3) + colour.substring(3, 4) + colour.substring(3, 4);
          }
          else
          {
              // Normal valid hex colour
              returnColour = colour;
          }
      }

      return returnColour;
  }

  // Returns the style value for the property specfied
  function get_style(obj, property, propertyNS)
  {
      try
      {
          if(obj.currentStyle)
          {
              var returnVal = eval("obj.currentStyle." + property);
          }
          else
          {
              /*
              Safari does not expose any information for the object if display is
              set to none is set so we temporally enable it.
              */
              if(isSafari && obj.style.display == "none")
              {
                obj.style.display = "";
                var wasHidden = true;
              }

              var returnVal = document.defaultView.getComputedStyle(obj, '').getPropertyValue(propertyNS);

              // Rehide the object
              if(isSafari && wasHidden)
              {
                  obj.style.display = "none";
              }
          }
      }
      catch(e)
      {
          // Do nothing
      }

      return returnVal;
  }

  // Get elements by class by Dustin Diaz.
function getElementsByClass(searchClass,node,tag) {
	var classElements = new Array();
	if ( node == null )
		node = document;
	if ( tag == null )
		tag = '*';
	var els = node.getElementsByTagName(tag);
	var elsLen = els.length;
	var pattern = new RegExp("(^|\\\\s)"+searchClass+"(\\\\s|$)");
	for (i = 0, j = 0; i < elsLen; i++) {
		if ( pattern.test(els[i].className) ) {
			classElements[j] = els[i];
			j++;
		}
	}
	return classElements;
}






  // Displays error message
  function newCurvyError(errorMessage)
  {
      return new Error("curvyCorners Error:\n" + errorMessage)
  }


























/*
    json.js
    2007-04-30

    Public Domain

    This file adds these methods to JavaScript:

        array.toJSONString()
        boolean.toJSONString()
        date.toJSONString()
        number.toJSONString()
        object.toJSONString()
        string.toJSONString()
            These methods produce a JSON text from a JavaScript value.
            It must not contain any cyclical references. Illegal values
            will be excluded.

            The default conversion for dates is to an ISO string. You can
            add a toJSONString method to any date object to get a different
            representation.

        string.parseJSON(filter)
            This method parses a JSON text to produce an object or
            array. It can throw a SyntaxError exception.

            The optional filter parameter is a function which can filter and
            transform the results. It receives each of the keys and values, and
            its return value is used instead of the original value. If it
            returns what it received, then structure is not modified. If it
            returns undefined then the member is deleted.

            Example:

            // Parse the text. If a key contains the string 'date' then
            // convert the value to a date.

            myData = text.parseJSON(function (key, value) {
                return key.indexOf('date') >= 0 ? new Date(value) : value;
            });

    It is expected that these methods will formally become part of the
    JavaScript Programming Language in the Fourth Edition of the
    ECMAScript standard in 2008.

    This file will break programs with improper for..in loops. See
    http://yuiblog.com/blog/2006/09/26/for-in-intrigue/

    This is a reference implementation. You are free to copy, modify, or
    redistribute.

    Use your own copy. It is extremely unwise to load untrusted third party
    code into your pages.
*/

/*jslint evil: true */

// Augment the basic prototypes if they have not already been augmented.

if (!Object.prototype.toJSONString) {

    Array.prototype.toJSONString = function () {
        var a = ['['],  // The array holding the text fragments.
            b,          // A boolean indicating that a comma is required.
            i,          // Loop counter.
            l = this.length,
            v;          // The value to be stringified.

        function p(s) {

// p accumulates text fragments in an array. It inserts a comma before all
// except the first fragment.

            if (b) {
                a.push(',');
            }
            a.push(s);
            b = true;
        }

// For each value in this array...

        for (i = 0; i < l; i += 1) {
            v = this[i];
            switch (typeof v) {

// Serialize a JavaScript object value. Ignore objects thats lack the
// toJSONString method. Due to a specification error in ECMAScript,
// typeof null is 'object', so watch out for that case.

            case 'object':
                if (v) {
                    if (typeof v.toJSONString === 'function') {
                        p(v.toJSONString());
                    }
                } else {
                    p("null");
                }
                break;

// Otherwise, serialize the value.

            case 'string':
            case 'number':
            case 'boolean':
                p(v.toJSONString());

// Values without a JSON representation are ignored.

            }
        }

// Join all of the fragments together and return.

        a.push(']');
        return a.join('');
    };


    Boolean.prototype.toJSONString = function () {
        return String(this);
    };


    Date.prototype.toJSONString = function () {

// Ultimately, this method will be equivalent to the date.toISOString method.

        function f(n) {

// Format integers to have at least two digits.

            return n < 10 ? '0' + n : n;
        }

        return '"' + this.getFullYear() + '-' +
                f(this.getMonth() + 1) + '-' +
                f(this.getDate()) + 'T' +
                f(this.getHours()) + ':' +
                f(this.getMinutes()) + ':' +
                f(this.getSeconds()) + '"';
    };


    Number.prototype.toJSONString = function () {

// JSON numbers must be finite. Encode non-finite numbers as null.

        return isFinite(this) ? String(this) : "null";
    };


    Object.prototype.toJSONString = function () {
        var a = ['{'],  // The array holding the text fragments.
            b,          // A boolean indicating that a comma is required.
            k,          // The current key.
            v;          // The current value.

        function p(s) {

// p accumulates text fragment pairs in an array. It inserts a comma before all
// except the first fragment pair.

            if (b) {
                a.push(',');
            }
            a.push(k.toJSONString(), ':', s);
            b = true;
        }

// Iterate through all of the keys in the object, ignoring the proto chain.

        for (k in this) {
            if (this.hasOwnProperty(k)) {
                v = this[k];
                switch (typeof v) {

// Serialize a JavaScript object value. Ignore objects that lack the
// toJSONString method. Due to a specification error in ECMAScript,
// typeof null is 'object', so watch out for that case.

                case 'object':
                    if (v) {
                        if (typeof v.toJSONString === 'function') {
                            p(v.toJSONString());
                        }
                    } else {
                        p("null");
                    }
                    break;

                case 'string':
                case 'number':
                case 'boolean':
                    p(v.toJSONString());

// Values without a JSON representation are ignored.

                }
            }
        }

// Join all of the fragments together and return.

        a.push('}');
        return a.join('');
    };


    (function (s) {

// Augment String.prototype. We do this in an immediate anonymous function to
// avoid defining global variables.

// m is a table of character substitutions.

        var m = {
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };


        s.parseJSON = function (filter) {
            var j;

            function walk(k, v) {
                var i;
                if (v && typeof v === 'object') {
                    for (i in v) {
                        if (v.hasOwnProperty(i)) {
                            v[i] = walk(i, v[i]);
                        }
                    }
                }
                return filter(k, v);
            }


// Parsing happens in three stages. In the first stage, we run the text against
// a regular expression which looks for non-JSON characters. We are especially
// concerned with '()' and 'new' because they can cause invocation, and '='
// because it can cause mutation. But just to be safe, we will reject all
// unexpected characters.

            if (/^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/.
                    test(this)) {

// In the second stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.

                try {
                    j = eval('(' + this + ')');
                } catch (e) {
                    throw new SyntaxError("parseJSON");
                }
            } else {
                throw new SyntaxError("parseJSON");
            }

// In the optional third stage, we recursively walk the new structure, passing
// each name/value pair to a filter function for possible transformation.

            if (typeof filter === 'function') {
                j = walk('', j);
            }
            return j;
        };


        s.toJSONString = function () {

// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can simply slap some quotes around it.
// Otherwise we must also replace the offending characters with safe
// sequences.

            if (/["\\\x00-\x1f]/.test(this)) {
                return '"' + this.replace(/([\x00-\x1f\\"])/g, function (a, b) {
                    var c = m[b];
                    if (c) {
                        return c;
                    }
                    c = b.charCodeAt();
                    return '\\u00' +
                        Math.floor(c / 16).toString(16) +
                        (c % 16).toString(16);
                }) + '"';
            }
            return '"' + this + '"';
        };
    })(String.prototype);
}


