  initInteractive = function()
    {    
      if (!$.browser.msie)
      {
      if (typeof(paper) != "undefined") {paper.remove(); $("starContainer").empty();};
      var container, i , j, outerRadius, mainSet, LinesSet, calcButton = null;  
      $("#costCalc").html("0");
      // Размеры кругов и отступ
      var centerRadius = 60;  
      var marginOuter = 40;
      var buttonWidth = 130;       
  
      // Определяем линии и точки на линиях
      var lines = new Array(
        {
          title: "Дизайн",
          tOffsetX: -60, // Костыли, ибо чтобы нормально расположить текст относительно финальной точки на линии нужно очень извращаться 
          tOffsetY: -25,
          pOffsetX: -20, // Смещение мелких надписей
          pOffsetY: 0,
          align: "end",
          points: [{title: "Шаблонный", desc: "Неуникальный, шаблонный дизайн.", type: "radio", cost: 4000, selected: false},
                   {title: "Информационный", desc: "Информационный дизайн разрабатывается индивидуально,\nно упор сделан на тексте, простоте и понятности подачи информации.", type: "radio", cost: 8000, selected: true},
                   {title: "Эксклюзивный", desc: "Индивидуальный дизайн с оригинальными решениями.", type: "radio", cost: 16000, selected: false},
                   {title: "Промо", desc: "Дорогой, престижный и элегантный дизайн, призванный максимально\nпривлечь внимание клиента к услуге или товару.\nВключает в себя множество интерактивных деталей.", type: "radio", cost: 32000, selected: false},]
        },
        {
          title: "Верстка",
          tOffsetX: -40, // Костыли, ибо чтобы нормально расположить текст относительно финальной точки на линии нужно очень извращаться 
          tOffsetY: -25,
          pOffsetX: +20, // Смещение мелких надписей
          pOffsetY: 0,
          align: "start",
          points: [{title: "Базовая", desc: "Верстка дизайна без интерактивных элементов.", type: "radio", cost: 4000, selected: true},
                   {title: "С интерактивными элементами", desc: "Верстка с использованием сложной\nанимации меню, интерактивных слайдеров,\nс использованием ajax и любых прочих\nэлементов интерактива.", type: "radio", cost: 10000, selected: false}]
        },
        {
          title: "Функционал",
          tOffsetX: -10, // Костыли, ибо чтобы нормально расположить текст относительно финальной точки на линии нужно очень извращаться 
          tOffsetY: +20,
          pOffsetX: 0, // Смещение мелких надписей
          pOffsetY: -15,
          align: "start",
          points: [{title: "Сайт-визитка", desc: "Решение с системой управления сайтом,\nвозможностью создания неограниченного\nколичества страниц и обратной связью.", type: "radio", cost: 10000, selected: false},
                   {title: "Корпоративный сайт", desc: "Каталог товаров\\услуг.\nЗагружаемые прайсы.\nВопрос-ответ.\nНовости и rss.\n", type: "radio", cost: 20000, selected: true},
                   {title: "Интернет-магазин", desc: "Корзина покупателя\nУчет заказов\nСистемы оплаты\n", type: "radio", cost: 40000, selected: false}]
        },
        {
          title: "Сопровождение",
          tOffsetX: -90, // Костыли, ибо чтобы нормально расположить текст относительно финальной точки на линии нужно очень извращаться 
          tOffsetY: +20,
          pOffsetX: +15, // Смещение мелких надписей
          pOffsetY: 0,
          align: "start",
          points: [{title: "Домен", desc: "Домен в зоне .ru, зоны .com, .net, .info - 600 рублей\nДругие зоны оговариваются индивидуально.\nОплата за год.", type: "check", cost: 400, selected: true},
                   {title: "Хостинг Малый", desc: "300 мегабайт на диске.\nПодойдет для сайтов, где нет необходимости выкладывать\nмного мультимедийного контента.\nОплата за год.", type: "radio", cost: 2000, selected: true},
                   {title: "Хостинг Большой", desc: "1 гигабайт на диске.\nВыкладывайте ваши презентации, видео и другие файлы,\nне ограничивая себя в пространстве на диске.\nОплата за год.", type: "radio", cost: 4000, selected: false},
                   {title: "Техническая поддержка", desc: "Услуга оплачивается ежемесячно.\nКонсультации по любым вопросам по телефону и лично.\nПомощь по внесению небольших изменений на сайт.", type: "check", cost: 5000, selected: false}]
        },
        {
          title: "Продвижение",
          tOffsetX: -150, // Костыли, ибо чтобы нормально расположить текст относительно финальной точки на линии нужно очень извращаться 
          tOffsetY: +20,
          pOffsetX: 0, // Смещение мелких надписей
          pOffsetY: -15,
          align: "end",
          points: [{title: "Аналитика", desc: "Предварительные работы по анализу ключевых слов,\nмониторинг конкурентов в поисковой выдаче,\nсбор данных об аудитории по ключевым словам.", type: "check", cost: 4000, selected: false},
                   {title: "Оптимизация", desc: "Подготовка страниц сайта к продвижению, корректировка текстов на сайте,\nподключение систем мониторинга позиций сайта в поисковых системах.", type: "check", cost: 10000, selected: false},
                   {title: "Ссылочное продвижение", desc: "На ссылочное продвижение выделяются ежемесячные бюджеты.\nВ данном случае приводится среднестатистическая сумма\nна 2 месяца продвижения, с бюджетами по 4000 рублей на месяц.", type:"check", cost: 8000, selected: false}]
        });
  
      setTimeout(function(){return initDiv();}, 1500);
      
      // Инициализация и отображение кругов в центре
      function initDiv()
        {
          container = $("#starContainer");
          outerRadius = (container.height() - marginOuter*2)/2;
  
          // Объекты Raphael и сеты          
          paper = Raphael(document.getElementById("starContainer"), container.width(), container.height());
            
          // Внешний круг
          paper.circle(container.width()/2, container.height()/2, "1").attr({"fill":"r#a51212-#000", "fill-opacity" : 0.3}).animate(
            {
              "r" : outerRadius,
              "fill-opacity" : 0.3,
            }, 700);
  
          // Внутренний круг
          paper.circle(container.width()/2, container.height()/2, "1").animate(
            {
              "r" : centerRadius,
              "stroke-width": 8,
              "stroke-opacity": 1
            }, 700, function(){return centerText();}).attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff"});
        }   
  
    function centerText()
      {
        ct = paper.set();
        ct.push(
          paper.print(296, 223, "Ваш сайт", paper.getFont("Calibri"), 24).attr({"fill":"#000", "fill-opacity" : 0}),
          paper.print(295, 222, "Ваш сайт", paper.getFont("Calibri"), 24).attr({"fill":"#fff", "fill-opacity" : 0}));
        ct.animate({"fill-opacity": 1}, 400, function(){return drawLines();});  
      }
  
    // Рисовалка всех линий
    function drawLines()
      {
        // Определяем начальные и конечные точки для линий
        var angle = 360/lines.length; // делим круг
        var angleShift = angle/2-90; // Смещаем щтабэ на 0 градусов находилась середина между 2мя линиями
          
        for (i = 0; i < lines.length; i++)
          {     
            lines[i].angleCurrent = (angleShift) + angle*(i-1);
            lines[i].angleRad = (Math.PI/180)*lines[i].angleCurrent; // Переводим в радианы   
            lines[i].startX = Math.cos(lines[i].angleRad)*centerRadius + container.width()/2;
            lines[i].startY = Math.sin(lines[i].angleRad)*centerRadius + container.height()/2;
            lines[i].endX = Math.cos(lines[i].angleRad)*outerRadius + container.width()/2;
            lines[i].endY = Math.sin(lines[i].angleRad)*outerRadius + container.height()/2;    
          
            // Сначала создаем невидимую линию для пересчета и создания точек
            lines[i].objInv = paper.path("M"+lines[i].startX+" "+lines[i].startY+" L"+lines[i].endX+" "+lines[i].endY).attr({"opacity":"0", stroke: "#ffffff"});
            lines[i].nodeLength = lines[i].objInv.getTotalLength()/(lines[i].points.length+1);
          
            lines[i].obj = paper.path("M"+lines[i].startX+" "+lines[i].startY+" L"+lines[i].startX+" "+lines[i].startY).attr({"stroke": "#ffffff", "stroke-width" : 4, "stroke-linecap" : "round"});
          
          }
        // Запускаем на рекурсию рисовалку линий                                
        drawLine(0);
      }
    
  function drawLine(i)
    {
      if (i < lines.length)
        {
          // Изначально мы делали "невидимую" линию чтобы сейчас её анимировать.
          lines[i].obj.animate(
            {
            "path": "M"+lines[i].startX+" "+lines[i].startY+" L"+lines[i].endX+" "+lines[i].endY
            },
            800,
            "elastic",
            function(){drawLineTitle(i);});          
        }
      else
        {
          // Когда все линии отрисованы, отображаем кнопку отправки данных
          createButton();
        }
    }
    
  function drawLineTitle(i)
    {
      nodeLength = lines[i].nodeLength - 0.5;
      var titleSet = paper.set();
      var pX = lines[i].objInv.getPointAtLength(nodeLength*(lines[i].points.length+1)).x;
      var pY = lines[i].objInv.getPointAtLength(nodeLength*(lines[i].points.length+1)).y;
      lines[i].titleCircleObj = paper.circle(pX, pY, 8).attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff", "stroke-width" : 2, "opacity": 0});
      lines[i].titleObj = paper.print(pX + lines[i].tOffsetX, pY + lines[i].tOffsetY, lines[i].title, paper.getFont("Calibri"), 24).attr({"fill":"#fff", "opacity": 0});
      titleSet.push(lines[i].titleCircleObj);
      titleSet.push(lines[i].titleObj);      
      titleSet.animate({"opacity": 1}, 400, function(){drawPoints(i);});
    }
   
  function drawPoints(i)
    {      
      // Создаем точки на линиях      
      drawPoint(i, 0);
    }
  
  function drawPoint(i, j)
    {
          nodeLength = lines[i].nodeLength - 0.5;
          var cX = lines[i].objInv.getPointAtLength(nodeLength*(j+1)).x;
          var cY = lines[i].objInv.getPointAtLength(nodeLength*(j+1)).y;
          if (lines[i].points[j].selected == true)
            {
              lines[i].points[j].obj = paper.circle(cX, cY, 6).attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff", "stroke-width" : 2, r: 6, opacity: 0, cursor: "pointer"});
              $("#costCalc").html(parseInt($("#costCalc").html()) + lines[i].points[j].cost);
            }
          else
            {
              lines[i].points[j].obj = paper.circle(cX, cY, 4).attr({fill: "#fff", cursor: "pointer", stroke : 0});
            }
          
          lines[i].points[j].obj.node.onclick = function()
            { 
              var ii = i;
              var jj = j;
                  // Радиобутоны
                  if (lines[ii].points[jj].type == "radio")
                    {
                      for (var jjj = 0; jjj<lines[ii].points.length; jjj++)
                        { 
                          if (lines[ii].points[jjj].type == "radio")
                            {
                              if (lines[ii].points[jjj].selected == true)
                                {
                                  lines[ii].points[jjj].selected = false;
                                  $("#costCalc").html(parseInt($("#costCalc").html()) - lines[ii].points[jjj].cost);
                                }
                              lines[ii].points[jjj].obj.attr({"stroke-width" : "1", fill: "#fff", r: 4, scale: 1});
                            }
                        } 
                      lines[ii].points[jj].obj.attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff", "stroke-width" : 2, r: 6, scale: 1});
                      lines[ii].points[jj].selected = true;
                      $("#costCalc").html(parseInt($("#costCalc").html()) + lines[ii].points[jj].cost); 
                    }
                  // Чекбоксы
                  else if(lines[ii].points[jj].type == "check")
                    {
                      if (lines[ii].points[jj].selected)
                        {
                          lines[ii].points[jj].selected = false;
                          lines[ii].points[jj].obj.attr({"stroke-width" : "1", fill: "#fff", scale: 1.5, r: 6});
                          $("#costCalc").html(parseInt($("#costCalc").html()) - lines[ii].points[jj].cost);
                        }
                      else
                        {
                          lines[ii].points[jj].selected = true;
                          lines[ii].points[jj].obj.attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff", "stroke-width" : 2, scale: 1.5, r: 9});
                          $("#costCalc").html(parseInt($("#costCalc").html()) + lines[ii].points[jj].cost);
                        }
                    }
                } 
          
          lines[i].points[j].obj.nmb = j;
          lines[i].points[j].obj.hover( 
            function(event)
              {   
                var j = this.nmb;                
                var x = lines[i].points[j].obj.attr("cx");
                var y = lines[i].points[j].obj.attr("cy");
                var label = paper.set();
                label.push(paper.text(0, 0, "Цена: " + lines[i].points[j].cost + " рублей").attr({fill:"#000", "text-anchor" : "start", "font-size": 14}));
                label.push(paper.text(0, 40, lines[i].points[j].desc).attr({fill:"#000", "font-size": 11}));
                lines[i].points[j].popObj = paper.popup(Math.round(x), Math.round(y)-8, label, "top-left").attr({fill: "290-#ead883-#ffffbe", "stroke-width": 0.5});
                lines[i].points[j].labelObj = label;
                label.attr({"text-anchor" : "start"});
                lines[i].points[j].obj.animate({scale: 1.5}, 1000, "bounce");
              }, 
            function(event)
              { 
                var j = this.nmb;
                lines[i].points[j].popObj.hide();
                lines[i].points[j].labelObj.hide();
                lines[i].points[j].obj.animate({scale: 1}, 1000, "bounce");
              });
          lines[i].points[j].obj.animate({opacity: 1}, 200, function(){drawPointText(i, j);})
    }
  
  function drawPointText(i, j)
    {
      var cX = lines[i].objInv.getPointAtLength(nodeLength*(j+1)).x;
      var cY = lines[i].objInv.getPointAtLength(nodeLength*(j+1)).y;
      lines[i].points[j].objText = paper.text(cX+lines[i].pOffsetX, cY+lines[i].pOffsetY, lines[i].points[j].title).attr({"fill":"#fff", "text-anchor": lines[i].align, opacity: 0});
      
      if (j < lines[i].points.length-1)
        {
          lines[i].points[j].objText.animate({opacity: 1}, 200, function(){drawPoint(i, j+1);}); // Рисуем следующую точку
        }
      else
        {
          lines[i].points[j].objText.animate({opacity: 1}, 200, function(){drawLine(i+1);}); // Рисуем следующую точку
        }
    }
    
  function createButton()
    {
      calcButton = paper.set();
      buttonText = paper.set();      
      calcButton.push(paper.rect(container.width()-buttonWidth-10, container.height()-40, buttonWidth, 30, 5).attr({"fill": "290-#ea1d1d-#881111", stroke: "#ffffff", "stroke-width" : 2, opacity: 0}));      
      calcButton.animate({opacity: 1}, 800);
      buttonText.push(
        paper.print(1, 1, "Отправить заявку", paper.getFont("Calibri"), 16).attr({"fill":"#000", opacity : 1}),
        paper.print(0, 0, "Отправить заявку", paper.getFont("Calibri"), 16).attr({"fill":"#fff", opacity : 1}));
      dX = (calcButton.getBBox().width - buttonText.getBBox().width);
      dY = (calcButton.getBBox().height - buttonText.getBBox().height);      
      buttonText.translate(calcButton.getBBox().x + dX - 10, calcButton.getBBox().y + dY - 2); // Лень разбираться почему не так как надо смещает ;)
      calcButton.push(buttonText);
      buttonText.animate({opacity: 1});
      calcButton.attr({cursor: "pointer"});
      
      calcButton.hover(
        function()
          {
            calcButton[0].animate({scale: 1.05}, 100);
          },
        function()
          {
            calcButton[0].animate({scale: 1}, 100);
          }
      );
      $("#calcArea").show();
      
      calcButton[0].node.onclick = function(){preOrder();};
      // Не нашел как навесить event на весь set, будем извращаться и вешать на каждую буковку)
      for(x in calcButton[1][1])
        {
          calcButton[1][1][x].node.onclick = function(){preOrder();};
        }  
    }
  
  function preOrder()
    {
      var message = "Здравствуйте, меня интересует разработка следующего проекта:\n";
      
      for (i in lines)
        {
          message += lines[i].title + ": ";
          for (j in lines[i].points)
            {
              if (lines[i].points[j].selected)
                {
                  message += lines[i].points[j].title + ";\n";
                } 
            }
        }
      
      message += "Связаться со мной можно по телефону: \n";
      message += "Либо по электронной почте, указанной выше.\n";
      $("#message").html(message);
      closeThis('contacts');
    }
  
  $("#preorder").hover(function() {$(this).css("background", "url('/images/order.jpg')");},function(){$(this).css("background", "url('/images/order_in.jpg')");});
  
  
  //paper.path("M320,240c-50,100,50,110,0,190");
  
  
  /*
    SVG Path format Cheatsheet :)
    
    M (absolute) m (relative) moveto (x y)+
    Start a new sub-path at the given (x,y) coordinate. M (uppercase) indicates that absolute coordinates will follow; m (lowercase) indicates that relative coordinates will follow. If a moveto is followed by multiple pairs of coordinates, the subsequent pairs are treated as implicit lineto commands. Hence, implicit lineto commands will be relative if the moveto is relative, and absolute if the moveto is absolute. If a relative moveto (m) appears as the first element of the path, then it is treated as a pair of absolute coordinates. In this case, subsequent pairs of coordinates are treated as relative even though the initial moveto is interpreted as an absolute moveto.
    
    Z or z closepath (none)
    Close the current subpath by drawing a straight line from the current point to current subpath's initial point. Since the Z and z commands take no parameters, they have an identical effect.
    
    L (absolute) l (relative) lineto (x y)+
    Draw a line from the current point to the given (x,y) coordinate which becomes the new current point. L (uppercase) indicates that absolute coordinates will follow; l (lowercase) indicates that relative coordinates will follow. A number of coordinates pairs may be specified to draw a polyline. At the end of the command, the new current point is set to the final set of coordinates provided.
   
    H (absolute) h (relative) horizontal lineto x+
    Draws a horizontal line from the current point (cpx, cpy) to (x, cpy). H (uppercase) indicates that absolute coordinates will follow; h (lowercase) indicates that relative coordinates will follow. Multiple x values can be provided (although usually this doesn't make sense). At the end of the command, the new current point becomes (x, cpy) for the final value of x.
    
    V (absolute) v (relative) vertical lineto y+ 
    Draws a vertical line from the current point (cpx, cpy) to (cpx, y). V (uppercase) indicates that absolute coordinates will follow; v (lowercase) indicates that relative coordinates will follow. Multiple y values can be provided (although usually this doesn't make sense). At the end of the command, the new current point becomes (cpx, y) for the final value of y.
    
    // Кривые
    C (absolute) c (relative) curveto (x1 y1 x2 y2 x y)+
    Draws a cubic Bézier curve from the current point to (x,y) using (x1,y1) as the control point at the beginning of the curve and (x2,y2) as the control point at the end of the curve. C (uppercase) indicates that absolute coordinates will follow; c (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
    
    S (absolute) s (relative) shorthand/smooth curveto (x2 y2 x y)+
    Draws a cubic Bézier curve from the current point to (x,y). The first control point is assumed to be the reflection of the second control point on the previous command relative to the current point. (If there is no previous command or if the previous command was not an C, c, S or s, assume the first control point is coincident with the current point.) (x2,y2) is the second control point (i.e., the control point at the end of the curve). S (uppercase) indicates that absolute coordinates will follow; s (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
    
    Q (absolute) q (relative) quadratic Bézier curveto (x1 y1 x y)+ 
    Draws a quadratic Bézier curve from the current point to (x,y) using (x1,y1) as the control point. Q (uppercase) indicates that absolute coordinates will follow; q (lowercase) indicates that relative coordinates will follow. Multiple sets of coordinates may be specified to draw a polybézier. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
    
    T (absolute) t (relative) Shorthand/smooth quadratic Bézier curveto	(x y)+
    Draws a quadratic Bézier curve from the current point to (x,y). The control point is assumed to be the reflection of the control point on the previous command relative to the current point. (If there is no previous command or if the previous command was not a Q, q, T or t, assume the control point is coincident with the current point.) T (uppercase) indicates that absolute coordinates will follow; t (lowercase) indicates that relative coordinates will follow. At the end of the command, the new current point becomes the final (x,y) coordinate pair used in the polybézier.
    
    A (absolute) a (relative) elliptical arc (rx ry x-axis-rotation large-arc-flag sweep-flag x y)+
    Draws an elliptical arc from the current point to (x, y). The size and orientation of the ellipse are defined by two radii (rx, ry) and an x-axis-rotation, which indicates how the ellipse as a whole is rotated relative to the current coordinate system. The center (cx, cy) of the ellipse is calculated automatically to satisfy the constraints imposed by the other parameters. large-arc-flag and sweep-flag contribute to the automatic calculations and help determine how the arc is drawn.
    
    M - начало кривой (x,y)
    Z - конец кривой (без координат)
    L -прямая линия (x,y)
    H - горизонтальная линия
    V - вертикальная линия
    Q - квадратичная кривая по одной точке
    T - продолжение кривой с отражением предыдущей точки - упрощает рисование повторяющихся ритмов
    С - собственно кривая Безье третьего порядка по двум точкам
    S - упрощённая версия C
    A - эллиптическая кривая (радиусы, поворот)
    Трансформации и возможности
    Объекты в SVG пожно искажать, крутить и перемещать при помощи фильтров, которые указываются в качестве параметров:

    translate - перенос объекта
    rotate -вращение
    scale - масштабирование
    scewX, scewY - искажение
    matrix - смешанная трансформация
     
  */

  }
  else
    {
      $("#text-cost").show();
      $("#interactiveDiv").hide();
      $(".costNode").click(function()
        {
          $(".costTooltip").hide();
          $(this).next("div").css({"display" : "block", "clear":"both"}); 
          $(this).next("div").show();
        });
    }
  
  };

(function () {
var tokenRegex = /\{([^\}]+)\}/g,
    objNotationRegex = /(?:(?:^|\.)(.+?)(?=\[|\.|$|\()|\[('|")(.+?)\2\])(\(\))?/g, // matches .xxxxx or ["xxxxx"] to run over object properties
    replacer = function (all, key, obj) {
        var res = obj;
        key.replace(objNotationRegex, function (all, name, quote, quotedName, isFunc) {
            name = name || quotedName;
            if (res) {
                if (name in res) {
                    res = res[name];
                }
                typeof res == "function" && isFunc && (res = res());
            }
        });
        res = (res == null || res == obj ? all : res) + "";
        return res;
    },
    fill = function (str, obj) {
        return String(str).replace(tokenRegex, function (all, key) {
            return replacer(all, key, obj);
        });
    };
    Raphael.fn.popup = function (X, Y, set, pos, ret) {
        pos = String(pos || "top-middle").split("-");
        pos[1] = pos[1] || "middle";
        var r = 5,
            bb = set.getBBox(),
            w = Math.round(bb.width),
            h = Math.round(bb.height),
            x = Math.round(bb.x) - r,
            y = Math.round(bb.y) - r,
            gap = Math.min(h / 2, w / 2, 10),
            shapes = {
                top: "M{x},{y}h{w4},{w4},{w4},{w4}a{r},{r},0,0,1,{r},{r}v{h4},{h4},{h4},{h4}a{r},{r},0,0,1,-{r},{r}l-{right},0-{gap},{gap}-{gap}-{gap}-{left},0a{r},{r},0,0,1-{r}-{r}v-{h4}-{h4}-{h4}-{h4}a{r},{r},0,0,1,{r}-{r}z",
                bottom: "M{x},{y}l{left},0,{gap}-{gap},{gap},{gap},{right},0a{r},{r},0,0,1,{r},{r}v{h4},{h4},{h4},{h4}a{r},{r},0,0,1,-{r},{r}h-{w4}-{w4}-{w4}-{w4}a{r},{r},0,0,1-{r}-{r}v-{h4}-{h4}-{h4}-{h4}a{r},{r},0,0,1,{r}-{r}z",
                right: "M{x},{y}h{w4},{w4},{w4},{w4}a{r},{r},0,0,1,{r},{r}v{h4},{h4},{h4},{h4}a{r},{r},0,0,1,-{r},{r}h-{w4}-{w4}-{w4}-{w4}a{r},{r},0,0,1-{r}-{r}l0-{bottom}-{gap}-{gap},{gap}-{gap},0-{top}a{r},{r},0,0,1,{r}-{r}z",
                left: "M{x},{y}h{w4},{w4},{w4},{w4}a{r},{r},0,0,1,{r},{r}l0,{top},{gap},{gap}-{gap},{gap},0,{bottom}a{r},{r},0,0,1,-{r},{r}h-{w4}-{w4}-{w4}-{w4}a{r},{r},0,0,1-{r}-{r}v-{h4}-{h4}-{h4}-{h4}a{r},{r},0,0,1,{r}-{r}z"
            },
            offset = {
                hx0: X - (x + r + w - gap * 2),
                hx1: X - (x + r + w / 2 - gap),
                hx2: X - (x + r + gap),
                vhy: Y - (y + r + h + r + gap),
                "^hy": Y - (y - gap)
                
            },
            mask = [{
                x: x + r,
                y: y,
                w: w,
                w4: w / 4,
                h4: h / 4,
                right: 0,
                left: w - gap * 2,
                bottom: 0,
                top: h - gap * 2,
                r: r,
                h: h,
                gap: gap
            }, {
                x: x + r,
                y: y,
                w: w,
                w4: w / 4,
                h4: h / 4,
                left: w / 2 - gap,
                right: w / 2 - gap,
                top: h / 2 - gap,
                bottom: h / 2 - gap,
                r: r,
                h: h,
                gap: gap
            }, {
                x: x + r,
                y: y,
                w: w,
                w4: w / 4,
                h4: h / 4,
                left: 0,
                right: w - gap * 2,
                top: 0,
                bottom: h - gap * 2,
                r: r,
                h: h,
                gap: gap
            }][pos[1] == "middle" ? 1 : (pos[1] == "top" || pos[1] == "left") * 2];
            var dx = 0,
                dy = 0,
                out = this.path(fill(shapes[pos[0]], mask)).insertBefore(set);
            switch (pos[0]) {
                case "top":
                    dx = X - (x + r + mask.left + gap);
                    dy = Y - (y + r + h + r + gap);
                break;
                case "bottom":
                    dx = X - (x + r + mask.left + gap);
                    dy = Y - (y - gap);
                break;
                case "left":
                    dx = X - (x + r + w + r + gap);
                    dy = Y - (y + r + mask.top + gap);
                break;
                case "right":
                    dx = X - (x - gap);
                    dy = Y - (y + r + mask.top + gap);
                break;
            }
            out.translate(dx, dy);
            if (ret) {
                ret = out.attr("path");
                out.remove();
                return {
                    path: ret,
                    dx: dx,
                    dy: dy
                };
            }
            set.translate(X-8, Y-set.getBBox().height-8);
            /*set[0].translate(x - 50, y);
            set[1].translate(x, y + 20);*/
            return out;
    };
})();
