if (FJ === undefined) alert('fj.js must be included first');
else if (SWFAddress === undefined) alert('swfaddress.js must be included first');
else {
	FJ.Async = Class.create({
	
		CLASSNAME: 'Async',
		
		ELEMENTS_TO_UPDATE: ['.navigation', '.scrolling'],
		
		location: function () { return window.location.toString().match(/(https?:\/\/)([^\/]+)([^#]*)#?([^#]*)/); },
		protocol: function () { return this.location()[1]; },
		host: function () { return this.location()[2]; },
		path: function () { return this.location()[3]; },
		swfaddress: function () { return window.location.toString().include("#"); },
		swfaddress_path: function () { return this.location()[4].blank() ? "/" : this.location()[4]; },
		
		initialize: function() {
			SWFAddress.onChange = this._on_location_change.bind(this);
			this._current_location = this.path();
			
			this.listen_to_clicks();
			// this.listen_to_submits();
		},
		
		listen_to_clicks: function () {
			Event.observe(document, "click", this._on_click.bind(this));
		},
		
		listen_to_submits: function () {
			Event.observe(document, "submit", this._on_submit.bind(this));
		},
		
		goto_location: function (url) {
			if (url.indexOf(this.host()) !== -1) {
				url = url.match(/https?:\/\/([^\/]+)([^#]*)/)[2];
			}
			
			if (url == this._current_location) {
				this._on_location_change();
			} else {
				SWFAddress.setValue(url);
			}
		},
		
		_on_click: function (event) {
			// disabled for IE6
      if (Prototype.Browser.IE6) return true;
			
			var elem = event.element();
			var a = elem.nodeName.toLowerCase() == "a" ? elem : elem.up("a");
			
			//ie debugging
			if (a === undefined) return;
			
			// scrolling the page on jumps
			var relAttr = a.readAttribute("rel");
			if (relAttr != null && relAttr.match(/jump_to:(.+)/)) {
				event.stop();
				new Effect.Tween(null, document.viewport.getScrollOffsets().top, $$(relAttr.match(/jump_to:(.+)/)[1])[0].cumulativeOffset().top, function(p){ window.scrollTo(0,p); });
				return;
			}
			
			var full_domain = a.href.indexOf(this.protocol() + this.host()) == 0;
			
			if (a 
				&& (a.href.indexOf("/") == 0 || full_domain)
				&& a.href.indexOf("/assets") == -1
				&& a.readAttribute('rel') != 'reload'
				&& (a.href.indexOf('#') != 0 || a.href.indexOf(this.protocol() + this.host() + '#') != 0)
				&& (!a.readAttribute('rel') || $$('.'+a.readAttribute('rel'))[0] === undefined)
				&& !Prototype.Browser.IE6
			) {
				
				event.stop();
				var href = (full_domain === true) ? (Prototype.Browser.IE6 ? "/" : "") + a.pathname : a.readAttribute("href");
				this.goto_location(href);
				
			} 
			
			else if (a.href.indexOf(this.protocol()) == 0 && a.href.indexOf(this.host()) == -1 ) {
				a.writeAttribute('target', '_blank');
			} 
			
			else if (a.readAttribute('rel') && $$('.'+a.readAttribute('rel'))[0] !== undefined) {
				
				var ie6 = Prototype.Browser.IE6;
				
				event.stop();
				
				var filter_display = a.hasClassName('left-tab-twitter') || a.hasClassName('widget-filter-button') ? "block" : "none";
				
				$A($$('.widget-filter-button')).each(function(filter_elem) {
					filter_elem.setStyle({display: filter_display});
				});
				
				var target_tab = a.readAttribute('rel');
				
				if (ie6) a.addClassName(a.readAttribute('class')+"-active");
				
				a.addClassName('active').siblings().each(function(anchor) {
					anchor.removeClassName('active').removeClassName(a.readAttribute('class')+"-active");
				});
				
				if (a.hasClassName('left-tab-twitter')) {
					$($$('.widget-controls a.widget-filter-button')).each(function(anchor) {
						$(anchor).removeClassName('active').removeClassName(a.readAttribute('class')+"-active");
					}.bind(this));
				}
				
				$$('.'+target_tab)[0].setStyle({display: 'block'}).siblings().each(function(sibling) {
					$(sibling).setStyle({display: 'none'});
				}.bind(this));
			}
		},
		
		_on_submit: function (event) {
			event.stop();
			
			var form_element = event.element();
			
			var requestdata = form_element.serialize(true);
			new Ajax.Request(form_element.readAttribute('action'), {
				method: form_element.readAttribute('method'),
				parameters: requestdata,
				onSuccess: this.request_success.bind(this)
			});
		},
		
		_on_location_change: function() {
			// IE6 homepage reload fix
      if (SWFAddress.getValue() == this._current_location && this._current_location == "/") return;
      if (Prototype.Browser.IE6) 
  			return;
			
      this.request_page(SWFAddress.getValue());
		},
		
		request_page: function(url) {
			this._loader_timeout = this.display_loader($$('.fixed')[0]);
			
			new Ajax.Request(url, {
				method: 'get',
				onSuccess: this.request_success.bind(this),
				onFailure: this.request_failure.bind(this)
			});
		},
		
		request_success:function(transport) {
			this.hide_loader();
			
			this._current_location = SWFAddress.getValue();
			
			this.track_link();
			SWFAddress.setTitle(this.decode_html_entities(transport.responseText.match(/<title>([\s\S]*)<\/title>/)[1]));
				var newElements = new Element('div', {'class': 'temp_response_container'});
				newElements.update(transport.responseText.match(/<div class="wrapper">([\s\S]*)<\/div><!--.wrapper-->/)[1]);
				document.body.appendChild(newElements);
			
			this.ELEMENTS_TO_UPDATE.each(function(selector) {
			  var new_element = $$('.temp_response_container '+selector)[0].remove();
			  var old_element = $(selector);
			  $$(selector)[0].replace(new_element);
			}.bind(this));
			
			if (FJ.ajaxyforms())
				FJ.ajaxyforms().activate_ajaxy_forms();
			
			FJ.display().scroll_to_top();
			
			$$('.temp_response_container').remove();
			
		},
		
		request_failure:function(response) {
			// TODO
			log("_request failed:");
			log(response);
		},
		
		display_loader: function(parent_element) {
			if (parent_element !== undefined) {
				if ($(parent_element).down('.loading') === undefined) {
					$(parent_element).appendChild(new Element("div", {"class": "loading", "style": 'display: none'}).update(new Element('div', {"class": "message", "id": "loading_message"})));
					swfobject.embedSWF(
						THEMEBASE + "/swfs/spinner.swf",
						"loading_message",
						"50",
						"50",
						"9",
						THEMEBASE + "/expressinstall.swf",
						{},
						{ "wmode": "transparent" },
						{ "class": "loading_swf" }
					);
				}
				
				if (Prototype.Browser.IE) {
					$$('.loading').each(function(elem) {
						elem.setStyle({'display': 'block'});
					});
				} else {
					$$('.loading').each(function(elem) {
						new Effect.Appear(elem, {duration : 0.5});
					});
				}
				
			}
		},
		
		hide_loader: function() {
			if (this._loader_timeout) window.clearTimeout(this._loader_timeout);
			var loaders = $$('.loading');
			if (loaders !== 'undefined') {
				loaders.each(function(elem) {
					new Effect.Fade(elem, {duration : 0.3});
					if (elem && elem.parentNode !== undefined) {
						Element.remove.delay(0.5, elem);
					}
				});
			}
		},
		
		track_link:function() {
			if (document.pageTracker) document.pageTracker._trackPageview();
		},
		
		parseXML: function(string) {
			try {
				xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
				xmlDoc.async = false;
				xmlDoc.loadXML(string);
				return xmlDoc;
			}
			catch (error_ie) {
				parser = new DOMParser();
				xmlDoc = parser.parseFromString(string, "text/xml");
				return xmlDoc;
			}
		},
		decode_html_entities:function(str) {
			try {
				var tarea=document.createElement('textarea');
				tarea.innerHTML = str; 
				return tarea.value.replace(/\s+/g, ' ');
				//tarea.parentNode.removeChild(tarea);
			} catch(e) {
				//for IE add <div id="htmlconverter" style="display:none;"></div> to the page
				document.getElementById("htmlconverter").innerHTML = '<textarea id="innerConverter">' + str + '</textarea>';
				var content = document.getElementById("innerConverter").value;
				document.getElementById("htmlconverter").innerHTML = "";
				return content;
			}
		}
		
	});
	
	// mixin modules
	FJ.Async.addMethods(FJ.Modules.BaseModule);
	FJ.Async.addMethods(FJ.Modules.InstanceRegistration);
	
	// singleton
	FJ.async = function () {
		var instance = FJ.get_singleton("Async");
		if (instance == null) instance = FJ.set_singleton("Async", new FJ.Async());
		return instance;
	};
	
	FJ.async();
}