var fontMinHeight = 1;
var fontMaxHeight = 13;

var currentFontWhich      = '';
var currentFontColorWhich = '';

var colorPicker;

var impressFormObj       ; 
var impressFontsObj      ; 
var impressFontColorsObj ; 

var xmlhttp=false;

function ajaxInit() {
    /*@cc_on @*/
    /*@if (@_jscript_version >= 5)
    // JScript gives us Conditional compilation, we can cope with old IE versions.
    // and security blocked creation of the objects.
    try {
        xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
    } catch (e) {
        try {
            xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
        } catch (E) {
            xmlhttp = false;
        }
    }
    @end @*/
    if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
        try {
            xmlhttp = new XMLHttpRequest();
        } catch (e) {
            xmlhttp=false;
        }
    }
    if (!xmlhttp && window.createRequest) {
        try {
            xmlhttp = window.createRequest();
        } catch (e) {
            xmlhttp=false;
        }
    }
}
ajaxInit();

var formObj = function (){
    this.font1         = 0                    ;
    this.color1        = 0                    ;
    this.text1         = 'Enter Text Here...' ;
    this.letterheight1 = 2                    ;

    this.font2         = 0                    ;
    this.color2        = 0                    ;
    this.text2         = ''                   ;
    this.letterheight2 = 2                    ;

    this.halign        = 'center'             ;
    this.valign        = 'center'             ;

    this.mirrorimage   = false                ;
    this.showgrid      = true                 ;

    this.textpath      = 'straight'           ;
    this.textpathw     = 10                   ;
    this.textpathh     = 10                   ;

    this.bgColor       = '255,255,255'        ;

    this.getValue = function(k)
    {
        return this[k];
    };

    this.updateValue = function(k, v)
    {
        this[k] = v;
    };

    this.submit = function(updateTotals)
    {
        document.getElementById('loading').innerHTML = ' ( Loading... )';
        var qstr = this._buildQueryString();

        document.getElementById('previewImg').src = '/customdesign/generatePreview.php' + qstr;
        document.getElementById('loading').innerHTML = '';

        if (!updateTotals)
            return;

        if (!xmlhttp) {
            alert('We were unable to update subtotal and Total Width and Height.  We apologize for the inconvenience.  You may contact us to find out about pricing.');
            return;
        }

        xmlhttp.open('GET', '/customdesign/setVars.php' + qstr, true);
        xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4) { eval(xmlhttp.responseText); } };
        xmlhttp.send(null);
    }

    this._buildQueryString = function()
    {
        var qstr = 'font1='       + impressFontsObj.getCodeName(       this.font1  ) + '&' + //------------- BEGIN: Font 1 --------------
                   'color1='      + impressFontColorsObj.getRGBString( this.color1 ) + '&' + //
                   'text1='       + this._encode(this.text1)                         + '&' + //
                   'height1='     + this.letterheight1                               + '&' + //
                   'textpath='    + this.textpath                                    + '&' + //
                   'textpathw='   + this.textpathw                                   + '&' + //
                   'textpathh='   + this.textpathh                                   + '&' + //------------- END: Font 1 ----------------
                   'halign='      + this.halign                                      + '&' +
                   'valign='      + this.valign                                      + '&' +
                   'font2='       + impressFontsObj.getCodeName(       this.font2  ) + '&' + //------------- BEGIN: Font 2 -------------
                   'color2='      + impressFontColorsObj.getRGBString( this.color2 ) + '&' + //
                   'text2='       + this._encode(this.text2)                         + '&' + //
                   'height2='     + this.letterheight2                               + '&' + //------------- END: Font 2 ---------------
                   'bgcolor='     + this.bgColor                                     + '&' +
                   'mirrorimage=' + (( this.mirrorimage ) ? '1' : '0')               + '&' +
                   'showgrid='    + (( this.showgrid    ) ? '1' : '0')                     ;

        return '?' + qstr;
    };
    this._encode = function(str) {
        return escape(str).replace(/\+/g, '%2B').replace(/\"/g,'%22').replace(/\'/g, '%27').replace(/\//g,'%2F');
    }
}


var fontsObj = function () {
    this.fonts = [
        {
            'name'      : 'Bernhard (001)' , 
            'codename'  : 'bernhard'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Big Fish (002)' , 
            'codename' : 'bigfish'
        },
        {
            'name'     : 'Book Worm (003)' , 
            'codename' : 'bookworm'
        },
        {
            'name'     : 'Bradley Hand (004)' , 
            'codename' : 'bradleyhand'
        },
        {
            'name'     : 'Bremen (005)' , 
            'codename' : 'bremen'
        },
        {
            'name'     : 'Brush (006)' , 
            'codename' : 'brush'
        },
        {
            'name'      : 'Curlz (007)' , 
            'codename'  : 'curlz'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Dauphin (008)' , 
            'codename' : 'dauphin'
        },
        {
            'name'      : 'Edwardian Script (009)' , 
            'codename'  : 'edwardianscript'        , 
            'minheight' : 3
        },
        {
            'name'     : 'Firenze (010)' , 
            'codename' : 'firenze'
        },
        {
            'name'     : 'Frosty (011)' , 
            'codename' : 'frosty'
        },
        {
            'name'     : 'Harrington (012)' , 
            'codename' : 'harrington'
        },
        {
            'name'      : 'Jenkins (013)' , 
            'codename'  : 'jenkins'       , 
            'minheight' : 2
        },
        {
            'name'      : 'Juice (014)' , 
            'codename'  : 'juice'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Jurassic (015)' , 
            'codename' : 'jurassic'
        },
        {
            'name'     : 'Love Letters (016)' , 
            'codename' : 'loveletters'
        },
        {
            'name'     : 'Lucida C (017)' , 
            'codename' : 'lucidac'
        },
        {
            'name'     : 'Lucida H (018)' , 
            'codename' : 'lucidah'
        },
        {
            'name'     : 'Magneto (019)' , 
            'codename' : 'magneto'
        },
        {
            'name'     : 'Maiandra (020)' , 
            'codename' : 'maiandra'
        },
        {
            'name'     : 'Marker Fine Point (021)' , 
            'codename' : 'markerfinepoint'
        },
        {
            'name'     : 'Monotype (022)' , 
            'codename' : 'monotype'
        },
        {
            'name'     : 'Old English (023)' , 
            'codename' : 'oldenglish'
        },
        {
            'name'     : 'Papyrus (024)' , 
            'codename' : 'papyrus'
        },
        {
            'name'     : 'Parry Hotter (025)' , 
            'codename' : 'parryhotter'
        },
        {
            'name'     : 'Poornut (026)' , 
            'codename' : 'poornut'
        },
        {
            'name'     : 'Porky’s (027)' , 
            'codename' : 'porkys'
        },
        {
            'name'     : 'Pristina (028)' , 
            'codename' : 'pristina'
        },
        {
            'name'     : 'Pussycat (029)' , 
            'codename' : 'pussycat'
        },
        {
            'name'      : 'Ribbon (030)' , 
            'codename'  : 'ribbon'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Sapin (031)' , 
            'codename' : 'sapin'
        },
        {
            'name'     : 'Saturn (032)' , 
            'codename' : 'saturn'
        },
        {
            'name'     : 'Scribble (033)' , 
            'codename' : 'scribble'
        },
        {
            'name'      : 'Scriptina (034)' , 
            'codename'  : 'scriptina'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Space Out (035)' , 
            'codename' : 'spaceout'
        },
        {
            'name'     : 'Staccato (036)' , 
            'codename' : 'staccato'
        },
        {
            'name'     : 'Stylistic (037)' , 
            'codename' : 'stylistic'
        },
        {
            'name'     : 'Swing Set (038)' , 
            'codename' : 'swingset'
        },
        {
            'name'     : 'Times New Roman (039)' , 
            'codename' : 'timesnewroman'
        },
        {
            'name'      : 'University (040)' , 
            'codename'  : 'university'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Viner Hand (041)' , 
            'codename' : 'vinerhand'
        },
        {
            'name'      : 'Vivaldi (042)' , 
            'codename'  : 'vivaldi'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Walt Disney (043)' , 
            'codename' : 'waltdisney'
        },
        {
            'name'     : 'Disney (044)' , 
            'codename' : 'disney'
        },
        {
            'name'     : 'Wonka (045)' , 
            'codename' : 'wonka'
        },
        {
            'name'     : 'Eccentric (046)' , 
            'codename' : 'eccentric'
        },
        {
            'name'     : 'Giddyup (047)' , 
            'codename' : 'giddyup'
        },
        {
            'name'     : 'Grenoble (048)' , 
            'codename' : 'grenoble'
        },
        {
            'name'      : 'Imprint (049)' , 
            'codename'  : 'imprint'       , 
            'minheight' : 2
        },
        {
            'name'     : 'Murray (050)' , 
            'codename' : 'murray'
        },
        {
            'name'     : 'Scrap Swirl (051)' , 
            'codename' : 'scrapswirl'
        },
        {
            'name'     : 'Words of Love (052)' , 
            'codename' : 'wordsoflove'
        },
        {
            'name'      : 'Passions Conflict (053)' , 
            'codename'  : 'passionsconflict'        , 
            'minheight' : 3
        }
    ];

    this.getCodeName = function(index)
    {
        return this.fonts[index].codename;
    };

    this.getFontName = function(index)
    {
        return this.fonts[index].name;
    };

    this.getMinMax = function(index)
    {
        return {
            'min' : this.fonts[index].minheight,
            'max' : this.fonts[index].maxheight
        };
    };

    this._buildPopup = function()
    {
        var mainDiv = document.getElementById('fontsPopup');

        for (var i=0; i<this.fonts.length; i++) {

            var a       = document.createElement('a')     ;
            a.onclick   = new Function( "return false;" ) ;
            a.className = 'fontOptionA'                   ;

            var fontDiv       = document.createElement('div')     ; 
            fontDiv.onclick   = new Function( "selectFont(this)") ; 
            fontDiv.className = 'fontOption'                      ;
            fontDiv.setAttribute('index', i); 

            var fontImg = document.createElement('img');
            fontImg.alt = this.fonts[i].name;
            fontImg.src = '/customdesign/images/fonts/'+this.getCodeName(i)+'.jpg';
            fontImg.setAttribute('index', i); 

            fontDiv.appendChild(fontImg);
            a.appendChild(fontDiv);
            mainDiv.appendChild(a);
        }
    }

    this.init = function()
    {
        for (var i in this.fonts) {

            if (typeof(this.fonts[i]['minheight']) != 'number') this.fonts[i]['minheight'] = fontMinHeight;
            if (typeof(this.fonts[i]['maxheight']) != 'number') this.fonts[i]['maxheight'] = fontMaxHeight;
        }

        this._buildPopup();
    };

};

var fontColorsObj = function () {
    this.colors = [
        {
            'name' : 'Black (314)',
            'textColor' : '#FFFFFF',
            'R'    : 0,
            'G'    : 0,
            'B'    : 0
        },
        {
            'name' : 'Brimstone (301)',
            'R'    : 255,
            'G'    : 255,
            'B'    : 64
        },
        {
            'name' : 'Yellow (302)',
            'R'    : 255,
            'G'    : 249,
            'B'    : 0
        },
        {
            'name' : 'Cream (303)',
            'R'    : 255,
            'G'    : 255,
            'B'    : 140
        },
        {
            'name' : 'Beige (304)',
            'R'    : 221,
            'G'    : 182,
            'B'    : 114
        },
        {
            'name'      : 'Chocolate Brown (305)',
            'textColor' : '#FFFFFF',
            'R'         : 98,
            'G'         : 36,
            'B'         : 28
        },
        {
            'name' : 'White (306)',
            'R'    : 255,
            'G'    : 255,
            'B'    : 255
        },
        {
            'name' : 'Forest Green (307)',
            'textColor' : '#FFFFFF',
            'R'    : 0,
            'G'    : 81,
            'B'    : 48
        },
        {
            'name' : 'Ice Blue (308)',
            'R'    : 64,
            'G'    : 150,
            'B'    : 255
        },
        {
            'name' : 'King Blue (309)',
            'textColor' : '#FFFFFF',
            'R'    : 40,
            'G'    : 0,
            'B'    : 100
        },
        {
            'name' : 'Soft Pink (310)',
            'R'    : 255,
            'G'    : 72,
            'B'    : 162
        },
        {
            'name' : 'Red (311)',
            'R'    : 243,
            'G'    : 0,
            'B'    : 0
        },
        {
            'name' : 'Country Red (312)',
            'R'    : 201,
            'G'    : 19,
            'B'    : 33
        },
        {
            'name' : 'Lilac (313)',
            'R'    : 229,
            'G'    : 114,
            'B'    : 204
        },
        //{
            //'name' : 'Etched Glass (315)'
        //},
        {
            'name' : 'Metallic Gold (316)',
            'R'    : 204,
            'G'    : 117,
            'B'    : 5
        },
        {
            'name' : 'Metallic Silver (317)',
            'R'    : 143,
            'G'    : 143,
            'B'    : 140
        },
        {
            'name' : 'Orange (318)',
            'R'    : 255,
            'G'    : 103,
            'B'    : 0
        },
        {
            'name' : 'Terracotta (319)',
            'R'    : 221,
            'G'    : 109,
            'B'    : 47
        },
        {
            'name' : 'Violet (320)',
            'R'    : 165,
            'G'    : 0,
            'B'    : 145
        }, 
        {
            'name' : 'Mocha Brown (321)',
            'R'    : 204,
            'G'    : 148,
            'B'    : 98
        },
        {
            'name' : 'Lime Green (322)',
            'R'    : 84,
            'G'    : 238,
            'B'    : 0
        }, 
        {
            'name' : 'Olive (323)',
            'R'    : 84,
            'G'    : 134,
            'B'    : 50
        }
    ];

    this.getRGBString = function(index)
    {
        return this.colors[index].R.toString() + ',' + this.colors[index].G.toString() + ',' + this.colors[index].B.toString();
    };
    this.getName = function (index)
    {
        return this.colors[index].name;
    };

    this.init = function()
    {
        for (var i in this.colors)
            if (!this.colors[i].textColor)
                this.colors[i].textColor = '#000000';

        this._buildPopup();
    };

    this._buildPopup = function()
    {
        var mainDiv = document.getElementById('fontColorsPopup');

        for (var i=0; i<this.colors.length; i++) {

            var a       = document.createElement('a')     ;
            a.onclick   = new Function( "return false;" ) ;
            a.className = 'fontOptionA'                   ;

            var colorDiv       = document.createElement('div')      ;
            colorDiv.onclick   = new Function( "selectColor(this)") ;
            colorDiv.className = 'fontColorOption'                  ;
            colorDiv.innerHTML = this.colors[i].name                ;
            colorDiv.setAttribute('index', i);
            colorDiv.style.backgroundColor = 'rgb('+this.getRGBString(i)+')';
            colorDiv.style.color = this.colors[i].textColor;

            a.appendChild(colorDiv);
            mainDiv.appendChild(a);
        }
    };
}




impressFormObj       = new formObj()       ;
impressFontsObj      = new fontsObj()      ;
impressFontColorsObj = new fontColorsObj() ;

function initPage()
{
    colorPicker = new YAHOO.widget.ColorPicker("colorPickerDiv", { 
        showhsvcontrols : true  , 
        showhexcontrols : true  , 
        showwebsafe     : false , 
        images: { 
            PICKER_THUMB : "http://yui.yahooapis.com/2.5.2/build/colorpicker/assets/picker_thumb.png" , 
            HUE_THUMB    : "http://yui.yahooapis.com/2.5.2/build/colorpicker/assets/hue_thumb.png"
        } 
    });
    colorPicker.on('rgbChange', function(o) {
        changeMade('bgColor', o.newValue);
        //document.getElementById('previewDiv').style.backgroundColor = 'rgb('+o.newValue+')';
    });

    impressFontsObj.init();
    impressFontColorsObj.init();
    impressFormObj.submit(true);

    rebuildLetterHeight('1');
    rebuildLetterHeight('2');

    //document.getElementById('text1').focus();
}




function startEmailDesign(print)
{
    if (typeof(print) != 'string' || print != 'yes')
        print = 'no';

    var s = base64_encode(impressFormObj._buildQueryString());

    var h = document.getElementById('totalHeight').innerHTML;
    var w = document.getElementById('totalWidth').innerHTML;
    var p = document.getElementById('subtotal').innerHTML;

    var dlh = impressFormObj.getValue('letterheight1');
    var dt = base64_encode(impressFormObj.getValue('text1'));
    var df = base64_encode(impressFontsObj.getFontName(impressFormObj.getValue('font1')));
    var dc = base64_encode(impressFontColorsObj.getName(impressFormObj.getValue('color1')));

    var olh = impressFormObj.getValue('letterheight2');
    var ot = base64_encode(impressFormObj.getValue('text2'));
    var of = base64_encode(impressFontsObj.getFontName(impressFormObj.getValue('font2')));
    var oc = base64_encode(impressFontColorsObj.getName(impressFormObj.getValue('color2')));

    var mi = (impressFormObj.getValue('mirrorimage')) ? 'yes' : 'no';
    var ha = impressFormObj.getValue('halign');

    var loc = 'email.php?s='+s+'&w='+w+'&h='+h+'&p='+p+'&dlh='+dlh+'&olh='+olh+'&dt='+dt+'&ot='+ot+'&df='+df+'&of='+of+'&dc='+dc+'&oc='+oc+'&mi='+mi+'&ha='+ha+'&print='+print;
    //alert(loc);
    window.location.href = loc;
}

function doPrintDesign()
{
    startEmailDesign('yes');
}


function changeMade(key, value)
{
    var updateTotals = false;
    if (key == 'text1' || key == 'text2' || key == 'letterheight1' || key == 'letterheight2')
        updateTotals = true;


    impressFormObj.updateValue(key, value);
    impressFormObj.submit(updateTotals);
}


function selectFont(optionDivElem)
{
    var index = optionDivElem.getAttribute('index').toString();

    document.getElementById('fontName'+currentFontWhich).innerHTML = impressFontsObj.getFontName(index);

    changeMade('font'+currentFontWhich, index);

    rebuildLetterHeight(currentFontWhich);

    toggleSelectFont(currentFontWhich);
}

function selectColor(optionDivElem)
{
    var index = optionDivElem.getAttribute('index').toString();
    var color = optionDivElem.style.color;
    var bgCol = optionDivElem.style.backgroundColor;

    var fontColorDiv = document.getElementById('fontColor'+currentFontColorWhich);
    fontColorDiv.innerHTML             = impressFontColorsObj.getName(index) ;
    fontColorDiv.style.color           = color                               ;
    fontColorDiv.style.backgroundColor = bgCol                               ;

    changeMade('color'+currentFontColorWhich, index);

    toggleSelectColor(currentFontColorWhich);
}

function toggleSelectFont(which)
{
    var popupStatus = toggleSelect('fontName', 'fontsPopup', which);
    if (popupStatus == 'on')
         currentFontWhich = which;
    else currentFontWhich = '';
}

function toggleSelectColor(which)
{
    var popupStatus = toggleSelect('fontColor', 'fontColorsPopup', which);
    if (popupStatus == 'on')
         currentFontColorWhich = which;
    else currentFontColorWhich = '';
}

function toggleSelect(selectName, popupName, which)
{
    var elemDiv = document.getElementById(selectName+which);
    var popupStatus = elemDiv.getAttribute('popup');

    var elemPopupDiv = document.getElementById(popupName);

    if (popupStatus == 'on') {

        elemDiv.setAttribute('popup', 'off');
        elemPopupDiv.style.display = 'none';
        return 'off';
    }

    elemDiv.setAttribute('popup', 'on');

    var pos = getElemPos(elemDiv);

    elemPopupDiv.style.display  = 'block'    ; 
    elemPopupDiv.style.position = 'absolute' ; 
    elemPopupDiv.style.top      = pos.y      ; 
    elemPopupDiv.style.left     = pos.x      ; 

    return 'on';
}

function rebuildLetterHeight(which)
{
    var fontIndex = impressFormObj.getValue('font'+which);
    var min_max   = impressFontsObj.getMinMax(fontIndex);
    var curHeight = impressFormObj.getValue('letterheight'+which);

    var elemSelect = document.getElementById('letterHeight'+which);

    for (var i=elemSelect.options.length-1; i>=0; i--) {

        elemSelect.options[i] = null;
    }

    var j = 0;
    for (var i=min_max.min; i<=min_max.max; i+=0.5) {

        elemSelect.options[j] = new Option(i.toString(), i.toString());
        j++;
    }

    changeToHeight = min_max.min;

    if (curHeight >= min_max.min && curHeight <= min_max.max) {
        for (var i=0; i<elemSelect.options.length; i++) {
            if (elemSelect.options[i].value == curHeight) {

                elemSelect.options[i].selected = true;
                changeToHeight = curHeight;
            }
        }
    }

    changeMade('letterheight'+which, changeToHeight);
}

function changeVerticalAlign(to)
{
    document.getElementById( 'valign_top'    ).src = '/customdesign/images/valign_top_up.png'    ;
    document.getElementById( 'valign_center' ).src = '/customdesign/images/valign_center_up.png' ;
    document.getElementById( 'valign_bottom' ).src = '/customdesign/images/valign_bottom_up.png' ;

    document.getElementById( 'valign_'+to ).src = '/customdesign/images/valign_'+to+'_down.png' ;

    changeMade('valign', to);
}

function changeHorizontalAlign(to)
{
    document.getElementById( 'halign_left'   ).src = '/customdesign/images/halign_left_up.png'   ;
    document.getElementById( 'halign_center' ).src = '/customdesign/images/halign_center_up.png' ;
    document.getElementById( 'halign_right'  ).src = '/customdesign/images/halign_right_up.png'  ;

    document.getElementById( 'halign_'+to ).src = '/customdesign/images/halign_'+to+'_down.png' ;

    changeMade('halign', to);
}

function textPathChange(value)
{
    if (value=='straight')
         document.getElementById('textPathAttrs').style.display='none';
    else document.getElementById('textPathAttrs').style.display='block';

    changeMade('textpath', value);
}


function updateElement(elemId, value)
{
    document.getElementById(elemId).innerHTML = value;
}

function getElemPos(elem)
{
    var curleft = curtop = 0;

    if (elem.offsetParent) {

        do {

            curleft += elem.offsetLeft;
            curtop  += elem.offsetTop;

        } while (elem = elem.offsetParent);
    }
    return {
        'x' : curleft,
        'y' : curtop
    };
}


function testFunc()
{
    alert(impressFormObj._buildQueryString());
}







function utf8_encode ( str_data ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Webtoolkit.info (http://www.webtoolkit.info/)
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)        
    // *     example 1: utf8_encode('Kevin van Zonneveld');
    // *     returns 1: 'Kevin van Zonneveld'
 
    str_data = str_data.replace(/\r\n/g,"\n");
    var tmp_arr = [], ac = 0;
 
    for (var n = 0; n < str_data.length; n++) {
        var c = str_data.charCodeAt(n);
        if (c < 128) {
            tmp_arr[ac++] = String.fromCharCode(c);
        } else if((c > 127) && (c < 2048)) {
            tmp_arr[ac++] = String.fromCharCode((c >> 6) | 192);
            tmp_arr[ac++] = String.fromCharCode((c & 63) | 128);
        } else {
            tmp_arr[ac++] = String.fromCharCode((c >> 12) | 224);
            tmp_arr[ac++] = String.fromCharCode(((c >> 6) & 63) | 128);
            tmp_arr[ac++] = String.fromCharCode((c & 63) | 128);
        }
    }
    
    return tmp_arr.join('');
}

function base64_encode( data ) {
    // http://kevin.vanzonneveld.net
    // +   original by: Tyler Akins (http://rumkin.com)
    // +   improved by: Bayron Guevara
    // +   improved by: Thunder.m
    // +   improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)        
    // -    depends on: utf8_encode
    // *     example 1: base64_encode('Kevin van Zonneveld');
    // *     returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
 
    // mozilla has this native
    // - but breaks in 2.0.0.12!
    //if (typeof window['atob'] == 'function') {
    //    return atob(data);
    //}
        
    var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
    var o1, o2, o3, h1, h2, h3, h4, bits, i = ac = 0, enc="", tmp_arr = [];
    data = utf8_encode(data);
    
    do { // pack three octets into four hexets
        o1 = data.charCodeAt(i++);
        o2 = data.charCodeAt(i++);
        o3 = data.charCodeAt(i++);
 
        bits = o1<<16 | o2<<8 | o3;
 
        h1 = bits>>18 & 0x3f;
        h2 = bits>>12 & 0x3f;
        h3 = bits>>6 & 0x3f;
        h4 = bits & 0x3f;
 
        // use hexets to index into b64, and append result to encoded string
        tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
    } while (i < data.length);
    
    enc = tmp_arr.join('');
    
    switch( data.length % 3 ){
        case 1:
            enc = enc.slice(0, -2) + '==';
        break;
        case 2:
            enc = enc.slice(0, -1) + '=';
        break;
    }
 
    return enc;
}