// Copyright 2014 PDFium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // Original code copyright 2014 Foxit Software Inc. http://www.foxitsoftware.com #include "fxjs/cjs_publicmethods.h" #include #include #include #include #include #include #include #include #include #include #include "core/fpdfdoc/cpdf_formcontrol.h" #include "core/fpdfdoc/cpdf_interactiveform.h" #include "core/fxcrt/fx_extension.h" #include "core/fxge/cfx_color.h" #include "fpdfsdk/cpdfsdk_formfillenvironment.h" #include "fpdfsdk/cpdfsdk_interactiveform.h" #include "fxjs/cjs_color.h" #include "fxjs/cjs_event_context.h" #include "fxjs/cjs_eventhandler.h" #include "fxjs/cjs_field.h" #include "fxjs/cjs_object.h" #include "fxjs/cjs_runtime.h" #include "fxjs/cjs_util.h" #include "fxjs/js_define.h" #include "fxjs/js_resources.h" // static const JSMethodSpec CJS_PublicMethods::GlobalFunctionSpecs[] = { {"AFDate_Format", AFDate_Format_static}, {"AFDate_FormatEx", AFDate_FormatEx_static}, {"AFDate_Keystroke", AFDate_Keystroke_static}, {"AFDate_KeystrokeEx", AFDate_KeystrokeEx_static}, {"AFExtractNums", AFExtractNums_static}, {"AFMakeNumber", AFMakeNumber_static}, {"AFMergeChange", AFMergeChange_static}, {"AFNumber_Format", AFNumber_Format_static}, {"AFNumber_Keystroke", AFNumber_Keystroke_static}, {"AFParseDateEx", AFParseDateEx_static}, {"AFPercent_Format", AFPercent_Format_static}, {"AFPercent_Keystroke", AFPercent_Keystroke_static}, {"AFRange_Validate", AFRange_Validate_static}, {"AFSimple", AFSimple_static}, {"AFSimple_Calculate", AFSimple_Calculate_static}, {"AFSpecial_Format", AFSpecial_Format_static}, {"AFSpecial_Keystroke", AFSpecial_Keystroke_static}, {"AFSpecial_KeystrokeEx", AFSpecial_KeystrokeEx_static}, {"AFTime_Format", AFTime_Format_static}, {"AFTime_FormatEx", AFTime_FormatEx_static}, {"AFTime_Keystroke", AFTime_Keystroke_static}, {"AFTime_KeystrokeEx", AFTime_KeystrokeEx_static}, }; namespace { #if _FX_OS_ != _FX_OS_ANDROID_ constexpr double kDoubleCorrect = 0.000000000000001; #endif constexpr const wchar_t* kMonths[] = {L"Jan", L"Feb", L"Mar", L"Apr", L"May", L"Jun", L"Jul", L"Aug", L"Sep", L"Oct", L"Nov", L"Dec"}; constexpr const wchar_t* kFullMonths[] = {L"January", L"February", L"March", L"April", L"May", L"June", L"July", L"August", L"September", L"October", L"November", L"December"}; constexpr const wchar_t* kDateFormats[] = {L"m/d", L"m/d/yy", L"mm/dd/yy", L"mm/yy", L"d-mmm", L"d-mmm-yy", L"dd-mmm-yy", L"yy-mm-dd", L"mmm-yy", L"mmmm-yy", L"mmm d, yyyy", L"mmmm d, yyyy", L"m/d/yy h:MM tt", L"m/d/yy HH:MM"}; constexpr const wchar_t* kTimeFormats[] = {L"HH:MM", L"h:MM tt", L"HH:MM:ss", L"h:MM:ss tt"}; template T StrTrim(const T& str) { T result = str; result.Trim(' '); return result; } void AlertIfPossible(CJS_EventContext* pContext, const WideString& swMsg) { CPDFSDK_FormFillEnvironment* pFormFillEnv = pContext->GetFormFillEnv(); if (pFormFillEnv) pFormFillEnv->JS_appAlert(swMsg, WideString(), JSPLATFORM_ALERT_BUTTON_OK, JSPLATFORM_ALERT_ICON_STATUS); } #if _FX_OS_ != _FX_OS_ANDROID_ ByteString CalculateString(double dValue, int iDec, int* iDec2, bool* bNegative) { *bNegative = dValue < 0; if (*bNegative) dValue = -dValue; // Make sure the number of precision characters will fit. iDec = std::min(iDec, std::numeric_limits::digits10); std::stringstream ss; ss << std::fixed << std::setprecision(iDec) << dValue; std::string value = ss.str(); size_t pos = value.find('.'); *iDec2 = pos == std::string::npos ? value.size() : static_cast(pos); return ByteString(value.c_str()); } #endif WideString CalcMergedString(const CJS_EventHandler* event, const WideString& value, const WideString& change) { WideString prefix = value.Left(event->SelStart()); WideString postfix; int end = event->SelEnd(); if (end >= 0 && static_cast(end) < value.GetLength()) postfix = value.Right(value.GetLength() - static_cast(end)); return prefix + change + postfix; } template >&)> void JSGlobalFunc(const char* func_name_string, const v8::FunctionCallbackInfo& info) { CJS_Object* pObj = CFXJS_Engine::GetObjectPrivate(info.Holder()); if (!pObj) return; CJS_Runtime* pRuntime = pObj->GetRuntime(); if (!pRuntime) return; std::vector> parameters; for (int i = 0; i < info.Length(); ++i) parameters.push_back(info[i]); CJS_Result result = (*F)(pRuntime, parameters); if (result.HasError()) { pRuntime->Error( JSFormatErrorString(func_name_string, nullptr, result.Error())); return; } if (result.HasReturn()) info.GetReturnValue().Set(result.Return()); } int WithinBoundsOrZero(int value, size_t size) { return value >= 0 && static_cast(value) < size ? value : 0; } int ValidStyleOrZero(int style) { return WithinBoundsOrZero(style, 4); } bool IsDigitSeparatorOrDecimalMark(int c) { return c == '.' || c == ','; } #if _FX_OS_ != _FX_OS_ANDROID_ bool IsStyleWithDigitSeparator(int style) { return style == 0 || style == 2; } char DigitSeparatorForStyle(int style) { ASSERT(IsStyleWithDigitSeparator(style)); return style == 0 ? ',' : '.'; } #endif bool IsStyleWithCommaDecimalMark(int style) { return style >= 2; } char DecimalMarkForStyle(int style) { return IsStyleWithCommaDecimalMark(style) ? ',' : '.'; } #if _FX_OS_ != _FX_OS_ANDROID_ void NormalizeDecimalMark(ByteString* str) { str->Replace(",", "."); } #endif void NormalizeDecimalMarkW(WideString* str) { str->Replace(L",", L"."); } bool IsValidMonth(int m) { return m >= 1 && m <= 12; } // TODO(thestig): Should this take the month into consideration? bool IsValidDay(int d) { return d >= 1 && d <= 31; } // TODO(thestig): Should 24 be allowed? Similarly, 60 for minutes and seconds. bool IsValid24Hour(int h) { return h >= 0 && h <= 24; } bool IsValidMinute(int m) { return m >= 0 && m <= 60; } bool IsValidSecond(int s) { return s >= 0 && s <= 60; } } // namespace CJS_PublicMethods::CJS_PublicMethods(v8::Local pObject, CJS_Runtime* pRuntime) : CJS_Object(pObject, pRuntime) {} CJS_PublicMethods::~CJS_PublicMethods() = default; // static void CJS_PublicMethods::DefineJSObjects(CFXJS_Engine* pEngine) { for (const auto& spec : GlobalFunctionSpecs) pEngine->DefineGlobalMethod(spec.pName, spec.pMethodCall); } #define JS_STATIC_GLOBAL_FUN(fun_name) \ void CJS_PublicMethods::fun_name##_static( \ const v8::FunctionCallbackInfo& info) { \ JSGlobalFunc(#fun_name, info); \ } JS_STATIC_GLOBAL_FUN(AFNumber_Format); JS_STATIC_GLOBAL_FUN(AFNumber_Keystroke); JS_STATIC_GLOBAL_FUN(AFPercent_Format); JS_STATIC_GLOBAL_FUN(AFPercent_Keystroke); JS_STATIC_GLOBAL_FUN(AFDate_FormatEx); JS_STATIC_GLOBAL_FUN(AFDate_KeystrokeEx); JS_STATIC_GLOBAL_FUN(AFDate_Format); JS_STATIC_GLOBAL_FUN(AFDate_Keystroke); JS_STATIC_GLOBAL_FUN(AFTime_FormatEx); JS_STATIC_GLOBAL_FUN(AFTime_KeystrokeEx); JS_STATIC_GLOBAL_FUN(AFTime_Format); JS_STATIC_GLOBAL_FUN(AFTime_Keystroke); JS_STATIC_GLOBAL_FUN(AFSpecial_Format); JS_STATIC_GLOBAL_FUN(AFSpecial_Keystroke); JS_STATIC_GLOBAL_FUN(AFSpecial_KeystrokeEx); JS_STATIC_GLOBAL_FUN(AFSimple); JS_STATIC_GLOBAL_FUN(AFMakeNumber); JS_STATIC_GLOBAL_FUN(AFSimple_Calculate); JS_STATIC_GLOBAL_FUN(AFRange_Validate); JS_STATIC_GLOBAL_FUN(AFMergeChange); JS_STATIC_GLOBAL_FUN(AFParseDateEx); JS_STATIC_GLOBAL_FUN(AFExtractNums); bool CJS_PublicMethods::IsNumber(const WideString& str) { WideString sTrim = StrTrim(str); const wchar_t* pTrim = sTrim.c_str(); const wchar_t* p = pTrim; bool bDot = false; bool bKXJS = false; wchar_t c; while ((c = *p) != L'\0') { if (IsDigitSeparatorOrDecimalMark(c)) { if (bDot) return false; bDot = true; } else if (c == L'-' || c == L'+') { if (p != pTrim) return false; } else if (c == L'e' || c == L'E') { if (bKXJS) return false; p++; c = *p; if (c != L'+' && c != L'-') return false; bKXJS = true; } else if (!std::iswdigit(c)) { return false; } p++; } return true; } bool CJS_PublicMethods::MaskSatisfied(wchar_t c_Change, wchar_t c_Mask) { switch (c_Mask) { case L'9': return !!std::iswdigit(c_Change); case L'A': return isascii(c_Change) && isalpha(c_Change); case L'O': return isascii(c_Change) && isalnum(c_Change); case L'X': return true; default: return (c_Change == c_Mask); } } bool CJS_PublicMethods::IsReservedMaskChar(wchar_t ch) { return ch == L'9' || ch == L'A' || ch == L'O' || ch == L'X'; } double CJS_PublicMethods::AF_Simple(const wchar_t* sFuction, double dValue1, double dValue2) { if (FXSYS_wcsicmp(sFuction, L"AVG") == 0 || FXSYS_wcsicmp(sFuction, L"SUM") == 0) { return dValue1 + dValue2; } if (FXSYS_wcsicmp(sFuction, L"PRD") == 0) return dValue1 * dValue2; if (FXSYS_wcsicmp(sFuction, L"MIN") == 0) return std::min(dValue1, dValue2); if (FXSYS_wcsicmp(sFuction, L"MAX") == 0) return std::max(dValue1, dValue2); return dValue1; } v8::Local CJS_PublicMethods::AF_MakeArrayFromList( CJS_Runtime* pRuntime, v8::Local val) { if (!val.IsEmpty() && val->IsArray()) return pRuntime->ToArray(val); WideString wsStr = pRuntime->ToWideString(val); ByteString t = wsStr.ToDefANSI(); const char* p = t.c_str(); int nIndex = 0; v8::Local StrArray = pRuntime->NewArray(); while (*p) { const char* pTemp = strchr(p, ','); if (!pTemp) { pRuntime->PutArrayElement( StrArray, nIndex, pRuntime->NewString(StrTrim(ByteString(p)).AsStringView())); break; } pRuntime->PutArrayElement( StrArray, nIndex, pRuntime->NewString(StrTrim(ByteString(p, pTemp - p)).AsStringView())); nIndex++; p = ++pTemp; } return StrArray; } int CJS_PublicMethods::ParseStringInteger(const WideString& str, size_t nStart, size_t* pSkip, size_t nMaxStep) { int nRet = 0; size_t nSkip = 0; for (size_t i = nStart; i < str.GetLength(); ++i) { if (i - nStart > 10) break; wchar_t c = str[i]; if (!std::iswdigit(c)) break; nRet = nRet * 10 + FXSYS_DecimalCharToInt(c); ++nSkip; if (nSkip >= nMaxStep) break; } *pSkip = nSkip; return nRet; } WideString CJS_PublicMethods::ParseStringString(const WideString& str, size_t nStart, size_t* pSkip) { WideString swRet; swRet.Reserve(str.GetLength()); for (size_t i = nStart; i < str.GetLength(); ++i) { wchar_t c = str[i]; if (!std::iswdigit(c)) break; swRet += c; } *pSkip = swRet.GetLength(); return swRet; } double CJS_PublicMethods::ParseNormalDate(const WideString& value, bool* bWrongFormat) { double dt = JS_GetDateTime(); int nYear = JS_GetYearFromTime(dt); int nMonth = JS_GetMonthFromTime(dt) + 1; int nDay = JS_GetDayFromTime(dt); int nHour = JS_GetHourFromTime(dt); int nMin = JS_GetMinFromTime(dt); int nSec = JS_GetSecFromTime(dt); int number[3]; size_t nSkip = 0; size_t nLen = value.GetLength(); size_t nIndex = 0; size_t i = 0; while (i < nLen) { if (nIndex > 2) break; wchar_t c = value[i]; if (std::iswdigit(c)) { number[nIndex++] = ParseStringInteger(value, i, &nSkip, 4); i += nSkip; } else { i++; } } if (nIndex == 2) { // TODO(thestig): Should the else case set |bWrongFormat| to true? // case2: month/day // case3: day/month if (IsValidMonth(number[0]) && IsValidDay(number[1])) { nMonth = number[0]; nDay = number[1]; } else if (IsValidDay(number[0]) && IsValidMonth(number[1])) { nDay = number[0]; nMonth = number[1]; } if (bWrongFormat) *bWrongFormat = false; } else if (nIndex == 3) { // TODO(thestig): Should the else case set |bWrongFormat| to true? // case1: year/month/day // case2: month/day/year // case3: day/month/year if (number[0] > 12 && IsValidMonth(number[1]) && IsValidDay(number[2])) { nYear = number[0]; nMonth = number[1]; nDay = number[2]; } else if (IsValidMonth(number[0]) && IsValidDay(number[1]) && number[2] > 31) { nMonth = number[0]; nDay = number[1]; nYear = number[2]; } else if (IsValidDay(number[0]) && IsValidMonth(number[1]) && number[2] > 31) { nDay = number[0]; nMonth = number[1]; nYear = number[2]; } if (bWrongFormat) *bWrongFormat = false; } else { if (bWrongFormat) *bWrongFormat = true; return dt; } // TODO(thestig): Should we set |bWrongFormat| to false here too? return JS_DateParse(WideString::Format(L"%d/%d/%d %d:%d:%d", nMonth, nDay, nYear, nHour, nMin, nSec)); } double CJS_PublicMethods::ParseDateUsingFormat(const WideString& value, const WideString& format, bool* bWrongFormat) { double dt = JS_GetDateTime(); if (format.IsEmpty() || value.IsEmpty()) return dt; int nYear = JS_GetYearFromTime(dt); int nMonth = JS_GetMonthFromTime(dt) + 1; int nDay = JS_GetDayFromTime(dt); int nHour = JS_GetHourFromTime(dt); int nMin = JS_GetMinFromTime(dt); int nSec = JS_GetSecFromTime(dt); int nYearSub = 99; // nYear - 2000; bool bPm = false; bool bExit = false; bool bBadFormat = false; size_t i = 0; size_t j = 0; while (i < format.GetLength()) { if (bExit) break; wchar_t c = format[i]; switch (c) { case ':': case '.': case '-': case '\\': case '/': i++; j++; break; case 'y': case 'm': case 'd': case 'H': case 'h': case 'M': case 's': case 't': { size_t oldj = j; size_t nSkip = 0; size_t remaining = format.GetLength() - i - 1; if (remaining == 0 || format[i + 1] != c) { switch (c) { case 'y': i++; j++; break; case 'm': nMonth = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 'd': nDay = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 'H': nHour = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 'h': nHour = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 'M': nMin = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 's': nSec = ParseStringInteger(value, j, &nSkip, 2); i++; j += nSkip; break; case 't': bPm = (j < value.GetLength() && value[j] == 'p'); i++; j++; break; } } else if (remaining == 1 || format[i + 2] != c) { switch (c) { case 'y': nYear = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 'm': nMonth = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 'd': nDay = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 'H': nHour = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 'h': nHour = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 'M': nMin = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 's': nSec = ParseStringInteger(value, j, &nSkip, 2); i += 2; j += nSkip; break; case 't': bPm = (j + 1 < value.GetLength() && value[j] == 'p' && value[j + 1] == 'm'); i += 2; j += 2; break; } } else if (remaining == 2 || format[i + 3] != c) { switch (c) { case 'm': { WideString sMonth = ParseStringString(value, j, &nSkip); bool bFind = false; for (int m = 0; m < 12; m++) { if (sMonth.CompareNoCase(kMonths[m]) == 0) { nMonth = m + 1; i += 3; j += nSkip; bFind = true; break; } } if (!bFind) { nMonth = ParseStringInteger(value, j, &nSkip, 3); i += 3; j += nSkip; } } break; case 'y': break; default: i += 3; j += 3; break; } } else if (remaining == 3 || format[i + 4] != c) { switch (c) { case 'y': nYear = ParseStringInteger(value, j, &nSkip, 4); j += nSkip; i += 4; break; case 'm': { bool bFind = false; WideString sMonth = ParseStringString(value, j, &nSkip); sMonth.MakeLower(); for (int m = 0; m < 12; m++) { WideString sFullMonths = WideString(kFullMonths[m]); sFullMonths.MakeLower(); if (sFullMonths.Contains(sMonth.c_str())) { nMonth = m + 1; i += 4; j += nSkip; bFind = true; break; } } if (!bFind) { nMonth = ParseStringInteger(value, j, &nSkip, 4); i += 4; j += nSkip; } } break; default: i += 4; j += 4; break; } } else { if (j >= value.GetLength() || format[i] != value[j]) { bBadFormat = true; bExit = true; } i++; j++; } if (oldj == j) { bBadFormat = true; bExit = true; } break; } default: if (value.GetLength() <= j) { bExit = true; } else if (format[i] != value[j]) { bBadFormat = true; bExit = true; } i++; j++; break; } } if (bPm) nHour += 12; if (nYear >= 0 && nYear <= nYearSub) nYear += 2000; if (!bBadFormat) { bBadFormat = !IsValidMonth(nMonth) || !IsValidDay(nDay) || !IsValid24Hour(nHour) || !IsValidMinute(nMin) || !IsValidSecond(nSec); } double dRet; if (bBadFormat) { dRet = ParseNormalDate(value, &bBadFormat); } else { dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay), JS_MakeTime(nHour, nMin, nSec, 0)); if (std::isnan(dRet)) dRet = JS_DateParse(value); } if (std::isnan(dRet)) dRet = ParseNormalDate(value, &bBadFormat); if (bWrongFormat) *bWrongFormat = bBadFormat; return dRet; } WideString CJS_PublicMethods::PrintDateUsingFormat(double dDate, const WideString& format) { WideString sRet; WideString sPart; int nYear = JS_GetYearFromTime(dDate); int nMonth = JS_GetMonthFromTime(dDate) + 1; int nDay = JS_GetDayFromTime(dDate); int nHour = JS_GetHourFromTime(dDate); int nMin = JS_GetMinFromTime(dDate); int nSec = JS_GetSecFromTime(dDate); size_t i = 0; while (i < format.GetLength()) { wchar_t c = format[i]; size_t remaining = format.GetLength() - i - 1; sPart.clear(); switch (c) { case 'y': case 'm': case 'd': case 'H': case 'h': case 'M': case 's': case 't': if (remaining == 0 || format[i + 1] != c) { switch (c) { case 'y': sPart += c; break; case 'm': sPart = WideString::Format(L"%d", nMonth); break; case 'd': sPart = WideString::Format(L"%d", nDay); break; case 'H': sPart = WideString::Format(L"%d", nHour); break; case 'h': sPart = WideString::Format(L"%d", nHour > 12 ? nHour - 12 : nHour); break; case 'M': sPart = WideString::Format(L"%d", nMin); break; case 's': sPart = WideString::Format(L"%d", nSec); break; case 't': sPart += nHour > 12 ? 'p' : 'a'; break; } i++; } else if (remaining == 1 || format[i + 2] != c) { switch (c) { case 'y': sPart = WideString::Format(L"%02d", nYear - (nYear / 100) * 100); break; case 'm': sPart = WideString::Format(L"%02d", nMonth); break; case 'd': sPart = WideString::Format(L"%02d", nDay); break; case 'H': sPart = WideString::Format(L"%02d", nHour); break; case 'h': sPart = WideString::Format(L"%02d", nHour > 12 ? nHour - 12 : nHour); break; case 'M': sPart = WideString::Format(L"%02d", nMin); break; case 's': sPart = WideString::Format(L"%02d", nSec); break; case 't': sPart = nHour > 12 ? L"pm" : L"am"; break; } i += 2; } else if (remaining == 2 || format[i + 3] != c) { switch (c) { case 'm': i += 3; if (IsValidMonth(nMonth)) sPart += kMonths[nMonth - 1]; break; default: i += 3; sPart += c; sPart += c; sPart += c; break; } } else if (remaining == 3 || format[i + 4] != c) { switch (c) { case 'y': sPart = WideString::Format(L"%04d", nYear); i += 4; break; case 'm': i += 4; if (IsValidMonth(nMonth)) sPart += kFullMonths[nMonth - 1]; break; default: i += 4; sPart += c; sPart += c; sPart += c; sPart += c; break; } } else { i++; sPart += c; } break; default: i++; sPart += c; break; } sRet += sPart; } return sRet; } // function AFNumber_Format(nDec, sepStyle, negStyle, currStyle, strCurrency, // bCurrencyPrepend) CJS_Result CJS_PublicMethods::AFNumber_Format( CJS_Runtime* pRuntime, const std::vector>& params) { #if _FX_OS_ != _FX_OS_ANDROID_ if (params.size() != 6) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventHandler* pEvent = pRuntime->GetCurrentEventContext()->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(L"No event handler"); WideString& Value = pEvent->Value(); ByteString strValue = StrTrim(Value.ToDefANSI()); if (strValue.IsEmpty()) return CJS_Result::Success(); int iDec = abs(pRuntime->ToInt32(params[0])); int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1])); int iNegStyle = ValidStyleOrZero(pRuntime->ToInt32(params[2])); // params[3] is iCurrStyle, it's not used. WideString wstrCurrency = pRuntime->ToWideString(params[4]); bool bCurrencyPrepend = pRuntime->ToBoolean(params[5]); // Processing decimal places NormalizeDecimalMark(&strValue); double dValue = atof(strValue.c_str()); if (iDec > 0) dValue += kDoubleCorrect; // Calculating number string bool bNegative; int iDec2; strValue = CalculateString(dValue, iDec, &iDec2, &bNegative); if (strValue.IsEmpty()) { dValue = 0; strValue = CalculateString(dValue, iDec, &iDec2, &bNegative); if (strValue.IsEmpty()) { strValue = "0"; iDec2 = 1; } } ASSERT(iDec2 >= 0); // Processing separator style if (static_cast(iDec2) < strValue.GetLength()) { if (IsStyleWithCommaDecimalMark(iSepStyle)) strValue.Replace(".", ","); if (iDec2 == 0) strValue.Insert(iDec2, '0'); } if (IsStyleWithDigitSeparator(iSepStyle)) { char cSeparator = DigitSeparatorForStyle(iSepStyle); for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3) strValue.Insert(iDecPositive, cSeparator); } // Processing currency string Value = WideString::FromLocal(strValue.AsStringView()); if (bCurrencyPrepend) Value = wstrCurrency + Value; else Value = Value + wstrCurrency; // Processing negative style if (bNegative) { if (iNegStyle == 0) { Value.InsertAtFront(L'-'); } else if (iNegStyle == 2 || iNegStyle == 3) { Value.InsertAtFront(L'('); Value += L')'; } if (iNegStyle == 1 || iNegStyle == 3) { if (CJS_Field* fTarget = pEvent->Target_Field()) { v8::Local arColor = pRuntime->NewArray(); pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString(L"RGB")); pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(1)); pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0)); pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0)); fTarget->set_text_color(pRuntime, arColor); } } } else { if (iNegStyle == 1 || iNegStyle == 3) { if (CJS_Field* fTarget = pEvent->Target_Field()) { v8::Local arColor = pRuntime->NewArray(); pRuntime->PutArrayElement(arColor, 0, pRuntime->NewString(L"RGB")); pRuntime->PutArrayElement(arColor, 1, pRuntime->NewNumber(0)); pRuntime->PutArrayElement(arColor, 2, pRuntime->NewNumber(0)); pRuntime->PutArrayElement(arColor, 3, pRuntime->NewNumber(0)); CJS_Result result = fTarget->get_text_color(pRuntime); CFX_Color crProp = CJS_Color::ConvertArrayToPWLColor( pRuntime, pRuntime->ToArray(result.Return())); CFX_Color crColor = CJS_Color::ConvertArrayToPWLColor(pRuntime, arColor); if (crColor != crProp) fTarget->set_text_color(pRuntime, arColor); } } } #endif return CJS_Result::Success(); } // function AFNumber_Keystroke(nDec, sepStyle, negStyle, currStyle, strCurrency, // bCurrencyPrepend) CJS_Result CJS_PublicMethods::AFNumber_Keystroke( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() < 2) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); CJS_EventHandler* pEvent = pContext->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); WideString& val = pEvent->Value(); WideString& wstrChange = pEvent->Change(); WideString wstrValue = val; if (pEvent->WillCommit()) { WideString swTemp = StrTrim(wstrValue); if (swTemp.IsEmpty()) return CJS_Result::Success(); NormalizeDecimalMarkW(&swTemp); if (!IsNumber(swTemp)) { pEvent->Rc() = false; WideString sError = JSGetStringFromID(JSMessage::kInvalidInputError); AlertIfPossible(pContext, sError); return CJS_Result::Failure(sError); } // It happens after the last keystroke and before validating, return CJS_Result::Success(); } WideString wstrSelected; if (pEvent->SelStart() != -1) { wstrSelected = wstrValue.Mid(pEvent->SelStart(), pEvent->SelEnd() - pEvent->SelStart()); } bool bHasSign = wstrValue.Contains(L'-') && !wstrSelected.Contains(L'-'); if (bHasSign) { // can't insert "change" in front of sign position. if (!wstrSelected.IsEmpty() && pEvent->SelStart() == 0) { pEvent->Rc() = false; return CJS_Result::Success(); } } int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1])); const wchar_t cSep = DecimalMarkForStyle(iSepStyle); bool bHasSep = wstrValue.Contains(cSep); for (size_t i = 0; i < wstrChange.GetLength(); ++i) { if (wstrChange[i] == cSep) { if (bHasSep) { pEvent->Rc() = false; return CJS_Result::Success(); } bHasSep = true; continue; } if (wstrChange[i] == L'-') { if (bHasSign) { pEvent->Rc() = false; return CJS_Result::Success(); } // sign's position is not correct if (i != 0) { pEvent->Rc() = false; return CJS_Result::Success(); } if (pEvent->SelStart() != 0) { pEvent->Rc() = false; return CJS_Result::Success(); } bHasSign = true; continue; } if (!std::iswdigit(wstrChange[i])) { pEvent->Rc() = false; return CJS_Result::Success(); } } val = CalcMergedString(pEvent, wstrValue, wstrChange); return CJS_Result::Success(); } // function AFPercent_Format(nDec, sepStyle) CJS_Result CJS_PublicMethods::AFPercent_Format( CJS_Runtime* pRuntime, const std::vector>& params) { #if _FX_OS_ != _FX_OS_ANDROID_ if (params.size() != 2) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventHandler* pEvent = pRuntime->GetCurrentEventContext()->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); WideString& Value = pEvent->Value(); ByteString strValue = StrTrim(Value.ToDefANSI()); if (strValue.IsEmpty()) return CJS_Result::Success(); int iDec = abs(pRuntime->ToInt32(params[0])); int iSepStyle = ValidStyleOrZero(pRuntime->ToInt32(params[1])); // for processing decimal places double dValue = atof(strValue.c_str()); dValue *= 100; if (iDec > 0) dValue += kDoubleCorrect; int iDec2; int iNegative = 0; strValue = fcvt(dValue, iDec, &iDec2, &iNegative); if (strValue.IsEmpty()) { dValue = 0; strValue = fcvt(dValue, iDec, &iDec2, &iNegative); } if (iDec2 < 0) { ByteString zeros; { pdfium::span zeros_ptr = zeros.GetBuffer(abs(iDec2)); std::fill(std::begin(zeros_ptr), std::end(zeros_ptr), '0'); } zeros.ReleaseBuffer(abs(iDec2)); strValue = zeros + strValue; iDec2 = 0; } int iMax = strValue.GetLength(); if (iDec2 > iMax) { for (int iNum = 0; iNum <= iDec2 - iMax; iNum++) strValue += '0'; iMax = iDec2 + 1; } // for processing seperator style if (iDec2 < iMax) { char mark = DecimalMarkForStyle(iSepStyle); strValue.Insert(iDec2, mark); iMax++; if (iDec2 == 0) strValue.Insert(iDec2, '0'); } if (IsStyleWithDigitSeparator(iSepStyle)) { char cSeparator = DigitSeparatorForStyle(iSepStyle); for (int iDecPositive = iDec2 - 3; iDecPositive > 0; iDecPositive -= 3) { strValue.Insert(iDecPositive, cSeparator); iMax++; } } // negative mark if (iNegative) strValue.InsertAtFront('-'); strValue += '%'; Value = WideString::FromLocal(strValue.AsStringView()); #endif return CJS_Result::Success(); } // AFPercent_Keystroke(nDec, sepStyle) CJS_Result CJS_PublicMethods::AFPercent_Keystroke( CJS_Runtime* pRuntime, const std::vector>& params) { return AFNumber_Keystroke(pRuntime, params); } // function AFDate_FormatEx(cFormat) CJS_Result CJS_PublicMethods::AFDate_FormatEx( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); CJS_EventHandler* pEvent = pContext->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); WideString& val = pEvent->Value(); WideString strValue = val; if (strValue.IsEmpty()) return CJS_Result::Success(); WideString sFormat = pRuntime->ToWideString(params[0]); double dDate; if (strValue.Contains(L"GMT")) { // for GMT format time // such as "Tue Aug 11 14:24:16 GMT+08002009" dDate = MakeInterDate(strValue); } else { dDate = ParseDateUsingFormat(strValue, sFormat, nullptr); } if (std::isnan(dDate)) { WideString swMsg = WideString::Format( JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); AlertIfPossible(pContext, swMsg); return CJS_Result::Failure(JSMessage::kParseDateError); } val = PrintDateUsingFormat(dDate, sFormat); return CJS_Result::Success(); } double CJS_PublicMethods::MakeInterDate(const WideString& strValue) { std::vector wsArray; WideString sTemp; for (const auto& c : strValue) { if (c == L' ' || c == L':') { wsArray.push_back(std::move(sTemp)); continue; } sTemp += c; } wsArray.push_back(std::move(sTemp)); if (wsArray.size() != 8) return 0; int nMonth = 1; sTemp = wsArray[1]; for (size_t i = 0; i < FX_ArraySize(kMonths); ++i) { if (sTemp.Compare(kMonths[i]) == 0) { nMonth = i + 1; break; } } int nDay = FX_atof(wsArray[2].AsStringView()); int nHour = FX_atof(wsArray[3].AsStringView()); int nMin = FX_atof(wsArray[4].AsStringView()); int nSec = FX_atof(wsArray[5].AsStringView()); int nYear = FX_atof(wsArray[7].AsStringView()); double dRet = JS_MakeDate(JS_MakeDay(nYear, nMonth - 1, nDay), JS_MakeTime(nHour, nMin, nSec, 0)); if (std::isnan(dRet)) dRet = JS_DateParse(strValue); return dRet; } // AFDate_KeystrokeEx(cFormat) CJS_Result CJS_PublicMethods::AFDate_KeystrokeEx( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) { return CJS_Result::Failure( WideString(L"AFDate_KeystrokeEx's parameter size not correct")); } CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); CJS_EventHandler* pEvent = pContext->GetEventHandler(); if (!pEvent->WillCommit()) return CJS_Result::Success(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); const WideString& strValue = pEvent->Value(); if (strValue.IsEmpty()) return CJS_Result::Success(); WideString sFormat = pRuntime->ToWideString(params[0]); bool bWrongFormat = false; double dRet = ParseDateUsingFormat(strValue, sFormat, &bWrongFormat); if (bWrongFormat || std::isnan(dRet)) { WideString swMsg = WideString::Format( JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); AlertIfPossible(pContext, swMsg); pEvent->Rc() = false; } return CJS_Result::Success(); } CJS_Result CJS_PublicMethods::AFDate_Format( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(kDateFormats)); std::vector> newParams; newParams.push_back(pRuntime->NewString(kDateFormats[iIndex])); return AFDate_FormatEx(pRuntime, newParams); } // AFDate_KeystrokeEx(cFormat) CJS_Result CJS_PublicMethods::AFDate_Keystroke( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(kDateFormats)); std::vector> newParams; newParams.push_back(pRuntime->NewString(kDateFormats[iIndex])); return AFDate_KeystrokeEx(pRuntime, newParams); } // function AFTime_Format(ptf) CJS_Result CJS_PublicMethods::AFTime_Format( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(kTimeFormats)); std::vector> newParams; newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex])); return AFDate_FormatEx(pRuntime, newParams); } CJS_Result CJS_PublicMethods::AFTime_Keystroke( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); int iIndex = WithinBoundsOrZero(pRuntime->ToInt32(params[0]), FX_ArraySize(kTimeFormats)); std::vector> newParams; newParams.push_back(pRuntime->NewString(kTimeFormats[iIndex])); return AFDate_KeystrokeEx(pRuntime, newParams); } CJS_Result CJS_PublicMethods::AFTime_FormatEx( CJS_Runtime* pRuntime, const std::vector>& params) { return AFDate_FormatEx(pRuntime, params); } CJS_Result CJS_PublicMethods::AFTime_KeystrokeEx( CJS_Runtime* pRuntime, const std::vector>& params) { return AFDate_KeystrokeEx(pRuntime, params); } // function AFSpecial_Format(psf) CJS_Result CJS_PublicMethods::AFSpecial_Format( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventHandler* pEvent = pRuntime->GetCurrentEventContext()->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); const WideString& wsSource = pEvent->Value(); WideString wsFormat; switch (pRuntime->ToInt32(params[0])) { case 0: wsFormat = L"99999"; break; case 1: wsFormat = L"99999-9999"; break; case 2: if (CJS_Util::printx(L"9999999999", wsSource).GetLength() >= 10) wsFormat = L"(999) 999-9999"; else wsFormat = L"999-9999"; break; case 3: wsFormat = L"999-99-9999"; break; } pEvent->Value() = CJS_Util::printx(wsFormat, wsSource); return CJS_Result::Success(); } // function AFSpecial_KeystrokeEx(mask) CJS_Result CJS_PublicMethods::AFSpecial_KeystrokeEx( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() < 1) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); CJS_EventHandler* pEvent = pContext->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); const WideString& valEvent = pEvent->Value(); WideString wstrMask = pRuntime->ToWideString(params[0]); if (wstrMask.IsEmpty()) return CJS_Result::Success(); if (pEvent->WillCommit()) { if (valEvent.IsEmpty()) return CJS_Result::Success(); size_t iIndexMask = 0; for (; iIndexMask < valEvent.GetLength(); ++iIndexMask) { if (!MaskSatisfied(valEvent[iIndexMask], wstrMask[iIndexMask])) break; } if (iIndexMask != wstrMask.GetLength() || (iIndexMask != valEvent.GetLength() && wstrMask.GetLength() != 0)) { AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kInvalidInputError)); pEvent->Rc() = false; } return CJS_Result::Success(); } WideString& wideChange = pEvent->Change(); if (wideChange.IsEmpty()) return CJS_Result::Success(); WideString wChange = wideChange; size_t iIndexMask = pEvent->SelStart(); size_t combined_len = valEvent.GetLength() + wChange.GetLength() + pEvent->SelStart() - pEvent->SelEnd(); if (combined_len > wstrMask.GetLength()) { AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError)); pEvent->Rc() = false; return CJS_Result::Success(); } if (iIndexMask >= wstrMask.GetLength() && !wChange.IsEmpty()) { AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError)); pEvent->Rc() = false; return CJS_Result::Success(); } for (size_t i = 0; i < wChange.GetLength(); ++i) { if (iIndexMask >= wstrMask.GetLength()) { AlertIfPossible(pContext, JSGetStringFromID(JSMessage::kParamTooLongError)); pEvent->Rc() = false; return CJS_Result::Success(); } wchar_t wMask = wstrMask[iIndexMask]; if (!IsReservedMaskChar(wMask)) wChange.SetAt(i, wMask); if (!MaskSatisfied(wChange[i], wMask)) { pEvent->Rc() = false; return CJS_Result::Success(); } iIndexMask++; } wideChange = std::move(wChange); return CJS_Result::Success(); } // function AFSpecial_Keystroke(psf) CJS_Result CJS_PublicMethods::AFSpecial_Keystroke( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventHandler* pEvent = pRuntime->GetCurrentEventContext()->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); const char* cFormat = ""; switch (pRuntime->ToInt32(params[0])) { case 0: cFormat = "99999"; break; case 1: cFormat = "999999999"; break; case 2: if (pEvent->Value().GetLength() + pEvent->Change().GetLength() > 7) cFormat = "9999999999"; else cFormat = "9999999"; break; case 3: cFormat = "999999999"; break; } std::vector> params2; params2.push_back(pRuntime->NewString(cFormat)); return AFSpecial_KeystrokeEx(pRuntime, params2); } CJS_Result CJS_PublicMethods::AFMergeChange( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventHandler* pEventHandler = pRuntime->GetCurrentEventContext()->GetEventHandler(); WideString swValue; if (pEventHandler->m_pValue) swValue = pEventHandler->Value(); if (pEventHandler->WillCommit()) return CJS_Result::Success(pRuntime->NewString(swValue.AsStringView())); return CJS_Result::Success(pRuntime->NewString( CalcMergedString(pEventHandler, swValue, pEventHandler->Change()) .AsStringView())); } CJS_Result CJS_PublicMethods::AFParseDateEx( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 2) return CJS_Result::Failure(JSMessage::kParamError); WideString sValue = pRuntime->ToWideString(params[0]); WideString sFormat = pRuntime->ToWideString(params[1]); double dDate = ParseDateUsingFormat(sValue, sFormat, nullptr); if (std::isnan(dDate)) { WideString swMsg = WideString::Format( JSGetStringFromID(JSMessage::kParseDateError).c_str(), sFormat.c_str()); AlertIfPossible(pRuntime->GetCurrentEventContext(), swMsg); return CJS_Result::Failure(JSMessage::kParseDateError); } return CJS_Result::Success(pRuntime->NewNumber(dDate)); } CJS_Result CJS_PublicMethods::AFSimple( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 3) return CJS_Result::Failure(JSMessage::kParamError); return CJS_Result::Success(pRuntime->NewNumber(static_cast(AF_Simple( pRuntime->ToWideString(params[0]).c_str(), pRuntime->ToDouble(params[1]), pRuntime->ToDouble(params[2]))))); } CJS_Result CJS_PublicMethods::AFMakeNumber( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); WideString ws = pRuntime->ToWideString(params[0]); NormalizeDecimalMarkW(&ws); v8::Local val = pRuntime->MaybeCoerceToNumber(pRuntime->NewString(ws.AsStringView())); if (!val->IsNumber()) return CJS_Result::Success(pRuntime->NewNumber(0)); return CJS_Result::Success(val); } CJS_Result CJS_PublicMethods::AFSimple_Calculate( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 2) return CJS_Result::Failure(JSMessage::kParamError); if ((params[1].IsEmpty() || !params[1]->IsArray()) && !params[1]->IsString()) return CJS_Result::Failure(JSMessage::kParamError); CPDFSDK_InteractiveForm* pReaderForm = pRuntime->GetFormFillEnv()->GetInteractiveForm(); CPDF_InteractiveForm* pForm = pReaderForm->GetInteractiveForm(); WideString sFunction = pRuntime->ToWideString(params[0]); double dValue = wcscmp(sFunction.c_str(), L"PRD") == 0 ? 1.0 : 0.0; v8::Local FieldNameArray = AF_MakeArrayFromList(pRuntime, params[1]); int nFieldsCount = 0; for (size_t i = 0; i < pRuntime->GetArrayLength(FieldNameArray); ++i) { WideString wsFieldName = pRuntime->ToWideString(pRuntime->GetArrayElement(FieldNameArray, i)); for (size_t j = 0; j < pForm->CountFields(wsFieldName); ++j) { CPDF_FormField* pFormField = pForm->GetField(j, wsFieldName); if (!pFormField) continue; double dTemp = 0.0; switch (pFormField->GetFieldType()) { case FormFieldType::kTextField: case FormFieldType::kComboBox: { WideString trimmed = pFormField->GetValue(); trimmed.TrimRight(); trimmed.TrimLeft(); dTemp = FX_atof(trimmed.AsStringView()); break; } case FormFieldType::kPushButton: break; case FormFieldType::kCheckBox: case FormFieldType::kRadioButton: for (int c = 0; c < pFormField->CountControls(); ++c) { CPDF_FormControl* pFormCtrl = pFormField->GetControl(c); if (!pFormField || !pFormCtrl->IsChecked()) continue; WideString trimmed = pFormCtrl->GetExportValue(); trimmed.TrimRight(); trimmed.TrimLeft(); dTemp = FX_atof(trimmed.AsStringView()); break; } break; case FormFieldType::kListBox: if (pFormField->CountSelectedItems() <= 1) { WideString trimmed = pFormField->GetValue(); trimmed.TrimRight(); trimmed.TrimLeft(); dTemp = FX_atof(trimmed.AsStringView()); } break; default: break; } if (i == 0 && j == 0 && (wcscmp(sFunction.c_str(), L"MIN") == 0 || wcscmp(sFunction.c_str(), L"MAX") == 0)) { dValue = dTemp; } dValue = AF_Simple(sFunction.c_str(), dValue, dTemp); nFieldsCount++; } } if (wcscmp(sFunction.c_str(), L"AVG") == 0 && nFieldsCount > 0) dValue /= nFieldsCount; dValue = floor(dValue * FXSYS_pow(10, 6) + 0.49) / FXSYS_pow(10, 6); CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); if (pContext->GetEventHandler()->m_pValue) { pContext->GetEventHandler()->Value() = pRuntime->ToWideString(pRuntime->NewNumber(dValue)); } return CJS_Result::Success(); } // This function validates the current event to ensure that its value is // within the specified range. CJS_Result CJS_PublicMethods::AFRange_Validate( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 4) return CJS_Result::Failure(JSMessage::kParamError); CJS_EventContext* pContext = pRuntime->GetCurrentEventContext(); CJS_EventHandler* pEvent = pContext->GetEventHandler(); if (!pEvent->m_pValue) return CJS_Result::Failure(JSMessage::kBadObjectError); if (pEvent->Value().IsEmpty()) return CJS_Result::Success(); double dEentValue = atof(pEvent->Value().ToDefANSI().c_str()); bool bGreaterThan = pRuntime->ToBoolean(params[0]); double dGreaterThan = pRuntime->ToDouble(params[1]); bool bLessThan = pRuntime->ToBoolean(params[2]); double dLessThan = pRuntime->ToDouble(params[3]); WideString swMsg; if (bGreaterThan && bLessThan) { if (dEentValue < dGreaterThan || dEentValue > dLessThan) swMsg = WideString::Format( JSGetStringFromID(JSMessage::kRangeBetweenError).c_str(), pRuntime->ToWideString(params[1]).c_str(), pRuntime->ToWideString(params[3]).c_str()); } else if (bGreaterThan) { if (dEentValue < dGreaterThan) swMsg = WideString::Format( JSGetStringFromID(JSMessage::kRangeGreaterError).c_str(), pRuntime->ToWideString(params[1]).c_str()); } else if (bLessThan) { if (dEentValue > dLessThan) swMsg = WideString::Format( JSGetStringFromID(JSMessage::kRangeLessError).c_str(), pRuntime->ToWideString(params[3]).c_str()); } if (!swMsg.IsEmpty()) { AlertIfPossible(pContext, swMsg); pEvent->Rc() = false; } return CJS_Result::Success(); } CJS_Result CJS_PublicMethods::AFExtractNums( CJS_Runtime* pRuntime, const std::vector>& params) { if (params.size() != 1) return CJS_Result::Failure(JSMessage::kParamError); WideString str = pRuntime->ToWideString(params[0]); if (str.GetLength() > 0 && IsDigitSeparatorOrDecimalMark(str[0])) str.InsertAtFront(L'0'); WideString sPart; v8::Local nums = pRuntime->NewArray(); int nIndex = 0; for (const auto& wc : str) { if (std::iswdigit(wc)) { sPart += wc; } else if (sPart.GetLength() > 0) { pRuntime->PutArrayElement(nums, nIndex, pRuntime->NewString(sPart.AsStringView())); sPart.clear(); nIndex++; } } if (sPart.GetLength() > 0) { pRuntime->PutArrayElement(nums, nIndex, pRuntime->NewString(sPart.AsStringView())); } if (pRuntime->GetArrayLength(nums) > 0) return CJS_Result::Success(nums); return CJS_Result::Success(pRuntime->NewUndefined()); }