var rocon=(function(){/**
 * Общие методы и свойства для rocon
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 */

var re_rule = /\.rc(\d+)\b/,
	re_class = /\brc(\d+)\b/,
	re_shape_flag = /\brc-shape\b/,

	/** Префиск для создаваемых CSS-правил */
	rule_prefix = 'rocon__',

	/** Базовый класс для создаваемых элементов */
	base_class = 'rocon',
	
	/** Привязанные к определенным классам фоны */
	binded_props = [],

	/** Результат, возвращаемый в объект <code>rocon</code> */
	result = {
		/**
		 * Добавление/обновление уголков для динамически созданных элементов.
		 * Может принимать неограниченное количество элементов либо массивов
		 * элементов, у которых нужно обновить уголки  
		 */
		update: function(){},
		bindProperties: function(){
			var id = 1;
			return function(rule, bg, border_width) {
				binded_props.push({
					'id': id++,
					'rule': rule, 
					'bg': mapArray(expandProperty(bg), function(val){
						if (val.charAt(0) != '#')
							val = '#' + val;
						return convertColorToHex(val);
					}),
					'border_width': border_width || 0
				});
			}
		}(),
		
		process: function(context) {
			processRoundedElements(context);
		}
	},

	/** @type {CSSStyleSheet} Таблица стилей для уголков */
	corners_ss = null,

	/** Кэш для уголков */
	_corner_cache = {},
	
	/** Классы элементов, которым нужно добавить скругленные уголки */
	elem_classes = [],
	
	/** Список функций, которые нужно выполнить при загрузке DOM-дерева */
	dom_ready_list = [],
	
	/** Загрузился ли DOM? */
	is_ready = false,
	
	/** Привязано ли событие, ожидающее загрузку DOM? */
	readyBound = false,

	userAgent = navigator.userAgent.toLowerCase(),
	
	/** 
	 * CSS-селекторы, которые уже были добавлены в стили. 
	 * Используется для того, чтобы не создавать одинаковые правила
	 */
	processed_rules = {},

	/** Тип и версия браузера пользователя. Взято с jQuery */
	browser = {
		version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [])[1],
		safari: /webkit/.test( userAgent ),
		opera: /opera/.test( userAgent ),
		msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
		mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
	};

/**
 * Выполняет все функции, добавленные на событие onDomContentLoaded.
 * Взято с jQuery
 */
function fireReady() {
	//Make sure that the DOM is not already loaded
	if (!is_ready) {
		// Remember that the DOM is ready
		is_ready = true;

		// If there are functions bound, to execute
		if ( dom_ready_list.length ) {
			
			for (var i = 0; i < dom_ready_list.length; i++) {
				dom_ready_list[i].call(document);
			}
			
//			walkArray(dom_ready_list, function(){
//				this.call(document);
//			});

			// Reset the list of functions
			dom_ready_list = null;
		}
	}
}

/**
 * Добавляет слушателя на событие onDomContentLoaded
 * @type {Function} fn Слушатель
 */
function addDomReady(fn) {
	dom_ready_list.push(fn);
}

/**
 * Проверка на наступление события onDomContentLoaded. 
 * Взято с jQuery
 */
function bindReady(){
	if ( readyBound ) return;
	readyBound = true;

	// Mozilla, Opera and webkit nightlies currently support this event
	if ( document.addEventListener ) {
		// Use the handy event callback
		document.addEventListener( "DOMContentLoaded", function(){
			document.removeEventListener( "DOMContentLoaded", arguments.callee, false );
			fireReady();
		}, false );

	// If IE event model is used
	} else if ( document.attachEvent ) {
		// ensure firing before onload,
		// maybe late but safe also for iframes
		document.attachEvent("onreadystatechange", function(){
			if ( document.readyState === "complete" ) {
				document.detachEvent( "onreadystatechange", arguments.callee );
				fireReady();
			}
		});

		// If IE and not an iframe
		// continually check to see if the document is ready
		if ( document.documentElement.doScroll && !window.frameElement ) (function(){
			if ( is_ready ) return;

			try {
				// If IE is used, use the trick by Diego Perini
				// http://javascript.nwbox.com/IEContentLoaded/
				document.documentElement.doScroll("left");
			} catch( error ) {
				setTimeout( arguments.callee, 0 );
				return;
			}

			// and execute any waiting functions
			fireReady();
		})();
	}
}

/**
 * Вспомогательная функция, которая пробегается по всем элементам массива
 * <code>ar</code> и выполняет на каждом элементе его элементе функцию
 * <code>fn</code>. <code>this</code> внутри этой функции указывает на 
 * элемент массива
 * @param {Array} ar Массив, по которому нужно пробежаться
 * @param {Function} fn Функция, которую нужно выполнить на каждом элементе массива
 * @param {Boolean} forward Перебирать значения от начала массива (п умолчанию: с конца)
 */
function walkArray(ar, fn, forward) {
	if (forward) {
		for (var i = 0, len = ar.length; i < len; i++)
			if (fn.call(ar[i], i) === false)
				break;
	} else {
		for (var i = ar.length - 1, result; i >= 0; i--)
			if (fn.call(ar[i], i) === false)
				break;
	}
}

/**
 * Преобразует один массив элементов в другой с помощью функции callback.
 * Взято в jQuery
 * @param {Array} elems
 * @param {Function} callback
 * @return {Array}
 */
function mapArray(elems, callback) {
	var ret = [];

	// Go through the array, translating each of the items to their
	// new value (or values).
	for ( var i = 0, length = elems.length; i < length; i++ ) {
		var value = callback( elems[ i ], i );

		if ( value != null )
			ret[ ret.length ] = value;
	}

	return ret.concat.apply( [], ret );
}

/**
 * Функция добавления скругленных уголков элементу. Для каждого браузера 
 * будет своя функция
 */
function addCorners(){
	return;
};

// TODO Добавить исключение при правильной работе border-radius

/**
 * Преобразует цвет из RGB-предствления в hex
 * @param {String} color
 * @return {String}
 */
function convertColorToHex(color) {
	var result;
	function s(num) {
		var n = parseInt(num, 10).toString(16);
		return (n.length == 1) ? n + n : n;
	}
	
	function p(num) {
		return s(Math.round(num * 2.55));
	}
	
	if (result = /rgb\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*\)/.exec(color))
		return '#' + s(result[1]) + s(result[2]) + s(result[3]);

	// Look for rgb(num%,num%,num%)
	if (result = /rgb\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*\)/.exec(color))
		return '#' + p(result[1]) + p(result[2]) + p(result[3]); 

	// Look for #a0b1c2
	if (result = /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/i.exec(color))
		return '#' + result[1] + result[2] + result[3];
		
	if (result = /#([a-f0-9])([a-f0-9])([a-f0-9])/i.exec(color))
		return '#' + result[1] + result[1] + result[2] + result[2] + result[3] + result[3];
	
	s = null;
	p = null;
		
	return color;
}

/**
 * Создает HTML-элемент <code>name</code> с классом <code>class_name</code>
 * @param {String} name Название элемента
 * @param {String} class_name Класс элемента
 * @return {Element}
 */
function createElement(name, class_name) {
	var elem = document.createElement(name);
	if (class_name) {
		elem.className = class_name;
	}
	return elem;
}

/**
 * Простая проверка наличия определенного класса у элемента
 * @param {HTMLElement} elem
 * @param {String} class_name
 * @return {Boolean}
 */
function hasClass(elem, class_name) {
	var re = new RegExp('\\b' + class_name + '\\b');
	return elem.nodeType == 1 && re.test(elem.className || '');
}

/**
 * Возвращает значение CSS-свойства <b>name</b> элемента <b>elem</b>
 * @author John Resig (http://ejohn.org)
 * @param {Element} elem Элемент, у которого нужно получить значение CSS-свойства
 * @param {String|Array} name Название CSS-свойства
 * @return {String|Object}
 */
function getStyle(elem, name) {
	var cs, 
		result = {},
		camel = function(str, p1){return p1.toUpperCase();};
	
	walkArray(name instanceof Array ? name : [name], function(){
		var n = this, 
			name_camel = n.replace(/\-(\w)/g, camel);
		
		// If the property exists in style[], then it's been set
		// recently (and is current)
		if (elem.style[name_camel]) {
			result[name_camel] = elem.style[name_camel];
		}
		//Otherwise, try to use IE's method
		else if (browser.msie) {
			result[name_camel] = elem.currentStyle[name_camel];
		}
		// Or the W3C's method, if it exists
		else if (document.defaultView && document.defaultView.getComputedStyle) {
			if (!cs)
				cs = document.defaultView.getComputedStyle(elem, "");
			result[name_camel] = cs && cs.getPropertyValue(n);
		}
	});
	
	return name instanceof Array ? result : result[name.replace(/\-(\w)/g, camel)];
		
}

/**
 * Разворачивает краткую запись четырехзначного свойства в полную:<br>
 * 	— a      -&gt; a,a,a,a<br>
 *	— a_b    -&gt; a,b,a,b<br>
 *	— a_b_с  -&gt; a,b,с,b<br>
 * 
 * @param {String} prop Значение, которое нужно раскрыть
 * @return {Array} Массив с 4 значениями
 */
function expandProperty(prop) {
	var chunks = (prop || '').split('_');
		
	switch (chunks.length) {
		case 1:
			return [chunks[0], chunks[0], chunks[0], chunks[0]];
		case 2:
			return [chunks[0], chunks[1], chunks[0], chunks[1]];
		case 3:
			return [chunks[0], chunks[1], chunks[2], chunks[1]];
		case 4:
			return chunks;
	}
	
	return null;
}

/**
 * Возвращает цвет фона элемента
 * @type {Function}
 * @param {Element} elem Элемент, для которого нужно достать цвет фона
 * @param {Boolean} use_shape Для элемента создаются уголки в виде формы
 * @return {Array} Массив из 4 элементов фона
 */
var getBg = (function() {

	var session_elems = [], 
		default_color = '#ffffff';
	
	/**
	 * Основной цикл с использованием кэширования
	 */
	function mainLoopCache(elem) {
		var c;
		do {
			if (elem.nodeType != 1)
				break;
			
			if (elem.rocon_bg) { // цвет был найден ранее
				return elem.rocon_bg;
			} else { // цвет еще не найден
				session_elems.push(elem);
				c = getStyle(elem, 'background-color');
				if (c != 'transparent')
					return convertColorToHex(c);
			}
				
		} while (elem = elem.parentNode);
		
		return default_color;
	}
	
	/**
	 * Основной цикл без кэширования
	 */
	function mainLoopNoCache(elem) {
		var c;
		do {
			if (elem.nodeType != 1)
				break;
				
			c = getStyle(elem, 'background-color');
			if (c != 'transparent')
				return convertColorToHex(c);
				
		} while (elem = elem.parentNode);
		
		return default_color;
	}
	
	return function(elem, use_shape){
		var cl = /* String */elem.className, 
			bg = null;
		
		// сначала посмотрим, указан ли фон в классе элемента
		var bg_props = /\brcbg([a-f0-9_]+)\b/i.exec(cl);
		
		if (bg_props) {
			
			bg =  mapArray(expandProperty(bg_props[1]), function(el){
				return convertColorToHex('#' + el);
			});
			
			return bg;
		}
		
		// Теперь проверяем, есть ли привязанный через rocon.bindBg() к классу фон
		var elem_props = getBindedProperties(elem);
		if (elem_props) {
			return elem_props.bg;
		}
		
		if (!use_shape) 
			elem = elem.parentNode;
		
		if (getBg.use_cache) {
			session_elems = [];
			bg = mainLoopCache(elem);
			// закэшируем цвет фона у всех элементов, по которым проходились
			walkArray(session_elems, function(){
				this.rocon_bg = bg;
				getBg.processed_elems.push(this);
			});
			
			session_elems = null;
		} else {
			bg = mainLoopNoCache(elem);
		}
		
		return expandProperty(bg);
	}
})();

getBg.use_cache = true;
getBg.processed_elems = [];

function getBindedProperties(elem) {
	var cl = elem.className, result = null;
	walkArray(binded_props, function(){
		if (
			// проверка наличия подстроки
			(typeof(this.rule) == 'string' && cl.indexOf(this.rule) != -1) ||
			// проверка по регулярке
			cl.search(this.rule) != -1
		) {
			result = this;
			return false;
		}
	}, true);
	
	return result;
}

/**
 * Добавляет CSS-правило в стиль
 * @param {String} selector CSS-селектор, для которого нужно добавить правила
 * @param {String} rules CSS-правила
 */
function addRule(selector, rules) {
	corners_ss.insertRule(selector + ' {' + rules + '}', corners_ss.cssRules.length);
}

/**
 * Функция поиска правил для скругленных уголков
 * @param {Function} addFunc Функция добавления уголков
 */
function findRules(addFunc) {
	/** @type {String[]}  */
	var match;
	
	walkArray(document.styleSheets, function(){
		walkArray(this.cssRules || this.rules, function(){
			if (match = re_rule.exec(this.selectorText))
				addFunc(this, parseInt(match[1], 10));
		});
	});
}

/**
 * Очищает элемент от предыдущих вставок скругленных уголков
 * @param {Element} elem
 * @param {String} [add_class] Классы, которые нужно добавить
 * @return {Element} Переданный элемент
 */
function cleanUp(elem, add_class) {
	var	cl = (elem.className || '').replace(new RegExp('\\s*' + base_class + '[\-_].+?\\b', 'ig'), '');
	if (add_class) {
		cl += ' ' + add_class;
	}
	
	elem.className = cl;
	return elem;
}

	
/**
 * Функция добавления правил для скругленных уголков
 */
function addRoundedProperties(/* CSSStyleRule */ rule, /* Number */ radius) {
	elem_classes.push(rule.selectorText.substr(1));
}

/**
 * Создает новую таблицу стилей на странице, куда будут добавляться правила
 * для описания скругленных уголков
 * @return {CSSStyleSheet}
 */
function createStylesheet() {
	if (!corners_ss) {
		if (document.createStyleSheet) {
			corners_ss = document.createStyleSheet();
		} else {
			var style = createElement('style');
			style.rel = 'rocon';
			document.getElementsByTagName('head')[0].appendChild(style);
			
			/*
			 * Просто получить самый последний стиль не получится: иногда стили
			 * добавляются внутрь <body> (так делает счетчик Яндекса, например),
			 * в этом случае мы не можем быть уверены, что только что 
			 * добавленная таблица стилей — последняя. Поэтому пробегаетмся 
			 * по всем таблицам в поисках нашей  
			 */ 
			walkArray(document.styleSheets, function(){
				if (this.ownerNode.rel == 'rocon') {
					corners_ss = this;
					return false;
				}
			});
		}
	}
	
	return corners_ss;
}

/**
 * Возвращает массив элементов, которым нужно добавить скругленные уголки.
 * Элементом массива является объект со свойствами <code>node</code> 
 * и <code>radius</code>
 * @param {Element} [context] Откуда брать элементы
 * @return {Array}
 */
function getElementsToProcess(context) {
	var elems = [], m;
	
	walkArray((context || document).getElementsByTagName('*'), function(){
		if (m = re_class.exec(this.className || '')) {
			elems.push({node: this, radius: parseInt(m[1], 10)});
		}
	});
	
	return elems;
}

/**
 * Обрабатывает все элементы на странице, которым нужно добавить скругленные
 * уголки
 */
function processRoundedElements(context){
	var elems = getElementsToProcess(context);
	if (elems.length) {
		createStylesheet();
		walkArray(elems, function(){
			addCorners(this.node, this.radius);
		});
	}
}
/**
 * Проверяет, был ли добавлен CSS-selector в таблицу стилей
 * @param {String} selector
 * @return {Boolean}
 */
function isProcessed(selector) {
	return processed_rules[selector] ? true : false;
}

/**
 * Возвращает параметры уголка элемента
 * @param {Element} elem Элемент, у которого нужно получить параметры уголка
 * @param {Number} [radius] Радиус скругления
 */
function getCornerParams(elem, radius) {
	var cl = elem.className || '';
	radius = radius || parseInt(cl.match(re_class)[1], 10);
	var use_shape = re_shape_flag.test(cl),
		props = getBindedProperties(elem);
	
	var border_color = '';
	var border_width = props ? props.border_width : (parseInt(getStyle(elem, 'border-left-width')) || 0);
	if (border_width) {
		// нужно отрисовать бордюр
		border_color = convertColorToHex(getStyle(elem, 'border-left-color') || '#000');
	}
	
	return {
		'radius': radius,
		'bg_color': getBg(elem, use_shape),
		
		// толщина бордюра не может быть больше радиуса скругления
		// (так по CSS3 спецификации)
		'border_width': (border_width > radius) ? radius : border_width,
		'real_border_width': border_width,
		'border_color': border_color,
		'use_shape': use_shape
	};
}

/**
 * Применяет уголки к элементам, переданным в массиве. В основном вызывается из
 * <code>rocon.update()</code>
 * @param {arguments} args Аргументы функции
 * @param {Function} fn Функция, которую нужно выполнить на каждом элементе
 */
function applyCornersToArgs(args, fn) {
	walkArray(args, function(){
		walkArray((this instanceof Array) ? this : [this], fn);
	});
}

/**
 * Делает копию объекта
 * @param {Object} obj
 * @return {Object}
 */
function copyObj(obj) {
	var result = {};
	for (var p in obj) 
		if (obj.hasOwnProperty(p))
			result[p] = obj[p];
	
	return result;
}

/**
 * Корректирует CSS-свойства элемента для правильного рисования уголков в виде
 * формы
 * @param {HTMLElement} elem Элемент, который нужно подкорректировать
 * @param {String} class_name Имя создаваемого класса
 * @param {getCornerParams()} options параметры рисования уголка
 */
function adjustBox(elem, class_name, options) {
	var elem_styles = getStyle(elem, ['padding-top', 'padding-bottom', 'margin-top', 'margin-bottom']);
	function getProp(prop) {
		return parseInt(elem_styles[prop], 10) || 0;
	}
	
	/*
	 * Используем форму, поэтому у блока снижаем верхние и нижние
	 * бордюры, а также на величину радиуса снижаем верхний 
	 * и нижний паддинг 
	 */
	
	var padding_top = Math.max(getProp('paddingTop') - options.radius + options.border_width, 0),
		padding_bottom = Math.max(getProp('paddingBottom') - options.radius + options.border_width, 0),
		margin_top = getProp('marginTop') + options.radius,
		margin_bottom = getProp('marginBottom') + options.radius,
		border_width = options.real_border_width - options.border_width;
	
	addRule('.' + class_name, 
			'border-top-width:' + border_width + 'px;' +
			'border-bottom-width:' + border_width + 'px;' +
			'padding-top:' + padding_top + 'px;' +
			'padding-bottom:' + padding_bottom + 'px;' +
			'margin-top:' + margin_top + 'px;' +
			'margin-bottom:' + margin_bottom + 'px' );
}

addDomReady(processRoundedElements);
// после того, как добавили уголки, необходимо очистить кэш фона,
// иначе будут проблемы с динамическим обновлением блоков
addDomReady(function(){
	walkArray(getBg.processed_elems, function(){
		this.removeAttribute('rocon_bg');
	});
	getBg.use_cache = false;
});

bindReady();/**
 * Добавление уголков для Safari
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @include "common.js"
 */

if (browser.safari) {
	addCorners = function(elem, radius) {
		var selector = '.rc' + radius;
		if (!isProcessed(selector)) {
			addRule(selector, '-webkit-border-radius:' + radius + 'px; -khtml-border-radius:' + radius);
			processed_rules[selector] = true;
		}
	}
	
	result.update = function() {
		applyCornersToArgs(arguments, function(){
			var m = re_class.exec(this.className || '');
			if (m) 
				addCorners(this, parseInt(m[1]));
		});
	}
}/**
 * Добавление уголков для Firefox
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @include "common.js"
 */

if (browser.mozilla) {
	addCorners = function(elem, radius) {
		var selector = '.rc' + radius;
		if (!isProcessed(selector)) {
			addRule(selector, '-moz-border-radius:' + radius + 'px');
			processed_rules[selector] = true;
		}
	}
	
	result.update = function() {
		applyCornersToArgs(arguments, function(){
			var m = re_class.exec(this.className || '');
			if (m) 
				addCorners(this, parseInt(m[1]));
		});
	}
}/**
 * Добавление уголков для Opera
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @include "common.js"
 * @include "/js-libs/canvas-doc.js"
 */
 
if (browser.opera) {
	/*
	 * Нужно дожаться, пока загрузится DOM-дерево, после чего получить все 
	 * элементы, которым нужно скруглить уголки, и добавить соотвествующие 
	 * стили и элементы
	 */
	
	createStylesheet();
	addRule('.' + base_class, 'position:absolute;background-repeat:no-repeat;z-index:1;display:none');
	addRule('.' + base_class + '-init', 'position:relative;');
	addRule('.' + base_class + '-init>.' + base_class, 'display:inline-block;');
	addRule('.' + base_class + '-tl', 'top:0;left:0;background-position:100% 100%;');
	addRule('.' + base_class + '-tr', 'top:0;right:0;background-position:0 100%;');
	addRule('.' + base_class + '-bl', 'bottom:0;left:0;background-position:100% 0;');
	addRule('.' + base_class + '-br', 'bottom:0;right:0;');
	
	/** @type {HTMLCanvasElement} Холст, на котором будут рисоваться уголки */
	var cv = createElement('canvas');
	
	/**
	 * Возвращает подготовленный контекст рисования на холсте
	 * @param {getCornerParams()} options Параметры рисования уголка
	 * @param {Boolean} is_shape Будем рисовать форму (true) или контр-форму (false)?
	 * @return {CanvasRenderingContext2D}
	 */
	function getDrawingContext(options) {
		options.border_width = (options.border_width > options.radius) 
				? options.radius 
				: options.border_width;
		
		if (options.border_width > 1)
			options.radius -= options.border_width / 2;
			
		
		var width = options.radius * 2 + options.border_width, height = width;
		if (options.use_shape) {
			width = 2000;
			if (options.border_width < options.real_border_width) {
				height += (options.real_border_width - options.border_width) * 2;
			}
		}
		
		
		if (options.border_width == 1) {
			width--;
			height--;
		}
		
		cv.width = options.width = width;
		cv.height = options.height = height;
		
		/** @type {CanvasRenderingContext2D} */
		var ctx = cv.getContext('2d');
		
		ctx.strokeStyle = options.border_color;
		ctx.lineWidth = options.border_width;
		ctx.lineJoin = 'miter';
		ctx.lineCap = 'square';
		ctx.fillStyle = options.bg_color[0];
		
		ctx.clearRect(0, 0, width, height);
		return ctx;
	}
	
	/**
	 * Делает обводку в виде звездочки
	 * @param {CanvasRenderingContext2D} ctx Контекст рисования
	 * @param {Number} options.radius Радиус скругления
	 * @param {String} options.color Цвет уголка в hex-формате
	 * @param {Number} options.border_width Толщина обводки
	 * @param {String} options.border_color Цвет обводки
	 */
	function strokeStar(ctx, options) {
		var deg90 = Math.PI / 2, 
			b2 = (options.border_width > 1) ? options.border_width : 0,
			rb2 = options.radius * 2 + b2;
		
		ctx.beginPath();
		ctx.arc(0, 0, options.radius, deg90, 0, true);
		ctx.stroke();
		
		ctx.beginPath();
		ctx.arc(rb2, 0, options.radius, deg90 * 2, deg90, true);
		ctx.stroke();
		
		ctx.beginPath();
		ctx.arc(rb2, rb2, options.radius, -deg90, deg90 * 2, true);
		ctx.stroke();
	
		ctx.beginPath();
		ctx.arc(0, rb2, options.radius, 0, -deg90, true);
		ctx.stroke();
	}
	
	/**
	 * Рисует «звездочку» для создания формы уголков через canvas
	 * @param {Number} options.radius Радиус скругления
	 * @param {String} options.color Цвет уголка в hex-формате
	 * @param {Number} options.border_width Толщина обводки
	 * @param {String} options.border_color Цвет обводки
	 * @return {String} Картинка в формате data:URL
	 */
	function drawStarShape(options) {
		options = copyObj(options);
		
		var ctx = getDrawingContext(options),
			deg90 = Math.PI / 2, 
			deg360 = Math.PI * 2,
			bw = options.border_width,
			b2 = (bw > 1) ? bw : 0,
			rb2 = options.radius * 2 + b2,
			diff = 0,
			draw_borders = (options.border_width < options.real_border_width);
			
		var drawCircle = function(x, y) {
			ctx.beginPath();
			ctx.arc(x, y, options.radius, 0, deg360, true);
			ctx.closePath();
			ctx.fill();
		}
		
		if (draw_borders) {
			// нужно дорисовать толщину бордера
			diff = options.real_border_width - options.border_width;
			ctx.save();
			ctx.translate(0, diff);
		}
		
		drawCircle(0, 0);
		drawCircle(rb2, 0);
		drawCircle(rb2, rb2);
		drawCircle(0, rb2);
		
		ctx.fillRect(rb2, 0, options.width, options.height);
		
		if (bw) {
			strokeStar(ctx, options);
			ctx.fillStyle = ctx.strokeStyle;
			ctx.fillRect(rb2, options.radius - (bw > 1 ? bw / 2 : bw), options.width, bw * 2);
			
			if (draw_borders) {
				ctx.restore();
				ctx.fillStyle = options.border_color;
				ctx.fillRect(0, 0, options.width, diff);
				ctx.fillRect(0, options.height - diff, options.width, diff);
				ctx.fillStyle = options.bg_color;
			}
		}
		
		return ctx.canvas.toDataURL();
	}
	
	/**
	 * Рисует «звездочку» через canvas
	 * @param {Number} options.radius Радиус скругления
	 * @param {String} options.color Цвет уголка в hex-формате
	 * @param {Number} options.border_width Толщина обводки
	 * @param {String} options.border_color Цвет обводки
	 * @return {String} Картинка в формате data:URL
	 */
	function drawStar(options) {
		var old_opt = options;
		options = copyObj(options);
		
		var ctx = getDrawingContext(options),
			radius = options.radius,
			b2 = (options.border_width > 1) ? options.border_width : 0,
			rb2 = radius * 2 + b2,
			r = old_opt.radius,
			deg90 = Math.PI / 2;

		ctx.save();
		ctx.beginPath();
		ctx.arc(0, 0, radius, deg90, 0, true);
		ctx.arc(rb2, 0, radius, deg90 * 2, deg90, true);
		ctx.arc(rb2, rb2, radius, -deg90, deg90 * 2, true);
		ctx.arc(0, rb2, radius, 0, -deg90, true);
		ctx.closePath();
		ctx.clip();
	
		
		ctx.fillStyle = options.bg_color[2];
		ctx.fillRect(0, 0, r, r)
		
		ctx.fillStyle = options.bg_color[3];
		ctx.fillRect(r, 0, r, r);
		
		ctx.fillStyle = options.bg_color[0];
		ctx.fillRect(r, r, r, r);
		
		ctx.fillStyle = options.bg_color[1];
		ctx.fillRect(0, r, r, r);
		ctx.restore();
		
		if (options.border_width)
			strokeStar(ctx, options);
		
		return ctx.canvas.toDataURL();
	}
	
	/**
	 * Возвращает ключ, по которому кэшируются отрисованные элементы
	 * @param {getCornerParams()} cparams Параметры скругления блока
	 * @param {HTMLElement} elem Элемент, для которого делаем скругление
	 * @return {String}
	 */
	function getCacheKey(cparams, elem) {
		var binded = getBindedProperties(elem);
		return [
			cparams.radius, 
			cparams.bg_color.join('-'), 
			cparams.real_border_width, 
			cparams.border_color, 
			cparams.use_shape,
			binded ? binded.id : 0
		].join(':');
	}
	
	/**
	 * Создает CSS-правила для уголков определенного радиуса и цвета
	 * @param {getCornerParams()} cparams Параметры скругления блока
	 * @param {HTMLElement} elem Элемент, для которого делаем скругление
	 * @return {String} Имя класса, которое нужно присвоить элементу
	 */
	function createCSSRulesOpera(cparams, elem) {
		var cache_key = getCacheKey(cparams, elem),
			radius = cparams.radius,
			bw = cparams.real_border_width || 0,
			diff = (cparams.use_shape) ? bw - cparams.border_width : 0;
		
		// смотрим, делали ли правило с такими же параметрами
		if (!_corner_cache[cache_key]) {
			// создаем новое правило
			var cur_class = rule_prefix + corners_ss.cssRules.length;
			_corner_cache[cache_key] = cur_class;
			
			addRule('.' + cur_class + '>.' + base_class, 
				'background-image: url("' + ( cparams.use_shape ? drawStarShape(cparams) : drawStar(cparams) ) + '");' +
				'width: '+ radius +'px;' +
				'height: ' + (radius + diff) + 'px;'
			);
			
			var offset_x = -bw, offset_y = -bw;
			if (cparams.use_shape) {
				offset_y = -radius - diff;
				adjustBox(elem, cur_class, cparams);
				addRule(
					'.' + cur_class + '>.' + base_class + '-tl, .' + cur_class + '>.' + base_class + '-bl', 
					'width:auto;left:0;right:'+ (radius - bw) +'px;background-position:-' + radius + 'px 100%;'
				);
				addRule('.' + cur_class + '>.' + base_class + '-bl', 'background-position:-' + radius + 'px 0;');
			}
			
			if (offset_x || offset_y) {
				addRule('.' + cur_class + '>.' + base_class + '-tl', 'top:'+ offset_y +'px; left:'+ offset_x +'px');
				addRule('.' + cur_class + '>.' + base_class + '-tr', 'top:'+ offset_y +'px; right:'+ offset_x +'px');
				addRule('.' + cur_class + '>.' + base_class + '-bl', 'bottom:'+ offset_y +'px; left:'+ offset_x +'px');
				addRule('.' + cur_class + '>.' + base_class + '-br', 'bottom:'+ offset_y +'px; right:'+ offset_x +'px');
			}
		}
		
		return _corner_cache[cache_key];
	}

	/**
	 * Добавляет уголки элементу
	 * @param {Element} elem
	 */
	addCorners = function(elem, radius){
		// если у элемента нет класса — значит, нет указания, какие уголки
		// нужно добавить
		if (!elem.className)
			return;
		
		// проверим, нужно ли добавлять элементы с уголками
		var dont_add = false;
		walkArray(elem.childNodes, function(){
			if (hasClass(this, base_class)) {
				dont_add = true;
				return false;
			}
		});
		
		var elem_class = createCSSRulesOpera(getCornerParams(elem, radius), elem);
		
		if (!dont_add) 
			// добавляем уголки
			walkArray(['tl', 'tr', 'bl', 'br'], function(){
				elem.appendChild( createElement('span', base_class + ' ' + base_class +'-' + this) );
			});
		
		cleanUp(elem, elem_class + ' ' + base_class + '-init');
	};
	
	addDomReady(function(){
		/*
		 * Одна из причин, по которой я ненавижу Оперу — это 
		 * необходимость до сих пор вставлять подобные костыли, 
		 * чтобы что-то отобразились на странице
		 */
		document.documentElement.style.outline = 'none';
	});
	
	result.update = function() {
		applyCornersToArgs(arguments, function(){
			addCorners( cleanUp(this) );
		});
	}
}/**
 * Добавление уголков для IE
 * @author Sergey Chikuyonok (sc@design.ru)
 * @copyright Art.Lebedev Studio (http://www.artlebedev.ru)
 * @include "common.js"
 */

if (browser.msie) {
	/*
	 * Уголки для IE создаем через VML.
	 * 
	 * У IE в этом скрипте есть одно очень узкое место: динамическое добавление
	 * CSS-правил в таблицу стилей (функция addRule()). Для увеличения 
	 * производительности был применен следующий трюк: сначала, при первичной 
	 * инициализации, весь CSS накапливается в переменной css_text, и после того,
	 * как все необходимые правила для существующих блоков были созданы, 
	 * накопленный CSS применяется к созданной таблице стилей. После этого 
	 * функция addRule() уже указывает на метод corners_ss.addRule()  
	 */
	
	_corner_cache.ix = 0;
	_corner_cache.created = {};
	
	var css_text = '',
		corner_types = {
			tl: 0,
			tr: 1,
			br: 2,
			bl: 3
		};
	
	var vml_class = 'vml-' + base_class; //использую именно класс, чтобы работало в IE8
	
	try {
		if (!document.namespaces["v"])
			document.namespaces.add("v", "urn:schemas-microsoft-com:vml");
	} catch(e) { }
	
	createStylesheet();
	var dot_class = '.' + base_class;
	corners_ss.cssText = "." + vml_class + " {behavior:url(#default#VML);display:inline-block;position:absolute}" +
		dot_class + "-init {position:relative;zoom:1;}" +
		dot_class + " {position:absolute; display:inline-block; zoom: 1; overflow:hidden}" +
		dot_class + "-tl ." + vml_class + "{flip: 'y'}" +
		dot_class + "-tr ." + vml_class + "{rotation: 180;right:1px;}" +
		dot_class + "-br ." + vml_class + "{flip: 'x'; right:1px;}";
		
	if (browser.version < 7) {
		corners_ss.cssText += dot_class + '-tr, ' + dot_class + '-br {margin-left: 100%;}';
//				dot_class + ' .' + vml_class + '{position:absolute}' +
//				dot_class + '-tr .' + vml_class + '{right: 0}';
	}
	
	addRule = function(selector, rules){
		css_text += selector + '{' + rules + '}';
	};
	
	/**
	 * Создает элемент со скругленным уголком. В функции используется
	 * кэширование, то есть ранее созданный уголок дублируется, 
	 * а не создается заново
	 * @param {getCornerParams()} options Параметры рисования уголка 
	 * @return {HTMLElement}
	 */
	function createCornerElementIE(options) {
		var radius = options.radius,
			border_width = options.border_width,
			cache_key = radius + ':' + border_width + ':' + options.use_shape;
		
		if (!createCornerElementIE._cache[cache_key]) { // элемент еще не создан
			
			var multiplier = 10;
			
			var cv = createElement('v:shape');
			cv.className = vml_class;
			cv.strokeweight = border_width + 'px';
			cv.stroked = (border_width) ? true : false;
			var stroke = createElement('v:stroke');
			stroke.className = vml_class;
			stroke.joinstyle = 'miter';
			cv.appendChild(stroke);
			
			var w = radius, h = w;
			
			cv.style.width = w + 'px';
			cv.style.height = h + 'px';
			
			radius -= border_width / 2;
			radius *= multiplier;
			var bo = border_width / 2 * multiplier;
			var px = Math.round((radius + bo) / w);
			var rbo = radius + bo;
			
			cv.coordorigin = Math.round(px / 2) + ' ' + Math.round(px / 2);
			cv.coordsize = rbo + ' ' + rbo;
			
			var path = '';
			var max_width = rbo + px;
			
			if (options.use_shape) {
				max_width = 2000 * multiplier;
				path = 'm' + max_width + ',0 ns l' + bo +',0  qy' + rbo + ',' + radius + ' l' + max_width + ',' + radius + ' e ';
			} else {
				path = 'm0,0 ns l' + bo +',0  qy' + rbo + ',' + radius + ' l' + rbo + ',' + rbo + ' l0,' + rbo + ' e ';
			}
			
			
			// stroke
			path += 'm' + bo + ',' + (-px) + ' nf l' + bo + ',0 qy' + rbo + ',' + radius + ' l ' + (max_width) +','+ radius +' e x';
			
			cv.path = path;
				
			createCornerElementIE._cache[cache_key] = cv;
		}
		
		return createCornerElementIE._cache[cache_key].cloneNode(true);
	}
	
	createCornerElementIE._cache = {};
	
	/**
	 * Создает скругленный уголок
	 * @param {getCornerParams()} cparams параметры уголка
	 * @param {String} type Тип уголка (tl, tr, bl, br)
	 */
	function drawCornerIE(cparams, type){
		var cv = createCornerElementIE(cparams);
		cv.fillcolor = cparams.bg_color[corner_types[type]] || '#000';
		cv.strokecolor = cparams.border_color || '#000';
		
		var elem = createElement('span', base_class + ' ' + base_class + '-' + type);
		elem.appendChild(cv);
		
		return elem;
	}
	
	/**
	 * Удаляет у элемента старые уголки
	 * @param {HTMLElement} elem Элемент, у которого нужно удалить уголки 
	 */
	function removeOldCorners(elem) {
		walkArray(elem.childNodes, function(){
			if (hasClass(this, base_class)) {
				elem.removeChild(this);
			}
		});
		
		cleanUp(elem);
	}
	
	/**
	 * Возвращает имя класса для переданных параметров. Используется для 
	 * того, чтобы не плодить много разных классов для одних и тех же правил
	 * @param {getCornerParams()} options Параметры рисования уголка
	 * @return {String}
	 */
	function getClassName(options) {
		var key = options.radius + ':' + (options.real_border_width || 0) + ':' + options.use_shape;
		if (!_corner_cache[key]) {
			_corner_cache[key] = rule_prefix + _corner_cache.ix++;
		}
		
		return _corner_cache[key];
	}
	
	/**
	 * Создает CSS-правила для скругленных уголков
	 * @param {getCornerParams()} options Параметры рисования уголка
	 * @param {HTMLElement} elem Элемент, которому добавляются уголки
	 * @param {Number} border_width Толщина бордюра
	 */
	function createCSSRules(options, elem) {
		var radius = options.radius,
			border_width = options.real_border_width || 0,
			diff = (options.use_shape) ? options.real_border_width - options.border_width : 0;
//		border_width += 10;
		
//		corners_ss.disabled = true;
			
		var class_name = getClassName(options);
		if (!_corner_cache.created[class_name]) {
			// такое правило еще не создано в CSS, создадим его
			var prefix = (browser.version < 7) 
							? '.' + class_name + ' .' + base_class  // IE6 
							: '.' + class_name + '>.' + base_class; // IE7+
			
			var offset_x = -border_width, 
				offset_y = -1 -border_width;
			
				
			addRule(prefix, 'width:' + (radius + border_width + 1) + 'px;height:' + (radius + 1) + 'px');
			
			if (options.use_shape) {
				offset_y = -radius - 1 - diff;
				var left_adjust = radius + options.border_width * 2 + diff;
				adjustBox(elem, class_name, options);
				var clip_size = Math.max(radius - border_width * 2, 0),
					pad_size = Math.min(radius - border_width * 2, 0) * -1;
				
				if (browser.version < 7) {
					pad_size += parseInt(getStyle(elem, 'padding-left') || 0) + parseInt(getStyle(elem, 'padding-right') || 0);
				}
				
				var css_rules = 'width:100%;clip:rect(auto auto auto ' + clip_size + 'px);padding-right:' + pad_size + 'px;left:' + (-border_width - clip_size) + 'px;';
				addRule(prefix + '-tl', css_rules + 'top:' + offset_y + 'px;');
				addRule(prefix + '-tl .' + vml_class, 'left:' + clip_size + 'px');
				
				addRule(prefix + '-bl', css_rules +'bottom:' + offset_y + 'px;');
				addRule(prefix + '-bl .' + vml_class, 'left:' + clip_size + 'px');
			} else {
				addRule(prefix + '-tl', 'left:' + offset_x + 'px;top:' + offset_y + 'px;');
				addRule(prefix + '-bl', 'left:' + offset_x + 'px;bottom:' + offset_y + 'px;');
			}
			
			if (browser.version < 7) {
				offset_x = -radius + (border_width ? radius % 2 - border_width % 2 : -radius % 2);
				
				addRule(prefix + '-tr', 'left:' + offset_x + 'px;top:' + offset_y + 'px;');
				addRule(prefix + '-br', 'left:' + offset_x + 'px;bottom:' + offset_y + 'px;');
			} else {
				addRule(prefix + '-tr', 'right:' + offset_x + 'px;top:' + offset_y + 'px;');
				addRule(prefix + '-br', 'right:' + offset_x + 'px;bottom:' + offset_y + 'px;');
			}
			
			_corner_cache.created[class_name] = true;
		}
	}
	
	addCorners = function(elem, radius) {
		var cparams = getCornerParams(elem, radius);
		
		createCSSRules(cparams, elem);
		
		// теперь добавляем сами уголки в элемент
		walkArray(['tl', 'tr', 'bl', 'br'], function(){
			elem.appendChild(drawCornerIE(cparams, this));
		});
		
		
		// говорим, что все добавилось
		elem.className += ' ' + getClassName(cparams) + ' ' + base_class + '-init';
		
	};
	
	result.update = function() {
		applyCornersToArgs(arguments, function(){
			removeOldCorners(this);
			addCorners(this);
		});
	};
	
	addDomReady(function(){
		corners_ss.cssText += css_text;
		css_text = '';
		addRule = corners_ss.addRule;
	});
};return result;})();
