/* Скрипт для загрузки и отображения списка пользователей выбранного клуба.
*
* Принцип работы:
* 1. Определяется актуальная область видимости карты и масштаб.
* 2. Загружается YMapsML-документ с набором данных с сервера Yandex с учетом параметров из п.1.
*    Запрашивая область подгружается в 9 раз больше по размеру, чем актуальная.
*    Это сделано для уменьшения количества запросов к серверу.
* 3. На метки навешиваются обработчики на события открытия и закрытия балуна.
*    Это сделано для того, чтобы если из-за балуна карта переместилась - не произошло обновление точек.
* 4. При открытии балуна для одиночных меток с помощью ajax-запроса подгружается дополнительная информация.
*
* Спустя 2 минуты после последнего обновления точек происходит принудительное обновление данных.
* Это сделано для того, чтобы избежать "устаревания" данных.
*
*/
window.onload = function () {
   var map = new YMaps.Map(document.getElementById("YMapsID"));
   manager = new PointsManager(map);
   geoManager = new GeocoderManager(map);
   managersize = new MapSize(map,manager);

   /* Стили для оверлеев (begin) */
   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   //style.iconStyle.href="http://hevil.narod.ru/img/temp.png";
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_group.png";
   style.iconStyle.size = new YMaps.Point(23);
   style.iconStyle.offset = new YMaps.Point(-12);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleGroup', style); // Стиль для группы объектов

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   //style.iconStyle.href="http://hevil.narod.ru/img/temp2.png";
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_one.png";
   style.iconStyle.size = new YMaps.Point(20);
   style.iconStyle.offset = new YMaps.Point(-10);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleSingle', style); // Стиль для одного объекта

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_celeb.png";
   style.iconStyle.size = new YMaps.Point(34);
   style.iconStyle.offset = new YMaps.Point(-12);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleCeleb', style); // Стиль для пользователей "Звезд"

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_football.png";
   style.iconStyle.size = new YMaps.Point(23);
   style.iconStyle.offset = new YMaps.Point(-12);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleFootball', style); // Стиль для акции "футбол"

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_arfuel.png";
   style.iconStyle.size = new YMaps.Point(23);
   style.iconStyle.offset = new YMaps.Point(-12);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleARFuel', style); // Стили для "Звездных пилотов"

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/zapr_black.png";
   style.iconStyle.size = new YMaps.Point(17,19);
   style.iconStyle.offset = new YMaps.Point(-12);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleTankful', style); // Стили для "заправки питер"

   var style = new YMaps.Style();
   style.iconStyle = new YMaps.IconStyle();
   style.iconStyle.href="http://probki.avtoradio.ru/Esdbpics/star_ribbon.png";
   style.iconStyle.size = new YMaps.Point(35);
   style.iconStyle.offset = new YMaps.Point(-15);
   style.balloonContentStyle = new YMaps.BalloonContentStyle(new YMaps.Template('<div>$[desc]</div>'));
   YMaps.Styles.add('autoradio#styleRibbon', style); // Стиль для акции "Георгиевская лента"
   /* Стили для оверлеев (end) */

   if(!startpoint) startpoint = 37.6242;
   if(!finishpoint) finishpoint = 55.7561;
   if(!scale) scale = 11;

   map.setCenter(new YMaps.GeoPoint(startpoint,finishpoint), scale);
   //map.setCenter(new YMaps.GeoPoint(35.96869668974394,56.21664192297758), 11);
   map.addControl(new YMaps.Zoom());
   map.addControl(new YMaps.TypeControl());
   map.addControl(new YMaps.ToolBar());
   map.addControl(new YMaps.TrafficButton(new YMaps.TrafficControl({showOnLoad:1})));  // Включаем пробки
   new YMaps.ControlPosition(YMaps.ControlPosition.TOP_LEFT, new YMaps.Point(10));
   map.enableScrollZoom();

   // Обновление данных на карте происходит в трех случаях:
   // 1) Событие карты Update - смена масштаба или центра
   // 2) Событие карты MoveEnd - пользователь изменил область видимости карты
   // 3) Раз в 2 минуты для поддержания актуальной информации, если пользователь не совершает каких-либо действий
   YMaps.Events.observe(map, map.Events.MoveEnd, function () {
      manager.update();
      //document.getElementById("center").innerHTML = map.getCenter(); /*DEBUG*/
   })

   YMaps.Events.observe(map, map.Events.Update, function () {
      if (geoManager.geoManagerUpdate) {
         return 0;
      }
      map.closeBalloon();
      manager.update();
      //document.getElementById("zoom").innerHTML = map.getZoom(); /*DEBUG*/
      //document.getElementById("center").innerHTML = map.getCenter(); /*DEBUG*/
   });

   manager.update(1);

   //document.getElementById("zoom").innerHTML = map.getZoom(); /*DEBUG*/
   //document.getElementById("center").innerHTML = map.getCenter(); /*DEBUG*/
};

// Часы, отвечает за обновление раз в timePeriod
// Заодно и за отсчет времени (сделано для отладки, но может и пригодится)
function Clock (timePeriod, manager) {
   var  time = timePeriod,
   timeout;

   // Запуск обратного отсчета
   this.start = function () {
      this.reset();
      tick ();
   }

   // Сброс обратного отсчета
   this.reset = function () {
      clearTimeout(timeout);
      time = timePeriod;
   }

   // Один такт обратного отсчета
   function tick () {
      if (time) {
         time-=1000;
         document.getElementById("update").innerHTML = 'До обновления: ' + YMaps.humanDuration(time/1000); /*DEBUG*/
         timeout = setTimeout(tick, 1000);
      } else {
         manager.update(1);
      }
   }

   this.start();
}

// Класс отвечает за получение данных с сервера в формате YMapsML
function Data () {
   // Шаблон URL'а, по которому нужно обращаться к серверу
   //var urlTemplate = new YMaps.Template('<i>http://mobile.maps.yandex.net/clubusers?tl_lat=$[tl_lat]&tl_lon=$[tl_lon]&br_lat=$[br_lat]&br_lon=$[br_lon]&zoom=$[zoom]&clubids=1</i>');
   var urlTemplate = new YMaps.Template('<i>http://probki.avtoradio.ru/a_php/yandex/ajax/starslist.php?tl_lat=$[tl_lat]&tl_lon=$[tl_lon]&br_lat=$[br_lat]&br_lon=$[br_lon]&zoom=$[zoom]&clubids=1</i>');

   // Сборка URL'а для конкретного bounds и масштаба
   this.buildUrl = function (data) {
      var tl = data.bounds.getLeftTop(),
      br = data.bounds.getRightBottom(),
      tmpl = urlTemplate.build({
      "tl_lat" : tl.getLat(),
      "tl_lon" : tl.getLng(),
      "br_lat" : br.getLat(),
      "br_lon" : br.getLng(),
      "zoom"  : data.zoom
      });
      //alert(tmpl.textContent);

      return tmpl.innerText || tmpl.textContent;
   }

   // Загрузка YMapsML с сервера
   this.getYMapsML = function (data) {
      //document.getElementById("url").innerHTML = this.buildUrl(data); /*DEBUG*/
      return new YMaps.YMapsML(this.buildUrl(data), {notFromCache: 1});
   }
}

// Менеджер по управлению точками
function PointsManager (map) {
   var flagBalloonOpen = 0,            // Если открыт балун, то не перерисовываем данные
   ymapsmlData = new Data(),           // Объект данных для отображения (ymapsml + url для его получения)
   paramsData = {},                    // Параметры заданной области: область видимости и масштаб
   ml,                                 // Текущий в данный момент YMapsML-документ
   listeners = [],                     // Массив слушателей событий
   counter = new Clock(120000, this);  // Часы для обновления

   // Обновление данных
   this.update = function (needUpdate)
   {
      // Если задан флаг needUpdateFlag, то обновление произойдет, даже если другие условия не выполняются.
      var needUpdateFlag = needUpdate || 0;
      // Если не открыт балун, изменился масштаб или сдвинули за границы кэшированной области, то update
      if (!flagBalloonOpen &&(paramsData.zoom != map.getZoom()) || !this.inCachedBounds(map.getBounds()) || needUpdateFlag)
      {
         document.getElementById("update").innerHTML = 'Идет загрузка...'; /*DEBUG*/

         // Обновление данных обрабатываемой области
         paramsData = {
            bounds : this.getCacheBounds(map.getBounds()),
            zoom: map.getZoom()
         };

         // Очищаем
         this.clean();

         // Получаем новый YMapsML-документ
         ml = ymapsmlData.getYMapsML(paramsData);
         //alert(ml);

         // Слушатель загрузки YMapsML-документа
         listeners.push(
         YMaps.Events.observe(ml, ml.Events.Load, this.handlingPoints, this)
         );
         // Слушатель неудачной загрузки YMapsML-документа
         listeners.push(
         YMaps.Events.observe(ml, ml.Events.Fault, function (error) {
            //document.getElementById("error").innerHTML = '<span style="color:red">При загрузке документа произошла ошибка: "' + error + '"</span>'; /*DEBUG*/
            //alert('error: '+error);
            counter.start();
         })
         );
         counter.start();
      }
   };

   // Метод для очистки "мусора"
   this.clean = function () {
      // Отключаем навешенные слушатели событий
      for (var i = 0, l = listeners.length; i < l; i++) {
         listeners[i].cleanup();
      }
      listeners = [];
   }

   // Метод формирует "большой" bounds, чтобы не было лишних запросов к серверу.
   // С сервера запрашивается область в 9 раз больше актуальной.
   this.getCacheBounds = function (bounds) {
      var span = bounds.getSpan(),
      lt = bounds.getLeftBottom(),
      rb = bounds.getRightTop();

      return new YMaps.GeoBounds(
      new YMaps.GeoPoint(lt.getLng() - span.x, lt.getLat() - span.y),
      new YMaps.GeoPoint(rb.getLng() + span.x, rb.getLat() + span.y)
      );
   };

   // Метод проверят находимся ли мы в закэшированной зоне
   this.inCachedBounds = function (bounds) {
      if (!paramsData.bounds) return 0;
      var cache = paramsData.bounds;
      return  (cache.contains(bounds.getLeftTop()) && cache.contains(bounds.getRightBottom()));
   };

   // Навешивание обработчиков для точек
   this.handlingPoints = function (ml) {
      var _this = this,
      manager = new YMaps.ObjectManager();

      // Для каждой метки нужно поставить два слушателя событий: на открытие и закрытие балуна. Это нужно для того, чтобы
      // в том случае, когда балун открыается и смещает карту - не происходил update точек.
      // Также воспользуемся ObjectManager'ом для повышения производительности.
      ml.get(0).forEach(function (placemark) {
         //placemark.description = Base64.decode(placemark.description);
         // Делаем, чтобы метка не скрывалась при открытии балуна
         placemark.setOptions({hideIcon:false});
         manager.add(placemark);
         listeners.push(
         YMaps.Events.observe(placemark, placemark.Events.BalloonOpen, function () {
            flagBalloonOpen = 1;
            _this.getInfo(this);
         })
         );

         listeners.push(
         YMaps.Events.observe(placemark, placemark.Events.BalloonClose, function () {
            flagBalloonOpen = 0;
         })
         );
      });

      // Удаляем все объекты с карты
      map.removeAllOverlays();

      // Объявляем класс поиска пользователей
      searchManager = new SearchUsers(ml);

      // Добавляем обновленные
      map.addOverlay(manager);
      document.getElementById("points").innerHTML = ml.get(0).length(); /*DEBUG*/
      document.getElementById("error").innerHTML = ''; /*DEBUG*/
      counter.start();
      ml = null;
   };

   // Получение дополнительной информации
   this.getInfo = function (placemark)
   {
      //if(this.ParseUserList(placemark)<1) return 1;
      /* Пример получения данных с помощью ajax-запроса (begin)*/
      var oldContent = placemark.getBalloonContent(); /*DEBUG*/
      $.ajax({
         type: 'POST',
         url: '/?an=showgroup',
         error: function () {
            placemark.setBalloonContent(oldContent); /*DEBUG*/
            alert('При получении данных с сервера произошла ошибка.');
         },
         //dataType: 'json',
         data: {
            login: placemark.description
         },
         success: function (data) {
            placemark.info = data;
            placemark.setBalloonContent(data); /*DEBUG*/
            placemark.update();
         }
      });
      placemark.setBalloonContent(
      '<img src="/a_php/common/icon/ajax.gif" alt="Загрузка..."> Загрузка данных'
      //'<div style="padding-right:20px">Загрузка данных</div>'
      );
      /*
      // Загружаем дополнительную информцию, только для одного пользователя (группы не трогаем)
      if ((placemark.description +'').indexOf(',') < 0) {
      // Покажем пользователю, что идет процесс загрузки информации.
      placemark.setBalloonContent(
      '<img style="position:absolute;right:0" src="/a_php/common/icon/ajax.gif" alt="Загрузка...">' +
      '<div style="padding-right:20px">Загрузка информации</div>'
      );
      }
      */
      /* Пример получения данных с помощью ajax-запроса (end)*/
   };
   // получаем список всех пользователей по шаблону
   this.ParseUserList = function (placemark)
   {
      list_login = placemark.description;
      //alert(placemark.description);
      //placemark.description = '';
      $.ajax({
         type: 'POST',
         cache: true,
         async: true,
         url: '/?an=showgroup',
         error: function(){alert('Ошибка получения данных');},
         data: {
            login: list_login
         },
         success: function (data) {
            //alert(data);
            //placemark.desc = data;
            placemark.desc = data;
            placemark.update();
            delete(list_login);
         }
      });
      return 0;
      //var balloonContent = $(placemark.getBalloonContent()); // Создаем элемент Jquery
      //alert(balloonContent.find('img').attr('src'));
   }
}

/* Класс для запуска процесса геокодирования и отображения результатов поиска.
*
* Включает в себя ряд следующих функций:
* 1. Поиск по почтовому адресу.
* 2. Исправление опечаток.
* 3. Сброс результатов поиска.
*/
function GeocoderManager (map)
{
   var _this = this;
   this.geoManagerUpdate = 0; // Флаг, который устанавливается в true, когда GeocoderManager
   // делает Update карты

   // Обработка нажатия клавиш.
   // По нажатию Enter запускаем поиск.
   $('#SearchMap').bind('keyup', function (e) {
      if (e.keyCode == 13) {
         _this.search();
      }
   });
   // Обработчик кликов по кнопке "Поиск"
   $('#SearchMapButton').bind('click', function () {
      _this.search();
   });
   // Метода запускает процесс геокодирования
   this.search = function () {
      geocoder = new YMaps.Geocoder($('#SearchMap').attr('value'),{results:12});
      YMaps.Events.observe(geocoder, geocoder.Events.Load, _this.geocodeLoad, this);
   };
   // Обработка полученных результатов геокодирования
   this.geocodeLoad = function (geo)
   {
      // Сброс предыдущих результатов
      var searchMapResults = $('#SearchMapResults').empty();
      // Если в исходном запросе была опечатка, то сообщим об этом
      if (geo.suggest) {
         $('<div class="suggest">Быть может, Вы искали "<a id="geocoder-suggest" href="#">' + geo.suggest + '</a>"</div>')
         .find('#geocoder-suggest')
         .bind('click', function () {
            $('#SearchMap').attr('value', $(this).text());
            _this.search();
            return false;
         }).end()
         .appendTo(searchMapResults);
      }
      // Если что-то было найдено, то выводим
      if (geo.length())
      {
         searchMapResults
         .append('<ul id="SearchMapResultsList"></ul>')
         .append($('<a href="#" id="clearres">Очистить результаты поиска</a>')
         .bind('click', function () {
            $('#SearchMapResults').empty();
            return false;
         }));

         for (var i = 0, l = geo.length(); i < l; i++) {
            $("#SearchMapResultsList").append(this.createLi(geo.get(i)));
         }
      }else {
         searchMapResults.html('<span style="color:red;">Искомая коомбинация на карте не встречается</span>');
      }
   }

   // Метод создает ссылку на результат геокодирования
   this.createLi = function (obj) {
      return $('<li><a href="#" class="geores">' + obj.text + '</a></li>')
      .bind('click', function () {
         //$('#SearchMapResultsList li').removeClass('current');
         //$(this).addClass('current');

         _this.geoManagerUpdate = 1;
         map.setBounds(obj.getBounds());
         _this.geoManagerUpdate = 0;
         map.openBalloon(obj.getGeoPoint(), obj.text);
         return false;
      })
   }
}


/* Класс для изменения размеров карты.
*
* Включает в себя ряд следующих функций:
* 1. определение видимой области в браузере
* 2. Изменение размера контейнера карты
* 3. Возврат исходных размеров контейнера карты
*/
function MapSize(map,manager)
{
   //alert(document.body.clientWidth);
   //alert(document.body.clientHeight);
   var koef = 50; // Коэффициент изменения размеров карты
   size_parent_x_start = $("#YMapsID").css("width");
   size_parent_x_start = size_parent_x_start.replace("px","")*1;
   size_parent_y_start = $("#YMapsID").css("height");
   size_parent_y_start = size_parent_y_start.replace("px","")*1;

   // Метод определения видимой области в браузере
   this.docSize = function()
   {
      this.width = window.innerWidth ? window.innerWidth : document.body.clientWidth;
      this.height = window.innerHeight ? window.innerHeight : document.body.clientHeight;
   }
   // Метод изменяет размер контейнера карты
   this.ChangeSize = function(direction_name,direction_more)
   {
      direction_screen_x = 0;
      direction_screen_y = 0;
      size_x = $("#YMapsID").css("width");
      size_x = size_x.replace("px","")*1;
      size_y = $("#YMapsID").css("height");
      size_y = size_y.replace("px","")*1;
      if(direction_name=='width') size = size_x;
      else size = size_y;
      if(direction_more>0) direction = size + koef;
      else direction = size - koef;
      if(direction_name=='width') {
         direction_screen_x = direction;
      }
      else {
         direction_screen_y = direction;
      }
      if((direction_name=='width' && direction_screen_x < size_parent_x_start) || (direction_name=='height' && direction_screen_y < size_parent_y_start)) {
         //alert('Размер карты не может быть меньше первоначального');
         $("#error").html('Размер карты не может быть меньше первоначального');
         return false;
      }
      if(screen.width-100 < direction_screen_x || screen.height-100 < direction_screen_y) {
         $("#error").html('Нельзя сделать размер карты больше разрешения Вашего монитора');
         //alert('Нельзя сделать размер карты больше разрешения Вашего монитора');
         return false;
      }
      $("#error").empty();
      $("#YMapsID").css(direction_name,direction+'px');
      map.setZoom(14);
      map.redraw();
      //manager.update();
      return false;
   }
   // Метод возвращает контейнер карты к исходному размеру
   this.clearsize = function()
   {
      $("#YMapsID").css('width',size_parent_x_start+'px');
      $("#YMapsID").css('height',size_parent_y_start+'px');
      map.redraw();
      map.setZoom(14);
      $("#error").empty();
      return false;
   }
}

function SearchUsers1(map)
{
   coordinate = '0.00 0.00';
   // Метод делает запрос к серверу для получения списка пользователей
   this.Search = function()
   {
      val = $("#loginsearch").val();
      if(val=='' || val=='none') {
         alert('Введите логин для поиска'); return false;
      }
      if(val==undefined) {
         alert('Неправильный шаблон'); return false;
      }
      $.ajax({
         type: 'POST',
         url: '/?an=searchuser',
         error: function () {
            alert('При получении данных с сервера произошла ошибка.'); return false;
         },
         //dataType: 'json',
         data: {
            loginsearch: val
         },
         success: function (data) {
            data = data + '<div><a href="#" onclick="return searchManager.clear();">Очистить результаты</a></div>';
            $("#usersearch").html(data);
         }
      });
      return false;
   }

   // метод открывает карту при результате поиска
   this.OpenMap = function(coordinate_x,coordinate_y)
   {
      map.setCenter(new YMaps.GeoPoint(coordinate_x,coordinate_y), 16); return false;
   }
   this.clear = function()
   {
      $("#usersearch").empty(); return false;
   }
}

function SearchUsers (users)
{
   _this = this;
   $('#SearchUser').bind('keyup', function (e) {
      if (e.keyCode == 13) {
         _this.search();
      }
   });
   // Обработчик кликов по кнопке "Поиск"
   $('#SearchUserButton').bind('click', function () {
      _this.search();
   });
   this.search = function ()
   {
      $("#usersearch").empty();
      // Подробная документация здесь: http://api.yandex.ru/maps/jsapi/doc/ref/reference/group.xml#filter
      var userLogin = $('#SearchUser').attr('value'); // Логин пользователя
      result = users.filter(function(obj)
      {
         if (obj.description) {
            var user = Base64.decode(obj.description).match(userLogin);
            if (user) {
                return 1;
            }
         }
         return 0;
      });
      if (result.length) {
         result[0].openBalloon();
      } else {
         $("#usersearch").html('Пользователь не найден');
      }
      return false;
   }
}

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/

var Base64 = {
   // private property
   _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
   // public method for encoding
   encode : function (input) {
      var output = "";
      var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
      var i = 0;
      input = Base64._utf8_encode(input);
      while (i < input.length) {
         chr1 = input.charCodeAt(i++);
         chr2 = input.charCodeAt(i++);
         chr3 = input.charCodeAt(i++);
         enc1 = chr1 >> 2;
         enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
         enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
         enc4 = chr3 & 63;
         if (isNaN(chr2)) {
            enc3 = enc4 = 64;
         } else if (isNaN(chr3)) {
            enc4 = 64;
         }
         output = output +
         this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
         this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
      }
      return output;
   },

   // public method for decoding
   decode : function (input) {
      var output = "";
      var chr1, chr2, chr3;
      var enc1, enc2, enc3, enc4;
      var i = 0;
      input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
      while (i < input.length) {
         enc1 = this._keyStr.indexOf(input.charAt(i++));
         enc2 = this._keyStr.indexOf(input.charAt(i++));
         enc3 = this._keyStr.indexOf(input.charAt(i++));
         enc4 = this._keyStr.indexOf(input.charAt(i++));
         chr1 = (enc1 << 2) | (enc2 >> 4);
         chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
         chr3 = ((enc3 & 3) << 6) | enc4;
         output = output + String.fromCharCode(chr1);
         if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
         }
         if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
         }
      }
      output = Base64._utf8_decode(output);
      return output;
   },

   // private method for UTF-8 encoding
   _utf8_encode : function (string) {
      string = string.replace(/\r\n/g,"\n");
      var utftext = "";
      for (var n = 0; n < string.length; n++) {
         var c = string.charCodeAt(n);
         if (c < 128) {
            utftext += String.fromCharCode(c);
         }
         else if((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
         }
         else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
         }
      }
      return utftext;
   },

   // private method for UTF-8 decoding
   _utf8_decode : function (utftext) {
      var string = "";
      var i = 0;
      var c = c1 = c2 = 0;
      while ( i < utftext.length ) {
         c = utftext.charCodeAt(i);
         if (c < 128) {
            string += String.fromCharCode(c);
            i++;
         }
         else if((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
            i += 2;
         }
         else {
            c2 = utftext.charCodeAt(i+1);
            c3 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
         }
      }
      return string;
   }
}


function dump(arr,level)
{
    var dumped_text = "";
    if(!level) level = 0;
    //The padding given at the beginning of the line.
    var level_padding = "";
    for(var j=0;j<level+1;j++) level_padding += "    ";
    if(typeof(arr) == 'object') { //Array/Hashes/Objects
        for(var item in arr) {
            var value = arr[item];
            if(typeof(value) == 'object') { //If it is an array,
                dumped_text += level_padding + "'" + item + "' ...\n";
                dumped_text += dump(value,level+1);
            } else {
                dumped_text += level_padding + "'" + item + "' => \"" + value + "\"\n";
            }
        }
    } else { //Stings/Chars/Numbers etc.
        dumped_text = "===>"+arr+"<===("+typeof(arr)+")";
    }
    return dumped_text;
}
// Установить куки
function setCookie(name, value)
{
      valueEscaped = escape(value);
      expiresDate = new Date();
      expiresDate.setTime(expiresDate.getTime() + 365 * 24 * 60 * 60 * 1000); // срок - 1 год, но его можно изменить
      expires = expiresDate.toGMTString();
      newCookie = name + "=" + valueEscaped + "; path=/; expires=" + expires;
      if (valueEscaped.length <= 4000) document.cookie = newCookie + ";";
}

// Получить куки
function getCookie(name)
{
      prefix = name + "=";
      cookieStartIndex = document.cookie.indexOf(prefix);
      if (cookieStartIndex == -1) return null;
      cookieEndIndex = document.cookie.indexOf(";", cookieStartIndex + prefix.length);
      if (cookieEndIndex == -1) cookieEndIndex = document.cookie.length;
      return unescape(document.cookie.substring(cookieStartIndex + prefix.length, cookieEndIndex));
}
//Функция подгружает список городов в профайле пользователя
function SetCity(block)
{
   val = jQuery("select#country_ank").val();
   if(val > 0)
   {
      url = '/a_php/sessauth/a_dmin/profile/list_town.php?country='+val;
      return LoadContent(url,block);
   }
   else {
      mess = '<select id="city" name="city" class="select_city"><option value="0">Выберите страну</option></select>';
      jQuery("#"+block).html(mess);
      mess = null;
   }
}
//Функция загружает с сервера данные и помещает его в указанный блок
function LoadContent(url,block,param,notpreloading)
{
   if(typeof(url) == 'object') url = url.href;
   if(url.indexOf(window.location.hostname) < 0) url = 'http://'+window.location.hostname+url;
   if(!param) param = new Array();
   jQuery("#"+block).empty();
   if(!notpreloading)
   {
      message_preload = '<div style="text-align:center;font-weight:bold;"><img src="/a_php/common/icon/ajax.gif" border="0"></div>';
      jQuery("#"+block).html(message_preload);
   }
   data_param = ' ';  // Переменная, хранящая массив передаваемых параметров
   for (key in param)
   {
      data_param += key+'='+param[key]+'&';
   }
   data_param = data_param.substring(0,data_param.length-1);
   data_param = jQuery.trim(data_param);
   method_send = data_param == '' ? 'GET':'POST';
   jQuery.ajax({
      type: method_send,
      url: url,
      data: data_param,
      error: function(XMLHttpRequest){
         msg_error = SetErrorQuery(XMLHttpRequest);
         msg_error = '<div style="text-align:center;font-weight:bold;">'+msg_error+'<br>'+url+'</div>';
         jQuery("#"+block).html(msg_error);
         msg_error = null;
      },
      cache: false,
      async: true,
      success: function(msg){
         jQuery("#"+block).html(msg);
         msg = null;
      }
   });
   return false;
}