﻿/*
 * Obsługa ładowania stron.
 *
 * Wymagane biblioteki:
 *
 * - ImagePreloader.js
 *
 * @author Łukasz Świerżewski <lukasz at zirpe dot com>
 * @version 1.0
 */

function PageLoader(m_destenation) {

 /*
  * ATRYBUTY:
  */

 var _o_self = this;
 var _o_imagePreoader, _o_dataLoader;
 var $_o_destenation;

 var _b_busy, _b_dataLoaded, _b_cssLoaded, _b_jsLoaded, _b_forceFinish, _b_canAbort;
 var _as_css, _as_js;

 var _am_options, _am_ajaxOptions;
 var _am_defaultOptions = {
  imagePreloaderArea: null,
  truncateFirstElementTopMargin: true,
  truncateFirstElementTopMarginTags: 'p,h1,h2,h3,h4,h5,h6',
  callbackBeforeLoad: null,
  callbackAfterLoad: null,
  callbackOnAbort: null,
  callbackOnError: null
 };

 /*
  * METODY PRYWATNE:
  */

 /*
  * Konstruktor.
  *
  * @param mixed Obiekt, do którego ładowane będą dane.
  */
 function _init(m_destenation) {
  _am_options = {};
  _as_css = [];
  _as_js = [];
  _b_busy = _b_dataLoaded = _b_cssLoaded = _b_jsLoaded = _b_forceFinish = _b_canAbort = false;

  $_o_destenation = $(m_destenation || document.body);
 };

 /*
  * Próbuje zakończyć ładowanie danych, jeśli poszczególne elementy zostały poprawnie załadowane.
  */
 function _tryFinish() {
  if (_b_forceFinish || (_b_dataLoaded && _b_cssLoaded && _b_jsLoaded && _o_imagePreloader && _o_imagePreloader.ready())) _finish();
 }

 /*
  * Kończy ładowanie danych i przekierowuje sterowanie.
  */
 function _finish() {
  _b_busy = false;
  if (_am_options.callbackAfterLoad && $.isFunction(_am_options.callbackAfterLoad)) _am_options.callbackAfterLoad();
 }

 /*
  * Ładuje zawartość wskazanych plików CSS do części nagłówkowej strony.
  *
  * Po poprawnym załadowaniu wszystkich plików wymuszane jest załadowanie wszystkich grafik
  * dostępnych wewnątrz obszaru, do którego ładowane są dane.
  *
  * @param array Lista ścieżek do plików CSS do załadowania.
  */
 function _loadCss(as_list) {
  if (!as_list) as_list = [];

  var i_currentLoaded = 0;
  var as_correctList = [];

  var f_checkIsLoaded = function() {
   if (++i_currentLoaded >= as_list.length) {
    _as_css = as_correctList;
    _b_cssLoaded = true;
	var m_imagePreloaderArea = _am_options.imagePreloaderArea || $_o_destenation;
    _o_imagePreloader = new ImagePreloader($('*', $(m_imagePreloaderArea)).add($(m_imagePreloaderArea)), {callbackOnComplete: _tryFinish});
    _loadJs(_as_js);
   }
  };

  if (as_list.length == 0) {
   f_checkIsLoaded();
   return;
  }

  var f_load = function(s_url) {
   if (!s_url || s_url == '') f_checkIsLoaded();
   else {
	s_url += '?datetime=' + escape((new Date()).getTime());
    as_correctList.push(s_url);

	var o_style = document.createElement('link');
	o_style.setAttribute('rel', 'stylesheet');
	o_style.setAttribute('type', 'text/css');
	o_style.setAttribute('media', 'screen');
	o_style.setAttribute('href', s_url);
	document.getElementsByTagName('head')[0].appendChild(o_style);
    $.ajax({
	 url: s_url,
	 cache: true,
	 success: function (m_data) {
      f_checkIsLoaded();
	 },
     error: _ajaxError
	});
/*
	var $o_style = $('<style type="text/css" title="' + s_url + '"></style>').appendTo($('head', document));
    $.ajax({
	 url: s_url,
	 cache: false,
	 success: function (m_data) {
	  var o_rules = document.createTextNode(m_data);
	  if ($o_style.get(0).styleSheet) $o_style.get(0).styleSheet.cssText = o_rules.nodeValue;
	  else $o_style.append(o_rules);
	  f_checkIsLoaded();
	 },
     error: _ajaxError
	});
*/	
   }
  };

  for (var i = 0; i < as_list.length; i++) f_load(as_list[i]);
 }

 /*
  * Usuwa zawartość wskazanych plików CSS z części nagłówkowej strony.
  *
  * @param array Lista ścieżek do plików CSS, których zawartość ma być usunięta z części nagłówkowej strony.
  */
 function _unloadCss(as_list) {
  var as_toRemove = [];
  for (var i = 0; i < as_list.length; i++) {
//   if (as_list[i] && as_list[i] != '') as_toRemove.push('style[title=' + as_list[i] + ']:first');
   if (as_list[i] && as_list[i] != '') as_toRemove.push('link[href=' + as_list[i] + ']:first');
  }
  if (as_toRemove.length > 0) $(as_toRemove.join(','), $('head', document)).remove();
  _as_css = [];
 }

 /*
  * Ładuje i wykonuje zawartość wskazanych plików JS.
  *
  * @param array Lista ścieżek do plików JS do załadowania i wykonania.
  */
 function _loadJs(as_list) {
  if (!as_list) as_list = [];

  var i_currentLoaded = 0;
  var as_correctList = [];

  var f_checkIsLoaded = function() {
   if (++i_currentLoaded >= as_list.length) {
    _as_js = as_correctList;
    _b_jsLoaded = true;
	_tryFinish();
   }
  };
  
  if (as_list.length == 0) {
   f_checkIsLoaded();
   return;
  }

  var f_load = function(s_url) {
   if (!s_url || s_url == '') f_checkIsLoaded();
   else {
    as_correctList.push(s_url);
    $.ajax({
	 url: s_url,
	 dataType: 'script',
	 cache: false,
	 success: function (m_data) { f_checkIsLoaded(); },
	 error: _ajaxError
	});
   }
  };

  for (var i = 0; i < as_list.length; i++) f_load(as_list[i]);
 }

 /*
  * Usuwa wskazane skrypty JS.
  *
  * @param array Lista ścieżek do plików JS do usunięcia.
  */
 function _unloadJs(as_list) {
  _as_js = [];
 }

 /*
  * Obsługa błędów powstałych podczas wykonywania żądania załadowania danych strony, plików CSS lub JS.
  *
  * Dostępne typy błędów zwracane w drugim argumencie: 'null', 'timeout', 'error', 'notmodified', 'parsererror'.
  *
  * @param XMLHttpRequest Objekt zapytania AJAX.
  * @param string Typ wywołanego błędu.
  * @param object Objekt wyjątku jeśli wystapił.
  */
 function _ajaxError(o_XMLHttpRequest, s_textStatus, o_errorThrown) {
  _b_busy = _b_canAbort = false;
  if (_am_options.callbackOnError && $.isFunction(_am_options.callbackOnError)) _am_options.callbackOnError(o_XMLHttpRequest, s_textStatus, o_errorThrown);
 }

 /*
  * METODY PUBLICZNE:
  */

 /*
  * Ustawia ustawienia.
  *
  * @param array Ustawienia.
  * @param boolean Flaga informująca o wyczyszczeniu bieżących ustawień do domyślnych (domyślnie: tak).
  */
 this.setOptions = function(am_options, b_clear) {
  if (b_clear === undefined) b_clear = true;
  _am_options = $.extend({}, b_clear ? _am_defaultOptions : _am_options, am_options);
 };

 /*
  * Zwraca bieżące ustawienia.
  *
  * @return array Bieżące ustawienia.
  */
 this.getOptions = function() {
  return _am_options;
 };

 /*
  * Rozpoczyna ładowanie danych strony.
  *
  * @param array Parametry AJAX wykorzystywane do załadowania strony.
  * @return boolean <i>TRUE</i> jeśli ładowanie strony zostało rozpoczęte pomyślnie, <i>FALSE</i> w przeciwnym wypadku.
  */
 this.load = function(am_ajaxOptions) {
  if (_b_busy || !am_ajaxOptions || am_ajaxOptions.length == 0 || !am_ajaxOptions.url) return false;

  _b_busy = _b_canAbort = true;

  _am_ajaxOptions = $.extend({}, am_ajaxOptions, {
   cache: false,
   dataType: 'html',
   success: function(m_data) {
	_b_canAbort = false;
    _b_dataLoaded = true;

	if ($_o_destenation && $_o_destenation.length > 0) {

	 _unloadCss(_as_css);
	 _unloadJs(_as_js);

	 $_o_destenation.html(m_data);

     if (_am_options.truncateFirstElementTopMargin) $_o_destenation.find(_am_options.truncateFirstElementTopMarginTags).filter(':first').css({marginTop: 0});

	 var $o_css = $_o_destenation.find('#PAGE_LOADER_CSS');
	 var $o_js = $_o_destenation.find('#PAGE_LOADER_JS');

	 _as_css = ($o_css && $o_css.length > 0 && $o_css.html()) ? $o_css.html().split(';') : [];
	 _as_js = ($o_js && $o_js.length > 0 && $o_js.html()) ? $o_js.html().split(';') : [];

	 $o_css.add($o_js).remove();

	 _loadCss(_as_css);

	} else {

     _b_forceFinish = true;
     _tryFinish();

	}
   },
   error: _ajaxError
  });

  _b_dataLoaded = _b_cssLoaded = _b_jsLoaded = _b_forceFinish = false;
  _o_imagePreloader = null;

  if (_am_options.callbackBeforeLoad && $.isFunction(_am_options.callbackBeforeLoad)) _am_options.callbackBeforeLoad();

  _o_dataLoader = $.ajax(_am_ajaxOptions);

  return true;
 };

 /*
  * Usuwa dane dotyczące ostatnio załadowanej strony.
  */
 this.unload = function() {
  $_o_destenation.empty();
  _unloadCss(_as_css);
  _unloadJs(_as_js);
 };

 /*
  * Sprawdza czy można anulować bieżące żądanie.
  *
  * @return boolean <i>TRUE</i> jeśli można anulować bieżące żądanie, <i>FALSE</i> w przeciwnym wypadku.
  */
 this.canAbort = function() {
  return _b_canAbort && _b_busy;
 };

 /*
  * Sprawdza czy jest zajęty.
  *
  * @return boolean <i>TRUE</i> jeśli zajęty, <i>FALSE</i> w przeciwnym wypadku.
  */
 this.isBusy = function() {
  return _b_busy;
 };

 /*
  * Anuluje ładowanie danych.
  *
  * @return boolean <i>TRUE</i> jeśli bieżące żądanie zostało anulowane pomyślnie, <i>FALSE</i> w przeciwnym wypadku.
  */  
 this.abort = function() {
  if (!_o_self.canAbort()) return false;

  _o_dataLoader = false;
  _o_imagePreloader = null;
  _b_busy = false;

  if (_am_options.callbackOnAbort && $.isFunction(_am_options.callbackOnAbort)) _am_options.callbackOnAbort();

  return true;
 };

 this.unload = function() {
 };

 /*
  * WYWOŁANIE KONSTRUKTORA:
  */
 _init(m_destenation);

 return this;
}
