/* $Id: lib.js 2017 2008-04-30 13:22:15Z chelout $ */

// EVENT BINDING *****************************************************************
var _eventRegistry = [];
var _lastEventId = 0;

/**
 * Добавление обработчика события к объекту
 * @param {Object} object
 * @param {String} eventName без 'on'
 * @param {Object} eventHandler
 * @param {Boolean} НЕ использовать конструкцию eventHandler.apply(object) в IE
 *  использование apply позволяет в IE обращаться к object в eventHandler как
 *  к this (т.е. как в Mozilla)
 * @return {Number} eventId
 */
function bindEvent(object, eventName, eventHandler, dontAddApplyInExplorer) {

  var fn = eventHandler;
  if (object.addEventListener) {
    object.addEventListener(eventName, fn, false);
  }
  else if (object.attachEvent) {
    if (!dontAddApplyInExplorer) fn = function() { eventHandler.apply(object); }
    object.attachEvent("on" + eventName, fn);
  }
  // добавлен "event": чтобы не "съезжали" id при удалении события из реестра
  var eventId = "event" + _lastEventId++;
  _eventRegistry[eventId] = {object: object, eventName: eventName, eventHandler: fn};
  return eventId;
}

/**
 * Удаление обработчика события eventId, добавленного через bindEvent()
 * @param {Object} eventId
 * @return {Boolean}
 */
function unbindEvent(eventId) {

  if (!_eventRegistry[eventId] || typeof _eventRegistry[eventId] != 'object') return false;

  var object = _eventRegistry[eventId].object;
  var eventName = _eventRegistry[eventId].eventName;
  var eventHandler = _eventRegistry[eventId].eventHandler;

  if (object.removeEventListener) {
    object.removeEventListener(eventName, eventHandler, false);
  }
  else if (object.detachEvent) {
    object.detachEvent("on" + eventName, eventHandler);
  }

  _eventRegistry.splice(eventId, 1);

  return true;
}

/**
  * отвязка всех событий
  */
function unbindAllEvents() {
  for (var i in _eventRegistry) {
    try { unbindEvent(i); } catch(e) {}
  }
}

// remove all events on page unload to prevent memory leaks
bindEvent(window, 'unload', unbindAllEvents);


/**
 * Позиция объекта относительно BODY или объекта с id=STOPID
 * @param {Object} object
 * @param {String} stopObjectId
 * @param {Boolean} addFrameOffset

 * @return {Object} {left: x, top: y}
 */
function getOffset(object, stopObjectId, addFrameOffset) {

  var pos = { top: 0, left: 0 };

  // weak chain
  if (addFrameOffset) {
    if (object.ownerDocument.defaultView) {
      pos.top  = object.ownerDocument.defaultView.frameOffset.top -
                object.ownerDocument.body.scrollTop;
      pos.left = object.ownerDocument.defaultView.frameOffset.left -
                 object.ownerDocument.body.scrollLeft;
    }
    else {
      pos.top = object.ownerDocument.parentWindow.frameOffset.top -
                object.ownerDocument.body.scrollTop;
      pos.left = object.ownerDocument.parentWindow.frameOffset.left -
                 object.ownerDocument.body.scrollLeft;
    }
  }

  var isIE = (document.all ? true : false); // weak chain

/*
  if (isIE) {
    // баг IE? если высота объекта не задана и он находится внутри
    // iframe, то offset - значение относительно BODY!
    if (ieOffsetBugX) { pos.left += object.offsetLeft; }
    if (ieOffsetBugY) { pos.top  += object.offsetTop; }
    if (ieOffsetBugX && ieOffsetBugY) { return pos; }
  }
*/
//var str = "";
  while (object && object.tagName!="BODY") {
    if (!isIE || (isIE && object.id != "siteTreeContainer")) {
      pos.left += object.offsetLeft;
    }
    pos.top += object.offsetTop;

    object = object.offsetParent;
    if (stopObjectId && object.id == stopObjectId) break;
  }
//alert(str);
  return pos;
}


/**
 * Create element
 * @param {String} tagName
 * @param {Object} attributes hash [optional]
 * @param {Object} oParent [optional]
 */
function createElement(tagName, attributes, oParent) {
  var obj = document.createElement(tagName);
  for (var i in attributes) {
    if (i.indexOf('.')) { // e.g. 'style.display'
      eval('obj.'+i+'=attributes[i]');
    }
    else {
      obj[i] = attributes[i];
    }
  }
  if (oParent) {
    oParent.appendChild(obj);
  }
  return obj;
}

// FADE OUT FUNCTIONS
var fadeIntervals = [];

/**
  * FADE OUT
  * @param {String} ID объекта
  */
function fadeOut(id)
{
   var dst = document.getElementById(id);

   if (dst.filters)
   {
      dst.style.filter="blendTrans(duration=1)";

      if (dst.filters.blendTrans.status != 2)
      {
         dst.filters.blendTrans.apply();
         dst.style.visibility="hidden";
         dst.filters.blendTrans.play();
      }
      return;
   }

   if (dst.style.opacity==0)
   {
      clearInterval(fadeIntervals[id]);
      fadeIntervals[id] = null;
      dst.style.visibility='hidden';
      dst.style.opacity = 1;
      return;
   }

   dst.style.opacity = (Number(dst.style.opacity) - 0.05);

   // setup interval
   if (!fadeIntervals[id]) fadeIntervals[id] = setInterval("fadeOut('"+id+"')",40);
}



// returns all object property values as a STRING
function dump(object, regexpFilter) {
  var str = '';
  for (i in object) {
    if (!regexpFilter || i.match(regexpFilter)) {
      str += i+' = ' + object[i]+"<br>\n";
    }
  }
  return str;
}

// ---------------------------------------------------------------------------
// HTTP REQUEST
// ---------------------------------------------------------------------------
// Create XMLHttpRequest object

/**
 * This XMLHttpRequest is NOT ASYNCHRONOUS by default
 * @param {Boolean} isAsync
 */
function httpRequest(isAsync) {
   this.xhr = null;

   try { this.xhr = new XMLHttpRequest(); } catch(e) { // Mozilla, IE7
   try { this.xhr = new ActiveXObject("Msxml2.XMLHTTP"); } catch(e) {
   try { this.xhr = new ActiveXObject("Microsoft.XMLHTTP"); } catch(e) { return false; } } }

   this.isAsync = isAsync ? true : false;
   this.statusHandlers = {};
}

// ----------------------------------------------------------------------------
/**
 * Make request
 * @param {String} method GET|POST
 * @param {String} url
 * @param {Object} urlParams { hash }
 * @param {Object} e.g. { '200': 'alert(200)'. '403': 'alert("NO RIGHTS") }
 *    { '*': 'alert("Обработчик всех ответов - с любым статусом")' }
 * @return {String} status ('200', '404' etc) -- only if isAsync==false
 */
httpRequest.prototype.request = function(method, url, urlParams, statusHandlers) {

  this.statusHandlers = statusHandlers;
  if (method!='POST') method = 'GET';

  var encParams = urlEncodeArray(urlParams);

  if (encParams && method=='GET') {
    url += (url.match(/\?/) ?  "&" : "?") + encParams;
  }

  this.xhr.open(method, url, this.isAsync);
  if (method=='POST') { this.xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded, charset=utf-8"); }
  this.xhr.send(encParams);

  if (this.isAsync) {
    var oXhr = this;
    this.xhr.onreadystatechange = function() { oXhr.trackStatus(); };
  }
  else {
    this.trackStatus();
    return this.xhr.status;
  }
}

httpRequest.prototype.trackStatus = function() {

  try {
    if (!this.statusHandlers) return;

    var handler = this.statusHandlers[this.xhr.status];

    // DEFAULT STATUS HANDLER (fires on all status codes)
    if (!handler && this.statusHandlers['*']) {
      handler = this.statusHandlers['*'];
    }

    if (handler) {
      try { eval(handler); }
      catch(e) { alert('Failed ['+this.xhr.status+']: '+handler); }
    }
  } catch (outerException) {}
}

// getJson requests are always synchronous
httpRequest.prototype.getJson = function(url, urlParams, statusHandlers) {
  var isAsync = this.isAsync;
  this.isAsync = false;
  this.request('GET', url, urlParams, statusHandlers);
  this.isAsync = isAsync;

  if (this.xhr.status!='200' || !this.xhr.responseText.length) { return null; }
  try {
    return eval(this.xhr.responseText);
  }
  catch (e) {
    return null;
  }
}

httpRequest.prototype.getResponseText = function() {
  return this.xhr.responseText;
}

// ----------------------------------------------------------------------------
// string to use with POST requests (recursive!)
function urlEncodeArray(data, parent)
{
  if (data==null) return '';

  if (!parent) parent = "";
  var query = [];

  if (data instanceof Object) {
    for (var k in data) {
      var key = parent ? parent+"["+k+"]" : k;

      query.push( data[k] instanceof Object
                              ? urlEncodeArray(data[k], key)
                              : encodeURIComponent(key) + "=" + encodeURIComponent(data[k]));
     }
     return query.join('&');
  }
  else {
    return encodeURIComponent(data);
  }
}

// Скроллер: прокручивает экран при приближении курсора мыши к краю экрана
var scroller = {
  scrollInterval: null, // для хранения ID интервала (setInterval)
  scrollDelay: 15,
  scrollAmount: 5,
  scrollAreaHeight: 60,
  scrollBottomK: 150, // ??? неправильно определяет body.scrollHeight?

  scroll: function(e) {
    if (!e) e = event;

    // высота окна
    var windowHeight = document.body.clientHeight;
    // место положения мыши
    var mouseY = e.clientY ? e.clientY : e.y;

    if (mouseY < scroller.scrollAreaHeight && scroller.canScrollUp()) {
      if (!scroller.scrollInterval) {
        scroller.scrollInterval = setInterval(scroller.scrollUp, scroller.scrollDelay);
      }
    }
    else if (mouseY > (windowHeight - scroller.scrollAreaHeight) && scroller.canScrollDown()) {
      if (!scroller.scrollInterval) {
        scroller.scrollInterval = setInterval(scroller.scrollDown, scroller.scrollDelay);
      }
    }
    else {
      scroller.scrollStop();
    }
  },

  canScrollUp: function() {
    return (document.body.scrollTop > 0);
  },

  canScrollDown: function() {
    return ((document.body.scrollHeight) > (document.body.scrollTop + document.body.clientHeight));
  },

  scrollUp: function() {
    if (scroller.canScrollUp()) { document.body.scrollTop -= scroller.scrollAmount; }
                           else { scroller.scrollStop(); }
  },

  scrollDown: function() {
    if (scroller.canScrollDown()) { document.body.scrollTop += scroller.scrollAmount; }
                             else { scroller.scrollStop(); }
  },

  scrollStop: function() {
    if (scroller.scrollInterval) {
      clearInterval(scroller.scrollInterval);
      scroller.scrollInterval = null;
    }
  }
}

