@indodev/toolkit 0.1.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -208,6 +208,46 @@ function maskNIK(nik, options = {}) {
208
208
  return startPart + char.repeat(maskLength) + endPart;
209
209
  }
210
210
 
211
+ // src/nik/utils.ts
212
+ function getAge(nik, referenceDate = /* @__PURE__ */ new Date()) {
213
+ const info = parseNIK(nik);
214
+ if (!info || !info.birthDate) {
215
+ return null;
216
+ }
217
+ const birthDate = info.birthDate;
218
+ let age = referenceDate.getFullYear() - birthDate.getFullYear();
219
+ const m = referenceDate.getMonth() - birthDate.getMonth();
220
+ if (m < 0 || m === 0 && referenceDate.getDate() < birthDate.getDate()) {
221
+ age--;
222
+ }
223
+ return age;
224
+ }
225
+ function formatBirthDate(nik, options = {
226
+ day: "numeric",
227
+ month: "long",
228
+ year: "numeric"
229
+ }, locale = "id-ID") {
230
+ const info = parseNIK(nik);
231
+ if (!info || !info.birthDate) {
232
+ return null;
233
+ }
234
+ return new Intl.DateTimeFormat(locale, options).format(info.birthDate);
235
+ }
236
+ function isValidForGender(nik, gender) {
237
+ const info = parseNIK(nik);
238
+ if (!info) {
239
+ return false;
240
+ }
241
+ return info.gender === gender;
242
+ }
243
+ function isValidForBirthDate(nik, birthDate) {
244
+ const info = parseNIK(nik);
245
+ if (!info || !info.birthDate) {
246
+ return false;
247
+ }
248
+ return info.birthDate.getFullYear() === birthDate.getFullYear() && info.birthDate.getMonth() === birthDate.getMonth() && info.birthDate.getDate() === birthDate.getDate();
249
+ }
250
+
211
251
  // src/phone/constants.ts
212
252
  var OPERATOR_PREFIXES = {
213
253
  // Telkomsel (Halo, Simpati, by.U)
@@ -850,6 +890,37 @@ function maskPhoneNumber(phone, options = {}) {
850
890
  return masked;
851
891
  }
852
892
 
893
+ // src/phone/links.ts
894
+ function generateWALink(phone, message) {
895
+ if (!validatePhoneNumber(phone)) {
896
+ return "";
897
+ }
898
+ const e164 = toE164(phone);
899
+ let link = `https://wa.me/${e164}`;
900
+ if (message) {
901
+ link += `?text=${encodeURIComponent(message)}`;
902
+ }
903
+ return link;
904
+ }
905
+ function generateSmsLink(phone, body) {
906
+ if (!validatePhoneNumber(phone)) {
907
+ return "";
908
+ }
909
+ const e164 = toE164(phone);
910
+ let link = `sms:+${e164}`;
911
+ if (body) {
912
+ link += `?body=${encodeURIComponent(body)}`;
913
+ }
914
+ return link;
915
+ }
916
+ function generateTelLink(phone) {
917
+ if (!validatePhoneNumber(phone)) {
918
+ return "";
919
+ }
920
+ const e164 = toE164(phone);
921
+ return `tel:+${e164}`;
922
+ }
923
+
853
924
  // src/phone/parse.ts
854
925
  function parsePhoneNumber(phone) {
855
926
  if (!validatePhoneNumber(phone)) {
@@ -898,6 +969,13 @@ function getOperator(phone) {
898
969
  const prefix = normalized.substring(0, 4);
899
970
  return OPERATOR_PREFIXES[prefix] || null;
900
971
  }
972
+ function isProvider(phone, providerName) {
973
+ const operator = getOperator(phone);
974
+ if (!operator) {
975
+ return false;
976
+ }
977
+ return operator.toLowerCase() === providerName.toLowerCase();
978
+ }
901
979
  function getRegion(phone) {
902
980
  if (!phone.startsWith("0")) {
903
981
  return null;
@@ -923,6 +1001,184 @@ function normalizeToNational2(phone) {
923
1001
  return "";
924
1002
  }
925
1003
 
1004
+ // src/npwp/validate.ts
1005
+ function validateNPWP(npwp) {
1006
+ if (!npwp || typeof npwp !== "string") {
1007
+ return false;
1008
+ }
1009
+ const cleaned = npwp.replace(/[^\d]/g, "");
1010
+ if (cleaned.length !== 15 && cleaned.length !== 16) {
1011
+ return false;
1012
+ }
1013
+ if (!/^\d+$/.test(cleaned)) {
1014
+ return false;
1015
+ }
1016
+ return true;
1017
+ }
1018
+
1019
+ // src/npwp/format.ts
1020
+ function formatNPWP(npwp) {
1021
+ if (!validateNPWP(npwp)) {
1022
+ return npwp;
1023
+ }
1024
+ const cleaned = npwp.replace(/[^\d]/g, "");
1025
+ if (cleaned.length === 15) {
1026
+ return `${cleaned.substring(0, 2)}.${cleaned.substring(
1027
+ 2,
1028
+ 5
1029
+ )}.${cleaned.substring(5, 8)}.${cleaned.substring(8, 9)}-${cleaned.substring(
1030
+ 9,
1031
+ 12
1032
+ )}.${cleaned.substring(12, 15)}`;
1033
+ }
1034
+ return cleaned;
1035
+ }
1036
+ function parseNPWP(npwp) {
1037
+ if (!validateNPWP(npwp)) {
1038
+ return null;
1039
+ }
1040
+ const cleaned = npwp.replace(/[^\d]/g, "");
1041
+ const isNikBased = cleaned.length === 16;
1042
+ if (isNikBased) {
1043
+ return {
1044
+ npwp: cleaned,
1045
+ type: cleaned.substring(0, 2),
1046
+ serial: cleaned.substring(2, 8),
1047
+ checksum: cleaned.substring(8, 9),
1048
+ taxOfficeCode: cleaned.substring(9, 12),
1049
+ branchCode: cleaned.substring(12, 16),
1050
+ isNikBased: true
1051
+ };
1052
+ }
1053
+ return {
1054
+ npwp: cleaned,
1055
+ type: cleaned.substring(0, 2),
1056
+ serial: cleaned.substring(2, 8),
1057
+ checksum: cleaned.substring(8, 9),
1058
+ taxOfficeCode: cleaned.substring(9, 12),
1059
+ branchCode: cleaned.substring(12, 15),
1060
+ isNikBased: false
1061
+ };
1062
+ }
1063
+ function maskNPWP(npwp, options) {
1064
+ if (!npwp) return "";
1065
+ const { visibleStart = 2, visibleEnd = 3, maskChar = "*" } = options || {};
1066
+ if (npwp.includes(".") || npwp.includes("-")) {
1067
+ let digitCount = 0;
1068
+ const digitsOnly = npwp.replace(/[^\d]/g, "");
1069
+ const totalDigits = digitsOnly.length;
1070
+ return npwp.split("").map((char) => {
1071
+ if (/\d/.test(char)) {
1072
+ digitCount++;
1073
+ if (digitCount <= visibleStart || digitCount > totalDigits - visibleEnd) {
1074
+ return char;
1075
+ }
1076
+ return maskChar;
1077
+ }
1078
+ return char;
1079
+ }).join("");
1080
+ }
1081
+ const cleaned = npwp.replace(/[^\d]/g, "");
1082
+ if (cleaned.length < visibleStart + visibleEnd) {
1083
+ return cleaned.replace(/./g, maskChar);
1084
+ }
1085
+ const start = cleaned.substring(0, visibleStart);
1086
+ const end = cleaned.substring(cleaned.length - visibleEnd);
1087
+ const middle = maskChar.repeat(cleaned.length - visibleStart - visibleEnd);
1088
+ return `${start}${middle}${end}`;
1089
+ }
1090
+
1091
+ // src/plate/regions.ts
1092
+ var PLATE_REGIONS = {
1093
+ A: "Banten",
1094
+ B: "Jakarta, Depok, Tangerang, Bekasi",
1095
+ D: "Bandung, Cimahi",
1096
+ E: "Cirebon, Indramayu, Majalengka, Kuningan",
1097
+ F: "Bogor, Cianjur, Sukabumi",
1098
+ G: "Pekalongan, Pemalang, Batang, Tegal, Brebes",
1099
+ H: "Semarang, Salatiga, Kendal, Demak",
1100
+ K: "Pati, Kudus, Jepara, Rembang, Blora, Grobogan",
1101
+ L: "Surabaya",
1102
+ M: "Madura",
1103
+ N: "Malang, Probolinggo, Pasuruan, Lumajang, Batu",
1104
+ P: "Besuki, Bondowoso, Situbondo, Jember, Banyuwangi",
1105
+ R: "Banyumas, Cilacap, Purbalinggo, Banjarnegara",
1106
+ S: "Bojonegoro, Tuban, Lamongan, Jombang, Mojokerto",
1107
+ T: "Purwakarta, Subang, Karawang",
1108
+ AA: "Kedu, Magelang, Purworejo, Kebumen, Temanggung, Wonosobo",
1109
+ AB: "Yogyakarta",
1110
+ AD: "Surakarta, Boyolali, Sukoharjo, Karanganyar, Wonogiri, Sragen, Klaten",
1111
+ AE: "Madiun, Ngawi, Magetan, Ponorogo, Pacitan",
1112
+ AG: "Kediri, Blitar, Tulungagung, Nganjuk, Trenggalek",
1113
+ BA: "Sumatera Barat",
1114
+ BB: "Sumatera Utara (Pantai Barat)",
1115
+ BD: "Bengkulu",
1116
+ BE: "Lampung",
1117
+ BG: "Sumatera Selatan",
1118
+ BH: "Jambi",
1119
+ BK: "Sumatera Utara (Pantai Timur)",
1120
+ BL: "Aceh",
1121
+ BM: "Riau",
1122
+ BN: "Kepulauan Bangka Belitung",
1123
+ BP: "Kepulauan Riau",
1124
+ DA: "Kalimantan Selatan",
1125
+ DB: "Sulawesi Utara (Daratan)",
1126
+ DC: "Sulawesi Barat",
1127
+ DD: "Sulawesi Selatan (Selatan)",
1128
+ DE: "Maluku",
1129
+ DF: "Timor Timur (Historical)",
1130
+ DG: "Maluku Utara",
1131
+ DH: "NTT (Timor)",
1132
+ DK: "Bali",
1133
+ DL: "Sulawesi Utara (Kepulauan)",
1134
+ DM: "Gorontalo",
1135
+ DN: "Sulawesi Tengah",
1136
+ DP: "Sulawesi Selatan (Utara)",
1137
+ DR: "NTB (Lombok)",
1138
+ DS: "Papua",
1139
+ DT: "Sulawesi Tenggara",
1140
+ EA: "NTB (Sumbawa)",
1141
+ EB: "NTT (Flores)",
1142
+ ED: "NTT (Sumba)",
1143
+ KB: "Kalimantan Barat",
1144
+ KH: "Kalimantan Tengah",
1145
+ KT: "Kalimantan Timur",
1146
+ KU: "Kalimantan Utara",
1147
+ PA: "Papua",
1148
+ PB: "Papua Barat"
1149
+ };
1150
+
1151
+ // src/plate/utils.ts
1152
+ function validatePlate(plate) {
1153
+ if (!plate || typeof plate !== "string") {
1154
+ return false;
1155
+ }
1156
+ const cleaned = plate.replace(/\s+/g, "").toUpperCase();
1157
+ const regex = /^[A-Z]{1,2}\d{1,4}[A-Z]{1,3}$/;
1158
+ return regex.test(cleaned);
1159
+ }
1160
+ function getRegionFromPlate(plate) {
1161
+ if (!plate || typeof plate !== "string") {
1162
+ return null;
1163
+ }
1164
+ const cleaned = plate.replace(/\s+/g, "").toUpperCase();
1165
+ const match = cleaned.match(/^([A-Z]{1,2})/);
1166
+ if (!match) {
1167
+ return null;
1168
+ }
1169
+ const prefix = match[1];
1170
+ return PLATE_REGIONS[prefix] || null;
1171
+ }
1172
+ function formatPlate(plate) {
1173
+ if (!plate) return "";
1174
+ const cleaned = plate.replace(/\s+/g, "").toUpperCase();
1175
+ const match = cleaned.match(/^([A-Z]{1,2})(\d{1,4})([A-Z]{1,3})$/);
1176
+ if (!match) {
1177
+ return cleaned;
1178
+ }
1179
+ return `${match[1]} ${match[2]} ${match[3]}`;
1180
+ }
1181
+
926
1182
  // src/currency/format.ts
927
1183
  function formatRupiah(amount, options) {
928
1184
  const {
@@ -1148,6 +1404,1739 @@ function capitalize(str) {
1148
1404
  return str.charAt(0).toUpperCase() + str.slice(1);
1149
1405
  }
1150
1406
 
1151
- export { cleanPhoneNumber, formatCompact, formatNIK, formatPhoneNumber, formatRupiah, getOperator, isLandlineNumber, isMobileNumber, maskNIK, maskPhoneNumber, parseNIK, parsePhoneNumber, parseRupiah, toE164, toInternational, toNational, toWords, validateNIK, validatePhoneNumber };
1407
+ // src/currency/utils.ts
1408
+ function roundToClean(amount, unit = "ribu") {
1409
+ const divisors = {
1410
+ ribu: 1e3,
1411
+ "ratus-ribu": 1e5,
1412
+ juta: 1e6
1413
+ };
1414
+ const divisor = divisors[unit];
1415
+ return Math.round(amount / divisor) * divisor;
1416
+ }
1417
+ function formatAccounting(amount, options) {
1418
+ const isNegative = amount < 0;
1419
+ const formatted = formatRupiah(Math.abs(amount), options);
1420
+ if (isNegative) {
1421
+ return `(${formatted})`;
1422
+ }
1423
+ return formatted;
1424
+ }
1425
+ function calculateTax(amount, rate = 0.11) {
1426
+ return amount * rate;
1427
+ }
1428
+ function addRupiahSymbol(amount) {
1429
+ if (typeof amount === "number") {
1430
+ return `Rp ${amount.toLocaleString("id-ID")}`;
1431
+ }
1432
+ if (amount.trim().startsWith("Rp")) {
1433
+ return amount;
1434
+ }
1435
+ return `Rp ${amount.trim()}`;
1436
+ }
1437
+
1438
+ // src/text/constants.ts
1439
+ var LOWERCASE_WORDS = [
1440
+ // Indonesian prepositions (kata depan)
1441
+ "di",
1442
+ "ke",
1443
+ "dari",
1444
+ "pada",
1445
+ "dalam",
1446
+ "untuk",
1447
+ "dengan",
1448
+ "oleh",
1449
+ "kepada",
1450
+ "terhadap",
1451
+ "tentang",
1452
+ "tanpa",
1453
+ "hingga",
1454
+ "sampai",
1455
+ "sejak",
1456
+ "menuju",
1457
+ "melalui",
1458
+ // Indonesian conjunctions (kata hubung)
1459
+ "dan",
1460
+ "atau",
1461
+ "tetapi",
1462
+ "namun",
1463
+ "serta",
1464
+ "maupun",
1465
+ "melainkan",
1466
+ "sedangkan",
1467
+ // Indonesian articles/particles
1468
+ "yang",
1469
+ "sebagai",
1470
+ "adalah",
1471
+ "ialah",
1472
+ "yaitu",
1473
+ "bahwa",
1474
+ "akan",
1475
+ "telah",
1476
+ "sudah",
1477
+ "belum",
1478
+ // English articles
1479
+ "a",
1480
+ "an",
1481
+ "the",
1482
+ // English conjunctions
1483
+ "and",
1484
+ "or",
1485
+ "but",
1486
+ "nor",
1487
+ "for",
1488
+ "yet",
1489
+ "so",
1490
+ "as",
1491
+ // English prepositions (short ones, < 5 letters)
1492
+ "at",
1493
+ "by",
1494
+ "in",
1495
+ "of",
1496
+ "on",
1497
+ "to",
1498
+ "up",
1499
+ "via",
1500
+ "per",
1501
+ "off",
1502
+ "out"
1503
+ // English prepositions (5+ letters - optional, some style guides capitalize these)
1504
+ // 'about',
1505
+ // 'above',
1506
+ // 'across',
1507
+ // 'after',
1508
+ // 'among',
1509
+ // 'below',
1510
+ // 'under',
1511
+ // 'until',
1512
+ // 'with',
1513
+ ];
1514
+ var ACRONYMS = [
1515
+ // Indonesian government & military
1516
+ "DKI",
1517
+ // Daerah Khusus Ibukota
1518
+ "DIY",
1519
+ // Daerah Istimewa Yogyakarta
1520
+ "TNI",
1521
+ // Tentara Nasional Indonesia
1522
+ "POLRI",
1523
+ // Kepolisian Republik Indonesia
1524
+ "ABRI",
1525
+ // Angkatan Bersenjata Republik Indonesia
1526
+ "MPR",
1527
+ // Majelis Permusyawaratan Rakyat
1528
+ "DPR",
1529
+ // Dewan Perwakilan Rakyat
1530
+ "KPK",
1531
+ // Komisi Pemberantasan Korupsi
1532
+ "BIN",
1533
+ // Badan Intelijen Negara
1534
+ // Indonesian business entities
1535
+ "PT",
1536
+ // Perseroan Terbatas
1537
+ "CV",
1538
+ // Commanditaire Vennootschap
1539
+ "UD",
1540
+ // Usaha Dagang
1541
+ "PD",
1542
+ // Perusahaan Daerah
1543
+ "Tbk",
1544
+ // Terbuka (publicly traded)
1545
+ "BUMN",
1546
+ // Badan Usaha Milik Negara
1547
+ "BUMD",
1548
+ // Badan Usaha Milik Daerah
1549
+ // Indonesian banks
1550
+ "BCA",
1551
+ // Bank Central Asia
1552
+ "BRI",
1553
+ // Bank Rakyat Indonesia
1554
+ "BNI",
1555
+ // Bank Negara Indonesia
1556
+ "BTN",
1557
+ // Bank Tabungan Negara
1558
+ "BSI",
1559
+ // Bank Syariah Indonesia
1560
+ "BPD",
1561
+ // Bank Pembangunan Daerah
1562
+ // Indonesian government services
1563
+ "KTP",
1564
+ // Kartu Tanda Penduduk
1565
+ "NIK",
1566
+ // Nomor Induk Kependudukan
1567
+ "NPWP",
1568
+ // Nomor Pokok Wajib Pajak
1569
+ "SIM",
1570
+ // Surat Izin Mengemudi
1571
+ "STNK",
1572
+ // Surat Tanda Nomor Kendaraan
1573
+ "BPJS",
1574
+ // Badan Penyelenggara Jaminan Sosial
1575
+ "KIS",
1576
+ // Kartu Indonesia Sehat
1577
+ "KIP",
1578
+ // Kartu Indonesia Pintar
1579
+ "PKH",
1580
+ // Program Keluarga Harapan
1581
+ // Indonesian utilities & infrastructure
1582
+ "PLN",
1583
+ // Perusahaan Listrik Negara
1584
+ "PDAM",
1585
+ // Perusahaan Daerah Air Minum
1586
+ "PGN",
1587
+ // Perusahaan Gas Negara
1588
+ "KAI",
1589
+ // Kereta Api Indonesia
1590
+ "MRT",
1591
+ // Mass Rapid Transit
1592
+ "LRT",
1593
+ // Light Rail Transit
1594
+ // Indonesian taxes & fees
1595
+ "PBB",
1596
+ // Pajak Bumi dan Bangunan
1597
+ "PPh",
1598
+ // Pajak Penghasilan
1599
+ "PPN",
1600
+ // Pajak Pertambahan Nilai
1601
+ "BPHTB",
1602
+ // Bea Perolehan Hak atas Tanah dan Bangunan
1603
+ // Indonesian education
1604
+ "UI",
1605
+ // Universitas Indonesia
1606
+ "ITB",
1607
+ // Institut Teknologi Bandung
1608
+ "UGM",
1609
+ // Universitas Gadjah Mada
1610
+ "IPB",
1611
+ // Institut Pertanian Bogor
1612
+ "ITS",
1613
+ // Institut Teknologi Sepuluh Nopember
1614
+ "UNPAD",
1615
+ // Universitas Padjadjaran
1616
+ "UNDIP",
1617
+ // Universitas Diponegoro
1618
+ "UNAIR",
1619
+ // Universitas Airlangga
1620
+ "UNS",
1621
+ // Universitas Sebelas Maret
1622
+ // Indonesian degrees (gelar)
1623
+ "S.Pd",
1624
+ // Sarjana Pendidikan
1625
+ "S.H",
1626
+ // Sarjana Hukum
1627
+ "S.E",
1628
+ // Sarjana Ekonomi
1629
+ "S.T",
1630
+ // Sarjana Teknik
1631
+ "S.Kom",
1632
+ // Sarjana Komputer
1633
+ "S.Si",
1634
+ // Sarjana Sains
1635
+ "S.Sos",
1636
+ // Sarjana Sosial
1637
+ "M.Pd",
1638
+ // Magister Pendidikan
1639
+ "M.M",
1640
+ // Magister Manajemen
1641
+ "M.T",
1642
+ // Magister Teknik
1643
+ "M.Kom",
1644
+ // Magister Komputer
1645
+ // Common services
1646
+ "ATM",
1647
+ // Automated Teller Machine
1648
+ "POS",
1649
+ // Point of Sale
1650
+ "SMS",
1651
+ // Short Message Service
1652
+ "GPS",
1653
+ // Global Positioning System
1654
+ "WiFi",
1655
+ // Wireless Fidelity (technically Wi-Fi)
1656
+ "USB",
1657
+ // Universal Serial Bus
1658
+ "PIN",
1659
+ // Personal Identification Number
1660
+ "OTP",
1661
+ // One Time Password
1662
+ "QR",
1663
+ // Quick Response
1664
+ // Technology & IT
1665
+ "IT",
1666
+ // Information Technology
1667
+ "AI",
1668
+ // Artificial Intelligence
1669
+ "ML",
1670
+ // Machine Learning
1671
+ "API",
1672
+ // Application Programming Interface
1673
+ "UI",
1674
+ // User Interface (duplicate with Universitas Indonesia, context matters)
1675
+ "UX",
1676
+ // User Experience
1677
+ "SEO",
1678
+ // Search Engine Optimization
1679
+ "SaaS",
1680
+ // Software as a Service
1681
+ "CRM",
1682
+ // Customer Relationship Management
1683
+ "ERP",
1684
+ // Enterprise Resource Planning
1685
+ // Business titles
1686
+ "CEO",
1687
+ // Chief Executive Officer
1688
+ "CFO",
1689
+ // Chief Financial Officer
1690
+ "CTO",
1691
+ // Chief Technology Officer
1692
+ "COO",
1693
+ // Chief Operating Officer
1694
+ "CMO",
1695
+ // Chief Marketing Officer
1696
+ "HR",
1697
+ // Human Resources
1698
+ "PR",
1699
+ // Public Relations
1700
+ "VP",
1701
+ // Vice President
1702
+ "GM",
1703
+ // General Manager
1704
+ // International organizations
1705
+ "UN",
1706
+ // United Nations
1707
+ "WHO",
1708
+ // World Health Organization
1709
+ "UNESCO",
1710
+ // United Nations Educational, Scientific and Cultural Organization
1711
+ "NATO",
1712
+ // North Atlantic Treaty Organization
1713
+ "ASEAN",
1714
+ // Association of Southeast Asian Nations
1715
+ "APEC",
1716
+ // Asia-Pacific Economic Cooperation
1717
+ "WTO",
1718
+ // World Trade Organization
1719
+ "IMF",
1720
+ // International Monetary Fund
1721
+ // Medical
1722
+ "ICU",
1723
+ // Intensive Care Unit
1724
+ "ER",
1725
+ // Emergency Room
1726
+ "MRI",
1727
+ // Magnetic Resonance Imaging
1728
+ "CT",
1729
+ // Computed Tomography
1730
+ "DNA",
1731
+ // Deoxyribonucleic Acid
1732
+ "RNA",
1733
+ // Ribonucleic Acid
1734
+ "HIV",
1735
+ // Human Immunodeficiency Virus
1736
+ "AIDS",
1737
+ // Acquired Immunodeficiency Syndrome
1738
+ "COVID",
1739
+ // Coronavirus Disease
1740
+ // Measurements & units
1741
+ "KM",
1742
+ // Kilometer
1743
+ "CM",
1744
+ // Centimeter
1745
+ "MM",
1746
+ // Millimeter
1747
+ "KG",
1748
+ // Kilogram
1749
+ "RPM",
1750
+ // Revolutions Per Minute
1751
+ "MPH",
1752
+ // Miles Per Hour
1753
+ "KPH",
1754
+ // Kilometers Per Hour
1755
+ // Finance
1756
+ "IPO",
1757
+ // Initial Public Offering
1758
+ "ATM",
1759
+ // Automated Teller Machine (duplicate)
1760
+ "ROI",
1761
+ // Return on Investment
1762
+ "GDP",
1763
+ // Gross Domestic Product
1764
+ "VAT"
1765
+ // Value Added Tax
1766
+ ];
1767
+ var ABBREVIATIONS = {
1768
+ // ========== Address Abbreviations ==========
1769
+ "Jl.": "Jalan",
1770
+ "Gg.": "Gang",
1771
+ "No.": "Nomor",
1772
+ "Kp.": "Kampung",
1773
+ "Ds.": "Desa",
1774
+ "Kel.": "Kelurahan",
1775
+ "Kec.": "Kecamatan",
1776
+ "Kab.": "Kabupaten",
1777
+ Kota: "Kota",
1778
+ "Prov.": "Provinsi",
1779
+ "Prop.": "Provinsi",
1780
+ "Rt.": "Rukun Tetangga",
1781
+ "Rw.": "Rukun Warga",
1782
+ Blok: "Blok",
1783
+ "Komp.": "Kompleks",
1784
+ Perumahan: "Perumahan",
1785
+ "Perum.": "Perumahan",
1786
+ // ========== Academic Titles ==========
1787
+ "Dr.": "Doktor",
1788
+ "Ir.": "Insinyur",
1789
+ "Prof.": "Profesor",
1790
+ "Drs.": "Doktorandus",
1791
+ "Dra.": "Doktoranda",
1792
+ // Bachelor degrees
1793
+ "S.Pd.": "Sarjana Pendidikan",
1794
+ "S.H.": "Sarjana Hukum",
1795
+ "S.E.": "Sarjana Ekonomi",
1796
+ "S.T.": "Sarjana Teknik",
1797
+ "S.Kom.": "Sarjana Komputer",
1798
+ "S.Si.": "Sarjana Sains",
1799
+ "S.Sos.": "Sarjana Sosial",
1800
+ "S.I.Kom.": "Sarjana Ilmu Komunikasi",
1801
+ "S.S.": "Sarjana Sastra",
1802
+ "S.Psi.": "Sarjana Psikologi",
1803
+ "S.Farm.": "Sarjana Farmasi",
1804
+ "S.Ked.": "Sarjana Kedokteran",
1805
+ // Master degrees
1806
+ "M.Sc.": "Master of Science",
1807
+ "M.M.": "Magister Manajemen",
1808
+ "M.Pd.": "Magister Pendidikan",
1809
+ "M.T.": "Magister Teknik",
1810
+ "M.Kom.": "Magister Komputer",
1811
+ "M.Si.": "Magister Sains",
1812
+ "M.H.": "Magister Hukum",
1813
+ "M.A.": "Master of Arts",
1814
+ MBA: "Master of Business Administration",
1815
+ // ========== Honorifics ==========
1816
+ "Bpk.": "Bapak",
1817
+ Ibu: "Ibu",
1818
+ "Sdr.": "Saudara",
1819
+ "Sdri.": "Saudari",
1820
+ "Yth.": "Yang Terhormat",
1821
+ "H.": "Haji",
1822
+ "Hj.": "Hajjah",
1823
+ "Tn.": "Tuan",
1824
+ "Ny.": "Nyonya",
1825
+ "Nn.": "Nona",
1826
+ // ========== Organizations ==========
1827
+ "PT.": "Perseroan Terbatas",
1828
+ "CV.": "Commanditaire Vennootschap",
1829
+ "UD.": "Usaha Dagang",
1830
+ "PD.": "Perusahaan Daerah",
1831
+ "Tbk.": "Terbuka",
1832
+ Koperasi: "Koperasi",
1833
+ Yayasan: "Yayasan",
1834
+ // ========== Common Abbreviations ==========
1835
+ "dst.": "dan seterusnya",
1836
+ "dsb.": "dan sebagainya",
1837
+ "dll.": "dan lain-lain",
1838
+ "dkk.": "dan kawan-kawan",
1839
+ "a.n.": "atas nama",
1840
+ "u.p.": "untuk perhatian",
1841
+ "u.b.": "untuk beliau",
1842
+ "c.q.": "casu quo",
1843
+ "hlm.": "halaman",
1844
+ "tgl.": "tanggal",
1845
+ "bln.": "bulan",
1846
+ "thn.": "tahun",
1847
+ "ttd.": "tertanda",
1848
+ // ========== Contact Information ==========
1849
+ "Tlp.": "Telepon",
1850
+ "Telp.": "Telepon",
1851
+ "HP.": "Handphone",
1852
+ Fax: "Faksimile",
1853
+ Email: "Email",
1854
+ Website: "Website",
1855
+ // ========== Days (Indonesian) ==========
1856
+ "Sen.": "Senin",
1857
+ "Sel.": "Selasa",
1858
+ "Rab.": "Rabu",
1859
+ "Kam.": "Kamis",
1860
+ "Jum.": "Jumat",
1861
+ "Sab.": "Sabtu",
1862
+ "Min.": "Minggu",
1863
+ // ========== Months (Indonesian) ==========
1864
+ "Jan.": "Januari",
1865
+ "Feb.": "Februari",
1866
+ "Mar.": "Maret",
1867
+ "Apr.": "April",
1868
+ Mei: "Mei",
1869
+ "Jun.": "Juni",
1870
+ "Jul.": "Juli",
1871
+ "Agt.": "Agustus",
1872
+ "Sep.": "September",
1873
+ "Okt.": "Oktober",
1874
+ "Nov.": "November",
1875
+ "Des.": "Desember",
1876
+ // ========== Units & Measurements ==========
1877
+ "kg.": "kilogram",
1878
+ "gr.": "gram",
1879
+ "lt.": "liter",
1880
+ "ml.": "mililiter",
1881
+ "km.": "kilometer",
1882
+ "cm.": "sentimeter",
1883
+ "mm.": "milimeter",
1884
+ "m2.": "meter persegi",
1885
+ "m3.": "meter kubik",
1886
+ "ha.": "hektar"
1887
+ };
1888
+ var PROFANITY = [
1889
+ "anjing",
1890
+ "babi",
1891
+ "bangsat",
1892
+ "bajingan",
1893
+ "brengsek",
1894
+ "goblok",
1895
+ "tolol",
1896
+ "idiot",
1897
+ "perek",
1898
+ "jablay",
1899
+ "kontol",
1900
+ "memek",
1901
+ "ngewe",
1902
+ "puki",
1903
+ "jembut",
1904
+ "asu",
1905
+ "itil",
1906
+ "lanjiao",
1907
+ "pantek",
1908
+ "anying",
1909
+ "anjrit"
1910
+ ];
1911
+ var STOPWORDS = [
1912
+ "ada",
1913
+ "adalah",
1914
+ "adanya",
1915
+ "adapun",
1916
+ "agak",
1917
+ "agaknya",
1918
+ "agar",
1919
+ "akan",
1920
+ "akankah",
1921
+ "akhir",
1922
+ "akhiri",
1923
+ "akhirnya",
1924
+ "aku",
1925
+ "akulah",
1926
+ "amat",
1927
+ "amatlah",
1928
+ "anda",
1929
+ "andalah",
1930
+ "antar",
1931
+ "antara",
1932
+ "antaranya",
1933
+ "apa",
1934
+ "apaan",
1935
+ "apabila",
1936
+ "apakah",
1937
+ "apalagi",
1938
+ "apatah",
1939
+ "artinya",
1940
+ "asal",
1941
+ "asalkan",
1942
+ "atas",
1943
+ "atau",
1944
+ "ataukah",
1945
+ "ataupun",
1946
+ "awal",
1947
+ "awalnya",
1948
+ "bagai",
1949
+ "bagaikan",
1950
+ "bagaimana",
1951
+ "bagaimanakah",
1952
+ "bagaimanapun",
1953
+ "bagi",
1954
+ "bagian",
1955
+ "bahkan",
1956
+ "bahwa",
1957
+ "bahwasanya",
1958
+ "baik",
1959
+ "bakal",
1960
+ "bakalan",
1961
+ "balik",
1962
+ "banyak",
1963
+ "bapak",
1964
+ "baru",
1965
+ "bawah",
1966
+ "beberapa",
1967
+ "begini",
1968
+ "beginian",
1969
+ "beginikah",
1970
+ "beginilah",
1971
+ "begitu",
1972
+ "begitukah",
1973
+ "begitulah",
1974
+ "begitupun",
1975
+ "bekerja",
1976
+ "belakang",
1977
+ "belakangan",
1978
+ "belum",
1979
+ "belumlah",
1980
+ "benar",
1981
+ "benarkah",
1982
+ "benarlah",
1983
+ "berada",
1984
+ "berakhir",
1985
+ "berakhirlah",
1986
+ "berakhirnya",
1987
+ "berapa",
1988
+ "berapakah",
1989
+ "berapalah",
1990
+ "berapapun",
1991
+ "berarti",
1992
+ "berawal",
1993
+ "berbagai",
1994
+ "berikut",
1995
+ "berikutnya",
1996
+ "berjumlah",
1997
+ "berkali-kali",
1998
+ "berkata",
1999
+ "berkeinginan",
2000
+ "berkenaan",
2001
+ "berlainan",
2002
+ "berlalu",
2003
+ "berlangsung",
2004
+ "berlebihan",
2005
+ "bermacam",
2006
+ "bermacam-macam",
2007
+ "bermaksud",
2008
+ "bermula",
2009
+ "bersama",
2010
+ "bersama-sama",
2011
+ "bersiap",
2012
+ "bersiap-siap",
2013
+ "bertanya",
2014
+ "bertanya-tanya",
2015
+ "berturut",
2016
+ "berturut-turut",
2017
+ "bertutur",
2018
+ "berujar",
2019
+ "berupa",
2020
+ "besar",
2021
+ "betul",
2022
+ "betulkah",
2023
+ "biasa",
2024
+ "biasanya",
2025
+ "bila",
2026
+ "bilakah",
2027
+ "bisa",
2028
+ "bisakah",
2029
+ "boleh",
2030
+ "bolehkah",
2031
+ "bolehlah",
2032
+ "buat",
2033
+ "bukan",
2034
+ "bukankah",
2035
+ "bukanlah",
2036
+ "bukannya",
2037
+ "bulan",
2038
+ "bung",
2039
+ "cara",
2040
+ "caranya",
2041
+ "cukup",
2042
+ "cukupkah",
2043
+ "cukuplah",
2044
+ "cuma",
2045
+ "dahulu",
2046
+ "dalam",
2047
+ "dan",
2048
+ "dapat",
2049
+ "dari",
2050
+ "daripada",
2051
+ "datang",
2052
+ "dekat",
2053
+ "demi",
2054
+ "demikian",
2055
+ "demikianlah",
2056
+ "dengan",
2057
+ "depan",
2058
+ "di",
2059
+ "dia",
2060
+ "diakhiri",
2061
+ "diakhirinya",
2062
+ "dialah",
2063
+ "diantara",
2064
+ "diantaranya",
2065
+ "diberi",
2066
+ "diberikan",
2067
+ "diberikannya",
2068
+ "dibuat",
2069
+ "dibuatnya",
2070
+ "didapat",
2071
+ "didatangkan",
2072
+ "digunakan",
2073
+ "diibaratkan",
2074
+ "diingat",
2075
+ "diingatkan",
2076
+ "diinginkan",
2077
+ "dijawab",
2078
+ "dijelaskan",
2079
+ "dijelaskannya",
2080
+ "dikarenakan",
2081
+ "dikatakan",
2082
+ "dikatakannya",
2083
+ "dikerjakan",
2084
+ "diketahui",
2085
+ "diketahuinya",
2086
+ "dikira",
2087
+ "dilakukan",
2088
+ "dilalui",
2089
+ "dilihat",
2090
+ "dimaksud",
2091
+ "dimaksudkan",
2092
+ "dimaksudkannya",
2093
+ "dimana",
2094
+ "dimanalah",
2095
+ "dimulai",
2096
+ "dimulailah",
2097
+ "dimulainya",
2098
+ "diminta",
2099
+ "dimintai",
2100
+ "dimisalkan",
2101
+ "dimungkinkan",
2102
+ "dini",
2103
+ "dipastikan",
2104
+ "diperbuat",
2105
+ "diperbuatnya",
2106
+ "dipergunakan",
2107
+ "diperkirakan",
2108
+ "diperlihatkan",
2109
+ "diperlukan",
2110
+ "diperlukannya",
2111
+ "dipersoalkan",
2112
+ "dipertanyakan",
2113
+ "dipunyai",
2114
+ "diri",
2115
+ "dirinya",
2116
+ "disampaikan",
2117
+ "disebut",
2118
+ "disebutkan",
2119
+ "disebutkannya",
2120
+ "disini",
2121
+ "disinilah",
2122
+ "disitulah",
2123
+ "diterangkan",
2124
+ "diterangkannya",
2125
+ "diteruskan",
2126
+ "ditujukan",
2127
+ "ditunjuk",
2128
+ "ditunjuki",
2129
+ "ditunjukkan",
2130
+ "ditunjukkannya",
2131
+ "ditunjuknya",
2132
+ "dituturkan",
2133
+ "dituturkannya",
2134
+ "diucapkan",
2135
+ "diucapkannya",
2136
+ "diungkapkan",
2137
+ "dua",
2138
+ "dulu",
2139
+ "empat",
2140
+ "enggak",
2141
+ "enggaknya",
2142
+ "entah",
2143
+ "entahlah",
2144
+ "guna",
2145
+ "gunakan",
2146
+ "hal",
2147
+ "hampir",
2148
+ "hanya",
2149
+ "hanyalah",
2150
+ "hari",
2151
+ "harus",
2152
+ "haruslah",
2153
+ "harusnya",
2154
+ "hendak",
2155
+ "hendaklah",
2156
+ "hendaknya",
2157
+ "hingga",
2158
+ "ia",
2159
+ "ialah",
2160
+ "ibarat",
2161
+ "ibaratkan",
2162
+ "ibaratnya",
2163
+ "ibu",
2164
+ "ikut",
2165
+ "ingat",
2166
+ "ingat-ingat",
2167
+ "ingin",
2168
+ "inginkah",
2169
+ "inginkan",
2170
+ "ini",
2171
+ "inikah",
2172
+ "inilah",
2173
+ "itu",
2174
+ "itukah",
2175
+ "itulah",
2176
+ "jadi",
2177
+ "jadilah",
2178
+ "jadinya",
2179
+ "jangan",
2180
+ "jangankan",
2181
+ "janganlah",
2182
+ "jauh",
2183
+ "jawab",
2184
+ "jawaban",
2185
+ "jawabnya",
2186
+ "jelas",
2187
+ "jelaskan",
2188
+ "jelaslah",
2189
+ "jelasnya",
2190
+ "jika",
2191
+ "jikalau",
2192
+ "juga",
2193
+ "jumlah",
2194
+ "jumlahnya",
2195
+ "justru",
2196
+ "kala",
2197
+ "kalau",
2198
+ "kalaulah",
2199
+ "kalaupun",
2200
+ "kali",
2201
+ "kalian",
2202
+ "kami",
2203
+ "kamilah",
2204
+ "kamu",
2205
+ "kamulah",
2206
+ "kan",
2207
+ "kapan",
2208
+ "kapankah",
2209
+ "kapanpun",
2210
+ "karena",
2211
+ "karenanya",
2212
+ "ke",
2213
+ "keadaan",
2214
+ "kebetulan",
2215
+ "kecil",
2216
+ "kedua",
2217
+ "keduanya",
2218
+ "keinginan",
2219
+ "kelak",
2220
+ "kelihatan",
2221
+ "kelihatannya",
2222
+ "kelima",
2223
+ "keluar",
2224
+ "kembali",
2225
+ "kemudian",
2226
+ "kemungkinan",
2227
+ "kemungkinannya",
2228
+ "kenapa",
2229
+ "kepada",
2230
+ "kepadanya",
2231
+ "kesampaian",
2232
+ "keseluruhan",
2233
+ "keseluruhannya",
2234
+ "keterlaluan",
2235
+ "ketika",
2236
+ "khususnya",
2237
+ "kini",
2238
+ "kinilah",
2239
+ "kira",
2240
+ "kira-kira",
2241
+ "kiranya",
2242
+ "kita",
2243
+ "kitalah",
2244
+ "kok",
2245
+ "kurang",
2246
+ "lagi",
2247
+ "lagian",
2248
+ "lah",
2249
+ "lain",
2250
+ "lainnya",
2251
+ "lalu",
2252
+ "lama",
2253
+ "lamanya",
2254
+ "lanjut",
2255
+ "lanjutnya",
2256
+ "lebih",
2257
+ "lewat",
2258
+ "luar",
2259
+ "macam",
2260
+ "maka",
2261
+ "makanya",
2262
+ "makin",
2263
+ "malah",
2264
+ "malahan",
2265
+ "mampu",
2266
+ "mampukah",
2267
+ "mana",
2268
+ "manakala",
2269
+ "manalagi",
2270
+ "masih",
2271
+ "masihkah",
2272
+ "masing",
2273
+ "masing-masing",
2274
+ "mau",
2275
+ "maupun",
2276
+ "melainkan",
2277
+ "melakukan",
2278
+ "melalui",
2279
+ "melihat",
2280
+ "melihatnya",
2281
+ "memang",
2282
+ "memastikan",
2283
+ "memberi",
2284
+ "memberikan",
2285
+ "membuat",
2286
+ "memerlukan",
2287
+ "memihak",
2288
+ "meminta",
2289
+ "memisalkan",
2290
+ "memperbuat",
2291
+ "mempergunakan",
2292
+ "memperkirakan",
2293
+ "memperlihatkan",
2294
+ "mempersiapkan",
2295
+ "mempersoalkan",
2296
+ "mempertanyakan",
2297
+ "mempunyai",
2298
+ "memulai",
2299
+ "memungkinkan",
2300
+ "memutuskan",
2301
+ "menanti",
2302
+ "menanti-nanti",
2303
+ "menantikan",
2304
+ "menunjuk",
2305
+ "menunjuknya",
2306
+ "menuju",
2307
+ "menurut",
2308
+ "menurutnya",
2309
+ "menurutmu",
2310
+ "menurutku",
2311
+ "menurutnya",
2312
+ "menurut mereka",
2313
+ "menyampaikan",
2314
+ "menyebut",
2315
+ "menyebutkan",
2316
+ "menjelaskan",
2317
+ "menjadi",
2318
+ "menjadikan",
2319
+ "menjalani",
2320
+ "menjelang",
2321
+ "menjawab",
2322
+ "menunjukkan",
2323
+ "menuangkan",
2324
+ "menulis",
2325
+ "menyatakan",
2326
+ "merupakan",
2327
+ "mereka",
2328
+ "merekalah",
2329
+ "meski",
2330
+ "meskipun",
2331
+ "mula",
2332
+ "mulai",
2333
+ "mulailah",
2334
+ "mulanya",
2335
+ "mungkin",
2336
+ "mungkinkah",
2337
+ "nah",
2338
+ "naik",
2339
+ "namun",
2340
+ "nanti",
2341
+ "nantinya",
2342
+ "nyaris",
2343
+ "oleh",
2344
+ "olehnya",
2345
+ "orang",
2346
+ "pada",
2347
+ "padahal",
2348
+ "padanya",
2349
+ "pakai",
2350
+ "paling",
2351
+ "panjang",
2352
+ "pantas",
2353
+ "para",
2354
+ "pasti",
2355
+ "pastilah",
2356
+ "pagi",
2357
+ "per",
2358
+ "pernah",
2359
+ "persoalan",
2360
+ "pertama",
2361
+ "pertama-tama",
2362
+ "perlu",
2363
+ "perlukah",
2364
+ "perlulah",
2365
+ "pernah",
2366
+ "pihak",
2367
+ "pihaknya",
2368
+ "pukul",
2369
+ "pula",
2370
+ "pun",
2371
+ "punya",
2372
+ "rasa",
2373
+ "rasanya",
2374
+ "rata",
2375
+ "rupanya",
2376
+ "saat",
2377
+ "saatnya",
2378
+ "saja",
2379
+ "sajalah",
2380
+ "salam",
2381
+ "saling",
2382
+ "sama",
2383
+ "sama-sama",
2384
+ "sambil",
2385
+ "sampai",
2386
+ "sampai-sampai",
2387
+ "sampaikan",
2388
+ "sana",
2389
+ "sangat",
2390
+ "sangatlah",
2391
+ "satu",
2392
+ "saya",
2393
+ "sayalah",
2394
+ "sayang",
2395
+ "seperti",
2396
+ "seperti-itu",
2397
+ "sepura",
2398
+ "sebab",
2399
+ "sebabnya",
2400
+ "sebagai",
2401
+ "sebagaimana",
2402
+ "sebagainya",
2403
+ "sebagian",
2404
+ "sebaik",
2405
+ "sebaik-baiknya",
2406
+ "sebaiknya",
2407
+ "sebaliknya",
2408
+ "sebanyak",
2409
+ "sebegini",
2410
+ "sebegitu",
2411
+ "sebelum",
2412
+ "sebelumnya",
2413
+ "sebenarnya",
2414
+ "seberapa",
2415
+ "sebesar",
2416
+ "sebetulnya",
2417
+ "sebisanya",
2418
+ "sebuah",
2419
+ "sebut",
2420
+ "sebutkan",
2421
+ "sebutnya",
2422
+ "secara",
2423
+ "secukupnya",
2424
+ "sedang",
2425
+ "sedangkan",
2426
+ "sedikit",
2427
+ "sedikitnya",
2428
+ "sedemikian",
2429
+ "sediakala",
2430
+ "sedikit",
2431
+ "sedikitnya",
2432
+ "segala",
2433
+ "segalanya",
2434
+ "segera",
2435
+ "seharusnya",
2436
+ "sehingga",
2437
+ "seingat",
2438
+ "sejak",
2439
+ "sejauh",
2440
+ "sejenak",
2441
+ "sejumlah",
2442
+ "sekali",
2443
+ "sekali-kali",
2444
+ "sekalian",
2445
+ "sekaligus",
2446
+ "sekalipun",
2447
+ "sekarang",
2448
+ "sekaranglah",
2449
+ "sekecil",
2450
+ "seketika",
2451
+ "sekiranya",
2452
+ "sekitar",
2453
+ "sekitarnya",
2454
+ "sekurang",
2455
+ "sekurangnya",
2456
+ "sela",
2457
+ "selalu",
2458
+ "selama",
2459
+ "selama-lamanya",
2460
+ "selamanya",
2461
+ "selanjutnya",
2462
+ "seluruh",
2463
+ "seluruhnya",
2464
+ "semacam",
2465
+ "semakin",
2466
+ "semampu",
2467
+ "semampunya",
2468
+ "semasa",
2469
+ "semata",
2470
+ "semata-mata",
2471
+ "semaunya",
2472
+ "sementara",
2473
+ "semisal",
2474
+ "semisalnya",
2475
+ "sempat",
2476
+ "semua",
2477
+ "semuanya",
2478
+ "semula",
2479
+ "sendiri",
2480
+ "sendirinya",
2481
+ "seolah",
2482
+ "seolah-olah",
2483
+ "seorang",
2484
+ "sepanjang",
2485
+ "sepantasnya",
2486
+ "sepantasnyalah",
2487
+ "seperempat",
2488
+ "seperti",
2489
+ "sepertinya",
2490
+ "sepihak",
2491
+ "sepuluh",
2492
+ "seratus",
2493
+ "seribu",
2494
+ "sering",
2495
+ "seringnya",
2496
+ "serta",
2497
+ "serupa",
2498
+ "sesaat",
2499
+ "sesama",
2500
+ "sesampai",
2501
+ "sesampainya",
2502
+ "sesegera",
2503
+ "sesekali",
2504
+ "seseorang",
2505
+ "sesuatu",
2506
+ "sesuatunya",
2507
+ "sesudah",
2508
+ "sesudahnya",
2509
+ "setelah",
2510
+ "setempat",
2511
+ "setengah",
2512
+ "seterusnya",
2513
+ "setiap",
2514
+ "setidaknya",
2515
+ "setinggi",
2516
+ "seusai",
2517
+ "sewaktu",
2518
+ "siap",
2519
+ "siapa",
2520
+ "siapakah",
2521
+ "siapapun",
2522
+ "sini",
2523
+ "sinilah",
2524
+ "situ",
2525
+ "situlah",
2526
+ "suatu",
2527
+ "sudah",
2528
+ "sudahkah",
2529
+ "sudahlah",
2530
+ "supaya",
2531
+ "tadi",
2532
+ "tadinya",
2533
+ "tahu",
2534
+ "tak",
2535
+ "tambah",
2536
+ "tambahnya",
2537
+ "tampak",
2538
+ "tampaknya",
2539
+ "tandas",
2540
+ "tandasnya",
2541
+ "tanpa",
2542
+ "tanya",
2543
+ "tanyakan",
2544
+ "tanyanya",
2545
+ "tapi",
2546
+ "tegas",
2547
+ "tegasnya",
2548
+ "telah",
2549
+ "tempat",
2550
+ "tengah",
2551
+ "tentang",
2552
+ "tentu",
2553
+ "tentulah",
2554
+ "tentunya",
2555
+ "tepat",
2556
+ "terakhir",
2557
+ "terasa",
2558
+ "terbanyak",
2559
+ "terdahulu",
2560
+ "terdapat",
2561
+ "terdiri",
2562
+ "terdiri-dari",
2563
+ "terhadap",
2564
+ "terhadapnya",
2565
+ "teringat",
2566
+ "teringat-ingat",
2567
+ "terjadi",
2568
+ "terjadilah",
2569
+ "terjadinya",
2570
+ "terkira",
2571
+ "terlalu",
2572
+ "terlebih",
2573
+ "terlihat",
2574
+ "termasuk",
2575
+ "ternyata",
2576
+ "tersampaikan",
2577
+ "tersebut",
2578
+ "tersebutlah",
2579
+ "tertentu",
2580
+ "tertuju",
2581
+ "terus",
2582
+ "terutama",
2583
+ "tetap",
2584
+ "tetapi",
2585
+ "tiap",
2586
+ "tiba",
2587
+ "tiba-tiba",
2588
+ "tidak",
2589
+ "tidakkah",
2590
+ "tidaklah",
2591
+ "tiga",
2592
+ "tadi",
2593
+ "tadinya",
2594
+ "tinggi",
2595
+ "toh",
2596
+ "tuju",
2597
+ "tunjuk",
2598
+ "turut",
2599
+ "tutur",
2600
+ "tuturnya",
2601
+ "ucap",
2602
+ "ucapnya",
2603
+ "ujar",
2604
+ "ujarnya",
2605
+ "umumnya",
2606
+ "ungkap",
2607
+ "ungkapnya",
2608
+ "untuk",
2609
+ "untaian",
2610
+ "usai",
2611
+ "usah",
2612
+ "waduh",
2613
+ "wah",
2614
+ "wahai",
2615
+ "walau",
2616
+ "walaupun",
2617
+ "wong",
2618
+ "yaitu",
2619
+ "yakin",
2620
+ "yakni",
2621
+ "yang"
2622
+ ];
2623
+
2624
+ // src/text/capitalization.ts
2625
+ function capitalize2(text) {
2626
+ if (!text) return text;
2627
+ return text.charAt(0).toUpperCase() + text.slice(1).toLowerCase();
2628
+ }
2629
+ function toTitleCase(text, options) {
2630
+ if (!text) return text;
2631
+ const {
2632
+ preserveAcronyms = true,
2633
+ strict = false,
2634
+ exceptions = []
2635
+ } = options || {};
2636
+ const lowercaseSet = /* @__PURE__ */ new Set([...LOWERCASE_WORDS, ...exceptions]);
2637
+ const acronymSet = new Set(ACRONYMS);
2638
+ const normalized = normalizeSpaces(text);
2639
+ const words = normalized.split(" ");
2640
+ return words.map((word, index) => {
2641
+ if (!word) return word;
2642
+ if (word.includes("-")) {
2643
+ return processHyphenatedWord(word, index === 0, {
2644
+ lowercaseSet,
2645
+ acronymSet,
2646
+ preserveAcronyms,
2647
+ strict
2648
+ });
2649
+ }
2650
+ return processWord(word, index === 0, {
2651
+ lowercaseSet,
2652
+ acronymSet,
2653
+ preserveAcronyms,
2654
+ strict
2655
+ });
2656
+ }).join(" ");
2657
+ }
2658
+ function normalizeSpaces(text) {
2659
+ return text.trim().replace(/\s+/g, " ");
2660
+ }
2661
+ function processWord(word, isFirstWord, context) {
2662
+ const { lowercaseSet, acronymSet, preserveAcronyms, strict } = context;
2663
+ const lowerWord = word.toLowerCase();
2664
+ const upperWord = word.toUpperCase();
2665
+ if (preserveAcronyms && acronymSet.has(upperWord)) {
2666
+ return upperWord;
2667
+ }
2668
+ if (!isFirstWord && lowercaseSet.has(lowerWord)) {
2669
+ return lowerWord;
2670
+ }
2671
+ if (strict) {
2672
+ return capitalizeFirstLetter(lowerWord);
2673
+ }
2674
+ return capitalizeFirstLetter(word.toLowerCase());
2675
+ }
2676
+ function processHyphenatedWord(word, isFirstWord, context) {
2677
+ return word.split("-").map(
2678
+ (part, index) => processWord(part, isFirstWord && index === 0, context)
2679
+ ).join("-");
2680
+ }
2681
+ function capitalizeFirstLetter(word) {
2682
+ if (!word) return word;
2683
+ return word.charAt(0).toUpperCase() + word.slice(1);
2684
+ }
2685
+ function toSentenceCase(text) {
2686
+ if (!text) return text;
2687
+ const normalized = text.trim().replace(/\s+/g, " ");
2688
+ let result = "";
2689
+ let shouldCapitalize = true;
2690
+ for (let i = 0; i < normalized.length; i++) {
2691
+ const char = normalized[i];
2692
+ if (shouldCapitalize && /[a-zA-ZÀ-ÿ]/.test(char)) {
2693
+ result += char.toUpperCase();
2694
+ shouldCapitalize = false;
2695
+ } else {
2696
+ result += char.toLowerCase();
2697
+ }
2698
+ if (isSentenceEnd(char)) {
2699
+ shouldCapitalize = true;
2700
+ }
2701
+ if (char === "." && i + 1 < normalized.length) {
2702
+ const nextChar = normalized[i + 1];
2703
+ if (nextChar !== " " && !/[.!?]/.test(nextChar)) {
2704
+ shouldCapitalize = false;
2705
+ }
2706
+ }
2707
+ }
2708
+ return result;
2709
+ }
2710
+ function isSentenceEnd(char) {
2711
+ return char === "." || char === "!" || char === "?";
2712
+ }
2713
+
2714
+ // src/text/slug.ts
2715
+ function slugify(text, options) {
2716
+ if (!text) return "";
2717
+ const {
2718
+ separator = "-",
2719
+ lowercase = true,
2720
+ replacements = {},
2721
+ trim = true
2722
+ } = options || {};
2723
+ let result = text;
2724
+ for (const [search, replace] of Object.entries(replacements)) {
2725
+ result = result.replace(new RegExp(escapeRegex(search), "g"), replace);
2726
+ }
2727
+ result = result.replace(/&/g, " dan ");
2728
+ result = result.replace(/\//g, " atau ");
2729
+ if (lowercase) {
2730
+ result = result.toLowerCase();
2731
+ }
2732
+ result = result.replace(/[.'@éèêëàâäôöûüùïîçñ™®©]/g, "");
2733
+ result = result.replace(/[^\w\s-]+/g, separator);
2734
+ result = result.replace(/\s+/g, separator);
2735
+ if (separator !== "-") {
2736
+ result = result.replace(/-/g, separator);
2737
+ }
2738
+ if (trim) {
2739
+ const separatorRegex = new RegExp(`\\${separator}+`, "g");
2740
+ result = result.replace(separatorRegex, separator);
2741
+ const trimRegex = new RegExp(`^\\${separator}+|\\${separator}+$`, "g");
2742
+ result = result.replace(trimRegex, "");
2743
+ }
2744
+ return result;
2745
+ }
2746
+ function escapeRegex(str) {
2747
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2748
+ }
2749
+
2750
+ // src/text/sanitize.ts
2751
+ function normalizeWhitespace(text) {
2752
+ if (!text) return text;
2753
+ return text.trim().replace(/\s+/g, " ");
2754
+ }
2755
+ function sanitize(text, options) {
2756
+ if (!text) return text;
2757
+ const {
2758
+ removeNewlines = false,
2759
+ removeExtraSpaces = true,
2760
+ removePunctuation = false,
2761
+ allowedChars,
2762
+ trim = true
2763
+ } = options || {};
2764
+ let result = text;
2765
+ if (removeNewlines) {
2766
+ result = result.replace(/[\n\r]/g, " ");
2767
+ }
2768
+ if (removePunctuation) {
2769
+ result = result.replace(/[!"#$%&'()*+,\-./:;<=>?@[\\\]^_`{|}~]/g, "");
2770
+ }
2771
+ if (allowedChars) {
2772
+ const allowedRegex = new RegExp(`[^${allowedChars}]`, "g");
2773
+ result = result.replace(allowedRegex, "");
2774
+ }
2775
+ if (removeExtraSpaces) {
2776
+ if (trim) {
2777
+ if (removeNewlines) {
2778
+ result = result.replace(/\s+/g, " ");
2779
+ } else {
2780
+ result = result.replace(/[ \t]+/g, " ");
2781
+ }
2782
+ } else {
2783
+ const leadingMatch = result.match(/^([ \t]*)/);
2784
+ const trailingMatch = result.match(/([ \t]*)$/);
2785
+ const leading = leadingMatch ? leadingMatch[1] : "";
2786
+ const trailing = trailingMatch ? trailingMatch[1] : "";
2787
+ const middle = result.slice(
2788
+ leading.length,
2789
+ result.length - trailing.length
2790
+ );
2791
+ const normalizedMiddle = removeNewlines ? middle.replace(/\s+/g, " ") : middle.replace(/[ \t]+/g, " ");
2792
+ result = leading + normalizedMiddle + trailing;
2793
+ }
2794
+ }
2795
+ if (trim) {
2796
+ result = result.trim();
2797
+ }
2798
+ return result;
2799
+ }
2800
+ function removeAccents(text) {
2801
+ if (!text) return text;
2802
+ const specialChars = {
2803
+ \u00D8: "O",
2804
+ \u00F8: "o",
2805
+ \u00C6: "AE",
2806
+ \u00E6: "ae",
2807
+ \u00C5: "A",
2808
+ \u00E5: "a",
2809
+ \u0110: "D",
2810
+ \u0111: "d",
2811
+ \u0141: "L",
2812
+ \u0142: "l",
2813
+ \u00DE: "TH",
2814
+ \u00FE: "th",
2815
+ \u00DF: "ss"
2816
+ };
2817
+ let result = text;
2818
+ for (const [accented, plain] of Object.entries(specialChars)) {
2819
+ result = result.replace(new RegExp(accented, "g"), plain);
2820
+ }
2821
+ return result.normalize("NFD").replace(/[\u0300-\u036f]/g, "");
2822
+ }
2823
+
2824
+ // src/text/abbreviation.ts
2825
+ function expandAbbreviation(text, options) {
2826
+ if (!text) return text;
2827
+ const { mode = "all", customMap = {}, preserveCase = false } = options || {};
2828
+ const abbreviationsMap = {
2829
+ ...getAbbreviationsByMode(mode),
2830
+ ...customMap
2831
+ };
2832
+ let result = text;
2833
+ const sortedAbbrevs = Object.keys(abbreviationsMap).sort(
2834
+ (a, b) => b.length - a.length
2835
+ );
2836
+ for (const abbrev of sortedAbbrevs) {
2837
+ const expansion = abbreviationsMap[abbrev];
2838
+ const startBoundary = /^\w/.test(abbrev) ? "\\b" : "";
2839
+ const endBoundary = /\w$/.test(abbrev) ? "\\b" : "";
2840
+ const regex = new RegExp(
2841
+ `${startBoundary}${escapeRegex2(abbrev)}${endBoundary}`,
2842
+ "gi"
2843
+ );
2844
+ result = result.replace(regex, (match) => {
2845
+ if (!preserveCase) {
2846
+ return expansion;
2847
+ }
2848
+ return matchCase(match, expansion);
2849
+ });
2850
+ }
2851
+ return result;
2852
+ }
2853
+ function getAbbreviationsByMode(mode) {
2854
+ if (mode === "all") {
2855
+ return ABBREVIATIONS;
2856
+ }
2857
+ const filtered = {};
2858
+ const addressAbbrevs = [
2859
+ "Jl.",
2860
+ "Gg.",
2861
+ "No.",
2862
+ "Kp.",
2863
+ "Ds.",
2864
+ "Kel.",
2865
+ "Kec.",
2866
+ "Kab.",
2867
+ "Kota",
2868
+ "Prov.",
2869
+ "Prop.",
2870
+ "Rt.",
2871
+ "Rw.",
2872
+ "Blok",
2873
+ "Komp.",
2874
+ "Perumahan",
2875
+ "Perum."
2876
+ ];
2877
+ const titleAbbrevs = [
2878
+ "Dr.",
2879
+ "Ir.",
2880
+ "Prof.",
2881
+ "Drs.",
2882
+ "Dra.",
2883
+ "S.Pd.",
2884
+ "S.H.",
2885
+ "S.E.",
2886
+ "S.T.",
2887
+ "S.Kom.",
2888
+ "S.Si.",
2889
+ "S.Sos.",
2890
+ "S.I.Kom.",
2891
+ "S.S.",
2892
+ "S.Psi.",
2893
+ "S.Farm.",
2894
+ "S.Ked.",
2895
+ "M.Sc.",
2896
+ "M.M.",
2897
+ "M.Pd.",
2898
+ "M.T.",
2899
+ "M.Kom.",
2900
+ "M.Si.",
2901
+ "M.H.",
2902
+ "M.A.",
2903
+ "MBA"
2904
+ ];
2905
+ const orgAbbrevs = [
2906
+ "PT.",
2907
+ "CV.",
2908
+ "UD.",
2909
+ "PD.",
2910
+ "Tbk.",
2911
+ "Koperasi",
2912
+ "Yayasan"
2913
+ ];
2914
+ for (const [abbrev, expansion] of Object.entries(ABBREVIATIONS)) {
2915
+ if (mode === "address" && addressAbbrevs.includes(abbrev)) {
2916
+ filtered[abbrev] = expansion;
2917
+ } else if (mode === "title" && titleAbbrevs.includes(abbrev)) {
2918
+ filtered[abbrev] = expansion;
2919
+ } else if (mode === "org" && orgAbbrevs.includes(abbrev)) {
2920
+ filtered[abbrev] = expansion;
2921
+ }
2922
+ }
2923
+ return filtered;
2924
+ }
2925
+ function escapeRegex2(str) {
2926
+ return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2927
+ }
2928
+ function matchCase(original, replacement) {
2929
+ if (original === original.toUpperCase()) {
2930
+ return replacement.toUpperCase();
2931
+ }
2932
+ if (original === original.toLowerCase()) {
2933
+ return replacement.toLowerCase();
2934
+ }
2935
+ if (original.charAt(0) === original.charAt(0).toUpperCase()) {
2936
+ return replacement.charAt(0).toUpperCase() + replacement.slice(1).toLowerCase();
2937
+ }
2938
+ return replacement;
2939
+ }
2940
+ function contractAbbreviation(text, options) {
2941
+ if (!text) return text;
2942
+ const { mode = "all" } = options || {};
2943
+ const abbreviationsMap = getAbbreviationsByMode(mode);
2944
+ const reverseMap = {};
2945
+ for (const [abbrev, expansion] of Object.entries(abbreviationsMap)) {
2946
+ reverseMap[expansion] = abbrev;
2947
+ }
2948
+ let result = text;
2949
+ const sortedExpansions = Object.keys(reverseMap).sort(
2950
+ (a, b) => b.length - a.length
2951
+ );
2952
+ for (const expansion of sortedExpansions) {
2953
+ const abbrev = reverseMap[expansion];
2954
+ const regex = new RegExp(`\\b${escapeRegex2(expansion)}\\b`, "gi");
2955
+ result = result.replace(regex, abbrev);
2956
+ }
2957
+ return result;
2958
+ }
2959
+
2960
+ // src/text/filter.ts
2961
+ function profanityFilter(text, mask = "*") {
2962
+ let filtered = text;
2963
+ PROFANITY.forEach((word) => {
2964
+ const regex = new RegExp(`\\b${word}\\b`, "gi");
2965
+ filtered = filtered.replace(regex, mask.repeat(word.length));
2966
+ });
2967
+ return filtered;
2968
+ }
2969
+ function removeStopwords(text) {
2970
+ const words = text.split(/\s+/);
2971
+ const filtered = words.filter(
2972
+ (word) => !STOPWORDS.includes(word.toLowerCase())
2973
+ );
2974
+ return filtered.join(" ");
2975
+ }
2976
+
2977
+ // src/text/normalization.ts
2978
+ var INFORMAL_MAP = {
2979
+ gw: "saya",
2980
+ gua: "saya",
2981
+ lu: "kamu",
2982
+ lo: "kamu",
2983
+ elo: "kamu",
2984
+ lagi: "sedang",
2985
+ gue: "saya",
2986
+ gwe: "saya",
2987
+ gak: "tidak",
2988
+ ga: "tidak",
2989
+ nggak: "tidak",
2990
+ kalo: "kalau",
2991
+ karna: "karena",
2992
+ tapi: "tetapi",
2993
+ udah: "sudah",
2994
+ dah: "sudah",
2995
+ aja: "saja",
2996
+ banget: "sekali",
2997
+ emang: "memang",
2998
+ pake: "pakai",
2999
+ bikin: "membuat",
3000
+ kasih: "memberi",
3001
+ dapet: "dapat",
3002
+ liat: "lihat",
3003
+ ngasih: "memberi",
3004
+ nyari: "mencari",
3005
+ nanya: "bertanya",
3006
+ bilang: "berkata"
3007
+ };
3008
+ function toFormal(text) {
3009
+ const words = text.split(/\s+/);
3010
+ const formalized = words.map((word) => {
3011
+ const lower = word.toLowerCase().replace(/[^\w]/g, "");
3012
+ const formal = INFORMAL_MAP[lower];
3013
+ if (formal) {
3014
+ if (word[0] === word[0].toUpperCase()) {
3015
+ return formal.charAt(0).toUpperCase() + formal.slice(1);
3016
+ }
3017
+ return formal;
3018
+ }
3019
+ return word;
3020
+ });
3021
+ return formalized.join(" ");
3022
+ }
3023
+ function isAlay(text) {
3024
+ if (!text) return false;
3025
+ const alternatingCaps = /([a-z][A-Z][a-z]|[A-Z][a-z][A-Z])/.test(text);
3026
+ const numberSub = /\b\w*[0431572]\w*\b/.test(text);
3027
+ const qSub = /q/i.test(text) && !/u/i.test(text);
3028
+ const excessiveChars = /(.)\1{2,}/.test(text);
3029
+ return alternatingCaps || numberSub || qSub || excessiveChars;
3030
+ }
3031
+
3032
+ // src/text/extract.ts
3033
+ function truncate(text, maxLength, options) {
3034
+ if (!text || maxLength <= 0) {
3035
+ return "";
3036
+ }
3037
+ const { ellipsis = "...", wordBoundary = true } = options || {};
3038
+ if (text.length <= maxLength) {
3039
+ return text;
3040
+ }
3041
+ const availableLength = maxLength - ellipsis.length;
3042
+ if (availableLength <= 0) {
3043
+ return ellipsis.slice(0, maxLength);
3044
+ }
3045
+ let truncated = text.slice(0, availableLength);
3046
+ if (wordBoundary) {
3047
+ const lastSpaceIndex = truncated.lastIndexOf(" ");
3048
+ if (lastSpaceIndex > 0) {
3049
+ truncated = truncated.slice(0, lastSpaceIndex);
3050
+ }
3051
+ }
3052
+ truncated = truncated.trimEnd();
3053
+ return truncated + ellipsis;
3054
+ }
3055
+ function extractWords(text, options) {
3056
+ if (!text || !text.trim()) {
3057
+ return [];
3058
+ }
3059
+ const {
3060
+ minLength = 0,
3061
+ includeHyphenated = true,
3062
+ lowercase = false
3063
+ } = options || {};
3064
+ let cleaned = text;
3065
+ if (includeHyphenated) {
3066
+ cleaned = text.replace(/[^\w\s-]/g, " ");
3067
+ } else {
3068
+ cleaned = text.replace(/[^\w\s]/g, " ");
3069
+ }
3070
+ const words = cleaned.split(/\s+/).map((word) => word.trim()).filter((word) => word.length > 0).filter((word) => !/^-+$/.test(word));
3071
+ let result = words;
3072
+ if (minLength > 0) {
3073
+ result = result.filter((word) => word.length >= minLength);
3074
+ }
3075
+ if (lowercase) {
3076
+ result = result.map((word) => word.toLowerCase());
3077
+ }
3078
+ return result;
3079
+ }
3080
+
3081
+ // src/text/compare.ts
3082
+ function compareStrings(str1, str2, options) {
3083
+ if (str1 === str2) {
3084
+ return true;
3085
+ }
3086
+ const s1 = str1 || "";
3087
+ const s2 = str2 || "";
3088
+ const {
3089
+ caseSensitive = false,
3090
+ ignoreWhitespace = false,
3091
+ ignoreAccents = false
3092
+ } = options || {};
3093
+ let normalized1 = s1;
3094
+ let normalized2 = s2;
3095
+ if (ignoreWhitespace) {
3096
+ normalized1 = normalizeWhitespace(normalized1);
3097
+ normalized2 = normalizeWhitespace(normalized2);
3098
+ }
3099
+ if (ignoreAccents) {
3100
+ normalized1 = removeAccents(normalized1);
3101
+ normalized2 = removeAccents(normalized2);
3102
+ }
3103
+ if (!caseSensitive) {
3104
+ normalized1 = normalized1.toLowerCase();
3105
+ normalized2 = normalized2.toLowerCase();
3106
+ }
3107
+ return normalized1 === normalized2;
3108
+ }
3109
+ function similarity(str1, str2) {
3110
+ if (str1 === str2) return 1;
3111
+ if (str1.length === 0) return str2.length === 0 ? 1 : 0;
3112
+ if (str2.length === 0) return 0;
3113
+ const len1 = str1.length;
3114
+ const len2 = str2.length;
3115
+ let prevRow = Array(len2 + 1).fill(0);
3116
+ let currentRow = Array(len2 + 1).fill(0);
3117
+ for (let j = 0; j <= len2; j++) {
3118
+ prevRow[j] = j;
3119
+ }
3120
+ for (let i = 1; i <= len1; i++) {
3121
+ currentRow[0] = i;
3122
+ for (let j = 1; j <= len2; j++) {
3123
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
3124
+ currentRow[j] = Math.min(
3125
+ currentRow[j - 1] + 1,
3126
+ // Insertion
3127
+ prevRow[j] + 1,
3128
+ // Deletion
3129
+ prevRow[j - 1] + cost
3130
+ // Substitution
3131
+ );
3132
+ }
3133
+ [prevRow, currentRow] = [currentRow, prevRow];
3134
+ }
3135
+ const distance = prevRow[len2];
3136
+ const maxLength = Math.max(len1, len2);
3137
+ return 1 - distance / maxLength;
3138
+ }
3139
+
3140
+ export { addRupiahSymbol, calculateTax, capitalize2 as capitalize, cleanPhoneNumber, compareStrings, contractAbbreviation, expandAbbreviation, extractWords, formatAccounting, formatBirthDate, formatCompact, formatNIK, formatNPWP, formatPhoneNumber, formatPlate, formatRupiah, generateSmsLink, generateTelLink, generateWALink, getAge, getOperator, getRegionFromPlate, isAlay, isLandlineNumber, isMobileNumber, isProvider, isValidForBirthDate, isValidForGender, maskNIK, maskNPWP, maskPhoneNumber, normalizeWhitespace, parseNIK, parseNPWP, parsePhoneNumber, parseRupiah, profanityFilter, removeAccents, removeStopwords, roundToClean, sanitize, similarity, slugify, toE164, toFormal, toInternational, toNational, toSentenceCase, toTitleCase, toWords, truncate, validateNIK, validateNPWP, validatePhoneNumber, validatePlate };
1152
3141
  //# sourceMappingURL=index.js.map
1153
3142
  //# sourceMappingURL=index.js.map