/**
 * Оджинали аппиаред ин джейКоммон
 * 
 * @author Vlad Yakovlev (red.scorpix@gmail.com)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @version 0.3 alpha 7
 * @date 2009-12-29
 * @requires jQuery 1.3.2
 * 
 * Отслеживает изменение размеров окна браузера и масштабирование текста.
 * Отслеживание запускается только при добавлении первого хэндлера.
 *
 * @example
 * function funcBind() { alert('yoop'); }
 * measurer.bind(funcBind);
 * @description Теперь функция будет выполняться всякий раз, когда изменится размер окна браузера или размер текста.
 * measurer.unbind(funcBind);
 * @description А теперь — нет.
 *
 * @version 1.0
 */
$measurer = function() {

	var
		callbacks = [],
		interval = 500,
		curHeight,
		el = null,
		isInit = false,
		isDocReady = false;

	$(function() {
		isDocReady = true;
		isInit && initBlock();
	});

	function createBlock() {
		if (el == null) {
			el = $('<div></div>').css('height', '1em').css('left', '0').css('lineHeight', '1em').css('margin', '0').
			css('position', 'absolute').css('padding', '0').css('top', '-1em').css('visibility', 'hidden').
			css('width', '1em').appendTo('body');

			curHeight = el.height();
		}
	}

	function getHeight() {
		return curHeight;
	}

	function initBlock() {
		createBlock();

		$(window).resize(callFuncs);

		/**
		 * В IE событие <code>onresize</code> срабатывает и на элементах.
		 */
		if ($.browser.msie) {
			el.resize(callFuncs);
			return;
		}

		/**
		 * Для остальных браузеров периодически проверяем изменение размера текста.
		 */
		curHeight = el.height();
		setInterval(function() {
			var newHeight = el.height();

			if (newHeight != curHeight) {
				curHeight = newHeight;
				callFuncs();
			}
		}, interval);
	}

	function callFuncs() {
		for(var i = 0; i < callbacks.length; i++) {
			callbacks[i]();
		}
	}

	return {
		/**
		 * Ручная инициализация события изменения размеров элементов на странице.
		 */
		resize: callFuncs,

		/**
		 * Добавляет обработчик события.
		 * @param {Function} func Ссылка на функцию, которую нужно выполнить.
		 */
		bind: function(func) {
			if (!el) {
				isInit = true;
				isDocReady && initBlock();
			}

			callbacks.push(func);
		},

		/**
		 * Удаляет обработчик события.
		 */
		unbind: function(func) {
			for(var i = 0; i < callbacks.length; i++) {
				callbacks[i] == func && callbacks.splice(i, 1);
			}
		},
		
		getHeight: getHeight,
		createBlock: createBlock
	};
}();

/**
 * @author Leechy (leechy@leechy.ru)
 * @link www.artlebedev.ru
 * @requires jQuery
 *
 * Description:
 * gradientText is a jQuery plugin that paints text in gradient colors
 *
 * Usage:
 * $(selector).gradientText(config);
 *
 * config is an object contents configuraton paramenters:
 * 	{Array} colors - array of hex colors, e.g. ['#000000', '#FFFFFF'];
 * 	{Array} toProcess - array of jQuery selectors, matched elements will be toProcessed
 */

(function($){
	// Параметры по умолчанию
	$.gradientText = $.gradientText || {version: '1.0'};

	$.gradientText.conf = {
		colors: ['#5f3db6', '#c10000'],
		toProcess: []
	};

	$.gradientTextSetup = function(conf) {
		$.extend($.gradientText.conf, conf);
	};

	$.fn.gradientText = function(conf) {
		
		// already constructed --> return API
		var el = this.data("gradientText");
		if (el) { return el; }
				
		// concatinate defined conf object with the user's one
		if (!conf) {
			conf = $.gradientText.conf;
		} else {
			if (typeof(conf.colors) == 'undefined') {
				conf.colors = $.gradientText.conf.colors;
			}
		}

		var aLetters = [];

		this.each(function(i) {
			aLetters[i] = new GradientLetters($(this), conf);
			$(this).data("gradientText", aLetters[i]);	
		});
		
		$(window).load(function() {
			var iLetters_amount = aLetters.length;
			for (var i = 0; i < iLetters_amount; i++) {
				aLetters[i].update();
			}
		});

		return conf.api ? el: this; 
	};


	function GradientLetters(jContainer, conf) {
		/**
		 * 	Если плагин уже поработал над элементом,
		 * 	то заново дробить его не нужно
		 */
		if (jContainer.find('span.gr-text').size() == 0) {
			/**
			 * 	getting nodes, good enough
			 * 	to be spliced in letters
			 */
			var jTextNodes = jContainer.contents().filter(function() {
				return (this.nodeType == 3 && /\S/.test(this.nodeValue))
			}).wrap('<span class="gr-text" />');

			if (typeof(conf.toProcess) != 'undefined') {
				var tags = conf.toProcess.toString();

				if (tags) {
					jTextNodes = jContainer.find(tags).contents().filter(function() {
						return (this.nodeType == 3 && /\S/.test(this.nodeValue))
					}).wrap('<span class="gr-text" />');
				}
			}

			/**
			 * 	width of the content can be less than jContainer's width
			 * 	that's why we have to use inline wrapper like span
			 */
			jContainer.html('<span class="gr-wrap">' + jContainer.html() + '</span>');
			jContainer = jContainer.find('.gr-wrap');

			/**
			*	Оборачиваем каждую букву в span.gr-letter.
			*	Пробелы заменяем на пробел нулевой ширины
			*/
			jContainer.find('span.gr-text').each(function(){
				var aText = $(this).text().split('');
				var sHTML = '';
				var iText_amount = aText.length;

				for (var i = 0; i < iText_amount; i++) {
					if (aText[i] != ' ') {
						sHTML += '<span class="gr-letter">' + aText[i] + '</span>';
					} else {
						sHTML += '<span class="gr-letter"><span style="display:none;">&#8203;</span> </span>';
					}
				}
				$(this).html(sHTML);
			});
		}

		var jWords = jContainer.find('span.gr-text');
		var jLetters = jContainer.find('span.gr-letter');
		var iHeight = 0;

		// Convert defined hex colors to rgb-colors
		conf.RGBcolors = [];
		for (var i = 0; i < conf.colors.length; i++) conf.RGBcolors[i] = hex2Rgb(conf.colors[i]);

		/**
		 * 	Measurer — некий объект, который понимает не только когда изменяется ширина окна,
		 * 	но и когда меняется размер шрифта.
		 *
		 * 	Плагин использует:
		 *	- jcommon, если он есть;
		 *	- measurer, если нет jcommon и подключен файл с measurer,
		 *	- resize, если по какой-то причине ни того, ни другого не обнаружено.
		 */
		if (typeof($c) != 'undefined') $c.measurer.bind(updateColors);
		else if (typeof($measurer) != 'undefined') $measurer.bind(updateColors);
		else $(window).resize(updateColors);

		PaintUnderlines();

		function updateColors() {
			var iRootLeftOffset = Math.round(jContainer[0].offsetLeft),
				iRootWidth = getMaxRootWidth(iRootLeftOffset),
				jLetters_amount = jLetters.size();

			if (iRootWidth < 200) iRootWidth = 200;

			for( var i = jLetters_amount; i--; ) {
				jLetters[i].style.color = getColor(Math.round(jLetters[i].offsetLeft - iRootLeftOffset), iRootWidth);
			}
		}

		function getMaxRootWidth(iRootLeftOffset) {
			var iMaxWidth = 0;
			jWords.each(function(index) {
				var iRightEdge = Math.round(this.offsetWidth + this.offsetLeft) - iRootLeftOffset;
				if (iRightEdge > iMaxWidth) iMaxWidth = iRightEdge;
			});
			return iMaxWidth;
		}

		function getColor(iLeftOffset, iRootWidth) {
			var
				fLeft = (iLeftOffset > 0)? (iLeftOffset / iRootWidth) : 0;
			for (var i = 0; i < conf.colors.length; i++) {
				fStopPosition = (i / (conf.colors.length - 1));
				fLastPosition = (i > 0)? ((i - 1) / (conf.colors.length - 1)) : 0;

				if (fLeft == fStopPosition) {
					return conf.colors[i];
				} else if (fLeft < fStopPosition) {
					fCurrentStop = (fLeft - fLastPosition) / (fStopPosition - fLastPosition);
					return getMidColor(conf.RGBcolors[i-1], conf.RGBcolors[i], fCurrentStop);
				}
			}
			return conf.colors[conf.colors.length - 1];
		}

		function getMidColor(aStart, aEnd, fMidStop) {
			var aRGBColor = [];

			for (var i = 0; i < 3; i++) {
				aRGBColor[i] = aStart[i] + Math.round((aEnd[i] - aStart[i]) * fMidStop)
			}

			return rgb2Hex(aRGBColor)
		}


		/**
		* To paint underline of gradiented text in right colors
		* every .gr-letter element has to have css rule:
		* 	text-decoration: underline;
		* so this function searching for .gr-text that is child
		* of underlined element
		*/
		function PaintUnderlines () {
			/* When gradiented element contains underlined child */
			jContainer.find('.gr-text').each(function(){
				if ($(this).parent().css('text-decoration') == 'underline') {
					$(this).parent().find('.gr-letter').css('text-decoration', 'underline');
				}
			});

			/* When gradiented element is underlined */
			if (jContainer.parent().css('text-decoration') == 'underline') {
				jContainer.find('.gr-letter').css('text-decoration', 'underline');
			}
		}

		return {
			update: updateColors
		}
	}

	/**
	 * Преобразует HEX-представление цвета в RGB.
	 * @param {String} hex
	 * @return {Array}
	 */
	function hex2Rgb(hex) {
		if ('#' == hex.substr(0, 1)) {
			hex = hex.substr(1);
		}
		if (3 == hex.length) {
			hex = hex.substr(0, 1) + hex.substr(0, 1) + hex.substr(1, 1) + hex.substr(1, 1) + hex.substr(2, 1) + hex.substr(2, 1);
		}

		return [parseInt(hex.substr(0, 2), 16), parseInt(hex.substr(2, 2), 16), parseInt(hex.substr(4, 2), 16)];
	}

	/**
	 * Преобразует RGB-представление цвета в HEX.
	 * @param {Array} rgb
	 * @return {String}
	 */
	function rgb2Hex(rgb) {
		var s = '0123456789abcdef';

		return '#' + s.charAt(parseInt(rgb[0] / 16)) + s.charAt(rgb[0] % 16) + s.charAt(parseInt(rgb[1] / 16)) +
			s.charAt(rgb[1] % 16) + s.charAt(parseInt(rgb[2] / 16)) + s.charAt(rgb[2] % 16);
	}
})( jQuery );
