1// Copyright 2014 PDFium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com
6
7#include "PublicMethods.h"
8
9#include <algorithm>
10
11#include "Field.h"
12#include "JS_Context.h"
13#include "JS_Define.h"
14#include "JS_EventHandler.h"
15#include "JS_Object.h"
16#include "JS_Runtime.h"
17#include "JS_Value.h"
18#include "color.h"
19#include "core/include/fxcrt/fx_ext.h"
20#include "fpdfsdk/include/fsdk_mgr.h"  // For CPDFDoc_Environment.
21#include "fpdfsdk/include/javascript/IJavaScript.h"
22#include "resource.h"
23#include "util.h"
24
25#define DOUBLE_CORRECT 0.000000000000001
26
27BEGIN_JS_STATIC_GLOBAL_FUN(CJS_PublicMethods)
28JS_STATIC_GLOBAL_FUN_ENTRY(AFNumber_Format)
29JS_STATIC_GLOBAL_FUN_ENTRY(AFNumber_Keystroke)
30JS_STATIC_GLOBAL_FUN_ENTRY(AFPercent_Format)
31JS_STATIC_GLOBAL_FUN_ENTRY(AFPercent_Keystroke)
32JS_STATIC_GLOBAL_FUN_ENTRY(AFDate_FormatEx)
33JS_STATIC_GLOBAL_FUN_ENTRY(AFDate_KeystrokeEx)
34JS_STATIC_GLOBAL_FUN_ENTRY(AFDate_Format)
35JS_STATIC_GLOBAL_FUN_ENTRY(AFDate_Keystroke)
36JS_STATIC_GLOBAL_FUN_ENTRY(AFTime_FormatEx)
37JS_STATIC_GLOBAL_FUN_ENTRY(AFTime_KeystrokeEx)
38JS_STATIC_GLOBAL_FUN_ENTRY(AFTime_Format)
39JS_STATIC_GLOBAL_FUN_ENTRY(AFTime_Keystroke)
40JS_STATIC_GLOBAL_FUN_ENTRY(AFSpecial_Format)
41JS_STATIC_GLOBAL_FUN_ENTRY(AFSpecial_Keystroke)
42JS_STATIC_GLOBAL_FUN_ENTRY(AFSpecial_KeystrokeEx)
43JS_STATIC_GLOBAL_FUN_ENTRY(AFSimple)
44JS_STATIC_GLOBAL_FUN_ENTRY(AFMakeNumber)
45JS_STATIC_GLOBAL_FUN_ENTRY(AFSimple_Calculate)
46JS_STATIC_GLOBAL_FUN_ENTRY(AFRange_Validate)
47JS_STATIC_GLOBAL_FUN_ENTRY(AFMergeChange)
48JS_STATIC_GLOBAL_FUN_ENTRY(AFParseDateEx)
49JS_STATIC_GLOBAL_FUN_ENTRY(AFExtractNums)
50END_JS_STATIC_GLOBAL_FUN()
51
52IMPLEMENT_JS_STATIC_GLOBAL_FUN(CJS_PublicMethods)
53
54static const FX_WCHAR* const months[] = {L"Jan",
55                                         L"Feb",
56                                         L"Mar",
57                                         L"Apr",
58                                         L"May",
59                                         L"Jun",
60                                         L"Jul",
61                                         L"Aug",
62                                         L"Sep",
63                                         L"Oct",
64                                         L"Nov",
65                                         L"Dec"};
66
67static const FX_WCHAR* const fullmonths[] = {L"January",
68                                             L"February",
69                                             L"March",
70                                             L"April",
71                                             L"May",
72                                             L"June",
73                                             L"July",
74                                             L"August",
75                                             L"September",
76                                             L"October",
77                                             L"November",
78                                             L"December"};
79
80FX_BOOL CJS_PublicMethods::IsNumber(const FX_WCHAR* string) {
81  CFX_WideString sTrim = StrTrim(string);
82  const FX_WCHAR* pTrim = sTrim.c_str();
83  const FX_WCHAR* p = pTrim;
84
85  FX_BOOL bDot = FALSE;
86  FX_BOOL bKXJS = FALSE;
87
88  wchar_t c;
89  while ((c = *p)) {
90    if (c == '.' || c == ',') {
91      if (bDot)
92        return FALSE;
93      bDot = TRUE;
94    } else if (c == '-' || c == '+') {
95      if (p != pTrim)
96        return FALSE;
97    } else if (c == 'e' || c == 'E') {
98      if (bKXJS)
99        return FALSE;
100
101      p++;
102      c = *p;
103      if (c == '+' || c == '-') {
104        bKXJS = TRUE;
105      } else {
106        return FALSE;
107      }
108    } else if (!FXSYS_iswdigit(c)) {
109      return FALSE;
110    }
111    p++;
112  }
113
114  return TRUE;
115}
116
117FX_BOOL CJS_PublicMethods::maskSatisfied(wchar_t c_Change, wchar_t c_Mask) {
118  switch (c_Mask) {
119    case L'9':
120      return FXSYS_iswdigit(c_Change);
121    case L'A':
122      return FXSYS_iswalpha(c_Change);
123    case L'O':
124      return FXSYS_iswalnum(c_Change);
125    case L'X':
126      return TRUE;
127    default:
128      return (c_Change == c_Mask);
129  }
130}
131
132FX_BOOL CJS_PublicMethods::isReservedMaskChar(wchar_t ch) {
133  return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X';
134}
135
136double CJS_PublicMethods::AF_Simple(const FX_WCHAR* sFuction,
137                                    double dValue1,
138                                    double dValue2) {
139  if (FXSYS_wcsicmp(sFuction, L"AVG") == 0 ||
140      FXSYS_wcsicmp(sFuction, L"SUM") == 0) {
141    return dValue1 + dValue2;
142  }
143  if (FXSYS_wcsicmp(sFuction, L"PRD") == 0) {
144    return dValue1 * dValue2;
145  }
146  if (FXSYS_wcsicmp(sFuction, L"MIN") == 0) {
147    return std::min(dValue1, dValue2);
148  }
149  if (FXSYS_wcsicmp(sFuction, L"MAX") == 0) {
150    return std::max(dValue1, dValue2);
151  }
152  return dValue1;
153}
154
155CFX_WideString CJS_PublicMethods::StrLTrim(const FX_WCHAR* pStr) {
156  while (*pStr && *pStr == L' ')
157    pStr++;
158
159  return pStr;
160}
161
162CFX_WideString CJS_PublicMethods::StrRTrim(const FX_WCHAR* pStr) {
163  const FX_WCHAR* p = pStr;
164  while (*p)
165    p++;
166  while (p > pStr && *(p - 1) == L' ')
167    p--;
168
169  return CFX_WideString(pStr, p - pStr);
170}
171
172CFX_WideString CJS_PublicMethods::StrTrim(const FX_WCHAR* pStr) {
173  return StrRTrim(StrLTrim(pStr).c_str());
174}
175
176CFX_ByteString CJS_PublicMethods::StrLTrim(const FX_CHAR* pStr) {
177  while (*pStr && *pStr == ' ')
178    pStr++;
179
180  return pStr;
181}
182
183CFX_ByteString CJS_PublicMethods::StrRTrim(const FX_CHAR* pStr) {
184  const FX_CHAR* p = pStr;
185  while (*p)
186    p++;
187  while (p > pStr && *(p - 1) == L' ')
188    p--;
189
190  return CFX_ByteString(pStr, p - pStr);
191}
192
193CFX_ByteString CJS_PublicMethods::StrTrim(const FX_CHAR* pStr) {
194  return StrRTrim(StrLTrim(pStr));
195}
196
197double CJS_PublicMethods::ParseNumber(const FX_WCHAR* swSource,
198                                      FX_BOOL& bAllDigits,
199                                      FX_BOOL& bDot,
200                                      FX_BOOL& bSign,
201                                      FX_BOOL& bKXJS) {
202  bDot = FALSE;
203  bSign = FALSE;
204  bKXJS = FALSE;
205
206  FX_BOOL bDigitExist = FALSE;
207
208  const FX_WCHAR* p = swSource;
209  wchar_t c;
210
211  const FX_WCHAR* pStart = NULL;
212  const FX_WCHAR* pEnd = NULL;
213
214  while ((c = *p)) {
215    if (!pStart && c != L' ') {
216      pStart = p;
217    }
218
219    pEnd = p;
220    p++;
221  }
222
223  if (!pStart) {
224    bAllDigits = FALSE;
225    return 0;
226  }
227
228  while (pEnd != pStart) {
229    if (*pEnd == L' ')
230      pEnd--;
231    else
232      break;
233  }
234
235  double dRet = 0;
236  p = pStart;
237  bAllDigits = TRUE;
238  CFX_WideString swDigits;
239
240  while (p <= pEnd) {
241    c = *p;
242
243    if (FXSYS_iswdigit(c)) {
244      swDigits += c;
245      bDigitExist = TRUE;
246    } else {
247      switch (c) {
248        case L' ':
249          bAllDigits = FALSE;
250          break;
251        case L'.':
252        case L',':
253          if (!bDot) {
254            if (bDigitExist) {
255              swDigits += L'.';
256            } else {
257              swDigits += L'0';
258              swDigits += L'.';
259              bDigitExist = TRUE;
260            }
261
262            bDot = TRUE;
263            break;
264          }
265        case 'e':
266        case 'E':
267          if (!bKXJS) {
268            p++;
269            c = *p;
270            if (c == '+' || c == '-') {
271              bKXJS = TRUE;
272              swDigits += 'e';
273              swDigits += c;
274            }
275            break;
276          }
277        case L'-':
278          if (!bDigitExist && !bSign) {
279            swDigits += c;
280            bSign = TRUE;
281            break;
282          }
283        default:
284          bAllDigits = FALSE;
285
286          if (p != pStart && !bDot && bDigitExist) {
287            swDigits += L'.';
288            bDot = TRUE;
289          } else {
290            bDot = FALSE;
291            bDigitExist = FALSE;
292            swDigits = L"";
293          }
294          break;
295      }
296    }
297
298    p++;
299  }
300
301  if (swDigits.GetLength() > 0 && swDigits.GetLength() < 17) {
302    CFX_ByteString sDigits = swDigits.UTF8Encode();
303
304    if (bKXJS) {
305      dRet = atof(sDigits);
306    } else {
307      if (bDot) {
308        char* pStopString;
309        dRet = ::strtod(sDigits, &pStopString);
310      } else {
311        dRet = atol(sDigits);
312      }
313    }
314  }
315
316  return dRet;
317}
318
319double CJS_PublicMethods::ParseStringToNumber(const FX_WCHAR* swSource) {
320  FX_BOOL bAllDigits = FALSE;
321  FX_BOOL bDot = FALSE;
322  FX_BOOL bSign = FALSE;
323  FX_BOOL bKXJS = FALSE;
324
325  return ParseNumber(swSource, bAllDigits, bDot, bSign, bKXJS);
326}
327
328FX_BOOL CJS_PublicMethods::ConvertStringToNumber(const FX_WCHAR* swSource,
329                                                 double& dRet,
330                                                 FX_BOOL& bDot) {
331  FX_BOOL bAllDigits = FALSE;
332  FX_BOOL bSign = FALSE;
333  FX_BOOL bKXJS = FALSE;
334
335  dRet = ParseNumber(swSource, bAllDigits, bDot, bSign, bKXJS);
336
337  return bAllDigits;
338}
339
340CJS_Array CJS_PublicMethods::AF_MakeArrayFromList(CJS_Runtime* pRuntime,
341                                                  CJS_Value val) {
342  CJS_Array StrArray(pRuntime);
343  if (val.IsArrayObject()) {
344    val.ConvertToArray(StrArray);
345    return StrArray;
346  }
347  CFX_WideString wsStr = val.ToCFXWideString();
348  CFX_ByteString t = CFX_ByteString::FromUnicode(wsStr);
349  const char* p = (const char*)t;
350
351  int ch = ',';
352  int nIndex = 0;
353
354  while (*p) {
355    const char* pTemp = strchr(p, ch);
356    if (!pTemp) {
357      StrArray.SetElement(nIndex, CJS_Value(pRuntime, StrTrim(p).c_str()));
358      break;
359    }
360
361    char* pSub = new char[pTemp - p + 1];
362    strncpy(pSub, p, pTemp - p);
363    *(pSub + (pTemp - p)) = '\0';
364
365    StrArray.SetElement(nIndex, CJS_Value(pRuntime, StrTrim(pSub).c_str()));
366    delete[] pSub;
367
368    nIndex++;
369    p = ++pTemp;
370  }
371  return StrArray;
372}
373
374int CJS_PublicMethods::ParseStringInteger(const CFX_WideString& string,
375                                          int nStart,
376                                          int& nSkip,
377                                          int nMaxStep) {
378  int nRet = 0;
379  nSkip = 0;
380  for (int i = nStart, sz = string.GetLength(); i < sz; i++) {
381    if (i - nStart > 10)
382      break;
383
384    FX_WCHAR c = string.GetAt(i);
385    if (!FXSYS_iswdigit(c))
386      break;
387
388    nRet = nRet * 10 + FXSYS_toDecimalDigitWide(c);
389    nSkip = i - nStart + 1;
390    if (nSkip >= nMaxStep)
391      break;
392  }
393
394  return nRet;
395}
396
397CFX_WideString CJS_PublicMethods::ParseStringString(
398    const CFX_WideString& string,
399    int nStart,
400    int& nSkip) {
401  CFX_WideString swRet;
402  nSkip = 0;
403  for (int i = nStart, sz = string.GetLength(); i < sz; i++) {
404    FX_WCHAR c = string.GetAt(i);
405    if (!FXSYS_iswdigit(c))
406      break;
407
408    swRet += c;
409    nSkip = i - nStart + 1;
410  }
411
412  return swRet;
413}
414
415double CJS_PublicMethods::ParseNormalDate(const CFX_WideString& value,
416                                          bool* bWrongFormat) {
417  double dt = JS_GetDateTime();
418
419  int nYear = JS_GetYearFromTime(dt);
420  int nMonth = JS_GetMonthFromTime(dt) + 1;
421  int nDay = JS_GetDayFromTime(dt);
422  int nHour = JS_GetHourFromTime(dt);
423  int nMin = JS_GetMinFromTime(dt);
424  int nSec = JS_GetSecFromTime(dt);
425
426  int number[3];
427
428  int nSkip = 0;
429  int nLen = value.GetLength();
430  int nIndex = 0;
431  int i = 0;
432  while (i < nLen) {
433    if (nIndex > 2)
434      break;
435
436    FX_WCHAR c = value.GetAt(i);
437    if (FXSYS_iswdigit(c)) {
438      number[nIndex++] = ParseStringInteger(value, i, nSkip, 4);
439      i += nSkip;
440    } else {
441      i++;
442    }
443  }
444
445  if (nIndex == 2) {
446    // case2: month/day
447    // case3: day/month
448    if ((number[0] >= 1 && number[0] <= 12) &&
449        (number[1] >= 1 && number[1] <= 31)) {
450      nMonth = number[0];
451      nDay = number[1];
452    } else if ((number[0] >= 1 && number[0] <= 31) &&
453               (number[1] >= 1 && number[1] <= 12)) {
454      nDay = number[0];
455      nMonth = number[1];
456    }
457
458    if (bWrongFormat)
459      *bWrongFormat = false;
460  } else if (nIndex == 3) {
461    // case1: year/month/day
462    // case2: month/day/year
463    // case3: day/month/year
464
465    if (number[0] > 12 && (number[1] >= 1 && number[1] <= 12) &&
466        (number[2] >= 1 && number[2] <= 31)) {
467      nYear = number[0];
468      nMonth = number[1];
469      nDay = number[2];
470    } else if ((number[0] >= 1 && number[0] <= 12) &&
471               (number[1] >= 1 && number[1] <= 31) && number[2] > 31) {
472      nMonth = number[0];
473      nDay = number[1];
474      nYear = number[2];
475    } else if ((number[0] >= 1 && number[0] <= 31) &&
476               (number[1] >= 1 && number[1] <= 12) && number[2] > 31) {
477      nDay = number[0];
478      nMonth = number[1];
479      nYear = number[2];
480    }
481
482    if (bWrongFormat)
483      *bWrongFormat = false;
484  } else {
485    if (bWrongFormat)
486      *bWrongFormat = true;
487    return dt;
488  }
489
490  CFX_WideString swTemp;
491  swTemp.Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear, nHour, nMin, nSec);
492  return JS_DateParse(swTemp.c_str());
493}
494
495double CJS_PublicMethods::MakeRegularDate(const CFX_WideString& value,
496                                          const CFX_WideString& format,
497                                          bool* bWrongFormat) {
498  double dt = JS_GetDateTime();
499
500  if (format.IsEmpty() || value.IsEmpty())
501    return dt;
502
503  int nYear = JS_GetYearFromTime(dt);
504  int nMonth = JS_GetMonthFromTime(dt) + 1;
505  int nDay = JS_GetDayFromTime(dt);
506  int nHour = JS_GetHourFromTime(dt);
507  int nMin = JS_GetMinFromTime(dt);
508  int nSec = JS_GetSecFromTime(dt);
509
510  int nYearSub = 99;  // nYear - 2000;
511
512  FX_BOOL bPm = FALSE;
513  FX_BOOL bExit = FALSE;
514  bool bBadFormat = false;
515
516  int i = 0;
517  int j = 0;
518
519  while (i < format.GetLength()) {
520    if (bExit)
521      break;
522
523    FX_WCHAR c = format.GetAt(i);
524    switch (c) {
525      case ':':
526      case '.':
527      case '-':
528      case '\\':
529      case '/':
530        i++;
531        j++;
532        break;
533
534      case 'y':
535      case 'm':
536      case 'd':
537      case 'H':
538      case 'h':
539      case 'M':
540      case 's':
541      case 't': {
542        int oldj = j;
543        int nSkip = 0;
544        int remaining = format.GetLength() - i - 1;
545
546        if (remaining == 0 || format.GetAt(i + 1) != c) {
547          switch (c) {
548            case 'y':
549              i++;
550              j++;
551              break;
552            case 'm':
553              nMonth = ParseStringInteger(value, j, nSkip, 2);
554              i++;
555              j += nSkip;
556              break;
557            case 'd':
558              nDay = ParseStringInteger(value, j, nSkip, 2);
559              i++;
560              j += nSkip;
561              break;
562            case 'H':
563              nHour = ParseStringInteger(value, j, nSkip, 2);
564              i++;
565              j += nSkip;
566              break;
567            case 'h':
568              nHour = ParseStringInteger(value, j, nSkip, 2);
569              i++;
570              j += nSkip;
571              break;
572            case 'M':
573              nMin = ParseStringInteger(value, j, nSkip, 2);
574              i++;
575              j += nSkip;
576              break;
577            case 's':
578              nSec = ParseStringInteger(value, j, nSkip, 2);
579              i++;
580              j += nSkip;
581              break;
582            case 't':
583              bPm = (j < value.GetLength() && value.GetAt(j) == 'p');
584              i++;
585              j++;
586              break;
587          }
588        } else if (remaining == 1 || format.GetAt(i + 2) != c) {
589          switch (c) {
590            case 'y':
591              nYear = ParseStringInteger(value, j, nSkip, 4);
592              i += 2;
593              j += nSkip;
594              break;
595            case 'm':
596              nMonth = ParseStringInteger(value, j, nSkip, 2);
597              i += 2;
598              j += nSkip;
599              break;
600            case 'd':
601              nDay = ParseStringInteger(value, j, nSkip, 2);
602              i += 2;
603              j += nSkip;
604              break;
605            case 'H':
606              nHour = ParseStringInteger(value, j, nSkip, 2);
607              i += 2;
608              j += nSkip;
609              break;
610            case 'h':
611              nHour = ParseStringInteger(value, j, nSkip, 2);
612              i += 2;
613              j += nSkip;
614              break;
615            case 'M':
616              nMin = ParseStringInteger(value, j, nSkip, 2);
617              i += 2;
618              j += nSkip;
619              break;
620            case 's':
621              nSec = ParseStringInteger(value, j, nSkip, 2);
622              i += 2;
623              j += nSkip;
624              break;
625            case 't':
626              bPm = (j + 1 < value.GetLength() && value.GetAt(j) == 'p' &&
627                     value.GetAt(j + 1) == 'm');
628              i += 2;
629              j += 2;
630              break;
631          }
632        } else if (remaining == 2 || format.GetAt(i + 3) != c) {
633          switch (c) {
634            case 'm': {
635              CFX_WideString sMonth = ParseStringString(value, j, nSkip);
636              FX_BOOL bFind = FALSE;
637              for (int m = 0; m < 12; m++) {
638                if (sMonth.CompareNoCase(months[m]) == 0) {
639                  nMonth = m + 1;
640                  i += 3;
641                  j += nSkip;
642                  bFind = TRUE;
643                  break;
644                }
645              }
646
647              if (!bFind) {
648                nMonth = ParseStringInteger(value, j, nSkip, 3);
649                i += 3;
650                j += nSkip;
651              }
652            } break;
653            case 'y':
654              break;
655            default:
656              i += 3;
657              j += 3;
658              break;
659          }
660        } else if (remaining == 3 || format.GetAt(i + 4) != c) {
661          switch (c) {
662            case 'y':
663              nYear = ParseStringInteger(value, j, nSkip, 4);
664              j += nSkip;
665              i += 4;
666              break;
667            case 'm': {
668              FX_BOOL bFind = FALSE;
669
670              CFX_WideString sMonth = ParseStringString(value, j, nSkip);
671              sMonth.MakeLower();
672
673              for (int m = 0; m < 12; m++) {
674                CFX_WideString sFullMonths = fullmonths[m];
675                sFullMonths.MakeLower();
676
677                if (sFullMonths.Find(sMonth.c_str(), 0) != -1) {
678                  nMonth = m + 1;
679                  i += 4;
680                  j += nSkip;
681                  bFind = TRUE;
682                  break;
683                }
684              }
685
686              if (!bFind) {
687                nMonth = ParseStringInteger(value, j, nSkip, 4);
688                i += 4;
689                j += nSkip;
690              }
691            } break;
692            default:
693              i += 4;
694              j += 4;
695              break;
696          }
697        } else {
698          if (j >= value.GetLength() || format.GetAt(i) != value.GetAt(j)) {
699            bBadFormat = true;
700            bExit = TRUE;
701          }
702          i++;
703          j++;
704        }
705
706        if (oldj == j) {
707          bBadFormat = true;
708          bExit = TRUE;
709        }
710      }
711
712      break;
713      default:
714        if (value.GetLength() <= j) {
715          bExit = TRUE;
716        } else if (format.GetAt(i) != value.GetAt(j)) {
717          bBadFormat = true;
718          bExit = TRUE;
719        }
720
721        i++;
722        j++;
723        break;
724    }
725  }
726
727  if (bPm)
728    nHour += 12;
729
730  if (nYear >= 0 && nYear <= nYearSub)
731    nYear += 2000;
732
733  if (nMonth < 1 || nMonth > 12)
734    bBadFormat = true;
735
736  if (nDay < 1 || nDay > 31)
737    bBadFormat = true;
738
739  if (nHour < 0 || nHour > 24)
740    bBadFormat = true;
741
742  if (nMin < 0 || nMin > 60)
743    bBadFormat = true;
744
745  if (nSec < 0 || nSec > 60)
746    bBadFormat = true;
747
748  double dRet = 0;
749
750  if (bBadFormat) {
751    dRet = ParseNormalDate(value, &bBadFormat);
752  } else {
753    dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay),
754                       JS_MakeTime(nHour, nMin, nSec, 0));
755
756    if (JS_PortIsNan(dRet)) {
757      dRet = JS_DateParse(value.c_str());
758    }
759  }
760
761  if (JS_PortIsNan(dRet)) {
762    dRet = ParseNormalDate(value, &bBadFormat);
763  }
764
765  if (bWrongFormat)
766    *bWrongFormat = bBadFormat;
767  return dRet;
768}
769
770CFX_WideString CJS_PublicMethods::MakeFormatDate(double dDate,
771                                                 const CFX_WideString& format) {
772  CFX_WideString sRet = L"", sPart = L"";
773
774  int nYear = JS_GetYearFromTime(dDate);
775  int nMonth = JS_GetMonthFromTime(dDate) + 1;
776  int nDay = JS_GetDayFromTime(dDate);
777  int nHour = JS_GetHourFromTime(dDate);
778  int nMin = JS_GetMinFromTime(dDate);
779  int nSec = JS_GetSecFromTime(dDate);
780
781  int i = 0;
782  while (i < format.GetLength()) {
783    FX_WCHAR c = format.GetAt(i);
784    int remaining = format.GetLength() - i - 1;
785    sPart = L"";
786    switch (c) {
787      case 'y':
788      case 'm':
789      case 'd':
790      case 'H':
791      case 'h':
792      case 'M':
793      case 's':
794      case 't':
795        if (remaining == 0 || format.GetAt(i + 1) != c) {
796          switch (c) {
797            case 'y':
798              sPart += c;
799              break;
800            case 'm':
801              sPart.Format(L"%d", nMonth);
802              break;
803            case 'd':
804              sPart.Format(L"%d", nDay);
805              break;
806            case 'H':
807              sPart.Format(L"%d", nHour);
808              break;
809            case 'h':
810              sPart.Format(L"%d", nHour > 12 ? nHour - 12 : nHour);
811              break;
812            case 'M':
813              sPart.Format(L"%d", nMin);
814              break;
815            case 's':
816              sPart.Format(L"%d", nSec);
817              break;
818            case 't':
819              sPart += nHour > 12 ? 'p' : 'a';
820              break;
821          }
822          i++;
823        } else if (remaining == 1 || format.GetAt(i + 2) != c) {
824          switch (c) {
825            case 'y':
826              sPart.Format(L"%02d", nYear - (nYear / 100) * 100);
827              break;
828            case 'm':
829              sPart.Format(L"%02d", nMonth);
830              break;
831            case 'd':
832              sPart.Format(L"%02d", nDay);
833              break;
834            case 'H':
835              sPart.Format(L"%02d", nHour);
836              break;
837            case 'h':
838              sPart.Format(L"%02d", nHour > 12 ? nHour - 12 : nHour);
839              break;
840            case 'M':
841              sPart.Format(L"%02d", nMin);
842              break;
843            case 's':
844              sPart.Format(L"%02d", nSec);
845              break;
846            case 't':
847              sPart = nHour > 12 ? L"pm" : L"am";
848              break;
849          }
850          i += 2;
851        } else if (remaining == 2 || format.GetAt(i + 3) != c) {
852          switch (c) {
853            case 'm':
854              i += 3;
855              if (nMonth > 0 && nMonth <= 12)
856                sPart += months[nMonth - 1];
857              break;
858            default:
859              i += 3;
860              sPart += c;
861              sPart += c;
862              sPart += c;
863              break;
864          }
865        } else if (remaining == 3 || format.GetAt(i + 4) != c) {
866          switch (c) {
867            case 'y':
868              sPart.Format(L"%04d", nYear);
869              i += 4;
870              break;
871            case 'm':
872              i += 4;
873              if (nMonth > 0 && nMonth <= 12)
874                sPart += fullmonths[nMonth - 1];
875              break;
876            default:
877              i += 4;
878              sPart += c;
879              sPart += c;
880              sPart += c;
881              sPart += c;
882              break;
883          }
884        } else {
885          i++;
886          sPart += c;
887        }
888        break;
889      default:
890        i++;
891        sPart += c;
892        break;
893    }
894
895    sRet += sPart;
896  }
897
898  return sRet;
899}
900
901/* -------------------------------------------------------------------------- */
902
903// function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency,
904// bCurrencyPrepend)
905FX_BOOL CJS_PublicMethods::AFNumber_Format(IJS_Context* cc,
906                                           const std::vector<CJS_Value>& params,
907                                           CJS_Value& vRet,
908                                           CFX_WideString& sError) {
909#if _FX_OS_ != _FX_ANDROID_
910  CJS_Context* pContext = (CJS_Context*)cc;
911  if (params.size() != 6) {
912    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
913    return FALSE;
914  }
915
916  CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
917  CJS_EventHandler* pEvent = pContext->GetEventHandler();
918  if (!pEvent->m_pValue)
919    return FALSE;
920
921  CFX_WideString& Value = pEvent->Value();
922  CFX_ByteString strValue = StrTrim(CFX_ByteString::FromUnicode(Value));
923  if (strValue.IsEmpty())
924    return TRUE;
925
926  int iDec = params[0].ToInt();
927  int iSepStyle = params[1].ToInt();
928  int iNegStyle = params[2].ToInt();
929  // params[3] is iCurrStyle, it's not used.
930  std::wstring wstrCurrency(params[4].ToCFXWideString().c_str());
931  FX_BOOL bCurrencyPrepend = params[5].ToBool();
932
933  if (iDec < 0)
934    iDec = -iDec;
935
936  if (iSepStyle < 0 || iSepStyle > 3)
937    iSepStyle = 0;
938
939  if (iNegStyle < 0 || iNegStyle > 3)
940    iNegStyle = 0;
941
942  //////////////////////////////////////////////////////
943  // for processing decimal places
944  strValue.Replace(",", ".");
945  double dValue = atof(strValue);
946  if (iDec > 0)
947    dValue += DOUBLE_CORRECT;
948
949  int iDec2;
950  int iNegative = 0;
951
952  strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
953  if (strValue.IsEmpty()) {
954    dValue = 0;
955    strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
956    if (strValue.IsEmpty()) {
957      strValue = "0";
958      iDec2 = 1;
959    }
960  }
961
962  if (iDec2 < 0) {
963    for (int iNum = 0; iNum < abs(iDec2); iNum++) {
964      strValue = "0" + strValue;
965    }
966    iDec2 = 0;
967  }
968  int iMax = strValue.GetLength();
969  if (iDec2 > iMax) {
970    for (int iNum = 0; iNum <= iDec2 - iMax; iNum++) {
971      strValue += "0";
972    }
973    iMax = iDec2 + 1;
974  }
975  ///////////////////////////////////////////////////////
976  // for processing seperator style
977  if (iDec2 < iMax) {
978    if (iSepStyle == 0 || iSepStyle == 1) {
979      strValue.Insert(iDec2, '.');
980      iMax++;
981    } else if (iSepStyle == 2 || iSepStyle == 3) {
982      strValue.Insert(iDec2, ',');
983      iMax++;
984    }
985
986    if (iDec2 == 0)
987      strValue.Insert(iDec2, '0');
988  }
989  if (iSepStyle == 0 || iSepStyle == 2) {
990    char cSeperator;
991    if (iSepStyle == 0)
992      cSeperator = ',';
993    else
994      cSeperator = '.';
995
996    for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3) {
997      strValue.Insert(iDecPositive, cSeperator);
998      iMax++;
999    }
1000  }
1001
1002  //////////////////////////////////////////////////////////////////////
1003  // for processing currency string
1004
1005  Value = CFX_WideString::FromLocal(strValue);
1006  std::wstring strValue2 = Value.c_str();
1007
1008  if (bCurrencyPrepend)
1009    strValue2 = wstrCurrency + strValue2;
1010  else
1011    strValue2 = strValue2 + wstrCurrency;
1012
1013  /////////////////////////////////////////////////////////////////////////
1014  // for processing negative style
1015  if (iNegative) {
1016    if (iNegStyle == 0) {
1017      strValue2.insert(0, L"-");
1018    }
1019    if (iNegStyle == 2 || iNegStyle == 3) {
1020      strValue2.insert(0, L"(");
1021      strValue2.insert(strValue2.length(), L")");
1022    }
1023    if (iNegStyle == 1 || iNegStyle == 3) {
1024      if (Field* fTarget = pEvent->Target_Field()) {
1025        CJS_Array arColor(pRuntime);
1026        CJS_Value vColElm(pRuntime);
1027        vColElm = L"RGB";
1028        arColor.SetElement(0, vColElm);
1029        vColElm = 1;
1030        arColor.SetElement(1, vColElm);
1031        vColElm = 0;
1032        arColor.SetElement(2, vColElm);
1033
1034        arColor.SetElement(3, vColElm);
1035
1036        CJS_PropValue vProp(pRuntime);
1037        vProp.StartGetting();
1038        vProp << arColor;
1039        vProp.StartSetting();
1040        fTarget->textColor(cc, vProp, sError);  // red
1041      }
1042    }
1043  } else {
1044    if (iNegStyle == 1 || iNegStyle == 3) {
1045      if (Field* fTarget = pEvent->Target_Field()) {
1046        CJS_Array arColor(pRuntime);
1047        CJS_Value vColElm(pRuntime);
1048        vColElm = L"RGB";
1049        arColor.SetElement(0, vColElm);
1050        vColElm = 0;
1051        arColor.SetElement(1, vColElm);
1052        arColor.SetElement(2, vColElm);
1053        arColor.SetElement(3, vColElm);
1054
1055        CJS_PropValue vProp(pRuntime);
1056        vProp.StartGetting();
1057        fTarget->textColor(cc, vProp, sError);
1058
1059        CJS_Array aProp(pRuntime);
1060        vProp.ConvertToArray(aProp);
1061
1062        CPWL_Color crProp;
1063        CPWL_Color crColor;
1064        color::ConvertArrayToPWLColor(aProp, crProp);
1065        color::ConvertArrayToPWLColor(arColor, crColor);
1066
1067        if (crColor != crProp) {
1068          CJS_PropValue vProp2(pRuntime);
1069          vProp2.StartGetting();
1070          vProp2 << arColor;
1071          vProp2.StartSetting();
1072          fTarget->textColor(cc, vProp2, sError);
1073        }
1074      }
1075    }
1076  }
1077  Value = strValue2.c_str();
1078#endif
1079  return TRUE;
1080}
1081
1082// function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency,
1083// bCurrencyPrepend)
1084FX_BOOL CJS_PublicMethods::AFNumber_Keystroke(
1085    IJS_Context* cc,
1086    const std::vector<CJS_Value>& params,
1087    CJS_Value& vRet,
1088    CFX_WideString& sError) {
1089  CJS_Context* pContext = (CJS_Context*)cc;
1090  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1091
1092  if (params.size() < 2)
1093    return FALSE;
1094  int iSepStyle = params[1].ToInt();
1095
1096  if (iSepStyle < 0 || iSepStyle > 3)
1097    iSepStyle = 0;
1098  if (!pEvent->m_pValue)
1099    return FALSE;
1100  CFX_WideString& val = pEvent->Value();
1101  CFX_WideString& w_strChange = pEvent->Change();
1102  CFX_WideString w_strValue = val;
1103
1104  if (pEvent->WillCommit()) {
1105    CFX_WideString wstrChange = w_strChange;
1106    CFX_WideString wstrValue = StrLTrim(w_strValue.c_str());
1107    if (wstrValue.IsEmpty())
1108      return TRUE;
1109
1110    CFX_WideString swTemp = wstrValue;
1111    swTemp.Replace(L",", L".");
1112    if (!IsNumber(swTemp.c_str())) {
1113      pEvent->Rc() = FALSE;
1114      sError = JSGetStringFromID(pContext, IDS_STRING_JSAFNUMBER_KEYSTROKE);
1115      Alert(pContext, sError.c_str());
1116      return TRUE;
1117    }
1118    return TRUE;  // it happens after the last keystroke and before validating,
1119  }
1120
1121  std::wstring w_strValue2 = w_strValue.c_str();
1122  std::wstring w_strChange2 = w_strChange.c_str();
1123  std::wstring w_strSelected;
1124  if (-1 != pEvent->SelStart())
1125    w_strSelected = w_strValue2.substr(pEvent->SelStart(),
1126                                       (pEvent->SelEnd() - pEvent->SelStart()));
1127  bool bHasSign = (w_strValue2.find('-') != std::wstring::npos) &&
1128                  (w_strSelected.find('-') == std::wstring::npos);
1129  if (bHasSign) {
1130    // can't insert "change" in front to sign postion.
1131    if (pEvent->SelStart() == 0) {
1132      FX_BOOL& bRc = pEvent->Rc();
1133      bRc = FALSE;
1134      return TRUE;
1135    }
1136  }
1137
1138  char cSep = L'.';
1139
1140  switch (iSepStyle) {
1141    case 0:
1142    case 1:
1143      cSep = L'.';
1144      break;
1145    case 2:
1146    case 3:
1147      cSep = L',';
1148      break;
1149  }
1150
1151  bool bHasSep = (w_strValue2.find(cSep) != std::wstring::npos);
1152  for (std::wstring::iterator it = w_strChange2.begin();
1153       it != w_strChange2.end(); it++) {
1154    if (*it == cSep) {
1155      if (bHasSep) {
1156        FX_BOOL& bRc = pEvent->Rc();
1157        bRc = FALSE;
1158        return TRUE;
1159      }
1160      bHasSep = TRUE;
1161      continue;
1162    }
1163    if (*it == L'-') {
1164      if (bHasSign) {
1165        FX_BOOL& bRc = pEvent->Rc();
1166        bRc = FALSE;
1167        return TRUE;
1168      }
1169      // sign's position is not correct
1170      if (it != w_strChange2.begin()) {
1171        FX_BOOL& bRc = pEvent->Rc();
1172        bRc = FALSE;
1173        return TRUE;
1174      }
1175      if (pEvent->SelStart() != 0) {
1176        FX_BOOL& bRc = pEvent->Rc();
1177        bRc = FALSE;
1178        return TRUE;
1179      }
1180      bHasSign = TRUE;
1181      continue;
1182    }
1183
1184    if (!FXSYS_iswdigit(*it)) {
1185      FX_BOOL& bRc = pEvent->Rc();
1186      bRc = FALSE;
1187      return TRUE;
1188    }
1189  }
1190
1191  std::wstring w_prefix = w_strValue2.substr(0, pEvent->SelStart());
1192  std::wstring w_postfix;
1193  if (pEvent->SelEnd() < (int)w_strValue2.length())
1194    w_postfix = w_strValue2.substr(pEvent->SelEnd());
1195  w_strValue2 = w_prefix + w_strChange2 + w_postfix;
1196  w_strValue = w_strValue2.c_str();
1197  val = w_strValue;
1198  return TRUE;
1199}
1200
1201// function AFPercent_Format(nDec, sepStyle)
1202FX_BOOL CJS_PublicMethods::AFPercent_Format(
1203    IJS_Context* cc,
1204    const std::vector<CJS_Value>& params,
1205    CJS_Value& vRet,
1206    CFX_WideString& sError) {
1207#if _FX_OS_ != _FX_ANDROID_
1208  CJS_Context* pContext = (CJS_Context*)cc;
1209  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1210
1211  if (params.size() != 2) {
1212    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1213    return FALSE;
1214  }
1215  if (!pEvent->m_pValue)
1216    return FALSE;
1217
1218  CFX_WideString& Value = pEvent->Value();
1219  CFX_ByteString strValue = StrTrim(CFX_ByteString::FromUnicode(Value));
1220  if (strValue.IsEmpty())
1221    return TRUE;
1222
1223  int iDec = params[0].ToInt();
1224  if (iDec < 0)
1225    iDec = -iDec;
1226
1227  int iSepStyle = params[1].ToInt();
1228  if (iSepStyle < 0 || iSepStyle > 3)
1229    iSepStyle = 0;
1230
1231  //////////////////////////////////////////////////////
1232  // for processing decimal places
1233  double dValue = atof(strValue);
1234  dValue *= 100;
1235  if (iDec > 0)
1236    dValue += DOUBLE_CORRECT;
1237
1238  int iDec2;
1239  int iNegative = 0;
1240  strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
1241  if (strValue.IsEmpty()) {
1242    dValue = 0;
1243    strValue = fcvt(dValue, iDec, &iDec2, &iNegative);
1244  }
1245
1246  if (iDec2 < 0) {
1247    for (int iNum = 0; iNum < abs(iDec2); iNum++) {
1248      strValue = "0" + strValue;
1249    }
1250    iDec2 = 0;
1251  }
1252  int iMax = strValue.GetLength();
1253  if (iDec2 > iMax) {
1254    for (int iNum = 0; iNum <= iDec2 - iMax; iNum++) {
1255      strValue += "0";
1256    }
1257    iMax = iDec2 + 1;
1258  }
1259  ///////////////////////////////////////////////////////
1260  // for processing seperator style
1261  if (iDec2 < iMax) {
1262    if (iSepStyle == 0 || iSepStyle == 1) {
1263      strValue.Insert(iDec2, '.');
1264      iMax++;
1265    } else if (iSepStyle == 2 || iSepStyle == 3) {
1266      strValue.Insert(iDec2, ',');
1267      iMax++;
1268    }
1269
1270    if (iDec2 == 0)
1271      strValue.Insert(iDec2, '0');
1272  }
1273  if (iSepStyle == 0 || iSepStyle == 2) {
1274    char cSeperator;
1275    if (iSepStyle == 0)
1276      cSeperator = ',';
1277    else
1278      cSeperator = '.';
1279
1280    for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3) {
1281      strValue.Insert(iDecPositive, cSeperator);
1282      iMax++;
1283    }
1284  }
1285  ////////////////////////////////////////////////////////////////////
1286  // negative mark
1287  if (iNegative)
1288    strValue = "-" + strValue;
1289  strValue += "%";
1290  Value = CFX_WideString::FromLocal(strValue);
1291#endif
1292  return TRUE;
1293}
1294// AFPercent_Keystroke(nDec, sepStyle)
1295FX_BOOL CJS_PublicMethods::AFPercent_Keystroke(
1296    IJS_Context* cc,
1297    const std::vector<CJS_Value>& params,
1298    CJS_Value& vRet,
1299    CFX_WideString& sError) {
1300  return AFNumber_Keystroke(cc, params, vRet, sError);
1301}
1302
1303// function AFDate_FormatEx(cFormat)
1304FX_BOOL CJS_PublicMethods::AFDate_FormatEx(IJS_Context* cc,
1305                                           const std::vector<CJS_Value>& params,
1306                                           CJS_Value& vRet,
1307                                           CFX_WideString& sError) {
1308  CJS_Context* pContext = (CJS_Context*)cc;
1309  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1310
1311  if (params.size() != 1) {
1312    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1313    return FALSE;
1314  }
1315  if (!pEvent->m_pValue)
1316    return FALSE;
1317
1318  CFX_WideString& val = pEvent->Value();
1319  CFX_WideString strValue = val;
1320  if (strValue.IsEmpty())
1321    return TRUE;
1322
1323  CFX_WideString sFormat = params[0].ToCFXWideString();
1324  double dDate = 0.0f;
1325
1326  if (strValue.Find(L"GMT") != -1) {
1327    // for GMT format time
1328    // such as "Tue Aug 11 14:24:16 GMT+08002009"
1329    dDate = MakeInterDate(strValue);
1330  } else {
1331    dDate = MakeRegularDate(strValue, sFormat, nullptr);
1332  }
1333
1334  if (JS_PortIsNan(dDate)) {
1335    CFX_WideString swMsg;
1336    swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSPARSEDATE).c_str(),
1337                 sFormat.c_str());
1338    Alert(pContext, swMsg.c_str());
1339    return FALSE;
1340  }
1341
1342  val = MakeFormatDate(dDate, sFormat);
1343  return TRUE;
1344}
1345
1346double CJS_PublicMethods::MakeInterDate(CFX_WideString strValue) {
1347  int nHour;
1348  int nMin;
1349  int nSec;
1350  int nYear;
1351  int nMonth;
1352  int nDay;
1353
1354  CFX_WideStringArray wsArray;
1355  CFX_WideString sMonth = L"";
1356  CFX_WideString sTemp = L"";
1357  int nSize = strValue.GetLength();
1358
1359  for (int i = 0; i < nSize; i++) {
1360    FX_WCHAR c = strValue.GetAt(i);
1361    if (c == L' ' || c == L':') {
1362      wsArray.Add(sTemp);
1363      sTemp = L"";
1364      continue;
1365    }
1366
1367    sTemp += c;
1368  }
1369
1370  wsArray.Add(sTemp);
1371  if (wsArray.GetSize() != 8)
1372    return 0;
1373
1374  sTemp = wsArray[1];
1375  if (sTemp.Compare(L"Jan") == 0)
1376    nMonth = 1;
1377  if (sTemp.Compare(L"Feb") == 0)
1378    nMonth = 2;
1379  if (sTemp.Compare(L"Mar") == 0)
1380    nMonth = 3;
1381  if (sTemp.Compare(L"Apr") == 0)
1382    nMonth = 4;
1383  if (sTemp.Compare(L"May") == 0)
1384    nMonth = 5;
1385  if (sTemp.Compare(L"Jun") == 0)
1386    nMonth = 6;
1387  if (sTemp.Compare(L"Jul") == 0)
1388    nMonth = 7;
1389  if (sTemp.Compare(L"Aug") == 0)
1390    nMonth = 8;
1391  if (sTemp.Compare(L"Sep") == 0)
1392    nMonth = 9;
1393  if (sTemp.Compare(L"Oct") == 0)
1394    nMonth = 10;
1395  if (sTemp.Compare(L"Nov") == 0)
1396    nMonth = 11;
1397  if (sTemp.Compare(L"Dec") == 0)
1398    nMonth = 12;
1399
1400  nDay = (int)ParseStringToNumber(wsArray[2].c_str());
1401  nHour = (int)ParseStringToNumber(wsArray[3].c_str());
1402  nMin = (int)ParseStringToNumber(wsArray[4].c_str());
1403  nSec = (int)ParseStringToNumber(wsArray[5].c_str());
1404  nYear = (int)ParseStringToNumber(wsArray[7].c_str());
1405
1406  double dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay),
1407                            JS_MakeTime(nHour, nMin, nSec, 0));
1408
1409  if (JS_PortIsNan(dRet)) {
1410    dRet = JS_DateParse(strValue.c_str());
1411  }
1412
1413  return dRet;
1414}
1415
1416// AFDate_KeystrokeEx(cFormat)
1417FX_BOOL CJS_PublicMethods::AFDate_KeystrokeEx(
1418    IJS_Context* cc,
1419    const std::vector<CJS_Value>& params,
1420    CJS_Value& vRet,
1421    CFX_WideString& sError) {
1422  CJS_Context* pContext = (CJS_Context*)cc;
1423  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1424
1425  if (params.size() != 1) {
1426    sError = L"AFDate_KeystrokeEx's parameters' size r not correct";
1427    return FALSE;
1428  }
1429
1430  if (pEvent->WillCommit()) {
1431    if (!pEvent->m_pValue)
1432      return FALSE;
1433    CFX_WideString strValue = pEvent->Value();
1434    if (strValue.IsEmpty())
1435      return TRUE;
1436
1437    CFX_WideString sFormat = params[0].ToCFXWideString();
1438    bool bWrongFormat = FALSE;
1439    double dRet = MakeRegularDate(strValue, sFormat, &bWrongFormat);
1440    if (bWrongFormat || JS_PortIsNan(dRet)) {
1441      CFX_WideString swMsg;
1442      swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSPARSEDATE).c_str(),
1443                   sFormat.c_str());
1444      Alert(pContext, swMsg.c_str());
1445      pEvent->Rc() = FALSE;
1446      return TRUE;
1447    }
1448  }
1449  return TRUE;
1450}
1451
1452FX_BOOL CJS_PublicMethods::AFDate_Format(IJS_Context* cc,
1453                                         const std::vector<CJS_Value>& params,
1454                                         CJS_Value& vRet,
1455                                         CFX_WideString& sError) {
1456  CJS_Context* pContext = (CJS_Context*)cc;
1457  if (params.size() != 1) {
1458    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1459    return FALSE;
1460  }
1461
1462  int iIndex = params[0].ToInt();
1463  const FX_WCHAR* cFormats[] = {L"m/d",
1464                                L"m/d/yy",
1465                                L"mm/dd/yy",
1466                                L"mm/yy",
1467                                L"d-mmm",
1468                                L"d-mmm-yy",
1469                                L"dd-mmm-yy",
1470                                L"yy-mm-dd",
1471                                L"mmm-yy",
1472                                L"mmmm-yy",
1473                                L"mmm d, yyyy",
1474                                L"mmmm d, yyyy",
1475                                L"m/d/yy h:MM tt",
1476                                L"m/d/yy HH:MM"};
1477
1478  if (iIndex < 0 || (static_cast<size_t>(iIndex) >= FX_ArraySize(cFormats)))
1479    iIndex = 0;
1480
1481  std::vector<CJS_Value> newParams;
1482  newParams.push_back(
1483      CJS_Value(CJS_Runtime::FromContext(cc), cFormats[iIndex]));
1484  return AFDate_FormatEx(cc, newParams, vRet, sError);
1485}
1486
1487// AFDate_KeystrokeEx(cFormat)
1488FX_BOOL CJS_PublicMethods::AFDate_Keystroke(
1489    IJS_Context* cc,
1490    const std::vector<CJS_Value>& params,
1491    CJS_Value& vRet,
1492    CFX_WideString& sError) {
1493  CJS_Context* pContext = (CJS_Context*)cc;
1494  if (params.size() != 1) {
1495    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1496    return FALSE;
1497  }
1498
1499  int iIndex = params[0].ToInt();
1500  const FX_WCHAR* cFormats[] = {L"m/d",
1501                                L"m/d/yy",
1502                                L"mm/dd/yy",
1503                                L"mm/yy",
1504                                L"d-mmm",
1505                                L"d-mmm-yy",
1506                                L"dd-mmm-yy",
1507                                L"yy-mm-dd",
1508                                L"mmm-yy",
1509                                L"mmmm-yy",
1510                                L"mmm d, yyyy",
1511                                L"mmmm d, yyyy",
1512                                L"m/d/yy h:MM tt",
1513                                L"m/d/yy HH:MM"};
1514
1515  if (iIndex < 0 || (static_cast<size_t>(iIndex) >= FX_ArraySize(cFormats)))
1516    iIndex = 0;
1517
1518  std::vector<CJS_Value> newParams;
1519  newParams.push_back(
1520      CJS_Value(CJS_Runtime::FromContext(cc), cFormats[iIndex]));
1521  return AFDate_KeystrokeEx(cc, newParams, vRet, sError);
1522}
1523
1524// function AFTime_Format(ptf)
1525FX_BOOL CJS_PublicMethods::AFTime_Format(IJS_Context* cc,
1526                                         const std::vector<CJS_Value>& params,
1527                                         CJS_Value& vRet,
1528                                         CFX_WideString& sError) {
1529  CJS_Context* pContext = (CJS_Context*)cc;
1530  if (params.size() != 1) {
1531    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1532    return FALSE;
1533  }
1534
1535  int iIndex = params[0].ToInt();
1536  const FX_WCHAR* cFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss",
1537                                L"h:MM:ss tt"};
1538
1539  if (iIndex < 0 || (static_cast<size_t>(iIndex) >= FX_ArraySize(cFormats)))
1540    iIndex = 0;
1541
1542  std::vector<CJS_Value> newParams;
1543  newParams.push_back(
1544      CJS_Value(CJS_Runtime::FromContext(cc), cFormats[iIndex]));
1545  return AFDate_FormatEx(cc, newParams, vRet, sError);
1546}
1547
1548FX_BOOL CJS_PublicMethods::AFTime_Keystroke(
1549    IJS_Context* cc,
1550    const std::vector<CJS_Value>& params,
1551    CJS_Value& vRet,
1552    CFX_WideString& sError) {
1553  CJS_Context* pContext = (CJS_Context*)cc;
1554  if (params.size() != 1) {
1555    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1556    return FALSE;
1557  }
1558
1559  int iIndex = params[0].ToInt();
1560  const FX_WCHAR* cFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss",
1561                                L"h:MM:ss tt"};
1562
1563  if (iIndex < 0 || (static_cast<size_t>(iIndex) >= FX_ArraySize(cFormats)))
1564    iIndex = 0;
1565
1566  std::vector<CJS_Value> newParams;
1567  newParams.push_back(
1568      CJS_Value(CJS_Runtime::FromContext(cc), cFormats[iIndex]));
1569  return AFDate_KeystrokeEx(cc, newParams, vRet, sError);
1570}
1571
1572FX_BOOL CJS_PublicMethods::AFTime_FormatEx(IJS_Context* cc,
1573                                           const std::vector<CJS_Value>& params,
1574                                           CJS_Value& vRet,
1575                                           CFX_WideString& sError) {
1576  return AFDate_FormatEx(cc, params, vRet, sError);
1577}
1578
1579FX_BOOL CJS_PublicMethods::AFTime_KeystrokeEx(
1580    IJS_Context* cc,
1581    const std::vector<CJS_Value>& params,
1582    CJS_Value& vRet,
1583    CFX_WideString& sError) {
1584  return AFDate_KeystrokeEx(cc, params, vRet, sError);
1585}
1586
1587// function AFSpecial_Format(psf)
1588FX_BOOL CJS_PublicMethods::AFSpecial_Format(
1589    IJS_Context* cc,
1590    const std::vector<CJS_Value>& params,
1591    CJS_Value& vRet,
1592    CFX_WideString& sError) {
1593  CJS_Context* pContext = (CJS_Context*)cc;
1594
1595  if (params.size() != 1) {
1596    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1597    return FALSE;
1598  }
1599
1600  std::string cFormat;
1601  int iIndex = params[0].ToInt();
1602
1603  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1604  if (!pEvent->m_pValue)
1605    return FALSE;
1606  CFX_WideString& Value = pEvent->Value();
1607  std::string strSrc = CFX_ByteString::FromUnicode(Value).c_str();
1608
1609  switch (iIndex) {
1610    case 0:
1611      cFormat = "99999";
1612      break;
1613    case 1:
1614      cFormat = "99999-9999";
1615      break;
1616    case 2: {
1617      std::string NumberStr;
1618      util::printx("9999999999", strSrc, NumberStr);
1619      if (NumberStr.length() >= 10)
1620        cFormat = "(999) 999-9999";
1621      else
1622        cFormat = "999-9999";
1623      break;
1624    }
1625    case 3:
1626      cFormat = "999-99-9999";
1627      break;
1628  }
1629
1630  std::string strDes;
1631  util::printx(cFormat, strSrc, strDes);
1632  Value = CFX_WideString::FromLocal(strDes.c_str());
1633  return TRUE;
1634}
1635
1636// function AFSpecial_KeystrokeEx(mask)
1637FX_BOOL CJS_PublicMethods::AFSpecial_KeystrokeEx(
1638    IJS_Context* cc,
1639    const std::vector<CJS_Value>& params,
1640    CJS_Value& vRet,
1641    CFX_WideString& sError) {
1642  CJS_Context* pContext = (CJS_Context*)cc;
1643  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1644
1645  if (params.size() < 1) {
1646    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1647    return FALSE;
1648  }
1649
1650  if (!pEvent->m_pValue)
1651    return FALSE;
1652  CFX_WideString& valEvent = pEvent->Value();
1653
1654  CFX_WideString wstrMask = params[0].ToCFXWideString();
1655  if (wstrMask.IsEmpty())
1656    return TRUE;
1657
1658  const size_t wstrMaskLen = wstrMask.GetLength();
1659  const std::wstring wstrValue = valEvent.c_str();
1660
1661  if (pEvent->WillCommit()) {
1662    if (wstrValue.empty())
1663      return TRUE;
1664    size_t iIndexMask = 0;
1665    for (const auto& w_Value : wstrValue) {
1666      if (!maskSatisfied(w_Value, wstrMask[iIndexMask]))
1667        break;
1668      iIndexMask++;
1669    }
1670
1671    if (iIndexMask != wstrMaskLen ||
1672        (iIndexMask != wstrValue.size() && wstrMaskLen != 0)) {
1673      Alert(
1674          pContext,
1675          JSGetStringFromID(pContext, IDS_STRING_JSAFNUMBER_KEYSTROKE).c_str());
1676      pEvent->Rc() = FALSE;
1677    }
1678    return TRUE;
1679  }
1680
1681  CFX_WideString& wideChange = pEvent->Change();
1682  std::wstring wChange = wideChange.c_str();
1683  if (wChange.empty())
1684    return TRUE;
1685
1686  int iIndexMask = pEvent->SelStart();
1687
1688  size_t combined_len = wstrValue.length() + wChange.length() -
1689                        (pEvent->SelEnd() - pEvent->SelStart());
1690  if (combined_len > wstrMaskLen) {
1691    Alert(pContext,
1692          JSGetStringFromID(pContext, IDS_STRING_JSPARAM_TOOLONG).c_str());
1693    pEvent->Rc() = FALSE;
1694    return TRUE;
1695  }
1696
1697  if (iIndexMask >= wstrMaskLen && (!wChange.empty())) {
1698    Alert(pContext,
1699          JSGetStringFromID(pContext, IDS_STRING_JSPARAM_TOOLONG).c_str());
1700    pEvent->Rc() = FALSE;
1701    return TRUE;
1702  }
1703
1704  for (std::wstring::iterator it = wChange.begin(); it != wChange.end(); it++) {
1705    if (iIndexMask >= wstrMaskLen) {
1706      Alert(pContext,
1707            JSGetStringFromID(pContext, IDS_STRING_JSPARAM_TOOLONG).c_str());
1708      pEvent->Rc() = FALSE;
1709      return TRUE;
1710    }
1711    wchar_t w_Mask = wstrMask[iIndexMask];
1712    if (!isReservedMaskChar(w_Mask)) {
1713      *it = w_Mask;
1714    }
1715    wchar_t w_Change = *it;
1716    if (!maskSatisfied(w_Change, w_Mask)) {
1717      pEvent->Rc() = FALSE;
1718      return TRUE;
1719    }
1720    iIndexMask++;
1721  }
1722
1723  wideChange = wChange.c_str();
1724  return TRUE;
1725}
1726
1727// function AFSpecial_Keystroke(psf)
1728FX_BOOL CJS_PublicMethods::AFSpecial_Keystroke(
1729    IJS_Context* cc,
1730    const std::vector<CJS_Value>& params,
1731    CJS_Value& vRet,
1732    CFX_WideString& sError) {
1733  CJS_Context* pContext = (CJS_Context*)cc;
1734  if (params.size() != 1) {
1735    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1736    return FALSE;
1737  }
1738
1739  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1740  if (!pEvent->m_pValue)
1741    return FALSE;
1742
1743  std::string cFormat;
1744  int iIndex = params[0].ToInt();
1745  CFX_WideString& val = pEvent->Value();
1746  std::string strSrc = CFX_ByteString::FromUnicode(val).c_str();
1747  std::wstring wstrChange = pEvent->Change().c_str();
1748
1749  switch (iIndex) {
1750    case 0:
1751      cFormat = "99999";
1752      break;
1753    case 1:
1754      // cFormat = "99999-9999";
1755      cFormat = "999999999";
1756      break;
1757    case 2: {
1758      std::string NumberStr;
1759      util::printx("9999999999", strSrc, NumberStr);
1760      if (strSrc.length() + wstrChange.length() > 7)
1761        // cFormat = "(999) 999-9999";
1762        cFormat = "9999999999";
1763      else
1764        // cFormat = "999-9999";
1765        cFormat = "9999999";
1766      break;
1767    }
1768    case 3:
1769      // cFormat = "999-99-9999";
1770      cFormat = "999999999";
1771      break;
1772  }
1773
1774  std::vector<CJS_Value> params2;
1775  params2.push_back(CJS_Value(CJS_Runtime::FromContext(cc), cFormat.c_str()));
1776  return AFSpecial_KeystrokeEx(cc, params2, vRet, sError);
1777}
1778
1779FX_BOOL CJS_PublicMethods::AFMergeChange(IJS_Context* cc,
1780                                         const std::vector<CJS_Value>& params,
1781                                         CJS_Value& vRet,
1782                                         CFX_WideString& sError) {
1783  CJS_Context* pContext = (CJS_Context*)cc;
1784  CJS_EventHandler* pEventHandler = pContext->GetEventHandler();
1785
1786  if (params.size() != 1) {
1787    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1788    return FALSE;
1789  }
1790
1791  CFX_WideString swValue;
1792  if (pEventHandler->m_pValue)
1793    swValue = pEventHandler->Value();
1794
1795  if (pEventHandler->WillCommit()) {
1796    vRet = swValue.c_str();
1797    return TRUE;
1798  }
1799
1800  CFX_WideString prefix, postfix;
1801
1802  if (pEventHandler->SelStart() >= 0)
1803    prefix = swValue.Mid(0, pEventHandler->SelStart());
1804  else
1805    prefix = L"";
1806
1807  if (pEventHandler->SelEnd() >= 0 &&
1808      pEventHandler->SelEnd() <= swValue.GetLength())
1809    postfix = swValue.Mid(pEventHandler->SelEnd(),
1810                          swValue.GetLength() - pEventHandler->SelEnd());
1811  else
1812    postfix = L"";
1813
1814  vRet = (prefix + pEventHandler->Change() + postfix).c_str();
1815
1816  return TRUE;
1817}
1818
1819FX_BOOL CJS_PublicMethods::AFParseDateEx(IJS_Context* cc,
1820                                         const std::vector<CJS_Value>& params,
1821                                         CJS_Value& vRet,
1822                                         CFX_WideString& sError) {
1823  CJS_Context* pContext = (CJS_Context*)cc;
1824  ASSERT(pContext);
1825
1826  if (params.size() != 2) {
1827    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1828    return FALSE;
1829  }
1830
1831  CFX_WideString sValue = params[0].ToCFXWideString();
1832  CFX_WideString sFormat = params[1].ToCFXWideString();
1833
1834  double dDate = MakeRegularDate(sValue, sFormat, nullptr);
1835
1836  if (JS_PortIsNan(dDate)) {
1837    CFX_WideString swMsg;
1838    swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSPARSEDATE).c_str(),
1839                 sFormat.c_str());
1840    Alert((CJS_Context*)cc, swMsg.c_str());
1841    return FALSE;
1842  }
1843
1844  vRet = dDate;
1845  return TRUE;
1846}
1847
1848FX_BOOL CJS_PublicMethods::AFSimple(IJS_Context* cc,
1849                                    const std::vector<CJS_Value>& params,
1850                                    CJS_Value& vRet,
1851                                    CFX_WideString& sError) {
1852  if (params.size() != 3) {
1853    CJS_Context* pContext = (CJS_Context*)cc;
1854    ASSERT(pContext);
1855
1856    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1857    return FALSE;
1858  }
1859
1860  vRet = (double)AF_Simple(params[0].ToCFXWideString().c_str(),
1861                           params[1].ToDouble(), params[2].ToDouble());
1862  return TRUE;
1863}
1864
1865FX_BOOL CJS_PublicMethods::AFMakeNumber(IJS_Context* cc,
1866                                        const std::vector<CJS_Value>& params,
1867                                        CJS_Value& vRet,
1868                                        CFX_WideString& sError) {
1869  if (params.size() != 1) {
1870    CJS_Context* pContext = (CJS_Context*)cc;
1871    ASSERT(pContext);
1872
1873    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1874    return FALSE;
1875  }
1876  vRet = ParseStringToNumber(params[0].ToCFXWideString().c_str());
1877  return TRUE;
1878}
1879
1880FX_BOOL CJS_PublicMethods::AFSimple_Calculate(
1881    IJS_Context* cc,
1882    const std::vector<CJS_Value>& params,
1883    CJS_Value& vRet,
1884    CFX_WideString& sError) {
1885  CJS_Context* pContext = (CJS_Context*)cc;
1886  if (params.size() != 2) {
1887    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1888    return FALSE;
1889  }
1890
1891  CJS_Value params1 = params[1];
1892  if (!params1.IsArrayObject() && params1.GetType() != CJS_Value::VT_string) {
1893    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1894    return FALSE;
1895  }
1896
1897  CPDFSDK_Document* pReaderDoc = pContext->GetReaderDocument();
1898  CPDFSDK_InterForm* pReaderInterForm = pReaderDoc->GetInterForm();
1899  CPDF_InterForm* pInterForm = pReaderInterForm->GetInterForm();
1900
1901  CFX_WideString sFunction = params[0].ToCFXWideString();
1902  double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0;
1903
1904  CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
1905  CJS_Array FieldNameArray = AF_MakeArrayFromList(pRuntime, params1);
1906  int nFieldsCount = 0;
1907
1908  for (int i = 0, isz = FieldNameArray.GetLength(); i < isz; i++) {
1909    CJS_Value jsValue(pRuntime);
1910    FieldNameArray.GetElement(i, jsValue);
1911    CFX_WideString wsFieldName = jsValue.ToCFXWideString();
1912
1913    for (int j = 0, jsz = pInterForm->CountFields(wsFieldName); j < jsz; j++) {
1914      if (CPDF_FormField* pFormField = pInterForm->GetField(j, wsFieldName)) {
1915        double dTemp = 0.0;
1916
1917        switch (pFormField->GetFieldType()) {
1918          case FIELDTYPE_TEXTFIELD:
1919          case FIELDTYPE_COMBOBOX: {
1920            dTemp = ParseStringToNumber(pFormField->GetValue().c_str());
1921            break;
1922          }
1923          case FIELDTYPE_PUSHBUTTON: {
1924            dTemp = 0.0;
1925            break;
1926          }
1927          case FIELDTYPE_CHECKBOX:
1928          case FIELDTYPE_RADIOBUTTON: {
1929            dTemp = 0.0;
1930            for (int c = 0, csz = pFormField->CountControls(); c < csz; c++) {
1931              if (CPDF_FormControl* pFormCtrl = pFormField->GetControl(c)) {
1932                if (pFormCtrl->IsChecked()) {
1933                  dTemp +=
1934                      ParseStringToNumber(pFormCtrl->GetExportValue().c_str());
1935                  break;
1936                }
1937              }
1938            }
1939            break;
1940          }
1941          case FIELDTYPE_LISTBOX: {
1942            if (pFormField->CountSelectedItems() > 1)
1943              break;
1944
1945            dTemp = ParseStringToNumber(pFormField->GetValue().c_str());
1946            break;
1947          }
1948          default:
1949            break;
1950        }
1951
1952        if (i == 0 && j == 0 && (wcscmp(sFunction.c_str(), L"MIN") == 0 ||
1953                                 wcscmp(sFunction.c_str(), L"MAX") == 0))
1954          dValue = dTemp;
1955
1956        dValue = AF_Simple(sFunction.c_str(), dValue, dTemp);
1957
1958        nFieldsCount++;
1959      }
1960    }
1961  }
1962
1963  if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0)
1964    dValue /= nFieldsCount;
1965
1966  dValue = (double)floor(dValue * FXSYS_pow((double)10, (double)6) + 0.49) /
1967           FXSYS_pow((double)10, (double)6);
1968  CJS_Value jsValue(pRuntime, dValue);
1969  if (pContext->GetEventHandler()->m_pValue)
1970    pContext->GetEventHandler()->Value() = jsValue.ToCFXWideString();
1971
1972  return TRUE;
1973}
1974
1975/* This function validates the current event to ensure that its value is
1976** within the specified range. */
1977
1978FX_BOOL CJS_PublicMethods::AFRange_Validate(
1979    IJS_Context* cc,
1980    const std::vector<CJS_Value>& params,
1981    CJS_Value& vRet,
1982    CFX_WideString& sError) {
1983  CJS_Context* pContext = (CJS_Context*)cc;
1984  CJS_EventHandler* pEvent = pContext->GetEventHandler();
1985
1986  if (params.size() != 4) {
1987    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
1988    return FALSE;
1989  }
1990
1991  if (!pEvent->m_pValue)
1992    return FALSE;
1993  if (pEvent->Value().IsEmpty())
1994    return TRUE;
1995  double dEentValue = atof(CFX_ByteString::FromUnicode(pEvent->Value()));
1996  FX_BOOL bGreaterThan = params[0].ToBool();
1997  double dGreaterThan = params[1].ToDouble();
1998  FX_BOOL bLessThan = params[2].ToBool();
1999  double dLessThan = params[3].ToDouble();
2000  CFX_WideString swMsg;
2001
2002  if (bGreaterThan && bLessThan) {
2003    if (dEentValue < dGreaterThan || dEentValue > dLessThan)
2004      swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSRANGE1).c_str(),
2005                   params[1].ToCFXWideString().c_str(),
2006                   params[3].ToCFXWideString().c_str());
2007  } else if (bGreaterThan) {
2008    if (dEentValue < dGreaterThan)
2009      swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSRANGE2).c_str(),
2010                   params[1].ToCFXWideString().c_str());
2011  } else if (bLessThan) {
2012    if (dEentValue > dLessThan)
2013      swMsg.Format(JSGetStringFromID(pContext, IDS_STRING_JSRANGE3).c_str(),
2014                   params[3].ToCFXWideString().c_str());
2015  }
2016
2017  if (!swMsg.IsEmpty()) {
2018    Alert(pContext, swMsg.c_str());
2019    pEvent->Rc() = FALSE;
2020  }
2021  return TRUE;
2022}
2023
2024FX_BOOL CJS_PublicMethods::AFExtractNums(IJS_Context* cc,
2025                                         const std::vector<CJS_Value>& params,
2026                                         CJS_Value& vRet,
2027                                         CFX_WideString& sError) {
2028  CJS_Context* pContext = (CJS_Context*)cc;
2029  if (params.size() != 1) {
2030    sError = JSGetStringFromID(pContext, IDS_STRING_JSPARAMERROR);
2031    return FALSE;
2032  }
2033
2034  CJS_Runtime* pRuntime = CJS_Runtime::FromContext(cc);
2035  CJS_Array nums(pRuntime);
2036
2037  CFX_WideString str = params[0].ToCFXWideString();
2038  CFX_WideString sPart;
2039
2040  if (str.GetAt(0) == L'.' || str.GetAt(0) == L',')
2041    str = L"0" + str;
2042
2043  int nIndex = 0;
2044  for (int i = 0, sz = str.GetLength(); i < sz; i++) {
2045    FX_WCHAR wc = str.GetAt(i);
2046    if (FXSYS_iswdigit(wc)) {
2047      sPart += wc;
2048    } else {
2049      if (sPart.GetLength() > 0) {
2050        nums.SetElement(nIndex, CJS_Value(pRuntime, sPart.c_str()));
2051        sPart = L"";
2052        nIndex++;
2053      }
2054    }
2055  }
2056
2057  if (sPart.GetLength() > 0) {
2058    nums.SetElement(nIndex, CJS_Value(pRuntime, sPart.c_str()));
2059  }
2060
2061  if (nums.GetLength() > 0)
2062    vRet = nums;
2063  else
2064    vRet.SetNull();
2065
2066  return TRUE;
2067}
2068