TaggingUtils = {};

//======================= test based on browser type =======================

TaggingUtils.sUserAgent = navigator.userAgent;
TaggingUtils.fAppVersion = parseFloat(navigator.appVersion);

TaggingUtils.isOpera = TaggingUtils.sUserAgent.indexOf("Opera") > -1;
TaggingUtils.isIE = TaggingUtils.sUserAgent.indexOf("compatible") > -1
    && TaggingUtils.sUserAgent.indexOf("MSIE") > -1
    && !TaggingUtils.isOpera;
TaggingUtils.isMinIE4 = TaggingUtils.isMinIE5 = TaggingUtils.isMinIE5_5 = TaggingUtils.isMinIE6 = false;

if (TaggingUtils.isIE) {
  var reIE = new RegExp("MSIE (\\d+\\.\\d+);");
  reIE.test(TaggingUtils.sUserAgent);
  var fIEVersion = parseFloat(RegExp["$1"]);

  TaggingUtils.isMinIE4 = fIEVersion >= 4;
  TaggingUtils.isMinIE5 = fIEVersion >= 5;
  TaggingUtils.isMinIE5_5 = fIEVersion >= 5.5;
  TaggingUtils.isMinIE6 = fIEVersion >= 6.0;
}

if (TaggingUtils.isOpera) {
  var fOperaVersion;
  if (navigator.appName == "Opera") {
      fOperaVersion = TaggingUtils.fAppVersion;
  } else {
      var reOperaVersion = new RegExp("Opera (\\d+\\.\\d+)");
      reOperaVersion.test(TaggingUtils.sUserAgent);
      fOperaVersion = parseFloat(RegExp["$1"]);
  }

  TaggingUtils.isMinOpera4 = fOperaVersion >= 4;
  TaggingUtils.isMinOpera5 = fOperaVersion >= 5;
  TaggingUtils.isMinOpera6 = fOperaVersion >= 6;
  TaggingUtils.isMinOpera7 = fOperaVersion >= 7;
  TaggingUtils.isMinOpera7_5 = fOperaVersion >= 7.5;
  TaggingUtils.isMinOpera8 = fOperaVersion >= 8;
}

TaggingUtils.getPageLeft = function(element) {
  element = $(element);
  if (!element) return 0;
  var x = 0;
  while (element) {
    if (element.offsetLeft) x += element.offsetLeft;
    element = element.offsetParent ? element.offsetParent : null;
  }
  return x;
}

TaggingUtils.getPageTop = function(element) {
  element = $(element);
  if (!element) return 0;
  var y = 0;
  while (element) {
    if (element.offsetTop) y += element.offsetTop;
    element = element.offsetParent ? element.offsetParent : null;
  }
  return y;
}

TaggingUtils.getScrollTop = function() {
  return window.pageYOffset
      || document.documentElement.scrollTop
      || document.body.scrollTop
      || 0;
}

TaggingUtils.getScrollLeft = function() {
  return window.pageXOffset
      || document.documentElement.scrollLeft
      || document.body.scrollLeft
      || 0;
}

TaggingUtils.observe = function(element, name, observer, useCapture) {
  element = $(element);
  useCapture = useCapture || false;

  if (element.addEventListener) {
    element.addEventListener(name, observer, useCapture);
  } else if (element.attachEvent) {
    element.attachEvent('on' + name, observer);
  }
}

TaggingUtils.stopObserving = function(element, name, observer, useCapture) {
  element = $(element);
  useCapture = useCapture || false;

  if (element.removeEventListener) {
    element.removeEventListener(name, observer, useCapture);
  } else if (element.detachEvent) {
    element.detachEvent('on' + name, observer);
  }
}

TaggingUtils.remove = function(element) {
  element = $(element);
  element.parentNode.removeChild(element);
}

TaggingUtils.replace = function(node, newNode) {
  node = $(node);
  newNode = $(newNode);
  var parent = node.parentNode;
  parent.replaceChild(newNode, node);
}

TaggingUtils.getParent = function (node, type) {
  node = $(node);
  if(!node) return null;
  var parent = node.parentNode;
  if(type) type = type.toLowerCase();
  while(parent && type && parent.nodeName.toLowerCase() != type){
    parent = parent.parentNode;
  }

  return parent;
}

TaggingUtils.getParent = function (node, type) {
  node = $(node);
  if(!node) return null;
  var parent = node.parentNode;
  if(type) type = type.toLowerCase();
  while(parent && type && parent.nodeName.toLowerCase() != type){
    parent = parent.parentNode;
  }

  return parent;
}

TaggingUtils.firstChild = function(element, type, name) {
  var elem = $(element).firstChild;
  if (name) {
    name = name.toUpperCase();
  }
  if (type && type != 1) {
    while (elem && elem.nodeType != type) {
      elem = elem.nextSibling;
    }
  } else {
    if (name) {
      while (elem && (elem.nodeType != 1 || (elem.nodeType == 1 && elem.nodeName.toUpperCase() != name))) {
        elem = elem.nextSibling;
      }
    } else {
      while (elem && elem.nodeType != 1) {
        elem = elem.nextSibling;
      }
    }
  }

  return elem;
}

TaggingUtils.nextSibling = function(element, type, name) {
  var elem = $(element).nextSibling;
  if (name) {
    name = name.toUpperCase();
  }
  if (type && type != 1) {
    while (elem && elem.nodeType != type) {
      elem = elem.nextSibling;
    }
  } else {
    if (name) {
      while (elem && (elem.nodeType != 1 || (elem.nodeType == 1 && elem.nodeName.toUpperCase() != name))) {
        elem = elem.nextSibling;
      }
    } else {
      while (elem && elem.nodeType != 1) {
        elem = elem.nextSibling;
      }
    }
  }

  return elem;
}

TaggingUtils.prevSibling = function(element, type, name) {
  var elem = $(element).previousSibling;
  if (name) {
    name = name.toUpperCase();
  }
  if (type && type != 1) {
    while (elem && elem.nodeType != type) {
      elem = elem.previousSibling;
    }
  } else {
    if (name) {
      while (elem && (elem.nodeType != 1 || (elem.nodeType == 1 && elem.nodeName.toUpperCase() != name))) {
        elem = elem.previousSibling;
      }
    } else {
      while (elem && elem.nodeType != 1) {
        elem = elem.previousSibling;
      }
    }
  }

  return elem;
}

TaggingUtils.getParentIndex = function (node) {
  var childNodes = node.parentNode.childNodes;
  var index = 0;
  for (var i = 0; i < childNodes.length; i++) {
    if (childNodes[i] == node) {
      return index;
    }
    if (childNodes[i].nodeType == 1) {
      index++;
    }
  }

  return -1;
}

TaggingUtils.swapNode = function (node1, node2) {
  if (node1.swapNode) {
    node1.swapNode(node2);
  } else {
    var nextSibling1 = node1.nextSibling;
    var nextSibling2 = node2.nextSibling;

    var parentNode1 = node1.parentNode;
    parentNode1.removeChild(node1, true);

    var parentNode2 = node2.parentNode;
    parentNode2.removeChild(node2, true);

    parentNode1.insertBefore(node2, nextSibling1);
    parentNode2.insertBefore(node1, nextSibling2);
  }
}

TaggingUtils.findNodes = function (parent, tagName, attrName) {
  var tags = parent.getElementsByTagName(tagName);
  if (attrName) {
    var result = new Array();
    for (var i = 0; i < tags.length; i++) {
      if (tags[i].getAttribute(attrName)) {
        result.push(tags[i]);
      }
    }
    return result;
  } else {
    return tags;
  }
}

TaggingUtils.copyObject = function(destination, source) {
  for (property in source) {
    destination[property] = source[property];
  }
  return destination;
}

TaggingUtils.bind = function(fct, object) {
  var __method = fct;
  return function() {
    __method.apply(object, arguments);
  }
}

TaggingUtils.bindAsEventListener = function(fct, object) {
  var __method = fct;
  return function(event) {
    return __method.apply(object, [event || window.event]);
  }
}

TaggingUtils.getEventPageX = function(evt) {
  if (evt.pageX) {
      return evt.pageX;
  }
  else if (evt.clientX) {
      return evt.clientX + TaggingUtils.getScrollLeft();
  }
  return 0;
}

TaggingUtils.getEventPageY = function(evt) {
  if (evt.pageY) {
      return evt.pageY;
  }
  else if (evt.clientY) {
      return evt.clientY + TaggingUtils.getScrollTop();
  }
  return 0;
}

TaggingUtils.isLeftClick = function(evt) {
  return ((evt.which) && (evt.which == 1)) || ((evt.button) && (evt.button == 1));
}

TaggingUtils.getEventKeyCode = function(evt) {
  if (evt.keyCode) {
    return evt.keyCode;
  } else if (evt.charCode) {
    return evt.charCode;
  } else if (evt.which) {
    return evt.which;
  }
}

TaggingUtils.getEventTarget = function(evt) {
  return evt.target || evt.srcElement;
}

TaggingUtils.stopEvent = function(event) {
  if (event.preventDefault) {
    event.preventDefault();
    event.stopPropagation();
  } else {
    event.cancelBubble = true;
    event.returnValue = false;
  }
}

TaggingUtils.trim = function(str) {
  // strip leading
  var s = str.replace(/^\s+/g, "");
  // strip trailing
  return s.replace(/\s+$/g, "");
}

function $() {
  var elements = new Array();

  for (var i = 0; i < arguments.length; i++) {
    var element = arguments[i];
    if (typeof element == 'string')
      element = document.getElementById(element);

    if (arguments.length == 1)
      return element;

    elements.push(element);
  }

  return elements;
}

TaggingUtils.isInArray = function(arr, value) {
  if (!arr || !value) return false;
  for (var i = 0; i < arr.length; i++) {
    if (arr[i] == value) {
      return true;
    }
  }
  return false;
}


function emptyFunction() {

}


////////////////////////////////////////////////////////////////////////////
//																																				//
//		general functions																										//
//																																				//
////////////////////////////////////////////////////////////////////////////

function TaggingInputKeyDown(evt) {
  var key = TaggingUtils.getEventKeyCode(evt);

  if (evt.shiftKey && key == 45) {//catch shift+insert
    TaggingUtils.stopEvent(evt);
  }
  if (evt.ctrlKey && (key == 118 || key == 86)) {//catch ctrl+v
    TaggingUtils.stopEvent(evt);
  }
  //  prevent submision if input is in a form
  if (key == 13) {
    TaggingUtils.stopEvent(evt);
  }

  if (!document.all) {
    if ((key == 46 || key == 8 || key == 9 || key == 37
        || key == 39 || key == 35 || key == 36)) {
      window.taggingAllowKey = true;
    }
  }
}

function TaggingInputKeyPress(evt, allowComma) {
  var key = TaggingUtils.getEventKeyCode(evt);

  if (!document.all) {
    if (window.taggingAllowKey) {
      window.taggingAllowKey = false;
      return;
    }
  }

  if (evt.shiftKey && key == 45) {//catch shift+insert
    TaggingUtils.stopEvent(evt);
  }
  if (evt.ctrlKey && (key == 118 || key == 86)) {//catch ctrl+v
    TaggingUtils.stopEvent(evt);
  }
  //  prevent submision if input is in a form
  if (key == 13) {
    TaggingUtils.stopEvent(evt);
  }

  if (!(key == 32 ||
        key == 45 ||
        key == 8 ||
        (allowComma && key == ','.charCodeAt(0)) ||
        (48 <= key && key <= 57) ||
        (65 <= key && key <= 90) ||
        key == 95 ||
        (97 <= key && key <= 122))) {
    TaggingUtils.stopEvent(evt);
  }
}

var TaggingAjax = {};

//global static function for getting a request object
TaggingAjax.getTransport = function() {
  if (window.XMLHttpRequest) {
    return new XMLHttpRequest();
  }
  else if (window.ActiveXObject) {
    var MSXML_VERSIONS = new Array('MSXML2.XMLHTTP.5.0', 'MSXML2.XMLHTTP.4.0', 'MSXML2.XMLHTTP.3.0', 'MSXML2.XMLHTTP', 'Microsoft.XMLHTTP');
    for (var i = 0; i < MSXML_VERSIONS.length; i++) {
      try {
        return new ActiveXObject(MSXML_VERSIONS[i]);
      } catch(e) {
      }
    }
  }
}

//TaggingUtils.isMinIE5_5
TaggingAjax.isSupported = TaggingAjax.getTransport() ? true : false;

//===============================================================================

TaggingAjax.Request = function(options) {
  this.options = TaggingUtils.copyObject({
    method:       'GET',
    asynchronous: true,
    sameInstance: true,
    parameters:   ''
  }, options);
}

TaggingAjax.Request.prototype.responseIsSuccess = function() {
  return this.transport.status == "undefined"
      || this.transport.status == 0
      || (this.transport.status >= 200 && this.transport.status < 300);
}

TaggingAjax.Request.prototype.responseIsFailure = function() {
  return !this.responseIsSuccess();
}

TaggingAjax.Request.prototype.makeRequest = function(url) {

  var parameters = this.options.parameters || '';

  try {
    if (this.options.method == 'GET' && parameters.length > 0) {
      if (url.indexOf("?") == -1) {
        url += '?' + parameters;
      } else {
        url += '&' + parameters;
      }
    }

    //we create a transport object or we reuse existing one
    if (this.transport) {
      if (this.options.sameInstance && this.transport) {
        if (this.transport.readyState != 0 && this.transport.readyState != 4) {
          this.transport.abort();
        }
      }
    } else {
      this.transport = TaggingAjax.getTransport();
    }

    //if we don't have a transport we don't make the request
    if (!this.transport) {
      return;
    }

    if (url.indexOf("?") == -1) {
      url += "?ts=" + (new Date()).getTime();
    } else {
      url += "&ts=" + (new Date()).getTime();
    }

    this.transport.open(this.options.method, url, this.options.asynchronous);

    this.transport.onreadystatechange = TaggingUtils.bind(this.onStateChange, this);

/*
    if (this.options.asynchronous) {
      setTimeout(TaggingUtils.bind(function() {
        this.respondToReadyState(1);
      }, this), 10);
    }
*/

    this.setRequestHeaders();

    var body = this.options.postBody ? this.options.postBody : parameters;

    this.timeoutid = setTimeout(TaggingUtils.bind(function() {
      this.respondToReadyState(5);
    }, this), 10000);
    this.errorFired = false;

    this.triggeredComplete = false;
    this.transport.send(this.options.method == 'POST' ? body : null);

  } catch (e) {
    if (this.timeoutid) {
      clearTimeout(this.timeoutid);
    }
    if (!this.errorFired) {
      (this.options['onError'] || emptyFunction)(this.transport);
      this.errorFired = true;
    }
  }
}

TaggingAjax.Request.prototype.setRequestHeaders = function() {
  var requestHeaders = new Array();
  if (this.options.method == 'POST') {
    requestHeaders.push('Content-type',
        'application/x-www-form-urlencoded');

    /* Force "Connection: close" for Mozilla browsers to work around
    * a bug where XMLHttpReqeuest sends an incorrect Content-length
    * header. See Mozilla Bugzilla #246651.
    */
    if (this.transport.overrideMimeType)
      requestHeaders.push('Connection', 'close');
  }

  if (this.options.requestHeaders)
    requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);

  for (var i = 0; i < requestHeaders.length; i += 2)
    this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i + 1]);
}

TaggingAjax.Request.prototype.onStateChange = function() {
  var readyState = this.transport.readyState;
  if (readyState != 1) {
    this.respondToReadyState(this.transport.readyState);
  }
}

TaggingAjax.Request.Events = ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete', 'Timeout'];

TaggingAjax.Request.prototype.respondToReadyState = function(readyState) {
  var event = TaggingAjax.Request.Events[readyState];

  if (event == 'Complete' && !this.triggeredComplete) {
    if (this.timeoutid) {
      clearTimeout(this.timeoutid);
    }
    this.triggeredComplete = true;
    if (this.responseIsSuccess()) {
      (this.options['on' + this.transport.status]
          || this.options['onSuccess']
          || emptyFunction)(this.transport);
    } else {
      if (!this.errorFired) {
        (this.options['on' + this.transport.statusText]
            || this.options['onError']
            || emptyFunction)(this.transport);
        this.errorFired = true;
      }
    }
  } else {
    (this.options['on' + event] || emptyFunction)(this.transport);
  }

  /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
  if (event == 'Complete') {
    this.transport.onreadystatechange = emptyFunction;
  }
}

//global object for internationalization
if (!window.TaggingI18N) {
    window.TaggingI18N = {
        values : {}
    }
}

TaggingI18N.setKey = function(key, value) {
    TaggingI18N.values[key] = value;
}

TaggingI18N.getKey = function(key, defaultValue) {
    var value = TaggingI18N.values[key];
    if (!value && defaultValue) {
        value = defaultValue;
    }

    return value;
}
