﻿published.strEmpty = function(str) { return (!str || 0 === str.length || /^\s*$/.test(str)); };

published.strAsBool = function(str, dflt) {
  if (typeof (str) === tUNDEFINED || str === null) return ((typeof (dflt) === tUNDEFINED) ? false : dflt);
  str = str.toString();
  return published.strOneOf(str, ["true", "t", "yes", "1"]);
};

published.strDefault = function(str, dflt) {
  return typeof (str) === tUNDEFINED || str === null ? (typeof (dflt) === tUNDEFINED || dflt === null ? '' : dflt) : str.toString();
};

published.strEmptyDefault = function(str, dflt) {
  return published.strEmpty(str) ? (typeof (dflt) === tUNDEFINED || dflt === null ? '' : dflt) : str.toString();
};

published.nlsNameDefault = function(nls, dflt) {
  var v = (typeof (nls) === tUNDEFINED || nls === null) ? null : nls.n;
  return published.strEmptyDefault(v, dflt);
};

published.nlsDescrDefault = function(nls, dflt) {
  var v = (typeof (nls) === tUNDEFINED || nls === null) ? null : nls.d;
  return published.strEmptyDefault(v, dflt);
};

published.nlsNameOrDescrDefault = function(nls, dflt) {
  var v = null;
  if (typeof (nls) !== tUNDEFINED && nls !== null) {
    v = nls.n;
    if (published.strEmpty(v)) v = nls.d;
  }
  return published.strEmptyDefault(v, dflt);
};

published.nlsDescrOrNameDefault = function(nls, dflt) {
  var v = null;
  if (typeof (nls) !== tUNDEFINED && nls !== null) {
    v = nls.d;
    if (published.strEmpty(v)) v = nls.n;
  }
  return published.strEmptyDefault(v, dflt);
};


published.strTrim = function(str) { return str.replace(/^\s+|\s+$/g, ''); };
published.strLTrim = function(str) { return str.replace(/^\s+/, ''); };
published.strRTrim = function(str) { return str.replace(/\s+$/, ''); };

// Truncates str if its length exceeds maxLen and adds endWith string to result end.
published.strTrunc = function(str, maxLen, endWith) {
  if (!str) return str;
  var len = str.length;
  if (len <= maxLen) return str;
  endWith = endWith || "...";
  return str.substr(0, maxLen - endWith.length) + endWith;
};

//Capitalizes first chars after spaces or dots, otionally converting chars in between to lower case
published.strCaps = function(str, norm) {
  //this does not use Regexp because regexp does not detect words correctly for non-english languages
  if (published.strEmpty(str)) return str;
  var c;
  var result = "";
  var sp = false;
  for (var i = 0; i < str.length; i++) {
    c = str[i];
    if (c === ' ' || c === '.') {
      sp = true;
      result += c;
      continue;
    }

    if (sp || i === 0)
      result += c.toUpperCase();
    else
      result += norm ? c.toLowerCase() : c;
    sp = false;
  }
  return result;
};

published.joinPathSegs = function() {
  if (arguments.length === 0) return '';
  var s = '';
  var first = true;
  for (var i = 0; i < arguments.length; i++) {
    var seg = arguments[i];
    if (!seg) continue;

    seg = (first ? WAVE.strLTrim(seg) : seg.replace(/^[\/\\\s]+/, '')).replace(/[\/\\\s]+$/, '');
    if (WAVE.strEmpty(seg)) continue;

    if (!first) s += '/';
    s += seg;
    first = false;
  }
  return s;
};

published.mapCurrencyISOToSymbol = function(ciso, dflt) {
  ciso = published.strDefault(ciso, "");
  ciso = ciso.toUpperCase();
  switch (ciso) {
    case "CAD":
    case "USD":
    case "MXN":
      return "$";
    case "RUB":
      return "₽";
    case "EUR":
      return "€";
    case "GBP":
      return "£";
    default:
      return published.strDefault(dflt, "?");
  }
};

var intPrefixes = ["", "k", "M", "G", "T", "P", "E", "Z", "Y"];
var floatPrefixes = ["", "m", "µ", "n", "p", "f", "a", "z", "y"];

var siPrefixes = ["y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M", "G", "T", "P", "E", "Z", "Y"];

// converts num to its string representation in SI (Le Système International d’Unités, SI) with precision desired
// so 1000 = "1.00k", .1="100.00m", 23.55 = "23.55", 999.999="1.00k"
published.siNum = function(num, decimalPlaces) {

  if (typeof (decimalPlaces) === tUNDEFINED) decimalPlaces = 2;

  if (num === 0) return num.toFixed(decimalPlaces);

  var n = num;
  if (num < 0) n = -n;

  var k = 0;
  var res = n.toFixed(decimalPlaces) + siPrefixes[k + 8];

  while (n >= 1000) { n /= 1000; k++; }
  while (n < 1) { n *= 1000; k--; }

  var roundK = Math.pow(10, decimalPlaces);

  n = Math.round(n * roundK) / roundK;

  while (n >= 1000) { n /= 1000; k++; }
  while (n < 1) { n *= 1000; k--; }

  if (num < 0) n = -n;
  res = n.toFixed(decimalPlaces) + siPrefixes[k + 8];

  return res;
};

//True for [a-zA-Z0-9]
published.charIsAZLetterOrDigit = function(c) {
  if (c === null) return false;
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9');
};

published.strStartsWith = function(str, s, scase) {
  return scase ? str.slice(0, s.length) === s : str.slice(0, s.length).toLowerCase() === s.toLowerCase();
};

published.strEndsWith = function(str, s, scase) {
  return scase ? str.slice(-s.length) === s : str.slice(-s.length).toLowerCase() === s.toLowerCase();
};

// Ensures that string ends with the specified string: strEnsureEnding("path",'/')
published.strEnsureEnding = function(str, ending) {
  return str + (str.slice(-ending.length) === ending ? '' : ending);
};

//Returns true when str contains a seg optionally respecting case
published.strContains = function(str, seg, scase) {
  return scase ? str.indexOf(seg) > -1 : str.toLowerCase().indexOf(seg.toLowerCase()) > -1;
};

//Returns true if both string contain the same trimmed case-insensitive value.
//This method is usefull for tasks like searches of components by name
published.strSame = function(str1, str2) {
  if (typeof (str1) === tUNDEFINED || typeof (str2) === tUNDEFINED) return false;
  if (str1 === null || str2 === null) return false;
  if (!WAVE.isString(str1)) str1 = str1.toString();
  if (!WAVE.isString(str2)) str2 = str2.toString();
  return published.strTrim(str1).toLowerCase() === published.strTrim(str2).toLowerCase();
};

//Returns true if the case-insensitive trimmed string is in the set of values
//Neither string nor set value may contain delimiter which is '|' by default:
//   strOneOf("car",["car","house","tax"],';')
published.strOneOf = function(str1, set, del) {
  if (str1 === null || set === null || !published.isArray(set)) return false;
  if (!del) del = "|";
  str1 = del + published.strTrim(str1).toLowerCase() + del;
  var vset = (del + set.join(del) + del).toLowerCase();
  return vset.indexOf(str1) >= 0;
};

var htmlEscapes = {
  "&": "&amp;",
  "<": "&lt;",
  ">": "&gt;",
  '"': '&quot;',
  "'": '&#39;',
  "/": '&#x2F;'
};

published.strEscapeHTML = function(content) {
  return String(content).replace(/[&<>"'\/]/g, function(esc) { return htmlEscapes[esc]; });
};


//Turns content like ' <td>@name@</td> ' -> '<td> Alex &amp; Boris </td>' provided that a = 'Alex & Boris'. Data is HTML escaped
published.strHTMLTemplate = function(tpl, args) {
  return tpl.replace(/@([\-\.0-9a-zA-Z]+)@/g, function(s, key) { return published.strEscapeHTML(args[key]); });
};

//Turns content like ' {a: "@name@"} ' -> '{a: "Alex & Boris"}' provided that a = 'Alex & Boris'. Data is not HTML escaped
published.strTemplate = function(tpl, args) {
  return tpl.replace(/@([\-\.0-9a-zA-Z]+)@/g, function(s, key) { return args[key]; });
};


//Turns content like ' {a: "@name@"} ' -> '{a: "Alex"}' provided that f = function(s, k){ return "Alex"}). Data is HTML escaped
published.strHTMLTemplateFun = function(tpl, f) {
  return tpl.replace(/@([\-\.0-9a-zA-Z]+)@/g, function(s, key) {return published.strEscapeHTML(f(s, key)); });
};

//Turns content like ' {a: "@name@"} ' -> '{a: "Alex"}' provided that f = function(s, k){ return "Alex"}). Data is not HTML escaped
published.strTemplateFun = function(tpl, f) {
  return tpl.replace(/@([\-\.0-9a-zA-Z]+)@/g, f);
};

//True if str contains valid email per: a@bc.de
published.strIsEMail = function(str){
  if (published.strEmpty(str)) return false;
  var iat=str.indexOf("@");
  if (iat<1 || iat===str.length-1) return false;

  if (str.indexOf("@", iat+1)>=0) return false;//duplicate @

  var ldot=str.lastIndexOf(".");
  var pass =  (ldot>iat+2) && (ldot+2<=str.length);
  if (!pass) return false;

  var c;
  for(var i=0; i<str.length; i++){
    c = str[i];
    if (c==='.'||c==='@'||c==='-'||c==='_') continue;
    if (!published.isValidScreenNameLetterOrDigit(c)) return false;
  }

  return true;
};

published.isValidScreenNameLetter = function(c){
                  var extra = "ёЁÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂăĄąĆćĈĉĊċČčĎďĐđĒēĔĕĖėĘęĚěĜĝĞğĠġĢģĤĥ";
                  return ((c>='A' && c<='Z') ||
                          (c>='a' && c<='z') ||
                          (c>='А' && c<='Я') ||
                          (c>='а' && c<='я') ||
                          (extra.indexOf(c)>=0));
};

published.isValidScreenNameLetterOrDigit = function(c){ return published.isValidScreenNameLetter(c) || (c>='0' && c<='9'); };

published.isValidScreenNameSeparator= function(c){   return (c==='.' || c==='-' || c==='_'); };

//True if str contains valid screen name/ID
published.strIsScreenName = function(name){
  if (published.strEmpty(name)) return false;
  name = published.strTrim(name);
  if (name.length===0) return false;
  var wasSeparator = false;
  var c;
  for(var i=0; i<name.length; i++)
  {
    c = name[i];
    if (i===0)
    {
      if (!published.isValidScreenNameLetter(c)) return false;
      continue;
    }

    if (published.isValidScreenNameSeparator(c))
    {
      if (wasSeparator) return false;
      wasSeparator = true;
      continue;
    }
    if (!published.isValidScreenNameLetterOrDigit(c)) return false;
    wasSeparator = false;
  }
  return !wasSeparator;
};

// Normalizes US phone string so it looks like (999) 999-9999x9999.
published.strNormalizeUSPhone = function(val){
  if (published.strEmpty(val)) return "";
  val = published.strTrim(val);
  if (val.length===0) return "";

  if (published.strStartsWith(val, "+", true)) return val; //international phone, just return as-is

  var isArea = false;
  var isExt = false;
  var area = "";
  var number = "";
  var ext = "";

  var chr;
  for (var i = 0; i < val.length; i++)
  {
    chr = val[i];

    if (!isArea && chr === '(' && area.length === 0)
    {
      isArea = true;
      continue;
    }

    if (isArea && chr === ')')
    {
      isArea = false;
      continue;
    }

    if (isArea && area.length === 3)
      isArea = false;


      if (number.length > 0 && !isExt)
      { //check extention
        if (chr === 'x' || chr === 'X' || (chr === '.' && number.length>6))
        {
          isExt = true;
          continue;
        }
        var trailer = val.substring(i).toUpperCase();

        if (published.strStartsWith(trailer,"EXT", false) && number.length >= 7)
        {
          isExt = true;
          i += 2;
          continue;
        }
        if (published.strStartsWith(trailer,"EXT.", false) && number.length >= 7)
        {
          isExt = true;
          i += 3;
          continue;
        }
    }

    if (!published.charIsAZLetterOrDigit(chr)) continue;

    if (isArea) area += chr;
    else{
      if (isExt)
        ext += chr;
      else
        number += chr;
    }
  }//for

  while (number.length < 7)  number += '?';

  if (area.length === 0){
    if (number.length >= 10)
    {
      area = number.substring(0, 3);
      number = number.substring(3);
    }
    else
      area = "???";
  }

  if (number.length > 7 && ext.length === 0){
    ext = number.substring(7);
    number = number.substring(0, 7);
  }

  number = number.substring(0, 3) + "-" + number.substring(3);

  if (ext.length > 0) ext = "x" + ext;

  return "("+area+") " + number + ext;
};

published.DATE_TIME_FORMATS = {
  LONG_DATE: "LongDate",
  SHORT_DATE: "ShortDate",
  LONG_DATE_TIME: "LongDateTime",
  SHORT_DATE_TIME: "ShortDateTime",
  TRAN_DATE_TIME: "TranDateTime",
  SHORT_DAY_MONTH: "ShortDayMonth",
  SHORT_TIME: "ShortTime",
  HOURS: "Hours",
};

var monthNames = ["January",
                  "February",
                  "March",
                  "April",
                  "May",
                  "June",
                  "July",
                  "August",
                  "September",
                  "October",
                  "November",
                  "December"];

var dayNames = [ 'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];

published.dateTimeToString = function(date, format, langIso) {
  if (typeof(date) === tUNDEFINED || date === null) return "";

  function addLeadingZero(num) {
    return ('0' + num.toString()).slice(-2);
  }

  function getMN(idx) {
    return published.strLocalize(langIso, "date", "", monthNames[idx]);
  }

  function gM() { return addLeadingZero(fDate.getMonth() + 1); }
  function gD() { return addLeadingZero(fDate.getDate()); }
  function gY() { return fDate.getFullYear(); }
  function gH() { return addLeadingZero(fDate.getHours()); }
  function gMin() { return addLeadingZero(fDate.getMinutes()); }
  function gS() { return addLeadingZero(fDate.getSeconds()); }

  var fFormat = WAVE.strDefault(format, published.DATE_TIME_FORMATS.LONG_DATE);
  var fDate = WAVE.isString(date) ? new Date(date) : date;
  switch (fFormat)
  {
    case published.DATE_TIME_FORMATS.LONG_DATE:
      return getMN(fDate.getMonth()) + " " + gD() + ", " + gY();
    case published.DATE_TIME_FORMATS.SHORT_DATE:
      return gM() + "/" + gD() + "/" + gY();
    case published.DATE_TIME_FORMATS.LONG_DATE_TIME:
      return getMN(fDate.getMonth()) + " " + gD() + ", " + gY() + " " + gH() + ":" + gMin() + ":" + gS();
    case published.DATE_TIME_FORMATS.SHORT_DATE_TIME:
      return gM() + "/" + gD() + "/" + gY() + " " + gH() + ":" + gMin();
    case published.DATE_TIME_FORMATS.TRAN_DATE_TIME:
      return gM() + "/" + gD() + "/" + gY() + " " + gH() + ":" + gMin() + ":" + gS();
    case published.DATE_TIME_FORMATS.SHORT_DAY_MONTH:
      return gM() + "/" + gD();
    case published.DATE_TIME_FORMATS.SHORT_TIME:
      return gH() + ":" + gMin();
    case published.DATE_TIME_FORMATS.HOURS:
      return gH();
  }
};

// Add toISOString support (i.e. "2012-01-01T12:30:15.120Z")
published.toISODateTimeString = function(dt){
  function pad(n) { return n < 10 ? '0' + n : n; }
  return dt.getUTCFullYear() + '-' +
        pad(dt.getUTCMonth()+1)+ '-' +
        pad(dt.getUTCDate())+ 'T' +
        pad(dt.getUTCHours())+ ':' +
        pad(dt.getUTCMinutes())+ ':' +
        pad(dt.getUTCSeconds())+ 'Z';
};

// Add MM/DD/YYYY HH:MM:SS
published.toUSDateTimeString = function(dt){
  function pad(n) { return n < 10 ? '0' + n : n; }

  return pad(dt.getMonth()+1)+"/"+
        pad(dt.getDate())+"/"+
        pad(dt.getFullYear())+" "+
        pad(dt.getHours())+':'+
        pad(dt.getMinutes())+':'+
        pad(dt.getSeconds());
};

// Add MM/DD/YYYY
published.toUSDateString = function(dt){
  function pad(n) { return n < 10 ? '0' + n : n; }

  return pad(dt.getMonth()+1)+"/"+
        pad(dt.getDate())+"/"+
        pad(dt.getFullYear());
};

// Parses duration string to total seconds: duration("1d 10h 7m 13s")
published.toSeconds = function(s) {
  var result  = 0,
    days    = s.match(/(\d+)\s*d/),
    hours   = s.match(/(\d+)\s*h/),
    minutes = s.match(/(\d+)\s*m/),
    seconds = s.match(/(\d+)\s*s/);
  if (days)    result += parseInt(days[1])*86400;
  if (hours)   result += parseInt(hours[1])*3600;
  if (minutes) result += parseInt(minutes[1])*60;
  if (seconds) result += parseInt(seconds[1]);
  return result;
};