
	jQuery.isUndefined = function(o) { return o === undefined };
	jQuery.isNumeric = function(n) { return !isNaN(parseInt(n)) };

	function JPMap() {			
		this.keys = [];
		this.values = [];
		this.iterator = -1;
		
		return this;
	}
	JPMap.prototype = {
		
		_get: function(key) {
			var itemIndex = $.inArray(key, this.keys);
			
			if (itemIndex == -1)
				return undefined;
			else return this.values[itemIndex];	
		},
		
		get: function(key) {
			var item = this._get(key);
			if ($.isUndefined(item)) return item;
			
			return this.isPreservant(item) ? item.vals : item;
		},
		
		isPreservant: function(value) {
			if ($.isUndefined(value) || value == null || typeof(value) !== "object") return false;
			return !!value.__JPMap__Preservant__;
		},
		
		set: function(key, val) {
			var itemIndex = $.inArray(key, this.keys);
			
			if (itemIndex == -1) {
				this.iterator++;
				this.keys[this.iterator] = key;
				this.values[this.iterator] = val;
			} else {
				this.values[itemIndex] = val;
			}
		},
		
		length: function() {
			return this.keys.length;
		},
		
		hasKey: function(key) {
			return $.inArray(key, this.keys) != -1;
		},
		
		first: function() {
			return this.values[0];
		},
		
		preservantSet: function(key, val) {
			if (!this.hasKey(key))
				return this.set(key, val);
			
			var currentVal = this._get(key);
			
			if (!this.isPreservant(currentVal)) {
				this.set(key, {
					__JPMap__Preservant__: true,
					vals: []
				});
				this._get(key).vals.push(currentVal);
			}
			
			this._get(key).vals.push(val);
		},
		
		remove: function(key) {
			var itemIndex = $.inArray(key, this.keys);
			
			if (itemIndex == -1)
				return undefined;
			
			return { 
				key: 	this.keys.splice(   itemIndex, 1 ), 
				value: 	this.values.splice( itemIndex, 1 ) 
			};
		},
		
		each: function(cb) {
			for (var i = 0; i < this.keys.length; ++i) cb(this.keys[i], this.values[i]);
		},
		
		uniqify: function() {
			var nk = [];
			var nv = [];
			for (var i = 0; i < this.keys.length; ++i) {
				if ($.inArray(this.values[i], nv) == -1) {
					nk.push(this.keys[i]);
					nv.push(this.values[i]);
				}
			}
			this.keys = nk;
			this.values = nv;
		},
		toString: function() {
			var str = "{";
			for (var i = 0; i < this.keys.length; ++i) str += "\"" + this.keys[i] + "\": \"" + this.values[i] + "\",";
			str = str.substr(0, str.length - 1) + "}";
			return str;
		},
		dump: function(dname, count) {
			var _c = count || this.length();
			var sdump = "";
			for (var i = 0; i < _c; ++i) sdump += (dname||"j")+".set(\"" + this.keys[i] + "\",\"" + this.values[i] + "\");";
			return sdump;
		}
	}
	
	function isetSelectionRange(input, selectionStart, selectionEnd) {
		if (input.createTextRange) {
			var range = input.createTextRange();
			range.collapse(true);
			range.moveEnd('character', selectionEnd);
			range.moveStart('character', selectionStart);
			range.select();
		}
		else if (input.setSelectionRange) {
			input.focus();
			input.setSelectionRange(selectionStart, selectionEnd);
		}
	}
	/**
		Some functionality from prototype
	**/
	Array.prototype.each = function(iterator, context) {
	    for (var i = 0, length = this.length >>> 0; i < length; i++) {
	      if (i in this) iterator.call(context, this[i], i, this);
	    }
	  }
	  
	Array.prototype.pluck = function(property) {
	    var results = [];
	    this.each(function(value) {
	      results.push(value[property]);
	    });
	    return results;
	  }
	  
	  Array.prototype.toStringify = function() {
	  	this.each(function(value) {
	  		value.toString = function() { return this.label; };
	  	})
	  }
	  
	
	// JP's port of Prototype's bindAsEventListener
	Function.prototype.bindWith = function(ctx, data) {
		return $.proxy(function(){
			var args = [];
			args.push(this.data); 
			for (var i = 0; i < arguments.length; ++i)
				args.push(arguments[i]);
			this.ofunc.apply(this.ctx, args);
		}, {data: data, ofunc: this, ctx: ctx});
	}
	
	var glob = new JPMap();
	
	jQuery.indexes = function(outer, inner) {
		if (!inner || inner.length == 0 || inner == "")
			return [];
		var idx = [];
		var l = 0;
		var outerl = outer.toLowerCase();
		var innerl = inner.toLowerCase();
		while ((l = outerl.indexOf(innerl,l)) != -1) { idx.push(l); l += inner.length; }
		return idx;
	}
	
	String.prototype.replaceAt = function(index, char) {
    	return this.substr(0, index) + char + this.substr(index+char.length);
   	}
	
	jQuery.contiguities = function(outer, inner) {
		var r = $.indexes(outer,inner);
		var f = [];
		for (var i = 0; i < r.length; ++i) {
			f.push({idx: r[i], len: inner.length, fin: (r[i]+inner.length)});
		}
		return f;
	}
	
	jQuery.wrapContiguities = function(outer, inner, wrfunc) {
		var c = $.contiguities(outer,inner);
		if (c.length == 0)
			return outer;
			
		var after  = outer.substring(c[c.length-1].fin);
		var newOuter = "";
		for (var i = 0; i < c.length; ++i) {
			newOuter += outer.substring(i == 0 ? 0 : (c[i-1].fin), c[i].idx);
			newOuter += wrfunc( outer.substr(c[i].idx, c[i].len) );
		}
		
		newOuter += after;
		
		return newOuter;
	}
	
	var searchTerm = "";
	var oldCountry, didSelectPCode=false;
	
	$(function() {
		$("#pcodes").focus(function() {
			didSelectPCode = false;
			oldCountry = $(".CountryDDLSelect").val();
			if (USER_COUNTRY=="AU") $(".CountryDDLSelect").val(1);
			else if (USER_COUNTRY=="US") $(".CountryDDLSelect").val(3);
			else $(".CountryDDLSelect").val(232);
		}).blur(function() {
			if (!didSelectPCode && ($(this).val() == "" || $(this).val() == "Enter Zip / Postcode Here")) $(".CountryDDLSelect").val(oldCountry);
			else if (!didSelectPCode) {
				var val = $(this).val();
				if (val.length == 4)  $(".CountryDDLSelect").val(1);
				else if (val.length == 5) $(".CountryDDLSelect").val(3);
				else  $(".CountryDDLSelect").val(232);
			} 
		})
		$("#pcodes").autocomplete({
			source: function(req,res) {
				var t = new Date().getTime();
				glob.set(t, {rcb: res});
				$.ajax({
					url: "include/multisite/ajax/MSAutoCompleter.asp?pcm=true",
					data: ("term="+$("#pcodes").val()),
					complete: $.proxy(function(xhr) {
						var r = $.parseJSON(xhr.responseText);
						r.toStringify();
						this.rcb(r);
					}, glob.get(t))
				});
			},
			open: function() {
				searchTerms = $("#pcodes").val().split(" ");
				$(".ui-autocomplete a").each(function() {
					var resultText = $(this).text();
					for (var i = 0; i < searchTerms.length; ++i)
						resultText = $.wrapContiguities(resultText, searchTerms[i], function(e) { return "[" + e + "]"; } );
						
					resultText = resultText.replace(/\[/g, "<b>");
					resultText = resultText.replace(/\]/g, "</b>");
					$(this).html(resultText);
				});
			},
			select: function(event, ui) {
				event.preventDefault();
				$("#pcodes").val(ui.item.pcode);
				didSelectPCode=true;
				$(".CountryDDLSelect").val(ui.item.country);
			},
			delay:200
		});
		/*$("#cloestLoc").autocomplete({
			source: function(req,res) {
				var t = new Date().getTime();
				glob.set(t, {rcb: res});
				$.ajax({
					url: "include/multisite/ajax/MSAutoCompleter.asp?pcm=true",
					data: ("term="+$("#cloestLoc").val()),
					complete: $.proxy(function(xhr) {
						var r = $.parseJSON(xhr.responseText);
						r.toStringify();
						this.rcb(r);
					}, glob.get(t))
				});
			},
			open: function() {
				searchTerms = $("#cloestLoc").val().split(" ");
				$(".ui-autocomplete a").each(function() {
					var resultText = $(this).text();
					for (var i = 0; i < searchTerms.length; ++i)
						resultText = $.wrapContiguities(resultText, searchTerms[i], function(e) { return "[" + e + "]"; } );
						
					resultText = resultText.replace(/\[/g, "<b>");
					resultText = resultText.replace(/\]/g, "</b>");
					$(this).html(resultText);
				});
			},
			select: function(event, ui) {
				event.preventDefault();
				$("#cloestLoc").val(ui.item.pcode);
				didSelectPCode=true;
				$(".CountryDDLSelect").val(ui.item.country);
			},
			delay:200
		});*/
		$("#avaLoc").autocomplete({
			source: function(req,res) {
				var t = new Date().getTime();
				glob.set(t, {rcb: res});
				$.ajax({
					url: "include/multisite/ajax/MSAutoCompleter.asp",
					data: ("term="+$("#avaLoc").val()),
					complete: $.proxy(function(xhr) {
						var r = $.parseJSON(xhr.responseText);
						r.toStringify();
						this.rcb(r);
					}, glob.get(t))
				});
			},
			open: function() {
				searchTerms = $("#avaLoc").val().split(" ");
				$(".ui-autocomplete a").each(function() {
					var resultText = $(this).text();
					for (var i = 0; i < searchTerms.length; ++i)
						resultText = $.wrapContiguities(resultText, searchTerms[i], function(e) { return "[" + e + "]"; } );
						
					resultText = resultText.replace(/\[/g, "<b>");
					resultText = resultText.replace(/\]/g, "</b>");
					$(this).html(resultText);
				});
			},
			select: function(event, ui) {
				setTimeout($.proxy(function() {
					$(".CountryDDLSelect").val(this.country);
					setStateList();
					$(".StateDDLSelect").val(this.state);
					setCityList();
					$(".CityDDLSelect").val(this.city);
				}, ui.item), 1);
			},
			delay:200
		});
		$("#countryCity").autocomplete({
			source: function(req,res) {
				var t = new Date().getTime();
				glob.set(t, {rcb: res});
				$.ajax({
					url: "include/multisite/ajax/MSAutoCompleter.asp",
					data: ("term="+$("#countryCity").val()),
					complete: $.proxy(function(xhr) {
						var r = $.parseJSON(xhr.responseText);
						r.toStringify();
						this.rcb(r);
					}, glob.get(t))
				});
			},
			open: function() {
				searchTerms = $("#countryCity").val().split(" ");
				$(".ui-autocomplete a").each(function() {
					var resultText = $(this).text();
					for (var i = 0; i < searchTerms.length; ++i)
						resultText = $.wrapContiguities(resultText, searchTerms[i], function(e) { return "[" + e + "]"; } );
						
					resultText = resultText.replace(/\[/g, "<b>");
					resultText = resultText.replace(/\]/g, "</b>");
					$(this).html(resultText);
				});
			},
			select: function(event, ui) {
				setTimeout($.proxy(function() {
					$(".CountryDDLSelect").val(this.country);
					setStateList();
					$(".StateDDLSelect").val(this.state);
					setCityList();
					$(".CityDDLSelect").val(this.city);
					$("#countryCity").focus();
					$("#countryCity").setCaretPos(1);
					$("#countryCity").blur();					
				}, ui.item), 1);
				isetSelectionRange(event.target,0,0);
			},
			delay:200
		});
	})
	
/*
 * jQuery geo_autocomplete plugin 2.1.1
 *
 * Copyright (c) 2010 Bob Hitching
 *
 * Dual licensed under the MIT and GPL licenses:
 *   http://www.opensource.org/licenses/mit-license.php
 *   http://www.gnu.org/licenses/gpl.html
 *
 * Requires jQuery UI Autocomplete
 * 
 */
$.widget( "ui.geo_autocomplete", {
        // setup the element as an autocomplete widget with some geo goodness added
        _init: function() {
                this.options._geocoder = new google.maps.Geocoder; // geocoder object
                this.options._cache = {}; // cache of geocoder responses
                this.element.autocomplete(this.options);
                
                // _renderItem is used to prevent the widget framework from escaping the HTML required to show the static map thumbnail
                this.element.data('autocomplete')._renderItem = function(_ul, _item) {
                        return $('<li></li>').data('item.autocomplete', _item).append(this.options.getItemHTML(_item)).appendTo(_ul);
                };
        },
        
        // default values
        options: {
                geocoder_region: '', // filter to a specific region, e.g. 'Europe'
                geocoder_types: 'locality,political,sublocality,neighborhood,country', // array of acceptable location types, see http://code.google.com/apis/maps/documentation/javascript/services.html#GeocodingAddressTypes
                geocoder_address: false, // true = use the full formatted address, false = use only the segment that matches the search term

                mapwidth: 100, // width of static map thumbn ail
                mapheight: 100, // height of static map thumbnail
                maptype: 'terrain', // see http://code.google.com/apis/maps/documentation/staticmaps/#MapTypes
                mapsensor: false, // see http://code.google.com/apis/maps/documentation/staticmaps/#Sensor
                postcodeOnly: false, // added by JP 24.1.11 for ci.wg

                minLength: 3, // see http://jqueryui.com/demos/autocomplete/#option-minLength
                delay: 300, // see http://jqueryui.com/demos/autocomplete/#option-delay
                // callback function to get autocomplete results
                source: function(_request, _response) {
                        if (_request.term in this.options._cache) {
                                _response(this.options._cache[_request.term]);
                        } else {
                                var self = this;
                                var _address = _request.term + (this.options.geocoder_region ? ', ' + this.options.geocoder_region : '');
                                this.options._geocoder.geocode({'address': _address}, function(_results, _status) {
                                        var _parsed = [];
                                        if (_results && _status && _status == 'OK') {
                                                var _types = self.options.geocoder_types.split(',');
                                                $.each(_results, function(_key, _result) {
                                                        // if this is an acceptable location type with a viewport, it's a good result
                                                        if ($.map(_result.types, function(_type) {
                                                                return $.inArray(_type, _types) != -1 ? _type : null;
                                                        }).length && _result.geometry && _result.geometry.viewport) {
																if (self.options.postcodeOnly) {
																	for (var i = 0; i < _result.address_components.length; ++i) {
																		if ($.inArray("postal_code", _result.address_components[i].types) != -1) {
																			_place = _result.address_components[i].short_name;
																			break;
																		}
																	}
																} else if (self.options.geocoder_address) {
                                                                        _place = _result.formatted_address;
                                                                } else {
                                                                        // place is first matching segment, or first segment
                                                                        var _place_parts = _result.formatted_address.split(',');
                                                                        var _place = _place_parts[0];
                                                                        $.each(_place_parts, function(_key, _part) {
                                                                                if (_part.toLowerCase().indexOf(_request.term.toLowerCase()) != -1) {
                                                                                        _place = $.trim(_part);
                                                                                        return false; // break
                                                                                }
                                                                        });
                                                                }
                                                        
                                                                _parsed.push({
                                                                        value: _place,
                                                                        label: _result.formatted_address,
                                                                        viewport: _result.geometry.viewport
                                                                });
                                                        }
                                                });
                                        }
                                        self.options._cache[_request.term] = _parsed;
                                        _response(_parsed);
                                });
                        }
                },
                // returns the HTML used for each autocomplete list item
                getItemHTML: function(_item) {          
                        var _src = 'http://maps.google.com/maps/api/staticmap?visible=' + _item.viewport.getSouthWest().toUrlValue() + '|' + _item.viewport.getNorthEast().toUrlValue() + '&size=' + this.mapwidth + 'x' + this.mapheight + '&maptype=' + this.maptype + '&sensor=' + (this.mapsensor ? 'true' : 'false');
                        return '<a><img style="float:left;margin-right:5px;" src="' + _src + '" width="' + this.mapwidth + '" height="' + this.mapheight + '" /> ' + _item.label.replace(/,/gi, ',<br/>') + '<br clear="both" /></a>'
                }
        }
});


/*! 
 * a-tools 1.2
 * 
 * Copyright (c) 2009 Andrey Kramarev, Ampparit Inc. (www.ampparit.com)
 * Licensed under the MIT license.
 * http://www.ampparit.fi/a-tools/license.txt
 *
 * Basic usage:
 
    <textarea></textarea>
    <input type="text" />

    // Get current selection
    var sel = $("textarea").getSelection()
    
    // Replace current selection
    $("input").replaceSelection("foo");

    // Count characters
    alert($("textarea").countCharacters());

    // Set max length without callback function
    $("textarea").setMaxLength(7);

    // Set max length with callback function which will be called when limit is exceeded
    $("textarea").setMaxLength(10, function() {
        alert("hello")
    });

    // Removing limit:
    $("textarea").setMaxLength(-1);
    
    // Insert text at current caret position
    $("#textarea").insertAtCaretPos("hello");
    
    // Set caret position (1 = beginning, -1 = end)
    $("#textArea").setCaretPos(10);

 */
var caretPositionAmp;

jQuery.fn.extend({
	getSelection: function() {  // function for getting selection, and position of the selected text
		var input = this.jquery ? this[0] : this;
		var start;
		var end;
		var part;
		if (document.selection) {
			// part for IE and Opera
			var s = document.selection.createRange();
			if (s.text) {
				var number = 0;
				part = s.text;
				if (input.value.match(/\n/g) != null) {
					number = input.value.match(/\n/g).length;// number of EOL simbols
				}
				var minus = 0;
				var position = 0;
				var minusEnd = 0;
				// OPERA support
				if (typeof(input.selectionStart) == "number") {
					start = input.selectionStart;
					end = input.selectionEnd;
					// return null if the selected text not from the needed area
					if (start == end) {
						return this;
					}
				} else {
					// IE support
					var re = input.createTextRange();
					var firstRe;
					var secondRe;
					var rc = re.duplicate();
					firstRe = re.text;
					re.moveToBookmark(s.getBookmark());
					secondRe = re.text;
					rc.setEndPoint("EndToStart", re);
					// return null if the selectyed text not from the needed area
					if (firstRe == secondRe && firstRe != s.text) {
						return this;
					}
					start = rc.text.length; 
					end = rc.text.length + s.text.length;
				}
				// remove all EOL to have the same start and end positons as in MOZILLA
				if (number > 0) {
					for (var i = 0; i <= number; i++) {
						var w = input.value.indexOf("\n", position);
						if (w != -1 && w < start) {
							position = w + 1;
							minus++;
							minusEnd = minus;
						} else if (w != -1 && w >= start && w <= end) {
							if (w == start + 1) {
								minus--;
								minusEnd--;
								position = w + 1;
								continue;
							}
							position = w + 1;
							minusEnd++;
						} else {
							i = number;
						}
					}
				}
				if (s.text.indexOf("\n", 0) == 1) {
					minusEnd = minusEnd + 2;
				}
				start = start - minus;
				end = end - minusEnd;
				return { start: start, end: end, text: s.text, length: end - start };
			}
			return false;
		} else if (typeof(input.selectionStart) == "number" && //MOZILLA support
				input.selectionStart != input.selectionEnd) {
			start = input.selectionStart;
			end = input.selectionEnd;
			part = input.value.substring(input.selectionStart, input.selectionEnd);
			return { start: start, end: end, text: part, length: end - start };
		} else { return { start: undefined, end: undefined, text: undefined, length: undefined }; }
	},

	// function for the replacement of the selected text
	replaceSelection: function(inputStr) {
		var input = this.jquery ? this[0] : this; 
		//part for IE and Opera
		var start;
		var end;
		var position = 0;
		var rc;
		var re;
		var number = 0;
		var minus = 0;
		if (document.selection && typeof(input.selectionStart) != "number") {
			var s = document.selection.createRange();
			
			// IE support
			if (typeof(input.selectionStart) != "number") { // return null if the selected text not from the needed area
				var firstRe;
				var secondRe;
				re = input.createTextRange();
				rc = re.duplicate();
				firstRe = re.text;
				re.moveToBookmark(s.getBookmark());
				secondRe = re.text;
				rc.setEndPoint("EndToStart", re);
				if (firstRe == secondRe && firstRe != s.text) {
					return this;
				}
			}
			if (s.text) {
				
			
				
				part = s.text;
				if (input.value.match(/\n/g) != null) {
					number = input.value.match(/\n/g).length;// number of EOL simbols
				}
				// IE support
				start = rc.text.length; 
								
				// remove all EOL to have the same start and end positons as in MOZILLA
				if (number > 0) {
					for (var i = 0; i <= number; i++) {
						var w = input.value.indexOf("\n", position);
						if (w != -1 && w < start) {
							position = w + 1;
							minus++;
							
						} else {
							i = number;
						}
					}
				}
				
				
				
				s.text = inputStr;
				caretPositionAmp = rc.text.length + inputStr.length;
				re.move("character", caretPositionAmp);
				document.selection.empty();
				input.blur();
			}
			return this;
		} else if (typeof(input.selectionStart) == "number" && // MOZILLA support
				input.selectionStart != input.selectionEnd) {
			
			start = input.selectionStart;
			end = input.selectionEnd;
			input.value = input.value.substr(0, start) + inputStr + input.value.substr(end);
			position = start + inputStr.length;
			input.setSelectionRange(position, position); 
			return this;
		}
		return this;
	},
	
	// insert text at current caret position
	insertAtCaretPos: function(inputStr) {
		var input = this.jquery ? this[0] : this; 
		var start;
		var end;
		var position;
		var s;
		var re;
		var rc;
		var point;
		var minus = 0;
		input.focus();
		if (document.selection && typeof(input.selectionStart) != "number") {
			if (input.value.match(/\n/g) != null) {
				number = input.value.match(/\n/g).length;// number of EOL simbols
			}
			point = parseInt(caretPositionAmp);
			if (number > 0) {
				for (var i = 0; i <= number; i++) {
					var w = input.value.indexOf("\n", position);
					if (w != -1 && w <= point) {
						position = w + 1;
						point = point - 1;
						minus++;
					} 
				}
			}
		}
		caretPositionAmp = parseInt(caretPositionAmp);
		// IE
		input.onclick = function() { // for IE because it loses caret position when focus changed
			if (document.selection && typeof(input.selectionStart) != "number") {
				s = document.selection.createRange();
				re = input.createTextRange();
				rc = re.duplicate();
				re.moveToBookmark(s.getBookmark());
				rc.setEndPoint("EndToStart", re);
				caretPositionAmp = rc.text.length;
			}
		}
		if (document.selection && typeof(input.selectionStart) != "number") {
			s = document.selection.createRange();
			if (s.text.length != 0) {
				return this;
			}
			re = input.createTextRange();
			textLength = re.text.length;
			rc = re.duplicate();
			re.moveToBookmark(s.getBookmark());
			rc.setEndPoint("EndToStart", re);
			start = rc.text.length; 
			if (caretPositionAmp >= 0 && start ==0 && caretPositionAmp != start) {
				minus = caretPositionAmp - minus;
				re.move("character", minus);
				re.select();
				s = document.selection.createRange();
				caretPositionAmp += inputStr.length;
			} else if (caretPositionAmp == undefined && start ==0) {
				re.move("character", textLength);
				re.select();
				s = document.selection.createRange();
				caretPositionAmp = inputStr.length;
			} else if (!(parseInt(caretPositionAmp) >= 0)) { 
				re.move("character", start);
				document.selection.empty();
				re.select();
				s = document.selection.createRange();
				caretPositionAmp = start + inputStr.length; 
			} else if (parseInt(caretPositionAmp) >= 0) { 
				re.move("character", caretPositionAmp-start);
				document.selection.empty();
				re.select();
				s = document.selection.createRange();
				caretPositionAmp = caretPositionAmp + inputStr.length; 
			} else { 
				re.move("character", caretPositionAmp-start);
				document.selection.empty();
				re.select();
				s = document.selection.createRange();
				caretPositionAmp = caretPositionAmp + inputStr.length; 
			}
			s.text = inputStr; 
			input.focus();

			return this;
		} else if (typeof(input.selectionStart) == "number" && // MOZILLA support
				input.selectionStart == input.selectionEnd) {
			position = input.selectionStart + inputStr.length;
			start = input.selectionStart;
			end = input.selectionEnd;
			input.value = input.value.substr(0, start) + inputStr + input.value.substr(end);
			input.setSelectionRange(position, position); 
			return this;
		}
		return this;
	},	

	
	// Set caret position
	setCaretPos: function(inputStr) {

		var input = this.jquery ? this[0] : this; 
		var s;
		var re;
		var position;
		var number;
		var minus = 0;
		var w;
		input.focus();
		if (parseInt(inputStr) == 0) {
			return this;
		}
		if (parseInt(inputStr) > 0) {
			inputStr = parseInt(inputStr) - 1;
			if (document.selection && typeof(input.selectionStart) == "number" && input.selectionStart == input.selectionEnd) {
				if (input.value.match(/\n/g) != null) {
					number = input.value.match(/\n/g).length;// number of EOL simbols
				}
				if (number > 0) {
					for (var i = 0; i <= number; i++) {
						w = input.value.indexOf("\n", position);
						if (w != -1 && w <= inputStr) {
							position = w + 1;
							inputStr = parseInt(inputStr) + 1;
						} 
					}
				}
			}
		} 
		else if (parseInt(inputStr) < 0) {
			inputStr = parseInt(inputStr) + 1;
			if (document.selection && typeof(input.selectionStart) != "number") {
				inputStr = input.value.length + parseInt(inputStr);
				if (input.value.match(/\n/g) != null) {
					number = input.value.match(/\n/g).length;// number of EOL simbols
				}
				if (number > 0) {
					for (var i = 0; i <= number; i++) {
						w = input.value.indexOf("\n", position);
						if (w != -1 && w <= inputStr) {
							position = w + 1;
							inputStr = parseInt(inputStr) - 1;
							minus += 1;
						} 
					}
					inputStr = inputStr + minus - number;
				}
			} else if (document.selection && typeof(input.selectionStart) == "number") {
				inputStr = input.value.length + parseInt(inputStr);
				if (input.value.match(/\n/g) != null) {
					number = input.value.match(/\n/g).length;// number of EOL simbols
				}
				if (number > 0) {
					inputStr = parseInt(inputStr) - number;
					for (var i = 0; i <= number; i++) {
						w = input.value.indexOf("\n", position);
						if (w != -1 && w <= (inputStr)) {
							position = w + 1;
							inputStr = parseInt(inputStr) + 1;
							minus += 1;
						} 
					}
				}
			} else { inputStr = input.value.length + parseInt(inputStr); }
		} else { return this; }
		// IE
		if (document.selection && typeof(input.selectionStart) != "number") {
			s = document.selection.createRange();
			if (s.text != 0) {
				return this;
			}
			re = input.createTextRange();
			re.moveToBookmark(s.getBookmark());
			re.move("character", inputStr);
			re.select();
			s = document.selection.createRange();
			s.text = ""; 
			caretPositionAmp = inputStr;
			input.focus();
			
			return this;
		} else	if (typeof(input.selectionStart) == "number" && // MOZILLA support
				input.selectionStart == input.selectionEnd) {
			input.setSelectionRange(inputStr, inputStr); 
			return this;
		}
		return this;
		
	},	
	
	
	
	
	countCharacters: function(str) {
		var input = this.jquery ? this[0] : this;
		if (input.value.match(/\r/g) != null) {
			return input.value.length - input.value.match(/\r/g).length;
		}
		return input.value.length;
	},

	setMaxLength: function(max, f) {
		this.each(function() {
			var input = this.jquery ? this[0] : this;
			var type = input.type;
			var isSelected;	
			var maxCharacters;
			// remove limit if input is a negative number
			if (parseInt(max) < 0) {
				max=100000000;
			}
			if (type == "text") {
				input.maxLength = max;
			}
			if (type == "textarea" || type == "text") {
				input.onkeypress = function(e) {
					var spacesR = input.value.match(/\r/g);
					maxCharacters = max;
					if (spacesR != null) {
						maxCharacters = parseInt(maxCharacters) + spacesR.length;
					}
					// get event
					var key = e || event;
					var keyCode = key.keyCode;
					// check if any part of text is selected
					if (document.selection) {
						isSelected = document.selection.createRange().text.length > 0;
					} else {
						isSelected = input.selectionStart != input.selectionEnd;
					}
					if (input.value.length >= maxCharacters && (keyCode > 47 || keyCode == 32 ||
							keyCode == 0 || keyCode == 13) && !key.ctrlKey && !key.altKey && !isSelected) {
						input.value = input.value.substring(0,maxCharacters);
						if (typeof(f) == "function") { f() } //callback function
						return false;
					}
				}
				input.onkeyup = function() { 
					var spacesR = input.value.match(/\r/g);
					var plus = 0;
					var position = 0;
					maxCharacters = max;
					if (spacesR != null) {
						for (var i = 0; i <= spacesR.length; i++) {
							if (input.value.indexOf("\n", position) <= parseInt(max)) {
								plus++;
								position = input.value.indexOf("\n", position) + 1;
							}
						}
						maxCharacters = parseInt(max) + plus;
					} 
					if (input.value.length > maxCharacters) {  
						input.value = input.value.substring(0, maxCharacters); 
						if (typeof(f) == "function") { f() }
						return this;
					}
				}
			} else { return this; }
		})
		return this;
	}
}); 
