
	/* numeric */
	function regexpEscape(s) {
		return s.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')
	}
	function addSeparators(s, decimals, cursor, decimal, thousands) {
		var olds = s, oldstart = cursor.start, oldend = cursor.end;
		// alte Separatoren entfernen
		if (thousands) {
			s = s.replace(new RegExp(regexpEscape(thousands), 'g'), function (match, offset) {
				if (oldstart > offset) cursor.start--;
				if (oldend > offset) cursor.end--;
				return '';
			});
		}
		s = s.split(decimal);
		// Nachkommastellen crimpen
		if (decimals > 0) {
			if (s[1] === undefined) s.push('');
			s[1] = (s[1] + '000000000000').slice(0, decimals);
		} else if (decimals < 0) {
			while ((new RegExp(regexpEscape(decimal) + '[0-9+0$]')).test(s[1])) {
				s[1] = s[1].substr(0, s[1].length - 1);
			}
		} else {
			while (s[1] !== undefined) s.pop();
		}
		// führende Nullen BIS ZUM CURSOR entfernen
		// (damit man z.B. 1|00,00 => |00,00 => 2|00,00 eingeben kann) 
		s[0] = s[0].replace(/^(-?)(0+)(.*)/, function(match, sign, zeros, rest) {
				result = sign+zeros.slice(cursor.start-sign.length)+rest;
				cursor.start -= Math.min(zeros.length, cursor.start-sign.length);
				cursor.end -= Math.min(zeros.length, cursor.end-sign.length);
				return result;
		});
		if (s[0] == '' || s[0] == '-') {
			s[0] += '0';
			cursor.start++;
			cursor.end++;
		}
		// Tausender wieder hinzufügen
		if (thousands == '') return s.join(decimal);
		while (true) {
			var s2 = s[0].replace(new RegExp('(\\d)(\\d{3})($|'+regexpEscape(decimal)+'|'+regexpEscape(thousands)+')'), function (match, before, numbers, rest, offset) {
				if (cursor.start > offset + before.length) cursor.start++;
				if (cursor.end > offset + before.length) cursor.end++;
				return before + thousands + numbers + rest;
			});
			if (s[0] == s2) {
				s = s.join(decimal);
				if (olds == s) {
					cursor.start = oldstart;
					cursor.end = oldend;
				}
				return s;
			}
			s[0] = s2;
		}
	}

	function bindNumericFields() {
		$('.fop-numeric input').each(function (i, input) {
			input = $(input);
			input.parent().removeClass('fop-numeric');
			var decimals = input.attr('fop-numeric');
			var ngModel = input.attr('fop-numeric-bind');
			var decimal = input.attr('fop-numeric-decimal');
			var thousands = input.attr('fop-numeric-thousands');
			var in_process = false;

			if (ngModel) {
				// wenn sich das Model extern changt
				$(input).scope().$watch(ngModel, function (new_value, old_value) {
					if (in_process) return;
					if (new_value === undefined) return;
					if (isNaN(new_value)) { input.val('Fehler'); return; }
					if (decimals >= 0) {
						// kaufmännisch runden auf die Präzision
						new_value = Math.round(new_value * Math.pow(10, decimals)) / Math.pow(10, decimals);
					}
					var cursor = {start: input[0].selectionStart, end: input[0].selectionEnd};
					input.val(addSeparators(String(new_value).replace('.', decimal), decimals, {start: 0, end: 0}, decimal, thousands));
					input[0].setSelectionRange(cursor.start, cursor.end);
				});
			}

			function updateModel() {
				if (ngModel) {
					var val = input.val().replaceAll(thousands, '').replace(decimal, '.');
					in_process = true;
					$(input).scope().$apply(ngModel + ' = ' + (/^-?\d+(?:\.\d+)?$/.test(val) ? Number(val) : 'undefined'));
					in_process = false;
					input[0].parentNode.MaterialTextfield.updateClasses_();
				}
			}
			
			input.on('keydown', function (e)  {
				if (event.keyCode == 13) {
					event.preventDefault();
					event.stopPropagation();
					tabNext(this);
					return false;
				}
				if ((e.key == decimal || e.key == thousands) && decimals != 0) {
					// Komma
					var cursor = {start: input[0].selectionStart + 1, end: input[0].selectionStart + 1};
					input.val(addSeparators(input.val().slice(0, input[0].selectionStart).replace(decimal, '') + decimal + input.val().slice(input[0].selectionEnd).replace(decimal, ''), decimals, cursor, decimal, thousands));
					input[0].setSelectionRange(cursor.start, cursor.end);
					updateModel();
					e.preventDefault();
				} else if (e.key >= '0' && e.key <= '9') {
					// Ziffer
					var cursor = {start: input[0].selectionStart + 1, end: input[0].selectionStart + 1};
					input.val(addSeparators(input.val().slice(0, input[0].selectionStart) + e.key + input.val().slice(input[0].selectionEnd), decimals, cursor, decimal, thousands));
					input[0].setSelectionRange(cursor.start, cursor.end);
					updateModel();
					e.preventDefault();
				} else if (e.key == '-' && input[0].selectionStart == 0 && (input[0].selectionEnd > 0 || input.val().slice(0,1) != '-')) {
					// '-' als Vorzeichen: normale Behandlung
					var cursor = {start: 1, end: 1};
					input.val(addSeparators('-' + input.val().slice(input[0].selectionEnd), decimals, cursor, decimal, thousands));
					input[0].setSelectionRange(cursor.start, cursor.end);
					updateModel();
					e.preventDefault();
				} else if (e.key.length == 1 && !e.altKey && !e.ctrlKey && !e.metaKey) {
					// druckbare Zeichen, aber keine Tastenkombination: nicht zugelassen
					e.preventDefault();
				} else if (e.key == 'Backspace' || e.key == 'Delete') {
					var cursor = {start: input[0].selectionStart, end: input[0].selectionEnd};
					// nichts ausgewählt => Zeichen vor/nach Cursor auswählen
					// Dezimaltrennzeichen soll dabei übersprungen werden (ease of use)
					if (cursor.start == cursor.end) {
						if (e.key == 'Backspace' && cursor.start > 0) {
							cursor.start--;
							if (input.val().charAt(cursor.start) == decimal && cursor.start > 0) {
								cursor.start--;
								cursor.end--;
							}
						}
						if (e.key == 'Delete') {
							cursor.end++;
							if (input.val().charAt(cursor.start) == decimal && cursor.end < input.val().length) {
								cursor.start++;
								cursor.end++;
							}
						}
					}
					// Auswahl entfernen
					input.val(addSeparators(input.val().slice(0,cursor.start) + input.val().slice(cursor.end), decimals, cursor, decimal, thousands));
					// Korrektur: Wenn Tausenderpunkt mit Entf gelöscht => Cursor rechts davon
					if (e.key == 'Delete' && input.val().charAt(cursor.start) == thousands) input[0].setSelectionRange(cursor.start+1, cursor.start+1);
					else input[0].setSelectionRange(cursor.start, cursor.start);
					updateModel();
					e.preventDefault();
				}
			});
			
			input.on('paste cut', function() {
				// Ausschneiden, Einfügen: nach 50ms Tausenderpunkte neu ausrichten
				setTimeout(function () {
					var cursor = {start: input[0].selectionStart, end: input[0].selectionEnd};
					input.val(addSeparators(input.val(), decimals, cursor, decimal, thousands));
					input[0].setSelectionRange(cursor.start, cursor.end);
					updateModel();
				}, 50)
			})
		});
	}
	