﻿/// <reference path="jquery-1.4.2.js" />
String.prototype.shift = function(index) {
	return index < 0 ? this.substr(this.length + index) : this.substr(index);
};

String.prototype.ucfirst = function() {
	return this.charAt(0).toUpperCase() + this.substring(1, this.length);
};

Date.prototype.toEtag = function() {
	return this.getUTCFullYear()
		+ ('0' + (this.getUTCMonth() + 1)).shift(-2)
		+ ('0' + this.getUTCDate()).shift(-2)
		+ ('0' + this.getUTCHours()).shift(-2)
		+ ('0' + this.getUTCMinutes()).shift(-2)
		+ ('0' + this.getUTCSeconds()).shift(-2)
		+ ('00' + this.getUTCMilliseconds()).shift(-3);
};

String.prototype.isNumeric = Number.prototype.isNumeric = function() {
	return this - 0 == this && this.toString().length > 0;
};

String.prototype.pad = function(len, chr) {
	if (len <= this.length)
		return this;

	var ret = this;
	chr = chr || '0';

	for (var i = this.length; i < len; i++)
		ret = chr + ret;

	return ret;
};

Number.prototype.pad = function(len) {
	return this.toString().pad(len, '0');
};

Date.prototype.addMinutes = function(minutes) {
	return new Date(new Date(this).setMinutes(this.getMinutes() + minutes));
};

Date.prototype.getLiteralMonth = (function() {
	var dict = {
		en: ['january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'novermber', 'december'],
		ru: ['января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря']
	};
	return function(lang) {
		dict[lang] == 'undefined' && (lang = 'en');
		return dict[lang][this.getMonth()];
	};
})();

window.location.isSameDomain = function(uri) {
	return !/^http/.test(uri)
		|| new RegExp('^' + this.protocol + '//' + this.hostname + '/').test(uri);
};

jQuery.extend(jQuery.easing,
{
	easeOutExpo: function(x, t, b, c, d) {
		return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
	}
});

$(function() { // ie6 png fix

	if ($.browser.msie && $.browser.version < 7) {
		var pix = '/i/pix.gif';

		$.fn.fixpng = function() {
			return this.each(function() {
				var image = this.src ? this.src : this.style.backgroundImage.substring(4, this.style.backgroundImage.length - 1);
				this.style.backgroundImage = 'none';
				this.src = pix;
				this.runtimeStyle.filter = "progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + image + "',sizingMethod='scale')";
			});
		};

		$('img.png').fixpng().each(function() {
			$(this).bind('load readystatechange', function(e) {
				if (this.complete || (this.readyState == 'complete' && e.type == 'readystatechange')) {
					if (!this.src.match(/\.png$/i))
						return;

					$(this).fixpng();
				}
			});
		});

		// ie6 flickering fix
		document.execCommand('BackgroundImageCache', false, true);
	}

});

$.browser.msie && $.browser.version == 6 && ($.support.opacity = true);

(function($) {
	var cache = {};

	// old regexp
	// /["']\\\/Date\((\d+)([\+\-]\d+)?\)\\\/["']/gi; 
	// /"\\\/(Date\(.*?\))\\\/"/gi;
	var dateRe = /\/Date\((\d+)([\+\-]\d+)?\)\//gi;

	var datesToDateObjects = function(obj) {
		for (p in obj) {
			if (typeof obj[p] === "string" && obj[p].search(dateRe) != -1)
				obj[p] = new Date(+obj[p].replace(dateRe, "$1"));
			if (typeof obj[p] === "object")
				obj[p] = datesToDateObjects(obj[p]);
		}

		return obj;
	}

	var base$parseJSON = $.parseJSON;
	var init = function() {
		var fn, context;

		for (var i = 0; i < arguments.length; i++)
			$.isFunction(arguments[i])
				? fn = arguments[i]
				: context = arguments[i]
				;

		if (!fn)
			throw 'Initialize function not found.';

		if (arguments.length != 1 && !context)
			throw 'Context is undefined.';

		return fn.call(context = context || {}) || context;
	};

	$.extend({
		init: init,
		sequencial: function(arr, fn) {
			var l = arr.length, seq = 0, callback = function() {
				seq != l ? fn.call(arr[seq++], callback) : null;
			};

			if (typeof l === 'number')
				callback();
		},
		cache: function(id, obj) {
			return cache[id] || (cache[id] = obj);
		},
		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 = options.path ? '; 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('');

				return value;
			} else {
				// only name given, get cookie
				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 + '=')) {
							value = decodeURIComponent(cookie.substring(name.length + 1));
							break;
						}
					}
				}
				return value;
			}
		},
		storage: {
			get: function(name) {
				var data = $.cookie(name);

				return data ? JSON.parse(data) : null;
			},
			set: function(name, data, options) {
				return $.cookie(name, JSON.stringify(data), $.extend({ expires: 90, path: '/' }, options)) && data;
			},
			remove: function(name) {
				return $.cookie(name, null);
			}
		},
		parseJSON: function(data) {
			return datesToDateObjects(base$parseJSON(data));
		},
		enableTextareaMaxlength: function(live) {
			this.__textareaMaxLengthEnabled = true;

			var handler = function() {
				var max = parseInt($(this).attr('maxlength'));
				if ($(this).val().length > max)
					$(this).val($(this).val().substr(0, $(this).attr('maxlength')));

				$(this).parent().find('.chars-remaining').html(max - $(this).val().length);
			}

			live ? $('textarea[maxlength]').live('keyup', handler) : $('textarea[maxlength]').keyup(handler);
			$('textarea[maxlength]').keyup();
		},
		window: (function() {
			var 
			self = this,
			onFocus = [],
			onBlur = [],

			handle = function(callbacks, focused) {
				return function(fn) {
					if ($.isFunction(fn))
						return callbacks.push(fn);

					if (focused === self.focused)
						return;

					for (var i = 0, length = callbacks.length; i < length; i++)
						callbacks[i]();

					self.focused = focused;

					$.notify('window-focus-state-changed', focused);
				};
			}

			;

			self.focus = handle(onFocus, true);
			self.blur = handle(onBlur, false);

			self.focused = true;

			if ($.browser.msie) {
				document.onfocusin = self.focus;
				document.onfocusout = self.blur;
			} else {
				window.onfocus = self.focus;
				window.onblur = self.blur;
			}

			return self;
		}).apply({})
	});

	// hotkeys
	$.extend({
		hotkeys: init(function() {
			var 
			_binds = {},
			_initialized = false,

			processHandlers = function(e) {
				for (var keyCode in _binds) {
					if (keyCode != e.keyCode) continue;

					for (var group in _binds[keyCode]) {
						for (var idx in _binds[keyCode][group]) {
							_binds[keyCode][group][idx].call();
						}
					}
				}
			}

			;

			this.keys = {
				tab: 9,
				enter: 13,
				escape: 27,
				left: 37,
				up: 38,
				right: 39,
				down: 40
			};

			this.init = function() {
				$(document).keyup(function(evt) {
					processHandlers(evt);
				});
			};

			this.listen = function(keyCode, handler, group) {
				if (!$.isFunction(handler))
					return;

				if (!_initialized)
					this.init();

				group = group ? group : '__anonymous';

				_binds[keyCode] = _binds[keyCode] ? _binds[keyCode] : {};
				_binds[keyCode][group] = _binds[keyCode][group] ? _binds[keyCode][group] : [];
				_binds[keyCode][group].push(handler);
			};

			this.clear = function(keyCode, group) {
				group = group ? group : '__anonymous';

				if (!_binds[keyCode] || _binds[keyCode][group])
					return;

				delete _binds[keyCode][group];
			};

			this.clearGroup = function(group) {
				for (var keyCode in _binds) {
					for (var g in _binds[keyCode]) {
						g == group && delete _binds[keyCode][g];
					}
				}
			};

			this.clearKey = function(keyCode) {
				_binds[keyCode] && delete _binds[keyCode];
			};
		})
	});


	// factory
	$.extend({
		factory: {
			create: function() {
				var name, fn, coll;

				for (var i = 0; i < arguments.length; i++) {
					switch (typeof arguments[i]) {
						case 'string': name = arguments[i]; break;
						case 'function': fn = arguments[i]; break;
						case 'object': coll = arguments[i]; break;
					}
				}

				if (name && fn)
					this._create(name, fn)
				else if (name || fn)
					throw { message: 'Name and Function expected.' };

				if (coll) {
					for (var name in coll) {
						if ($.isFunction(coll[name]))
							this._create(name, coll[name]);
						else throw { message: 'Argument ' + name + ' is not function.' };
					}
				}

				if (!(name && fn) && !coll)
					throw { message: 'Invalid arguments.' };
			},

			_create: function(name, fn) {
				this[name] = {
					getx: function() {
						var args = $.makeArray(arguments);
						var self = args.shift();

						if (self.__factoryinitialized == fn)
							throw { message: 'Factory ' + name + 'already initialized.' };

						return fn.apply(self, args);
					},

					get: function() {
						var args = $.makeArray(arguments);
						args.unshift({});

						return this.getx.apply(this, args);
					}
				};

				return this[name];
			}
		}
	});

	// global events

	$.extend({
		// TODO: Namespaces for events like in jQuery
		notify: (function() {
			var 
			handlers = [],
			bind = function() {
				var es = this.events;

				for (var e in es) {
					(handlers[e] || (handlers[e] = [])).push(es[e]);
				}

				return this;
			},
			unbind = function() {
				var h, es = this.events;

				for (var e in es)
					if (h = handlers[e])
					for (var i = 0, l = h.length; i < l; i++)
					if (h[i] === es[e]) {
					h.splice(i--, 1) && l--;
				}

				return this;
			}
			;

			return function(events, fn) {
				if ($.isPlainObject(events) || (typeof events === 'string' && $.isFunction(fn))) {
					var es;

					if (typeof events !== 'string') {
						es = events;
					} else {
						es = {};
						events = events.split(' ');

						for (var i = 0, l = events.length; i < l; i++)
							events[i] && (es[events[i]] = fn);
					}

					return $.extend({
						events: es
					}, {
						bind: bind,
						unbind: unbind
					}).bind();
				}

				events = events.split(' ');

				for (var i = 0, il = events.length; i < il; i++)
					if (events[i] && handlers[events[i]])
						for (var j = 0, jl = handlers[events[i]].length; j < jl; j++)
							try {
								handlers[events[i]][j].apply(null, $.isArray(fn) ? fn : [fn]);
							}
							catch (e) {
								$.browser.safari && console.error('An error occured on firing ' + events[i] + ' event. ' + e);
							}
			}
		})()
	});

	// -------------

	var $text = $.fn.text;

	$.fn.extend({
		bindo: function(data, clone) {
			var self = typeof clone === 'boolean' && !clone
				? this : this.clone().removeClass('template');

			return self.find('*[bind]').add(self.filter('[bind]')).each(function() {
				var fn;
				try {
					eval('fn = function(data) { ' + $(this).attr('bind') + ';};');
					fn.call($(this), data);
				} catch (error) {
					throw fn;
				}
			}).end().end();
		},
		store: function(prop, value) {
			return this.data('stored:' + prop, value);
		},
		restore: function(prop) {
			return this.data('stored:' + prop);
		},
		sequencial: function(fn) {
			$.sequential($.makeArray(this), fn);
		},
		text: function(text) {
			return typeof text !== "object" && text != null && $.browser.msie && $.browser.version < 8
			? $.each(this, function() { this.innerText = text; }) && this : $text.call(this, text);
		},
		scan: function(selector) {
			return this.filter(selector).add(this.find(selector));
		},
		reset: function() {
			return this.filter('form').each(function() {
				this.reset();
			}).find('textarea').each(function() {
				$.__textareaMaxLengthEnabled && $(this).keyup();
			});
		},
		focusEnd: function() {
			return this.each(function() {
				this.focus();

				var len = this.value.length * 2; // double length for opera

				this.setSelectionRange
					? this.setSelectionRange(len, len)
					: this.value = this.value
					;

				this.scrollTop = 999999; // fx and chrome
			});
		}
	});

	$.extend($.expr[':'], {
		'visible': function(e) {
			return (e.offsetWidth > 0 || e.offsetHeight > 0) && e.style.visibility != 'hidden';
		} /*,
		'in-view': function(e) {
		}*/
	});

	if ($.browser.msie && $.browser.version == 6) {
		jQuery.support.opacity = true;
	}
})(jQuery);
