function ibanTool ( ) {
	
	// Variablen innerhalb des IBAN-Tools
	this.IBAN = '';
	this.LandCode = '';
	this.CheckDigit = '';
	this.AccountNumber = '';
	this.BankCode = '';
	this.CheckSum = '';
	
	// Einige vordefinierte Länder
	this.ValidationInfos = {
		'DE': {
				'LandCode': 'DE',
				'NumericLandCode' : '1314',
				'Country': 'Deutschland',
				'Regex': /DE([0-9]{2})([0-9]{8})([0-9]{10})/,
				'Length': 22
			},
		'AT': {
				'LandCode': 'AT',
				'NumericLandCode' : '1029',
				'Country': 'Österreich',
				'Regex': /AT([0-9]{2})([0-9]{5})([0-9]{11})/,
				'Length': 20
			}
	};
	
	
	// ibanTool.validate()
	// Diese Methode überprüft die Gültigkeit einer übergebenen IBAN
	this.validate = function ( pIBAN ) {
		// Eingabe kapitalisieren
		pIBAN = pIBAN.toUpperCase().replace(/\s/g, '');
		
		// Ergebnis-Objekt
		results = new ValidationResult();
		
		// Überprüfe IBAN-Format
		LandCodeRegex = new RegExp(/^([a-zA-Z]{2})\d+/);
		if ( !LandCodeRegex.test(pIBAN) ) {
			results.addInvalid( 'Die angegebene IBAN entspricht nicht dem g&auml;ngigen Format!' );
			return results;
		}
		
		// Lese Ländercode
		this.LandCode = LandCodeRegex.exec(pIBAN)[1];
		
		// Lade Validierungs-Infos
		ValidationID = getElementPosition(this.ValidationInfos, this.LandCode);
		if ( ValidationID == -1 ) {
			results.addInvalid('Die angegebene IBAN enth&auml;lt einen unbekannten L&auml;ndercode');
			return results;
		} else {
			results.addValid('L&auml;ndercode wurde erkannt (' + this.ValidationInfos[this.LandCode]['Country'] + ')');
		}
		
		validInfo = this.ValidationInfos[this.LandCode];
		
		// Überprüfe IBAN-Länge
		if ( pIBAN.length != validInfo['Length'] ) {
			results.addInvalid('Die IBAN entspricht nicht der g&auml;ngigen L&auml;nge (' + validInfo['Length'] + ' Zeichen)');
			return results;
		}
		
		// Lese IBAN-Infos aus (Kontonummer etc.)
		ibanRegex = new RegExp(validInfo['Regex']);
		regexResults = ibanRegex.exec(pIBAN);
		
		this.CheckDigit = regexResults[1]; // Prüfsumme
		this.BankCode = regexResults[2]; // Kontonummer
		this.AccountNumber = regexResults[3]; // Bankleitzahl
		
		// Überprüfe Gültigkeit der Bankleitzahl
		bankData = this.validateBankCode()
		if ( bankData != false ) {
			text = 'Anhand der Bankleitzahl ' + this.BankCode + ' konnten folgende Informationen ermittelt werden:<br />' +
			'<span class="detail">Bankinstitut: <a href="/bankleitzahlen/verzeichnis/?keyword=' + bankData.name + '" target="_blank" id="findInstitutes">' + bankData.name + '</a></span>';
			
			if ( bankData.zip != '' && bankData.city != '' ) {
				text += '<br /><span class="detail">Ort: ' + bankData.zip + ' ' + bankData.city + '</span>';
			}

			if ( bankData.bic != '' ) {
				text += '<br /><span class="detail">Ermittelte BIC: ' + bankData.bic + '</span>';
			}
			
			results.addValid(text);
		} else {
			results.addInvalid('Die Bankleitzahl scheint falsch zu sein');
		}
		
		// Überprüfe Gültigkeit der Kontonummer
		/*if ( this.validateAccountNumber() ) {
			results.addValid('Die Kontonummer scheint richtig zu sein!');
		} else {
			results.addInvalid('Die Kontonummer scheint falsch zu sein!');
		}*/
		
		// Überprüfe Gültigkeit der Prüfsumme
		if ( this.validateChecksum() ) {
			results.addValid('Die Prüfsumme scheint richtig zu sein');
		} else {
			results.addInvalid('Die Prüfsumme scheint falsch zu sein');
		}
		
		
		return results;
	}
	
	// ibanTool.generate()
	// Gibt eine IBAN zurück
	this.generate = function ( country ) {
		validRes = new ValidationResult();
		validInfo = this.getValidationElement( country );
		
		if ( typeof(validInfo['Generate']) == 'function' ) {
			return validInfo['Generate']();
		}
		
		if ( !validInfo ) {
			validRes.addInvalid( 'Für dieses Land wird keine IBAN-Generierung angeboten!' );
			return validRes;
		}
		
		if ( !this.validateBankCode() /*|| !this.validateAccountNumber()*/ ) {
			validRes.addInvalid('Die Bankleitzahl scheint nicht korrekt zu sein!');
			return validRes;
		}
		
		
		tmpAccNo = this.AccountNumber;
		//if ( tmpAccNo.length < 10 ) 
		tmpAccNo1 = zeroFill( tmpAccNo, 8, 'prepend' );
		
		this.CheckSum = this.BankCode + '' + tmpAccNo1 + this.encryptLandCode(validInfo['LandCode']) + '00';
		//this.CheckSum = zeroFill( this.CheckSum, validInfo['Length'] );
		
		result = validInfo['LandCode'] + '' + this.generateCheckDigit( this.CheckSum ) + this.BankCode + '' + zeroFill( tmpAccNo, 8, 'prepend' );
		
		result = zeroFill( result, 22, 'append' );
		
		
		this.IBAN = result;
		
		return validRes;
	}
	
	
	// Prüfziffer generieren
	this.generateCheckDigit = function ( value ) {
		rest = modulo(value, 97);
		
		result = 98 - rest;
		
		if ( result < 10 ) {
			alert('0' + result);
			return '0' + result;
		} else {
			return result;
		}
	}
	
	// ibanTool.validateBankCode()
	// Diese Methode überprüft die Gültigkeit der Bankleitzahl
	this.validateBankCode = function ( ) {
		if ( typeof($) == 'undefined' ) return false;
		
		URL = '/bank_codes/getcodes/' + this.BankCode + '.json';
		
		// Setze MIME-Type (für Firefox 3)
		$.ajaxSetup({'beforeSend': function ( xhr ) {
									    if ( xhr.overrideMimeType ) xhr.overrideMimeType( "text/plain" );
									},
					 'async': false
				   });
		
	 	result = false;
	 	cBankCode = this.BankCode;
		$.getJSON(URL, function ( data ) {
		 	$.each( data, function( i, item ) {
				if ( item.BankCode.code == cBankCode ) {
					result = item.BankCode;
				}
			});
		});
		
		return result;
	}
	
	
	// ibanTool.validateAccountNumber()
	// Diese Methode übeprüft die Gültigkeit der eingegebenen Kontonummer
	this.validateAccountNumber = function ( ) {
		
		no = new String(this.AccountNumber);
		accNo = no.split('');
		accNo = accNo.reverse();
		no = accNo.join('');
		
		cCheckSum = 0;
		m = 1;
		
		for ( i = 0; i < accNo.length; i++ ) {
			m++;
			cCheckSum += ( accNo[i] * m );
			// document.write(accNo[i] + ' * ' + m + ' = ' + cCheckSum + '<br>');
			if ( m >= 7 ) m = 1;
		}
		
		rest = cCheckSum % 11;
		if ( rest == 1 ) rest = 0;
		
		if ( this.AccountNumber[9] == rest ) return true;
		
		return false;
	}
	
	
	// ibanTool.validateChecksum()
	// Diese Methode überprüft die Gültigkeit der Prüfsumme
	this.validateChecksum = function ( ) {
		this.CheckSum = this.BankCode + this.AccountNumber + this.encryptLandCode(this.LandCode) + this.CheckDigit;
		
		if ( modulo(this.CheckSum, 97) == 1 ) return true
		else return false;
	}
	
	
	// ibanTool.validateChecksum()
	// Diese Methode wandelt den Ländercode in einen Zahlenwert um
	this.encryptLandCode = function( value ) {
	
		var result = '';
		var chars = new Array(23);
		value = value.toLowerCase();
		
		i = 1;
		for ( c = 97; c < 123; c++ ) {
			chars[i] = String.fromCharCode(c).toLowerCase();
			i++;
		}
		
		for ( var element in value ) {
			result = result + ( getElementPosition(chars, value[element]) + 10 );
		}
		
		return result;
	}
	
	// Findet ValidationInfo anhand des Ländercodes oder Ländernamens
	this.getValidationElement = function( country ) {
		for ( var element in this.ValidationInfos ) {
			if ( element == country.toUpperCase() || this.ValidationInfos[element]['Country'] == country ) return this.ValidationInfos[element];
		}
		
		return false;
	}

	this.IBANtoPaper = function () {
		result = '';
		
		i = 0;
		
		alert(this.IBAN);
		
		for ( var c in this.IBAN ) {
			if ( i >= 4 ) {
				result += ' ' + c;
				i = 0;
			} else {
				result += '' + c;
			}
			
			i++;
		}
		
		return result;
	}	
}



function ValidationResult( ) {
	this.type = 'results';
	this.Valid = new Array();
	this.Invalid = new Array();
	
	
	this.Valid.each = function ( callback ) {
		if ( typeof(callback) == 'function' ) {
			for ( i = 0; i < this.length; i++) {
				callback( this[i] );
			} 
		}
	}
	
	this.Invalid.each = function ( callback ) {
		if ( typeof(callback) == 'function' ) {
			for ( i = 0; i < this.length; i++) {
				callback( this[i] );
			} 
		}
	}
	
	
	this.addValid = function ( value ) {
		this.Valid.push( value );
	}
	
	this.addInvalid = function ( value ) {
		this.Invalid.push( value );
	}
	
	this.empty = function () {
		this.Valid = [];
		this.Invalid = [];
	}
}


// Modulofunktion für große Zahlen
function modulo ( divident, divisor ) {
    var cDivident = '';
    var cRest = '';

    for (var i in divident ) {
        var cChar = divident[i];
        var cOperator = cRest + '' + cDivident + '' + cChar;

        if ( cOperator < parseInt(divisor) ) {
                cDivident += '' + cChar;
        } else {
                cRest = cOperator % divisor;
                if ( cRest == 0 ) {
                    cRest = '';
                }
                cDivident = '';
        }

    }

    cRest += '' + cDivident;

    if (cRest == '') {
        cRest == 0;
    }

    return cRest;
}



// Diese Funktion findet ein Element innerhalb eines Arrays
// Der Rückgabewert ist der Index des Elements
function getElementPosition( theArray, value ) {
	c = 0;
	
	for ( var element in theArray ) {
		if ( value == element || value == theArray[element] ) {
			return c;
		}
		c++;
	}
	
	return -1;
}

function zeroFill ( value, length, method ) {
	switch ( method ) {
		case 'prepend':
			while ( value.length < length ) {
				value = '0' + value;
			}
			break;
		default:
			while ( value.length < length ) {
				value += '0';
			}
	}
	
	
	return value;
}
