/**
 *	@description
 *	Класс для подписки на различные события страницы.
 *	Поддерживается 4 типа событий:
 *		- windowResize - имзменение размеров окна браузера
 *		- fontChange - имзменение размера шрифта браузера
 *		- layoutChange - изменение высоты контента страницы
 *		- reflow - любое из трёх вышеперечисленных событий
 *
 * 	@requires jQuery (http://jquery.com/)
 *	@author Alexander Samilyak (aleksam@design.ru)
 *	@copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 *	@example
 *	function fOne(){ alert("Window has been resized"); }
 *	function fTwo(){ alert("Font size has changed"); }
 *	function fThree(){ alert("Something happended"); }
 *	
 *	Reflow.addListener("windowResize", fOne); // добавляем обработчики событий
 *	Reflow.addListener("fontChange", fTwo);
 *	Reflow.addListener("reflow", fThree);
 *	
 *	Reflow.removeListener("fontChange", fTwo); // мы передумали и решили убрать один обработчик
 */

Reflow = function(){
	
	var
		EVENTS = {
			reflow : 1, windowResize : 1, fontChange : 1, layoutChange : 1
		},
		QUERY_INTERVAL = 200; // мс

	var
		eMeasurer,
		eOuter;
	
	var
		aListeners = [],
		iFontSize,
		iLayoutHeight;
	


	$(document).ready(init);
		
	function init(){
	 	initMeasurer();
		initOuter();
		initStartValues();
		attachEvents();
	}
	
	function initMeasurer(){
	 	var
			sMeasurerId = "measurer",
			jOldMeasurer = $("#" + sMeasurerId);
			
		if(jOldMeasurer.size()){
			eMeasurer = jOldMeasurer[0];
		}
		else{
			$(document.body).append(
				'<div id="' + sMeasurerId + '" style="position:absolute; left:-10000px; top:0; height:1em;"></div>'
			);
			eMeasurer = $("#" + sMeasurerId)[0];
		}
	}
	
	function initOuter(){
	 	var jOuter = $("#outer");
		eOuter = jOuter.size() ? jOuter[0] : null;
	}
	
	function initStartValues(){
	 	iFontSize = eMeasurer.offsetHeight;
		if(eOuter){
			iLayoutHeight = eOuter.offsetHeight;
		}
	}
	
	function attachEvents(){
	 	attachResize();
		attachFontChange();
		if(eOuter){
			attachLayoutChange();
		}
	}
	
	function attachResize(){
	 	$(window).resize(
			function(){
				broadcast("windowResize");
				broadcast("reflow");
			}
		);
	}
	
	function attachFontChange(){
	 	setInterval(
			function(){ 	
				var iCurrentFontSize = eMeasurer.offsetHeight;
				if(iCurrentFontSize != iFontSize){
					iFontSize = iCurrentFontSize;
					broadcast("fontChange");
					broadcast("reflow");
				}
			},
			QUERY_INTERVAL
		);
	}
	
	function attachLayoutChange(){
	 	setInterval(
			function(){
				var iCurrentLayoutHeight = eOuter.offsetHeight;
				if(iCurrentLayoutHeight != iLayoutHeight){
					iLayoutHeight = iCurrentLayoutHeight;
					broadcast("layoutChange");
					broadcast("reflow");
				}
			},
			QUERY_INTERVAL
		);
	}
	
	function broadcast(sEventType){
		each(
			sEventType,
			function(){
				if(this instanceof Function){
					this(sEventType);
				}
				else if(this.update instanceof Function){
					this.update(sEventType);
				}
			}
		);
	}
	
	function each(sEventType, fCall){
		var
			aEventListeners = aListeners[sEventType];
		
		if(aEventListeners){
			for(var i = 0, iLength = aEventListeners.length; i < iLength; i++){
				var mReturn = fCall.call(aEventListeners[i], i);
				if(mReturn === false){
					break;
				}
			}
		}
	}
	
	return {
		
		addListener : function(sEventType, mListener){
		 	if(
				!(mListener instanceof Object) ||
				!EVENTS[sEventType]
			){
				return;
			}
			if(!aListeners[sEventType]){
				aListeners[sEventType] = [];
			}
			
			aListeners[sEventType].push(mListener);
		},
		
		removeListener : function(sEventType, mListener){
			var aWithoutListenersToRemove = [];
			
			each(
				sEventType,
				function(i){
					if(this !== mListener){
						aWithoutListenersToRemove.push(this);
					}
				}
			);
			
			if(aListeners[sEventType]){
				aListeners[sEventType] = aWithoutListenersToRemove;
			}
		}
		
	}
	
}();