/*
http://akelpad.sourceforge.net/forum/viewtopic.php?p=30653#30653
Version: 2016-07-20
Author: KDJ

*** Replace text in edit window ***

TextReplace_function.js is library script, should be saved in ...\AkelPad\AkelFiles\Plugs\Scripts\Include\TextReplace.js.
Contains function: TextReplace().
It works similarly to "replace" method in JScript, but uses syntax of AkelPad regular expressions.

Usage in script:
  if (AkelPad.Include("TextReplace_function.js"))
    TextReplace(sFindIt, ReplaceWith[, sFlags[, nRangeStart[, nRangeEnd]]]);

Usage in menu or toolbar:
  Call("Scripts::Main", 1, "EvalCmd.js", `if (AkelPad.Include("TextReplace_function.js")) TextReplace(sFindIt, ReplaceWith[, sFlags[, nRangeStart[, nRangeEnd]]]);`)

-----------------------------------------------------------------------
TextReplace(sFindIt, ReplaceWith[, sFlags[, nRangeStart[, nRangeEnd]]])
-----------------------------------------------------------------------
Arguments
  sFindIt
    Text to find. Can be pattern of AkelPad regular expression.
  ReplaceWith
    Can be:
     - text to replace with,
     - JScript function that should return this text,
     - string as function body (required "f" flag).
    To ReplaceWith function is passed one argument - array aArg[s0, s1, s2, ..., sN, n0, n1, n2, n3]:
      s0 - entire matched text,
      s1 - first captured substring in parentheses (if "r" flag is used),
      s2 - second captured substring,
      sN - last captured substring,
      n0 - total number of sFindIt occurences in search range,
      n1 - current sFindIt occurence (from 1 to n0),
      n2 - offset of first character matched text,
      n3 - offset of last character matched text + 1.
      N is the number of left capturing parentheses in sFindIt.
      If "r" flag is not used, N==0 and items s1, s2, ..., sN does not occur (aArg[s0, n0, n1, n2, n3]).
      Length of aArg array is equal to N+5.
    If ReplaceWith is string as function body, in function body you can use arguments[0] to get aArg array or predefined variables _s0, _s1, ..., _n0, _n1, _n2, _n3:
      _s0 == s0,
      _s1 == s1,
      ...
      _sN == sN,
      _n0 == n0,
      _n1 == n1.
      _n2 == n2.
      _n3 == n3.
    ReplaceWith function should return any string to replace with or boolean value (true/false).
    If function returns boolean value - current sFindIt occurence will not be replaced and:
      - if returns true - TextReplace action will continue,
      - if returns false - TextReplace will terminate.
  sFlags
    May be combination of the following:
    "f" - ReplaceWith argument is string as function body,
    "g" - global search - replace all occurrences of sFindIt in search range, if not set, only first occurence will be replaced,
    "i" - ignore case,
    "r" - search with regular expressions,
    "n" - dot in regular expression specifies any character except new line "\n",
    "w" - whole word.
    Default is "".
  nRangeStart
    Index (offset) of first char in search range or predefined value of range.
    Default is 0 - first char in document.
    Can be used predefined values for range (in this case nRangeEnd argument is ignored):
    -1 - from first char to caret char,
    -2 - from caret char to last char,
    -3 - from first sel char to last sel char,
    -4 - bookmarked lines.
  nRangeEnd
    Index of last char in search range.
    Default is -1 - last char in document.

Return value
  Number - count of changes.
  Or if an error occurred:
    -10 - there is no edit window,
    -11 - invalid argument sFindIt (not string or empty string),
    -12 - invalid argument ReplaceWith (not string and not function),
    -13 - error in ReplaceWith function body passed as string - can not create function,
    -14 - ReplaceWith function doesn't return a string or boolean value.
    If there is syntax error in regular expression (sFindIt), the return value is (-100 - PatternOffset).
    If run time error occurs while execution of ReplaceWith function, the return value is Error object.

---------------------------------------------------------------------------------------
Example 1 - single replace date in format "yyyy-mm-dd" with date in format "dd-mm-yyyy"
---------------------------------------------------------------------------------------
if (AkelPad.Include("TextReplace_function.js"))
  TextReplace("\\b(\\d{4})-(\\d{2})-(\\d{2})\\b", "\\3-\\2-\\1", "r");

-------------------------------------------------------------------
Example 2 - replace all hex values (0x0 - 0xFFFFFFFF) with decimals
-------------------------------------------------------------------
if (AkelPad.Include("TextReplace_function.js"))
  TextReplace("\\b0x[\\dA-F]{1,8}\\b", function(a) {return parseInt(a[0]).toString();}, "gir");

or (ReplaceWith is string as function body):

if (AkelPad.Include("TextReplace_function.js"))
  TextReplace("\\b0x[\dA-F]{1,8}\\b", "return parseInt(arguments[0][0]).toString();", "fgir");

or:

if (AkelPad.Include("TextReplace_function.js"))
  TextReplace("\\b0x[\dA-F]{1,8}\\b", "return parseInt(_s0).toString();", "fgir");

------------------------------------------------------------------
Example 3 - insert counter at the beginning of all non-empty lines
------------------------------------------------------------------
if (AkelPad.Include("TextReplace_function.js"))
  WScript.Echo("Count of changes: " + TextReplace("^[ \\t]*([^ \\t\\n]++[ \\t]*)++$", ReplaceFunc, "gr"));

function ReplaceFunc(a)
{
  var sCount = a[a.length - 3].toString();
  var nLen   = a[a.length - 4].toString().length;

  while (sCount.length < nLen)
    sCount = "0" + sCount;

  return sCount + ": " + a[0];
}
*/

function TextReplace(sFindIt, ReplaceWith, sFlags, nRange1, nRange2)
{
  var hMainWnd = AkelPad.GetMainWnd();
  var hEditWnd = AkelPad.GetEditWnd();
  var nOffset1;
  var nOffset2;
  var nOffset3;
  var nOffset4;
  var nSel1;
  var nSel2;
  var nBook1;
  var nDoc1;
  var nAEFR;
  var nFRF;
  var lpWhat;
  var lpFind;
  var lpIndex1;
  var lpIndex2;
  var lpIndex3;
  var lpIndex4;
  var bBatch;
  var nTextLen;
  var nDiffLen;
  var nCount;
  var nCountAll;
  var nChanges;
  var bWholeDoc;
  var vWith;
  var lpCaret;
  var lpSelect;
  var lpPoint64;
  var aArg;
  var nRESE;
  var lpDelim;
  var lpPat;
  var lpPatExec;
  var nMaxDelim;
  var bContinue;
  var n;

  if (! hEditWnd)
    return -10;
  if ((typeof sFindIt != "string") || (sFindIt == ""))
    return -11;
  if ((typeof ReplaceWith != "string") && (typeof ReplaceWith != "function"))
    return -12;

  if (typeof sFlags != "string")
    sFlags = "";

  sFlags = sFlags.toLowerCase();

  if ((typeof ReplaceWith == "string") && ReplaceWith.length && (sFlags.indexOf("f") > -1))
  {
    try
    {
      ReplaceWith = 'var _arg={}, _i;\n' +
                    'for (_i=0; _i<arguments[0].length - 2; ++_i) _arg["_s" + _i]=arguments[0][_i];\n' +
                    '_arg._n0=arguments[0][arguments[0].length-4];\n' +
                    '_arg._n1=arguments[0][arguments[0].length-3];\n' +
                    '_arg._n2=arguments[0][arguments[0].length-2];\n' +
                    '_arg._n3=arguments[0][arguments[0].length-1];\n' +
                    'with (_arg) {' + ReplaceWith + '}';
      ReplaceWith = new Function(ReplaceWith);
    }
    catch (oError)
    {
      return -13;
    }
  }

  if (AkelPad.GetEditReadOnly(0))
    return 0;

  bBatch   = sFlags.indexOf("b") > -1;
  nTextLen = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 2 /*AEGI_LASTCHAR*/, 0);

  if (typeof nRange1 == "number")
    nRange1 = Math.round(nRange1);
  else
    nRange1 = 0;

  if (nRange1 == -1)
  {
    nOffset1 = 0;
    nOffset2 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 5 /*AEGI_CARETCHAR*/, 0);
  }
  else if (nRange1 == -2)
  {
    nOffset1 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 5 /*AEGI_CARETCHAR*/, 0);
    nOffset2 = nTextLen;
  }
  else if (nRange1 == -3)
  {
    nOffset1 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 3 /*AEGI_FIRSTSELCHAR*/, 0);
    nOffset2 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 4 /*AEGI_LASTSELCHAR*/, 0);
  }
  else if (nRange1 < -3)
  {
    nRange1   = -4;
    bContinue = true;

    if (! (AkelPad.IsPluginRunning("LineBoard::Main") && NextBookmark(-1)))
      return 0;
  }
  else
  {
    if (typeof nRange2 == "number")
      nRange2 = Math.round(nRange2);
    else
      nRange2 = -1;

    nOffset1 = nRange1;

    if (nRange2 < 0)
      nOffset2 = nTextLen;
    else
      nOffset2 = nRange2;
  }

  nAEFR = 0x00000001 /*AEFR_DOWN*/;
  nFRF  = 0x00000001 /*FRF_DOWN*/;
  if (sFlags.indexOf("i") == -1)
  {
    nAEFR |= 0x00000004 /*AEFR_MATCHCASE*/;
    nFRF  |= 0x00000004 /*FRF_MATCHCASE*/;
  }
  if (sFlags.indexOf("w") > -1)
  {
    nAEFR |= 0x00000002 /*AEFR_WHOLEWORD*/;
    nFRF  |= 0x00000002 /*FRF_WHOLEWORD*/;
  }
  if (sFlags.indexOf("r") > -1)
  {
    if (sFlags.indexOf("n") > -1)
    {
      nAEFR |= 0x00100000 /*AEFR_REGEXPNONEWLINEDOT*/;
      nFRF  |= 0x00040000 /*FRF_REGEXPNONEWLINEDOT*/;
    }

    nAEFR  |= 0x00080000 /*AEFR_REGEXP*/;
    nFRF   |= 0x00080000 /*FRF_REGEXP*/;
    sFindIt = sFindIt.replace(/[\n\r]/g, "\\n");
  }
  else
    sFindIt = sFindIt.replace(/\n/g, "\r");

  if (! bBatch)
  {
    lpCaret   = AkelPad.MemAlloc(_X64 ? 24 : 12 /*sizeof(AECHARINDEX)*/);
    lpSelect  = AkelPad.MemAlloc(_X64 ? 56 : 32 /*sizeof(AESELECTION)*/);
    lpPoint64 = AkelPad.MemAlloc(_X64 ? 16 :  8 /*sizeof(POINT64)*/);
    AkelPad.SendMessage(hEditWnd, 3125 /*AEM_GETSEL*/, lpCaret, lpSelect);
    AkelPad.SendMessage(hEditWnd, 3179 /*AEM_GETSCROLLPOS*/, 0, lpPoint64);
    AkelPad.SendMessage(hEditWnd, 3185 /*AEM_LOCKSCROLL*/, 3 /*SB_BOTH*/, 1);
    AkelPad.SendMessage(hMainWnd, 11 /*WM_SETREDRAW*/, 0, 0);
    AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 0, 0);
    AkelPad.SendMessage(hEditWnd, 3080 /*AEM_STOPGROUPTYPING*/, 0, 0);
    AkelPad.SendMessage(hEditWnd, 3081 /*AEM_BEGINUNDOACTION*/, 0, 0);
  }

  lpWhat = AkelPad.MemAlloc((sFindIt.length + 1) * 2);
  lpFind = AkelPad.MemAlloc(_X64 ? 136 : 68 /*sizeof(AEFINDTEXTW)*/);
  AkelPad.MemCopy(lpWhat, sFindIt, 1 /*DT_UNICODE*/);
  AkelPad.MemCopy(lpFind /*dwFlags*/, nAEFR, 3 /*DT_DWORD*/);
  AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ?  8 :  4) /**pText*/, lpWhat, 2 /*DT_QWORD*/);
  AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ? 16 :  8) /*dwTextLen*/, sFindIt.length, 2 /*DT_QWORD*/);
  AkelPad.MemCopy(_PtrAdd(lpFind, _X64 ? 24 : 12) /*nNewLine*/, 5 /*AELB_R*/, 3 /*DT_DWORD*/);
  lpIndex1  = _PtrAdd(lpFind, _X64 ?  32 : 16);
  lpIndex2  = _PtrAdd(lpFind, _X64 ?  56 : 28);
  lpIndex3  = _PtrAdd(lpFind, _X64 ?  80 : 40);
  lpIndex4  = _PtrAdd(lpFind, _X64 ? 104 : 52);
  nCount    = 0;
  nChanges  = 0;
  bWholeDoc = (nRange1 == 0) && (nRange2 < 0);

  if (sFlags.indexOf("r") > -1)
  {
    AkelPad.SetSel(-1, -1);
    nCount = AkelPad.TextReplace(hEditWnd, sFindIt, "", 0x80080001 /*FRF_TEST|FRF_REGEXP|FRF_DOWN*/, 0x1 /*RRF_ALL*/);
  }

  if (nCount > -1)
  {
    if (typeof ReplaceWith == "string")
    {
      if ((sFlags.indexOf("g") > -1))
      {
        if (bWholeDoc)
          nFRF |= 0x00200000 /*FRF_BEGINNING*/;
        else
        {
          nFRF |= 0x00400000 /*FRF_SELECTION*/;
          AkelPad.SetSel(nOffset1, nOffset2);
        }

        if (nRange1 == -4) //bookmarks
        {
          for (;;)
          {
            if ((n = AkelPad.TextReplace(hEditWnd, sFindIt, ReplaceWith, nFRF, 0x1 /*RRF_ALL*/)) > 0)
            {
              nCount += n;
              nSel1   = nOffset1;
              nSel2   = AkelPad.GetSelEnd();
            }

            if (NextBookmark(AkelPad.GetSelEnd()))
              AkelPad.SetSel(nOffset1, nOffset2);
            else
              break;
          }

          if (nCount > 0)
            AkelPad.SetSel(nSel1, nSel2);
        }
        else
          nCount = AkelPad.TextReplace(hEditWnd, sFindIt, ReplaceWith, nFRF, 0x1 /*RRF_ALL*/);
      }
      else
      {
        do
        {
          AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset1, lpIndex1);
          AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset2, lpIndex2);

          if (AkelPad.SendMessage(hEditWnd, 3041 /*AEM_FINDTEXTW*/, 0, lpFind))
            nCount = 1;
        }
        while ((nCount == 0) && (nRange1 == -4 /*bookmarks*/) && NextBookmark(nOffset2));

        if (nCount == 1)
        {
          nOffset3 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex3);
          nOffset4 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex4);
          AkelPad.SetSel(nOffset3, nOffset4);
          AkelPad.TextReplace(hEditWnd, sFindIt, ReplaceWith, nFRF | 0x00400000 /*FRF_SELECTION*/, 0);

          nDiffLen = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 2 /*AEGI_LASTCHAR*/, 0) - nTextLen;
          nOffset1 = nOffset4 + nDiffLen;

          if (bWholeDoc)
            nOffset2 = nOffset1;
          else
            nOffset2 += nDiffLen;

          AkelPad.SetSel(nOffset2, nOffset1);
        }
      }

      nChanges = nCount;
    }
    else //with function
    {
      nCountAll = 0;
      GetCounAll();

      aArg  = [];
      nRESE = 0x2002 /*RESE_ISMATCH|RESE_MULTILINE*/;
      if (sFlags.indexOf("i") == -1)
        nRESE |= 0x0001 /*RESE_MATCHCASE*/;
      if (sFlags.indexOf("n") > -1)
        nRESE |= 0x0008 /*RESE_NONEWLINEDOT*/;
      if (sFlags.indexOf("w") > -1)
        nRESE |= 0x0004 /*RESE_WHOLEWORD*/;

      lpDelim   = AkelPad.MemAlloc(128 /*WORD_DELIMITERS_SIZE*/ * 2);
      lpPat     = AkelPad.MemAlloc((sFindIt.length + 1) * 2);
      lpPatExec = AkelPad.MemAlloc(_X64? 216 : 108 /*PATEXEC*/);
      nMaxDelim = AkelPad.SendMessage(hEditWnd, 3243 /*AEM_GETWORDDELIMITERS*/, 128 /*WORD_DELIMITERS_SIZE*/, lpDelim) - 2;

      AkelPad.MemCopy(lpPat, sFindIt, 1 /*DT_UNICODE*/);
      AkelPad.MemCopy(lpPatExec /*PATEXEC.dwOptions*/, nRESE, 3 /*DT_DWORD*/);
      AkelPad.MemCopy(_PtrAdd(lpPatExec, _X64 ?   8 :  4) /*PATEXEC.wpDelim*/, nMaxDelim ? lpDelim : 0, 2 /*DT_QWORD*/);
      AkelPad.MemCopy(_PtrAdd(lpPatExec, _X64 ?  16 :  8) /*PATEXEC.wpMaxDelim*/, _PtrAdd(lpDelim, nMaxDelim * 2), 2 /*DT_QWORD*/);
      AkelPad.MemCopy(_PtrAdd(lpPatExec, _X64 ? 112 : 56) /*PATEXEC.wpPat*/, lpPat, 2 /*DT_QWORD*/);
      AkelPad.MemCopy(_PtrAdd(lpPatExec, _X64 ? 120 : 60) /*PATEXEC.wpMaxPat*/, _PtrAdd(lpPat, sFindIt.length * 2), 2 /*DT_QWORD*/);

      do
      {
        AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset1, lpIndex1);
        AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset2, lpIndex2);

        if (AkelPad.SendMessage(hEditWnd, 3041 /*AEM_FINDTEXTW*/, 0, lpFind))
        {
          ++nCount;
          nOffset3 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex3);
          nOffset4 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex4);
          AkelPad.SetSel(nOffset3, nOffset4);

          vWith = GetWithText();

          if ((typeof vWith == "string") || (typeof vWith == "boolean"))
          {
            nSel1 = nOffset1;

            if (typeof vWith == "string")
            {
              AkelPad.ReplaceSel(vWith);
              ++nChanges;
              nOffset1  = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 5 /*AEGI_CARETCHAR*/, 0);
              nOffset2 -= nTextLen - (nTextLen = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 2 /*AEGI_LASTCHAR*/, 0));
              nBook1    = nSel1;
              nDoc1     = nOffset1;
              nSel2     = nOffset2;
            }
            else if (vWith)
              nOffset1 = nOffset4;
            else
              break;

            if (sFlags.indexOf("g") > -1)
            {
              AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset1, lpIndex1);
              AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset2, lpIndex2);

              while (AkelPad.SendMessage(hEditWnd, 3041 /*AEM_FINDTEXTW*/, 0, lpFind))
              {
                ++nCount;
                nOffset3 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex3);
                nOffset4 = AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, lpIndex4);
                AkelPad.SetSel(nOffset3, nOffset4);

                vWith = GetWithText();

                if ((typeof vWith == "string") || (typeof vWith == "boolean"))
                {
                  if (typeof vWith == "string")
                  {
                    AkelPad.ReplaceSel(vWith);
                    ++nChanges;
                    nOffset1  = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 5 /*AEGI_CARETCHAR*/, 0);
                    nOffset2 -= nTextLen - (nTextLen = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 2 /*AEGI_LASTCHAR*/, 0));
                    nBook1    = nSel1;
                    nDoc1     = nOffset1;
                    nSel2     = nOffset2;
                    AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset1, lpIndex1);
                    AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset2, lpIndex2);
                  }
                  else if (vWith)
                  {
                    nOffset1 = nOffset4;
                    AkelPad.SendMessage(hEditWnd, 3137 /*AEM_RICHOFFSETTOINDEX*/, nOffset1, lpIndex1);
                  }
                  else
                  {
                    bContinue = false;
                    break;
                  }
                }
                else
                {
                  nCount = -14;
                  break;
                }
              }
            }
          }
          else
            nCount = -14;
        }
      }
      while (bContinue && ((nCount == 0) || (nCount > 0) && (sFlags.indexOf("g") > -1)) && (nRange1 == -4 /*bookmarks*/) && NextBookmark(nOffset2));

      AkelPad.MemFree(lpDelim);
      AkelPad.MemFree(lpPat);
      AkelPad.MemFree(lpPatExec);
    }
  }

  if (! bBatch)
  {
    AkelPad.SendMessage(hEditWnd, 3082 /*AEM_ENDUNDOACTION*/, 0, 0);
    AkelPad.SendMessage(hEditWnd, 3080 /*AEM_STOPGROUPTYPING*/, 0, 0);
    AkelPad.SendMessage(hEditWnd, 3185 /*AEM_LOCKSCROLL*/, 3 /*SB_BOTH*/, 0);
    AkelPad.SendMessage(hEditWnd, 3376 /*AEM_UPDATESCROLLBAR*/, 3 /*SB_BOTH*/, 0);
  }

  if ((nCount > 0) && (nChanges > 0))
  {
    if (typeof ReplaceWith == "function")
    {
      if (bWholeDoc)
        AkelPad.SetSel(nDoc1, nDoc1);
      else
      {
        if (sFlags.indexOf("g") > -1)
        {
          if (nRange1 == -4 /*bookmarks*/)
            AkelPad.SetSel(nBook1, nSel2);
          else
            AkelPad.SetSel(nSel1, nSel2);
        }
        else
          AkelPad.SetSel(nOffset2, nOffset1);
      }

      if (! bBatch)
        AkelPad.SendMessage(hEditWnd, 3183 /*AEM_SCROLLTOPOINT*/, 0, 0 /*scroll to caret*/);
    }
  }
  else
  {
    if (nCount == -14)
    {
      if ((nChanges > 0) && (! bBatch))
        AkelPad.SendMessage(hEditWnd, 3077 /*AEM_UNDO*/, 0, 0);

      if (vWith instanceof Error)
        nChanges = vWith;
      else
        nChanges = nCount;
    }
    else if (nCount <= -100)
      nChanges = nCount;

    if (! bBatch)
    {
      AkelPad.SendMessage(hEditWnd, 3132 /*AEM_INDEXUPDATE*/, 0, lpCaret);
      AkelPad.SendMessage(hEditWnd, 3132 /*AEM_INDEXUPDATE*/, 0, lpSelect);
      AkelPad.SendMessage(hEditWnd, 3132 /*AEM_INDEXUPDATE*/, 0, _PtrAdd(lpSelect, _X64 ? 24 : 12));
      AkelPad.SendMessage(hEditWnd, 3126 /*AEM_SETSEL*/, lpCaret, lpSelect);
      AkelPad.SendMessage(hEditWnd, 3180 /*AEM_SETSCROLLPOS*/, 0, lpPoint64);
    }
  }

  if (! bBatch)
  {
    AkelPad.SendMessage(hMainWnd, 11 /*WM_SETREDRAW*/, 1, 0);
    AkelPad.SendMessage(hEditWnd, 11 /*WM_SETREDRAW*/, 1, 0);
    AkelPad.SystemFunction().Call("User32::InvalidateRect", hMainWnd, 0, 1);
    AkelPad.SystemFunction().Call("User32::InvalidateRect", hEditWnd, 0, 1);
    AkelPad.MemFree(lpCaret);
    AkelPad.MemFree(lpSelect);
    AkelPad.MemFree(lpPoint64);
  }

  AkelPad.MemFree(lpWhat);
  AkelPad.MemFree(lpFind);

  return nChanges;

  function NextBookmark(nOffset)
  {
    var bIsNext = false;
    var lpLen   = AkelPad.MemAlloc(4 /*int*/);
    var nLen;
    var lpBooks;
    var nLine;
    var aBook;
    var i, n;

    AkelPad.CallW("LineBoard::Main", 12, hEditWnd, 0, 0, lpLen);

    if ((nLen = AkelPad.MemRead(lpLen, 3 /*DT_DWORD*/)) > 1)
    {
      nLine   = (nOffset < 0) ? -1 : AkelPad.SendMessage(hEditWnd, 3129 /*AEM_GETLINENUMBER*/, 21 /*AEGI_UNWRAPLINEFROMRICHOFFSET*/, nOffset);
      lpBooks = AkelPad.MemAlloc(nLen * 2);
      AkelPad.CallW("LineBoard::Main", 12, hEditWnd, 0, lpBooks, 0);
      aBook = AkelPad.MemRead(lpBooks, 1 /*DT_UNICODE*/).split(",");
      AkelPad.MemFree(lpBooks);

      if (nLine < aBook[aBook.length - 1])
      {
        for (i = 0; i < aBook.length; ++i)
        {
          if (nLine < aBook[i])
          {
            for (n = i; (n + 1 < aBook.length) && (aBook[n + 1] - aBook[n] <= 1); ++n);
  
            nOffset1 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 41 /*AEGI_RICHOFFSETFROMUNWRAPLINE*/, parseInt(aBook[i]));
            nOffset2 = AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 19 /*AEGI_WRAPLINEEND*/, AkelPad.SendMessage(hEditWnd, 3138 /*AEM_GETRICHOFFSET*/, 41 /*AEGI_RICHOFFSETFROMUNWRAPLINE*/, parseInt(aBook[n])));
            bIsNext  = true;
            break;
          }
        }
      }
    }

    AkelPad.MemFree(lpLen);

    return bIsNext;
  }

  function GetCounAll()
  {
    nFRF |= 0x80000000 /*FRF_TEST*/;
    if (bWholeDoc)
      nFRF |= 0x00200000 /*FRF_BEGINNING*/;
    else
    {
      nFRF |= 0x00400000 /*FRF_SELECTION*/;
      AkelPad.SetSel(nOffset1, nOffset2);
    }

    if (nRange1 == -4) //bookmarks
    {
      nSel1 = nOffset1;
      nSel2 = nOffset2;

      for (;;)
      {
        nCountAll += AkelPad.TextReplace(hEditWnd, sFindIt, "", nFRF, 0x1 /*RRF_ALL*/);

        if (NextBookmark(nOffset2))
          AkelPad.SetSel(nOffset1, nOffset2);
        else
          break;
      }

      nOffset1 = nSel1;
      nOffset2 = nSel2;
    }
    else
      nCountAll = AkelPad.TextReplace(hEditWnd, sFindIt, "", nFRF, 0x1 /*RRF_ALL*/);
  }

  function GetWithText()
  {
    var lpREGroup;
    var nIndex;
    var vText;

    aArg.length = 0;

    if (sFlags.indexOf("r") > -1)
    {
      AkelPad.MemCopy(_PtrAdd(lpPatExec, _X64 ? 104 : 52) /*PATEXEC.lpREGroupStack*/, 0, 2 /*DT_QWORD*/);
      AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 3 /*AEGI_FIRSTSELCHAR*/, _PtrAdd(lpPatExec, _X64 ?  56 : 28) /*PATEXEC.ciRange*/);
      AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 4 /*AEGI_LASTSELCHAR*/,  _PtrAdd(lpPatExec, _X64 ?  80 : 40) /*PATEXEC.ciMaxRange*/);
      AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 3 /*AEGI_FIRSTSELCHAR*/, _PtrAdd(lpPatExec, _X64 ? 144 : 72) /*PATEXEC.ciStr*/);
      AkelPad.SendMessage(hEditWnd, 3130 /*AEM_GETINDEX*/, 4 /*AEGI_LASTSELCHAR*/,  _PtrAdd(lpPatExec, _X64 ? 168 : 84) /*PATEXEC.ciMaxStr*/);
      AkelPad.SendMessage(hMainWnd, 1415 /*AKD_PATEXEC*/, 0, lpPatExec);

      lpREGroup = AkelPad.MemRead(AkelPad.MemRead(_PtrAdd(lpPatExec, _X64 ? 104 : 52 /*PATEXEC.lpREGroupStack*/), 2 /*DT_QWORD*/) /*REGROUP.first*/, 2 /*DT_QWORD*/);

      do
      {
        nIndex = AkelPad.MemRead(_PtrAdd(lpREGroup, _X64 ? 172 : 96 /*REGROUP.nIndex*/), 3 /*DT_DWORD*/);

        if (nIndex > -1)
          aArg[nIndex] = AkelPad.GetTextRange(AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, _PtrAdd(lpREGroup, _X64 ?  96 : 48) /*REGROUP.ciStrStart*/), AkelPad.SendMessage(hEditWnd, 3136 /*AEM_INDEXTORICHOFFSET*/, 0, _PtrAdd(lpREGroup, _X64 ? 120 : 60) /*REGROUP.ciStrEnd*/));
      }
      while (lpREGroup = AkelPad.SendMessage(hMainWnd, 1420 /*AKD_PATNEXTGROUP*/, lpREGroup, 0))

      AkelPad.SendMessage(hMainWnd, 1423 /*AKD_PATFREE*/, 0, lpPatExec);
    }
    else
      aArg[0] = AkelPad.GetSelText();

    aArg.push(nCountAll, nCount, nOffset3, nOffset4);

    try
    {
      vText = ReplaceWith(aArg);
    }
    catch (oError)
    {
      vText = oError;
    }

    return vText;
  }
}
