/**
 * @file
 * Файл с утилитами.
 *
 * Функции начинающиеся со знака "_" являются внутренними и не предназначены для использования.
 */

// Переменные, определяющие версии браузера.
var isIE = /MSIE/.test(window.navigator.userAgent);
var isIE6 = /MSIE 6.0/.test(window.navigator.userAgent);
var isIE7 = /MSIE 7.0/.test(window.navigator.userAgent);
var isIE8 = /MSIE 8.0/.test(window.navigator.userAgent);
var isFF = /Firefox/.test(window.navigator.userAgent);
var isOpera = /Opera/.test(window.navigator.userAgent);
var isChrome = /Chrome/.test(window.navigator.userAgent);

//Глобальный массив для установки и сброса таймеров.
var timer = new Array;

// Внутренний счётчик количества вызовов функции.
var count = new Array;

// Этот массив приведён для примера.
/*var func = {
  alert: function (any_array) {
    alert('This' + any_array);
  }
}*/
// Ассоциативный массив, предназначенный для вызова функций по их названию.
// Например, приведённое описание массива func(см. до комментария),
// повзволит пользователю вызвать фукцию таким образом:
// func['alert']('Hello!');
// Для того, чтобы иметь возможность вызывать функции после завершения каких-либо эффектов
// (например opacityVisible()), необходимо объявить глобально этот массив у себя в скрипте.
// Например, если вы хотите, чтобы после завершения какого-либо эффекта вызывалась функция
// с именем funcGo, нужно будет сделать такое объявление массива:
// var func = {
// funcGo: function(in_array) {
//    funcGo(in_array);
//  }
// };
// Массив in_array принимает и передаёт параметры.
// Первый элемент массива всегда должен называться 'name'. В нём содержится имя вызываемой функции.
// Если у вас есть несколько функций необходимых для возврата,
// то перечислите их в массиве через запятую, например:
// var func = {
//   funcGo: function(in_array) {
//    funcGo(in_array);
//  },
//   funcSome: function(in_array) {
//    funcSome(in_array);
//  },
//   SimpleFunc: function(in_array) {
//    SimpleFunc(in_array);
//  },
//  ...
// };
//
// Для того, чтобы была вызвана ваша функция после завершения эффекта, вам необходимо
// передать в функцию (которую вы вызываете для создания эффекта) массив со следующией структурой:
// var funcAnyArray = {
//   name: 'funcGo',  // это поле содержит имя вашей функции
//   name_param1: value_of_parametr1, // эти поля могут называться как вам угодно
//   next_param2: value_of_parametr2, // это могут быть даже объекты
//   ...
// };
// Теперь можно вызвать функцию для создания эффекта, например так:
// opacityVisible(element, 10, 1, 100, 1, 'id-timer', funcAnyArray);
// Когда эффект закончится, функция передаст управление нашей функции funcGo,
// которая может выглядеть так:
// function funcGo(our_array) {
//   ...
// }
// Массив our_array получит переданный вами массив funcAnyArray (с названием
// этой же функции и параметрами для неё).




/************************************************************************
 *                Функции для работы с DOM и XML                        *
 ************************************************************************/

function __GOTO_DOM_XML() {};

/**
 * Возвращаем элемент по указанному id.
 * Используется вместо длинной записи через document...
 *
 * id
 * id элемента.
 *
 * return
 * элемент.
 */
function gebi(id) {
  return document.getElementById(id);
}

/**
 * Возвращаем массив элементов с определённым именем.
 * Используется вместо длинной записи через document...
 *
 * in_name
 * Имя элемента.
 *
 * return
 * Массив элементов.
 */
function gebtn(in_name) {
  return document.getElementsByTagName(in_name);
}

/**
 * Создаём элемент с именем name.
 * Используется вместо длинной записи через document...
 *
 * name
 * имя создаваемого элемента.
 *
 * return
 * созданный элемент.
 */
function ce(name) {
  return document.createElement(name);
}

/**
 * Создаём текстовый узел.
 * Используется вместо длинной записи через document...
 *
 * text
 * текст узла.
 *
 * return
 * созданный текстовый узел.
 */
function ctn(text) {
  return document.createTextNode(text);
}

/**
 * Удаляем узел.
 * Используется вместо длинной записи.
 *
 * node
 * узел.
 */
function rn(node) {
  node.parentNode.removeChild(node);
}

/**
 * Заменяет в узле старый потомок узел-1 на новый потомок узел-2.
 * Используется вместо длинной записи.
 * in_node родительский узел, в котором необходимо произвести замену.
 * in_node1 заменяемый узел.
 * in_node2 заменяющий узел.
 */
function rc(in_node, in_node1, in_node2) {
  in_node.replaceChild(in_node2, in_node1);
}

/**
 * Возвращает елемент body.
 * return body
 */
function body() {
  return gebtn("body")[0];
}

/**
 * Преобразует XML-элемент в HTML.
 * Данная функция особенно актуальна при получении данных с сервера в формате XML (ajax),
 * которые необходимо вставить в объектную модель документа как есть.
 *
 *
 * in_XMLElement
 * XML-элемент.
 *
 * return
 * преобразованный HTML-элемент.
 */
function XMLtoDocument(in_XMLElement) {
  var el = in_XMLElement;
  var node = _nextChild(el);
  return node;
}

/**
 * Вспомогательная рекурсивная функция, вызывается из XMLtoDocument().
 * Функция перебирает всех потомков элемента.
 * Внимание! Между элементов не должно быть пробелов!
 *
 * in_el
 * XML-элемент.
 *
 * return
 * преобразованный HTML-элемент.
 */
function _nextChild(in_el) {
  // Создаём элемент.
  if (in_el.nodeValue == null) {
    var nodeName = in_el.nodeName;
    var node = ce(nodeName);
    // Копируем атрибуты.
    if (in_el.attributes != null) {
      var nodeAttrs = in_el.attributes;
      for (var i = 0; i < nodeAttrs.length; i++) {
        var attrName = nodeAttrs[i].nodeName;
        var attrValue = nodeAttrs[i].nodeValue;
        node.setAttribute(attrName, attrValue);
      }
    }
  } else {  // Создаём текстовый узел.
    var nodeValue = in_el.nodeValue;
    var node = ctn(nodeValue);
  }

  // Перебираем всех потомков.
  if (in_el.hasChildNodes()) {
    while (in_el.firstChild) {
      var child = _nextChild(in_el.firstChild);
      // Добавляем узел к родителю.
      node.appendChild(child);
    }
    // Удаляем XML-элемент, не имеющий потомков.
    in_el.parentNode.removeChild(in_el);
  } else {
    // Удаляем XML-элемент, не имеющий потомков.
    in_el.parentNode.removeChild(in_el);
  }

  return node;
}






/************************************************************************
 *                 Функции для работы с эфектами                        *
 ************************************************************************/

function __GOTO_GRAPH() {};

/**
 * Плавное изменение прозрачности элемента.
 * При завершении может вызывать функцию,
 * определённую пользователем, если это необходимо.
 *
 * elem
 * элемент.
 *
 * dOp
 * шаг изменения прозрачности (от 1 до 100).
 *
 * beginOp
 * начальное значение прозрачности (1-100).
 *
 * finishOp
 * конечное значение прозрачности.
 *
 * interTime
 * временной интервал изменения прозрачности на величину dOp (в мс).
 * Этот параметр сильно зависит от используемого браузера.
 *
 * in_t
 * уникальный таймер.
 *
 * in_func
 * ассоциативный массив, содержащий в себе имя функции
 * (которая будет вызвана после изменения прозрачности) и её параметры.
 * Первый элемент массива всегда должен называться 'name'.
 * Пример структуры массива:
 * var funcArray = {
 *   name: 'simpleFuncName',
 *   img: in_img,
 *   time: in_time
 * };
 * Данный параметр не обязателен, если вы не хотите вызывать какую-либо функцию
 * после изменения прозрачности.
 */
function opacityVisible(elem, dOp, beginOp, finishOp, interTime, in_t, in_func) {
  setOpacity(elem, beginOp);
  clearInterval(timer[elem.id]);
  clearInterval(in_t);
  timer[in_t] = setInterval(function() {_opacity(elem, dOp, finishOp, in_t, in_func);}, interTime);
}

/**
 * Установка прозрачности элемента.
 *
 * in_el
 * элемент.
 *
 * in_op
 * значение прозрачности (от 1 до 100).
 */
function setOpacity(in_el, in_op) {
  if (in_op > 100) {
    in_op = 100;
  }
  if (in_op < 0) {
    in_op = 0;
  }
  in_el.style.filter = 'alpha(opacity = ' + in_op + ')';
  in_el.style.opacity = in_op / 100;
}

/**
 * Получение прозрачности элемента.
 * Данный метод работает только для элементов,
 * у которых явно установленно значение прозрачности в стилях.
 *
 * in_el
 * элемент.
 *
 * return
 * значение прозрачности (от 1 до 100) или false в случае неудачи.
 */
function getOpacity(in_el) {
  if (in_el != null) {
    var op = null;
    if (in_el.style.opacity) {
      op = in_el.style.opacity * 100;
    } else if (in_el.filters.alpha.opacity) {
        op = in_el.filters.alpha.opacity;
      } else {
        return false;
      }
    return Math.round(op);
  }
}

/**
 * Перемещает элемент по заданным координатам с возможностью ондовременного изменения размеров.
 * Данный метод работает только для элементов,
 * у которых явно установленно значение прозрачности в стилях.
 * При завершении может вызывать функцию,
 * определённую пользователем, если это необходимо.
 *
 * in_el
 * элемент.
 *
 * in_t
 * уникальный таймер.
 *
 * in_time
 * время перемещения (в мс).
 * Этот параметр сильно зависит от используемого браузера.
 *
 * in_left
 * горизонтальная позиция элемента, в которую необходимо его переместить.
 * Позиция задаётся относительно родительского элемента.
 *
 * in_top
 * вертикальная позиция элемента, в которую необходимо его переместить.
 * Позиция задаётся относительно родительского элемента.
 *
 * in_width
 * ширина элемента в конце перемещения.
 *
 * in_height
 * высота элемента в конце перемещения.
 *
 * in_func
 * ассоциативный массив, содержащий в себе имя функции
 * (которая будет вызвана после перемещения) и её параметры.
 * Первый элемент массива всегда должен называться 'name'.
 * Пример структуры массива:
 * var funcArray = {
 *   name: 'simpleFuncName',
 *   img: in_img,
 *   time: in_time
 * };
 * Данный параметр не обязателен, если вы не хотите вызывать какую-либо функцию
 * после изменения прозрачности.
 *
 * return
 * ничего  или false.
 */
function moveResizeElement(in_el, in_t, in_time, in_left, in_top, in_width, in_height, in_func) {
  if (in_el == null) {
    return false;
  }
  var interTime = 1;
  var k = (in_time);
  timer[in_t] = setInterval(function() {_moveResize(in_el, in_t, k, in_left, in_top, in_width, in_height, in_func);}, interTime);
}

/**
 * Создаёт передний слой на всю страницу с определённым цветом.
 * color (string) цвет в формате #RRGGBB или цветовые константы red, blue, green и т.д.
 */
function foreground(color) {
  var divEl = ce('div');
  divEl.setAttribute('id', '');
  divEl.style.position = 'absolute';
  divEl.style.top = '0px';
  divEl.style.left = '0px';
  var body = documentSize();
  divEl.style.width = body[0] + 'px';
  divEl.style.height = body[1] + 'px';
  divEl.style.backgroundColor = color;
  gebtn('body')[0].appendChild(divEl);
  return divEl;
}

/**
 * Определяет позицию left элемента относительно <body>.
 *
 * in_el
 * элемент.
 *
 * return
 * значение left (только число, без постфикса 'px') или false.
 */
function getLeft(in_el) {
  if (in_el == null) {
    return false;
  }
  var left = in_el.offsetLeft;
  var leftParent = in_el;
  while (leftParent = leftParent.offsetParent) {
    left = left + leftParent.offsetLeft;
  }
  return left - gebtn('body')[0].offsetLeft;
}

/**
 * Определяет позицию top элемента относительно <body>.
 *
 * in_el элемент.
 * return значение top (только число, без постфикса 'px')  или false.
 */
function getTop(in_el) {
  if (in_el == null) {
    return false;
  }
  var topParent = in_el;
  var top = in_el.offsetTop - 1;
  while (topParent = topParent.offsetParent) {
    top = top + topParent.offsetTop;
  }
  return top - gebtn('body')[0].offsetTop;
}

/**
 * Возвращает левую и верхнюю позицию элемента относительно тела документа.
 *
 * @param element object - элемент.
 * @return array - координаты элемента.
 */
function offsetPosition(element) {
  var offsetLeft = 0, offsetTop = 0;
  do {
      offsetLeft += element.offsetLeft;
      offsetTop  += element.offsetTop;
  } while (element = element.offsetParent);
  return [offsetLeft, offsetTop];
}

/**
 * Возвращает левую и верхнюю позицию элемента относительно тела документа в свойствах left и top соответственно.
 * @param element object - элемент.
 * @return {left: x, top: y} - координаты элемента.
 */
function getPos(element) {
  var offsetLeft = 0, offsetTop = 0;
  do {
      offsetLeft += element.offsetLeft;
      offsetTop  += element.offsetTop;
  } while (element = element.offsetParent);
  var pos = {left: offsetLeft, top: offsetTop};
  return pos;
}

/**
 * Возвращает размеры документа.
 */
function documentSize() {
  //var dxScroll = (window.scrollX != null) ? window.scrollX : document.documentElement.scrollLeft; // для Chrome, IE
  var scroll = offsetScroll();
  var w = gebtn('body')[0].offsetWidth + scroll.left;
  var h = gebtn('body')[0].offsetHeight;
  return [w, h];
}

/**
 * Возвращает левую и верхнюю позицию скролла в окне.
 */
function offsetScroll() {
  var dxScroll = (window.scrollX != null) ? window.scrollX : document.documentElement.scrollLeft; // для Chrome, IE
  var dyScroll = (window.scrollY != null) ? window.scrollY : document.documentElement.scrollTop; // для Chrome, IE
  var scroll = {
    left: dxScroll,
    top: dyScroll
  }
  return scroll;
}

/**
 * Возвращает размеры элемента или рабочей области.
 * el (object) элемент, если не указан, то вся рабочая область.
 */
function elementSize(el) {
  if (el) {
    var size = [el.offsetWidth, el.offsetHeight];
  } else {
    var h = window.innerHeight ? window.innerHeight : (document.documentElement.clientHeight ? document.documentElement.clientHeight : document.body.offsetHeight);
    var w = window.innerWidth ? window.innerWidth : (document.documentElement.clientWidth ? document.documentElement.clientWidth : document.body.offsetWidth);
    var size = [w, h];
  }
  return size;
}

/**
 * Центрует элемент в окне браузера.
 * el (object) элемент.
 */
function centeringElementWindow(el) {
  var win = elementSize();
  var el_size = elementSize(el);
  var scroll = offsetScroll();
  var doc_size = documentSize();
  var left = (win[0] - el_size[0]) / 2 + scroll.left;
  var top = (win[1] - el_size[1]) / 2 + scroll.top;
  top = top + el_size[1] > doc_size[1] ? doc_size[1] - el_size[1] : top;
  setLeft(el, left);
  setTop(el, top);
}

/**
 * Возвращает внешние размеры браузера.
 */
function winSize() {
  var w = window.outerWidth ? window.outerWidth : (document.documentElement.offsetWidth);
  var h = window.outerHeight ? window.outerHeight : (document.documentElement.offsetHeight);
  return [w, h];
}

/**
 * Устанавливает размеры браузера (IE, FF).
 */
function setWinSize(w, h) {
  window.resizeTo(w, h);
}

/**
 * Устанавливает высоту элемента в пикселах.
 */
function setH(el, h) {
  el.style.height = h + 'px';
}

/**
 * Устанавливает ширину элемента в пикселах.
 */
function setW(el, w) {
  el.style.width = w + 'px';
}

/**
 * Устанавливает левую позицию элемента в пикселах.
 */
function setLeft(el, l) {
  el.style.left = l + 'px';
}

/**
 * Устанавливает верхнюю позицию элемента в пикселах.
 */
function setTop(el, t) {
  el.style.top = t + 'px';
}

/**
 * Определяет позицию top элемента, необходимую для его вырвнивания по вертикали относительно родительского элемента.
 * Внимание! Данная функция работает коректно, если для элемента и его родителя явно заданы свойства CSS:
 * position(relative/absolute), height(%/px/...).
 * Также необходимо элементу body задавать параметр CSS - position: absolute.
 *
 * in_el
 * элемент.
 *
 * in_relative
 * true или false, в зависимости от того как позиционирован элемент (absolute/relative).
 * Если элемент позиционирован абсолютно, то необходимо передать этому параметру false.
 * Если элемент позиционирован относительно и вы хотите, чтобы он выравнивался по вертикали относительно родителя,
 * но с учётом верхних элементов (если такие есть), которые тоже позиционированы относительно родителя,
 * то передайте true.
 *
 * return
 * вертикальная позиция элемента (только число, без постфикса 'px') или false.
 */
function getCenterVertical(in_el, in_relative) {
  if (in_el == null) {
    return false;
  }
  var parentHeight = in_el.parentNode.offsetHeight;
  var elHeight = in_el.offsetHeight;
  var elTop = 0;
  if (in_relative == true) {
    elTop = in_el.offsetTop;
  }

  // Высота родительского элемента не должна быть меньше высоты дочернего, определяем истинную высоту
  // родительского элемента. DOCTYPE XHTML 1.1 определяет высоту родительского элемента не коректно.
  if (parentHeight + elTop < elHeight) {
    var parentEls = in_el.parentNode.childNodes;
    var height = 0;
    if (in_relative == true) {
      for (var i = 0; i < parentEls.length; i++) {
        if ((parentEls[i].nodeValue == null)) {
          height = height + parentEls[i].offsetHeight;
        }
      }
      parentHeight = height;
    } else {
      parentHeight = elHeight;
    }
  }

  elTop = (parentHeight - elHeight - elTop) / 2;
  return elTop;
}

/**
 * Определяет позицию left элемента, необходимую для его вырвнивания по горизонтали
 * относительно родительского элемента.
 * Внимание! Данная функция работает коректно, если для элемента и его родителя явно заданы свойства CSS:
 * position(relative/absolute), width(%/px/...).
 * Также необходимо элементу body задавать параметр CSS - position: absolute.
 *
 * in_el
 * элемент.
 *
 * return
 * горизонтальная позиция элемента (только число, без постфикса 'px') или false.
 */
function getCenterHorizontal(in_el) {
  if (in_el == null) {
    return false;
  }
  var parentWidth = in_el.parentNode.offsetWidth;
  var elWidth = in_el.offsetWidth;
  var elLeft = in_el.offsetLeft;
  elLeft = (parentWidth - elWidth) / 2;
  return elLeft;
}

/**
 * Устанавливает элемент по центру вертикально.
 * Внимание! Данная функция работает коректно, если для элемента и его родителя явно заданы свойства CSS:
 * position(relative/absolute), width(%/px/...).
 * Также необходимо элементу body задавать параметр CSS - position: absolute.
 *
 * in_el
 * элемент.
 *
 * in_relative
 * true или false, в зависимости от того как позиционирован элемент (absolute/relative).
 * Если элемент позиционирован абсолютно, то необходимо передать этому параметру false.
 * Если элемент позиционирован относительно и вы хотите, чтобы он выравнивался по вертикали относительно родителя,
 * но с учётом верхних элементов (если такие есть), которые тоже позиционированы относительно родителя,
 * то передайте true.
 */
function setCenterVertical(in_el, in_relative) {
  var topEl = getCenterVertical(in_el, in_relative);
  in_el.style.top = topEl + 'px';
}

/**
 * Устанавливает элемент по центру вертикально.
 * Внимание! Данная функция работает коректно, если для элемента и его родителя явно заданы свойства CSS:
 * position(relative/absolute), width(%/px/...).
 * Также необходимо элементу body задавать параметр CSS - position: absolute.
 *
 * in_el
 * элемент.
 */
function setCenterHorizontal(in_el) {
  var leftEl = getCenterHorizontal(in_el);
  in_el.style.left = leftEl + 'px';
}

/**
 * Разворачивает слой по вертикали.
 * id - айди слоя, который необходимо развернуть.
 */
function rollYOn(id) {
  var el = gebi(id);
  el.style.height = 'auto';
}

/**
 * Сворачивает слой по вертикали.
 * id - айди слоя, который необходимо свернуть.
 * dy - шаг изменения высоты в пикселях.
 * height - высота блока.
 * return - таймер.
 */
function rollY(id, dy, height_max, height_min) {
  var el = gebi(id);
  var height_max = height_max ? height_max : el.offsetHeight;
  var height_min = height_min ? height_min : 0;
  timer[el.id + "_roll"] = setInterval(function() {_roll(el, dy, height_max, height_min);}, 1);
  return timer[el.id + "_roll"];
}

/**
 * Скрывает все select на странице.
 * ignore_arr - массив элементов, которые игнорируются при скрытии.
 */
var hidden_selects = new Array();
function hiddenSelects(ignore_arr) {
  var selects = gebtn('select');
  for (var i = 0; i < selects.length; i++) {
    if (selects[i].style.visibility != 'hidden' && !in_array(selects[i], ignore_arr)) {
      hidden_selects.push(selects[i]);
      selects[i].style.visibility = 'hidden';
    }
  }
}

/**
 * Показывает все скрытые select на странице.
 */
function visibleSelects() {
  for (var i = 0; i < hidden_selects.length; i++) {
    hidden_selects[i].style.visibility = 'visible';
  }
}

/**
 * Скрывает все object на странице.
 */
function hiddenObjects() {
  var objs = gebtn('object');
  for (var i = 0; i < objs.length; i++) {
    objs[i].style.visibility = 'hidden';
  }
}

/**
 * Показывает все скрытые object на странице.
 */
function visibleObjects() {
  var objs = gebtn('object');
  for (var i = 0; i < objs.length; i++) {
    objs[i].style.visibility = 'visible';
  }
}

/**
 * Вспомогательная функция для сворачивания и разворачивания слоёв.
 * Не используйте её!
 */
function _roll(el, dy, height_max, height_min) {
  var y = el.offsetHeight;
  if ((y >= height_min && dy > 0 && (y + dy <= height_max)) || (y >= height_min && dy < 0 && (y + dy >= 0))) {
    y = y + dy;
    el.style.height = y + 'px';
  } else {
    if (dy > 0) {
      el.style.height = height_max + 'px';//'100%';
      clearInterval(timer[el.id + "_roll"]);
    } else {
      if (dy < 0) {
        el.style.height = height_min + 'px';
        clearInterval(timer[el.id + "_roll"]);
      } else {
        clearInterval(timer[el.id + "_roll"]);
      }
    }
  }
}

/**
 * Вызывается из opacityVisible() и является вспомогательной.
 * Функция изменяет прозрачность элемента и по завершении вызывает функцию,
 * определённую пользоателем, если это необходимо.
 * Не используйте её!
 *
 * elem
 * элемент.
 *
 * dOp
 * шаг изменения прозрачности (от 1 до 100).
 *
 * finishOp
 * конечное значение прозрачности.
 *
 * in_t
 * уникальный таймер.
 *
 * in_func
 * ассоциативный массив, содержащий в себе имя функции
 * (которая будет вызвана после изменения прозрачности) и её параметры.
 * Первый элемент массива всегда должен называться 'name'.
 * Пример структуры массива:
 * var funcArray = {
 *   name: 'simpleFuncName',
 *   img: in_img,
 *   time: in_time
 * };
 * Данный параметр не обязателен, если вы не хотите вызывать какую-либо функцию
 * после изменения прозрачности.
 *
 * return
 * false в случае невозможности изменения прозрачности элемента, либо ничего не возвращает.
 */
function _opacity(elem, dOp, finishOp, in_t, in_func) {
  var curOp = getOpacity(elem);
  if (curOp == false) {
    clearInterval(timer[in_t]);
    timer[in_t] = null;
    return false;
  }
  curOp = Math.round(curOp + dOp);
  if (((curOp >= finishOp) && (dOp > 0)) || ((curOp <= finishOp) && (dOp < 0))) {
    curOp = finishOp;
    clearInterval(timer[in_t]);
    timer[in_t] = null;
    if (in_func != null) {
      _gotoFunc(in_func);
    }
  } else if (curOp <= 0) {
    curOp = 0;
    clearInterval(timer[in_t]);
    timer[in_t] = null;
    if (in_func != null) {
      _gotoFunc(in_func);
    }
  }
  setOpacity(elem, curOp);
}

/**
 * Вызывается из moveResizeElement() и является вспомогательной.
 * Функция изменяет прозрачность элемента и по завершении вызывает функцию,
 * определённую пользоателем, если это необходимо.
 * Не используйте её!
 *
 * in_el
 * элемент.
 *
 * in_t
 * уникальный таймер.
 *
 * in_time
 * время перемещения (в мс).
 * Этот параметр сильно зависит от используемого браузера.
 *
 * in_left
 * горизонтальная позиция элемента, в которую необходимо его переместить.
 * Позиция задаётся относительно родительского элемента.
 *
 * in_top
 * вертикальная позиция элемента, в которую необходимо его переместить.
 * Позиция задаётся относительно родительского элемента.
 *
 * in_width
 * ширина элемента в конце перемещения.
 *
 * in_height
 * высота элемента в конце перемещения.
 *
 * in_func
 * ассоциативный массив, содержащий в себе имя функции
 * (которая будет вызвана после изменения прозрачности) и её параметры.
 * Первый элемент массива всегда должен называться 'name'.
 * Пример структуры массива:
 * var funcArray = {
 *   name: 'simpleFuncName',
 *   img: in_img,
 *   time: in_time
 * };
 * Данный параметр не обязателен, если вы не хотите вызывать какую-либо функцию
 * после изменения прозрачности.
 */
count['_moveResize'] = 0; // внутренний счётчик количества вызовов функции
function _moveResize(in_el, in_t, in_k, in_left, in_top, in_width, in_height, in_func) {
  // Если счётчик обращений к функции равен переданному временному интервалу, то завершаем работу функции.
  if (count['_moveResize'] == in_k) {
    count['_moveResize'] = 0;
    in_el.style.left = in_left + 'px';
    in_el.style.top = in_top + 'px';
    in_el.style.width = in_width + 'px';
    in_el.style.height = in_height + 'px';
    clearInterval(timer[in_t]);
    // Передача управления следующей функции.
    if (in_func != null) {
      _gotoFunc(in_func);
    }
  }
  // Коэфициент деления.
  var k = in_k - count['_moveResize'];
  // Вычисление интервалов для изменения позиции и размера элемента.
  var dL = (in_left - getLeft(in_el)) / k;
  var dT = (in_top - getTop(in_el)) / k;
  var dW = (in_width - in_el.offsetWidth) / k;
  var dH = (in_height - in_el.offsetHeight) / k;
  // Изменение позиции и размера элемента.
  in_el.style.left = getLeft(in_el) + dL + 'px';
  in_el.style.top = getTop(in_el) + dT + 'px';
  in_el.style.width = in_el.offsetWidth + dW + 'px';
  in_el.style.height = in_el.offsetHeight + dH + 'px';
  // Увиличием счётчик обращений к функции.
  count['_moveResize']++;
}

/**
 * Вызывает функцию пользователя и передаёт ей параметры. Является вспомогательной.
 * Не используйте её!
 *
 * in_func
 * ассоциативный массив, содержащий в себе имя функции
 * (которая будет вызвана после изменения прозрачности) и её параметры.
 * Первый элемент массива всегда должен называться 'name'.
 * Пример структуры массива:
 * {
 *   name: 'simpleFuncName',
 *   img: in_img,
 *   time: in_time
 * };
 */
function _gotoFunc(in_func) {
  func[in_func['name']](in_func);
}











/************************************************************************
 *                              События                                 *
 ************************************************************************/

function __GOTO_EVENT() {};

/**
 * Возвращает массив данных произошедшего собятия.
 * e - объект события.
 * return (array) - событие, элемент.
 */
/*function event(e) {
  return event = (e ? e : (window.event ? window.event : null));
  //var elem = event.target ? event.target : (event.srcElement ? event.srcElement : null);
  //return [event, elem];
}*/

/**
 * Возвращает координаты мыши относительно документа.
 * evt - объект события.
 * return (array) - координаты x,y.
 */
function getCoords(evt) {
  var coords = {x: 0, y: 0};
  if (evt.pageX) {
    coords.x = evt.pageX;
    coords.y = evt.pageY;
  } else if (evt.clientX) {
    var scroll = offsetScroll();
    coords.x = evt.clientX + scroll.left;//document.body.scrollLeft - document.body.clientLeft;
    coords.y = evt.clientY + scroll.top;//document.body.scrollTop - document.body.clientTop;
    /*if (document.body.parentElement && document.body.parentElement.clientLeft) {
      var bodPar = document.body.parentElement;
      coords.x += bodPar.scrollLeft + bodPar.clientLeft;
      coords.y += bodPar.scrollTop + bodPar.clientTop;
    }*/
  }
  return coords;
}

/**
 * Возвращает координаты мыши относительно экрана.
 * evt - объект события.
 * return (array) - координаты x,y.
 */
function getCoordsScreen(evt) {
  var coords = {x: evt.screenX, y: evt.screenY};
  return coords;
}

/**
 * Возвращает координаты мыши относительно элемента, на котором сработало событие.
 * evt - объект события.
 * elem - элемент, на котором сработало событие.
 * return (array) - координаты x,y.
 */
function getCoordsElement(evt, elem) {
  var coords = {x: 0, y: 0};
  var pos = offsetPosition(elem);
  //alert(pos);
  var left = pos[0];
  var top = pos[1];
  if (evt.pageX) {
    coords.x = evt.pageX - left;
    coords.y = evt.pageY - top;
  } else if (evt.clientX) {
    coords.x = evt.clientX - left;
    coords.y = evt.clientY - top;
  }
  return coords;
}







/************************************************************************
 *               Формирование сложных элементов на экране               *
 ************************************************************************/

function __GOTO_HTML_FRAMEWORK() {};

/**
 * Создаёт окошко с текстом.
 *
 * @param in_mes (string):
 * выводимое сообщение.
 *
 * @param in_class (string):
 * класс CSS для окна.
 *
 * @return (object):
 * элемент div, представляющий собой окошко с текстом.
 */
function getMessageBox(in_mes, in_class) {
  var newDiv = ce('div');
  newDiv.className = in_class;
  var textDiv = ce('div');
  var newText = ctn(in_mes);
  textDiv.appendChild(newText);
  newDiv.appendChild(textDiv);
  return newDiv;
}

/**
 * Создаёт слой на всю страницу, который затеняет её.
 *
 * @param in_beg_op (int):
 * начальная прозрачность от 1 до 100.
 *
 * @param in_fin_op (int):
 * конечная прозрачность от 1 до 100.
 *
 * @param in_class (string):
 * класс CSS , в котором определены цвет фона, координаты и прозрачность. Например:
 * .shadowpage {
 *  position: absolute;
 *  left: 0px;
 *  top: 0px;
 *  background: #000000;
 *  opacity: 0.01;
 *  filter: alpha(Opacity = 1);
 * }
 *
 * @param in_func (string):
 * название функции, которой необходимо передать управление после показа слоя.
 */
function showShadowPage(in_beg_op, in_fin_op, in_class, in_func) {
  var newDiv = ce('div');
  gebtn('body')[0].appendChild(newDiv);
  newDiv.className = in_class;
  newDiv.style.width = gebtn('body')[0].offsetWidth + 'px';
  newDiv.style.height = gebtn('body')[0].offsetHeight + 'px';
  var dOp = 10;
  var beginOp = in_beg_op;
  var finishOp = (in_fin_op >= in_beg_op) ? in_fin_op : in_beg_op;
  var interTime = 2;
  var timer = 'shadow';
  var funcArr = {
    name: in_func,
    div: newDiv
  };
  opacityVisible(newDiv, dOp, beginOp, finishOp, interTime, timer, funcArr);
}

/**
 * Создаёт слой с прогрессбаром и возвращает его.
 *
 * @param in_class_back (string):
 * класс CSS , в котором определены параметры фона прогрессбара и его размеры.
 *
 * @param in_class_bar (string):
 * класс CSS , в котором определены параметры самого прогрессбара и его размеры (ширина динамическая).
 *
 * @return (object):
 * элемент div, представляющий собой прогрессбар.
 */
var prBarDiv = null; // Слой, который отображает шкалу прогрессбара.
var prPcentDiv = null; // Слой, который отображает цифры прогрессбара.
function createProgressBar(in_class_back, in_class_bar) {
  var newDiv = ce('div');
  newDiv.className = in_class_back;
  prBarDiv = ce('div');
  prBarDiv.className = in_class_bar;
  prPcentDiv = ce('div');
  newDiv.appendChild(prBarDiv);
  var prBarText = ctn('0%');
  prPcentDiv.appendChild(prBarText);
  newDiv.appendChild(prPcentDiv);
  return newDiv;
}

/**
 * Задаёт в процентах текущее значение прогрессбара.
 *
 * @param in_percent (int):
 * значение прогрессбара от 0 до 100.
 */
function setProgressBar(in_percent) {
  prBarDiv.style.width = in_percent + '%';
  prPcentDiv.innerHTML = in_percent + '%';
}








/************************************************************************
 *                 Функции для работы с элементами HTML                 *
 ************************************************************************/

function __GOTO_HTML() {};

/**
 * Устанавливает или снимает галочку в чекбоксе.
 *
 * @param in_id (string):
 * айди чекбокса.
 *
 * @return (bool):
 * true в случае успеха и false, если чекбокс не существует.
 */
function checkBox(in_id) {
  if (gebi(in_id)) {
    gebi(in_id).checked = (gebi(in_id).checked == true) ? false : true;
    // Для чекбокса, в котором галочка отображается не стандартными средствами,
    // а с помощью картинки с айди 'img_' + айди чекбокса.
    if (gebi('img_' + in_id)) {
      gebi('img_' + in_id).style.visibility = (gebi(in_id).checked == true) ? 'visible' : 'hidden';
    }

    // Передаём управление пользовательской функции, которая будет обслуживать клик по чекбоксу.
    // Эта функция должна быть определена в вашем скрипте.
    // С помощью неё вы сможете произвести дополнитльную обработку при клике по чекбоксу.
    // Если в ней нет необходимости, то просто создайте пустую функцию.
    // function onclickCheckbox(in_id) {}.
    onclickCheckbox(in_id);
    return true;
  } else {
    return false;
  }
}

/*>>>>>>>>>>>>>>>>>>>>>>>>>>>
  > Начало класса Checkbox. >
  >>>>>>>>>>>>>>>>>>>>>>>>>>>*/
/*
  Конструктор.
  @param group_name (string) - имена чекбоксов в группе (simplegroupename[]).
  @param el_group (objectHTMLInput) групповой чекбокс.
 */
function Checkbox(group_name, el_group) {
  this.group_name = group_name;
  this.el_group = el_group;
  // группа чекбоксов
  var chbox_arr = gebtn('input');
  this.group_chbox = [];
  for (var i = 0; i < chbox_arr.length; i++) {
    var name = chbox_arr[i].name.replace(/\[[0-9]+\]/, '[]');
    if (name == this.group_name) {
      this.group_chbox.push(chbox_arr[i]);
    }
  }
}
/*
  Прототип.
 */
Checkbox.prototype = {}
/*
  Отмечает или снимает галочки с группы чекбоксов.
  @param el (objectHTMLInput) чекбокс.
 */
Checkbox.prototype.mark_group = function(el) {
  if (el == this.el_group) {
    if (el.checked) {
      this.mark_all(true);
      //alert('Check general chbox');
    } else {
      this.mark_all(false);
      //alert('Uncheck general chbox');
    }
  } else {
    this.check_mark_group();
    //alert('This is simple chbox');
  }
}
/*
  Отмечает или снимает галочки со всех чекбоксов в группе.
  @param check (bool) true - значит отметить все, false - снять все.
 */
Checkbox.prototype.mark_all = function(check) {
  for (var i = 0; i < this.group_chbox.length; i++) {
    this.group_chbox[i].checked = check;
  }
}
/*
  Отмечает или снимает галочки со всех чекбоксов в группе.
  @param check (bool) true - значит отметить все, false - снять все.
 */
Checkbox.prototype.check_mark_group = function() {
  var need_check_all = !this.el_group.checked;
  var need_uncheck_all = this.el_group.checked;
  for (var i = 0; i < this.group_chbox.length; i++) {
    if (this.group_chbox[i].checked == false) {
      need_check_all = false;
    } else {
      need_uncheck_all = false;
    }
  }
  if (need_check_all || need_uncheck_all) {
    this.mark_all(need_check_all || !need_uncheck_all);
  }
  this.el_group.checked = need_check_all;
}
/*<<<<<<<<<<<<<<<<<<<<<<<<<<
  < Конец класса Checkbox. <
  <<<<<<<<<<<<<<<<<<<<<<<<<<*/


/*>>>>>>>>>>>>>>>>>>>>>>>>>>>
  > Начало класса TextCaret >
  >>>>>>>>>>>>>>>>>>>>>>>>>>>*/

/*
  Конструктор
  @param el_text (obj) поле для ввода текста, например textarea
 */
function TextCaret(el_text) {
  this.el_text = el_text ? el_text : null;
}
/*
  Прототип
 */
TextCaret.prototype = {}
/*
  Возвращает позицию курсора
  @return int позиция курсора (отсчет от 0)
 */
TextCaret.prototype.getPos = function () {
  if (!this.el_text) return this.el_text;
  this.el_text.focus();
  var pos = this.el_text.selectionStart ? this.el_text.selectionStart : 0;
  if (document.selection) { // IE
    var sel = document.selection.createRange();
    var clone = sel.duplicate();
    sel.collapse(true);
    clone.moveToElementText(this.el_text);
    clone.setEndPoint('EndToEnd', sel);
    pos = clone.text.length;
  }
  return pos;
}
/*
  Возвращает выделенный текст
  @return text_ins (string) текст
 */
TextCaret.prototype.getText = function () {
  if (!this.el_text) return null;
  this.el_text.focus();
  var text_sel = "";
  if (this.el_text.selectionStart || this.el_text.selectionStart == 0) {
    var start = this.el_text.selectionStart;
    var end = this.el_text.selectionEnd;
    text_sel = this.el_text.value.substring(start, end);
  }
  if (document.selection) { // IE, Внимание! При потери фокуса объект всегда пуст, при необходимости сохраняйте его до потери фокуса и присваивайте перед тем как заменить в нем выделенный текст
    var sel = document.selection.createRange();
    text_sel = sel.text;
  }
  return text_sel;
}
/*
  Вставляет текст, начиная с текущей поизиции курсора
  @param text_ins (string) текст
 */
TextCaret.prototype.insText = function (text_ins) {
  if (!this.el_text) return;
  this.el_text.focus();
  if (this.el_text.selectionStart || this.el_text.selectionStart == 0) {
    var start = this.el_text.selectionStart;
    var end = this.el_text.selectionEnd;
    this.el_text.value = this.el_text.value.substring(0, start) + text_ins + this.el_text.value.substring(end, this.el_text.value.length);
    this.el_text.selectionStart = this.el_text.selectionEnd = start + text_ins.length;
  }
  if (document.selection) { // IE, Внимание! При потери фокуса объект всегда пуст, при необходимости сохраняйте его до потери фокуса и присваивайте перед тем как заменить в нем выделенный текст
    var sel = document.selection.createRange();
    sel.text = text_ins;
  }
}
/*
  Устанавливает курсор в конец
 */
TextCaret.prototype.toEnd = function () {
  if (!this.el_text) return;
  this.el_text.focus();
  if (this.el_text.selectionStart) {
    this.el_text.setSelectionRange(this.el_text.value.length, this.el_text.value.length);
  }
  if (this.el_text.createTextRange) { // IE
    var r = this.el_text.createTextRange();
    r.collapse(false);
    r.select();
  }
}
/*
  Устанавливает курсор в начало
 */
TextCaret.prototype.toStart = function () {
  if (!this.el_text) return;
  this.el_text.focus();
  if (this.el_text.selectionStart) {
    this.el_text.setSelectionRange(0, 0);
  }
  if (this.el_text.createTextRange) { // IE
    var r = this.el_text.createTextRange();
    r.collapse(true);
    r.select();
  }
}

/*<<<<<<<<<<<<<<<<<<<<<<<<<<
  < Конец класса TextCaret <
  <<<<<<<<<<<<<<<<<<<<<<<<<<*/





/************************************************************************
 *                     Функции для работы с массивами                   *
 ************************************************************************/

function __GOTO_ARRAY() {};

/**
 * Ищет значение в массиве и возвращает true, если находи, иначе false.
 * search - элемент поиска.
 * stakc_arr - массив в котором происходит поиск.
 */
function in_array(search, stack_arr) {
  for (val in stack_arr) {
    if (stack_arr[val] == search) {
      return true;
    }
  }
  return false;
}



/************************************************************************
 *                       Функции для работы с ООП                       *
 ************************************************************************/

function __GOTO_OOP() {};

/**
 * Наследует объект Parent для объекта Child.
 */
function extend(Child, Parent) {
  var F = function() { }
  F.prototype = Parent.prototype
  Child.prototype = new F()
  Child.prototype.constructor = Child
  Child.superclass = Parent.prototype
}

/**
 * Клонирует объект
 */
function clone(o) {
  if(!o || 'object' !== typeof o)  {
   return o;
  }
  var c = 'function' === typeof o.pop ? [] : {};
  var p, v;
  for(p in o) {
    if(o.hasOwnProperty(p)) {
      v = o[p];
      if(v && 'object' === typeof v) {
        c[p] = clone(v);
      } else {
        c[p] = v;
      }
    }
  }
  return c;
}


/**********
 * РАЗНОЕ *
 **********/

function __GO_TO_ANOTHER() {};

/**
 * Используется для установки стартовой страницы.
 * Вызов из html:
 * <a href="#" onclick="sHomePage(this, 'http://www.mysite.ru/'); return false;">Сделать страницу стартовой</a>
 */
function gHomePage(objSrc, homepage) {
  objSrc.style.behavior='url(#default#homepage)';
  objSrc.setHomePage(homepage);
}

/**
 * Редирект на страницу page через time миллисекунд
 */
function redirect(page, time) {
  var page = page ? page : '/';
  var time = time ? time : 0;
  setTimeout(function (){location = page;}, time);
}


/**********
 * jQuery *
 **********/

function __GO_TO_JQUERY() {};

/**
 * Контролирует ввод, чтобы содержимое поля ввода соответствовало формату IPv4 aaa.bbb.ccc.ddd
 * Применение: $(selector).inputbox_ip();
 */
jQuery.fn.inputbox_ip = function() {
  var me = this;
  var ip;
  var key_0 = 48;
  var key_9 = 57;
  var key_dot = 190; // точка
  var key_bspace = 8; // backspace
  var key_ins = 45; // insert
  var key_del = 46; // delete
  var key_left = 37; // left arrow
  var key_right = 39; // right arrow

  $(me).keydown(function (event){
    var key_array = [key_bspace, key_del, key_left, key_right];
    var key = event.keyCode;
    ip = $(me).val();
    //alert(event.code);
    if (key >= key_0 && key <= key_9 || key == key_dot && !/^[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{1,3}$/.test(ip)) {
      var next_ip = ip + (key == key_dot ? '.' : (key - 48));
      if (!/^[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{3}$/.test(ip) && !/[.]{2}$/.test(next_ip)) {
        next_ip = !/^[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{0,3}\.[0-9]{3}$/.test(next_ip) ? next_ip.replace(/([0-9]{3})$/, '$1.') : next_ip;
        if (/^([0-9]{0,3}\.)?([0-9]{0,3}\.)?([0-9]{0,3}\.)?([0-9]{0,3})?$/.test(next_ip)) {
          $(me).val(next_ip);
        }
      }
      event.preventDefault();
    } else if (key == key_ins) {
      return;
    } else if (!in_array(key, key_array)) {
      event.preventDefault();
    }
  });

  $(me).keyup(function (event){
    var key = event.keyCode;
    var ins_ip = $(me).val();
    ins_ip = ins_ip.replace(/^([0-9]{3})([0-9]{3})([0-9]{3})([0-9]{1,3})$/, '$1.$2.$3.$4');
    ins_ip = ins_ip.replace(/^([0-9]{3})([0-9]{3})([0-9]{1,3})$/, '$1.$2.$3');
    ins_ip = ins_ip.replace(/^([0-9]{3})([0-9]{1,3})$/, '$1.$2');
    ins_ip = ins_ip.replace(/^([0-9]{3})$/, '$1.');
    if (!/^([0-9]{0,3}\.)?([0-9]{0,3}\.)?([0-9]{0,3}\.)?([0-9]{0,3})?$/.test(ins_ip)) {
      $(me).val(ip);
    } else {
      ip = ins_ip;
      $(me).val(ip);
    }
  });
};

/**
 * Контролирует ввод, чтобы поле ввода содержало только цифры
 * Применение: $(selector).inputbox_number();
 */
jQuery.fn.inputbox_number = function() {
  var me = this;
  var maxlen = $(me).attr('maxlength') ? $(me).attr('maxlength') : false;
  var value;
  var key_0 = 48;
  var key_9 = 57;
  var key_bspace = 8; // backspace
  var key_ins = 45; // insert
  var key_del = 46; // delete
  var key_left = 37; // left arrow
  var key_right = 39; // right arrow

  $(me).keydown(function (event){
    var key_array = [key_bspace, key_del, key_left, key_right];
    var key = event.keyCode;
    value = $(me).val();
    //alert(event.code);
    if (key >= key_0 && key <= key_9) {
      if (!maxlen || maxlen && value.length < maxlen) {
        var next_value = value + (key - key_0);
        $(me).val(next_value);
      }
      event.preventDefault();
    } else if (key == key_ins) {
      return;
    } else if (!in_array(key, key_array)) {
      event.preventDefault();
    }
  });

  $(me).keyup(function (event){
    var key = event.keyCode;
    var ins_value = $(me).val();
    if (key == key_ins) {
      ins_value = ins_value.replace(/[^0-9]+/ig, '');
      if (!/^[0-9]+$/.test(ins_value)) {
        $(me).val(value);
      } else {
        value = ins_value;
        $(me).val(value);
      }
    }
  });
};

/**
 * Таймер обратного отсчета
 * @param id_str (string) список айди элементов, содержащих секунды, минуты, часы, дни ('sec,min,hour,day')
 *
 * Объект генерирует следующие события (при достижении нуля какого-либо из параметров):
 * timerEventSec - генерируется каждую секунду
 * timerEventMin - генерируется каждую минуту
 * timerEventHour - генерируется каждый час
 * timerEventDay - генерируется каждые сутки
 * timerEventEnd - генерируется при обнулении всех параметров (остановка таймера)
 */
jQuery.fn.timerCountdown = function (id_str) {
  //defaultStatus = id_str;
  var me = this;
  var id_arr = new Array;
  while ((id = /[\w\-]+/.exec(id_str)) !== null) {
    var reg_id = new RegExp('('+id+' ?,? ?)');
    id_str = id_str.replace(reg_id, '');
    id_arr.push(id);
  }
  var sec = $('#'+id_arr[0]);
  var sec_str = $(sec).text();
  var min = $('#'+id_arr[1]);
  var min_str = $(min).text();
  var hour = $('#'+id_arr[2]);
  var hour_str = $(hour).text();
  var day = $('#'+id_arr[3]);
  var day_str = $(day).text();
  //alert([sec_str, min_str, hour_str, day_str]);
  var myTimer = setInterval(timerEventStart, 1000);

  function timerEventStart () {
    var pat = new RegExp('^([0-9])$');
    if (sec_str == 0) {
      if (min_str == 0) {
        if (hour_str == 0) {
          if (day_str == 0) {
            $(me).trigger('timerEventEnd');
            clearInterval(myTimer);
            return;
          }
          day_str--;
          $(day).text(day_str);
          $(me).trigger('timerEventDay');
          hour_str = 24;
        }
        hour_str--;
        hour_str = hour_str.toString().replace(pat, '0$1');
        $(hour).text(hour_str);
        $(me).trigger('timerEventHour');
        min_str = 60;
      }
      min_str--;
      min_str = min_str.toString().replace(pat, '0$1');
      $(min).text(min_str);
      $(me).trigger('timerEventMin');
      sec_str = 60;
    }
    sec_str--;
    //defaultStatus = sec_str;
    sec_str = sec_str.toString().replace(pat, '0$1');
    $(sec).text(sec_str);
    $(me).trigger('timerEventSec');
  }
}

/**
 * Триггер для скрытия текста по-умолчанию в текстовое поле при получении им фокуса
 */
jQuery.fn.hinttext = function () {
  var me = this;
  var text = $(me).val();
  $(me).focus(function () {
    if ($(this).val() == text) {
      $(this).val('');
    }
  });
  $(me).blur(function () {
    if ($(this).val() == '') {
      $(this).val(text);
    }
  });
  $(me).keypress(function (e) {
    var KEY_ENTER = 13;
    var key = e.keyCode;
    if (key != KEY_ENTER || $(this).val() == '') return;
    $(me).trigger('keyEnter');
  });

}
