/*
 * Copyright (c) 1998-2006 TeamDev Ltd. All Rights Reserved.
 * Use is subject to license terms.
 */

var Q__AJAX_REQUEST_HEADER = "teamdev_ajax";
var Q__UPDATE_PORTIONS_SUFFIX = "tc_ajax_portions";
var Q__SUBMIT_PARAMS_SUFFIX = "_ajax_submit_params";
var Q__PARAM_COMPONENT_ID = "componentId";
var Q__TAG_AJAX_RESPONSE = "ajaxResponse";
var Q__TAG_AJAX_UPDATABLE = "updatable";
var Q__TAG_AJAX_SCRIPT = "script";
var Q__TAG_AJAX_CSS = "css";
var Q__TAG_AJAX_STYLE = "style";

var Q__UPDATE_TYPE_SIMPLE = "simple";
var Q__UPDATE_TYPE_PORTION = "portion";
var Q__UPDATE_TYPE_STATE = "state";

var Q__UPDATE_TYPE_INITIALIZATION = "initialization";

function q__submitComponentWithAdditionalParam(componentId, paramName, paramValue) { //
  var component = document.getElementById(componentId);
  var field = q__addHiddenField(component, paramName, paramValue);
  q__reloadComponent(componentId);
  field.parentNode.removeChild(field);
}

function q__reloadComponent(componentId, completionCallback) {
  q__sendAjaxRequest(componentId, null, null, completionCallback);
}

function q__requestComponentPortions(componentId, portionNames, portionProcessor) {
  q__sendAjaxRequest(componentId, portionNames, portionProcessor, null);
}

function q__sendAjaxRequest(componentId, portionNames, portionProcessor, completionCallback) {
  var component = q__getElement(componentId);

  //we need to fire validation for enclosing form (if there are no client validation support - just skip client validation (there will be server side validation only)
  //if form is not valid - no AJAX request at all
  if (document.tc_clientValidationSupport)
    q_validateEnclosingForm(component);

  var form = q__findParentNode(component, "FORM");
  q__assert(component, "q__sendAjaxRequest: Enclosing form not found for element with id: " + component.id);

  var ajaxObject = new q__AjaxObject();
  if (window.XMLHttpRequest) {
    ajaxObject._request = new XMLHttpRequest();
  } else {
    ajaxObject._request = new ActiveXObject("Microsoft.XMLHTTP");
  }
  ajaxObject._targetId = componentId;

  var paramsBuf = new q__StringBuffer();
  q__prepareFormParams(form, paramsBuf);
  paramsBuf.append("&");
  q__prepareUpdates(paramsBuf, componentId, portionNames);
  paramsBuf.append("&");
  paramsBuf.append(Q__PARAM_COMPONENT_ID).append("=").append(componentId);

  if (portionProcessor) {
    ajaxObject._customProcessor = portionProcessor;
  }

  ajaxObject._request.onreadystatechange = q__getEventHandlerFunction("_processResponse", null, ajaxObject);
  ajaxObject._completionCallback = completionCallback;
  ajaxObject._requestedComponentId = componentId;

  var url = form.action;
  ajaxObject._request.open("POST", url, true);
  ajaxObject._request.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  ajaxObject._request.setRequestHeader(Q__AJAX_REQUEST_HEADER, "true");
  ajaxObject._request.send(paramsBuf.toString());

  q__requestStarted(ajaxObject);
}

var q__ajaxRequestsInProgress = 0;

function q__requestStarted(ajaxObject) {
  q__ajaxRequestsInProgress++;
  if (q__ajaxRequestsInProgress == 1)
    q__showAjaxProgressMessage();
}

function q__requestFinished(ajaxObject) {
  q__ajaxRequestsInProgress--;
  if (q__ajaxRequestsInProgress == 0)
    q__hideAjaxProgressMessage();
}

function q__setAjaxMessageHTML(messageHTML) {
  var div = document.createElement("div");
  var simulateFixedPos = q__isExplorer();
  div.style.position = simulateFixedPos ? "absolute" : "fixed";
  div.style.right = 0;
  div.style.top = 0;
  div.style.visibility = "hidden";
  div.innerHTML = messageHTML;
  document._ajaxInProgressMessage = div;
  q__addLoadEvent(function() {
    var parent = document.forms[0];
    if (!parent)
      parent = document.body;
    parent.appendChild(document._ajaxInProgressMessage);
    setTimeout("if (document._ajaxInProgressMessage.style.visibility=='hidden'){document._ajaxInProgressMessage.style.display='none'; document._ajaxInProgressMessage.style.visibility='';}", 1000);
  })
}

function q__showAjaxProgressMessage() {
  var message = document._ajaxInProgressMessage;
  q__assert(message, "q__showAjaxProgressMessage: no message was registered");
  var simulateFixedPos = q__isExplorer();
  if (simulateFixedPos)
    q__updateAjaxInProgressMessagePos();

  if (message.style.visibility == "hidden")
    message.style.visibility = "";
  else
    message.style.display = "";
  document.body.oldCursor = document.body.style.cursor;
  document.body.style.cursor = "progress";

  if (simulateFixedPos) {
    window.attachEvent("onscroll", q__updateAjaxInProgressMessagePos);
    window.attachEvent("onresize", q__updateAjaxInProgressMessagePos);
  }
}

function q__hideAjaxProgressMessage() {
  var simulateFixedPos = q__isExplorer();
  if (simulateFixedPos) {
    window.detachEvent("onscroll", q__updateAjaxInProgressMessagePos);
    window.detachEvent("onresize", q__updateAjaxInProgressMessagePos);
  }
  document.body.style.cursor = document.body.oldCursor;
  var message = document._ajaxInProgressMessage;
  q__assert(message, "q__hideAjaxProgressMessage: no message was registered");
  message.style.display = "none";
}

function q__updateAjaxInProgressMessagePos() {
  var message = document._ajaxInProgressMessage;
  message.style.top = q__getPageScrollPos().y;
}

// q__AjaxObject class
function q__AjaxObject() {
  this._processResponse = function() {
    var request = this._request;
    if (request.readyState != 4)
      return;

    if (request.status != 200) {
      if (request)
        q__requestFinished(this);
      alert("Error while retrieving the XML data\n" + request.status);
      return;
    }

    //test if there were no validation error during processing request on the server-side
    if (q__validationErrorDuringProcessing(request)) {
      q__requestFinished(this);
      q__submitById(this._targetId);
      return;
    }

    //set flag - ajax update in progress
    document.tc_page_is_already_initialized = true;

    var responseXML = request.responseXML;
    var jsIncludes = responseXML.getElementsByTagName(Q__TAG_AJAX_SCRIPT);
    var updatables = responseXML.getElementsByTagName(Q__TAG_AJAX_UPDATABLE);
    var styles = responseXML.getElementsByTagName(Q__TAG_AJAX_STYLE);
    var cssFiles = responseXML.getElementsByTagName(Q__TAG_AJAX_CSS);
    this._styles = styles;
    this._cssFiles = cssFiles;
    this._jsIncludes = jsIncludes;
    this._updatables = updatables;

    q__processStylesIncludes(styles);
    q__processCSSFilesIncludes(cssFiles);
    //substitute document.write method to prevent error if foreign js library contains code that executes on library
    //loading and invokes document.write method to render some HTML. Otherwise document.write() method rewrites all
    //currently rendered in page content with content passed to it on AJAX response
    q__substituteDocumentWrite();

    q__processJSIncludes(jsIncludes);
    this._processUpdatesWhenReady();

    document.tc_page_is_already_initialized = null;
  }
  this._processUpdatesWhenReady = function() {
    var jsIncludes = this._jsIncludes;
    if (!q__areRequiredLibrariesLoaded(jsIncludes)) {
      setTimeout(q__getEventHandlerFunction("_processUpdatesWhenReady", null, this), 100);
      return
    }
    //restore document.write() method to original. used in pair with q__substituteDocumentWrite() method.
    q__restoreDocumentWrite();

    this._processUpdates();
    q__requestFinished(this);

    this._jsIncludes = undefined;
    this._updatables = undefined;
    this._styles = undefined;
    this._cssFiles = undefined;
    this._request = false;
  }

  this._processUpdates = function() {
    var updatables = this._updatables;
    if (!updatables || updatables.length == 0)
      return;

    var rtLibrary = undefined;

    for (var i = 0; i < updatables.length; i ++) {
      var upd = updatables[i];
      var updType = upd.getAttribute("type");
      var updId = upd.getAttribute("id");
      var updHTML = upd.getAttribute("value");
      var updScripts = upd.getAttribute("scripts");
      //runtime generated library with initialization scripts for all components presented on page
      if (updType == Q__UPDATE_TYPE_INITIALIZATION) {
        rtLibrary = updHTML;
      }
      if (q__isSafari()) {  //unescape ampersands for Safari
        updHTML = updHTML.replace(/&#38;/g, '&');
        updHTML = updHTML.replace(/&amp;/g, '&');
      }

      if (updType == Q__UPDATE_TYPE_SIMPLE)
        this._processSimpleUpdate(updId, updHTML, updScripts);
      else if (updType == Q__UPDATE_TYPE_STATE)
        q__processStateUpdate(updId, updHTML);
      else if (updType == Q__UPDATE_TYPE_PORTION)
        this._processPortionUpdate(updId, updHTML, updScripts);
    }

    if (rtLibrary) {
      q__processJSInclude(rtLibrary);
    }

    if (this._completionCallback)
      this._completionCallback(this._requestedComponentId);
  }

  this._processSimpleUpdate = function(updId, updHTML, updScripts) {
    var delayedCompletionCallback = null;
    var delayedCompletionCallbackCoponentId = null;
    if (!updHTML) {
      var params = window[updId];
      updId = params[0];
      updHTML = params[1];
      updScripts = params[2];
      delayedCompletionCallback = params[3];
      delayedCompletionCallbackComponentId = params[4];
    }

    var replacedElement = document.getElementById(updId);

    if (replacedElement.onComponentUnload) {
      var delayedUpdate = replacedElement.onComponentUnload();
      if (delayedUpdate) {
        replacedElement.onComponentUnload = null;
        var varName = "tc_ajaxParams_" + q__lastAjaxId++;
        window[varName] = [updId, updHTML, updScripts, this._completionCallback, this._requestedComponentId];
        this._completionCallback = null;

        setTimeout(q__getEventHandlerFunction("_processSimpleUpdate", "'" + varName + "'", this), 1);
        return;
      }
    }

    var prnt = replacedElement.parentNode;
    var tempDiv = document.createElement("div");
    tempDiv.innerHTML = updHTML;

    var newElements = [];
    for (var childIndex = 0, childCount = tempDiv.childNodes.length; childIndex < childCount; childIndex++) {
      var newElement = tempDiv.childNodes[childIndex];
      q__ajax_pushElementsWithId(newElements, newElement);
    }
    for (var childIndex = 0, childCount = newElements.length; childIndex < childCount; childIndex++) {
      var newElement = newElements[childIndex];
      q__assert(newElement.id, "tc_processSimpleUpdate: newElement without id encountered");
      var elementId = newElement.id;
      var oldElement = document.getElementById(elementId);
      q__assert(oldElement, "Couldn't find component to replace: " + elementId);
      var parent = oldElement.parentNode;
      parent.replaceChild(newElement, oldElement);

      if (q__isOpera()) { // needed for Opera8.5 only (JSFC-1170)
        var oldClassName = parent.className;
        parent.className = parent.className + " _non_existing_class_name_tc__123_";
        parent.className = oldClassName;
      }

    }
    //todo: insert runtime js library invokation with all initial scripts (instead of scripts passing to ajax response) here.
    //todo: replace q__executeScripts() method with runtime js library invokation
    q__executeScripts(updScripts);
    if (delayedCompletionCallback)
      delayedCompletionCallback(delayedCompletionCallbackComponentId);
  }

  this._processPortionUpdate = function(portionName, portionHTML, portionScripts) {
    var componentId = this._targetId;
    var component = document.getElementById(componentId);
    q__assert(component, "Couldn't find component by id: " + componentId);
    this._customProcessor(component, portionName, portionHTML, portionScripts);
  }

}


function q__processStateUpdate(updId, updHTML) {
  var updatedElement = document.getElementById(updId);

  var prnt = updatedElement.parentNode;
  var tempDiv = document.createElement("div");
  tempDiv.innerHTML = updHTML;

  var stateField = tempDiv.childNodes[0];
  q__assert(stateField, "No state field found in updateable");
  q__assert(tempDiv.childNodes.length == 1, "There should be only one element in the state field array");
  var stateFieldId = stateField.id;
  var stateOnPage = document.getElementById(stateFieldId);
  if (stateOnPage)
    stateOnPage.parentNode.replaceChild(stateField, stateOnPage);
  else
    prnt.appendChild(stateField);
}

var q__lastAjaxId = 0;

function q__ajax_pushElementsWithId(destElements, element) {
  if (!element)
    return;
  if (element.id) {
    destElements.push(element);
    return;
  }

  for (var childIndex = 0, childCount = element.childNodes.length; childIndex < childCount; childIndex++) {
    var subElement = element.childNodes[childIndex];
    q__ajax_pushElementsWithId(destElements, subElement);
  }

}

function q__findStateField(node) {
  var children = node.childNodes;
  for (var i = 0, count = children.length; i < count; i++) {
    var child = children[i];
    if (q__isStateField(child))
      return child;
  }
  return null;
}

function q__isStateField(element) {
  if (!element || !element.id)
    return false;
  return q__stringEndsWith(element.id, "::_state")
}

function q__processJSIncludes(jsIncludes) {
  if (jsIncludes) {
    for (var i = 0; i < jsIncludes.length; i ++) {
      var include = jsIncludes[i].getAttribute("value");
      q__processJSInclude(include);
    }
  }
}

function q__processJSInclude(jsInclude) {
  var found = q__isLibraryLoaded(jsInclude);
  if (!found) {
    var newScript = document.createElement("script");
    newScript.type = "text/javascript";
    newScript.src = jsInclude;
    var head = document.getElementsByTagName("head")[0];
    head.appendChild(newScript);
  }
}
function q__processCSSFilesIncludes(cssFiles) {
  if (cssFiles) {
    var head = document.getElementsByTagName("head")[0];
    for (var i = 0; i < cssFiles.length; i ++) {
      var cssFile = cssFiles[i].getAttribute("value");
      var newCSSFile = document.createElement("link");
      newCSSFile.type = "text/css";
      newCSSFile.href = cssFile;
      newCSSFile.rel = "stylesheet";
      head.appendChild(newCSSFile);
    }
  }
}

function q__processStylesIncludes(styles) {
  if (styles) {
    var head = document.getElementsByTagName("head")[0];
    for (var i = 0; i < styles.length; i ++) {
      var rule = styles[i].getAttribute("value");
      q__addRule(rule);
    }
  }
}

function q__executeScripts(source) { //should not be invoked cause all init scripts are moved to runtime generated js library (as for now) 
  if (!source || source.length == 0) return;
  var idx1 = source.indexOf("<script");
  var result;
  if (idx1 > -1) {
    result = source.substring(idx1);
    idx1 = result.indexOf(">");
    idx1 += 1;

    var idx2 = result.indexOf("</script>");
    var script = result.substring(idx1, idx2);
    script = script.replace(/<!--/g, "");
    script = script.replace(/\/\/-->/g, "");
    try {
      window.eval(script);
    } catch (e) {
      alert("Couldn't execute script on Ajax request: \n" + script);
      throw e;
    }
    idx2 += "</script>".length;
    result = result.substring(idx2);
    q__executeScripts(result);
  }
  return;
}

function q__prepareSubmitParameters(componentId) {
  var component = eval(componentId + Q__SUBMIT_PARAMS_SUFFIX);
  var result = "";
  for (var i = 0; i < component.length; i ++) {
    var c = document.getElementById(component[i]);
    if (c.id || c.name) {
      result += (c.id ? c.id : c.name) + "=";
      result += c.value;
      if (i < component.length - 1) {
        result += "&";
      }
    }
  }
  return result;
}

function q__prepareUpdates(buf, componentId, portionName) {
  var component = document.getElementById(componentId);
  if (!portionName || portionName.length <= 0) return;

  buf.append(Q__UPDATE_PORTIONS_SUFFIX).append("=");
  for (var i = 0, count = portionName.length; i < count; i ++) {
    buf.append(portionName[i]);
    if (i < count - 1) {
      buf.append(",");
    }
  }
}

function q__prepareFormParams(form, buf) {
  var elements = form.elements;
  var namesToIndexes = new Array();
  for (var i = 0, count = elements.length; i < count; i ++) {
    var element = elements[i];
    var elementName = element.name;
    if (!elementName)
      continue;
    var elementType = element.type
    if (elementType == "image" || elementType == "button" || elementType == "submit")
      continue;

    var paramName = encodeURIComponent(elementName);
    var paramValue = encodeURIComponent(element.value);
    buf.append(elementName).append("=").append(paramValue);
    if (i < count - 1) {
      buf.append("&");
    }
  }
}

function q__areRequiredLibrariesLoaded(libs) {
  if (!libs) return true;
  for (var i = 0; i < libs.length; i ++) {
    var lib = libs[i].getAttribute("value");
    var loaded = q__isLibraryLoaded(lib);
    if (!loaded) return false;
  }
  return true;
}

function q__isLibraryLoaded(lib) {
  var result = eval("window['tc_loadedLibrary:" + lib + "']");
  return result;
}

function q__validationErrorDuringProcessing(request) {
  if (!request)
    return true;
  if (request.responseText && request.responseText.indexOf("validation error") > -1)
    return true;
  return false;
}

function q__substituteDocumentWrite() {
  document._oldWrite = document.write;
  document._tempAjaxStr = "";
  document.write = function(str) {
    document._tempAjaxStr += str;
  };
}

function q__restoreDocumentWrite(placeHolderId) {
  if (document._tempAjaxStr.length > 0) {
    var div = document.createElement("div");
    div.innerHTML = document._tempAjaxStr;
    var childNodes = div.childNodes;
    if (!placeHolderId) {
      for (var i = 0; i < childNodes.length; i ++) {
        document.body.appendChild(childNodes[i]);
      }
    } else {
      var placeholderEl = document.getElementById(placeHolderId);
      if (placeholderEl) {
        var parentEl = placeholderEl.parentNode;
        for (var i = 0; i < childNodes.length; i ++) {
          parentEl.insertBefore(childNodes[i], placeholderEl);
        }
        parentEl.removeChild(placeholderEl);
      }
    }
  }
  document.write = document._oldWrite;
  document._tempAjaxStr = undefined;
}

//AUTO GENERATED CODE

window['tc_loadedLibrary:/teamdev/internalResource/teamdev/jsf/renderkit/util/ajaxUtil.js'] = true;