document.write('<style type="text/css">\nbody {visibility: hidden;}</style>');

function lpopro_vars() {
  this.pageUrl = document.URL;
  this.pageReferrer = document.referrer;
  this.isControlScript = true;
  this.responsePoints = false;
  this.variations = false;
  this.page = "";
  this.body = "";
  this.experimentId = null;
  this.timeout = null;
  this.onloadfunc;
  this.isMSIE = /*@cc_on!@*/false;

  // these variables should be overwritten by client on page running experiments
  this.experimentIds = null;
  this.websiteId = null;
  this.js_url = null; // could be set to default url
  this.cookieDomain = null;

  // auxiliary variable used to create div to sanitize body's innerHTML
  this.pageDiv = null;

  this.lpoid = 0;
  this.variationIds = new Array();

  this.matches = 0;
  this.message = "";

  this.replaceBody = false;

  this.replacementDone = false;

  // Optional HTML DOM element within which replacements should be done.
  // Assign if you don't need to replace the whole body and want to avoid
  // various weird problems.
  this.context = false;
  this.contexts = new Array();
  this.contextSepharator = "#__lpopro_seph^";

  this.timeout = 3000;
}

var LPOPRO_VARS = new lpopro_vars();

if (typeof(lpopro_custom_timeout) != "undefined" && !isNaN(lpopro_custom_timeout)) {
  LPOPRO_VARS.timeout = lpopro_custom_timeout;
}

/**
 * LPOPRO WS url
 */
LPOPRO_VARS.js_url = "//api.hurra.com/lpopro/js/api/";

/**
 * LPOPRO_CONTROL class
 *
 */
function LPOPRO_CONTROL() {}

/*
 * cache classes as static methods of LPOPRO_CONTROL:
 */

//main class of cache - contains collection of response points
//and collection of combinations, moreover has id of experiment
LPOPRO_CONTROL.experiment = function() {
  this.rps = [];
  this.vars = [];
}

//assignment class -> is extends by lpopro_variation and lpopro_responsePoint
LPOPRO_CONTROL.assignment = function(id, evalSeq, bdTxtCnd, cgiNmCnd, cgiValCnd,
    refUrlCnd, stpAftHit, urlCnd, srchTxt, rplTxt) {
  this.id = id;
  this.evalSeq = evalSeq;
  this.bdTxtCnd = bdTxtCnd;
  this.cgiNmCnd = cgiNmCnd;
  this.cgiValCnd = cgiValCnd;
  this.refUrlCnd = refUrlCnd;
  this.stpAftHit = stpAftHit;
  this.urlCnd = urlCnd;
  this.srchTxt = srchTxt;
  this.rplTxt = rplTxt;
}

//classes to represent variations and response points
LPOPRO_CONTROL.variation = function() {}
LPOPRO_CONTROL.responsePoint = function() {}


/*
 * LPOPRO_CONTROL class static methods:
 */

LPOPRO_CONTROL.errhdl = function() {
  if (!document.body) {
    setTimeout(LPOPRO_CONTROL.errhdl, 100);
    return;
  }
  document.body.style.visibility = 'visible';
  if(document.getElementById('lpopro_test_span') == null) {
    if (LPOPRO_VARS.onloadfunc && typeof LPOPRO_VARS.onloadfunc == 'function') {
      LPOPRO_VARS.onloadfunc();
    }
  } else {
    clearTimeout(LPOPRO_VARS.timeout);
  }

}

LPOPRO_CONTROL.addLoadEvent = function(func) {
  if (window.addEventListener) {

    window.addEventListener("load", func, false);
  } else if (window.attachEvent) {
    window.attachEvent('onload', func);
  } else {
    LPOPRO_VARS.onloadfunc = window.onload;
    window.onload = func;
  }
}

LPOPRO_CONTROL.loadScript = function(src) {
  var lpoproScript = document.createElement("script");
  lpoproScript.setAttribute('type',"text/javascript");
  lpoproScript.setAttribute('src',src);
  document.getElementsByTagName("head").item(0).appendChild(lpoproScript);
  return lpoproScript;
}

/*
 * checkBody(bodyCondition) - tries to match given regular
 * expression to page content.
 *
 * Parameters: bodyCondition - body condition (can be a regular expression).
 * Should be sent in _T_W_O_ parts (e.g. "sam" + "ple");
 */
LPOPRO_CONTROL.checkBody = function(bodyCondition) {
  var timestamp = (new Date()).getTime();
  var marker = "dc0164b2-1e9e-11de-" + timestamp;
  bodyCondition = LPOPRO_CONTROL.encodeSearchtext(bodyCondition);
  var lpopro_bodycondition = document.createElement('div');
  lpopro_bodycondition.id = 'lpopro_bodycondition' + marker;
  lpopro_bodycondition.innerHTML = bodyCondition + marker;

  bodyCondition = LPOPRO_CONTROL.sanitize(lpopro_bodycondition.innerHTML, marker);

  bodyCondition = bodyCondition.replace(/\(/gi, "\\(");
  bodyCondition = bodyCondition.replace(/\)/gi, "\\)");
  bodyCondition = bodyCondition.replace(/xxFxPAREG_xx/gi, ")");
  bodyCondition = bodyCondition.replace(/xxFxPAREGxx/gi, "(");

  // List fix for IE - must be called after escaping parentheses!!
  bodyCondition = bodyCondition.replace(/<\/li>/gmi, '(\\s*$&)?');
  bodyCondition = LPOPRO_CONTROL.decodeReqularExpression(bodyCondition);
  var pageBody = LPOPRO_VARS.body;
  var re = new RegExp(bodyCondition, "gmi");
  var match = false;
  while (match = re.test(pageBody)) {
    if (!LPOPRO_CONTROL.isInsideForbiddenBlock(pageBody.substr(0, re.lastIndex))) {
      return match;
    } else {
      continue;
    }
    pageBody = pageBody.substr(re.lastIndex);
    re = new RegExp(bodyCondition, "gmi");
  }
}

/*
 * checkReferrer(referrer) - tries to match given referrer value
 * to current page referrer
 *
 * Parameters: referrer - referrer (can be a regular expression)
 */
LPOPRO_CONTROL.checkReferrer = function(referrer) {
  if(!LPOPRO_VARS.isMSIE) {
    LPOPRO_VARS.pageReferrer = encodeURIComponent(LPOPRO_VARS.pageReferrer);
    LPOPRO_VARS.pageReferrer = LPOPRO_CONTROL.decodeReqularExpression(LPOPRO_VARS.pageReferrer);
    referrer = encodeURIComponent(referrer);
    referrer = LPOPRO_CONTROL.decodeReqularExpression(referrer);
  }
  var re = new RegExp(referrer);
  return re.test(LPOPRO_VARS.pageReferrer);
}


/*
 * checkUrl(url) - tries to match given url value to current
 * page url
 *
 * Parameters: url - url (can be a regular expression)
 */
LPOPRO_CONTROL.checkUrl = function(url) {
  if(!LPOPRO_VARS.isMSIE) {
    url = encodeURIComponent(url);
    url = LPOPRO_CONTROL.decodeReqularExpression(url);
  }
  var re = new RegExp(url);
  return re.test(LPOPRO_VARS.pageUrl);
}

/*
 * checkCGIParameter(CGIParameter, CGIValue) - tries to match
 * given parameter and value to current page url parameters
 *
 * Parameters: CGIParameter - name of parameter to check (cannot be a
 * regular expression) CGIValue - value of the parameter (can be a regular
 * expression).
 */
LPOPRO_CONTROL.checkCGIParameter = function(CGIParameter, CGIValue) {
  var match = false;

  if(!LPOPRO_VARS.isMSIE){
    CGIParameter = encodeURIComponent(CGIParameter);
    CGIParameter = LPOPRO_CONTROL.decodeReqularExpression(CGIParameter);
    CGIValue = encodeURIComponent(CGIValue);
    CGIValue = LPOPRO_CONTROL.decodeReqularExpression(CGIValue);
  }

  if (LPOPRO_VARS.pageUrl.indexOf("?") > -1) {
    var strQueryString = LPOPRO_VARS.pageUrl.substr(LPOPRO_VARS.pageUrl.indexOf("?")+1);
    var aQueryString = strQueryString.split("&");
    for (var i = 0; i < aQueryString.length; i++) {
      if (aQueryString[i].indexOf("=") > -1) {
        var CGIval = aQueryString[i].split("=");
        var re1 = new RegExp(CGIParameter);
        var re2 = new RegExp(CGIValue);
        if (re1.exec(CGIval[0]) && re2.exec(CGIval[1])) {
          match = true;
        }
      }
    }
  }
  return match;
}

/*
 * Decode special signs used in regular expression that are encoded by
 * encodeURIComponent function.
 *
 * Parameters: regex - regular expression to decode
 */
LPOPRO_CONTROL.decodeReqularExpression = function(regex) {
  regex = ' ' + regex;
  regex = regex.replace(/%5C/g, '\\');
  regex = regex.replace(/([^\\])%7C/g, '$1|');
  regex = regex.replace(/%2B/g, '+');
  regex = regex.replace(/([^\\])%5B/g, '$1[');
  regex = regex.replace(/([^\\])%5D/g, '$1]');
  regex = regex.replace(/%3F/g, '?');
  regex = regex.replace(/([^\\])%5E/g, '$1^');
  regex = regex.replace(/%24/g, '$');
  regex = regex.replace(/([^\\])%7B/g, '$1{');
  regex = regex.replace(/([^\\])%7D/g, '$1}');
  regex = regex.replace(/%3D/g, '=');
  regex = regex.replace(/%3A/g, ':');
  regex = regex.replace(/%25/g, '%');
  regex = regex.replace(/%26/g, '&');
  regex = regex.replace(/%2F/g, '/');
  regex = regex.replace(/%2C/g, ',');
  regex = regex.replace(/%3B/g, ';');
  regex = regex.replace(/%23/g, '#');
  regex = regex.substring(1);
  return regex;
}

/*
 * matchVariation(search, replacement) - tries to match given regular
 * expression to page content.
 *
 * Parameters: search, replacement - regexp values containing search and
 * replacement string respectively.
 */
LPOPRO_CONTROL.markVariation = function(prefix, suffix, variationId,varMatches, variationType) {
  var search = prefix + '((.|\\s)*?)' + suffix;
  // before this method countMatches should be run
  var matches = LPOPRO_CONTROL.countMatches(search);
  var replacement = '$1';
  var match = false;
  // Fetch site body from global variable.
  var pageBody = LPOPRO_VARS.page;
  // Reset variation matches counter (it is global variable).
  if (pageBody) {
    var localRe = new RegExp(search, "mi");
    var matched = null;
    var tmpBody = "";
    for (i=0;i<matches;i++) {
      var re = new RegExp(search,"gmi");
      matched = re.exec(pageBody);
      if (matched==null) return false;
      if (LPOPRO_CONTROL.isInsideForbiddenBlock(pageBody.substr(0, matched.index))) {
        continue;
      }
      match = true;
      var leftSide = pageBody.substr(0, matched.index);
      var rightSide = pageBody.substr(re.lastIndex, pageBody.length - re.lastIndex);
      var text = pageBody.substr(matched.index, re.lastIndex - matched.index);
      var replacementText = replacement;
      var openIndex = leftSide.lastIndexOf("<");
      var selfCloseIndex = leftSide.lastIndexOf("/>");
      var closeIndex = leftSide.lastIndexOf(">");
      var selfRightCloseIndex = rightSide.indexOf("/>");
      var rightCloseIndex = rightSide.indexOf(">");
      var idRegex = /\sid=/gi;
      var leftIdMatch = leftSide.toLowerCase().match(idRegex);
      if (leftIdMatch) {
        var leftIdPosition = leftSide.toLowerCase().lastIndexOf(leftIdMatch[leftIdMatch.length-1])+1;
      }
      else
        var leftIdPosition = -1;
      var leftIdMatched = /id=(?:"|'|)(\w+)(?:"|'|)/i.exec(leftSide.substr(leftIdPosition, leftSide.length - leftIdPosition));
      var rightIdMatch = rightSide.toLowerCase().match(idRegex);
      if (rightIdMatch) {
        var rightIdPosition = rightSide.toLowerCase().indexOf(rightIdMatch[0])+1;
      }
      else
        var rightIdPosition = -1;
      var rightIdMatched = /id=(?:"|'|)(\w+)(?:"|'|)/i.exec(rightSide.substr(rightIdPosition, rightSide.length - rightIdPosition));

      if (variationType == "V") {
        // Search text is not a part of any tag and does not contain any.
        if ( (openIndex < closeIndex) && (text.indexOf("<") == -1) && (text.indexOf(">") == -1) ) {
          LPOPRO_CONTROL.putVariationId(variationId,"lpo_variation_");
          var decoratorOpenTag = "<span class=\"lpopro_preview\" id=\"lpo_variation_" + LPOPRO_VARS.lpoid + "\">";
          var decoratorCloseTag = "</span>";
          replacementText = decoratorOpenTag + replacement + decoratorCloseTag;
        }
        // Search text is the entire opening tag, but without the content and closing tag
        // only search for id within search text
        // if id not found within the search text, create one
        else if (text.indexOf("<")>=0 && text.indexOf(">") == -1) {
          var idInText = text.match(/\\sid=(?:"|'|)(\w+)(?:"|'|)/i);
              if (idInText == null) {
                LPOPRO_CONTROL.putVariationId(variationId,"lpo_variation_");
                text = text.replace('>', ' id="lpo_variation_' + LPOPRO_VARS.lpoid + '">');
              }
        }
        // Search text is part of a tag.
        // Try to find id of a tag within left context (from begin of processed body to current match).
        else if (leftIdMatched && (leftIdMatched.index + leftIdPosition > openIndex)) {
          foundId = leftIdMatched[1];
          LPOPRO_CONTROL.putVariationId(variationId,foundId);
        }
        // If not found try to find id within right context (from current match to end of processed body).
        // Search text does not contain closing tag within itself.
        else if (text.indexOf(">") == -1 && rightIdMatched && (rightIdMatched.index + rightIdPosition < rightCloseIndex)) {
          foundId = rightIdMatched[1];
          LPOPRO_CONTROL.putVariationId(variationId,foundId);
        }
        // If id not found try to assign new one.
        else if (text.indexOf(">") == -1){
          LPOPRO_CONTROL.putVariationId(variationId,"lpo_variation_");
          rightSide = rightSide.replace(/(>|\/>)/," id=\"lpo_variation_"+ LPOPRO_VARS.lpoid +"\" $1");
        }
        // Search text contains some tag or group of tags. Wrap it with a span.
        // And add flag for highlighter indicating that a parent element should
        // be marked up (We can not highlight that span because we do not know
        // if there are block or inline tags within a search text.
        // Block tags inside span could break layout).
        else {
          LPOPRO_CONTROL.putVariationId(variationId,"lpo_get_parent");
          var decoratorOpenTag = "<span class=\"lpopro_preview\" id=\"lpo_get_parent" + LPOPRO_VARS.lpoid + "\">";
          var decoratorCloseTag = "</span>";
          replacementText = decoratorOpenTag + replacement + decoratorCloseTag;
        }
      }
      text = text.replace(localRe, replacementText);
      // page body is in fact remaining part of body
      pageBody = rightSide;
      tmpBody = tmpBody + leftSide + text;
    }
    LPOPRO_VARS.page = tmpBody + pageBody;
  }
  return match;
}

LPOPRO_CONTROL.putVariationId = function(variationId,lpoHtmlId) {
  var htmlId = null;
  if (lpoHtmlId.indexOf("lpo_")!=-1) {
    LPOPRO_VARS.lpoid += 1;
    htmlId = lpoHtmlId + LPOPRO_VARS.lpoid;
  } else {
    htmlId = lpoHtmlId;
  }
  if (variationId) {
    LPOPRO_VARS.variationIds[htmlId] = variationId;
  } else {
    LPOPRO_VARS.variationIds[htmlId] = "factor_"+LPOPRO_VARS.lpoid;
  }
}

/*
 * matchVariation(search, replacement) - tries to match given regular
 * expression to page content.
 *
 * Parameters: search, replacement - regexp values containing search and
 * replacement string respectively.
 */
LPOPRO_CONTROL.matchVariation = function(search, replacement) {
  search = LPOPRO_CONTROL.decodeReqularExpression(search);

  var match = false;
  var pageBody = LPOPRO_VARS.page;

  if (replacement) {
    if (pageBody) {
      var re = new RegExp(search, "gmi");
      if (re.test(pageBody)) {
        LPOPRO_VARS.replaceBody = true;
        match = true;
        pageBody = pageBody.replace(re, replacement);
        LPOPRO_VARS.page = pageBody;
      }
    }
  }

  return match;
};

/*
 * matchOriginalVariation(search) - tries to match given regular
 * expression to page content.
 *
 * Parameters: search - regexp values containing search string
 */
LPOPRO_CONTROL.matchOriginalVariation = function(search) {
  search = LPOPRO_CONTROL.decodeReqularExpression(search);
  var match = false;
  var pageBody = LPOPRO_VARS.page;
  if (pageBody) {
    var re = new RegExp(search, "gmi");
    while (match = re.test(pageBody)) {
      if (!LPOPRO_CONTROL.isInsideForbiddenBlock(pageBody.substr(0, re.lastIndex))) {
        return match;
      } else {
        continue;
      }
      pageBody = pageBody.substr(re.lastIndex);
      re = new RegExp(search, "gmi");
    }
  }
  return match;
};

/*
 * Replaces all occurences of a fixed string in a given text
 */
LPOPRO_CONTROL.replaceAll = function(strText, strTarget, strSubString) {
  var intIndexOfMatch = strText.indexOf( strTarget );
  while (intIndexOfMatch != -1){
    strText = strText.replace( strTarget, strSubString )
    intIndexOfMatch = strText.indexOf( strTarget );
  }
  return strText ;
}


/*
 * Clears all incorrect entries in search text from div
 */
LPOPRO_CONTROL.sanitize = function(divText, marker) {
  var lastMarker = divText.indexOf(marker);
  divText = divText.substring(0, lastMarker);

  //re-replace brackets
  divText = LPOPRO_CONTROL.replaceAll(divText, 'xxFxQREGxx', '\?');
  divText = LPOPRO_CONTROL.replaceAll(divText, 'xxfxqregxx', '\?');
  divText = LPOPRO_CONTROL.replaceAll(divText, '%28', '(');
  divText = LPOPRO_CONTROL.replaceAll(divText, '%29', ')');
  divText = LPOPRO_CONTROL.replaceAll(divText, '&lt;', '<');
  divText = LPOPRO_CONTROL.replaceAll(divText, '&gt;', '>');
  divText = LPOPRO_CONTROL.replaceAll(divText, 'xxFxBSLHxx', "\\");
  divText = LPOPRO_CONTROL.replaceAll(divText, 'xxfxbslhxx', "\\");

  divText = divText.replace(/\s+/gm, '\\s*');
  return divText;
};

LPOPRO_CONTROL.appendResponsePointId = function(responsePointId) {
  if(LPOPRO_VARS.responsePoints) {
    LPOPRO_VARS.responsePoints += ',' + responsePointId;
  } else	{
    LPOPRO_VARS.responsePoints = 'rps=' + responsePointId;
  }
}

LPOPRO_CONTROL.appendVariationId = function(variationId) {
  if(LPOPRO_VARS.variations) {
    LPOPRO_VARS.variations += ',' + variationId;
  } else	{
    LPOPRO_VARS.variations = 'fvs=' + variationId;
  }
}

LPOPRO_CONTROL.createCookie = function(name,value,days) {
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
  }
  else var expires = "";
  var cookieDomain = '';

  if (LPOPRO_VARS.cookieDomain != null && LPOPRO_VARS.cookieDomain != '') {
    cookieDomain = ' domain=' + LPOPRO_VARS.cookieDomain;
  }

  document.cookie = name+"="+value+expires+"; path=/;" + cookieDomain;
}

LPOPRO_CONTROL.deleteCookie = function(name) {
  if (LPOPRO_VARS.cookieDomain != null && LPOPRO_VARS.cookieDomain != '') {
    document.cookie = name +'=; expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/; domain=' + LPOPRO_VARS.cookieDomain;
  } else {
    document.cookie = name +'=; expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/';
  }
}

LPOPRO_CONTROL.deleteAllDomainCookies = function(name, domain) {
  var currentDomain = domain;
  document.cookie = name + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/';
  while (true) {
    if (currentDomain.indexOf('.')>0) {
      currentDomain = currentDomain.replace(currentDomain.substring(0, currentDomain.indexOf('.')), '');
    } else if (currentDomain.indexOf('.')==0) {
      currentDomain = currentDomain.substring(1, currentDomain.length);
    } else {
      break;
    }
    document.cookie = name + '=; expires=Thu, 01-Jan-1970 00:00:01 GMT;path=/; domain=' + currentDomain;
  }
}

LPOPRO_CONTROL.refreshCombinationCookie = function() {
  var name = '__lpopro_combinationid';

  var cookies = document.cookie.split(';');
  var cmb = LPOPRO_CONTROL.readCookie(name);

  if (cmb!=null && cmb!='') {
    LPOPRO_CONTROL.deleteAllDomainCookies(name, document.domain);
    LPOPRO_CONTROL.createCookie(name, cmb, 365);
  }
}

LPOPRO_CONTROL.trim = function(str) {
  return str.replace(/^\s+/, '').replace(/\s+$/, '');
}

LPOPRO_CONTROL.readCookie = function(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) {
      var value = c.substring(nameEQ.length,c.length);
      // look for cookies from higher level domain
      // in case there was a change in cookie domain in backoffice
      if (value=='') continue; // return null;
      else if (value=='null') return null;
      else return value;
    }
  }
  return null;
}


LPOPRO_CONTROL.isCookie = function(name) {
  var nameEQ = name + "=";
  var ca = document.cookie.split(';');
  for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0) {
      return true;
    }
  }
  return false;
}

LPOPRO_CONTROL.compareEval = function(a, b) {
  var evalDiff = a.evalSeq - b.evalSeq;

  if (evalDiff != 0) return evalDiff;

  if (a.stpAftHit && !b.stpAftHit) {
    return 1;
  } else if (!a.stpAftHit && b.stpAftHit) {
    return -1;
  } else {
    return a.id - b.id;
  }
}

LPOPRO_CONTROL.getExperiment = function() {
  if (LPOPRO_VARS.experimentId == null) {
    LPOPRO_CONTROL.postProcessing();
    return false;
  }
  var src = LPOPRO_VARS.js_url + "lpopro-experiment.js?experiment_id=" + LPOPRO_VARS.experimentId + "&encoding=" + LPOPRO_CONTROL.getCharacterEncoding();
  var lpopro_assignments = LPOPRO_CONTROL.loadScript(src);

  if (LPOPRO_VARS.isMSIE) {
    lpopro_assignments.onreadystatechange = function () {
      if (lpopro_assignments.readyState == 'loaded' || lpopro_assignments.readyState == 'complete') {
        LPOPRO_CONTROL.runProcessing();
      }
    }
  } else {
    lpopro_assignments.onload=function() {
      LPOPRO_CONTROL.runProcessing();
    }
  }

}


LPOPRO_CONTROL.encodeCommon = function(text) {
  return text;
}

LPOPRO_CONTROL.encodeText = function(text) {
  text = LPOPRO_CONTROL.encodeCommon(text);
  text = text.replace(/\?/gi, "xxFxQMARKxx");
  return text;
}

LPOPRO_CONTROL.encodeSearchtext = function(searchtext) {
  searchtext = LPOPRO_CONTROL.encodeCommon(searchtext);

  searchtext = searchtext.replace(/\\\)/gi, "_-TEMP-_");
          searchtext = searchtext.replace(/\)/gi, "xxFxPAREG_xx");
          searchtext = searchtext.replace(/_-TEMP-_/gi, ")");

          searchtext = searchtext.replace(/\\\(/gi, "_-TEMP-_");
          searchtext = searchtext.replace(/\(/gi, "xxFxPAREGxx");
          searchtext = searchtext.replace(/_-TEMP-_/gi, "(");

          searchtext = searchtext.replace(/\\\?/gi, "xxFxQMARKxx");
          searchtext = searchtext.replace(/\?/gi, "xxFxQREGxx");
          // x instead of # because IE puts / before first # in url
          searchtext = searchtext.replace(/\\/gi, "xxFxBSLHxx");
          return searchtext;
}

LPOPRO_CONTROL.decodeText = function(text) {
  text = text.replace(/xxFxQMARKxx/gi, "?");
  text = text.replace(/xxfxqmarkxx/gi, "?");
  return text;
}

/*
 * Makes the browser process the source given by setting it as innerHTML
 * in a fake DOM element innerHTML. Ensures that all src attributes are
 * cleared in order to avoid unwanted image requests. After pulling the
 * source back from the innerHTML, src attributes are re-attached.
 */
LPOPRO_CONTROL.innerhtmlize = function(source) {
  var origUrlRegexp = new RegExp('src="(.*?)"', 'gm');
  var lpopro_search = document.createElement('div');
  var origSrcs = new Array(); // the original src attributes from search text
  var origPaths = new Array(); // like origSrcs, but only the value of src
  var origPaths2 = new Array(); // like origPaths, but without xxFxBSLHxx

  var timestamp = (new Date()).getTime();
  var marker = "dc0164b2-1e9e-11de-" + timestamp;

  processed = source;

  var i = 0;
  while ((result = origUrlRegexp.exec(processed)) != null) {
    var orig = result[0];
    var orig2 = orig.replace(/xxFxBSLHxx/gm, '');
    origPaths[i] = result[1];
    origPaths2[i] = result[1].replace(/xxFxBSLHxx/gm, '');

    processed = processed.replace(orig, orig2);
    origSrcs[i] = orig;
    i++;
  }

  lpopro_search.innerHTML = processed + marker;
  processed = LPOPRO_CONTROL.sanitize(lpopro_search.innerHTML, marker);
  // change srcs back
  for (i = 0; i < origSrcs.length; i++) {
    var changeBackRegex = new RegExp('(src=".*?)' + origPaths2[i] + '(")', 'gmi');
    processed = processed.replace(changeBackRegex, '$1' + origPaths[i] + '$2');
  }

  return processed;
}

LPOPRO_CONTROL.prepareSearchText = function(searchText, assignment_id) {
  searchText = searchText.replace(/(href=["']?)xxFxBSLHxx\.xxFxBSLHxx\.\//gmi, '$1../');
                                          searchText = searchText.replace(/(href=["']?)xxFxBSLHxx\.\//gmi, '$1./');

                                                                                  searchText = LPOPRO_CONTROL.innerhtmlize(searchText);

                                                                                  searchText = searchText.replace(/\/(['" >])/gi, '/?$1');

                                                                                      searchText = searchText.replace(/\(/gi, "\\(");
                                                                                      searchText = searchText.replace(/\)/gi, "\\)");
                                                                                  searchText = searchText.replace(/xxFxPAREG_xx/gi, ")");
                                                                                  searchText = searchText.replace(/xxFxPAREGxx/gi, "(");
                                                                                  searchText = searchText.replace(/xxFxBSLHxx/gi, "\\");

                                                                                  // List fix for IE - must be called after escaping parentheses!!
                                                                                  searchText = searchText.replace(/<\/li>/gmi, '(\\s*$&)?');

                                                                                  return searchText;
}

LPOPRO_CONTROL.checkAssignmentsCondition = function(assignment, assignment_condition) {
  if (assignment.bdTxtCnd){
    if (assignment_condition) assignment_condition = LPOPRO_CONTROL.checkBody(assignment.bdTxtCnd);
  }
  if (assignment.refUrlCnd){
    if (assignment_condition) assignment_condition = LPOPRO_CONTROL.checkReferrer(assignment.refUrlCnd);
  }
  if (assignment.cgiNmCnd && assignment.cgiValCnd){
    if (assignment_condition) assignment_condition = LPOPRO_CONTROL.checkCGIParameter(assignment.cgiNmCnd, assignment.cgiValCnd);
  }
  if (assignment.urlCnd){
    if (assignment_condition) assignment_condition = LPOPRO_CONTROL.checkUrl(assignment.urlCnd);
  }

  return assignment_condition;
}

/*
 * Performs some tests in order to ensure that currently matched
 * regular expression pattern resides inside valid context.
 * Returns true when matched pattern resides inside forbidden context,
 * otherwise returns false.
 *
 * Invalid contexts:
 * 1) commented block  - e.g. &lt;!-- pattern --&gt;
 * 2) script block - e.g. &lt;script language="javascript"&gt;pattern&lt;/script&gt;
 * 3) inline script block - e.g. &lt;script language="javascript" src="pattern" /&gt;
 *
 * @param String leftContext - string laying left to the matched pattern,
 * @return boolean - true when matched pattern resides inside forbidden
 *                   context, false otherwise.
 */
LPOPRO_CONTROL.isInsideForbiddenBlock = function(leftContext)
{
  var commentRe = new RegExp("<!--(?!(.|\n)*-->)", "gm");
  var scriptRe = new RegExp("<script(?!(.|\n)+<\/script>)", "gmi");
  var inlineScriptRe = new RegExp("<script.*/>", "gmi");

  if (leftContext.search(commentRe) != -1) {
    return true;
  }
  var matched = null;
  while((matched = scriptRe.exec(leftContext)) != null) {
    var inlineContext = leftContext.substr(matched.index, leftContext.length);

    if (inlineContext.search(inlineScriptRe) != -1) {
      continue;
    }
    return true;
  }
  return false;
}

LPOPRO_CONTROL.countMatches = function(search, id) {
  var count = 0;
  var pageBody = LPOPRO_VARS.page;
  var srchText = LPOPRO_CONTROL.encodeSearchtext(search);

  srchText = LPOPRO_CONTROL.prepareSearchText(srchText, id);
  srchText = LPOPRO_CONTROL.decodeReqularExpression(srchText);

  // reset variation matches counter (it is global variable)
  if (srchText) {
    if (pageBody) {
      var re = new RegExp(srchText, "gmi");
      while (re.test(pageBody)) {
        if (!LPOPRO_CONTROL.isInsideForbiddenBlock(pageBody.substr(0, re.lastIndex))) {
          count += 1;
        } else {
          continue;
        }
        pageBody = pageBody.substr(re.lastIndex);
        re = new RegExp(srchText, "gmi");
      }
    }
  }
  return count;
}

LPOPRO_CONTROL.match = function(id,searchText, rplTxt, assignment_condition) {

  var srchText = LPOPRO_CONTROL.encodeSearchtext(searchText);

  srchText = LPOPRO_CONTROL.prepareSearchText(srchText, id);

  if (rplTxt == null) {
    if(assignment_condition) assignment_condition = LPOPRO_CONTROL.matchOriginalVariation(srchText);
  } else {
    if(assignment_condition) assignment_condition = LPOPRO_CONTROL.matchVariation(srchText , rplTxt);
  }

  return assignment_condition;
}

LPOPRO_CONTROL.cookieMonster = function() {
  LPOPRO_CONTROL.refreshCombinationCookie();

  var lpopro_combinationId_cookie = LPOPRO_CONTROL.readCookie('__lpopro_combinationid');

  if (lpopro_combinationId_cookie == null && LPOPRO_VARS.experimentId != null) {
    LPOPRO_CONTROL.createCookie('__lpopro_combinationid', LPOPRO_VARS.experimentId, 365);
  }

}

LPOPRO_CONTROL.preparePageContent = function(page) {
  page = page.replace(/\r/gm, ' ');
  page = page.replace(/\n/gm, ' ');
  page = page.replace(/\t/gm, ' ');
  page = LPOPRO_CONTROL.encodeText(page);
  return page;
}

LPOPRO_CONTROL.runProcessing = function() {
  LPOPRO_CONTROL.cookieMonster();
  LPOPRO_VARS.page = LPOPRO_CONTROL.preparePageContent(LPOPRO_VARS.page);
  LPOPRO_VARS.body = LPOPRO_CONTROL.preparePageContent(LPOPRO_VARS.body)
  var rps = LPOPRO_VARS.experiment.rps;
  var vars = LPOPRO_VARS.experiment.vars;
  var assignments = rps.concat(vars).sort(LPOPRO_CONTROL.compareEval);
  if(!assignments) return false;

  var continue_process = true;
  for (var i=0;i<assignments.length;i++){
    var assignment_condition = true;

    if (!continue_process) continue;

    assignment_condition = LPOPRO_CONTROL.checkAssignmentsCondition(assignments[i], assignment_condition);

    // if search text is not null it means that assignment is variation
    if (assignments[i].srchTxt != null) {

      assignment_condition = LPOPRO_CONTROL.match(assignments[i].id, assignments[i].srchTxt, assignments[i].rplTxt, assignment_condition);

      if(assignment_condition) LPOPRO_CONTROL.appendVariationId(assignments[i].id);
    }

    if (!assignments[i].srchTxt) {
      if (assignment_condition) LPOPRO_CONTROL.appendResponsePointId(assignments[i].id);
    }

    if (assignments[i].stpAftHit) {
      if(assignment_condition) continue_process = false;
    }
  }

  // setTimeout is necessary here, do not remove if you are not sure what you are doing!
  setTimeout('LPOPRO_CONTROL.postProcessing();', 0);
  LPOPRO_CONTROL.track();

  return true;
}

LPOPRO_CONTROL.postProcessing = function() {
  document.body.style.visibility = 'visible';

  if (LPOPRO_VARS.onloadfunc) {
    LPOPRO_VARS.onloadfunc();
    LPOPRO_VARS.onloadfunc = null;
  }

  if (LPOPRO_CONTROL.postprocessingCallback && LPOPRO_VARS.replaceBody) {
    LPOPRO_CONTROL.callPostprocessingCallback();
  }
}

LPOPRO_CONTROL.replaceBody = function() {
  if (LPOPRO_VARS.replaceBody) {
    if ((LPOPRO_CONTROL.typeOf(LPOPRO_VARS.contexts) == 'array') && (LPOPRO_VARS.contexts.length > 0)) {
      var pageContexts = LPOPRO_VARS.page.split(LPOPRO_VARS.contextSepharator);

      for (i = 0; i < LPOPRO_VARS.contexts.length; i++) {
        if (pageContexts[i]) LPOPRO_VARS.contexts[i].innerHTML = LPOPRO_CONTROL.decodeText(pageContexts[i]);
      }
    } else if (LPOPRO_VARS.context) {
      LPOPRO_VARS.context.innerHTML = LPOPRO_CONTROL.decodeText(LPOPRO_VARS.page);
    } else {
      document.body.innerHTML = LPOPRO_CONTROL.decodeText(LPOPRO_VARS.page);
    }
  }
  LPOPRO_VARS.replacementDone = true;
}

LPOPRO_CONTROL.track = function() {
  LPOPRO_CONTROL.replaceBody();

  var src = LPOPRO_VARS.js_url + 'lpopro-tracking.js?';
  var timestamp = (new Date()).getTime();

  src+='experiment_id=' + LPOPRO_VARS.experimentId + '&rnd=' + timestamp;
  if (LPOPRO_VARS.responsePoints)
    src+='&' + LPOPRO_VARS.responsePoints;
  if (LPOPRO_VARS.variations)
    src+='&' + LPOPRO_VARS.variations;
  LPOPRO_CONTROL.loadScript(src);

}

LPOPRO_CONTROL.waitForSession = function() {
  LPOPRO_CONTROL.pageDivFilter();
  var url = LPOPRO_VARS.pageUrl;
  var experimentIds = null;
  if (LPOPRO_VARS.experimentIds != "undefined") {
    experimentIds = LPOPRO_VARS.experimentIds;
  }
  var websiteId = null;
  var src = "";
  if (LPOPRO_VARS.websiteId != "undefined")
    websiteId = LPOPRO_VARS.websiteId;

  if (experimentIds != null) {
    src = LPOPRO_VARS.js_url + "lpopro-visit.js?campaign_ids=" + experimentIds + "&url="+encodeURIComponent(url);
  }
  if (websiteId != null) {
    src = LPOPRO_VARS.js_url + "lpopro-visit.js?website_id=" + websiteId + "&url="+encodeURIComponent(url);
  }
  if (LPOPRO_CONTROL.readCookie('__lpopro_combinationid')!=null) {
    src+= "&experiment_id="+LPOPRO_CONTROL.readCookie('__lpopro_combinationid');
  }
  if (src!="") {
    var lpopro_session = LPOPRO_CONTROL.loadScript(src);
    if (LPOPRO_VARS.isMSIE) {
      lpopro_session.onreadystatechange = function () {
        if (lpopro_session.readyState == 'loaded' || lpopro_session.readyState == 'complete') {
          LPOPRO_CONTROL.getExperiment();
        }
      }
    } else {
      lpopro_session.onload=function() {
        LPOPRO_CONTROL.getExperiment();
      }
    }
  } else {
    LPOPRO_CONTROL.postProcessing();
  }
}

LPOPRO_CONTROL.getElementsByClassName = function(classname, node)  {
  if(!node) node = document.getElementsByTagName("body")[0];
  var ewc = [];
  var re = new RegExp('\\b' + classname + '\\b');
  var els = node.getElementsByTagName("*");
  for(var i=0,j=els.length; i<j; i++) if(re.test(els[i].className))ewc.push(els[i]);
  return ewc;
}

LPOPRO_CONTROL.getElementsByAttribute = function(elementTag, attrName, attrValues) {
  var lpopro_els = document.getElementsByTagName(elementTag);
  var lpopro_found_elements = new Array();
  for (k = 0; k < lpopro_els.length; k++) {
    var lpopro_el = lpopro_els[k];
    var lpopro_attr = lpopro_el.getAttribute(attrName);
    if (typeof lpopro_attr == 'string') {
      ;
    } else if ((attrName == 'style') && (lpopro_attr != null) && (typeof lpopro_attr == 'object')) {
      lpopro_attr = lpopro_attr.cssText;
    }

    var lpopro_is_found = true;
    for (j = 0; j < attrValues.length; j++) {
      var attrValue = attrValues[j];

      if (attrValue != null) attrValue = attrValue.split(' ').join('');
      var re = new RegExp(attrValue, "i");

      if (lpopro_attr != null) lpopro_attr = lpopro_attr.split(' ').join('');
      if (!re.test(lpopro_attr)) {
        lpopro_is_found = false;
        break;
      }
    }

    if (lpopro_is_found == true) {
      lpopro_found_elements.push(lpopro_els[k]);
    }
  }
  return lpopro_found_elements;
}

LPOPRO_CONTROL.getCharacterEncoding = function() {
  var charSet = "";
  // list of properties to try read for the current pages encoding.
  // document.characterSet is used by FF 2/3 &amp; Safari
  // document.charset is used by IE 6/7
  // the others are for belt and braces
  var testList = ["document.characterSet",
                  "document.charset",
                  "document.defaultCharset",
                  "document.actualEncoding",
                  "document.inputEncoding"
                  ];
  for (var i=0;i<testList.length;i++) {
    try {
      charSet="notset";
      eval("charSet = "+testList[i]+";");
      if (charSet != "" && charSet != null) {
        break;
      }
    } catch(e) {
    }
  }

  return charSet;
}

LPOPRO_CONTROL.typeOf =  function(obj) {
  if (typeof(obj) == 'object')  {
    if (obj.length) {
      return 'array';
    } else {
      return 'object';
    }
  }
  return typeof(obj);
}

LPOPRO_CONTROL.pageDivFilter = function() {
  LPOPRO_VARS.pageDiv = document.createElement('div');
  LPOPRO_VARS.pageDiv.id = 'lpopro_pageDiv';
  LPOPRO_VARS.pageDiv.setAttribute('style','visibility:hidden');
  if ((LPOPRO_CONTROL.typeOf(LPOPRO_VARS.contexts) == 'array') && (LPOPRO_VARS.contexts.length > 0)) {
    var ctx;
    var contextsLength = LPOPRO_VARS.contexts.length;
    for (z = 0; z < contextsLength; z++) {
      ctx = LPOPRO_VARS.contexts[z];
      if (ctx) LPOPRO_VARS.pageDiv.innerHTML += ctx.innerHTML;
      if (z < (contextsLength - 1)) LPOPRO_VARS.pageDiv.innerHTML += LPOPRO_VARS.contextSepharator;
    }
  } else if (LPOPRO_VARS.context) {
    LPOPRO_VARS.pageDiv.innerHTML = LPOPRO_VARS.context.innerHTML;
  } else {
    LPOPRO_VARS.pageDiv.innerHTML = document.body.innerHTML;
  }

  LPOPRO_VARS.body = document.body.innerHTML;
  LPOPRO_VARS.page = LPOPRO_VARS.pageDiv.innerHTML;
}

LPOPRO_CONTROL.init = function() {
  if (LPOPRO_VARS.pageDiv==null) {
    LPOPRO_CONTROL.addLoadEvent(LPOPRO_CONTROL.waitForSession);
  }
}

LPOPRO_VARS.experimentId = LPOPRO_CONTROL.readCookie('__lpopro_combinationid');
LPOPRO_VARS.timeout = setTimeout(LPOPRO_CONTROL.errhdl, LPOPRO_VARS.timeout);

/*
 * Ensures that postprocessingCallback function is invoked after the
 * replacement is done.
 */
LPOPRO_CONTROL.callPostprocessingCallback = function() {
  if (!LPOPRO_VARS.replacementDone) {
    setTimeout(LPOPRO_CONTROL.callPostprocessingCallback, 100);
    return;
  }

  LPOPRO_CONTROL.postprocessingCallback();
}


//Set this function with whatever JS should be executed after replacements.
//The function will be invoked at the end of the post-processing stage, after
//executing the page replacement and invoking whatever function was found in
//window onLoad.
//LPOPRO_CONTROL.postprocessingCallback = function() {};
