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) {
 //alert(corners_ss.cssRules.length); 
 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;})();
