// Copyright 2005 Google Inc.
// All rights reserved.

/**
 * Helper Functions used by the various CreateAd
 * javascript files.
 */

/** Namespace */
function awCreateAdUtil() {
}

/** Clears the text field with the given id */
awCreateAdUtil.clearField = function(name) {
  var f = document.getElementById(name);
  if (f) {
    f.value = "";
  }
}

/** Clears checkbox with given the given id */
awCreateAdUtil.clearCheckbox = function(name) {
  var c = document.getElementById(name);
  if(c){
    c.checked = false;
  }
}

/**
 * Clears each creative field if it has the default value in it.
 * This is called when the user clicks on each field.
 */
awCreateAdUtil.clearInputIfDefault = function(srcId, valueId) {
  var src = document.getElementById(srcId);
  var defaultValue = awCreateAdUtil.getMessage(valueId);

  if (src && defaultValue && src.value == defaultValue) {
    awCreateAdUtil.clearField(srcId);
  }
}

/**
 * Places the default values in each creative field if it is empty.
 */
awCreateAdUtil.placeDefaultIfClear = function(srcId, valueId) {
  var src = document.getElementById(srcId);
  var defaultValue = awCreateAdUtil.getMessage(valueId);

  if (src && defaultValue && src.value == "") {
    src.value = defaultValue;
  }
};

awCreateAdUtil.isDefaultOrClear = function(srcId, valueId) {
  var src = document.getElementById(srcId);
  var defaultValue = awCreateAdUtil.getMessage(valueId);
  return (src && (src.value == "" ||
                  (defaultValue && src.value == defaultValue)));
};

/**
 * Handler for keypress events in the Creative text fields.
 * This isn't working in IE for some reason, so for now this function is
 * only being used at page load time.
 *
 * @param srcId           the ID of the text field that was modified
 * @param destId          the ID of the &lt;span&gt; where the text should be
 *                        copied.
 * @param maxLenId        the ID of the &lt;span&gt; containing the max length
 * @param defaultTextId   the ID of the &lt;span&gt; containing the text to
 *                        copy to destId,if source is not available. Only set
 *                        on page load.  Ignored if null.
 */
awCreateAdUtil.textChanged = function(srcId, destId, maxLenId, defaultTextId) {
  var field = document.getElementById(srcId);
  if (!field) return;

  var maxLen = parseInt(awCreateAdUtil.getMessage(maxLenId), 10);
  if (!maxLen) return;

  awCreateAdUtil.enforceFieldLimit(field, maxLen, false);

  var value = field.value;
  if (value.length == 0) {
    if (defaultTextId && document.getElementById(defaultTextId)) {
      value = awCreateAdUtil.getMessage(defaultTextId);
    }
  }
  awCreateAdUtil.setText(destId, value);
  awCreateAdUtil.setText("long" + destId, value);
}

/** Copy the 'value' property from one element to another */
awCreateAdUtil.copyValue = function(destination, source) {
  if (document.getElementById) {
    if (document.getElementById(destination)
        && document.getElementById(source)) {
      if ((document.getElementById(destination).value == '')
          && (document.getElementById(source).value != '')) {
        document.getElementById(destination).value
            = document.getElementById(source).value;
      }
    }
  }
}

/** Copy the 'value' property from one element to another
 *  The maximum length to be copied is specified by the element with maxLenId id
 */
awCreateAdUtil.copyValueWithMaxLen = function(destination, source, maxLenId) {
  if (!document.getElementById) return;

  var maxLen = parseInt(awCreateAdUtil.getMessage(maxLenId), 10);
  if (!maxLen) return;

  var dest = document.getElementById(destination);
  var src = document.getElementById(source);

  if (dest.value == '' && src.value != '') {
    var copy = src.value;
    if (copy.length > maxLen) copy = copy.substr(0, maxLen);
    dest.value = copy;
  }
}

/**
 * Sets the text on a form field, iff the field exists in the document
 * Null or undefined text will cause the field to be set to the empty string
 */
awCreateAdUtil.setText = function(fieldId, text) {
  var field = document.getElementById(fieldId);
  if (field) {
    if (text) {
      // don't want to put html tags inside the field.
      // copied from google3/javascript/closure/util/string.js
      text = text.replace(/&/g, '&amp;')
                 .replace(/</g, '&lt;')
                 .replace(/>/g, '&gt;')
                 .replace(/\"/g, '&quot;');
    }

    field.innerHTML = (text ? text : "");
  }
}

/**
 * Sets the href attribute of a anchor field, iff the field exists in the
 * document.
 * Null of undefined text will cause the attribute to be set to empty string
 */
awCreateAdUtil.setHref = function(fieldId, href) {
  var field = document.getElementById(fieldId);
  if (field) {
    field.href = (href ? href : "");
  }
}

/**
 * Changes the url with hrefToChange as its id to
 * protocolId.value + urlId.value. If either the protocolId or urlId values
 * are empty then the corresponding values of the example messages
 * exampleProtocolId and exampleUrlId are used.
 *
 */
awCreateAdUtil.urlChanged = function(hrefToChange,
                                     protocolId, exampleProtocolId,
                                     urlId, exampleUrlId) {
  var protocolField = document.getElementById(protocolId);
  if (!protocolField) return;
  var protocol = (protocolField.value.length > 0) ? protocolField.value
      : awCreateAdUtil.trim(awCreateAdUtil.getMessage(exampleProtocolId));

  var urlField = document.getElementById(urlId);
  if (!urlField) return;
  var url = (urlField.value.length > 0) ? urlField.value
      : awCreateAdUtil.trim(awCreateAdUtil.getMessage(exampleUrlId));

  awCreateAdUtil.setHref(hrefToChange, protocol + url);
}

/**
 * Selects the correct protocol field options based on the url the users types
 * into the destination url.
 */
awCreateAdUtil.pickCorrectProtocol = function(protocolId, urlId) {
  var urlField = document.getElementById(urlId);
  var protocolField = document.getElementById(urlId);

  var HTTP = "http://";
  var HTTPS = "https://";

  if (!urlField || !protocolField) {
    return;
  }

  if (urlField.value.indexOf(HTTP) == 0) {
    awCreateAdUtil.selectOptionWithValue(protocolId, HTTP)
    if (urlField.value.length > HTTP.length) {
      urlField.value = urlField.value.substring(HTTP.length);
    }
    return;
  }

  if (urlField.value.indexOf(HTTPS) == 0) {
    awCreateAdUtil.selectOptionWithValue(protocolId, HTTPS)
    if (urlField.value.length > HTTPS.length) {
      urlField.value = urlField.value.substring(HTTPS.length);
    }
    return;
  }
}

/**
 * Selects the option from the input:select specified by selectId whose value
 * matches optionValue.
 */
awCreateAdUtil.selectOptionWithValue = function(selectId, optionValue) {

  var selectField = document.getElementById(selectId);

  if (!selectField) {
    return;
  }

  var selectOptions = selectField.options;

  for (var i = 0; i < selectOptions.length; i++) {
    if (selectOptions[i].value.indexOf(optionValue) == 0) {
      selectOptions.selectedIndex = i;
      return;
    }
  }
}

awCreateAdUtil.disableLink = function(href) {
  var hrefField = document.getElementById(href);
  if (!hrefField) return;
  hrefField.href = "";
  hrefField.onclick = awCreateAdUtil.cancelLink;
}

awCreateAdUtil.enableLink = function(href) {
  var hrefField = document.getElementById(href);
  if (!hrefField) return;

  if (hrefField.onclick == awCreateAdUtil.cancelLink) {
    hrefField.onclick = "";
  }
}

awCreateAdUtil.cancelLink = function() {
  return false;
}

// TODO(veeraj) possibly move this function to Common.js as this exists in
// both wizard/javascript/Shared.js and here
awCreateAdUtil.getMessage = function(msgId) {
  // Note that we are not defensive here on purpose. Callers should
  // see an error if they call with a bum msgId, and be grateful for it!
  return document.getElementById(msgId).firstChild.data;
}

/**
 * Sets the message value for a given msg id
 */
awCreateAdUtil.setMessage = function(msgId, msgTxt) {
  // Note that we are not defensive here on purpose. Callers should
  // see an error if they call with a bum msgId, and be grateful for it!
  document.getElementById(msgId).innerHTML = msgTxt;
}

/**
 * Returns the number of extra characters due to dynamic text insertion syntax
 * in the given field.
 *
 * @param field the field to count for
 */
awCreateAdUtil.limitExtensionForDynamicText = function(field) {
  var templateTokens = new Array("keyword:", "param1:", "param2:");
  var extendBy = 0;
  for (var i = 0; i < templateTokens.length; i++) {
    var templateBase = "{" + templateTokens[i] + "}";
    var templateRegExp = new RegExp(
        "\\{" + templateTokens[i] + "[^\\}]+\\}", "i");
    var results = templateRegExp.exec(field.value);
    if (results) {
      extendBy += templateBase.length;
    }
  }

  var results = field.value.match(/\{keyword\}/);
  if (results) {
    extendBy += "{keyword}".length - 1;
  }

  return extendBy;
}

/**
 * This function will enforce the limit by dynamically trimming
 * characters in the field that exceed maxlimit.
 *
 * This approach is used instead of the HTML "maxlength" attribute because of
 * Dynamic Text Insertion (e.g. Creative Params). In such cases, we must allow
 * users to break the maxlength and enter the correct syntax. In such cases, the
 * text turns red until the user has finished typing legal syntax, and then the
 * text turns black again.
 *
 * Mostly copied from the textCounter() function in CreativeEditJavascript.gxp,
 * and given a better name.

 * @param field       field object to count
 * @param maxlimit    maximum length to enforce
 * @param ignoreDoubleByte true to count a double-byte unicode character
 *        as 1 display character instead of 2
 */
awCreateAdUtil.enforceFieldLimit = function(field, maxlimit, ignoreDoubleByte) {
  maxlimit += awCreateAdUtil.limitExtensionForDynamicText(field);
  if (ignoreDoubleByte) {
    if (field.value.indexOf("{") == -1) {
      if (field.value.length > maxlimit) {
        awCreateAdUtil.truncateAtCaret(field, maxlimit);
      }
      field.style.color = 'black';
    } else {
      if (field.value.length > maxlimit) {
        field.style.color = 'red';
      }
      else {
        field.style.color = 'black';
      }
    }
  } else {
    if (field.value.indexOf("{") == -1 && !awCreateAdUtil.stringHasCjk(field.value)) {
      if (awCreateAdUtil.displayLength(field) > maxlimit) {
        var overLengthIndex = awCreateAdUtil.overLengthIndex(field, maxlimit);
        awCreateAdUtil.truncateAtCaret(field, overLengthIndex);
      }
      field.style.color = 'black';
    } else {
      if (awCreateAdUtil.displayLength(field) > maxlimit) {
        field.style.color = 'red';
      }
      else {
        field.style.color = 'black';
      }
    }
  }
}

/**
 * Check if an input string contains and CJK charaters
 *
 * @param string  input string to test
 */
awCreateAdUtil.stringHasCjk = function(string) {
  for (var i = 0; i < string.length; i++) {
    if (awCreateAdUtil.charIsCjk(string.charAt(i))) {
      return true;
    }
  }
}


/**
 * This function is mostly copied from the GuessCJKLanguageFromUTF8Text method
 * in //depot/google3/i18n/langenc_detect/guess-cjk-lang.cc
 * See also:
 * http://www.unicode.org/Public/UNIDATA/Blocks.txt
 * http://www.unicode.org/charts/
 *
 * @param ch     charater to test
 */
awCreateAdUtil.charIsCjk = function(ch) {
  if (ch < '\u1100') {
         return false;
  }

  if (awCreateAdUtil.isBetween(ch, '\u4E00', '\u9FFF')       // CJK Unified Ideographs
      || awCreateAdUtil.isBetween(ch, '\uFA0C', '\uFAFF')    // CJK Compatibility Ideos
   	                                                         // (excluding Korean specific ones)
      || awCreateAdUtil.isBetween(ch, '\u3400', '\u4DBF')    // CJK Unified Ideographs Ext A
      || awCreateAdUtil.isBetween(ch,'\u20000', '\u2A6DF')   // CJK Unified Ideographs Ext B
      || awCreateAdUtil.isBetween(ch, '\u2F800', '\u2FA1F')  // CJK Compat. Ideog. Supplement
      || awCreateAdUtil.isBetween(ch, '\u3040', '\u30FF')    // 0x3040u-0x309Fu: Hiragana
                                                             // 0x30A0u-0x30FFu: Katakana
      || awCreateAdUtil.isBetween(ch, '\uFF65', '\uFF9F')    // Half width Katakana variants
      || awCreateAdUtil.isBetween(ch, '\u31F0', '\u31FF')    // Katakana Phonetic Extensions
      || awCreateAdUtil.isBetween(ch, '\uAC00', '\uD7A3')    // Hangul Syllables
      || awCreateAdUtil.isBetween(ch, '\u3130', '\u318F')    // Hangul Compatibility Jamos
      || awCreateAdUtil.isBetween(ch, '\uF900', '\uFA0B')    // Compat-Han (specific to Korean)
      || awCreateAdUtil.isBetween(ch, '\u1100', '\u11FF')    // Hangul Jamos
      || awCreateAdUtil.isBetween(ch, '\uFFA0', '\uFFDC')) {  // Do we want to keep halfwidth Hangul
                                                             // Jamos?
    return true;
  }
  //return false;
}

/**
 * Return true if min <= x <= max
 *
 * @param  x   value to test
 * @param min  lower bound
 * @param max  upper bound
 */
awCreateAdUtil.isBetween = function(x, min, max) {
  return ((x >= min) && (x <= max));
}

/**
 * Tries to remove (field.value.length - maxlength) characters before
 * the caret.  If we can't find the caret or we need to remove too many
 * characters, we take them from the end of the field.
 */
awCreateAdUtil.truncateAtCaret = function(field, maxlength) {
  var caret = awCreateAdUtil.getCaretPosition(field);
  var numCharsToTruncate = field.value.length - maxlength;
  if ((numCharsToTruncate > caret) || (caret == field.value.length)) {
    field.value = field.value.substring(0, maxlength);
  } else {
    field.value = field.value.substring(0, caret - numCharsToTruncate)
        + field.value.substring(caret, field.value.length);
    awCreateAdUtil.setCaretPosition(field, caret - numCharsToTruncate);
  }
}

/**
 * Tries to find the index of the caret position.  Failing, it returns -1.
 */
awCreateAdUtil.getCaretPosition = function(field) {
  var pos = 0;

  if (field.selectionStart) {  // Gecko
    pos = field.selectionStart;
  } else if (field.createTextRange) { // IE
    var range = document.selection.createRange().duplicate();
    if (range.parentElement() != field) {
      pos = -1;
    } else {
      // there's no concept of a carat position in IE, so fake it by looking
      // at the length of the range from the start to the current caret position
      range.moveStart('character', -field.value.length);
      pos = range.text.length;
    }
  }

  return pos;
}

/**
 * Tries to set the caret position to pos.
 */
awCreateAdUtil.setCaretPosition = function(field, pos) {
  if (field.selectionStart) {  // Gecko
    // just setting selectionStart and selectionEnd doesn't work in Opera
    field.setSelectionRange(pos, pos);
  } else if (field.createTextRange) { // IE
    var range = document.selection.createRange();
    range.move('character', -field.value.length);
    range.move('character', pos);
    range.select();
  }
}

/** Returns the point where the display length of the input field exceeds the
 *  length limit.
 */
awCreateAdUtil.overLengthIndex = function(field, maxlimit) {
  var count = 0;
  for (var i = 0; i < field.value.length; i++) {
    count += awCreateAdUtil.displayWidth(field.value.charAt(i));
    if (count > maxlimit) {
      return i;
    }
  }
  return field.value.length;
}

/** Returns the display length of the input field */
awCreateAdUtil.displayLength = function(field) {
  var count = 0;
  for (var i = 0; i < field.value.length; i++) {
    count += awCreateAdUtil.displayWidth(field.value.charAt(i));
  }
  return count;
}

/** Returns the display width of a Unicode character.
 *  We follow the same logic as in java's StringUtil.displayWidth()
 *  Specifically:
 *  returns the approximate display width of the character, measured
 *  in units of ascii characters.
 *
 *  This method should err on the side of caution. By default, characters
 *  are assumed to have width 2; this covers CJK ideographs, various
 *  symbols and miscellaneous weird scripts. Given below are some Unicode
 *  ranges for which it seems safe to assume that no character is
 *  substantially wider than an ascii character:
 *   - Latin, extended Latin, even more extended Latin.
 *   - Greek, extended Greek, Cyrillic.
 *   - Some symbols (including currency symbols) and punctuation.
 *   - Half-width Katakana and Hangul.
 *   - Hebrew
 *   - Arabic
 *   - Thai
 *  Characters in these ranges are given a width of 1.
 *
 * IMPORTANT: this function has analogs in C++ (encodingutils.cc,
 * named UnicodeCharWidth) and Java
 * (com.google.common.base.StringUtil.displayWidth()),
 * which need to be updated if you change the implementation here.
 */
awCreateAdUtil.displayWidth = function(ch) {
  if (ch <= '\u04f9' ||   // CYRILLIC SMALL LETTER YERU WITH DIAERESIS
      ch == '\u05be' ||   // HEBREW PUNCTUATION MAQAF
      (ch >= '\u05d0' && ch <= '\u05ea') ||  // HEBREW LETTER ALEF ... TAV
      ch == '\u05F3' ||   // HEBREW PUNCTUATION GERESH
      ch == '\u05f4' ||   // HEBREW PUNCTUATION GERSHAYIM
      (ch >= '\u0600' && ch <= '\u06ff') || // Block=Arabic
      (ch >= '\u0750' && ch <= '\u077f') || // Block=Arabic_Supplement
      (ch >= '\ufb50' && ch <= '\ufdff') || // Block=Arabic_Presentation_Forms-A
      (ch >= '\ufe70' && ch <= '\ufeff') || // Block=Arabic_Presentation_Forms-B
      (ch >= '\u1e00' && ch <= '\u20af') || // LATIN CAPITAL LETTER A WITH RING BELOW ... DRACHMA SIGN
      (ch >= '\u2100' && ch <= '\u213a') || // ACCOUNT OF ... ROTATED CAPITAL Q
      (ch >= '\u0e00' && ch <= '\u0e7f') || // Thai
      (ch >= '\uff61' && ch <= '\uffdc')) { // HALFWIDTH IDEOGRAPHIC FULL STOP ... HALFWIDTH HANGUL LETTER I
    return 1;
  }
  return 2;
}

/**
 * Returns only the domain portion of the srcId field value.
 */
awCreateAdUtil.chopToDomain = function(srcId, maxLenId) {
  return awCreateAdUtil.chopToDomainAndLength(srcId,
      parseInt(awCreateAdUtil.getMessage(maxLenId), 10));
}

/**
 * Pass in the ID of the source field, and the actual int value of the max
 * length of the chopped string.
 */
awCreateAdUtil.chopToDomainAndLength = function(srcId, maxLength) {
  var srcValue = awCreateAdUtil.trim(document.getElementById(srcId).value);
  var protocolLength = awCreateAdUtil.getLengthOfProtocolInString(srcValue);

  // If it's only http:/, http://, https:/, or https:// return the full value
  if (srcValue.length == protocolLength) {
    return srcValue;
  }

  // If the value starts with http:/, http://, https:/ or https://
  // and continues, then chop off the protocol.
  var choppedValue = srcValue.substring(protocolLength);

  // lop off everything after the "first" /
  var firstSlashIndex = choppedValue.indexOf("/");
  if (firstSlashIndex != -1) {
    choppedValue = choppedValue.substring(0, firstSlashIndex);
  }

  // length restriction
  if (maxLength) {
    if (choppedValue.length > maxLength) {
      choppedValue = choppedValue.substring(0, maxLength)
    }
  }

  return choppedValue;
}

awCreateAdUtil.getLengthOfProtocolInString = function(srcValue) {
  var HTTP_START = "http:/";
  var HTTP = "http://";
  var HTTPS_START = "https:/";
  var HTTPS = "https://";
  var httpIndex = srcValue.indexOf(HTTP);
  var httpStartIndex = srcValue.indexOf(HTTP_START);
  var httpsIndex = srcValue.indexOf(HTTPS);
  var httpsStartIndex = srcValue.indexOf(HTTPS_START);
  var protocolLength = 0;

  if (httpIndex == 0) {
    protocolLength = HTTP.length;
  } else if (httpStartIndex == 0) {
    protocolLength = HTTP_START.length;
  } else if (httpsIndex == 0) {
    protocolLength = HTTPS.length;
  } else if (httpsStartIndex == 0) {
    protocolLength = HTTPS_START.length;
  }

  return protocolLength;
}


/**
* Click handler for the different Create Ad links (Text, Image, ...) Change the
* form action to the link's href value (CreateImageAd, CreateAd, CreateGeo...),
* and submit the form. This way the common fields the user has already entered
* (urlrelated stuff, and important hidden fields like wizard type) will make it
* to the form that is being navigated too.
*/
awCreateAdUtil.toggleAdTypeOnClick = function(href) {
  var f = awCreateAdUtil.getMainForm();
  f.action = href;
  f.submit();
  return false;
  // Defeat the click, let the form choose where we land
}

/**
 * Returns a form that is specified by a hidden field on the page with an
 * id of mainSubmitForm
 */
awCreateAdUtil.getMainForm = function() {
  var mainFormName = awCreateAdUtil.getMessage("mainSubmitForm");
  return document.getElementById(mainFormName);
}

/**
 * Sets the onkeydown, onkeyup, and onblur of an element to the specified
 * function.  If the element does not exist, this method does nothing.
 */
awCreateAdUtil.setTextFieldChangeListener = function(elementId, func) {
  var elm = document.getElementById(elementId);
  if (elm) {
    elm.onkeydown = elm.onkeyup = elm.onblur = func;
  }
}

/**
 * If everyone was compiling their javascript, we could use closure's trim 
 * function here instead...
 */
awCreateAdUtil.trim = function(str) {
  if (!str) return "";
  return str.replace(/^\s+/, "").replace(/\s+$/, "");
}
