﻿
var generatorVersion = 1.2;

var MAX_FIELD_TEST = 1;

var useCaching = false;
var useValidation = false;
var usePaging = false;
var generateComments = false;
var providerID = 0;
var languageID = 0;
var className = "";
var nameSpace = "";

var fields = new Array();
var types = new Array();
var searchable = new Array();
var fieldSize = new Array();


$(function(){

    $('#tabs').tabs();
    
    $("#txtClassName").keyup(function(event) {
        
        $("#tabClassName").html( $("#txtClassName").val() + ".cs" );
    });

    $('#tabs').bind('tabsshow', function(event, ui) {
    
        CollectData();
        var selected = $('#tabs').tabs('option', 'selected');
        
        switch( selected )
        {
            case 1:
                GenerateBusinesObject();
                break;
            case 2:
                GenerateBaseClass();
                break;
            case 3:
                GenerateStoredProcedures();
                break;        
        }
        
    });

});

function optionsChange(jqThis)
{
    jqThis.parent().parent().find('LI.stringoption').hide();    
    
    if( jqThis.val() == "string")
    {
        jqThis.parent().parent().find('LI.stringoption').show();    
    }
}

function addNewField()
{
    MAX_FIELD_TEST = MAX_FIELD_TEST + 1;
    
    $('#divFields').append("<div class='newfield' style='padding: 2px 0 0 0';><input id='field" + MAX_FIELD_TEST + "' type='text' /> <select id='field" + MAX_FIELD_TEST + "Type' onchange='optionsChange($(this));' ><option>bool</option><option>Guid</option><option>DateTime</option><option>int</option><option selected='selected'>string</option><option>float</option><option>double</option></select> <a href='#' onclick='$(\"#divoptions" + MAX_FIELD_TEST + "\").toggle();return false;'>Options</a> <a href='#' onclick='return removeField($(this));'>X</a><div style='padding:5px;display:none;background:#232323;' id='divoptions" + MAX_FIELD_TEST + "'><ul><li class='stringoption'>Field Size <input type='text' id='txtFieldSize" + MAX_FIELD_TEST + "' value='250' /></li><li><input type=\"checkbox\" id='chkSearchable" + MAX_FIELD_TEST + "' value='250' /> <label for='chkSearchable" + MAX_FIELD_TEST + "'>Searchable</label></li><li><input type=\"checkbox\" id='txtRequired" + MAX_FIELD_TEST + "' value='250' disabled='disabled' /> <label for='txtRequired" + MAX_FIELD_TEST + "'>Required Validator</label></li><li class='stringoption'><input type=\"checkbox\" id='chkStringLengthValidator" + MAX_FIELD_TEST + "' value='250' disabled='disabled' /> <label for='chkStringLengthValidator" + MAX_FIELD_TEST + "'>String Length Validator</label> | Min Value: <input type='text' id='txtMin" + MAX_FIELD_TEST + "' value='250' disabled='disabled' /></li></ul></div></div>");
    
}

function removeField( jqThis ) 
{
    jqThis.parent().remove();
    MAX_FIELD_TEST--;
    
    var idx = 2;
    $("#divFields div.newfield input[id^='field']").each(function(index, item){ item.id = "field" + idx; idx++; });
    idx = 2;
    $("#divFields div.newfield select[id^='field']").each(function(index, item){ item.id = "field" + idx + "Type";  idx++; });
    idx = 2;
    $("#divFields div.newfield input[id^='txtFieldSize']").each(function(index, item){ item.id = "txtFieldSize" + idx; idx++; });
    idx = 2;
    $("#divFields div.newfield input[id^='chkSearchable']").each(function(index, item){ item.id = "chkSearchable" + idx; idx++; });
    idx = 2;
    $("#divFields div.newfield input[id^='txtRequired']").each(function(index, item){ item.id = "txtRequired" + idx; idx++; });
    idx = 2;
    $("#divFields div.newfield input[id^='chkStringLengthValidator']").each(function(index, item){ item.id = "chkStringLengthValidator" + idx; idx++; });
    idx = 2;
    $("#divFields div.newfield input[id^='txtMin']").each(function(index, item){ item.id = "txtMin" + idx; idx++; });
    
    return false;
}

function CollectData()
{
    fields = new Array();
    types = new Array();
    searchable = new Array();
    fieldSize = new Array();

    className = $("#txtClassName").val();
    nameSpace = $("#txtNameSpace").val();
    useCaching = $("#chkCaching").attr("checked");
    useValidation = $("#chkEnterpriseLibraryValidation").attr("checked");
    generateComments = $("#chkComments").attr("checked");
    providerID = $("#lstProvider").val();

    for( var i = 1; i <= MAX_FIELD_TEST; i++ )
    {
        if( $("#field" + i).val() != "" )
        {
            fields[i-1] = $("#field" + i).val();
            types[i-1] = $("#field" + i + "Type").val();
            searchable[i-1] = $("#chkSearchable" + i).attr("checked");
            fieldSize[i-1] = $("#txtFieldSize" + i).val();
        }// else break;
    }
}

function AddLine(line, target)
{
    if(!target)
    {
        $('#txtOutput').val( $('#txtOutput').val() + line + "\n");
    }
    else
    {
        $('#' + target).val($('#' + target).val() + line + "\n");
    }
}

function AddMembers()
{
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( fields[i] )
        {
            AddLine("private " + types[i] + " _" + fields[i] + ";");
        } else break;
    }
}

function AddProperties()
{
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( fields[i] )
        {
            var fieldName =  fields[i].charAt(0).toUpperCase() + fields[i].substr(1, fields[i].length);
            
            AddLine("public " + types[i] + " " + fieldName);
            AddLine("{");
            AddLine("get");
            AddLine("{");
            AddLine("return _" + fields[i] + ";");
            AddLine("}");
            AddLine("set");
            AddLine("{");
            AddLine("_" + fields[i] + " = value;");
            AddLine("}");
            AddLine("}");
        } else break;
    }

}

function AddFactoryConstructors()
{

    if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// Constructor for " + className);
        AddLine("/// </summary>");
    }
    AddLine("public " + className + "()");
    AddLine("{");
    
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( fields[i] )
        {
            if(types[i] == "string")
            {
                AddLine("_" + fields[i] + " = string.Empty;");
            }
            else if(types[i] == "Guid")
            {
                AddLine("_" + fields[i] + " = Guid.Empty;");
            }
            else if(types[i] == "double")
            {
                AddLine("_" + fields[i] + " = 0.0D;");
            }
            else if(types[i] == "float")
            {
                AddLine("_" + fields[i] + " = 0.0F;");
            }
            else if(types[i] == "DateTime")
            {
               AddLine("_" + fields[i] + " = DateTime.Now;");
            }
            else if(types[i] == "int")
            {
               AddLine("_" + fields[i] + " = int.MinValue;");
            }
            else if(types[i] == "bool")
            {
               AddLine("_" + fields[i] + " = false;");
            }
        } else break;
    }
    
    AddLine("}");
    
    AddLine("");
    
    if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// Common Factory Loader, maps the database field to private members");
        AddLine("/// </summary>");
        AddLine("/// <param name=\"item\">a unloaded instance of business object</param>");
        AddLine("/// <param name=\"row\">a datarow with selected data</param>");
        AddLine("/// <returns>A loaded insance of given business object</returns>");
    }    
    AddLine("private static " + className + " GetFromDataRow(" + className + " item, DataRow row)");
    AddLine("{");
    
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( fields[i] )
        {
        
            AddLine("if (!row.IsNull(\"" + fields[i] + "\"))");
            AddLine("{");
        
            if(types[i] == "string")
            {
                AddLine("item._" + fields[i] + " = row[\""+  fields[i]  +"\"].ToString();");
            }
            else if(types[i] == "Guid")
            {
                AddLine("item._" + fields[i] + " = new Guid(row[\""+  fields[i]  +"\"].ToString());");
            }
            else if(types[i] == "double")
            {
                AddLine("item._" + fields[i] + " = double.Parse(row[\""+  fields[i]  +"\"].ToString());");
            }
            else if(types[i] == "float")
            {
                AddLine("item._" + fields[i] + " = float.Parse(row[\""+  fields[i]  +"\"].ToString());");
            }
            else if(types[i] == "DateTime")
            {
                AddLine("item._" + fields[i] + " = DateTime.Parse(row[\""+  fields[i]  +"\"].ToString());");
            }
            else if(types[i] == "int")
            {
               AddLine("item._" + fields[i] + " = int.Parse(row[\""+  fields[i]  +"\"].ToString());");
            }
            else if(types[i] == "bool")
            {
               AddLine("item._" + fields[i] + " = (int.Parse(row[\""+  fields[i]  +"\"].ToString()) == 1);");
            }
            
            AddLine("}");
            
        } else break;
    }
    
    AddLine("\nreturn item;\n");
    
    AddLine("}");
    
    AddLine("");
    
    if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// GetSingle - Load a given object, if not found - null is returned");
        AddLine("/// </summary>");
        AddLine("/// <param name=\"ID\">The GUID ID of the record</param>");
        AddLine("/// <returns>an loaded instance of this business object</returns>");
    }
    AddLine("public static " + className + " GetSingle(" + types[0] + " ID)");
    AddLine("{");
    
    if(useCaching)
    {
        AddLine(className + " obj = null;");
        AddLine("string key = \"" + className  + "\" + ID.ToString();");
        AddLine("obj = BusinessObject.GetObject(key) as " + className  + ";");
        AddLine("if (obj == null)");
        AddLine("{");
        AddLine("obj = new " + className + "();");
        
        AddLine("DataTable dt = " + className + ".SelectSingle(ID);");
        AddLine("if( dt.Rows.Count > 0 )");
        AddLine("{");
        AddLine("obj = GetFromDataRow(obj, dt.Rows[0]);");
        AddLine("BusinessObject.AddToCache(key, obj);");
        AddLine("}");
        
        AddLine("}");
        
        AddLine("return obj;");
    }
    else
    {
        AddLine(className + " obj = null;");
        AddLine("DataTable dt = " + className + ".SelectSingle(ID);");
        AddLine("if( dt.Rows.Count > 0 )");
        AddLine("{");
        AddLine("obj = GetFromDataRow(obj, dt.Rows[0]);");
        AddLine("}");
        AddLine("return obj;");
    }

    AddLine("}");
    
    AddLine("");
    
    if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// GetAll - Gets all the records of this business object type");
        AddLine("/// </summary>");
        AddLine("/// <returns>A generic list of results</returns>");
    }
    
    AddLine("public static List<" + className + "> GetAll(" + GetSearchableParamsCSharp("", true, true) + ")");
    AddLine("{");
    
    if(useCaching)
    {
        AddLine("List<" + className + "> list = null;");
        
        var ctr = 0;
        var toAdd = new Array();
        for( var i = 0; i < MAX_FIELD_TEST; i++ )
        {
            if( fields[i] )
            {
                if( searchable[i] )
                {
                    toAdd[ctr] = fields[i];
                    ctr++;
                }
            } else break;
        }
        
        var keyAppend = "";
        
        for( var i = 0; i < ctr; i++ )
        {
            keyAppend += toAdd[i].toUpperCase() + "{" + i + "}";
        }
        
        AddLine("string key = string.Format(\"" + className + keyAppend + "\", new object[] {" + GetSearchableParamsCSharp("", false, true) + "});");
        AddLine("list = BusinessObject.GetObject(key) as List<" + className + ">;");

        AddLine("if (list == null)");
        AddLine("{");
        AddLine("list = new List<" + className + ">();");

        AddLine("foreach (DataRow row in " + className + ".SelectAll(" + GetSearchableParamsCSharp('', false) + ").Rows)");
        AddLine("{");
        AddLine(""+ className + " i = new "+ className + "();");
        AddLine("list.Add(GetFromDataRow(i, row));");
        AddLine("}");

        AddLine("BusinessObject.AddToCache(key, list);");
        AddLine("}");
        AddLine("return list;");
    }
    else
    {
        AddLine("List<" + className + "> list = new List<" + className + ">();");
        AddLine("foreach (DataRow row in " + className + ".SelectAll(" + GetSearchableParamsCSharp('', false) + ").Rows)");
        AddLine("{");
        AddLine(""+ className + " i = new "+ className + "();");
        AddLine("list.Add(GetFromDataRow(i, row));");
        AddLine("}");
        AddLine("return list;");
    }
    
    AddLine("}");
}

function AddMethods()
{
    if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// Save - Saves an instance of a business object");
        AddLine("/// </summary>");
    }
    AddLine("public void Save()");
    AddLine("{");
    AddLine("AddUpdate();");
    
    if(useCaching)
    {
        AddLine("BusinessObject.ClearCache(\"" + className + "\");");
    }
    
    AddLine("}");
    
    AddLine("");
    
     if( generateComments )
    {
        AddLine("/// <summary>");
        AddLine("/// Delete - Deletes a business object by ID");
        AddLine("/// </summary>");
    }
    AddLine("public static void Delete(" + types[0] + " ID)");
    AddLine("{");
    
    AddLine("DeleteSingle(ID);");
    if(useCaching)
    {
        AddLine("BusinessObject.ClearCache(\"" + className + "\");");
    }
    
    AddLine("}");
}

function BuildReverseCode()
{
    var code = "";
    code += generatorVersion + "|" + className + "|" + nameSpace + "|" + useCaching + "|" + useValidation + "|" + generateComments + "|" + usePaging + "|" + providerID + "|" + languageID + "|";
    code += MAX_FIELD_TEST;
    for( var i = 0; i < MAX_FIELD_TEST; ++i )
    {
        code += "~";
        code += fields[i] + "!" + types[i] + "!" + searchable[i] + "!" + fieldSize[i];
    }

  
    return encode64(code);
}



function LoadReverseCode()
{
    //reset UI
    $(".newfield").remove();
    MAX_FIELD_TEST = 1;

    var code = $("#txtReverseEngineerCode").val();
    code = decode64(code);
    var mainSplit = code.split("|");
    
    if( mainSplit.length > 0 )
    {
        loadingGeneratorVersion = mainSplit[0];
         
        if( loadingGeneratorVersion == generatorVersion && mainSplit.length == 10 )
        {
            $("#txtClassName").val(mainSplit[1]);
            $("#txtNameSpace").val(mainSplit[2]);

            if(mainSplit[3] == true) 
            {  
                $("#chkCaching").attr("checked", "checked");
            }
            else if(mainSplit[3] == false) 
            {
                $("#chkCaching").attr("checked", "");
            }
            
            if(mainSplit[4] == true) 
            {  
                $("#chkEnterpriseLibraryValidation").attr("checked", "checked");
            }
            else if(mainSplit[4] == false) 
            {
                $("#chkEnterpriseLibraryValidation").attr("checked", "");
            }
            
            if(mainSplit[5] == true) 
            {  
                $("#chkComments").attr("checked", "checked");
            }
            else if(mainSplit[5] == false) 
            {
                $("#chkComments").attr("checked", "");
            }
            
            if(mainSplit[6] == true) 
            {  
                $("#chkPaging").attr("checked", "checked");
            }
            else if(mainSplit[6] == false) 
            {
                $("#chkPaging").attr("checked", "");
            }
            
            document.getElementById("lstProvider").selectedIndex = mainSplit[7];
            
            //mainSplit[8] language
            var fields = mainSplit[9].split("~");
            for( var i = 0; i < fields.length; ++i)
            {
                var fieldstring = fields[i].split("!");
                if(fieldstring.length == 4 )
                {
                    if(i > 1)
                    {
                        addNewField();
                    }
                    
                    $("#field" + i).val(fieldstring[0]);
                    $("#field" + i + "Type").val(fieldstring[1]);
                    optionsChange($("#field" + i + "Type"));    
                    if(fieldstring[2] == "true") 
                    {  
                        $("#chkSearchable" + i).attr("checked", "checked");
                    }
                    else if(fieldstring[2] == "false") 
                    {
                        $("#chkSearchable" + i).attr("checked", "");
                    }
                 
                    $("#txtFieldSize" + i).val(fieldstring[3]);
                }
            }
        }
        else
        {
            alert("invalid code!  dont copy in the //REVERSE ENGINEER CODE START// or //REVERSE ENGINEER CODE STOP// part");
        }
    }
    else
    {
        alert("invalid code!  dont copy in the //REVERSE ENGINEER CODE START// or //REVERSE ENGINEER CODE STOP// part");
    }
}

function TagHeader(txt, isSQL)
{
    var prefix = "//";
    if( isSQL == true )
    {
        prefix = "--";
    }
    
    var d = new Date();
    AddLine(prefix + "BITPORTERS LTD CODE GENERATOR V" +generatorVersion + " http://bitporters.net/code-generator", txt);
    AddLine(prefix + "GENERATED DATE: " + d, txt);
    AddLine(prefix +"REVERSE ENGINEER CODE: " + BuildReverseCode(), txt);
}

function GenerateBusinesObject()
{
    $('#txtOutput').val("");
    
    TagHeader('txtOutput');

    AddLine("\nusing System;");
    AddLine("using System.Collections.Generic;");
    AddLine("using System.Text;");
    AddLine("using System.Xml.Serialization;");
    AddLine("using System.Xml;");
    
    AddDAL_Includes("txtOutput");
       
    AddLine("\nnamespace " + nameSpace);
    AddLine("{");

    AddLine("\npublic class " + className + " : BusinessObject");
    AddLine("{\n");
    
    AddLine("#region Members\n");
    
    AddMembers();
    
    AddLine("\n#endregion\n");
    
    AddLine("#region Properties\n");
    
    AddProperties();
    
    AddLine("\n#endregion\n");
    
    AddLine("#region Factories/Constructor\n");
    
    AddFactoryConstructors();
    
    AddLine("\n#endregion\n");
    
    AddLine("#region Methods\n");
    
    AddMethods();
    
    AddLine("\n#endregion\n");
    
    AddLine("\n#region DATA_ACCESS\n");
    
    AddDAL();
    
    AddLine("\n#endregion\n");
    
    AddLine("}");
    
    
    AddLine("}");
    
    
  
    
}





function GetMethodCachingHashString()
{
    var paramstringv = "";
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( i < MAX_FIELD_TEST-1 )
        {
            if( fields[i] )
            {
                if( searchable[i] )
                {
                    paramstring += types[i] + " " + strPrefix + fields[i];
                }
            } else break;
        }    
    }
    
    return paramstring;
}

function GetSearchableParamsCSharp(strPrefix, delcareType)
{
    if(!strPrefix) strPrefix = "";
    var paramstring = "";
    for( var i = 0; i < MAX_FIELD_TEST; i++ )
    {
        if( i < MAX_FIELD_TEST-1 )
        {
            if( fields[i] )
            {
                if( searchable[i] )
                {
                    if( delcareType )
                    {
                        paramstring += types[i] + " " + strPrefix + fields[i];
                    }
                    else
                    {
                        paramstring += strPrefix + fields[i];
                    }
                    
                    if( fields[i] && fields[i+1] )
                    
                    {
                        paramstring += ", ";
                    }
                }
            } else break;
        }    
    }
    
    return paramstring;
}

function GenerateBaseClass()
{
    $('#txtBaseClass').val("");
    
    TagHeader('txtBaseClass');

    AddLine("\nusing System;", 'txtBaseClass');
    AddLine("using System.Collections;", 'txtBaseClass');
    AddLine("using System.Collections.Generic;", 'txtBaseClass');
    AddDAL_Includes("txtBaseClass");
    
    AddLine("using System.Text;", 'txtBaseClass');

    AddLine("\nnamespace " + nameSpace, 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("public class BusinessObject", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("public static string ConnectionString", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("get", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("return System.Configuration.ConfigurationManager.ConnectionStrings[\"MY_DATABASE\"].ConnectionString;", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    
    AddDAL_BaseClassFactories();
    
    if(useCaching)
    {
    
    AddLine("public static void ClearCache(string prefix)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("if (System.Web.HttpContext.Current.Cache != null)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("List<string> oRemove = new List<string>();", 'txtBaseClass');
    AddLine("IEnumerator iterator = System.Web.HttpContext.Current.Cache.GetEnumerator();", 'txtBaseClass');
    AddLine("while (iterator.MoveNext())", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("DictionaryEntry ent = (DictionaryEntry)iterator.Current;", 'txtBaseClass');
    AddLine("if (ent.Key.ToString().StartsWith(prefix))", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("oRemove.Add(ent.Key.ToString());", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("foreach (string sKey in oRemove)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("System.Web.HttpContext.Current.Cache.Remove(sKey);", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');

    AddLine("public static bool IsInCache(string key)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("return (System.Web.HttpContext.Current.Cache[key] != null);", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');

    AddLine("public static object GetObject(string key)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("return System.Web.HttpContext.Current.Cache[key];", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');

    AddLine("public static void AddToCache(string key, object o)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("if (o != null)", 'txtBaseClass');
    AddLine("{", 'txtBaseClass');
    AddLine("System.Web.HttpContext.Current.Cache.Add(key, o, null, DateTime.Now.AddMinutes(30), TimeSpan.Zero, System.Web.Caching.CacheItemPriority.NotRemovable, null);", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');
    
    }
    
    AddLine("}", 'txtBaseClass');
    AddLine("}", 'txtBaseClass');

}




// This code was written by Tyler Akins and has been placed in the
// public domain.  It would be nice if you left this header intact.
// Base64 code from Tyler Akins -- http://rumkin.com

var keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

function encode64(input) {
   var output = "";
   var chr1, chr2, chr3;
   var enc1, enc2, enc3, enc4;
   var i = 0;

   do {
      chr1 = input.charCodeAt(i++);
      chr2 = input.charCodeAt(i++);
      chr3 = input.charCodeAt(i++);

      enc1 = chr1 >> 2;
      enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
      enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
      enc4 = chr3 & 63;

      if (isNaN(chr2)) {
         enc3 = enc4 = 64;
      } else if (isNaN(chr3)) {
         enc4 = 64;
      }

      output = output + keyStr.charAt(enc1) + keyStr.charAt(enc2) + 
         keyStr.charAt(enc3) + keyStr.charAt(enc4);
   } while (i < input.length);
   
   return output;
}

function decode64(input) {
   var output = "";
   var chr1, chr2, chr3;
   var enc1, enc2, enc3, enc4;
   var i = 0;

   // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
   input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

   do {
      enc1 = keyStr.indexOf(input.charAt(i++));
      enc2 = keyStr.indexOf(input.charAt(i++));
      enc3 = keyStr.indexOf(input.charAt(i++));
      enc4 = keyStr.indexOf(input.charAt(i++));

      chr1 = (enc1 << 2) | (enc2 >> 4);
      chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
      chr3 = ((enc3 & 3) << 6) | enc4;

      output = output + String.fromCharCode(chr1);

      if (enc3 != 64) {
         output = output + String.fromCharCode(chr2);
      }
      if (enc4 != 64) {
         output = output + String.fromCharCode(chr3);
      }
   } while (i < input.length);

   return output;
}