var Wbn = (function() {

	/* Mootools embedded, some functionality left out on purpose */

	var Native = function(options){
		options = options || {};
		var name = options.name;
		var legacy = options.legacy;
		var protect = options.protect;
		var methods = options.implement;
		var generics = options.generics;
		var initialize = options.initialize;
		var afterImplement = options.afterImplement || function(){};
		var object = initialize || legacy;
		generics = generics !== false;

		object.constructor = Native;
		object.$family = {name: 'native'};
		if (legacy && initialize) object.prototype = legacy.prototype;
		object.prototype.constructor = object;

		if (name){
			var family = name.toLowerCase();
			object.prototype.$family = {name: family};
			Native.typize(object, family);
		}

		var add = function(obj, name, method, force){
			if (!protect || force || !obj.prototype[name]) obj.prototype[name] = method;
			if (generics) Native.genericize(obj, name, protect);
			afterImplement.call(obj, name, method);
			return obj;
		};

		object.alias = function(a1, a2, a3){
			if (typeof a1 == 'string'){
				if ((a1 = this.prototype[a1])) return add(this, a2, a1, a3);
			}
			for (var a in a1) this.alias(a, a1[a], a2);
			return this;
		};

		object.implement = function(a1, a2, a3){
			if (typeof a1 == 'string') return add(this, a1, a2, a3);
			for (var p in a1) add(this, p, a1[p], a2);
			return this;
		};

		if (methods) object.implement(methods);

		return object;
	};

	Native.genericize = function(object, property, check){
		if ((!check || !object[property]) && typeof object.prototype[property] == 'function') object[property] = function(){
			var args = Array.prototype.slice.call(arguments);
			return object.prototype[property].apply(args.shift(), args);
		};
	};

	Native.implement = function(objects, properties){
		for (var i = 0, l = objects.length; i < l; i++) objects[i].implement(properties);
	};

	Native.typize = function(object, family){
		if (!object.type) object.type = function(item){
			return ($type(item) === family);
		};
	};

	var Hash = new Native({

		name: 'Hash',

		initialize: function(object){
			if ($type(object) == 'hash') object = $unlink(object.getClean());
			for (var key in object) this[key] = object[key];
			return this;
		}

	});


	Hash.implement({

		forEach: function(fn, bind){
			for (var key in this){
				if (this.hasOwnProperty(key)) fn.call(bind, this[key], key, this);
			}
		},

		getClean: function(){
			var clean = {};
			for (var key in this){
				if (this.hasOwnProperty(key)) clean[key] = this[key];
			}
			return clean;
		},

		getLength: function(){
			var length = 0;
			for (var key in this){
				if (this.hasOwnProperty(key)) length++;
			}
			return length;
		}

	});

	Hash.alias('forEach', 'each');

	Hash.implement({

		has: Object.prototype.hasOwnProperty,

		keyOf: function(value){
			for (var key in this){
				if (this.hasOwnProperty(key) && this[key] === value) return key;
			}
			return null;
		},

		hasValue: function(value){
			return (Hash.keyOf(this, value) !== null);
		},

		extend: function(properties){
			Hash.each(properties, function(value, key){
				Hash.set(this, key, value);
			}, this);
			return this;
		},

		combine: function(properties){
			Hash.each(properties, function(value, key){
				Hash.include(this, key, value);
			}, this);
			return this;
		},

		erase: function(key){
			if (this.hasOwnProperty(key)) delete this[key];
			return this;
		},

		get: function(key){
			return (this.hasOwnProperty(key)) ? this[key] : null;
		},

		set: function(key, value){
			if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
			return this;
		},

		empty: function(){
			Hash.each(this, function(value, key){
				delete this[key];
			}, this);
			return this;
		},

		include: function(key, value){
			var k = this[key];
			if (k == undefined) this[key] = value;
			return this;
		},

		map: function(fn, bind){
			var results = new Hash;
			Hash.each(this, function(value, key){
				results.set(key, fn.call(bind, value, key, this));
			}, this);
			return results;
		},

		filter: function(fn, bind){
			var results = new Hash;
			Hash.each(this, function(value, key){
				if (fn.call(bind, value, key, this)) results.set(key, value);
			}, this);
			return results;
		},

		every: function(fn, bind){
			for (var key in this){
				if (this.hasOwnProperty(key) && !fn.call(bind, this[key], key)) return false;
			}
			return true;
		},

		some: function(fn, bind){
			for (var key in this){
				if (this.hasOwnProperty(key) && fn.call(bind, this[key], key)) return true;
			}
			return false;
		},

		getKeys: function(){
			var keys = [];
			Hash.each(this, function(value, key){
				keys.push(key);
			});
			return keys;
		},

		getValues: function(){
			var values = [];
			Hash.each(this, function(value){
				values.push(value);
			});
			return values;
		},

		toQueryString: function(base){
			var queryString = [];
			Hash.each(this, function(value, key){
				if (base) key = base + '[' + key + ']';
				var result;
				switch ($type(value)){
					case 'object': result = Hash.toQueryString(value, key); break;
					case 'array':
						var qs = {};
						value.each(function(val, i){
							qs[i] = val;
						});
						result = Hash.toQueryString(qs, key);
					break;
					default: result = key + '=' + encodeURIComponent(value);
				}
				if (value != undefined) queryString.push(result);
			});

			return queryString.join('&');
		}

	});

	Hash.alias({keyOf: 'indexOf', hasValue: 'contains'});

	Array.implement({

		forEach: function(fn, bind){
			for (var i = 0, l = this.length; i < l; i++) fn.call(bind, this[i], i, this);
		}

	});

	Array.alias('forEach', 'each');

	function $A(iterable){
		if (iterable.item){
			var array = [];
			for (var i = 0, l = iterable.length; i < l; i++) array[i] = iterable[i];
			return array;
		}
		return Array.prototype.slice.call(iterable);
	};

	function $arguments(i){
		return function(){
			return arguments[i];
		};
	};

	function $chk(obj){
		return !!(obj || obj === 0);
	};

	function $clear(timer){
		clearTimeout(timer);
		clearInterval(timer);
		return null;
	};

	function $defined(obj){
		return (obj != undefined);
	};

	function $each(iterable, fn, bind){
		var type = $type(iterable);
		((type == 'arguments' || type == 'collection' || type == 'array') ? Array : Hash).each(iterable, fn, bind);
	};

	function $empty(){};

	function $extend(original, extended){
		for (var key in (extended || {})) original[key] = extended[key];
		return original;
	};

	function $H(object){
		return new Hash(object);
	};

	function $lambda(value){
		return (typeof value == 'function') ? value : function(){
			return value;
		};
	};

	function $merge(){
		var mix = {};
		for (var i = 0, l = arguments.length; i < l; i++){
			var object = arguments[i];
			if ($type(object) != 'object') continue;
			for (var key in object){
				var op = object[key], mp = mix[key];
				mix[key] = (mp && $type(op) == 'object' && $type(mp) == 'object') ? $merge(mp, op) : $unlink(op);
			}
		}
		return mix;
	};

	function $pick(){
		for (var i = 0, l = arguments.length; i < l; i++){
			if (arguments[i] != undefined) return arguments[i];
		}
		return null;
	};

	function $random(min, max){
		return Math.floor(Math.random() * (max - min + 1) + min);
	};

	function $splat(obj){
		var type = $type(obj);
		return (type) ? ((type != 'array' && type != 'arguments') ? [obj] : obj) : [];
	};

	var $time = Date.now || function(){
		return +new Date;
	};

	function $try(){
		for (var i = 0, l = arguments.length; i < l; i++){
			try {
				return arguments[i]();
			} catch(e){}
		}
		return null;
	};

	function $type(obj){
		if (obj == undefined) return false;
		if (obj.$family) return (obj.$family.name == 'number' && !isFinite(obj)) ? false : obj.$family.name;
		if (obj.nodeName){
			switch (obj.nodeType){
				case 1: return 'element';
				case 3: return (/\S/).test(obj.nodeValue) ? 'textnode' : 'whitespace';
			}
		} else if (typeof obj.length == 'number'){
			if (obj.callee) return 'arguments';
			else if (obj.item) return 'collection';
		}
		return typeof obj;
	};

	function $unlink(object){
		var unlinked;
		switch ($type(object)){
			case 'object':
				unlinked = {};
				for (var p in object) unlinked[p] = $unlink(object[p]);
			break;
			case 'hash':
				unlinked = new Hash(object);
			break;
			case 'array':
				unlinked = [];
				for (var i = 0, l = object.length; i < l; i++) unlinked[i] = $unlink(object[i]);
			break;
			default: return object;
		}
		return unlinked;
	};

	function $xmlhttprequest() {
		return $try(function(){
			return new XMLHttpRequest();
		}, function(){
			return new ActiveXObject('MSXML2.XMLHTTP');
		});
	}


	var Chain = new Class({

		$chain: [],

		chain: function(){
			this.$chain.extend(Array.flatten(arguments));
			return this;
		},

		callChain: function(){
			return (this.$chain.length) ? this.$chain.shift().apply(this, arguments) : false;
		},

		clearChain: function(){
			this.$chain.empty();
			return this;
		}

	});

	var Events = new Class({

		addEvent: function(type, fn, internal){
			type = Events.removeOn(type);
			if (fn != $empty){
				this.$events = this.$events || {};
				this.$events[type] = this.$events[type] || [];
				this.$events[type].include(fn);
				if (internal) fn.internal = true;
			}
			return this;
		},

		// autoremoving-onceFiring-flash events
		addEventOnce: function(type, fn) {
			this.addEvent(type, function() {
				this.removeEvent(type, arguments.callee);
				fn.run(arguments);
			});
		},

		addEventsOnce: function(events){
			for (var type in events) this.addEventOnce(type, events[type]);
			return this;
		},

		addEvents: function(events){
			for (var type in events) this.addEvent(type, events[type]);
			return this;
		},

		fireEvent: function(type, args, delay){
			type = Events.removeOn(type);
			if (!this.$events || !this.$events[type]) return this;
			$A(this.$events[type]).each(function(fn){
				fn.create({'bind': this, 'delay': delay, 'arguments': args})();
			}, this);
			return this;
		},

		removeEvent: function(type, fn){
			type = Events.removeOn(type);
			if (!this.$events || !this.$events[type]) return this;
			if (!fn.internal) this.$events[type].erase(fn);
			return this;
		},

		removeEvents: function(what){
			if ($type(what) == 'object'){
				for (var type in what) this.removeEvent(type, what[type]);
				return this;
			}
			if (what) what = Events.removeOn(what);
			for (var type in this.$events){
				if (what && what != type) continue;
				var fns = this.$events[type];
				for (var i = fns.length; i--; i) this.removeEvent(type, fns[i]);
			}
			return this;
		}

	});

	Events.removeOn = function(string){
		return string.replace(/^on([A-Z])/, function(full, first) {
			return first.toLowerCase();
		});
	};

	var Options = new Class({

		setOptions: function(){
			this.options = $merge.run([this.options].extend(arguments));
			if (!this.addEvent) return this;
			for (var option in this.options){
				if ($type(this.options[option]) != 'function' || !(/^on[A-Z]/).test(option)) continue;
				this.addEvent(option, this.options[option]);
				delete this.options[option];
			}
			return this;
		}

	});

	var Request = new Class({

		Implements: [Chain, Events, Options],

		options: {/*
			onRequest: $empty,
			onComplete: $empty,
			onCancel: $empty,
			onSuccess: $empty,
			onFailure: $empty,
			onException: $empty,*/
			url: '',
			data: '',
			headers: {
				'X-Requested-With': 'XMLHttpRequest',
				'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
			},
			async: true,
			format: false,
			method: 'post',
			link: 'ignore',
			isSuccess: null,
			emulation: true,
			urlEncoded: true,
			encoding: 'utf-8',
			evalScripts: false,
			evalResponse: false
		},

		initialize: function(options){
			this.xhr = new $xmlhttprequest();
			this.setOptions(options);
			this.options.isSuccess = this.options.isSuccess || this.isSuccess;
			this.headers = new Hash(this.options.headers);
		},

		onStateChange: function(){
			if (this.xhr.readyState != 4 || !this.running) return;
			this.running = false;
			this.status = 0;
			$try(function(){
				this.status = this.xhr.status;
			}.bind(this));
			if (this.options.isSuccess.call(this, this.status)){
				this.response = {text: this.xhr.responseText, xml: this.xhr.responseXML};
				this.success(this.response.text, this.response.xml);
			} else {
				this.response = {text: null, xml: null};
				this.failure();
			}
			this.xhr.onreadystatechange = $empty;
		},

		isSuccess: function(){
			return ((this.status >= 200) && (this.status < 300));
		},

		processScripts: function(text){
			if (this.options.evalResponse || (/(ecma|java)script/).test(this.getHeader('Content-type'))) return $exec(text);
			return text.stripScripts(this.options.evalScripts);
		},

		success: function(text, xml){
			this.onSuccess(this.processScripts(text), xml);
		},

		onSuccess: function(){
			this.fireEvent('complete', arguments).fireEvent('success', arguments).callChain();
		},

		failure: function(){
			this.onFailure();
		},

		onFailure: function(){
			this.fireEvent('complete').fireEvent('failure', this.xhr);
		},

		setHeader: function(name, value){
			this.headers.set(name, value);
			return this;
		},

		getHeader: function(name){
			return $try(function(){
				return this.xhr.getResponseHeader(name);
			}.bind(this));
		},

		check: function(caller){
			if (!this.running) return true;
			switch (this.options.link){
				case 'cancel': this.cancel(); return true;
				case 'chain': this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
			}
			return false;
		},

		send: function(options){
			if (!this.check(arguments.callee, options)) return this;
			this.running = true;

			var type = $type(options);
			if (type == 'string' || type == 'element') options = {data: options};

			var old = this.options;
			options = $extend({data: old.data, url: old.url, method: old.method}, options);
			var data = options.data, url = options.url, method = options.method;

			switch ($type(data)){
				case 'element': data = $(data).toQueryString(); break;
				case 'object': case 'hash': data = Hash.toQueryString(data);
			}

			if (this.options.format){
				var format = 'format=' + this.options.format;
				data = (data) ? format + '&' + data : format;
			}

			if (this.options.emulation && ['put', 'delete'].contains(method)){
				var _method = '_method=' + method;
				data = (data) ? _method + '&' + data : _method;
				method = 'post';
			}

			if (this.options.urlEncoded && method == 'post'){
				var encoding = (this.options.encoding) ? '; charset=' + this.options.encoding : '';
				this.headers.set('Content-type', 'application/x-www-form-urlencoded' + encoding);
			}

			if (data && method == 'get'){
				url = url + (url.contains('?') ? '&' : '?') + data;
				data = null;
			}

			this.xhr.open(method.toUpperCase(), url, this.options.async);

			this.xhr.onreadystatechange = this.onStateChange.bind(this);

			this.headers.each(function(value, key){
				try {
					this.xhr.setRequestHeader(key, value);
				} catch (e){
					this.fireEvent('exception', [key, value]);
				}
			}, this);

			this.fireEvent('request');
			this.xhr.send(data);
			if (!this.options.async) this.onStateChange();
			return this;
		},

		cancel: function(){
			if (!this.running) return this;
			this.running = false;
			this.xhr.abort();
			this.xhr.onreadystatechange = $empty;
			this.xhr = new $xmlhttprequest();
			this.fireEvent('cancel');
			return this;
		}

	});


	var JSON = new Hash({

		$specialChars: {'\b': '\\b', '\t': '\\t', '\n': '\\n', '\f': '\\f', '\r': '\\r', '"' : '\\"', '\\': '\\\\'},

		$replaceChars: function(chr){
			return JSON.$specialChars[chr] || '\\u00' + Math.floor(chr.charCodeAt() / 16).toString(16) + (chr.charCodeAt() % 16).toString(16);
		},

		encode: function(obj){
			switch ($type(obj)){
				case 'string':
					return '"' + obj.replace(/[\x00-\x1f\\"]/g, JSON.$replaceChars) + '"';
				case 'array':
					return '[' + String(obj.map(JSON.encode).filter($defined)) + ']';
				case 'object': case 'hash':
					var string = [];
					Hash.each(obj, function(value, key){
						var json = JSON.encode(value);
						if (json) string.push(JSON.encode(key) + ':' + json);
					});
					return '{' + string + '}';
				case 'number': case 'boolean': return String(obj);
				case false: return 'null';
			}
			return null;
		},

		decode: function(string, secure){
			if ($type(string) != 'string' || !string.length) return null;
			if (secure && !(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''))) return null;
			return eval('(' + string + ')');
		}

	});


	function sendRequest(options) {
		new Request(options).send();
	}

	function invokeServiceAction(actionName, instancePageUrl, el, paramsSelector, additionalParams) {
		var serviceElement = null,
			serviceInstanceName = null,
			serviceInstancePageID = null;

		if (document.body.className && -1 != document.body.className.indexOf('wbn-dynamic')) {
		    var parts = document.body.className.split(' ');
		    if (3 != parts.length) {
		        console.warn('Invalid body class declaration for dynamic page.');
		        return;
		    }
		    serviceElement = document.body;
		    serviceInstancePageID = parts[2];
		} else {
    		while (el && document.body != el) {
    			if (el.className && -1 != el.className.indexOf('wbn-component-')) {
    				serviceElement = el;
    				break;
    			}
    			el = el.parentNode;
    		}
		}
		// console.log(serviceElement);
		if (null == serviceElement) {
			console.warn('Service element not found.');
			return;
		} else {
			var re = /wbn-part-([a-zA-Z0-9-_]*)/;
			var m = re.exec(serviceElement.className);
			if (!(m && m[1])) {
				console.warn('Could not read service instance name.');
				return;
			}
			serviceInstanceName = m[1];
		}
		// console.log(serviceInstanceName);
		if (paramsSelector) {
			try {
				window.eval('paramsSelector = ' + paramsSelector.replace(/\|/g, "'"));
			} catch (ex) {
				paramsSelector = null;
			}
		}
		var actionParams = {};
		// console.log(paramsSelector);
		if ('object' == $type(paramsSelector)) {
			$H(paramsSelector).each(function(selector, paramName) {
				try {
					window.eval(['actionParams[paramName] = serviceElement.getElementsByTagName("', selector.replace('.', ').').replace(':', '").item(')].join(''));
				} catch (ex) {
					console.warn('Error executing selector for ' + paramName + ': ' + selector);
				}
			}, this);
		}
		sendRequest({
			url: '/wbn-services/public/invokeServiceAction',
			data: {
				serviceInstanceName: serviceInstanceName,
				serviceInstancePageUrl: '/' + instancePageUrl,
				serviceInstancePageID: serviceInstancePageID,
				actionName: actionName,
				actionOptions: JSON.encode($merge(additionalParams || {}, actionParams)),
				pageUrl: self.location.pathname
			},
			onComplete: function(response) {
				if (response) {
					try {
						response = JSON.decode(response);
					} catch (ex) {
						response = null;
						console.warn('Error while parsing remote result.');
					}
				}
                // console.info(response);
				if (response && response.statusCode && response.result && 200 == response.statusCode) {
					if (response.result.type && 'Error' != response.result.type) {
						processServiceAction(serviceElement, response.result)
					}
					else
					{
					    alert('An error occured.');
					}
				}
			}
		});
		return false;
	}

	function processServiceAction(serviceElement, result) {
	    alert(result.content);
        // console.log(result);
	}

	var bidFormInited = false;
	function dialogServiceAction(actionName, el, paramsSelector) {
	    if (!bidFormInited) initForm();
        bidFormInited = true;
	    showForm();

	    bf.actionName = actionName;
	    bf.el = el;
	    bf.paramsSelector = paramsSelector;

        //
        // var username = prompt('Please enter your Allegro username: ');
        // if (username) {
        //     var password = prompt('Your password: ');
        //     if (username && password) {
        //         invokeServiceAction(actionName, el, paramsSelector, {bUsername: username, bPassword: password});
        //     }
        // }
        // return false;
        return false;
	}


    function invokeBid() {

        invokeServiceAction(bf.actionName, '', bf.el, bf.paramsSelector, {bUsername: $('busername').value, bPassword: $('bpass').value});
        hideForm();

    }



/* ***/
//p: tohle je desna sracka emulujici budouci feats. nutne pro allegro bid akci


	var bf = {formWidth: 430, formTop: 175, fx:null, elBg:null, elForm:null, els:null, timeOut: 10000, elForm:null};

	function initForm() {
		buildForm();
		setStyles();
		initFx();
		registerEvents();
	}

	function buildForm() {
		var cntCss = 'margin: 0; padding: 0; visibility: hidden; position: ' + (Browser.Engine.trident4 ? 'absolute' : 'fixed') + '; width: ' + bf.formWidth + 'px; height: 190px; z-index: 997; top: ' + bf.formTop + 'px; ';
		bf.elBg = new Element('div', { style: cntCss + "background-color: #000" });
		bf.elForm = new Element('div', { id: 'wbn-bid-form', style: cntCss });
		bf.els = [bf.elBg, bf.elForm];
		bf.elForm.innerHTML = '<h1>Bid on auction</h1><label for="email">Your Allegro username</label><input name="busername" id="busername" type="text" /><span></span><br /><label for="pass">Password</label><input name="bpass" id="bpass" type="password" /><span></span><br /><br /><div><input id="btn-bid-cancel" class="submit" type="submit" value="Close dialog" /><input id="btn-bid" type="submit" value="Place bid!" /></div><span class="info"></span>';
		bf.elForm.getElements('input[type=text], input[type=password]')
			.setStyles({ width: 180, paddingTop: Browser.Engine.gecko ? 2 : 1 });
		bf.elForm.getElements('input[type=submit]').setStyles({ 'float': 'right', margin: 4 });
		$$(bf.els).inject(document.body);
	}

	function setStyles() {
		['#wbn-bid-form h1, #wbn-bid-form label, #wbn-bid-form span { color: white; font-family: arial;}',
		 '#wbn-bid-form h1 { font-size: 16px; margin: 17px 35px;}',
		 '#wbn-bid-form br { clear: both;}',
		 '#wbn-bid-form label, #wbn-bid-form input, #wbn-bid-form span { float: left; font-size: 13px; font-family: arial; margin: 2px; padding: 1px}',
		 '#wbn-bid-form span { color: #ff0066; margin: 4px; visibility: hidden}',
		 '#wbn-bid-form div { margin-right: 110px; }',
		 '#wbn-bid-form div.checkbox { margin-left: 128px; }',
		 '#wbn-bid-form div.checkbox label { float: left; margin:0; padding:2px; width:160px; font-size:11px; color:#bbb}',
		 '#wbn-bid-form label { margin: 4px 8px 0px 40px; width: 83px; text-align: right; }',
		 '#wbn-bid-form .info { visibility: visible; position: absolute; left: 10px; bottom: 2px; }'].join('').injectCssInHeader();
	}

	function initFx() {
		fx = new Fx.Elements(bf.els, {
			duration: 700,
			transition: Fx.Transitions.Quad.easeOut,
			link: 'cancel',
			onComplete: function() {
				$$(bf.els).setStyle('top', bf.formTop);
				fx.options.duration = 700;
				if (bf.elForm.style.visibility == 'visible') bf.elForm.getElement('input').focus();
			}
		});
	}

	function showForm() {
		// if (window.wbnData && wbnData.userToken) {
		//  return onServerResponse('({statusCode: 200, result: {sites:{}}})');
		// }
		setToMiddle();
		// resetText();
		fx.start({ 0: { opacity: [0, 0.8] }, 1: { opacity: [0, 1]} });
	}

	function hideForm() {
		fx.start({ 0: { opacity: [0], top: [320] }, 1: { opacity: [0], top: [320]} });
	}


	function setToMiddle(el) {
		$$(bf.els).setStyle('left', (window.getSize().x / 2) - (bf.formWidth / 2));
	}

	function registerEvents() {
		// if (!$('wbn-login')) return;
		bf.elForm.addEvents({
			click: function(e) {
			},
			keypress: function(e) { if (e.key == 'enter') invokeBid(); }
		});
		document.addEvent('keypress', function(e) {
			if (e.key == 'esc') hideForm();
		});
		$('btn-bid').addEvent('click', function(e) { invokeBid(); e.stop(); });
		$('btn-bid-cancel').addEvent('click', function(e) { hideForm(); e.stop(); });
		// if (!$('wbn-login-create')) return;
		// $('wbn-login-create').addEvent('click', function(e) { shouldCreatePageAfterLogin = true; showForm(); e.stop(); });
	}

/****/

	$logempty = function() {
		// alert(arguments.join('\n'));
	}

	if (!('console' in window)) {
		window.console = {
			log: $logempty,
			info: $logempty,
			warn: $logempty,
			error: $logempty
		}
	}

	return {
		Request: {
			send: sendRequest
		},
		Service: {
			invokeAction: invokeServiceAction,
			dialogAction: dialogServiceAction
		}
	}
})();
