/**
sprintf() for JavaScript 0.7-beta1
http://www.diveintojavascript.com/projects/javascript-sprintf

Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of sprintf() for JavaScript nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


Changelog:
2010.09.06 - 0.7-beta1
  - features: vsprintf, support for named placeholders
  - enhancements: format cache, reduced global namespace pollution

2010.05.22 - 0.6:
 - reverted to 0.4 and fixed the bug regarding the sign of the number 0
 Note:
 Thanks to Raphael Pigulla <raph (at] n3rd [dot) org> (http://www.n3rd.org/)
 who warned me about a bug in 0.5, I discovered that the last update was
 a regress. I appologize for that.

2010.05.09 - 0.5:
 - bug fix: 0 is now preceeded with a + sign
 - bug fix: the sign was not at the right position on padded results (Kamal Abdali)
 - switched from GPL to BSD license

2007.10.21 - 0.4:
 - unit test and patch (David Baird)

2007.09.17 - 0.3:
 - bug fix: no longer throws exception on empty paramenters (Hans Pufal)

2007.09.11 - 0.2:
 - feature: added argument swapping

2007.04.03 - 0.1:
 - initial release
**/

var sprintf = (function() {
	function get_type(variable) {
		return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
	}
	function str_repeat(input, multiplier) {
		for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
		return output.join('');
	}

	var str_format = function() {
		if (!str_format.cache.hasOwnProperty(arguments[0])) {
			str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
		}
		return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
	};

	str_format.format = function(parse_tree, argv) {
		var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
		for (i = 0; i < tree_length; i++) {
			node_type = get_type(parse_tree[i]);
			if (node_type === 'string') {
				output.push(parse_tree[i]);
			}
			else if (node_type === 'array') {
				match = parse_tree[i]; // convenience purposes only
				if (match[2]) { // keyword argument
					arg = argv[cursor];
					for (k = 0; k < match[2].length; k++) {
						if (!arg.hasOwnProperty(match[2][k])) {
							throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
						}
						arg = arg[match[2][k]];
					}
				}
				else if (match[1]) { // positional argument (explicit)
					arg = argv[match[1]];
				}
				else { // positional argument (implicit)
					arg = argv[cursor++];
				}

				if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
					throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
				}
				switch (match[8]) {
					case 'b': arg = arg.toString(2); break;
					case 'c': arg = String.fromCharCode(arg); break;
					case 'd': arg = parseInt(arg, 10); break;
					case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
					case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
					case 'o': arg = arg.toString(8); break;
					case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
					case 'u': arg = Math.abs(arg); break;
					case 'x': arg = arg.toString(16); break;
					case 'X': arg = arg.toString(16).toUpperCase(); break;
				}
				arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
				pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
				pad_length = match[6] - String(arg).length;
				pad = match[6] ? str_repeat(pad_character, pad_length) : '';
				output.push(match[5] ? arg + pad : pad + arg);
			}
		}
		return output.join('');
	};

	str_format.cache = {};

	str_format.parse = function(fmt) {
		var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
		while (_fmt) {
			if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
				parse_tree.push(match[0]);
			}
			else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
				parse_tree.push('%');
			}
			else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
				if (match[2]) {
					arg_names |= 1;
					var field_list = [], replacement_field = match[2], field_match = [];
					if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
						field_list.push(field_match[1]);
						while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
							if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
								field_list.push(field_match[1]);
							}
							else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
								field_list.push(field_match[1]);
							}
							else {
								throw('[sprintf] huh?');
							}
						}
					}
					else {
						throw('[sprintf] huh?');
					}
					match[2] = field_list;
				}
				else {
					arg_names |= 2;
				}
				if (arg_names === 3) {
					throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
				}
				parse_tree.push(match);
			}
			else {
				throw('[sprintf] huh?');
			}
			_fmt = _fmt.substring(match[0].length);
		}
		return parse_tree;
	};

	return str_format;
})();

var vsprintf = function(fmt, argv) {
	argv.unshift(fmt);
	return sprintf.apply(null, argv);
};







/**
 *  Добавим в jquery событие mousewheel
 */

( function( jQuery ) {
	
	var types = [ 'DOMMouseScroll', 'mousewheel' ];
	
	jQuery.event.special.mousewheel = {
		setup: function() {
			if ( this.addEventListener )
				for ( var i=types.length; i; )
					this.addEventListener( types[ --i ], handler, false );
			else
				this.onmousewheel = handler;
		},
		teardown: function() {
			if ( this.removeEventListener )
				for ( var i=types.length; i; )
					this.removeEventListener( types[ --i ], handler, false );
			else
				this.onmousewheel = null;
		}
	}
	
	jQuery.fn.extend( {
		mousewheel: function( fn ) {
			return fn ? this.bind( "mousewheel", fn ) : this.trigger( "mousewheel" );
		},
		unmousewheel: function( fn ) {
			return this.unbind( "mousewheel", fn );
		}
	} );
	
	function handler( event ) {
		var args = [].slice.call( arguments, 1 ), delta = 0, returnValue = true;
		event = jQuery.event.fix( event || window.event );
		event.type = "mousewheel";
		if ( event.wheelDelta )
			delta = event.wheelDelta / 120;
		if ( event.detail )
			delta = -event.detail / 3;
		// Add events and delta to the front of the arguments
		args.unshift( event, delta );
		return jQuery.event.handle.apply( this, args );
	}
})( jQuery );



/**
 *  Добавим к jquery методы:
 *  
 *  escape( value ) - экранирует строку для url, используя encodeURIComponent или escape
 *  unescape( value ) - обратное действие к escape
 *  escapeHTML( value ) - аналог функции php htmlspecialchars
 *  unescapeHTML( value ) - обратное действие к escapeHTML
 *  toRelativeURL( string ) - превращает url из вида "http://www.domain.ru/path/file" в "/path/file"
 *  maxZIndex( parentNode ) - возвращает наибольшее установленное значение z-index (поиск ведется по всем дочерним элементам к parentNode)
 *  	если parentNode = null, то поиск ведется по всему документу
 *  pageSize() - возвращает полный размер документа в px
 *  viewPortSize() - возвращает размер рабочей области браузера в px
 *  ajaxFileUpload - BUG документировать
 *  ajaxFormPost - BUG документировать
 *  
 */

jQuery.extend({
	
		generateUID : function () {
			arguments.callee.uid = arguments.callee.uid || 0;
			arguments.callee.uid++;
			return 'uid' + arguments.callee.uid + Math.round( Math.random() * 100 );
		},

		escape : function ( value ) {
			return window.encodeURIComponent ? encodeURIComponent ( value ) : escape ( value )
		},
		
		unescape : function ( value ) {
			return window.decodeURIComponent ? decodeURIComponent ( value ) : unescape ( value )
		},
		
		escapeHTML : function ( value ) {
			value = value.replace( /</g, "&lt;" ).replace( />/g, "&gt;" ).replace( /&/g, "&amp;" ).replace( /"/g, "&quot;" );
			return value.replace( /'/g, "&#39;" );
		},

		unescapeHTML : function ( value ) {
			value = value.replace( /&lt;/g, "<" ).replace( /&gt;/g, ">" );
			return value.replace( /&quot;/g, '"' ).replace( /&#39;/g, "'" );
		},
		
		toRelativeURL : function( string ) {
			if ( string.charAt ( 0 ) == '/' )
				return string;
			if ( ( string.indexOf ( '://' ) != -1 ) ) {
				var a = string.split ( '/' ), ret = '', i;
				for ( i = 3; i < a.length; i++ )
					ret += '/'+ a[ i ];
				return ret;
			}
			return string;
		},

		maxZIndex : function ( parentNode ) {
			parentNode = parentNode || document;
			var zIndex = 0, i, z;
			jQuery( parentNode ).find( '*' ).each(
				function() {
					var z = parseInt( jQuery.css( this, "z-index" ) ); 
					if ( z > zIndex ) zIndex = z;
				}
			);
			return zIndex;
		},

		pageSize : function () {
			var b = document.documentElement && !jQuery.browser.opera ? document.documentElement : document.body;
			return {
				width : Math.max( b.scrollWidth, b.offsetWidth, b.clientWidth ),
				height : Math.max( b.scrollHeight, b.offsetHeight, b.clientHeight )
			}
		},
		
		viewPortSize : function ( w ) {
			w = w || window;
			var result
			if ( jQuery.browser.safari && !document.evaluate ) {
				// Safari <3.0 needs self.innerWidth/Height
				result = { width : self.innerWidth, height : self.innerHeight }
			} else if ( jQuery.browser.opera && parseFloat( window.opera.version() ) < 9.5 ) {
				// Opera <9.5 needs document.body.clientWidth/Height
				result = { width : document.body.clientWidth, height : document.body.clientHeight }
			} else {
				result = { width : document.documentElement.clientWidth, height : document.documentElement.clientHeight }
			}
			result[ 'left' ] = window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft;
			result[ 'top' ] = window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop;
			result[ 'centerLeft' ] = parseInt( result.width / 2 ) + result.left;
			result[ 'centerTop' ] = parseInt( result.height / 2 ) + result.top;
			return result;
		},
		
		ajaxFileUpload : function ( s ) {
			s = jQuery.extend( {}, jQuery.ajaxSettings, s );
			arguments.callee.iuid = arguments.callee.iuid || 0;
			var
				xml = {
					isIframe : true
				},
				requestDone = false,
				id = 'form_pf_' + arguments.callee.iuid++,
				frameId = 'jUploadFrame' + id,
				formId = 'jUploadForm' + id,
				form = jQuery( '<form name="' + formId + '" id="' + formId + '" method="POST" encoding="multipart/form-data" enctype="multipart/form-data" action="' + s.url + '" target="' + frameId + '"/>' ).appendTo( jQuery( '<div style="display:none;"><iframe name="' + frameId + '" id="' + frameId + '" src="about:blank" style="display:none;"> </iframe></div>' ).appendTo( 'body' ) );
			/*
			jQuery( form ).attr( 'action', s.url );
			jQuery( form ).attr( 'method', 'POST' );
			jQuery( form ).attr( 'target', frameId );
			if ( form.encoding ) {
				form.encoding = 'multipart/form-data';
			} else {
				form.enctype = 'multipart/form-data';
			} */

			// Watch for a new set of requests
			if ( s.global && ! jQuery.active++ ) {
				jQuery.event.trigger( "ajaxStart" );
			}
			// Create the request object
			if ( s.global )
				jQuery.event.trigger( "ajaxSend", [ xml, s ] );
			//cloning file elements to form
			jQuery( s.fileElems ).each(
				function() {
					this.parentNode.insertBefore( this.cloneNode( false ), this.nextSibling );
					form.append( this );	
				}
			);
			//building input elemets for data
			s.data = s.data || {};
			if ( $.isArray( s.data ) && !!s.data.length ) {
				//serialized by serializeArray
				function add( key, value ){
					jQuery( '<input type="text" name="' + key + '" id="' + key + '" value="' + value.replace(/\"/g, "&quot;") + '"/>' ).appendTo( form );		
				};
				$.each( s.data, function(){ add( this.name, this.value ) } );
			} else if ( typeof s.data === "object" && !$.isArray( s.data ) ) {
				//just object {}
				for( var i in ( s.data || {} ) ) {
					jQuery( '<input type="text" name="' + i + '" id="' + i + '" value="' + s.data[ i ].replace(/\"/g, "&quot;") + '"/>' ).appendTo( form );		
				}
			}
				
			// Wait for a response to come back
			var uploadCallback = function( isTimeout ) {
				var io = document.getElementById( frameId );
				try {
					if( io.contentDocument && io.contentDocument.document ) {
						xml.responseText = io.contentDocument.document.body ? io.contentDocument.document.body.innerHTML : null;
						xml.responseXML =
							io.contentDocument.document.XMLDocument ? io.contentDocument.document.XMLDocument :
								io.contentDocument.document;
					} else if ( io.contentWindow && io.contentWindow.document ) {
						xml.responseText = io.contentWindow.document.body ? io.contentWindow.document.body.innerHTML : null;
						xml.responseXML =
							io.contentWindow.document.XMLDocument ? io.contentWindow.document.XMLDocument :
								io.contentWindow.document;
					}
				} catch( e ) {
					jQuery.handleError( s, xml, null, e );
				}
				if ( xml || isTimeout == "timeout" ) {
					requestDone = true;
					var status;
					try {
						status = isTimeout != "timeout" ? "success" : "error";
						// Make sure that the request was successful or notmodified
						if ( status != "error" ) {
							// process the data (runs the xml through httpData regardless of callback)
							var data = !s.dataType;
							if ( s.dataType == "xml" || data ) {
								data = xml.responseXML;
							} else {
								//Opera add <pre> at begin only
								//IE add <pre> at begin and </pre> at end
								//Safary/Chrome add <pre.......> at begin and </pre> at end
								data = xml.responseText.replace( /^<pre[^>]*>/ig, '' );
								data = data.replace( /<\/pre>$/ig, '' );
								data = jQuery.unescapeHTML( data );
								xml.responseText = data;
							}
							// If the s.dataType is "script", eval it in global context
							if ( s.dataType == "script" ) jQuery.globalEval( data );
							// Get the JavaScript object, if JSON is used.
							if ( s.dataType == "json" ) eval( "data = " + data );
							// evaluate scripts within html
							if ( s.dataType == "html" ) jQuery( "<div>" ).html( data ).evalScripts();
							// If a local callback was specified, fire it and pass it the data
							if ( s.success )
								s.success( data, status );
							// Fire the global callback
							if( s.global )
								jQuery.event.trigger( "ajaxSuccess", [ xml, s ] );
						} else
							jQuery.handleError( s, xml, status );
					} catch( e ) {
						status = "error";
						jQuery.handleError( s, xml, status, e );
					}
					// The request was completed
					if( s.global )
						jQuery.event.trigger( "ajaxComplete", [ xml, s ] );
					// Handle the global AJAX counter
					if ( s.global && ! --jQuery.active )
						jQuery.event.trigger( "ajaxStop" );
					// Process result
					if ( s.complete )
						s.complete( xml, status );
					jQuery( io ).unbind()
					setTimeout( function() {
							try {
								jQuery( io ).remove();
								jQuery( cont ).remove();
							} catch( e ) {
								jQuery.handleError( s, xml, null, e );
							}
						}, 100 );
					xml = null;
				}
			}
			// Timeout checker
			if ( s.timeout > 0 ) {
				setTimeout( function(){
					// Check to see if the request is still happening
					if( !requestDone )
						uploadCallback( "timeout" );
				}, s.timeout );
			}
			if( window.attachEvent ) {
				document.getElementById( frameId ).attachEvent( 'onload', uploadCallback );
			} else {
				document.getElementById( frameId ).addEventListener( 'load', uploadCallback, false );
			}
			try {
				jQuery( form ).submit();
			} catch( e ) {
				jQuery.handleError( s, xml, null, e );
			}
			return { abort: function () {} };
	},
	
	
	ajaxFormPost : function ( formElem, callback, url ) {
		var
			serializedForm = $( formElem ).serializeArray(),
			fileElems = $( formElem ).find( "input[type='file']" );
		if ( callback )
			serializedForm = callback( 'data', serializedForm ) || serializedForm;
		if ( fileElems.length ) {
			if ( callback )
				callback( 'send' );
			$.ajaxFileUpload( {
				fileElems: fileElems,
				url: url,
				data: serializedForm,
				success: callback ? function ( response ) { callback( 'success', response ) } : null,
				dataType: 'text'
			} );
		} else {
			if ( !jQuery.ajaxSettings.uniqueCounter )
				jQuery.ajaxSettings.uniqueCounter = 1;
			jQuery.ajaxSettings.uniqueCounter++;
			var
				boundaryString = "qQ" + jQuery.ajaxSettings.uniqueCounter + Math.round( Math.random() * 100 ) +"zZ",
				boundary = '--'+ boundaryString,
				s = [];
			if ( $.isArray( serializedForm ) && !!serializedForm.length ) {
				function add( key, value ){
					// key'%5B %5D'
					s[ s.length ] =
						'Content-Disposition: form-data; name="'+ encodeURIComponent( key ).replace( /%5B/gi, '[' ).replace( /%5D/gi, ']' ) +'"'+'\n'+
						'Content-Type: text/plain'+'\n'+
						'\n'+
						encodeURIComponent( value );
				};
				$.each( serializedForm, function(){
					add( this.name, this.value );
				});
			}
			serializedForm = boundary +'\n'+ s.join ( '\n'+ boundary +'\n' ) +'\n'+ boundary +'\n';
			if ( callback )
				callback( 'send' );
			$.ajax( {
				type: "POST",
				url: url,
				processData: false,
				data: serializedForm,
				success: callback ? function ( response ) { callback( 'success', response ) } : null,
				dataType: 'text',
				contentType : "multipart/form-data; boundary=\""+ boundaryString +"\""
			} );

		}
	}
	
});





jQuery.fn.positionedOffset = function() {
	var offsetParent = this.offsetParent(), offset = this.offset(), position = this.position();
	if ( !/^body|html$/i.test( offsetParent[ 0 ].tagName ) ) {
		return { left : position.left, top : position.top, from : offsetParent }
	} else {
		return { left : offset.left, top : offset.top, from : offsetParent }
	}
}


jQuery.fn.absolutize = function() {
	
	if ( arguments[ 1 ] ) {
		var correction = { left : arguments[ 0 ], top : arguments[ 1 ] }
	} else {
		var correction = arguments[ 0 ] || { left : 0, top : 0 }
	}

  return this.each( function() {
    var element = jQuery( this );
    if ( element.css( 'position' ) == 'absolute' ) {
      return element;
    }

    var offsets = element.positionedOffset();
    var width = element[ 0 ].clientWidth;
    var height = element[ 0 ].clientHeight;

    element._originalPosition = element.css( "position" );
    element._originalLeft = offsets.left - parseFloat( element.css( "left" ) || 0 );
    element._originalTop = offsets.top - parseFloat( element.css( "top" ) || 0 );
    element._originalWidth = element.css( "width" );
    element._originalHeight = element.css( "height" );

    element.css( "position", "absolute" );
    element.css( "top", offsets.top + correction.top + 'px' );
    element.css( "left", offsets.left + correction.left + 'px' );
    element.css( "width", width + 'px' );
    element.css( "height", height + 'px' );
    return element;

  });
}



/**
 * Cookie plugin
 *
 * Copyright (c) 2006 Klaus Hartl (stilbuero.de)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 */

/**
 * Create a cookie with the given name and value and other optional parameters.
 *
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Set the value of a cookie.
 * @example $.cookie('the_cookie', 'the_value', { expires: 7, path: '/', domain: 'jquery.com', secure: true });
 * @desc Create a cookie with all available options.
 * @example $.cookie('the_cookie', 'the_value');
 * @desc Create a session cookie.
 * @example $.cookie('the_cookie', null);
 * @desc Delete a cookie by passing null as value. Keep in mind that you have to use the same path and domain
 *       used when the cookie was set.
 *
 * @param String name The name of the cookie.
 * @param String value The value of the cookie.
 * @param Object options An object literal containing key/value pairs to provide optional cookie attributes.
 * @option Number|Date expires Either an integer specifying the expiration date from now on in days or a Date object.
 *                             If a negative value is specified (e.g. a date in the past), the cookie will be deleted.
 *                             If set to null or omitted, the cookie will be a session cookie and will not be retained
 *                             when the the browser exits.
 * @option String path The value of the path atribute of the cookie (default: path of page that created the cookie).
 * @option String domain The value of the domain attribute of the cookie (default: domain of page that created the cookie).
 * @option Boolean secure If true, the secure attribute of the cookie will be set and the cookie transmission will
 *                        require a secure protocol (like HTTPS).
 * @type undefined
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */

/**
 * Get the value of a cookie with the given name.
 *
 * @example $.cookie('the_cookie');
 * @desc Get the value of a cookie.
 *
 * @param String name The name of the cookie.
 * @return The value of the cookie.
 * @type String
 *
 * @name $.cookie
 * @cat Plugins/Cookie
 * @author Klaus Hartl/klaus.hartl@stilbuero.de
 */
jQuery.cookie = function(name, value, options) {
    if (typeof value != 'undefined') { // name and value given, set cookie
        options = options || {};
        if (value === null) {
            value = '';
            options.expires = -1;
        }
        var expires = '';
        if (options.expires && (typeof options.expires == 'number' || options.expires.toUTCString)) {
            var date;
            if (typeof options.expires == 'number') {
                date = new Date();
                date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
            } else {
                date = options.expires;
            }
            expires = '; expires=' + date.toUTCString(); // use expires attribute, max-age is not supported by IE
        }
        // CAUTION: Needed to parenthesize options.path and options.domain
        // in the following expressions, otherwise they evaluate to undefined
        // in the packed version for some reason...
        var path = '; path=' + ( options.path ? options.path : '/' );
        var domain = options.domain ? '; domain=' + (options.domain) : '';
        var secure = options.secure ? '; secure' : '';
        document.cookie = [name, '=', encodeURIComponent(value), expires, path, domain, secure].join('');
    } else { // only name given, get cookie
        var cookieValue = null;
        if (document.cookie && document.cookie != '') {
            var cookies = document.cookie.split(';');
            for (var i = 0; i < cookies.length; i++) {
                var cookie = jQuery.trim(cookies[i]);
                // Does this cookie string begin with the name we want?
                if (cookie.substring(0, name.length + 1) == (name + '=')) {
                    cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                    break;
                }
            }
        }
        return cookieValue;
    }
}




jQuery.tableRowHighlighter = {
	
	build : function ( options ) {

		options = jQuery.extend( {
                onHoverClass: 'hovered',
                cellsSelector: 'td'
            }, options || {} );
						
		this.find( options.cellsSelector )
			.each( function() {
				this._tableRowHighlighterOptions = jQuery.extend( options, {} );
			} )
			.bind( 'mouseover', jQuery.tableRowHighlighter._tr_hover_helper )
			.bind( 'mouseout', jQuery.tableRowHighlighter._tr_unhover_helper );
		return this;
	},
	

	_tr_hover_helper : function ( ev ) {
		$( this ).parent().addClass( this._tableRowHighlighterOptions.onHoverClass );
	},

	_tr_unhover_helper : function ( ev ) {
		$( this ).parent().removeClass( this._tableRowHighlighterOptions.onHoverClass );
	}
	
}

jQuery.fn.extend(
	{
		tableRowHighlighter : jQuery.tableRowHighlighter.build
	}
);




//Absolutize
(function($){
	jQuery.fn.absolutize = function() {
		return this.each(function(){
		    
			var elem = $(this);
		    if (elem.css('position') == 'absolute')
		      return elem;
			
		    elem.css("top", elem.offset().top);
		    elem.css("left", elem.offset().left);
		    elem.css("position", "absolute");
        });
	}
})(jQuery);





/*
 * jQuery Caret Range plugin
 * Copyright (c) 2009 Matt Zabriskie
 * Released under the MIT and GPL licenses.
 *
 */

/* USAGE
 * 
var input = $("#selector");
var range = input.caret();
var text = null;

// Get selected text
text = input.val().substr(range.start, range.end - 1);

// Insert text at caret then restore caret
var value = input.val();
text = " New Text ";
input.val(value.substr(0, range.start) + text + value.substr(range.end, value.length));
input.caret(range.start + text.length);

// Select first ten characters of text
input.caret(0, 10);

*/



(function($) {
	$.extend($.fn, {
		caret: function (start, end) {
			var elem = this[0];

			if (elem) {							
				// get caret range
				if (typeof start == "undefined") {
					if (elem.selectionStart) {
						start = elem.selectionStart;
						end = elem.selectionEnd;
					}
					else if (document.selection) {
						var val = this.val();
						var range = document.selection.createRange().duplicate();
						range.moveEnd("character", val.length)
						start = (range.text == "" ? val.length : val.lastIndexOf(range.text));

						range = document.selection.createRange().duplicate();
						range.moveStart("character", -val.length);
						end = range.text.length;
					}
					//я добавил, т.к. вместо нуля шли underfined
					if (typeof start != "number") start = 0;
					if (typeof end != "number") end = 0;
				}
				// set caret range
				else {
					var val = this.val();

					if (typeof start != "number") start = -1;
					if (typeof end != "number") end = -1;
					if (start < 0) start = 0;
					if (end > val.length) end = val.length;
					if (end < start) end = start;
					if (start > end) start = end;

					elem.focus();

					if (elem.selectionStart) {
						elem.selectionStart = start;
						elem.selectionEnd = end;
					}
					else if (document.selection) {
						var range = elem.createTextRange();
						range.collapse(true);
						range.moveStart("character", start);
						range.moveEnd("character", end - start);
						range.select();
					}
				}

				return {start:start, end: end};
			}
		}
	});
})(jQuery);




jQuery.fn.maxScrollPosition = function() {
	return { left : ( this.get(0).scrollWidth - this.get(0).clientWidth ), top : ( this.get(0).scrollHeight - this.get(0).clientHeight ) };
}






/*
 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
 *
 * Uses the built in easing capabilities added In jQuery 1.1
 * to offer multiple easing options
 *
 * TERMS OF USE - jQuery Easing
 * 
 * Open source under the BSD License. 
 * 
 * Copyright В© 2008 George McGinley Smith
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
*/

// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];

jQuery.extend( jQuery.easing,
{
	def: 'easeOutCubic',
	swing: function (x, t, b, c, d) {
		//alert(jQuery.easing.default);
		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
	},
	easeInQuad: function (x, t, b, c, d) {
		return c*(t/=d)*t + b;
	},
	easeOutQuad: function (x, t, b, c, d) {
		return -c *(t/=d)*(t-2) + b;
	},
	easeInOutQuad: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t + b;
		return -c/2 * ((--t)*(t-2) - 1) + b;
	},
	easeInCubic: function (x, t, b, c, d) {
		return c*(t/=d)*t*t + b;
	},
	easeOutCubic: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t + 1) + b;
	},
	easeInOutCubic: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t + b;
		return c/2*((t-=2)*t*t + 2) + b;
	},
	easeInQuart: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t + b;
	},
	easeOutQuart: function (x, t, b, c, d) {
		return -c * ((t=t/d-1)*t*t*t - 1) + b;
	},
	easeInOutQuart: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
		return -c/2 * ((t-=2)*t*t*t - 2) + b;
	},
	easeInQuint: function (x, t, b, c, d) {
		return c*(t/=d)*t*t*t*t + b;
	},
	easeOutQuint: function (x, t, b, c, d) {
		return c*((t=t/d-1)*t*t*t*t + 1) + b;
	},
	easeInOutQuint: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
		return c/2*((t-=2)*t*t*t*t + 2) + b;
	},
	easeInSine: function (x, t, b, c, d) {
		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
	},
	easeOutSine: function (x, t, b, c, d) {
		return c * Math.sin(t/d * (Math.PI/2)) + b;
	},
	easeInOutSine: function (x, t, b, c, d) {
		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
	},
	easeInExpo: function (x, t, b, c, d) {
		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
	},
	easeOutExpo: function (x, t, b, c, d) {
		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
	},
	easeInOutExpo: function (x, t, b, c, d) {
		if (t==0) return b;
		if (t==d) return b+c;
		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
	},
	easeInCirc: function (x, t, b, c, d) {
		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
	},
	easeOutCirc: function (x, t, b, c, d) {
		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
	},
	easeInOutCirc: function (x, t, b, c, d) {
		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
	},
	easeInElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
	},
	easeOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
	},
	easeInOutElastic: function (x, t, b, c, d) {
		var s=1.70158;var p=0;var a=c;
		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
		if (a < Math.abs(c)) { a=c; var s=p/4; }
		else var s = p/(2*Math.PI) * Math.asin (c/a);
		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
	},
	easeInBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*(t/=d)*t*((s+1)*t - s) + b;
	},
	easeOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158;
		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
	},
	easeInOutBack: function (x, t, b, c, d, s) {
		if (s == undefined) s = 1.70158; 
		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
	},
	easeInBounce: function (x, t, b, c, d) {
		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
	},
	easeOutBounce: function (x, t, b, c, d) {
		if ((t/=d) < (1/2.75)) {
			return c*(7.5625*t*t) + b;
		} else if (t < (2/2.75)) {
			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
		} else if (t < (2.5/2.75)) {
			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
		} else {
			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
		}
	},
	easeInOutBounce: function (x, t, b, c, d) {
		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
	}
});

/*
 *
 * TERMS OF USE - EASING EQUATIONS
 * 
 * Open source under the BSD License. 
 * 
 * Copyright В© 2001 Robert Penner
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 * 
 * Redistributions of source code must retain the above copyright notice, this list of 
 * conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, this list 
 * of conditions and the following disclaimer in the documentation and/or other materials 
 * provided with the distribution.
 * 
 * Neither the name of the author nor the names of contributors may be used to endorse 
 * or promote products derived from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE. 
 *
 */



/*
 * чтобы свершилось ниже, надо:
 * 
 *	type: "POST"
 *	processData: false
 *	contentType : "multipart/form-data"
 *
 */

jQuery.extend( jQuery.ajaxSettings, {
	beforeSend : function ( xhr ) {
		if ( ( this.type.toUpperCase() == 'POST' ) && ( this.contentType == 'multipart/form-data' ) && !this.processData ) {
			if ( !jQuery.ajaxSettings.uniqueCounter )
				jQuery.ajaxSettings.uniqueCounter = 1;
			jQuery.ajaxSettings.uniqueCounter++;
			var
				boundaryString = "qQ" + jQuery.ajaxSettings.uniqueCounter + Math.round( Math.random() * 100 ) +"zZ",
				boundary = '--'+ boundaryString,
				s = [];
			if ( typeof this.data === "string" && !!this.data.length ) {
				s.push( 
					'Content-Disposition: form-data; name="query"'+'\n'+
					'Content-Type: text/plain'+'\n'+
					'\n'+
					encodeURIComponent( this.data )
				); 
			}
			if ( $.isArray( this.data ) && !!this.data.length ) {
				function add( key, value ){
					// key'%5B %5D'
					s[ s.length ] =
						'Content-Disposition: form-data; name="'+ encodeURIComponent( key ).replace( /%5B/gi, '[' ).replace( /%5D/gi, ']' ) +'"'+'\n'+
						'Content-Type: text/plain'+'\n'+
						'\n'+
						encodeURIComponent( value );
				};
				$.each( this.data, function(){
					add( this.name, this.value );
				});
			}
			if ( typeof this.data === "object" && !$.isArray( this.data ) ) {
				for ( var x in this.data )
					if ( !$.isFunction( this.data[ x ] ) ) {
						if ( typeof this.data[ x ] === 'boolean' )
							this.data[ x ] = this.data[ x ] ? 1 : 0;
						s.push( 
							'Content-Disposition: form-data; name="'+ x +'"'+'\n'+
							'Content-Type: text/plain'+'\n'+
							'\n'+
							( this.data[ x ] === undefined ? 'undefined' : encodeURIComponent( this.data[ x ] + '' ) )
						);
					}
			}
			this.data = boundary +'\n'+ s.join ( '\n'+ boundary +'\n' ) +'\n'+ boundary +'\n';
			xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=\""+ boundaryString +"\"" );
		}	
	}
} );




/*
 * jQuery autoResize (textarea auto-resizer)
 * @Уже давно как переписал
 */

(function( $ ){
    
    $.fn.autoResize = function(options) {
        
        // Just some abstracted details,
        // to make plugin users happy:
        var settings = $.extend({
            onResize : function(){},
            animate : true,
            animateDuration : 150,
            animateCallback : function(){},
            limit: 1000,
            extraSpace : 10
        }, options );
        
        // Only textarea's auto-resize:
        this.filter('textarea').each(function(){
            
                // Get rid of scrollbars and disable WebKit resizing:
            var textarea = $( this ).css( { resize: 'none', 'overflow':'hidden' } ),
                // Cache original height, for use later:
                origHeight = parseInt( textarea.css( 'height' ) ),
                
                // Need clone of textarea, hidden off screen:
                clone = (function(){
                    
                    // Properties which may effect space taken up by chracters:
                    var propOb = {};
                        
                    // Create object of styles to apply:
                    $.each( [
							'lineHeight','textDecoration','letterSpacing', 'fontWeight', 'fontSize', 'fontFamily',
							'paddingLeft', 'paddingTop', 'paddingBottom', 'paddingRight'
					], function( i, prop ){
                        propOb[ prop ] = textarea.css( prop );
                    });
                    $.extend( propOb, {
                    	width : textarea.outerWidth() + 'px',
                        position: 'absolute',
                        top: '0px',
                        left: '-9999px'
                    } );
                    // Clone the actual textarea removing unique properties
                    // and insert before original textarea:
                    return $( '<div>' + textarea.attr( 'value' ) + '</div>' ).css( propOb ).insertBefore( textarea );
                })(),
                updateSize = function() {
            		textarea.get( 0 ).scrollTop = 0;
            		
                    // Prepare the clone:
                    clone.html( $.escapeHTML( this.value ).replace( /\n{1}/gi, '<br/>' ) + '<br/><br/><br/>' );
                    textarea.stop();
                    var newHeight = clone.innerHeight(), curHeight = parseInt( textarea.css( 'height' ) );
                    // + settings.extraSpace
                    
                    if ( newHeight >= settings.limit ) {
                    	textarea.css( 'overflow', '' );
                    	newHeight = settings.limit;
                    } else {
                    	textarea.css( 'overflow', 'hidden' );
                    	if ( newHeight <= origHeight )
                        	newHeight = origHeight;
                    }
                    
                    if ( newHeight >= curHeight - 2 && newHeight <= curHeight + 2 )
                    	return true;
                    
                    // Fire off callback:
                    if ( settings.onResize )
                    	settings.onResize.call( this );
                    
                    if ( settings.animate && textarea.css( 'display' ) === 'block' && !$.browser.msie ) {
//                    	var carretStore = textarea.caret();
                    	var saveThis = this;
                    	textarea.animate( { 'height': newHeight + 'px' }, 150, 'linear', function () {
//                    		textarea.caret( carretStore.start, carretStore.end );
                    		settings.animateCallback.call( saveThis );
                    	} );
                    } else {
                    	textarea.css( { 'height' : newHeight + 'px' } );
                    	settings.animateCallback.call( this );
                    }
                    return true;
                };
                // я добавил, чтобы сами ресайзились на загрузке
                updateSize.call( this );
            
            // Bind namespaced handlers to appropriate events:
            textarea
                .unbind('.dynSiz')
                .bind('keyup.dynSiz', updateSize)
                .bind('focus.dynSiz', updateSize)
//                .bind('keydown.dynSiz', updateSize)
                .bind('change.dynSiz', updateSize);
            
        });
        
        // Chain:
        return this;
        
    };
    
    
    
})(jQuery);
