(function( window, undefined ) {
	var Just = (function() {
		Just = function (selector, context) { return new Just.prototype.init( selector, context ); };
		Just.version = '0.1';
		Just.prototype = {
			init: function( selector, context ) { return Just.$ = jQuery.extend(jQuery(selector, context), Just); }
		};
		return Just;
	});
	window.Just = Just();
})(window);

/*** WATCH OBJECT --- Just.watch --- *****************************************/
/*
	if (!Object.prototype.watch) {
		Object.prototype.watch = function (prop, handler) {
			var oldval = this[prop], newval = oldval,
			getter = function () {
				return newval;
			},
			setter = function (val) {
				oldval = newval;
				return newval = handler.call(this, prop, oldval, val);
			};
			if (delete this[prop]) { // can't watch constants
				if (Object.defineProperty) { // ECMAScript 5
					Object.defineProperty(this, prop, {
						get: getter,
						set: setter,
						enumerable: false,
						configurable: true
					});
				} else if (Object.prototype.__defineGetter__ && Object.prototype.__defineSetter__) { // legacy
					Object.prototype.__defineGetter__.call(this, prop, getter);
					Object.prototype.__defineSetter__.call(this, prop, setter);
				}
			}
		};
	}

	// object.unwatch
	if (!Object.prototype.unwatch) {
		Object.prototype.unwatch = function (prop) {
			var val = this[prop];
			delete this[prop]; // remove accessors
			this[prop] = val;
		};
	}
*/

(function() {

/********************************** JS MODULES *******************************/

	$.fn.serializeJSON = function() {
		var json = {};
		jQuery.map($(this).serializeArray(), function(n, i) { json[n['name']] = n['value']; });
		return json;
	};

/*** String Format --- "".format() --- ***************************************/
	String.prototype.format = function() {
		var formatted = this;
		var regexp;
		for (var i=0,len=arguments.length;i<len;i++) {
			function _(item, value) { regexp = new RegExp('\\{'+item+'\\}', 'gi'); formatted = formatted.replace(regexp, value); }
			if (typeof (arguments[i]) == 'object') { for (var item in arguments[i]) { _(item, arguments[i][item]); } } else { _(i, arguments[i]); }
		}
		return formatted;
	};

	Just.size = function(obj) {var size=0,key;for(key in obj){if(obj.hasOwnProperty(key))size++;}return size;};

/*** --- CyrLat Converter --- ***************************************/

	Just.translate = function(source) {
		var _lat = [
					"a", "b", "v", "g", "d", "e", "e", "zh", "z", "i", "j", "k", "l", "m", "n",
		"o", "p", "r", "s", "t", "u", "f", "h", "c", "ch", "sh", "sh", "", "i", "", "e", "ju", "ja",
		"A", "B", "V", "G", "D", "E", "E", "Zh", "Z", "I", "J", "K", "L", "M", "N",
		"O", "P", "R", "S", "T", "U", "F", "H", "C", "Ch", "S", "Sh", "", "I", "", "E", "Ju", "Ja",
		'i', 'ie', 'YI', 'YE'];
		var _cyr = [
					"а", "б", "в", "г", "д", "е", "ё", "ж", "з", "и", "й", "к", "л", "м", "н",
		"о", "п", "р", "с", "т", "у", "ф", "х", "ц", "ч",  "ш", "щ", "ь", "ы", "ъ", "э", "ю", "я",
		"А", "Б", "В", "Г", "Д", "Е", "Ё", "Ж",  "З", "И", "Й", "К", "Л", "М", "Н",
		"О", "П", "Р", "С", "Т", "У", "Ф", "Х", "Ц", "Ч", "Ш", "Щ", "Ь", "Ы", "Ъ", "Э", "Ю", "Я",
		'ї', 'є', 'Ї', 'Є'];
		var assocArr = {}, sourceLen = source.length, output='';
		return {
			cyr2lat: function() {
				var count = _cyr.length;
				for (var s=0; s < count; s++){ assocArr[_cyr[s]] = _lat[s] }
				for (var i=0; i < sourceLen; i++) {
					everyChar = source.charAt(i);
					if (assocArr[everyChar] != undefined){ output += assocArr[everyChar] } else { output += everyChar }
				}
				return output
			},
			lat2cyr: function() {
				var count = _lat.length;
				for (var s=0; s < count; s++){ assocArr[_lat[s]] = _cyr[s] }
				for (var i=0; i < sourceLen; i++) {
					everyChar = source.charAt(i);
					if (assocArr[everyChar] != undefined){ output += assocArr[everyChar] } else { output += everyChar }
				}
				return output
			},
			urlize: function(whitespace) {
				if (whitespace == undefined) whitespace = '-';
				return this.cyr2lat().replace(/[\s.]/img, whitespace).replace(/[^a-zA-Z0-9-_+]/img, '');
			}
		};
	};

/*
 * Date Format 1.2.3
 * (c) 2007-2009 Steven Levithan <stevenlevithan.com>
 * MIT license
 *
 * Includes enhancements by Scott Trenda <scott.trenda.net>
 * and Kris Kowal <cixar.com/~kris.kowal/>
 *
 * Accepts a date, a mask, or a date and a mask.
 * Returns a formatted version of the given date.
 * The date defaults to the current date/time.
 * The mask defaults to dateFormat.masks.default.
 */

	Just.dateFormat = function () {
		var	token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g,
			timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g,
			timezoneClip = /[^-+\dA-Z]/g,
			pad = function (val, len) {
				val = String(val);
				len = len || 2;
				while (val.length < len) val = "0" + val;
				return val;
			};

		// Regexes and supporting functions are cached through closure
		return function (date, mask, utc) {
			var dF = Just.dateFormat;
			dF.masks = {
				"default":      "ddd mmm dd yyyy HH:MM:ss",
				shortDate:      "m/d/yy",
				mediumDate:     "mmm d, yyyy",
				longDate:       "mmmm d, yyyy",
				fullDate:       "dddd, mmmm d, yyyy",
				shortTime:      "h:MM TT",
				mediumTime:     "h:MM:ss TT",
				longTime:       "h:MM:ss TT Z",
				isoDate:        "yyyy-mm-dd",
				isoTime:        "HH:MM:ss",
				isoDateTime:    "yyyy-mm-dd'T'HH:MM:ss",
				isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'"
			}
			dF.i18n = {
				dayNames: [
					"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat",
					"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
				],
				monthNames: [
					"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
					"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
				]
			}
			// You can't provide utc if you skip other args (use the "UTC:" mask prefix)
			if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) {
				mask = date;
				date = undefined;
			}
			// Passing date through Date applies Date.parse, if necessary
			date = date ? new Date(date) : new Date;
			if (isNaN(date)) throw SyntaxError("invalid date");
			mask = String(dF.masks[mask] || mask || dF.masks["default"]);
			// Allow setting the utc argument via the mask
			if (mask.slice(0, 4) == "UTC:") {
				mask = mask.slice(4);
				utc = true;
			}
			var	_ = utc ? "getUTC" : "get",
				d = date[_ + "Date"](), D = date[_ + "Day"](), m = date[_ + "Month"](), y = date[_ + "FullYear"](), H = date[_ + "Hours"](), M = date[_ + "Minutes"](), s = date[_ + "Seconds"](), L = date[_ + "Milliseconds"](),
				o = utc ? 0 : date.getTimezoneOffset(),
				flags = {
					d:	d,
					dd:		pad(d),
					ddd:	dF.i18n.dayNames[D],
					dddd:	dF.i18n.dayNames[D + 7],
					m:		m + 1,
					mm: 	pad(m + 1),
					mmm:	dF.i18n.monthNames[m],
					mmmm:	dF.i18n.monthNames[m + 12],
					yy:		String(y).slice(2),
					yyyy:	y,
					h:		H % 12 || 12,
					hh:		pad(H % 12 || 12),
					H:		H,
					HH:		pad(H),
					M:		M,
					MM:		pad(M),
					s:		s,
					ss:		pad(s),
					l:		pad(L, 3),
					L:		pad(L > 99 ? Math.round(L / 10) : L),
					t:		H < 12 ? "a" : "p",
					tt:		H < 12 ? "am" : "pm",
					T:		H < 12 ? "A" : "P",
					TT:		H < 12 ? "AM" : "PM",
					Z:		utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""),
					o:		(o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4),
					S:		["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10]
				};
			return mask.replace(token, function ($0) {
				return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1);
			});
		};
	}();

	Date.prototype.format = function (mask, utc) {
		return Just.dateFormat(this, mask, utc);
	};
/*** --- MCU Checker --- ***************************************/
	$.fn.mcu_checker = function(options) {
		var defaults = {
			url: undefined,
			name: '',
			whitespace: '-',
			minlength: -1,
			wait: 750,
			captureEnter: true,
			dataType: 'json',
			type: 'POST',
			watch: undefined,
			translate: true,
			toCase: 'lower',
			callback: function(data) {}
		};

		return this.each(function() {
			var o = jQuery.extend(defaults, options);
			var self = this;

			if (typeof(o.watch) != 'undefined') o.watch = jQuery(o.watch);

			var callback = function() {
				if (this.el.id != jQuery(self).attr('id')) {
					self.value = o.watch.val();
				}

				if (o.translate) {
					var $val = Just.translate(self.value).urlize(o.whitespace);
					if (o.toCase == 'lower') {
						$val = $val.toLowerCase();
					} else if (o.toCase == 'upper') {
						$val = $val.toUpperCase();
					}
					if ($val != self.value) { self.value = $val; }
				}
				var data = {};
				data.field = (o.name == '') ? jQuery(self).attr('name').toString() : o.name;
				data.value = self.value;
				if (o.url != undefined) {
					jQuery.ajax({
						type: o.type,
						dataType: o.dataType,
						data: data,
						url: o.url,
						success: o.callback,
						async: false
					});
				} else {
					o.callback(self);
				}
			};

			if (typeof(o.watch) != 'undefined') {
				jQuery(o.watch).typeWatch({
					wait : o.wait,
					callback : callback,
					highlight : true,
					captureEnter : o.captureEnter,
					minTextLength : o.minlength
				});
			}
			
			jQuery(this).typeWatch({
				wait : o.wait,
				callback : callback,
				highlight : true,
				captureEnter : o.captureEnter,
				minTextLength : o.minlength
			});
		});
	};


/*** QUICK BUTTONS --- Just.quickButtons --- *********************************/

	Just.quickButtons = function (buttons, options) {
		var defaults = {
			defbtn: {
				title: '',
				alt: '',
				link: '#',
				icon: '',
				item: 'item',
				hovered: 'item-hover',
				active: 'item-active'
			},
			cls : 'quick-buttons'
		};

		var defined = {
			articles: {
				title: 'Статьи',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/articles'
			},
			articles_add: {
				title: 'Добавить статью',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: '/admin/articles/add'
			},
			articles_edit: {
				title: 'Редактировать статью',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: '/admin/articles/edit'
			},
			pages: {
				title: 'Страницы',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/pages'
			},
			pages_add: {
				title: 'Добавить страницу',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: '/admin/pages/add'
			},

			events: {
				title: 'События',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/events'
			},
			events_schedule: {
				title: 'Расписание',
				icon: '/media/images/icons/admin/16/document-table.png',
				link: '/admin/events/schedule'
			},
			events_add: {
				title: 'Добавить событие',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: '/admin/events/add/',
				alt: 'Добавить событие'
			},

			places: {
				title: 'Места',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/places'
			},
			places_add: {
				title: 'Добавить место',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: '/admin/places/add/',
				alt: 'Добавить место'
			},
			action_add: {
				title: 'Добавить',
				icon: '/media/images/icons/admin/16/document-plus.png',
				link: ''
			},
			action_edit: {
				title: '',
				icon: '/media/images/icons/admin/16/document-pencil.png',
				alt: 'Редактировать'
			},
			action_schedule: {
				title: '',
				icon: '/media/images/icons/admin/16/document-table.png',
				alt: 'Расписание'
			},
			action_gallery: {
				title: '',
				icon: '/media/images/icons/admin/16/image-plus.png',
				alt: 'Галерея'
			},
			action_del:{
				title: '',
				icon: '/media/images/icons/admin/16/bin.png',
				alt: 'Удалить'
			},
			action_restore:{
				title: '',
				icon: '/media/images/icons/admin/16/arrow-090.png',
				alt: 'Восстановить'
			},
			system_routes: {
				title: 'Разделы',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/system'
			},
			system_consts: {
				title: 'Константы',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/system/const'
			},
			system_modules: {
				title: 'Модули',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/system/modules'
			},
			system_users: {
				title: 'Администраторы',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/system/users'
			},
			users: {
				title: 'Пользователи',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/users'
			},
			comments: {
				title: 'Комментарии',
				icon: '/media/images/icons/admin/16/document-export.png',
				link: '/admin/comments'
			}
		};

		options = jQuery.extend(defaults, options);

		var $btns = {};

		this.each(function() {
			var o = options;

			var acts = {
				link: function(str, append) {
					if (str == undefined) {
						return this.find('a').attr('href');
					} else {
						if (append && append != undefined) {
							str = this.find('a').attr('href') + str.toString();
						}

						return this.find('a').attr('href', str);
					}
				},
				title: function(str) {
					if (str == undefined) {
						return this.find('span').text();
					} else {
						return this.find('span').text(str.toString());

					}
				},
				icon: function(str) {
					if (str == undefined) {
						return this.find('img').attr('src');
					} else {
						return this.find('img').attr('src', $config.root+str.toString());
					}
				},
				alt: function(str) {
					if (str == undefined) {
						return this.find('img').attr('alt');
					} else {
						this.find('a').attr('title', str.toString());
						return this.find('img').attr('alt', str.toString());
					}
				}
			};

			var obj = jQuery(this);

			var make = function() {
				obj.addClass(o.cls);
				for (var bt in buttons) {
					var btn;

					if (typeof(defined[bt]) == 'object') {
						if (buttons[bt].link == undefined) buttons[bt].link = $config.root + defined[bt].link;

						buttons[bt] = jQuery.extend(Just.clone(defined[bt]), buttons[bt]);
					}

					btn = jQuery.extend(o.defbtn, buttons[bt], {name: bt});

					$btns[bt] = createButton(btn);
					obj.append($btns[bt]);
				}
			};

			var createButton = function(btn) {
				var callback = function() {};

				if (typeof(btn.link) == 'function' ) {
					callback = Just.clone(btn.link);
					btn.link = '#';
				}

				var add = $('<div class="'+o.cls +'-'+ btn.item+' '+o.cls +'-'+ btn.item + '-' + btn.name+'">' +
						'<a href="'+btn.link+'" style="display: block; width: 100%; height: 100%;" title="'+btn.title+'"><img src="'+$config.root+btn.icon+'" alt="'+btn.alt+'"></a>' +
						'</div>');

//				if (window.location.pathname.substring(0, btn.link.length) == btn.link) add.addClass(o.cls +'-'+ o.defbtn.active);
				if (window.location.pathname == btn.link) add.addClass(o.cls +'-'+ o.defbtn.active);

				add.find('a').bind('click', callback).append((btn.title != undefined && btn.title && btn.title != '') ? '<span>'+btn.title+'</span>' : '');

				add.extend(acts);
				return add;
			};

			$btns.add = function(buttons) {
				for (var bt in buttons) {
					var btn;

					if (typeof(defined[bt]) == 'object') {
						buttons[bt] = jQuery.extend(Just.clone(defined[bt]), buttons[bt]);
					}

					btn = jQuery.extend(o.defbtn, buttons[bt], {name: bt});

					this[bt] = createButton(btn);
					this._holder.append(this[bt]);
				}
			};

			$btns.have = function(button) {
				return (this[button] != undefined);
			};

			$btns.remove = function(button) {
				if (this[button] != undefined) {
					this._holder.find('.'+o.cls +'-'+ this[button].item + '-' + this[button].name).remove();
					delete this[button];
				}
				return this;
			};

			obj.find('.'+o.cls +'-'+ o.defbtn.item).live("hover", function(){jQuery(this).toggleClass(o.cls +'-'+ o.defbtn.hovered);});
			$btns._holder = obj;
			make();
		});

		return $btns;
	};

/*** ACTIVE FORM --- Just.activeForm --- *************************************/

	Just.activeForm = function(options) {

		var defaults = {
			id: undefined,
			name_prefix: '',
			field_prefix: 'field_',
			field_data_name: 'data-form-field',
			input_class_prefix: 'input-type-',
			required: [],
			row: 'dl',
			focus_on: 'input, textarea, select',
			scroll_offset: 20,
			beforeSerialize: function(){},
			beforeSubmit: function(){},
			data: {},
			saved: {
				block: '.func_display_saved',
				delay: 2000,
				fadeout: 1000
			},
			error: {
				phrase: Just.decode('Ошибка при заполнении формы'),
				row: 'dl:first',
				hint: 'table-row-error-hint',
				block: '.error-field',
				css: 'table-row-error'
			},
			success: function() {}
		};
		options = jQuery.extend(true, defaults, options);
		var form = jQuery(this);
		var o = options;
		var redirect = '';
		var errorFields = this.errorFields = function (data) {
			if (data != undefined && data.clear != undefined && typeof(data.clear) == 'object') {
				for(field in data.clear) {
					var nrow = form.find(o.row + ' [name="'+data.clear[field]+'"]').parents(o.row + ':first');
					if (nrow && nrow != undefined) {
						nrow.removeClass(o.error.css);
						nrow.find('.'+o.error.hint).remove();
					}
				}
			} else {
				form.find(o.row).removeClass(o.error.css);
				form.find(o.error.block).text('');
				form.find('.'+o.error.hint).remove();
			}

			if (!data || data == undefined) return;

			if (data.error != undefined) {
				if (!data.error || data.error == '') data.error = o.error.phrase;
				form.find(o.error.block).text(data.error);
			}

			if (typeof(data.errors) == 'object') {
				var list = new Array();
				for(var field in data.errors) {
					var fld;
					if (form.find('['+o.field_data_name+'="'+field+'"]').length) {
						fld = form.find('['+o.field_data_name+'="'+field+'"]');
					} else {
						fld = form.find('[id="'+o.field_prefix+field+'"]');
					}
					fld.parents(o.error.row).addClass(o.error.css);
					list.push("#"+o.field_prefix+field);
					if (data.errors[field].length > 0) {
//						fld = form.find('[name="'+field+'"]');
						if (fld.parents('dd:first').length) {
							fld.parents('dd:first').append('<p class="'+o.error.hint+'">'+data.errors[field]+'</p>');
						} else {
							fld.after('<p class="'+o.error.hint+'">'+data.errors[field]+'</p>');
						}
					}
				}

				if (list.length) {
					var getted = "#" + jQuery(jQuery(list.join(',')).get(0)).attr('id');
					if ($(getted).length) {
						jQuery('html, body').animate({
							scrollTop: jQuery(getted).offset().top - o.scroll_offset
						}, 700);
						jQuery(getted).focus().val(jQuery(getted).val());
					}
				}
			}
		};


		if (o.id == undefined) o.id = 'id_'+ form.attr('name');

		jQuery.each(o.required, function(k,v){
			form.find('label[for="'+o.field_prefix+this+'"]').append(' *');
		});

		form.find('input:submit[redirect]').click(function() {
			redirect = $(this).attr('redirect');
		});

		form.find('input').each(function() {
			$(this).addClass(o.input_class_prefix+$(this).attr('type'));
		});

		form.ajaxForm({
			beforeSerialize: o.beforeSerialize,
			beforeSubmit: function(a,f,s) {
				errorFields();
				return o.beforeSubmit(a,f,o,s);
			},
			data: jQuery.extend(true, {"X_REQUESTED_WITH":"XMLHttpRequest"}, o.data),
			dataType: 'json',
			success: function(data) {
				var is_error = (data.error != undefined || Just.size(data.errors));

				if (data) {
					if (is_error) {
						errorFields(data);
					} else if (data.status == 1) {
						var $input = form.find('input[name="'+o.name_prefix+o.id+'"]');
						if (data.id && data.id != undefined && $input.val() == '') $input.val(data.id);
						form.find(o.saved.block).show().delay(o.saved.delay).fadeOut(o.saved.fadeout);
					}
				}

				var usr = o.success.call(form, data);
				if (redirect != undefined && redirect.length > 0 && !is_error && usr !== false) window.location.href = redirect;
			},
			error: function(data) {
				console.log(data);
			}
		});
		
		return form;
	};

/*** FOCUS PARENT --- Just.toggleFocusParent --- *****************************/

	Just.toggleFocusParent = function(target,parent,css) {
		var callback = function() {
			jQuery((target==undefined)?this:target).parents((parent==undefined)?'dl:first':parent).toggleClass((css==undefined)?'table-row-hovered':css);
		};
		return jQuery(this).bind('focus', callback).bind('blur', callback);
	};

/*** HTML DECODE --- Just.decode --- *****************************************/

	Just.decode = function(text) {
		var e = document.createElement('div');
		e.innerHTML = text;
		return e.childNodes[0].nodeValue;
	};

/*** CLONE OBJECT --- Just.clone --- *****************************************/

	Just.clone = function(o) {
		if (!o || 'object' !== typeof o) {
			return o;
		}
		var c = 'function' === typeof o.pop ? [] : {};
		var p, v;
		for (p in o) {
			if (o.hasOwnProperty(p)) {
				v = o[p];
				if (v && 'object' === typeof v) {
					c[p] = Just.clone(v);
				}
				else {
					c[p] = v;
				}
			}
		}
		return c;
	};

/*** Get URL parameter --- Just.getUrlParam --- ************************************/
	Just._conf = {
		url: {
			hash_param_delimiter: '=',
			hash_delimiter: '/',
			url_param_delimiter: '=',
			url_delimiter: '&'
		}
	};

	Just.getUrlParam = function ( name ) {
		name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
		var regexS = "[\\?"+Just._conf.url.url_delimiter+"]" + name + Just._conf.url.url_param_delimiter+"([^"+Just._conf.url.url_delimiter+"#]*)";
		var regex = new RegExp(regexS);
		var results = regex.exec(window.location.href);
		if (results == null)
			return false;
		else
			return decodeURIComponent(results[1].replace(/\+/g, " "));
	};

	Just.getUrlHash = function ( name ) {
		name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]");
		var regexS = "[\\#"+Just._conf.url.hash_delimiter+"]" + name + Just._conf.url.hash_param_delimiter + "([^"+Just._conf.url.hash_delimiter+"#]*)";
		var regex = new RegExp(regexS);
		var results = regex.exec(window.location.hash);
		if (results == null)
			return false;
		else
			return decodeURIComponent(results[1].replace(/\+/g, " "));
	};

	Just.setUrlHash = function ( name, value ) {
		var exists = Just.getUrlHash(name);
		if (exists === false && value !== false) {
			window.location.hash += (window.location.hash == '' ? '' : Just._conf.url.hash_delimiter ) + name + Just._conf.url.hash_param_delimiter + value;
		} else {
			var regexS = "([\\#"+Just._conf.url.hash_delimiter+"]" + name + Just._conf.url.hash_param_delimiter + ")([^"+Just._conf.url.hash_delimiter+"#]*)";
			var regex = new RegExp(regexS, "g");
			window.location.hash = window.location.hash.replace(regex, (value !== false ? '$1'+value : ''));
		}
	};

/*** Quick datepicker --- Just.quickdatepick --- ************************************/

	$.datepicker.regional['ru'] = {
		dayNames: ['Воскресенье', 'Понедельник', 'Вторник', 'Среда', 'Четверг', 'Пятница', 'Суббота'],
		dayNamesShort: ['Вск', 'Пнд', 'Втр', 'Срд', 'Чтв', 'Птн', 'Сбт'],
		dayNamesMin: ['Вс', 'Пн', 'Вт', 'Ср', 'Чт', 'Пт', 'Сб'],
		firstDay: 1,
		monthNames: ['Январь','Февраль','Март','Апрель','Май','Июнь','Июль','Август','Сентябрь','Октябрь','Ноябрь','Декабрь'],
		monthNamesShort: ['Янв','Фев','Мар','Апр','Май','Июн','Июл','Авг','Сен','Окт','Ноя','Дек'],
		showOn: "button",
		buttonImage: "/media/images/icons/admin/16/calendar.png",
		buttonImageOnly: true,
		changeMonth: true,
		changeYear: true,
		dateFormat: 'yy-mm-dd',
		timeFormat: 'hh:mm',
		stepHour: 1,
		stepMinute: 1,
		showButtonPanel: false
	};
	$.datepicker.setDefaults($.datepicker.regional['ru']);
	$.timepicker.regional['ru'] = {
		timeOnlyTitle: 'Выберите время',
		timeText: 'Время',
		hourText: 'Часы',
		minuteText: 'Минуты',
		secondText: 'Секунды',
		currentText: 'Сейчас',
		closeText: 'Закрыть',
		ampm: false
	};
	$.timepicker.setDefaults($.timepicker.regional['ru']);

	Just.quickdatepick = function(options) {
		return $.each(this, function() {
			var input = this;
			var readSelected = function(input) {
				var select = $(input).parents('dl:first').find('select');
				var $date =  select.eq(2).children(':selected').val()
					+'-'+ select.eq(1).children(':selected').val()
					+'-'+ select.eq(0).children(':selected').val();
				if (select.eq(3).length) $date += ' ' + select.eq(3).children(':selected').val() + ':' + select.eq(4).children(':selected').val();
				$(input).val($date);
				return {};
			};
			var onDateSelect = function(dateText, inst) {
				var dl, date;
				if (typeof(inst.$input) != 'undefined') {
					dl = inst.$input.parents('dl:first');
				} else {
					dl = inst.input.parents('dl:first');
				}

				date = $(this).datepicker( 'getDate' );
				dl.find('option:selected').prop('selected', false);
				var h = dl.find('select:eq(3)');
				var i = dl.find('select:eq(4)');
				dl.find('select:eq(0) option[value="'+date.getDate()+'"]').prop('selected', true);
				dl.find('select:eq(1) option[value="'+(date.getMonth()+1)+'"]').prop('selected', true);
				dl.find('select:eq(2) option[value="'+date.getFullYear()+'"]').prop('selected', true);
				if (h) h.find('option[value="'+date.getHours()+'"]').prop('selected', true);
				if (i) i.find('option[value="'+date.getMinutes()+'"]').prop('selected', true);
			};
			readSelected(input);
			$(input).parents('dl:first').find('select').bind('change', function() {readSelected(input);});
			var defaults = {
				onSelect: onDateSelect,
				beforeShow: readSelected
			};

			options = $.extend(true, defaults, options);

			if ($(input).parents('dl:first').find('select').eq(3).length) {
				$(input).datetimepicker(options);
			} else {
				$(input).datepicker(options);
			}
		});

	};


/*****************************************************************************/
/*** YandexMaps wrapper --- Just.YMap --- ************************************/


	Just.YMap = {

		/*** Yandex Maps wrapper *********************************************/
		/**
		 * @usage $.Just.YMap.init('#field_latitude', '#field_longitude');
		 */
			map: null, placemark: null, initiated: false, loaded: false,
			moveBack: function() {
				var oldPosition = new YMaps.GeoPoint(this.startPosition.longitude, this.startPosition.latitude);
				this.map.panTo(oldPosition);
				this.placemark.setGeoPoint(oldPosition);
			},
			moveToView: function() {
				var position = this.map.getCenter();
				this.placemark.setGeoPoint(position);
				$('#field_latitude').val(position.getLat());
				$('#field_longitude').val(position.getLng());
			},
			init: function(id_latitude, id_longitude, callback, options) {
				var defaults = {
//					startPosition: {
//						latitude: 43.25,
//						longitude: 76.9
//					},
					startPosition: $config.common.maps.yandex.startPosition,
					typeControl: true,
					toolbar: true,
					zoom: true,
					minimap: true,
					scaleLine: true,
					searchControl: true,
					startPlacemark: true,
					startPlacemarkDraggable: true
				};

				var o = $.extend(true,defaults, options);

				var obj = this;
				obj.initiated = true;
				if (callback == undefined) callback = function(){};
				if (id_longitude && id_latitude) {
					if ($(id_latitude).val()) {
						o.startPosition.latitude = $(id_latitude).val();
					} else {
						$(id_latitude).val(o.startPosition.latitude)
					}
					if ($(id_longitude).val()) {
						o.startPosition.longitude = $(id_longitude).val();
					} else {
						$(id_longitude).val(o.startPosition.longitude)
					}
				}

				function _init() {
					obj.loaded = true;
					if (document.getElementById(obj.map_block) == null)
						return;
					// create yandex map object and set main settings
					obj.map = new YMaps.Map(document.getElementById('map_container'));
					obj.map.setCenter(new YMaps.GeoPoint(o.startPosition.longitude, o.startPosition.latitude), o.startPosition.zoom);
					if (o.typeControl === true) obj.map.addControl(new YMaps.TypeControl());
					if (o.toolbar === true) obj.map.addControl(new YMaps.ToolBar());
					if (o.zoom === true) obj.map.addControl(new YMaps.Zoom());
					if (o.minimap === true) obj.map.addControl(new YMaps.MiniMap());
					if (o.scaleLine === true) obj.map.addControl(new YMaps.ScaleLine());
					if (o.searchControl === true) obj.map.addControl(new YMaps.SearchControl());
					if (o.startPlacemark === true) {
						obj.placemark = new YMaps.Placemark(new YMaps.GeoPoint(o.startPosition.longitude, o.startPosition.latitude), { draggable: o.startPlacemarkDraggable });
						obj.map.addOverlay(obj.placemark);
					}
					if (id_longitude && id_latitude) {
						YMaps.Events.observe(obj.placemark, obj.placemark.Events.Drag, function(marker) {
							$(id_latitude).attr('value', marker.getGeoPoint().getLat());
							$(id_longitude).attr('value', marker.getGeoPoint().getLng());
						});
						$(id_latitude+','+id_longitude).focusout(function() {
							var point = new YMaps.GeoPoint($(id_longitude).val(), $(id_latitude).val());
							obj.map.setCenter(point, 15);
							obj.placemark.setGeoPoint(point);
						});
					}
					callback();
				}

				$.ajax({
					type: "GET",
					url: obj.api_link,
					dataType: 'script',
					success: function() { if (typeof(YMaps) == 'object') YMaps.load(_init); },
					async: false
				});
			},
			set_center: function(latitude, longitude, zoom) {
				if (typeof (zoom) == 'undefined' || !zoom) zoom = 15;
				this.map.setCenter(new YMaps.GeoPoint(longitude, latitude), zoom);
			},
			set_marks: function(marks) {
				if (!this.loaded) return false;
				var m;
				for(m in marks) {
					var mark, opts = {};

					if (marks[m].content != undefined && marks[m].content.length) {
						opts.balloonOptions = {
							maxWidth: 160,
							hasCloseButton: true,
							mapAutoPan: 1
						};
					}

					mark = new YMaps.Placemark(new YMaps.GeoPoint(marks[m].longitude, marks[m].latitude), opts);

					var title;
					if (marks[m].link != undefined && marks[m].link.length ) {
						title = '<a href="'+marks[m].link+'">'+marks[m].title+'</a>';
					} else {
						title = marks[m].title;
					}
					if (marks[m].title != undefined && marks[m].title.length) {
						mark.name = title;
						mark.setIconContent(marks[m].title);
					}
					if (marks[m].content != undefined && marks[m].content.length) {
						mark.setBalloonContent('<b>'+title+'</b><br/>'+marks[m].content);
					}
					marks[m].placemark = mark;
					this.map.addOverlay(mark);
				}
				return marks;
			},
			api_link: $config.common.maps.yandex.api_link,
			map_block: 'map_container'
	};

	Just.Maps = {
		loaded: false,
		YMap: function(options) {
			var defaults = {
				oLatitude: false,
				oLongitude: false,
				startPosition: $config.common.maps.yandex.startPosition,
				typeControl: true,
				toolbar: true,
				zoom: true,
				minimap: true,
				scaleLine: true,
				searchControl: true,
				startPlacemark: true,
				startPlacemarkDraggable: true
			};
			var self = Just.$;
			function _init() {
				var o = $.extend(true, {}, defaults, options);

				var focusout = function() {
					var point = new YMaps.GeoPoint($(o.oLongitude).val(), $(o.oLatitude).val());
					self.map.setCenter(point, 15);
					self.placemark.setGeoPoint(point);
				};

				o.oLatitude = $(o.oLatitude)[0];
				o.oLongitude = $(o.oLongitude)[0];

				// create yandex map object and set main settings
				if (o.oLatitude.value.length) {
					o.startPosition.latitude = o.oLatitude.value = parseFloat(o.oLatitude.value.replace(',', '.'));
				}
				if (o.oLongitude.value.length) {
					o.startPosition.longitude = o.oLongitude.value = parseFloat(o.oLongitude.value.replace(',', '.'));
				}
				self.map = new YMaps.Map(self);
				self.map.setCenter(new YMaps.GeoPoint(o.startPosition.longitude, o.startPosition.latitude), o.startPosition.zoom);
				if (o.typeControl === true) self.map.addControl(new YMaps.TypeControl());
				if (o.toolbar === true) self.map.addControl(new YMaps.ToolBar());
				if (o.zoom === true) self.map.addControl(new YMaps.Zoom());
				if (o.minimap === true) self.map.addControl(new YMaps.MiniMap());
				if (o.scaleLine === true) self.map.addControl(new YMaps.ScaleLine());
				if (o.searchControl === true) self.map.addControl(new YMaps.SearchControl());
				if (o.startPlacemark === true) {
					self.placemark = new YMaps.Placemark(new YMaps.GeoPoint(o.startPosition.longitude, o.startPosition.latitude), { draggable: o.startPlacemarkDraggable });
					self.map.addOverlay(self.placemark);
				}
				if (o.oLatitude && o.oLongitude) {
					YMaps.Events.observe(self.placemark, self.placemark.Events.Drag, function(marker) {
						o.oLatitude.value = marker.getGeoPoint().getLat();
						o.oLongitude.value = marker.getGeoPoint().getLng();
					});
					$([o.oLatitude, o.oLongitude]).focusout(focusout);
				}

				self.placemark.toCenter = function() {
					var center = self.map.getCenter();
					o.oLatitude.value = center.__lat;
					o.oLongitude.value = center.__lng;
					self.placemark.setGeoPoint(center);
				}

				self.toStart = function() {
					self.map.setCenter(new YMaps.GeoPoint(o.startPosition.longitude, o.startPosition.latitude));
					self.placemark.toCenter();
				}

				self.setCenter = function(latitude, longitude, zoom) {
					self.map.setCenter(new YMaps.GeoPoint(longitude, latitude), zoom);
				}
			}

			if (typeof(YMaps) == 'object') {
				YMaps.load(_init);
			}
			return self;
		},
		load: function(callback) {
			return $.ajax({
				type: "GET",
				url: $config.common.maps.yandex.api_link,
				dataType: 'script',
				success: function() {
					Just.Maps.loaded = true;
					callback();
				},
				async: false,
				cache: true
			});
		}
	};

	Just.routesList = function(options) {
		var defaults = {
			titleSwitcher: undefined,
			withEmpty: 'Не привязывать',
			withEmptyPath: '--',
			module: undefined,
			name: 'id_route',
			withChecks: true,
			buttonChanger: true,
			filtrator: true,
			buttonAppend: true,
			root: ''
		}
		var o = $.extend(true, defaults, options);
			var $this = $(this);
			var list = (o.withEmpty && o.withEmpty != undefined) ? '<option value="0" data-path="'+(o.withChecks ? '[ ] ' : '')+o.withEmptyPath+'" data-title="'+(o.withChecks ? '[ ] ' : '') + o.withEmpty+'" data-modules="">'+(o.withChecks ? '[ ] ' : '') + o.withEmpty+'</option>' : '';
			var dataset = $this.dataset = {};
			function build_list(data) {
				dataset = data;
				var id;
				for (id in data) {
					list += '<option value="'+id+'" data-path="'+(o.withChecks ? '[ ] ' : '')+data[id].path+'" data-title="'+(o.withChecks ? '[ ] ' : '')+data[id].title+'" data-modules="'+data[id].modules+'">'+(o.withChecks ? '[ ] ' : '') + data[id].title+"</option>\r\n";
				}
			}
			$.ajax({
				url: $config.uri.admin+'/system/routes/get/list',
				type: 'POST',
				dataType: 'json',
				data: {delimiter: ' - ', root: o.root},
				success: build_list,
				async: false
			});
			list = '<select name="'+o.name+'" id="field_'+o.name+'" data-titles="1" class="routes-list-select">'+list+'</select>';
			$this.list = list;
			$this.filtered = 0;
			$this.append(list);
			var $select = $this.select = $this.find('select[name="'+o.name+'"]');
			var $options = $this.options = $select.children('option');
			$this.checkModule = function(module) {
				$options.filter('[data-modules*="'+module+'"]').each(function() {
					$t = $(this);
					$t.text($t.text().replace(/^(\[ \])/, '[+]'));
				});
			}
			$this.uncheck = function() {
				$options.each(function() {
					$t = $(this);
					$t.text($t.text().replace(/^(\[\+\])/, '[ ]'));
				});
			}
			$this.filterChecked = function() {
				var $filtered = $.extend({}, $options.filter(function() {return /^\[\+\]/.test($(this).text())}));
				$select.html($filtered);
			}
			$this.setFilter = function(regex, dataItem) {
				$select.html($options);
				var $filtered;
				if (dataItem == undefined) {
					$filtered = $.extend({}, $options.filter(function() {return regex.test($(this).text())}));
				} else {
					$filtered = $.extend({}, $options.filter(function() {return regex.test($(this).data(dataItem))}));
				}
				$select.html($filtered);
			}
			$this.unfilter = function() {
				$select.html($options);
			}
			$this.css('position', 'relative');
			if (o.filtrator || o.buttonChanger) {
				var $subform = $('<div class="routes-list-control"/>');
				$this.append($subform);
				$select.parent().hover(function(){$subform.show()},function(){if (!$subform.find('input:text').is(':focus')) $subform.hide()});
			}
			if (o.filtrator) {
				var $btn_filtrator = $('<input type="text" class="input-type-text routes-list-search"/><input type="button" value="→" class="input-type-button routes-list-search"/>')
				$btn_filtrator.filter('input:button').click(function() {
					var v = $btn_filtrator.filter('input:text').val();
					$this.setFilter(new RegExp('('+v+')', 'igm'));
				});
				$subform.append($btn_filtrator).append('<br/>');
			}
			if (o.buttonChanger) {
				var $btc = $('<input type="button" class="input-type-button routes-list-titles" title="Названия" data-state="0" value="Пути"/>');
				$btc.click(function() {
					var tmp;
					var $t = $(this);
					if ($t.data('state') == 1) {
						$t.data('state', 0);
						$options.each(function() {$(this).text($(this).data('title'));});
					} else {
						$t.data('state', 1);
						$options.each(function() {$(this).text($(this).data('path'));});
					}
					tmp = $t.val();
					$t.val($t.attr('title'));
					$t.attr('title', tmp);
				});
				$subform.append($btc);
				$this.buttonChanger = $btc;
			}
			var appendById = function(id) {
				var $selected = $options.filter('[value="'+id+'"]');
				if ($('input:hidden[name="'+o.name+'[]"][value="'+$selected.val()+'"]').length) return false;
				var $div = $('<div><input type="hidden" name="'+o.name+'[]" value="'+$selected.val()+'"/><span>'+$selected.data('title')+'</span><span class="routes-list-delete">Удалить</span></div>');
				$div.children('span:eq(1)').click(function(){$div.remove()});
				$this.append($div);
			}
			if (o.buttonAppend) {
				$select.removeAttr('name');
				var $bts = $('<input type="button" class="input-type-button routes-list-append" value="Добавить"/>');
				$bts.click(function(){
					appendById($select.children('option:selected').val());
				});
				$subform.append($bts);
				$this.buttonAppend = $bts;
			}
			if ($this.data('selected') != undefined && $this.data('selected').length) {
				var l = $this.data('selected').split(','), i;
				for (i in l) { appendById(l[i]); }
			}
			return $this;
	};

	Just.choser = function(options) {
		return this.each(function() {
			var defaults = { data: [], onSelect: function() {}, width: undefined };
			var o = $.extend(true, defaults, options),contents = [],$this = $(this),list, full_list, current, selected, index = 0;
			if (typeof(o.width) == 'undefined') o.width = $this.outerWidth();
			var self = $('<div class="just-choser just-choser-single" style="width: '+o.width+'px;">' +
				'<a href="javascript:void(0)" class="choser-single" tabindex="0"><span></span><div><b></b></div></a>' +
				'<div class="choser-drop" style="width: '+(o.width-2)+'px;"><div class="choser-search"><input type="text" autocomplete="off" style="width: '+(o.width-37)+'px;" tabindex="-1"></div><ul class="choser-results"></ul></div>' +
				'</div>').hide(),
				chosen = self.children('a'),
				drop = self.children('.choser-drop'),
				input = drop.children('.choser-search').children('input'),
				results = drop.children('.choser-results');
			var _fnListItemClick = function() { self.fnSelect(this); self.fnClose(); };
			var get_index = function(elem) { for (var i=0,len=list.length;i<len;i++){ if (elem == list[i]) return i; } return 0; };
			$this.hide().after(self);
			chosen.bind('click', function() { if (drop.is(':visible')) { self.fnClose(); } else { self.fnOpen(); } });
			self.fnOpen = function() { drop.show(); self.addClass('just-choser-active'); chosen.addClass('choser-single-with-drop'); if (selected) self.fnHover.call(selected, true); input.focus(); };
			self.fnClose = function() { self.removeClass('just-choser-active'); chosen.removeClass('choser-single-with-drop'); drop.hide(); };
			self.fnSelect = function(sel) { if (sel == undefined) return; chosen.children('span')[0].innerHTML = sel.innerHTML; $this[0].value = sel.value; $this.trigger('change'); $this.data('value', sel.value); self.fnHover.call(sel); selected = sel; o.onSelect(sel.value); };
			self.fnHover = function(center) {
				if (!list.length) return; list.filter('.highlighted').removeClass('highlighted'); current = $(this).addClass('highlighted');
				if (center === true) { results.scrollTop(this.offsetTop-results.height()/2+10); } else {
					var c_off_top = current.offset().top, r_off_top = results.offset().top, c_h = current.height(); index = get_index(current[0]);
					if (c_off_top < r_off_top) { results.scrollTop(this.offsetTop-c_h); }
					if (c_off_top+20 > (r_off_top+results.height())) { results.scrollTop(this.offsetTop-results.height()+c_h+20); }
				}
			};
			self.fnInit = function() {
				var i, opts = ''; contents=[]; results.html('');
				if (!o.data.length) {
					var _opts = $this.children('option'); for (i=0,len=_opts.length;i<len;i++) { var ts = _opts[i]; contents.push({ title: ts.innerHTML, value: ts.value }); }
				} else { contents = o.data; }
				for (i in contents) { opts += '<li id="just-elem-'+i+'" value="'+contents[i].value+'">'+contents[i].title+'</li>'; }
				full_list = list = $(opts).bind({ click: _fnListItemClick, mouseover: self.fnHover }); results.append(list);
				if (typeof ($this.data('value')) != 'undefined') { $this[0].value = $this.data('value'); }
				index = $this.children(':selected').index(); self.fnSelect(list[index]); self.fnClose(); self.show();
				return self;
			};
			input.keyup(function(e) {
				var offset = 7;
				switch (e.keyCode) {
					case 40: case 38: case 34: case 33: e.preventDefault(); break;
					case 13: // enter
						e.preventDefault(); self.fnSelect(list[index]); self.fnClose(); break;
					case 35: // end
						index=list.length-1; self.fnHover.call(list[index]); break;
					case 36: // home
						index=0; self.fnHover.call(list[index]); break;
					default: {
						self.fnFilter(this.value);
					}
				}
			}).keydown(function(e) {
				var offset = 7;
				switch (e.keyCode) {
					case 40: // down
						if(index+1<list.length-1){index+=1;}else{index=list.length-1}
						self.fnHover.call(list[index]);
						break;
					case 38: // up
						if(index-1>=0){index-=1;}else{index=0}
						self.fnHover.call(list[index]);
						break;
					case 34: // pgdown
						if (index+offset<list.length-1){index+=offset;}else{index=list.length-1}
						self.fnHover.call(list[index]);
						break;
					case 33: // pgup
						if (index-offset>=0){index-=offset;}else{index=0}
						self.fnHover.call(list[index]);
						break;
					case 13: case 35: case 36:
					  e.preventDefault();
					  break;
				}
			});
			self.fnSearch = function(str) {
				var regexp = new RegExp('('+str+')', 'igm');
				list = full_list.hide().filter(function() {return this.innerHTML.match(regexp)}).show();
				if (!list.filter('.highlighted').length && list.length) {
					index = 0; self.fnHover.call(list[index]);
				} else {
					self.fnHover.call(list.filter('.highlighted')[0]);
				}
			};
			self.fnFilter = function(str) {
				var regexp = new RegExp('('+str+')', 'igm');
				full_list.hide();
				list = [];
				for (i=0,len=contents.length;i<len;i++) {
					if (contents[i].title.match(regexp)) list.push(full_list[i]);
				}
				list = $(list).show();
				if (!list.filter('.highlighted').length && list.length) {
					index = 0;
					self.fnHover.call(list[index]);
				} else {
					self.fnHover.call(list.filter('.highlighted')[0]);
				}
			}
			return self.fnInit();
		});
	};

	Just.sha1 = function (msg) {
		function rotate_left(n, s) {
			var t4 = ( n << s ) | (n >>> (32 - s));
			return t4;
		};
		function lsb_hex(val) {
			var str = "", i, vh, vl;
			for (i = 0; i <= 6; i += 2) {
				vh = (val >>> (i * 4 + 4)) & 0x0f;
				vl = (val >>> (i * 4)) & 0x0f;
				str += vh.toString(16) + vl.toString(16);
			}
			return str;
		};
		function cvt_hex(val) {
			var str = "", i, v;
			for (i = 7; i >= 0; i--) {
				v = (val >>> (i * 4)) & 0x0f;
				str += v.toString(16);
			}
			return str;
		};
		function Utf8Encode(string) {
			string = string.replace(/\r\n/g, "\n");
			var utftext = "";
			for (var n = 0; n < string.length; n++) {
				var c = string.charCodeAt(n);
				if (c < 128) {
					utftext += String.fromCharCode(c);
				} else if ((c > 127) && (c < 2048)) {
					utftext += String.fromCharCode((c >> 6) | 192);
					utftext += String.fromCharCode((c & 63) | 128);
				} else {
					utftext += String.fromCharCode((c >> 12) | 224);
					utftext += String.fromCharCode(((c >> 6) & 63) | 128);
					utftext += String.fromCharCode((c & 63) | 128);
				}
			}
			return utftext;
		};
		var blockstart,i, j;
		var W = new Array(80);
		var H0 = 0x67452301;
		var H1 = 0xEFCDAB89;
		var H2 = 0x98BADCFE;
		var H3 = 0x10325476;
		var H4 = 0xC3D2E1F0;
		var A, B, C, D, E;
		var temp;
		msg = Utf8Encode(msg);
		var msg_len = msg.length;
		var word_array = new Array();
		for (i = 0; i < msg_len - 3; i += 4) {
			j = msg.charCodeAt(i) << 24 | msg.charCodeAt(i + 1) << 16 |
					msg.charCodeAt(i + 2) << 8 | msg.charCodeAt(i + 3);
			word_array.push(j);
		}
		switch (msg_len % 4) {
			case 0:
				i = 0x080000000;
				break;
			case 1:
				i = msg.charCodeAt(msg_len - 1) << 24 | 0x0800000;
				break;
			case 2:
				i = msg.charCodeAt(msg_len - 2) << 24 | msg.charCodeAt(msg_len - 1) << 16 | 0x08000;
				break;
			case 3:
				i = msg.charCodeAt(msg_len - 3) << 24 | msg.charCodeAt(msg_len - 2) << 16 | msg.charCodeAt(msg_len - 1) << 8 | 0x80;
				break;
		}
		word_array.push(i);
		while ((word_array.length % 16) != 14) word_array.push(0);
		word_array.push(msg_len >>> 29);
		word_array.push((msg_len << 3) & 0x0ffffffff);
		for (blockstart = 0; blockstart < word_array.length; blockstart += 16) {
			for (i = 0; i < 16; i++) W[i] = word_array[blockstart + i];
			for (i = 16; i <= 79; i++) W[i] = rotate_left(W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16], 1);
			A = H0;
			B = H1;
			C = H2;
			D = H3;
			E = H4;
			for (i = 0; i <= 19; i++) {
				temp = (rotate_left(A, 5) + ((B & C) | (~B & D)) + E + W[i] + 0x5A827999) & 0x0ffffffff;
				E = D;
				D = C;
				C = rotate_left(B, 30);
				B = A;
				A = temp;
			}
			for (i = 20; i <= 39; i++) {
				temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0x6ED9EBA1) & 0x0ffffffff;
				E = D;
				D = C;
				C = rotate_left(B, 30);
				B = A;
				A = temp;
			}
			for (i = 40; i <= 59; i++) {
				temp = (rotate_left(A, 5) + ((B & C) | (B & D) | (C & D)) + E + W[i] + 0x8F1BBCDC) & 0x0ffffffff;
				E = D;
				D = C;
				C = rotate_left(B, 30);
				B = A;
				A = temp;
			}
			for (i = 60; i <= 79; i++) {
				temp = (rotate_left(A, 5) + (B ^ C ^ D) + E + W[i] + 0xCA62C1D6) & 0x0ffffffff;
				E = D;
				D = C;
				C = rotate_left(B, 30);
				B = A;
				A = temp;
			}
			H0 = (H0 + A) & 0x0ffffffff;
			H1 = (H1 + B) & 0x0ffffffff;
			H2 = (H2 + C) & 0x0ffffffff;
			H3 = (H3 + D) & 0x0ffffffff;
			H4 = (H4 + E) & 0x0ffffffff;
		}
		temp = cvt_hex(H0) + cvt_hex(H1) + cvt_hex(H2) + cvt_hex(H3) + cvt_hex(H4);
		return temp.toLowerCase();
	};

	Just.run = function(type, controller, action, inner_call) {
		if (typeof(inner_call) == 'undefined') {
			for (var i in $config.route.modules) {
				var module = $config.route.modules[i];
				Just.run(module.type, module.module, module.action, true);
			}
			return;
		}
		type = type || $config.request._config.type;
		controller = controller || $config.request._config.controller;
		action = action || $config.request._config.action;
		if (typeof (Just[type]) == 'function') {
			Just[type]();
		}
		if (typeof (Just[type]) != 'undefined' && typeof (Just[type][controller]) == 'function') {
			Just[type][controller]();
		}
		if (typeof (Just[type]) != 'undefined' && typeof (Just[type][controller]) != 'undefined' && typeof (Just[type][controller][action]) == 'function') {
			Just[type][controller][action]();
		}
		return;
	};
})(Just);
