function IsDigit(c)
{
  return IsNumber(c);
}

function IsNumber(c)
{
  return (c!=' ') && ((c % 1)==0);
}

function DaysInMonth(Year, Month)
{
  switch(Month)
  {
    case 1:
      return 31;
    case 2:
      if(LeapYear(Year))
        return 29;
      else
        return 28;
    case 3:
      return 31;
    case 4:
      return 30;
    case 5:
      return 31;
    case 6:
      return 30;
    case 7:
      return 31;
    case 8:
      return 31;
    case 9:
      return 30;
    case 10:
      return 31;
    case 11:
      return 30;
    case 12:
      return 31;
  }
}

function LeapYear(Year)
{
  return (Year % 4 == 0) && ((Year % 400 == 0) || (Year % 100 != 0));
}

function StringStartsWith(Str, Start)
{
  return Str.substr(0, Start.length)==Start;
}

function StringEndsWith(Str, Finish)
{
  return Str.substr(Str.length-Finish.length)==Finish;
}

function HasLetters(Str)
{
  var l = Str.length;
  for(var i = 0; i<l; i++)
    if(IsLetter(Str.substr(i, 1)))
      return true;
  return false;
}

function HasDigits(Str)
{
  var l = Str.length;
  for(var i = 0; i<l; i++)
    if(IsDigit(Str.substr(i, 1)))
      return true;
  return false;
}

function CompressCanadianPostalCode(CanadianPostalCode)
{
  CanadianPostalCode = CanadianPostalCode.toUpperCase();
  var Result = '';
  for(var i = 0; i<CanadianPostalCode.length; i++)
  {
    if(IsAlphaNumeric(CanadianPostalCode.substr(i, 1)))
      Result += CanadianPostalCode.substr(i, 1);
  }
  return Result;
}

function IsLetter(c)
{
  if(c.length>1)
    return false;
  var r = /[a-zA-Z]/;
  var Result = r.test(c);
  return Result;
}

function IsAlphaNumeric(Str, AllowSpecialCharacters)
{
  var Allowed = 'ABCDEFGHIJKLMNOPRQSTUVWXYZ1234567890';
  if(AllowSpecialCharacters==true)
    Allowed += '_-.';
  else if(AllowSpecialCharacters>'')
    Allowed += AllowSpecialCharacters;
  for(i = 0; i<Str.length; i++)
    if(Allowed.indexOf(Str.substr(i, 1).toUpperCase())==-1)
      return false;
  return true;
}

function FloatString(f, digits, separateTriads)
{
  if(!digits)
    f = parseInt(f);
  var s = new String(f);
  if(s=='NaN')
    return '0';
  var p = s.indexOf('.');
  if(p==-1)
    p = s.indexOf(',');
  if(separateTriads)
  {
    var i;
    if(p==-1)
      i = s.length;
    else
      i = p;
    i -= 3;
    while(i>0)
    {
      s = s.substr(0, i)+','+s.substr(i);
      i -= 3;
      if(p>-1)
        p++;
    }
  }
  if(digits>0)
  {
    if(p==-1)
    {
      s = s+'.';
      p = s.length-1;
    }
    else
      s = s.substr(0, p+digits+1);
    var zerosToAdd = digits-(s.length-p-1);
    for(i = 0; i<zerosToAdd; i++)
      s = s+'0';
  }
  return s;
}

function SeparateTriads(s)
{
  s = ''+s;
  var i = s.indexOf('.');
  if(i==-1)
    i = s.length;
  if(s.substr(0, 1)=='-')
    l = 1;
  else
    l = 0;
  while(i>l)
  {
    i -= 3;
    if(i>l)
      s = s.substr(0, i)+','+s.substr(i, s.length);
  }
  return s;
}

function PasswordSecurityScore(Password)
{
  var HasDigits = false;
  var HasUpperCaseLetters = false;
  var HasLowerCaseLetters = false;
  var HasSpecialCharacters = false;
  l = Password.length;
  for(var i = 0; i<l; i++)
  {
    c = Password.substr(i, 1);
    if(IsDigit(c))
      HasDigits = true;
    else if(IsLetter(c))
      if(c.toUpperCase()==c)
        HasUpperCaseLetters = true;
      else
        HasLowerCaseLetters = true;
    else if(IsSpecialCharacter(c))
      HasSpecialCharacters = true;
  }
  var TotalScore = 0;
  if(HasDigits)
    TotalScore += 25;
  if(HasUpperCaseLetters)
    TotalScore += 25;
  if(HasLowerCaseLetters)
    TotalScore += 25;
  if(HasSpecialCharacters)
    TotalScore += 25;
  return TotalScore;
}

function urlencode(str)
{
  return escape(str).replace(/\+/g,'%2B').replace(/%20/g, '+').replace(/\*/g, '%2A').replace(/\//g, '%2F').replace(/@/g, '%40');
}

function CutQueryStringParam(URL, ParamName)
{
  var Start, Finish;
  Start = URL.indexOf('&'+ParamName);
  if(Start==-1)
    Start = URL.indexOf('?'+ParamName);
  if(Start==-1)
    Start = URL.indexOf('#'+ParamName);
  if(Start>0)
  {
    var c;
    Finish = 0;
    for(var i = Start+ParamName.length+1; i<URL.length; i++)
    {
      c = URL.substr(i, 1);
      if(c=='&' || c=='#')
      {
        Finish = i;
        break;
      }
    }
    if(Finish==0)
      return URL.substr(0, Start);
    else
    {
      c = URL.substr(Start, 1);
      if(c=='?' || c=='#')
        d = 1;
      else
        d = 0;
      return URL.substr(0, Start+d)+URL.substr(Finish+d);
    }
  }
  else
    return URL;
}

function ConcatURL(URL, AdditionalParams)
{
  if(AdditionalParams>'')
  {
    var Params = AdditionalParams.split('&'), ParamName, ParamsAndHash;
    for(var i = 0; i<Params.length; i++)
    {
      ParamName = Params[i].split('=')[0];
      URL = CutQueryStringParam(URL, ParamName);
    }
    if(URL>'')
    {
      ParamsAndHash = URL.split('#');
      if(ParamsAndHash[0].indexOf('?')>0)
        ParamsAndHash[0] += '&'+AdditionalParams;
      else
        ParamsAndHash[0] += '?'+AdditionalParams;
    }
    else
      ParamsAndHash[0] = '#'+AdditionalParams;
    var Result = ParamsAndHash[0];
    if(ParamsAndHash.length>1)
      Result += '#'+ParamsAndHash[1];
    return Result;
  }
  else
    return URL;
}

function SetHashParam(ParamName, Value, DefaultValue)
{
  var HashParams = window.location.hash.substr(1).split('&');
  var HashString = '';
  var aHashParam;
  var s;
  if(Value==null)
    Value = '';
  if(Value!=DefaultValue)
    HashString = '#'+ParamName+'='+Value;
  for(var i = 0; i<HashParams.length; i++)
  {
    s = HashParams[i];
    aHashParam = s.split('=');
    if(aHashParam.length==2)
      if(aHashParam[0]==ParamName)
        continue;
    if(s>'' && s!='0')
    {
      if(HashString=='')
        HashString = '#';
      else
        HashString += '&';
      HashString += s;
    }
  }
  if(HashString=='')
    window.location.hash = '0';
  else
    window.location.hash = HashString;
}

function GetHashParam(ParamName)
{
  var HashParams = window.location.hash.substr(1).split('&');
  var aHashParam;
  for(var i = 0; i<HashParams.length; i++)
  {
    aHashParam = HashParams[i].split('=');
    if(aHashParam.length==2)
      if(aHashParam[0]==ParamName)
        return aHashParam[1];
  }
  return '';
}

function SetTextWithEllipsis(Control, s, MaxWidth)
{
  Control.text(s);
  if(Control.outerWidth()>MaxWidth)
  {
    var ss = s+'...';
    while(ss.length>3)
    {
      ss = ss.substr(0, ss.length-4)+'...';
      Control.text(ss);
      if(Control.outerWidth()<=MaxWidth)
        break;
    }
  }
}

function GetAjaxURL(Suffix)
{
  var Result = window.location.href;
  if(Result.indexOf('#')>0)
  {
    var c;
    if(Result.indexOf('?')>0)
      c = '&';
    else
      c = '?';
    Result = Result.replace(/#/, c);
  }
  var ParamName;
  if(!Suffix)
    ParamName = 'a';
  else
    ParamName = Suffix;
  Result = ConcatURL(Result, ParamName+'=1');
  return Result;
}

function Ajax_Load(Callback_OnLoad, URL, Suffix)
{
  if(!URL)
    URL = GetAjaxURL(Suffix);
  if(!Suffix)
    Suffix = '';
  var divAjaxPlaceholder = $('#divAjaxPlaceholder'+Suffix);
  if(divAjaxPlaceholder.length!=1)
    return;
  divAjaxPlaceholder.show();
  $('#divAjaxOverlay'+Suffix)
      .css('width', $('#divAjaxPlaceholder'+Suffix).width()+'px')
      .css('height', $('#divAjaxPlaceholder'+Suffix).height()+'px')
      .show();
  $('#divAjaxLoading'+Suffix)
      .css('left', $('#divAjaxOverlay'+Suffix).position().left+
              Math.round(($('#divAjaxOverlay'+Suffix).width()-$('#divAjaxLoading'+Suffix).width())/2))
      .show();
  return $('#divAjaxContent'+Suffix).load(AddRandomParamToURL(URL), function() {
    $('#divAjaxOverlay'+Suffix).hide();
    $('#divAjaxLoading'+Suffix).hide();
    if($.isFunction(Callback_OnLoad))
      Callback_OnLoad();
  });
}

function IsSpecialCharacter(c)
{
  var SpecialCharacters = '!@#$%^&*()_-+={[}]:;"'+"'<,>.?/|\\";
  if(SpecialCharacters.indexOf(c)>-1)
    return true;
  return false;
}

function Ending(n, Str, DontSeparateTriads)
{
  var Result = n;
  if(!DontSeparateTriads)
    Result = SeparateTriads(Result);
  Result += ' '+PluralIfNeeded(n, Str);
  return Result;
}

function EndingB(n, Str, DontSeparateTriads)
{
  var Result = n;
  if(!DontSeparateTriads)
    Result = SeparateTriads(Result);
  Result = '<b>'+Result+'</b>';
  Result += ' '+PluralIfNeeded(n, Str);
  return Result;
}

function PluralIfNeeded(n, Str)
{
  if(n>1 || n==0)
    return Plural(Str);
  else
    return Str;
}

function IsOrAre(n)
{
  if(n==1)
    return 'is';
  else
    return 'are';
}

function HasOrHave(n)
{
  if(n==1)
    return 'has';
  else
    return 'have';
}

function WasOrWere(n)
{
  if(n==1)
    return 'was';
  else
    return 'were';
}

function Plural(Str)
{
  var ss = Str.toLowerCase();
  if(StringEndsWith(Str, 'es')
     || StringEndsWith(Str, 'ts')
     || StringEndsWith(Str, 'ds')
     || StringEndsWith(Str, 'ms')
     || StringEndsWith(Str, 'history')
     || StringEndsWith(Str, 'content')
     )
    return Str;
  var l = Str.length;
  if(ss.substr(l-1, 1)=='y' && ss.substr(l-2, 1)!='a')
    return Str.substr(0, l-1)+'ies';
  if(ss.substr(l-1, 1)=='s'
     || ss.substr(l-2)=='sh'
     || ss.substr(l-2)=='ch'
     || ss.substr(l-1)=='x')
    return Str+'es';
  else
    return Str+'s';
}

// from http://www.netlobo.com/url_query_string_javascript.html
function Param( name )
{
  name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
  var regexS = "[\\?&]"+name+"=([^&#]*)";
  var regex = new RegExp( regexS );
  var results = regex.exec( window.location.href );
  if( results == null )
    return "";
  else
    return results[1];
}

function Popup(URL, Width, Height)
{
  var w, h, l, t, s;
  s = '';
  if(Width && Width<screen.availWidth-20)
    w = Width;
  else
    w = screen.availWidth-20;
  l = Math.round((screen.availWidth-w)/2)-5;
  if(Height && Height<screen.availHeight-80)
    h = Height;
  else
    h = screen.availHeight-80;
  t = Math.round((screen.availHeight-h)/2)-25;
  s = 'width='+w+',height='+h+',left='+l+',top='+t+',scrollbars=yes,status=no,toolbar=no,menubar=no,location=no'+s;
  w = window.open(URL, 'Popup', s);
  w.focus();
  return false;
}

function Trim(s)
{
  return s.replace(/^\s+/, "").replace(/\s+$/, "");
}

function DistanceString(DistanceKilometers)
{
  if(DistanceKilometers<1)
    return Math.round(DistanceKilometers*1000)+' meters';
  if(DistanceKilometers<20)
    return FloatString(DistanceKilometers, 1)+' km';
  return SeparateTriads(Math.round(DistanceKilometers))+' km';
}

function MoneyString(Amount, NeedCents)
{
  var Decimals;
  if(!NeedCents && Amount==parseInt(Amount))
    Decimals = 0;
  else
    Decimals = 2;
  return '$'+FloatString(Amount, Decimals, true);
}

function ZeroPadding(s, ZeroCount)
{
  var Result = ''+s;
  while(Result.length<ZeroCount)
    Result = '0'+Result;
  return Result;
}

function GetValueByID(IDs, Values, ID)
{
  var n = IDs.length;
  for(var i = 0; i<n; i++)
    if(IDs[i]==ID)
      return Values[i];
  return false;
}

function SetValueByID(IDs, Values, ID, Value)
{
  var n = IDs.length;
  for(var i = 0; i<n; i++)
    if(IDs[i]==ID)
    {
      Values[i] = Value;
      break;
    }
}

function HtmlString(s)
{
  return s
        .replace(/&/g, '&amp;')
        .replace(/\</g, '&lt;')
        .replace(/\>/g, '&gt;')
        .replace(/\n/g, '<br>');
}

function RichHtmlString(s)
{
  var Result = HtmlString(s);
  return Result
//        .replace(/\[\&gt;\&gt; ([^\n\r\t\<\>\[\]]*) (http[s]?:\/\/)([^\s\<\:\]\)\"]*)(\:\d+)?([^\s\<\:\]\)\"]*)([^\.:<> ]?)\]/g, '<a href="$2$3$4$5$6" target=_blank class="aExternal">$1</a>')
//        .replace(/\[([^\n\r\t\<\>\[\]]+) (http[s]?:\/\/)([^\s\<\:\]\)\"]*)(\:\d+)?([^\s\<\:\]\)\"]*)([^\.:<> ]?)\]/g, '<a href="$2$3$4$5$6">$1</a>')
        .replace(/(http[s]?:\/\/)([^\s\<\:\]\)\"]*)(\:\d+)?([^\s\<\:\]\)\"]*)([^\.:<>\] ]?)/g, '<a href="$1$2$3$4$5">$1$2$3$4$5</a>')
        .replace(/\_(?!\s)([^\t\<\>\_]+)\_(?![^\s\<,:.)(\]\[])/g, '<i>$1</i>')
        .replace(/\*(?!\s)([^\n\r\t\<\>\*]+)\*(?![^\s\<,:.)(\]\[])/g, '<b>$1</b>');
}

function GetStringBeginningWithEllipsis(s, MaxLength)
{
  if(s.length>MaxLength)
    s = s.substr(0, MaxLength-3)+'...';
  return s;
}

function ShowOrHide(DOM_ID, SetVisible)
{
  if(SetVisible)
    $('#'+DOM_ID).show();
  else
    $('#'+DOM_ID).hide();
}

function ReloadPage()
{
  return Redirect(window.location.href);
}

function Redirect(URL)
{
  if($.isFunction($.cookie) && QuickMessage_HTML)
    $.cookie('QuickMessage_HTML', QuickMessage_HTML);
  if(window.location.href==URL)
    location.reload(true);
  else
    window.location.href = URL;
  return false;
}

// returns true if the lines intersect inside the two segments
// otherwise returns false
function TwoLinesIntersect(X1, Y1, X2, Y2, X3, Y3, X4, Y4)
{
  var D = (Y4-Y3)*(X2-X1) - (X4-X3)*(Y2-Y1), Ua, Ub;
  if(!D)
    return false;
  Ua = ((X4-X3)*(Y1-Y3) - (Y4-Y3)*(X1-X3)) / D;
  Ub = ((X2-X1)*(Y1-Y3) - (Y2-Y1)*(X1-X3)) / D;
/*
  this would be the actual intersection point:
  X = X1+Ua*(X2-X1);
  Y = Y1+Ua*(Y2-Y1);
*/
  return (Ua>=0 && Ua<=1 && Ub>=0 && Ub<=1);
}

function PercentageString(Particular, Total, Decimals)
{
  if(Total)
    return FloatString(Particular/Total*100, Decimals)+'%';
  else
    return '0%';
}

function AddRandomParamToURL(URL)
{
  if($.browser.msie || $.browser.webkit)
    URL = ConcatURL(URL, 'r='+FloatString(Math.random(), 5));     // prevents caching in IE. for other browsers Header Set Pragma "no-cache" etc. in httpd.conf
  return URL;
}

// from http://www.tutorialspoint.com/javascript/array_indexof.htm
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt /*, from*/)
  {
    var len = this.length;

    var from = Number(arguments[1]) || 0;
    from = (from < 0)
         ? Math.ceil(from)
         : Math.floor(from);
    if (from < 0)
      from += len;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}

function UpdateText(DOM_ID, Text)
{
  var o = $('#'+DOM_ID);
  if(o.text()!=Text)
    o.text(Text);
}

function UpdateText_Highlight(DOM_ID, Text, DontHighlight)
{
  var o = $('#'+DOM_ID);
  if(o.text()!=Text)
  {
    o.text(Text);
    if(!DontHighlight)
      o.highlightFade({speed:1500});
  }
}

/*
  from http://cuinl.tripod.com/Tips/o-1.htm
  *CARD TYPES            *PREFIX           *WIDTH
  American Express       34, 37            15
  Diners Club            300 to 305, 36    14
  Carte Blanche          38                14
  Discover               6011              16
  EnRoute                2014, 2149        15
  JCB                    3                 16
  JCB                    2131, 1800        15
  Master Card            51 to 55          16
  Visa                   4                 13, 16
*/
var CC_AMERICAN_EXPRESS = 1;
var CC_DINERS_CLUB = 2;
var CC_CARTE_BLANCHE = 3;
var CC_DISCOVER = 4;
var CC_ENROUTE = 5;
var CC_JCB = 6;
var CC_MASTER_CARD = 7;
var CC_VISA = 8;
function CreditCardTypeByNumber(CardNo)
{
  var Result = null;
  CardNo = CardNo.replace(/-/g, '');
  CardNo = CardNo.replace(/ /g, '');

  // Check that the minimum length of the string isn't less
  // than fourteen characters and -is- numeric
  if(CardNo.length<14 || !IsNumber(CardNo))
    return null;

  // Check the first two digits first
  var FirstTwo = CardNo.substr(0, 2);
  if(FirstTwo=='34' || FirstTwo=='37')
    Result = CC_AMERICAN_EXPRESS;
  else if(FirstTwo=='36')
    Result = CC_DINERS_CLUB;
  else if(FirstTwo=='38')
    Result = CC_CARTE_BLANCHE;
  else if(FirstTwo=='51' || FirstTwo=='52' || FirstTwo=='53' || FirstTwo=='54' || FirstTwo=='55')
    Result = CC_MASTER_CARD;
  else
  {
    // None of the above - so check the first four digits collectively
    var FirstFour = CardNo.substr(0, 4);
    if(FirstFour=='2014' || FirstFour=='2149')
      Result = CC_ENROUTE;
    else if(FirstFour=='2131' || FirstFour=='1800')
      Result = CC_JCB;
    else if(FirstFour=='6011')
      Result = CC_DISCOVER;
    else
    {
      // None of the above - so check the first three digits collectively
      var FirstThree = CardNo.substr(0, 3);
      if(FirstThree=='300' || FirstThree=='301' || FirstThree=='302' || FirstThree=='304' || FirstThree=='305')
        Result = CC_DINERS_CLUB;
      else
      {
        // None of the above - so simply check the first digit
        var FirstOne = CardNo.substr(0, 1);
        if(FirstOne=='3')
          Result = CC_JCB;
        else if(FirstOne=='4')
          Result = CC_VISA;
      }
    }
  }
  return Result;
}

function EscapeForJavascript(s)
{
  return s.replace('"', '\"');
}

function Now()
{
  var d = new Date();
  return d.getTime();
}

// from http://www.javascriptkit.com/javatutors/loadjavascriptcss.shtml
function LoadJS(URL)
{
  var Script = document.createElement('script');
  Script.setAttribute('type', 'text/javascript');
  Script.setAttribute('async', true);
  Script.setAttribute('src', URL);
  document.getElementsByTagName('head')[0].appendChild(Script);
}

// hack from http://bugs.jqueryui.com/ticket/3999
function HackDatepicker()
{
  $.datepicker._generateHTML_Old = $.datepicker._generateHTML; $.datepicker._generateHTML = function(inst) {
    res = this._generateHTML_Old(inst); res = res.replace("_hideDatepicker()","_clearDate('#"+inst.id+"')"); return res;
  }
}

function PhoneToInt(Phone)
{
  Phone = ''+Phone;
  var s = '';
  for(var i = 0; i<Phone.length; i++)
  {
    c = Phone.substr(i, 1);
    if(IsDigit(c))
      s += c;
  }
  return parseInt(s);
}

function MultiLevelCacheFilePath(FullPath)
{
  var Result = '', i;
  FullPath = ''+FullPath;
  for(i = 0; i<FullPath.length; i += 2)
  {
    if(i>0)
      Result += '/';
    Result += FullPath.substr(i, 2);
  }
  return Result;
}
