智慧水务管理系统 - 精河县供水工程综合管理平台

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885
  1. /*!
  2. * Autolinker.js
  3. * v4.1.5
  4. *
  5. * Copyright(c) 2025 Gregory Jacobs <greg@greg-jacobs.com>
  6. * MIT License
  7. *
  8. * https://github.com/gregjacobs/Autolinker.js
  9. */
  10. (function (global, factory) {
  11. typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  12. typeof define === 'function' && define.amd ? define(factory) :
  13. (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Autolinker = factory());
  14. })(this, (function () { 'use strict';
  15. /******************************************************************************
  16. Copyright (c) Microsoft Corporation.
  17. Permission to use, copy, modify, and/or distribute this software for any
  18. purpose with or without fee is hereby granted.
  19. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  20. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  21. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  22. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  23. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  24. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  25. PERFORMANCE OF THIS SOFTWARE.
  26. ***************************************************************************** */
  27. /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
  28. var extendStatics = function(d, b) {
  29. extendStatics = Object.setPrototypeOf ||
  30. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  31. function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
  32. return extendStatics(d, b);
  33. };
  34. function __extends(d, b) {
  35. if (typeof b !== "function" && b !== null)
  36. throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
  37. extendStatics(d, b);
  38. function __() { this.constructor = d; }
  39. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  40. }
  41. var __assign = function() {
  42. __assign = Object.assign || function __assign(t) {
  43. for (var s, i = 1, n = arguments.length; i < n; i++) {
  44. s = arguments[i];
  45. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  46. }
  47. return t;
  48. };
  49. return __assign.apply(this, arguments);
  50. };
  51. function __read(o, n) {
  52. var m = typeof Symbol === "function" && o[Symbol.iterator];
  53. if (!m) return o;
  54. var i = m.call(o), r, ar = [], e;
  55. try {
  56. while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
  57. }
  58. catch (error) { e = { error: error }; }
  59. finally {
  60. try {
  61. if (r && !r.done && (m = i["return"])) m.call(i);
  62. }
  63. finally { if (e) throw e.error; }
  64. }
  65. return ar;
  66. }
  67. function __spreadArray(to, from, pack) {
  68. if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
  69. if (ar || !(i in from)) {
  70. if (!ar) ar = Array.prototype.slice.call(from, 0, i);
  71. ar[i] = from[i];
  72. }
  73. }
  74. return to.concat(ar || Array.prototype.slice.call(from));
  75. }
  76. typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
  77. var e = new Error(message);
  78. return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
  79. };
  80. // Important: this file is generated from the 'build' script and should not be
  81. // edited directly
  82. var version = '4.1.5';
  83. var hasOwnProperty = Object.prototype.hasOwnProperty;
  84. /**
  85. * Simpler helper method to check for a boolean type simply for the benefit of
  86. * gaining better compression when minified by not needing to have multiple
  87. * `typeof` comparisons in the codebase.
  88. */
  89. function isBoolean(value) {
  90. return typeof value === 'boolean';
  91. }
  92. /**
  93. * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the
  94. * end of the string (by default, two periods: '..'). If the `str` length does not exceed
  95. * `len`, the string will be returned unchanged.
  96. *
  97. * @param {String} str The string to truncate and add an ellipsis to.
  98. * @param {Number} truncateLen The length to truncate the string at.
  99. * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str`
  100. * when truncated. Defaults to '...'
  101. */
  102. function ellipsis(str, truncateLen, ellipsisChars) {
  103. var ellipsisLength;
  104. if (str.length > truncateLen) {
  105. if (ellipsisChars == null) {
  106. ellipsisChars = '&hellip;';
  107. ellipsisLength = 3;
  108. }
  109. else {
  110. ellipsisLength = ellipsisChars.length;
  111. }
  112. str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars;
  113. }
  114. return str;
  115. }
  116. /**
  117. * Removes array elements based on a filtering function. Mutates the input
  118. * array.
  119. *
  120. * Using this instead of the ES5 Array.prototype.filter() function to prevent
  121. * creating many new arrays in memory for filtering.
  122. *
  123. * @param arr The array to remove elements from. This array is mutated.
  124. * @param fn The predicate function which should return `true` to remove an
  125. * element.
  126. */
  127. function removeWithPredicate(arr, fn) {
  128. for (var i = arr.length - 1; i >= 0; i--) {
  129. if (fn(arr[i]) === true) {
  130. arr.splice(i, 1);
  131. }
  132. }
  133. }
  134. /**
  135. * Function that should never be called but is used to check that every
  136. * enum value is handled using TypeScript's 'never' type.
  137. */
  138. /* istanbul ignore next */
  139. function assertNever(theValue) {
  140. throw new Error("Unhandled case for value: '".concat(theValue, "'"));
  141. }
  142. // Regular expression to match whitespace
  143. var whitespaceRe = /\s+/;
  144. /**
  145. * @class Autolinker.HtmlTag
  146. * @extends Object
  147. *
  148. * Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically.
  149. *
  150. * Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use
  151. * this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}.
  152. *
  153. * ## Examples
  154. *
  155. * Example instantiation:
  156. *
  157. * var tag = new Autolinker.HtmlTag( {
  158. * tagName : 'a',
  159. * attrs : { 'href': 'http://google.com', 'class': 'external-link' },
  160. * innerHtml : 'Google'
  161. * } );
  162. *
  163. * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
  164. *
  165. * // Individual accessor methods
  166. * tag.getTagName(); // 'a'
  167. * tag.getAttr( 'href' ); // 'http://google.com'
  168. * tag.hasClass( 'external-link' ); // true
  169. *
  170. *
  171. * Using mutator methods (which may be used in combination with instantiation config properties):
  172. *
  173. * var tag = new Autolinker.HtmlTag();
  174. * tag.setTagName( 'a' );
  175. * tag.setAttr( 'href', 'http://google.com' );
  176. * tag.addClass( 'external-link' );
  177. * tag.setInnerHtml( 'Google' );
  178. *
  179. * tag.getTagName(); // 'a'
  180. * tag.getAttr( 'href' ); // 'http://google.com'
  181. * tag.hasClass( 'external-link' ); // true
  182. *
  183. * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
  184. *
  185. *
  186. * ## Example use within a {@link Autolinker#replaceFn replaceFn}
  187. *
  188. * var html = Autolinker.link( "Test google.com", {
  189. * replaceFn : function( match ) {
  190. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance, configured with the Match's href and anchor text
  191. * tag.setAttr( 'rel', 'nofollow' );
  192. *
  193. * return tag;
  194. * }
  195. * } );
  196. *
  197. * // generated html:
  198. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  199. *
  200. *
  201. * ## Example use with a new tag for the replacement
  202. *
  203. * var html = Autolinker.link( "Test google.com", {
  204. * replaceFn : function( match ) {
  205. * var tag = new Autolinker.HtmlTag( {
  206. * tagName : 'button',
  207. * attrs : { 'title': 'Load URL: ' + match.getAnchorHref() },
  208. * innerHtml : 'Load URL: ' + match.getAnchorText()
  209. * } );
  210. *
  211. * return tag;
  212. * }
  213. * } );
  214. *
  215. * // generated html:
  216. * // Test <button title="Load URL: http://google.com">Load URL: google.com</button>
  217. */
  218. var HtmlTag = /** @class */ (function () {
  219. /**
  220. * @method constructor
  221. * @param {Object} [cfg] The configuration properties for this class, in an Object (map)
  222. */
  223. function HtmlTag(cfg) {
  224. if (cfg === void 0) { cfg = {}; }
  225. /**
  226. * @cfg {String} tagName
  227. *
  228. * The tag name. Ex: 'a', 'button', etc.
  229. *
  230. * Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString}
  231. * is executed.
  232. */
  233. this.tagName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  234. /**
  235. * @cfg {Object.<String, String>} attrs
  236. *
  237. * An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the
  238. * values are the attribute values.
  239. */
  240. this.attrs = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  241. /**
  242. * @cfg {String} innerHTML
  243. *
  244. * The inner HTML for the tag.
  245. */
  246. this.innerHTML = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  247. this.tagName = cfg.tagName || '';
  248. this.attrs = cfg.attrs || {};
  249. this.innerHTML = cfg.innerHtml || cfg.innerHTML || ''; // accept either the camelCased form or the fully capitalized acronym as in the DOM
  250. }
  251. /**
  252. * Sets the tag name that will be used to generate the tag with.
  253. *
  254. * @param {String} tagName
  255. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  256. */
  257. HtmlTag.prototype.setTagName = function (tagName) {
  258. this.tagName = tagName;
  259. return this;
  260. };
  261. /**
  262. * Retrieves the tag name.
  263. *
  264. * @return {String}
  265. */
  266. HtmlTag.prototype.getTagName = function () {
  267. return this.tagName;
  268. };
  269. /**
  270. * Sets an attribute on the HtmlTag.
  271. *
  272. * @param {String} attrName The attribute name to set.
  273. * @param {String} attrValue The attribute value to set.
  274. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  275. */
  276. HtmlTag.prototype.setAttr = function (attrName, attrValue) {
  277. var tagAttrs = this.getAttrs();
  278. tagAttrs[attrName] = attrValue;
  279. return this;
  280. };
  281. /**
  282. * Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`.
  283. *
  284. * @param {String} attrName The attribute name to retrieve.
  285. * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag.
  286. */
  287. HtmlTag.prototype.getAttr = function (attrName) {
  288. return this.getAttrs()[attrName];
  289. };
  290. /**
  291. * Sets one or more attributes on the HtmlTag.
  292. *
  293. * @param {Object.<String, String>} attrs A key/value Object (map) of the attributes to set.
  294. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  295. */
  296. HtmlTag.prototype.setAttrs = function (attrs) {
  297. Object.assign(this.getAttrs(), attrs);
  298. return this;
  299. };
  300. /**
  301. * Retrieves the attributes Object (map) for the HtmlTag.
  302. *
  303. * @return {Object.<String, String>} A key/value object of the attributes for the HtmlTag.
  304. */
  305. HtmlTag.prototype.getAttrs = function () {
  306. return this.attrs;
  307. };
  308. /**
  309. * Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag.
  310. *
  311. * @param {String} cssClass One or more space-separated CSS classes to set (overwrite).
  312. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  313. */
  314. HtmlTag.prototype.setClass = function (cssClass) {
  315. return this.setAttr('class', cssClass);
  316. };
  317. /**
  318. * Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes.
  319. *
  320. * @param {String} cssClass One or more space-separated CSS classes to add.
  321. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  322. */
  323. HtmlTag.prototype.addClass = function (cssClass) {
  324. var classAttr = this.getClass();
  325. var classes = !classAttr ? [] : classAttr.split(whitespaceRe);
  326. var newClasses = cssClass.split(whitespaceRe);
  327. var newClass;
  328. while ((newClass = newClasses.shift())) {
  329. if (classes.indexOf(newClass) === -1) {
  330. classes.push(newClass);
  331. }
  332. }
  333. this.getAttrs()['class'] = classes.join(' ');
  334. return this;
  335. };
  336. /**
  337. * Convenience method to remove one or more CSS classes from the HtmlTag.
  338. *
  339. * @param {String} cssClass One or more space-separated CSS classes to remove.
  340. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  341. */
  342. HtmlTag.prototype.removeClass = function (cssClass) {
  343. var classAttr = this.getClass();
  344. var classes = !classAttr ? [] : classAttr.split(whitespaceRe);
  345. var removeClasses = cssClass.split(whitespaceRe);
  346. var removeClass;
  347. while (classes.length && (removeClass = removeClasses.shift())) {
  348. var idx = classes.indexOf(removeClass);
  349. if (idx !== -1) {
  350. classes.splice(idx, 1);
  351. }
  352. }
  353. this.getAttrs()['class'] = classes.join(' ');
  354. return this;
  355. };
  356. /**
  357. * Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when
  358. * there are multiple.
  359. *
  360. * @return {String}
  361. */
  362. HtmlTag.prototype.getClass = function () {
  363. return this.getAttrs()['class'] || '';
  364. };
  365. /**
  366. * Convenience method to check if the tag has a CSS class or not.
  367. *
  368. * @param {String} cssClass The CSS class to check for.
  369. * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise.
  370. */
  371. HtmlTag.prototype.hasClass = function (cssClass) {
  372. return (' ' + this.getClass() + ' ').indexOf(' ' + cssClass + ' ') !== -1;
  373. };
  374. /**
  375. * Sets the inner HTML for the tag.
  376. *
  377. * @param {String} html The inner HTML to set.
  378. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  379. */
  380. HtmlTag.prototype.setInnerHTML = function (html) {
  381. this.innerHTML = html;
  382. return this;
  383. };
  384. /**
  385. * Backwards compatibility method name.
  386. *
  387. * @param {String} html The inner HTML to set.
  388. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  389. */
  390. HtmlTag.prototype.setInnerHtml = function (html) {
  391. return this.setInnerHTML(html);
  392. };
  393. /**
  394. * Retrieves the inner HTML for the tag.
  395. *
  396. * @return {String}
  397. */
  398. HtmlTag.prototype.getInnerHTML = function () {
  399. return this.innerHTML || '';
  400. };
  401. /**
  402. * Backward compatibility method name.
  403. *
  404. * @return {String}
  405. */
  406. HtmlTag.prototype.getInnerHtml = function () {
  407. return this.getInnerHTML();
  408. };
  409. /**
  410. * Generates the HTML string for the tag.
  411. *
  412. * @return {String}
  413. */
  414. HtmlTag.prototype.toAnchorString = function () {
  415. var tagName = this.getTagName();
  416. var attrsStr = this.buildAttrsStr();
  417. attrsStr = attrsStr ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes
  418. return ['<', tagName, attrsStr, '>', this.getInnerHtml(), '</', tagName, '>'].join('');
  419. };
  420. /**
  421. * Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate
  422. * the stringified HtmlTag.
  423. *
  424. * @protected
  425. * @return {String} Example return: `attr1="value1" attr2="value2"`
  426. */
  427. HtmlTag.prototype.buildAttrsStr = function () {
  428. var attrs = this.getAttrs(), attrsArr = [];
  429. for (var prop in attrs) {
  430. if (hasOwnProperty.call(attrs, prop)) {
  431. attrsArr.push(prop + '="' + attrs[prop] + '"');
  432. }
  433. }
  434. return attrsArr.join(' ');
  435. };
  436. return HtmlTag;
  437. }());
  438. /**
  439. * Date: 2015-10-05
  440. * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
  441. *
  442. * A truncation feature, where the ellipsis will be placed at a section within
  443. * the URL making it still somewhat human readable.
  444. *
  445. * @param {String} url A URL.
  446. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  447. * @param {String} ellipsisChars The characters to place within the url, e.g. "...".
  448. * @return {String} The truncated URL.
  449. */
  450. function truncateSmart(url, truncateLen, ellipsisChars) {
  451. var ellipsisLengthBeforeParsing;
  452. var ellipsisLength;
  453. if (ellipsisChars == null) {
  454. ellipsisChars = '&hellip;';
  455. ellipsisLength = 3;
  456. ellipsisLengthBeforeParsing = 8;
  457. }
  458. else {
  459. ellipsisLength = ellipsisChars.length;
  460. ellipsisLengthBeforeParsing = ellipsisChars.length;
  461. }
  462. // If the URL is shorter than the truncate length, return it as is
  463. if (url.length <= truncateLen) {
  464. return url;
  465. }
  466. var availableLength = truncateLen - ellipsisLength;
  467. var urlObj = parseUrl(url);
  468. // Clean up the URL by removing any malformed query string
  469. // (e.g. "?foo=bar?ignorethis")
  470. if (urlObj.query) {
  471. var matchQuery = urlObj.query.match(/^(.*?)(?=(\?|#))(.*?)$/i);
  472. if (matchQuery) {
  473. // Malformed URL; two or more "?". Removed any content behind the 2nd.
  474. urlObj.query = urlObj.query.substr(0, matchQuery[1].length);
  475. url = buildUrl(urlObj);
  476. }
  477. }
  478. if (url.length <= truncateLen) {
  479. return url; // removing a malformed query string brought the URL under the truncateLength
  480. }
  481. // Clean up the URL by removing 'www.' from the host if it exists
  482. if (urlObj.host) {
  483. urlObj.host = urlObj.host.replace(/^www\./, '');
  484. url = buildUrl(urlObj);
  485. }
  486. if (url.length <= truncateLen) {
  487. return url; // removing 'www.' brought the URL under the truncateLength
  488. }
  489. // Process and build the truncated URL, starting with the hostname
  490. var truncatedUrl = '';
  491. if (urlObj.host) {
  492. truncatedUrl += urlObj.host;
  493. }
  494. if (truncatedUrl.length >= availableLength) {
  495. if (urlObj.host.length === truncateLen) {
  496. return (urlObj.host.substr(0, truncateLen - ellipsisLength) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
  497. }
  498. return buildSegment(truncatedUrl, availableLength, ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
  499. }
  500. // If we still have available chars left, add the path and query string
  501. var pathAndQuery = '';
  502. if (urlObj.path) {
  503. pathAndQuery += '/' + urlObj.path;
  504. }
  505. if (urlObj.query) {
  506. pathAndQuery += '?' + urlObj.query;
  507. }
  508. if (pathAndQuery) {
  509. if ((truncatedUrl + pathAndQuery).length >= availableLength) {
  510. if ((truncatedUrl + pathAndQuery).length == truncateLen) {
  511. return (truncatedUrl + pathAndQuery).substr(0, truncateLen);
  512. }
  513. var remainingAvailableLength = availableLength - truncatedUrl.length;
  514. return (truncatedUrl + buildSegment(pathAndQuery, remainingAvailableLength, ellipsisChars)).substr(0, availableLength + ellipsisLengthBeforeParsing);
  515. }
  516. else {
  517. truncatedUrl += pathAndQuery;
  518. }
  519. }
  520. // If we still have available chars left, add the fragment
  521. if (urlObj.fragment) {
  522. var fragment = '#' + urlObj.fragment;
  523. if ((truncatedUrl + fragment).length >= availableLength) {
  524. if ((truncatedUrl + fragment).length == truncateLen) {
  525. return (truncatedUrl + fragment).substr(0, truncateLen);
  526. }
  527. var remainingAvailableLength2 = availableLength - truncatedUrl.length;
  528. return (truncatedUrl + buildSegment(fragment, remainingAvailableLength2, ellipsisChars)).substr(0, availableLength + ellipsisLengthBeforeParsing);
  529. }
  530. else {
  531. truncatedUrl += fragment;
  532. }
  533. }
  534. // If we still have available chars left, add the scheme
  535. if (urlObj.scheme && urlObj.host) {
  536. var scheme = urlObj.scheme + '://';
  537. if ((truncatedUrl + scheme).length < availableLength) {
  538. return (scheme + truncatedUrl).substr(0, truncateLen);
  539. }
  540. }
  541. if (truncatedUrl.length <= truncateLen) {
  542. return truncatedUrl;
  543. }
  544. var end = '';
  545. if (availableLength > 0) {
  546. end = truncatedUrl.substr(-1 * Math.floor(availableLength / 2));
  547. }
  548. return (truncatedUrl.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
  549. }
  550. /**
  551. * Parses a URL into its components: scheme, host, path, query, and fragment.
  552. */
  553. function parseUrl(url) {
  554. // Functionality inspired by PHP function of same name
  555. var urlObj = {};
  556. var urlSub = url;
  557. // Parse scheme
  558. var match = urlSub.match(/^([a-z]+):\/\//i);
  559. if (match) {
  560. urlObj.scheme = match[1];
  561. urlSub = urlSub.slice(match[0].length);
  562. }
  563. // Parse host
  564. match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i);
  565. if (match) {
  566. urlObj.host = match[1];
  567. urlSub = urlSub.slice(match[0].length);
  568. }
  569. // Parse path
  570. match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i);
  571. if (match) {
  572. urlObj.path = match[1];
  573. urlSub = urlSub.slice(match[0].length);
  574. }
  575. // Parse query
  576. match = urlSub.match(/^\?(.*?)(?=(#|$))/i);
  577. if (match) {
  578. urlObj.query = match[1];
  579. urlSub = urlSub.slice(match[0].length);
  580. }
  581. // Parse fragment
  582. match = urlSub.match(/^#(.*?)$/i);
  583. if (match) {
  584. urlObj.fragment = match[1];
  585. //urlSub = urlSub.slice(match[0].length); -- not used. Uncomment if adding another block.
  586. }
  587. return urlObj;
  588. }
  589. function buildUrl(urlObj) {
  590. var url = '';
  591. if (urlObj.scheme && urlObj.host) {
  592. url += urlObj.scheme + '://';
  593. }
  594. if (urlObj.host) {
  595. url += urlObj.host;
  596. }
  597. if (urlObj.path) {
  598. url += '/' + urlObj.path;
  599. }
  600. if (urlObj.query) {
  601. url += '?' + urlObj.query;
  602. }
  603. if (urlObj.fragment) {
  604. url += '#' + urlObj.fragment;
  605. }
  606. return url;
  607. }
  608. function buildSegment(segment, remainingAvailableLength, ellipsisChars) {
  609. var remainingAvailableLengthHalf = remainingAvailableLength / 2;
  610. var startOffset = Math.ceil(remainingAvailableLengthHalf);
  611. var endOffset = -1 * Math.floor(remainingAvailableLengthHalf);
  612. var end = '';
  613. if (endOffset < 0) {
  614. end = segment.substr(endOffset);
  615. }
  616. return segment.substr(0, startOffset) + ellipsisChars + end;
  617. }
  618. /**
  619. * Date: 2015-10-05
  620. * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
  621. *
  622. * A truncation feature, where the ellipsis will be placed in the dead-center of the URL.
  623. *
  624. * @param {String} url A URL.
  625. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  626. * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
  627. * @return {String} The truncated URL.
  628. */
  629. function truncateMiddle(url, truncateLen, ellipsisChars) {
  630. if (url.length <= truncateLen) {
  631. return url;
  632. }
  633. var ellipsisLengthBeforeParsing;
  634. var ellipsisLength;
  635. if (ellipsisChars == null) {
  636. ellipsisChars = '&hellip;';
  637. ellipsisLengthBeforeParsing = 8;
  638. ellipsisLength = 3;
  639. }
  640. else {
  641. ellipsisLengthBeforeParsing = ellipsisChars.length;
  642. ellipsisLength = ellipsisChars.length;
  643. }
  644. var availableLength = truncateLen - ellipsisLength;
  645. var end = '';
  646. if (availableLength > 0) {
  647. end = url.substr(-1 * Math.floor(availableLength / 2));
  648. }
  649. return (url.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
  650. }
  651. /**
  652. * A truncation feature where the ellipsis will be placed at the end of the URL.
  653. *
  654. * @param {String} anchorText
  655. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  656. * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
  657. * @return {String} The truncated URL.
  658. */
  659. function truncateEnd(anchorText, truncateLen, ellipsisChars) {
  660. return ellipsis(anchorText, truncateLen, ellipsisChars);
  661. }
  662. /**
  663. * @protected
  664. * @class Autolinker.AnchorTagBuilder
  665. * @extends Object
  666. *
  667. * Builds anchor (&lt;a&gt;) tags for the Autolinker utility when a match is
  668. * found.
  669. *
  670. * Normally this class is instantiated, configured, and used internally by an
  671. * {@link Autolinker} instance, but may actually be used indirectly in a
  672. * {@link Autolinker#replaceFn replaceFn} to create {@link Autolinker.HtmlTag HtmlTag}
  673. * instances which may be modified before returning from the
  674. * {@link Autolinker#replaceFn replaceFn}. For example:
  675. *
  676. * var html = Autolinker.link("Test google.com", {
  677. * replaceFn: function(match) {
  678. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
  679. * tag.setAttr('rel', 'nofollow');
  680. *
  681. * return tag;
  682. * }
  683. * });
  684. *
  685. * // generated html:
  686. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  687. */
  688. var AnchorTagBuilder = /** @class */ (function () {
  689. /**
  690. * @method constructor
  691. * @param {Object} [cfg] The configuration options for the AnchorTagBuilder instance, specified in an Object (map).
  692. */
  693. function AnchorTagBuilder(cfg) {
  694. if (cfg === void 0) { cfg = {}; }
  695. /**
  696. * @cfg {Boolean} newWindow
  697. * @inheritdoc Autolinker#newWindow
  698. */
  699. this.newWindow = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  700. /**
  701. * @cfg {Object} truncate
  702. * @inheritdoc Autolinker#truncate
  703. */
  704. this.truncate = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  705. /**
  706. * @cfg {String} className
  707. * @inheritdoc Autolinker#className
  708. */
  709. this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  710. this.newWindow = cfg.newWindow || false;
  711. this.truncate = cfg.truncate || {};
  712. this.className = cfg.className || '';
  713. }
  714. /**
  715. * Generates the actual anchor (&lt;a&gt;) tag to use in place of the
  716. * matched text, via its `match` object.
  717. *
  718. * @param match The Match instance to generate an anchor tag from.
  719. * @return The HtmlTag instance for the anchor tag.
  720. */
  721. AnchorTagBuilder.prototype.build = function (match) {
  722. return new HtmlTag({
  723. tagName: 'a',
  724. attrs: this.createAttrs(match),
  725. innerHtml: this.processAnchorText(match.getAnchorText()),
  726. });
  727. };
  728. /**
  729. * Creates the Object (map) of the HTML attributes for the anchor (&lt;a&gt;)
  730. * tag being generated.
  731. *
  732. * @protected
  733. * @param match The Match instance to generate an anchor tag from.
  734. * @return A key/value Object (map) of the anchor tag's attributes.
  735. */
  736. AnchorTagBuilder.prototype.createAttrs = function (match) {
  737. var attrs = {
  738. href: match.getAnchorHref(), // we'll always have the `href` attribute
  739. };
  740. var cssClass = this.createCssClass(match);
  741. if (cssClass) {
  742. attrs['class'] = cssClass;
  743. }
  744. if (this.newWindow) {
  745. attrs['target'] = '_blank';
  746. attrs['rel'] = 'noopener noreferrer'; // Issue #149. See https://mathiasbynens.github.io/rel-noopener/
  747. }
  748. if (this.truncate.length && this.truncate.length < match.getAnchorText().length) {
  749. attrs['title'] = match.getAnchorHref();
  750. }
  751. return attrs;
  752. };
  753. /**
  754. * Creates the CSS class that will be used for a given anchor tag, based on
  755. * the `matchType` and the {@link #className} config.
  756. *
  757. * Example returns:
  758. *
  759. * - "" // no {@link #className}
  760. * - "myLink myLink-url" // url match
  761. * - "myLink myLink-email" // email match
  762. * - "myLink myLink-phone" // phone match
  763. * - "myLink myLink-hashtag" // hashtag match
  764. * - "myLink myLink-mention myLink-twitter" // mention match with Twitter service
  765. *
  766. * @protected
  767. * @param match The Match instance to generate an
  768. * anchor tag from.
  769. * @return The CSS class string for the link. Example return:
  770. * "myLink myLink-url". If no {@link #className} was configured, returns
  771. * an empty string.
  772. */
  773. AnchorTagBuilder.prototype.createCssClass = function (match) {
  774. var className = this.className;
  775. if (!className) {
  776. return '';
  777. }
  778. else {
  779. var returnClasses = [className], cssClassSuffixes = match.getCssClassSuffixes();
  780. for (var i = 0, len = cssClassSuffixes.length; i < len; i++) {
  781. returnClasses.push(className + '-' + cssClassSuffixes[i]);
  782. }
  783. return returnClasses.join(' ');
  784. }
  785. };
  786. /**
  787. * Processes the `anchorText` by truncating the text according to the
  788. * {@link #truncate} config.
  789. *
  790. * @private
  791. * @param anchorText The anchor tag's text (i.e. what will be
  792. * displayed).
  793. * @return The processed `anchorText`.
  794. */
  795. AnchorTagBuilder.prototype.processAnchorText = function (anchorText) {
  796. anchorText = this.doTruncate(anchorText);
  797. return anchorText;
  798. };
  799. /**
  800. * Performs the truncation of the `anchorText` based on the {@link #truncate}
  801. * option. If the `anchorText` is longer than the length specified by the
  802. * {@link #truncate} option, the truncation is performed based on the
  803. * `location` property. See {@link #truncate} for details.
  804. *
  805. * @private
  806. * @param anchorText The anchor tag's text (i.e. what will be
  807. * displayed).
  808. * @return The truncated anchor text.
  809. */
  810. AnchorTagBuilder.prototype.doTruncate = function (anchorText) {
  811. var truncate = this.truncate;
  812. if (!truncate.length)
  813. return anchorText;
  814. var truncateLength = truncate.length, truncateLocation = truncate.location;
  815. if (truncateLocation === 'smart') {
  816. return truncateSmart(anchorText, truncateLength);
  817. }
  818. else if (truncateLocation === 'middle') {
  819. return truncateMiddle(anchorText, truncateLength);
  820. }
  821. else {
  822. return truncateEnd(anchorText, truncateLength);
  823. }
  824. };
  825. return AnchorTagBuilder;
  826. }());
  827. /**
  828. * @abstract
  829. * @class Autolinker.match.AbstractMatch
  830. *
  831. * Represents a match found in an input string which should be Autolinked. A Match object is what is provided in a
  832. * {@link Autolinker#replaceFn replaceFn}, and may be used to query for details about the match.
  833. *
  834. * For example:
  835. *
  836. * var input = "..."; // string with URLs, Email Addresses, and Mentions (Twitter, Instagram, Soundcloud)
  837. *
  838. * var linkedText = Autolinker.link( input, {
  839. * replaceFn : function( match ) {
  840. * console.log( "href = ", match.getAnchorHref() );
  841. * console.log( "text = ", match.getAnchorText() );
  842. *
  843. * switch( match.getType() ) {
  844. * case 'url' :
  845. * console.log( "url: ", match.getUrl() );
  846. *
  847. * case 'email' :
  848. * console.log( "email: ", match.getEmail() );
  849. *
  850. * case 'mention' :
  851. * console.log( "mention: ", match.getMention() );
  852. * }
  853. * }
  854. * } );
  855. *
  856. * See the {@link Autolinker} class for more details on using the {@link Autolinker#replaceFn replaceFn}.
  857. */
  858. var AbstractMatch = /** @class */ (function () {
  859. /**
  860. * @member Autolinker.match.Match
  861. * @method constructor
  862. * @param {Object} cfg The configuration properties for the Match
  863. * instance, specified in an Object (map).
  864. */
  865. function AbstractMatch(cfg) {
  866. /**
  867. * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required)
  868. *
  869. * Reference to the AnchorTagBuilder instance to use to generate an anchor
  870. * tag for the Match.
  871. */
  872. // @ts-expect-error Property used just to get the above doc comment into the ES5 output and documentation generator
  873. this._ = null;
  874. /**
  875. * @cfg {String} matchedText (required)
  876. *
  877. * The original text that was matched by the {@link Autolinker.matcher.Matcher}.
  878. */
  879. this.matchedText = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  880. /**
  881. * @cfg {Number} offset (required)
  882. *
  883. * The offset of where the match was made in the input string.
  884. */
  885. this.offset = 0; // default value just to get the above doc comment in the ES5 output and documentation generator
  886. this.tagBuilder = cfg.tagBuilder;
  887. this.matchedText = cfg.matchedText;
  888. this.offset = cfg.offset;
  889. }
  890. /**
  891. * Returns the original text that was matched.
  892. *
  893. * @return {String}
  894. */
  895. AbstractMatch.prototype.getMatchedText = function () {
  896. return this.matchedText;
  897. };
  898. /**
  899. * Sets the {@link #offset} of where the match was made in the input string.
  900. *
  901. * A {@link Autolinker.matcher.Matcher} will be fed only HTML text nodes,
  902. * and will therefore set an original offset that is relative to the HTML
  903. * text node itself. However, we want this offset to be relative to the full
  904. * HTML input string, and thus if using {@link Autolinker#parse} (rather
  905. * than calling a {@link Autolinker.matcher.Matcher} directly), then this
  906. * offset is corrected after the Matcher itself has done its job.
  907. *
  908. * @private
  909. * @param {Number} offset
  910. */
  911. AbstractMatch.prototype.setOffset = function (offset) {
  912. this.offset = offset;
  913. };
  914. /**
  915. * Returns the offset of where the match was made in the input string. This
  916. * is the 0-based index of the match.
  917. *
  918. * @return {Number}
  919. */
  920. AbstractMatch.prototype.getOffset = function () {
  921. return this.offset;
  922. };
  923. /**
  924. * Returns the CSS class suffix(es) for this match.
  925. *
  926. * A CSS class suffix is appended to the {@link Autolinker#className} in
  927. * the {@link Autolinker.AnchorTagBuilder} when a match is translated into
  928. * an anchor tag.
  929. *
  930. * For example, if {@link Autolinker#className} was configured as 'myLink',
  931. * and this method returns `[ 'url' ]`, the final class name of the element
  932. * will become: 'myLink myLink-url'.
  933. *
  934. * The match may provide multiple CSS class suffixes to be appended to the
  935. * {@link Autolinker#className} in order to facilitate better styling
  936. * options for different match criteria. See {@link Autolinker.match.Mention}
  937. * for an example.
  938. *
  939. * By default, this method returns a single array with the match's
  940. * {@link #getType type} name, but may be overridden by subclasses.
  941. *
  942. * @return {String[]}
  943. */
  944. AbstractMatch.prototype.getCssClassSuffixes = function () {
  945. return [this.type];
  946. };
  947. /**
  948. * Builds and returns an {@link Autolinker.HtmlTag} instance based on the
  949. * Match.
  950. *
  951. * This can be used to easily generate anchor tags from matches, and either
  952. * return their HTML string, or modify them before doing so.
  953. *
  954. * Example Usage:
  955. *
  956. * var tag = match.buildTag();
  957. * tag.addClass( 'cordova-link' );
  958. * tag.setAttr( 'target', '_system' );
  959. *
  960. * tag.toAnchorString(); // <a href="http://google.com" class="cordova-link" target="_system">Google</a>
  961. *
  962. * Example Usage in {@link Autolinker#replaceFn}:
  963. *
  964. * var html = Autolinker.link( "Test google.com", {
  965. * replaceFn : function( match ) {
  966. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
  967. * tag.setAttr( 'rel', 'nofollow' );
  968. *
  969. * return tag;
  970. * }
  971. * } );
  972. *
  973. * // generated html:
  974. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  975. */
  976. AbstractMatch.prototype.buildTag = function () {
  977. return this.tagBuilder.build(this);
  978. };
  979. return AbstractMatch;
  980. }());
  981. // NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
  982. // INSTEAD, RUN: npm run generate-char-utils
  983. /**
  984. * Determines if the given character `c` matches the regular expression /[\x00-\x1F\x7F]/
  985. * by checking it via character code in a binary search fashion.
  986. *
  987. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  988. * on the character itself.
  989. *
  990. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  991. *
  992. * npm run generate-char-utils
  993. */
  994. function isControlChar(c) {
  995. return ((c >= 0 && c <= 31) || c == 127);
  996. }
  997. /**
  998. * Determines if the given character `c` matches the regular expression /[A-Za-z]/
  999. * by checking it via character code in a binary search fashion.
  1000. *
  1001. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1002. * on the character itself.
  1003. *
  1004. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1005. *
  1006. * npm run generate-char-utils
  1007. */
  1008. function isAsciiLetterChar(c) {
  1009. return ((c >= 65 && c <= 90) || (c >= 97 && c <= 122));
  1010. }
  1011. /**
  1012. * Determines if the given character `c` matches the regular expression /\d/
  1013. * by checking it via character code in a binary search fashion.
  1014. *
  1015. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1016. * on the character itself.
  1017. *
  1018. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1019. *
  1020. * npm run generate-char-utils
  1021. */
  1022. function isDigitChar(c) {
  1023. return (c >= 48 && c <= 57);
  1024. }
  1025. /**
  1026. * Determines if the given character `c` matches the regular expression /['"]/
  1027. * by checking it via character code in a binary search fashion.
  1028. *
  1029. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1030. * on the character itself.
  1031. *
  1032. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1033. *
  1034. * npm run generate-char-utils
  1035. */
  1036. function isQuoteChar(c) {
  1037. return (c == 34 || c == 39);
  1038. }
  1039. /**
  1040. * Determines if the given character `c` matches the regular expression /\s/
  1041. * by checking it via character code in a binary search fashion.
  1042. *
  1043. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1044. * on the character itself.
  1045. *
  1046. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1047. *
  1048. * npm run generate-char-utils
  1049. */
  1050. function isWhitespaceChar(c) {
  1051. return (c < 8232 ? (c < 160 ? ((c >= 9 && c <= 13) || c == 32) : (c < 5760 ? c == 160 : (c == 5760 || (c >= 8192 && c <= 8202)))) : (c < 8287 ? ((c >= 8232 && c <= 8233) || c == 8239) : (c < 12288 ? c == 8287 : (c == 12288 || c == 65279))));
  1052. }
  1053. /**
  1054. * Determines if the given character `c` matches the regular expression /[A-Za-z\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B4\u08B6-\u08BD\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0AF9\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58-\u0C5A\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D54-\u0D56\u0D5F-\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F5\u13F8-\u13FD\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16F1-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u1884\u1887-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19B0-\u19C9\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FD5\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7AE\uA7B0-\uA7B7\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB65\uAB70-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC\u2700-\u27bf\udde6-\uddff\ud800-\udbff\udc00-\udfff\ufe0e\ufe0f\u0300-\u036f\ufe20-\ufe23\u20d0-\u20f0\ud83c\udffb-\udfff\u200d\u3299\u3297\u303d\u3030\u24c2\ud83c\udd70-\udd71\udd7e-\udd7f\udd8e\udd91-\udd9a\udde6-\uddff\ude01-\ude02\ude1a\ude2f\ude32-\ude3a\ude50-\ude51\u203c\u2049\u25aa-\u25ab\u25b6\u25c0\u25fb-\u25fe\u00a9\u00ae\u2122\u2139\udc04\u2600-\u26FF\u2b05\u2b06\u2b07\u2b1b\u2b1c\u2b50\u2b55\u231a\u231b\u2328\u23cf\u23e9-\u23f3\u23f8-\u23fa\udccf\u2935\u2934\u2190-\u21ff\u0300-\u036F\u0483-\u0489\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u065F\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u0711\u0730-\u074A\u07A6-\u07B0\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08D4-\u08E1\u08E3-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A70\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0F18\u0F19\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F\u109A-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u180B-\u180D\u1885\u1886\u18A9\u1920-\u192B\u1930-\u193B\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F\u1AB0-\u1ABE\u1B00-\u1B04\u1B34-\u1B44\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BE6-\u1BF3\u1C24-\u1C37\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFB-\u1DFF\u20D0-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA66F-\uA672\uA674-\uA67D\uA69E\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C5\uA8E0-\uA8F1\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9E5\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uFB1E\uFE00-\uFE0F\uFE20-\uFE2F0-9\u0660-\u0669\u06F0-\u06F9\u07C0-\u07C9\u0966-\u096F\u09E6-\u09EF\u0A66-\u0A6F\u0AE6-\u0AEF\u0B66-\u0B6F\u0BE6-\u0BEF\u0C66-\u0C6F\u0CE6-\u0CEF\u0D66-\u0D6F\u0DE6-\u0DEF\u0E50-\u0E59\u0ED0-\u0ED9\u0F20-\u0F29\u1040-\u1049\u1090-\u1099\u17E0-\u17E9\u1810-\u1819\u1946-\u194F\u19D0-\u19D9\u1A80-\u1A89\u1A90-\u1A99\u1B50-\u1B59\u1BB0-\u1BB9\u1C40-\u1C49\u1C50-\u1C59\uA620-\uA629\uA8D0-\uA8D9\uA900-\uA909\uA9D0-\uA9D9\uA9F0-\uA9F9\uAA50-\uAA59\uABF0-\uABF9\uFF10-\uFF19]/
  1055. * by checking it via character code in a binary search fashion.
  1056. *
  1057. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1058. * on the character itself.
  1059. *
  1060. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1061. *
  1062. * npm run generate-char-utils
  1063. */
  1064. function isAlphaNumericOrMarkChar(c) {
  1065. return (c < 4800 ? (c < 2949 ? (c < 2451 ? (c < 1425 ? (c < 768 ? (c < 192 ? (c < 169 ? (c < 65 ? (c >= 48 && c <= 57) : ((c >= 65 && c <= 90) || (c >= 97 && c <= 122))) : (c < 181 ? ((c >= 169 && c <= 170) || c == 174) : (c == 181 || c == 186))) : (c < 710 ? (c < 216 ? (c >= 192 && c <= 214) : ((c >= 216 && c <= 246) || (c >= 248 && c <= 705))) : (c < 748 ? ((c >= 710 && c <= 721) || (c >= 736 && c <= 740)) : (c == 748 || c == 750)))) : (c < 910 ? (c < 895 ? (c < 886 ? (c >= 768 && c <= 884) : ((c >= 886 && c <= 887) || (c >= 890 && c <= 893))) : (c < 904 ? (c == 895 || c == 902) : ((c >= 904 && c <= 906) || c == 908))) : (c < 1155 ? (c < 931 ? (c >= 910 && c <= 929) : ((c >= 931 && c <= 1013) || (c >= 1015 && c <= 1153))) : (c < 1369 ? ((c >= 1155 && c <= 1327) || (c >= 1329 && c <= 1366)) : (c == 1369 || (c >= 1377 && c <= 1415)))))) : (c < 1808 ? (c < 1552 ? (c < 1476 ? (c < 1471 ? (c >= 1425 && c <= 1469) : (c == 1471 || (c >= 1473 && c <= 1474))) : (c < 1488 ? ((c >= 1476 && c <= 1477) || c == 1479) : ((c >= 1488 && c <= 1514) || (c >= 1520 && c <= 1522)))) : (c < 1749 ? (c < 1568 ? (c >= 1552 && c <= 1562) : ((c >= 1568 && c <= 1641) || (c >= 1646 && c <= 1747))) : (c < 1770 ? ((c >= 1749 && c <= 1756) || (c >= 1759 && c <= 1768)) : ((c >= 1770 && c <= 1788) || c == 1791)))) : (c < 2230 ? (c < 2042 ? (c < 1869 ? (c >= 1808 && c <= 1866) : ((c >= 1869 && c <= 1969) || (c >= 1984 && c <= 2037))) : (c < 2112 ? (c == 2042 || (c >= 2048 && c <= 2093)) : ((c >= 2112 && c <= 2139) || (c >= 2208 && c <= 2228)))) : (c < 2406 ? (c < 2260 ? (c >= 2230 && c <= 2237) : ((c >= 2260 && c <= 2273) || (c >= 2275 && c <= 2403))) : (c < 2437 ? ((c >= 2406 && c <= 2415) || (c >= 2417 && c <= 2435)) : ((c >= 2437 && c <= 2444) || (c >= 2447 && c <= 2448))))))) : (c < 2693 ? (c < 2579 ? (c < 2519 ? (c < 2486 ? (c < 2474 ? (c >= 2451 && c <= 2472) : ((c >= 2474 && c <= 2480) || c == 2482)) : (c < 2503 ? ((c >= 2486 && c <= 2489) || (c >= 2492 && c <= 2500)) : ((c >= 2503 && c <= 2504) || (c >= 2507 && c <= 2510)))) : (c < 2534 ? (c < 2524 ? c == 2519 : ((c >= 2524 && c <= 2525) || (c >= 2527 && c <= 2531))) : (c < 2565 ? ((c >= 2534 && c <= 2545) || (c >= 2561 && c <= 2563)) : ((c >= 2565 && c <= 2570) || (c >= 2575 && c <= 2576))))) : (c < 2631 ? (c < 2613 ? (c < 2602 ? (c >= 2579 && c <= 2600) : ((c >= 2602 && c <= 2608) || (c >= 2610 && c <= 2611))) : (c < 2620 ? ((c >= 2613 && c <= 2614) || (c >= 2616 && c <= 2617)) : (c == 2620 || (c >= 2622 && c <= 2626)))) : (c < 2649 ? (c < 2635 ? (c >= 2631 && c <= 2632) : ((c >= 2635 && c <= 2637) || c == 2641)) : (c < 2662 ? ((c >= 2649 && c <= 2652) || c == 2654) : ((c >= 2662 && c <= 2677) || (c >= 2689 && c <= 2691)))))) : (c < 2821 ? (c < 2759 ? (c < 2730 ? (c < 2703 ? (c >= 2693 && c <= 2701) : ((c >= 2703 && c <= 2705) || (c >= 2707 && c <= 2728))) : (c < 2741 ? ((c >= 2730 && c <= 2736) || (c >= 2738 && c <= 2739)) : ((c >= 2741 && c <= 2745) || (c >= 2748 && c <= 2757)))) : (c < 2784 ? (c < 2763 ? (c >= 2759 && c <= 2761) : ((c >= 2763 && c <= 2765) || c == 2768)) : (c < 2809 ? ((c >= 2784 && c <= 2787) || (c >= 2790 && c <= 2799)) : (c == 2809 || (c >= 2817 && c <= 2819))))) : (c < 2887 ? (c < 2858 ? (c < 2831 ? (c >= 2821 && c <= 2828) : ((c >= 2831 && c <= 2832) || (c >= 2835 && c <= 2856))) : (c < 2869 ? ((c >= 2858 && c <= 2864) || (c >= 2866 && c <= 2867)) : ((c >= 2869 && c <= 2873) || (c >= 2876 && c <= 2884)))) : (c < 2911 ? (c < 2902 ? ((c >= 2887 && c <= 2888) || (c >= 2891 && c <= 2893)) : ((c >= 2902 && c <= 2903) || (c >= 2908 && c <= 2909))) : (c < 2929 ? ((c >= 2911 && c <= 2915) || (c >= 2918 && c <= 2927)) : (c == 2929 || (c >= 2946 && c <= 2947)))))))) : (c < 3517 ? (c < 3205 ? (c < 3046 ? (c < 2984 ? (c < 2969 ? (c < 2958 ? (c >= 2949 && c <= 2954) : ((c >= 2958 && c <= 2960) || (c >= 2962 && c <= 2965))) : (c < 2974 ? ((c >= 2969 && c <= 2970) || c == 2972) : ((c >= 2974 && c <= 2975) || (c >= 2979 && c <= 2980)))) : (c < 3014 ? (c < 2990 ? (c >= 2984 && c <= 2986) : ((c >= 2990 && c <= 3001) || (c >= 3006 && c <= 3010))) : (c < 3024 ? ((c >= 3014 && c <= 3016) || (c >= 3018 && c <= 3021)) : (c == 3024 || c == 3031)))) : (c < 3142 ? (c < 3086 ? (c < 3072 ? (c >= 3046 && c <= 3055) : ((c >= 3072 && c <= 3075) || (c >= 3077 && c <= 3084))) : (c < 3114 ? ((c >= 3086 && c <= 3088) || (c >= 3090 && c <= 3112)) : ((c >= 3114 && c <= 3129) || (c >= 3133 && c <= 3140)))) : (c < 3160 ? (c < 3146 ? (c >= 3142 && c <= 3144) : ((c >= 3146 && c <= 3149) || (c >= 3157 && c <= 3158))) : (c < 3174 ? ((c >= 3160 && c <= 3162) || (c >= 3168 && c <= 3171)) : ((c >= 3174 && c <= 3183) || (c >= 3200 && c <= 3203)))))) : (c < 3333 ? (c < 3274 ? (c < 3242 ? (c < 3214 ? (c >= 3205 && c <= 3212) : ((c >= 3214 && c <= 3216) || (c >= 3218 && c <= 3240))) : (c < 3260 ? ((c >= 3242 && c <= 3251) || (c >= 3253 && c <= 3257)) : ((c >= 3260 && c <= 3268) || (c >= 3270 && c <= 3272)))) : (c < 3296 ? (c < 3285 ? (c >= 3274 && c <= 3277) : ((c >= 3285 && c <= 3286) || c == 3294)) : (c < 3313 ? ((c >= 3296 && c <= 3299) || (c >= 3302 && c <= 3311)) : ((c >= 3313 && c <= 3314) || (c >= 3329 && c <= 3331))))) : (c < 3423 ? (c < 3389 ? (c < 3342 ? (c >= 3333 && c <= 3340) : ((c >= 3342 && c <= 3344) || (c >= 3346 && c <= 3386))) : (c < 3402 ? ((c >= 3389 && c <= 3396) || (c >= 3398 && c <= 3400)) : ((c >= 3402 && c <= 3406) || (c >= 3412 && c <= 3415)))) : (c < 3458 ? (c < 3430 ? (c >= 3423 && c <= 3427) : ((c >= 3430 && c <= 3439) || (c >= 3450 && c <= 3455))) : (c < 3482 ? ((c >= 3458 && c <= 3459) || (c >= 3461 && c <= 3478)) : ((c >= 3482 && c <= 3505) || (c >= 3507 && c <= 3515))))))) : (c < 3804 ? (c < 3722 ? (c < 3570 ? (c < 3535 ? (c < 3520 ? c == 3517 : ((c >= 3520 && c <= 3526) || c == 3530)) : (c < 3544 ? ((c >= 3535 && c <= 3540) || c == 3542) : ((c >= 3544 && c <= 3551) || (c >= 3558 && c <= 3567)))) : (c < 3664 ? (c < 3585 ? (c >= 3570 && c <= 3571) : ((c >= 3585 && c <= 3642) || (c >= 3648 && c <= 3662))) : (c < 3716 ? ((c >= 3664 && c <= 3673) || (c >= 3713 && c <= 3714)) : (c == 3716 || (c >= 3719 && c <= 3720))))) : (c < 3754 ? (c < 3737 ? (c < 3725 ? c == 3722 : (c == 3725 || (c >= 3732 && c <= 3735))) : (c < 3749 ? ((c >= 3737 && c <= 3743) || (c >= 3745 && c <= 3747)) : (c == 3749 || c == 3751))) : (c < 3776 ? (c < 3757 ? (c >= 3754 && c <= 3755) : ((c >= 3757 && c <= 3769) || (c >= 3771 && c <= 3773))) : (c < 3784 ? ((c >= 3776 && c <= 3780) || c == 3782) : ((c >= 3784 && c <= 3789) || (c >= 3792 && c <= 3801)))))) : (c < 4176 ? (c < 3902 ? (c < 3872 ? (c < 3840 ? (c >= 3804 && c <= 3807) : (c == 3840 || (c >= 3864 && c <= 3865))) : (c < 3895 ? ((c >= 3872 && c <= 3881) || c == 3893) : (c == 3895 || c == 3897))) : (c < 3974 ? (c < 3913 ? (c >= 3902 && c <= 3911) : ((c >= 3913 && c <= 3948) || (c >= 3953 && c <= 3972))) : (c < 4038 ? ((c >= 3974 && c <= 3991) || (c >= 3993 && c <= 4028)) : (c == 4038 || (c >= 4096 && c <= 4169))))) : (c < 4688 ? (c < 4301 ? (c < 4256 ? (c >= 4176 && c <= 4253) : ((c >= 4256 && c <= 4293) || c == 4295)) : (c < 4348 ? (c == 4301 || (c >= 4304 && c <= 4346)) : ((c >= 4348 && c <= 4680) || (c >= 4682 && c <= 4685)))) : (c < 4746 ? (c < 4698 ? ((c >= 4688 && c <= 4694) || c == 4696) : ((c >= 4698 && c <= 4701) || (c >= 4704 && c <= 4744))) : (c < 4786 ? ((c >= 4746 && c <= 4749) || (c >= 4752 && c <= 4784)) : ((c >= 4786 && c <= 4789) || (c >= 4792 && c <= 4798))))))))) : (c < 11035 ? (c < 7416 ? (c < 6176 ? (c < 5873 ? (c < 4992 ? (c < 4824 ? (c < 4802 ? c == 4800 : ((c >= 4802 && c <= 4805) || (c >= 4808 && c <= 4822))) : (c < 4888 ? ((c >= 4824 && c <= 4880) || (c >= 4882 && c <= 4885)) : ((c >= 4888 && c <= 4954) || (c >= 4957 && c <= 4959)))) : (c < 5121 ? (c < 5024 ? (c >= 4992 && c <= 5007) : ((c >= 5024 && c <= 5109) || (c >= 5112 && c <= 5117))) : (c < 5761 ? ((c >= 5121 && c <= 5740) || (c >= 5743 && c <= 5759)) : ((c >= 5761 && c <= 5786) || (c >= 5792 && c <= 5866))))) : (c < 6002 ? (c < 5920 ? (c < 5888 ? (c >= 5873 && c <= 5880) : ((c >= 5888 && c <= 5900) || (c >= 5902 && c <= 5908))) : (c < 5984 ? ((c >= 5920 && c <= 5940) || (c >= 5952 && c <= 5971)) : ((c >= 5984 && c <= 5996) || (c >= 5998 && c <= 6000)))) : (c < 6108 ? (c < 6016 ? (c >= 6002 && c <= 6003) : ((c >= 6016 && c <= 6099) || c == 6103)) : (c < 6155 ? ((c >= 6108 && c <= 6109) || (c >= 6112 && c <= 6121)) : ((c >= 6155 && c <= 6157) || (c >= 6160 && c <= 6169)))))) : (c < 6783 ? (c < 6512 ? (c < 6400 ? (c < 6272 ? (c >= 6176 && c <= 6263) : ((c >= 6272 && c <= 6314) || (c >= 6320 && c <= 6389))) : (c < 6448 ? ((c >= 6400 && c <= 6430) || (c >= 6432 && c <= 6443)) : ((c >= 6448 && c <= 6459) || (c >= 6470 && c <= 6509)))) : (c < 6608 ? (c < 6528 ? (c >= 6512 && c <= 6516) : ((c >= 6528 && c <= 6571) || (c >= 6576 && c <= 6601))) : (c < 6688 ? ((c >= 6608 && c <= 6617) || (c >= 6656 && c <= 6683)) : ((c >= 6688 && c <= 6750) || (c >= 6752 && c <= 6780))))) : (c < 7040 ? (c < 6832 ? (c < 6800 ? (c >= 6783 && c <= 6793) : ((c >= 6800 && c <= 6809) || c == 6823)) : (c < 6992 ? ((c >= 6832 && c <= 6846) || (c >= 6912 && c <= 6987)) : ((c >= 6992 && c <= 7001) || (c >= 7019 && c <= 7027)))) : (c < 7245 ? (c < 7168 ? (c >= 7040 && c <= 7155) : ((c >= 7168 && c <= 7223) || (c >= 7232 && c <= 7241))) : (c < 7376 ? ((c >= 7245 && c <= 7293) || (c >= 7296 && c <= 7304)) : ((c >= 7376 && c <= 7378) || (c >= 7380 && c <= 7414))))))) : (c < 8450 ? (c < 8130 ? (c < 8025 ? (c < 7960 ? (c < 7424 ? (c >= 7416 && c <= 7417) : ((c >= 7424 && c <= 7669) || (c >= 7675 && c <= 7957))) : (c < 8008 ? ((c >= 7960 && c <= 7965) || (c >= 7968 && c <= 8005)) : ((c >= 8008 && c <= 8013) || (c >= 8016 && c <= 8023)))) : (c < 8031 ? (c < 8027 ? c == 8025 : (c == 8027 || c == 8029)) : (c < 8118 ? ((c >= 8031 && c <= 8061) || (c >= 8064 && c <= 8116)) : ((c >= 8118 && c <= 8124) || c == 8126)))) : (c < 8205 ? (c < 8150 ? (c < 8134 ? (c >= 8130 && c <= 8132) : ((c >= 8134 && c <= 8140) || (c >= 8144 && c <= 8147))) : (c < 8178 ? ((c >= 8150 && c <= 8155) || (c >= 8160 && c <= 8172)) : ((c >= 8178 && c <= 8180) || (c >= 8182 && c <= 8188)))) : (c < 8305 ? (c < 8252 ? c == 8205 : (c == 8252 || c == 8265)) : (c < 8336 ? (c == 8305 || c == 8319) : ((c >= 8336 && c <= 8348) || (c >= 8400 && c <= 8432)))))) : (c < 8579 ? (c < 8486 ? (c < 8469 ? (c < 8455 ? c == 8450 : (c == 8455 || (c >= 8458 && c <= 8467))) : (c < 8482 ? (c == 8469 || (c >= 8473 && c <= 8477)) : (c == 8482 || c == 8484))) : (c < 8495 ? (c < 8488 ? c == 8486 : (c == 8488 || (c >= 8490 && c <= 8493))) : (c < 8517 ? ((c >= 8495 && c <= 8505) || (c >= 8508 && c <= 8511)) : ((c >= 8517 && c <= 8521) || c == 8526)))) : (c < 9410 ? (c < 9000 ? (c < 8592 ? (c >= 8579 && c <= 8580) : ((c >= 8592 && c <= 8703) || (c >= 8986 && c <= 8987))) : (c < 9193 ? (c == 9000 || c == 9167) : ((c >= 9193 && c <= 9203) || (c >= 9208 && c <= 9210)))) : (c < 9723 ? (c < 9654 ? (c == 9410 || (c >= 9642 && c <= 9643)) : (c == 9654 || c == 9664)) : (c < 10548 ? ((c >= 9723 && c <= 9726) || (c >= 9728 && c <= 10175)) : ((c >= 10548 && c <= 10549) || (c >= 11013 && c <= 11015)))))))) : (c < 43259 ? (c < 12445 ? (c < 11688 ? (c < 11520 ? (c < 11264 ? (c < 11088 ? (c >= 11035 && c <= 11036) : (c == 11088 || c == 11093)) : (c < 11360 ? ((c >= 11264 && c <= 11310) || (c >= 11312 && c <= 11358)) : ((c >= 11360 && c <= 11492) || (c >= 11499 && c <= 11507)))) : (c < 11568 ? (c < 11559 ? (c >= 11520 && c <= 11557) : (c == 11559 || c == 11565)) : (c < 11647 ? ((c >= 11568 && c <= 11623) || c == 11631) : ((c >= 11647 && c <= 11670) || (c >= 11680 && c <= 11686))))) : (c < 11744 ? (c < 11712 ? (c < 11696 ? (c >= 11688 && c <= 11694) : ((c >= 11696 && c <= 11702) || (c >= 11704 && c <= 11710))) : (c < 11728 ? ((c >= 11712 && c <= 11718) || (c >= 11720 && c <= 11726)) : ((c >= 11728 && c <= 11734) || (c >= 11736 && c <= 11742)))) : (c < 12330 ? (c < 11823 ? (c >= 11744 && c <= 11775) : (c == 11823 || (c >= 12293 && c <= 12294))) : (c < 12353 ? ((c >= 12330 && c <= 12341) || (c >= 12347 && c <= 12349)) : ((c >= 12353 && c <= 12438) || (c >= 12441 && c <= 12442)))))) : (c < 42512 ? (c < 12951 ? (c < 12549 ? (c < 12449 ? (c >= 12445 && c <= 12447) : ((c >= 12449 && c <= 12538) || (c >= 12540 && c <= 12543))) : (c < 12704 ? ((c >= 12549 && c <= 12589) || (c >= 12593 && c <= 12686)) : ((c >= 12704 && c <= 12730) || (c >= 12784 && c <= 12799)))) : (c < 19968 ? (c < 12953 ? c == 12951 : (c == 12953 || (c >= 13312 && c <= 19893))) : (c < 42192 ? ((c >= 19968 && c <= 40917) || (c >= 40960 && c <= 42124)) : ((c >= 42192 && c <= 42237) || (c >= 42240 && c <= 42508))))) : (c < 42891 ? (c < 42623 ? (c < 42560 ? (c >= 42512 && c <= 42539) : ((c >= 42560 && c <= 42610) || (c >= 42612 && c <= 42621))) : (c < 42775 ? ((c >= 42623 && c <= 42725) || (c >= 42736 && c <= 42737)) : ((c >= 42775 && c <= 42783) || (c >= 42786 && c <= 42888)))) : (c < 43072 ? (c < 42928 ? (c >= 42891 && c <= 42926) : ((c >= 42928 && c <= 42935) || (c >= 42999 && c <= 43047))) : (c < 43216 ? ((c >= 43072 && c <= 43123) || (c >= 43136 && c <= 43205)) : ((c >= 43216 && c <= 43225) || (c >= 43232 && c <= 43255))))))) : (c < 55243 ? (c < 43744 ? (c < 43488 ? (c < 43312 ? (c < 43261 ? c == 43259 : (c == 43261 || (c >= 43264 && c <= 43309))) : (c < 43392 ? ((c >= 43312 && c <= 43347) || (c >= 43360 && c <= 43388)) : ((c >= 43392 && c <= 43456) || (c >= 43471 && c <= 43481)))) : (c < 43600 ? (c < 43520 ? (c >= 43488 && c <= 43518) : ((c >= 43520 && c <= 43574) || (c >= 43584 && c <= 43597))) : (c < 43642 ? ((c >= 43600 && c <= 43609) || (c >= 43616 && c <= 43638)) : ((c >= 43642 && c <= 43714) || (c >= 43739 && c <= 43741))))) : (c < 43824 ? (c < 43785 ? (c < 43762 ? (c >= 43744 && c <= 43759) : ((c >= 43762 && c <= 43766) || (c >= 43777 && c <= 43782))) : (c < 43808 ? ((c >= 43785 && c <= 43790) || (c >= 43793 && c <= 43798)) : ((c >= 43808 && c <= 43814) || (c >= 43816 && c <= 43822)))) : (c < 44012 ? (c < 43868 ? (c >= 43824 && c <= 43866) : ((c >= 43868 && c <= 43877) || (c >= 43888 && c <= 44010))) : (c < 44032 ? ((c >= 44012 && c <= 44013) || (c >= 44016 && c <= 44025)) : ((c >= 44032 && c <= 55203) || (c >= 55216 && c <= 55238)))))) : (c < 64848 ? (c < 64298 ? (c < 64112 ? (c < 55296 ? (c >= 55243 && c <= 55291) : ((c >= 55296 && c <= 57343) || (c >= 63744 && c <= 64109))) : (c < 64275 ? ((c >= 64112 && c <= 64217) || (c >= 64256 && c <= 64262)) : ((c >= 64275 && c <= 64279) || (c >= 64285 && c <= 64296)))) : (c < 64320 ? (c < 64312 ? (c >= 64298 && c <= 64310) : ((c >= 64312 && c <= 64316) || c == 64318)) : (c < 64326 ? ((c >= 64320 && c <= 64321) || (c >= 64323 && c <= 64324)) : ((c >= 64326 && c <= 64433) || (c >= 64467 && c <= 64829))))) : (c < 65296 ? (c < 65024 ? (c < 64914 ? (c >= 64848 && c <= 64911) : ((c >= 64914 && c <= 64967) || (c >= 65008 && c <= 65019))) : (c < 65136 ? ((c >= 65024 && c <= 65039) || (c >= 65056 && c <= 65071)) : ((c >= 65136 && c <= 65140) || (c >= 65142 && c <= 65276)))) : (c < 65474 ? (c < 65345 ? ((c >= 65296 && c <= 65305) || (c >= 65313 && c <= 65338)) : ((c >= 65345 && c <= 65370) || (c >= 65382 && c <= 65470))) : (c < 65490 ? ((c >= 65474 && c <= 65479) || (c >= 65482 && c <= 65487)) : ((c >= 65490 && c <= 65495) || (c >= 65498 && c <= 65500))))))))));
  1066. }
  1067. /**
  1068. * Determines if the given character `c` matches the regular expression /[!#$%&'*+/=?^_`{|}~-]/
  1069. * by checking it via character code in a binary search fashion.
  1070. *
  1071. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1072. * on the character itself.
  1073. *
  1074. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1075. *
  1076. * npm run generate-char-utils
  1077. */
  1078. function isValidEmailLocalPartSpecialChar(c) {
  1079. return (c < 47 ? (c < 42 ? (c == 33 || (c >= 35 && c <= 39)) : ((c >= 42 && c <= 43) || c == 45)) : (c < 63 ? (c == 47 || c == 61) : (c < 94 ? c == 63 : ((c >= 94 && c <= 96) || (c >= 123 && c <= 126)))));
  1080. }
  1081. /**
  1082. * Determines if the given character `c` matches the regular expression /[-+&@#/%=~_()|'$*[\]{}\u2713]/
  1083. * by checking it via character code in a binary search fashion.
  1084. *
  1085. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1086. * on the character itself.
  1087. *
  1088. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1089. *
  1090. * npm run generate-char-utils
  1091. */
  1092. function isUrlSuffixAllowedSpecialChar(c) {
  1093. return (c < 91 ? (c < 47 ? ((c >= 35 && c <= 43) || c == 45) : (c < 61 ? c == 47 : (c == 61 || c == 64))) : (c < 95 ? (c == 91 || c == 93) : (c < 123 ? c == 95 : ((c >= 123 && c <= 126) || c == 10003))));
  1094. }
  1095. /**
  1096. * Determines if the given character `c` matches the regular expression /[?!:,.;^]/
  1097. * by checking it via character code in a binary search fashion.
  1098. *
  1099. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1100. * on the character itself.
  1101. *
  1102. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1103. *
  1104. * npm run generate-char-utils
  1105. */
  1106. function isUrlSuffixNotAllowedAsFinalChar(c) {
  1107. return (c < 58 ? (c < 44 ? c == 33 : (c == 44 || c == 46)) : (c < 63 ? (c >= 58 && c <= 59) : (c == 63 || c == 94)));
  1108. }
  1109. /**
  1110. * Determines if the given character `c` matches the regular expression /[({[]/
  1111. * by checking it via character code in a binary search fashion.
  1112. *
  1113. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1114. * on the character itself.
  1115. *
  1116. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1117. *
  1118. * npm run generate-char-utils
  1119. */
  1120. function isOpenBraceChar(c) {
  1121. return (c < 91 ? c == 40 : (c == 91 || c == 123));
  1122. }
  1123. /**
  1124. * Determines if the given character `c` matches the regular expression /[)}\]]/
  1125. * by checking it via character code in a binary search fashion.
  1126. *
  1127. * This technique speeds this function up by a factor of ~10x vs. running RegExp.prototype.test()
  1128. * on the character itself.
  1129. *
  1130. * NOTE: This function is generated. Do not edit manually. To regenerate, run:
  1131. *
  1132. * npm run generate-char-utils
  1133. */
  1134. function isCloseBraceChar(c) {
  1135. return (c < 93 ? c == 41 : (c == 93 || c == 125));
  1136. }
  1137. // NOTE: THIS IS A GENERATED FILE
  1138. // To update with the latest TLD list, run `npm run update-known-tlds`
  1139. var tldRegex = /^(?:xn--vermgensberatung-pwb|xn--vermgensberater-ctb|xn--clchc0ea0b2g2a9gcd|xn--w4r85el8fhu5dnra|travelersinsurance|vermögensberatung|xn--5su34j936bgsg|xn--bck1b9a5dre4c|xn--mgbah1a3hjkrd|xn--mgbai9azgqp6j|xn--mgberp4a5d4ar|xn--xkc2dl3a5ee0h|vermögensberater|xn--fzys8d69uvgm|xn--mgba7c0bbn0a|xn--mgbcpq6gpa1a|xn--xkc2al3hye2a|americanexpress|kerryproperties|sandvikcoromant|xn--i1b6b1a6a2e|xn--kcrx77d1x4a|xn--lgbbat1ad8j|xn--mgba3a4f16a|xn--mgbc0a9azcg|xn--nqv7fs00ema|americanfamily|weatherchannel|xn--54b7fta0cc|xn--6qq986b3xl|xn--80aqecdr1a|xn--b4w605ferd|xn--fiq228c5hs|xn--h2breg3eve|xn--jlq480n2rg|xn--mgba3a3ejt|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgbbh1a71e|xn--mgbca7dzdo|xn--mgbi4ecexp|xn--mgbx4cd0ab|xn--rvc1e0am3e|international|lifeinsurance|wolterskluwer|xn--cckwcxetd|xn--eckvdtc9d|xn--fpcrj9c3d|xn--fzc2c9e2c|xn--h2brj9c8c|xn--tiq49xqyj|xn--yfro4i67o|xn--ygbi2ammx|construction|lplfinancial|scholarships|versicherung|xn--3e0b707e|xn--45br5cyl|xn--4dbrk0ce|xn--80adxhks|xn--80asehdb|xn--8y0a063a|xn--gckr3f0f|xn--mgb9awbf|xn--mgbab2bd|xn--mgbgu82a|xn--mgbpl2fh|xn--mgbt3dhd|xn--mk1bu44c|xn--ngbc5azd|xn--ngbe9e0a|xn--ogbpf8fl|xn--qcka1pmc|accountants|barclaycard|blackfriday|blockbuster|bridgestone|calvinklein|contractors|creditunion|engineering|enterprises|investments|kerryhotels|lamborghini|motorcycles|olayangroup|photography|playstation|productions|progressive|redumbrella|williamhill|xn--11b4c3d|xn--1ck2e1b|xn--1qqw23a|xn--2scrj9c|xn--3bst00m|xn--3ds443g|xn--3hcrj9c|xn--42c2d9a|xn--45brj9c|xn--55qw42g|xn--6frz82g|xn--80ao21a|xn--9krt00a|xn--cck2b3b|xn--czr694b|xn--d1acj3b|xn--efvy88h|xn--fct429k|xn--fjq720a|xn--flw351e|xn--g2xx48c|xn--gecrj9c|xn--gk3at1e|xn--h2brj9c|xn--hxt814e|xn--imr513n|xn--j6w193g|xn--jvr189m|xn--kprw13d|xn--kpry57d|xn--mgbbh1a|xn--mgbtx2b|xn--mix891f|xn--nyqy26a|xn--otu796d|xn--pgbs0dh|xn--q9jyb4c|xn--rhqv96g|xn--rovu88b|xn--s9brj9c|xn--ses554g|xn--t60b56a|xn--vuq861b|xn--w4rs40l|xn--xhq521b|xn--zfr164b|சிங்கப்பூர்|accountant|apartments|associates|basketball|bnpparibas|boehringer|capitalone|consulting|creditcard|cuisinella|eurovision|extraspace|foundation|healthcare|immobilien|industries|management|mitsubishi|nextdirect|properties|protection|prudential|realestate|republican|restaurant|schaeffler|tatamotors|technology|university|vlaanderen|xn--30rr7y|xn--3pxu8k|xn--45q11c|xn--4gbrim|xn--55qx5d|xn--5tzm5g|xn--80aswg|xn--90a3ac|xn--9dbq2a|xn--9et52u|xn--c2br7g|xn--cg4bki|xn--czrs0t|xn--czru2d|xn--fiq64b|xn--fiqs8s|xn--fiqz9s|xn--io0a7i|xn--kput3i|xn--mxtq1m|xn--o3cw4h|xn--pssy2u|xn--q7ce6a|xn--unup4y|xn--wgbh1c|xn--wgbl6a|xn--y9a3aq|accenture|allfinanz|amsterdam|analytics|aquarelle|barcelona|bloomberg|christmas|community|directory|education|equipment|fairwinds|financial|firestone|fresenius|furniture|goldpoint|hisamitsu|homedepot|homegoods|homesense|institute|insurance|kuokgroup|landrover|lifestyle|marketing|marshalls|melbourne|microsoft|panasonic|pramerica|richardli|shangrila|solutions|statebank|statefarm|stockholm|travelers|vacations|xn--90ais|xn--c1avg|xn--d1alf|xn--e1a4c|xn--fhbei|xn--j1aef|xn--j1amh|xn--l1acc|xn--ngbrx|xn--nqv7f|xn--p1acf|xn--qxa6a|xn--tckwe|xn--vhquv|yodobashi|موريتانيا|abudhabi|airforce|allstate|attorney|barclays|barefoot|bargains|baseball|boutique|bradesco|broadway|brussels|builders|business|capetown|catering|catholic|cipriani|cleaning|clinique|clothing|commbank|computer|delivery|deloitte|democrat|diamonds|discount|discover|download|engineer|ericsson|exchange|feedback|fidelity|firmdale|football|frontier|goodyear|grainger|graphics|hdfcbank|helsinki|holdings|hospital|infiniti|ipiranga|istanbul|jpmorgan|lighting|lundbeck|marriott|mckinsey|memorial|merckmsd|mortgage|observer|partners|pharmacy|pictures|plumbing|property|redstone|reliance|saarland|samsclub|security|services|shopping|softbank|software|stcgroup|supplies|training|vanguard|ventures|verisign|woodside|xn--90ae|xn--node|xn--p1ai|xn--qxam|yokohama|السعودية|abogado|academy|agakhan|alibaba|android|athleta|auction|audible|auspost|banamex|bauhaus|bestbuy|booking|brother|capital|caravan|careers|channel|charity|chintai|citadel|clubmed|college|cologne|company|compare|contact|cooking|corsica|country|coupons|courses|cricket|cruises|dentist|digital|domains|exposed|express|farmers|fashion|ferrari|ferrero|finance|fishing|fitness|flights|florist|flowers|forsale|frogans|fujitsu|gallery|genting|godaddy|grocery|guitars|hamburg|hangout|hitachi|holiday|hosting|hotmail|hyundai|ismaili|jewelry|juniper|kitchen|komatsu|lacaixa|lanxess|lasalle|latrobe|leclerc|limited|lincoln|markets|monster|netbank|netflix|network|neustar|okinawa|organic|origins|philips|pioneer|politie|realtor|recipes|rentals|reviews|rexroth|samsung|sandvik|schmidt|schwarz|science|shiksha|singles|staples|storage|support|surgery|systems|temasek|theater|theatre|tickets|toshiba|trading|walmart|wanggou|watches|weather|website|wedding|whoswho|windows|winners|yamaxun|youtube|zuerich|католик|البحرين|الجزائر|العليان|پاکستان|كاثوليك|இந்தியா|abbott|abbvie|africa|agency|airbus|airtel|alipay|alsace|alstom|amazon|anquan|aramco|author|bayern|beauty|berlin|bharti|bostik|boston|broker|camera|career|casino|center|chanel|chrome|church|circle|claims|clinic|coffee|comsec|condos|coupon|credit|cruise|dating|datsun|dealer|degree|dental|design|direct|doctor|dunlop|dupont|durban|emerck|energy|estate|events|expert|family|flickr|futbol|gallup|garden|george|giving|global|google|gratis|health|hermes|hiphop|hockey|hotels|hughes|imamat|insure|intuit|jaguar|joburg|juegos|kaufen|kindle|kosher|latino|lawyer|lefrak|living|locker|london|luxury|madrid|maison|makeup|market|mattel|mobile|monash|mormon|moscow|museum|nagoya|nissan|nissay|norton|nowruz|office|olayan|online|oracle|orange|otsuka|pfizer|photos|physio|pictet|quebec|racing|realty|reisen|repair|report|review|rogers|ryukyu|safety|sakura|sanofi|school|schule|search|secure|select|shouji|soccer|social|stream|studio|supply|suzuki|swatch|sydney|taipei|taobao|target|tattoo|tennis|tienda|tjmaxx|tkmaxx|toyota|travel|unicom|viajes|viking|villas|virgin|vision|voting|voyage|walter|webcam|xihuan|yachts|yandex|zappos|москва|онлайн|ابوظبي|ارامكو|الاردن|المغرب|امارات|فلسطين|مليسيا|भारतम्|இலங்கை|ファッション|actor|adult|aetna|amfam|amica|apple|archi|audio|autos|azure|baidu|beats|bible|bingo|black|boats|bosch|build|canon|cards|chase|cheap|cisco|citic|click|cloud|coach|codes|crown|cymru|dance|deals|delta|drive|dubai|earth|edeka|email|epson|faith|fedex|final|forex|forum|gallo|games|gifts|gives|glass|globo|gmail|green|gripe|group|gucci|guide|homes|honda|horse|house|hyatt|ikano|irish|jetzt|koeln|kyoto|lamer|lease|legal|lexus|lilly|loans|locus|lotte|lotto|mango|media|miami|money|movie|music|nexus|nikon|ninja|nokia|nowtv|omega|osaka|paris|parts|party|phone|photo|pizza|place|poker|praxi|press|prime|promo|quest|radio|rehab|reise|ricoh|rocks|rodeo|rugby|salon|sener|seven|sharp|shell|shoes|skype|sling|smart|smile|solar|space|sport|stada|store|study|style|sucks|swiss|tatar|tires|tirol|tmall|today|tokyo|tools|toray|total|tours|trade|trust|tunes|tushu|ubank|vegas|video|vodka|volvo|wales|watch|weber|weibo|works|world|xerox|yahoo|ישראל|ایران|بازار|بھارت|سودان|سورية|همراه|भारोत|संगठन|বাংলা|భారత్|ഭാരതം|嘉里大酒店|aarp|able|aero|akdn|ally|amex|arab|army|arpa|arte|asda|asia|audi|auto|baby|band|bank|bbva|beer|best|bike|bing|blog|blue|bofa|bond|book|buzz|cafe|call|camp|care|cars|casa|case|cash|cbre|cern|chat|citi|city|club|cool|coop|cyou|data|date|dclk|deal|dell|desi|diet|dish|docs|dvag|erni|fage|fail|fans|farm|fast|fido|film|fire|fish|flir|food|ford|free|fund|game|gbiz|gent|ggee|gift|gmbh|gold|golf|goog|guge|guru|hair|haus|hdfc|help|here|host|hsbc|icbc|ieee|imdb|immo|info|itau|java|jeep|jobs|jprs|kddi|kids|kiwi|kpmg|kred|land|lego|lgbt|lidl|life|like|limo|link|live|loan|love|ltda|luxe|maif|meet|meme|menu|mini|mint|mobi|moda|moto|name|navy|news|next|nico|nike|ollo|open|page|pars|pccw|pics|ping|pink|play|plus|pohl|porn|post|prod|prof|qpon|read|reit|rent|rest|rich|room|rsvp|ruhr|safe|sale|sarl|save|saxo|scot|seat|seek|sexy|shia|shop|show|silk|sina|site|skin|sncf|sohu|song|sony|spot|star|surf|talk|taxi|team|tech|teva|tiaa|tips|town|toys|tube|vana|visa|viva|vivo|vote|voto|wang|weir|wien|wiki|wine|work|xbox|yoga|zara|zero|zone|дети|сайт|بارت|بيتك|ڀارت|تونس|شبكة|عراق|عمان|موقع|भारत|ভারত|ভাৰত|ਭਾਰਤ|ભારત|ଭାରତ|ಭಾರತ|ලංකා|アマゾン|グーグル|クラウド|ポイント|组织机构|電訊盈科|香格里拉|aaa|abb|abc|aco|ads|aeg|afl|aig|anz|aol|app|art|aws|axa|bar|bbc|bbt|bcg|bcn|bet|bid|bio|biz|bms|bmw|bom|boo|bot|box|buy|bzh|cab|cal|cam|car|cat|cba|cbn|ceo|cfa|cfd|com|cpa|crs|dad|day|dds|dev|dhl|diy|dnp|dog|dot|dtv|dvr|eat|eco|edu|esq|eus|fan|fit|fly|foo|fox|frl|ftr|fun|fyi|gal|gap|gay|gdn|gea|gle|gmo|gmx|goo|gop|got|gov|hbo|hiv|hkt|hot|how|ibm|ice|icu|ifm|inc|ing|ink|int|ist|itv|jcb|jio|jll|jmp|jnj|jot|joy|kfh|kia|kim|kpn|krd|lat|law|lds|llc|llp|lol|lpl|ltd|man|map|mba|med|men|mil|mit|mlb|mls|mma|moe|moi|mom|mov|msd|mtn|mtr|nab|nba|nec|net|new|nfl|ngo|nhk|now|nra|nrw|ntt|nyc|obi|one|ong|onl|ooo|org|ott|ovh|pay|pet|phd|pid|pin|pnc|pro|pru|pub|pwc|red|ren|ril|rio|rip|run|rwe|sap|sas|sbi|sbs|scb|sew|sex|sfr|ski|sky|soy|spa|srl|stc|tab|tax|tci|tdk|tel|thd|tjx|top|trv|tui|tvs|ubs|uno|uol|ups|vet|vig|vin|vip|wed|win|wme|wow|wtc|wtf|xin|xxx|xyz|you|yun|zip|бел|ком|қаз|мкд|мон|орг|рус|срб|укр|հայ|קום|عرب|قطر|كوم|مصر|कॉम|नेट|คอม|ไทย|ລາວ|ストア|セール|みんな|中文网|亚马逊|天主教|我爱你|新加坡|淡马锡|飞利浦|ac|ad|ae|af|ag|ai|al|am|ao|aq|ar|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|co|cr|cu|cv|cw|cx|cy|cz|de|dj|dk|dm|do|dz|ec|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|in|io|iq|ir|is|it|je|jm|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mk|ml|mm|mn|mo|mp|mq|mr|ms|mt|mu|mv|mw|mx|my|mz|na|nc|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|pa|pe|pf|pg|ph|pk|pl|pm|pn|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|ss|st|su|sv|sx|sy|sz|tc|td|tf|tg|th|tj|tk|tl|tm|tn|to|tr|tt|tv|tw|tz|ua|ug|uk|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|ye|yt|za|zm|zw|ελ|ευ|бг|ею|рф|გე|닷넷|닷컴|삼성|한국|コム|世界|中信|中国|中國|企业|佛山|信息|健康|八卦|公司|公益|台湾|台灣|商城|商店|商标|嘉里|在线|大拿|娱乐|家電|广东|微博|慈善|手机|招聘|政务|政府|新闻|时尚|書籍|机构|游戏|澳門|点看|移动|网址|网店|网站|网络|联通|谷歌|购物|通販|集团|食品|餐厅|香港)$/;
  1140. /**
  1141. * Regular expression to match an http:// or https:// scheme.
  1142. */
  1143. var httpSchemeRe = /https?:\/\//i;
  1144. /**
  1145. * Regular expression to match an http:// or https:// scheme as the prefix of
  1146. * a string.
  1147. */
  1148. var httpSchemePrefixRe = new RegExp('^' + httpSchemeRe.source, 'i');
  1149. /**
  1150. * A regular expression used to determine the schemes we should not autolink
  1151. */
  1152. var invalidSchemeRe = /^(javascript|vbscript):/i;
  1153. // A regular expression used to determine if the URL is a scheme match (such as
  1154. // 'http://google.com', and as opposed to a "TLD match"). This regular
  1155. // expression is used to parse out the host along with if the URL has an
  1156. // authority component (i.e. '//')
  1157. //
  1158. // Capturing groups:
  1159. // 1. '//' if the URL has an authority component, empty string otherwise
  1160. // 2. The host (if one exists). Ex: 'google.com'
  1161. //
  1162. // See https://www.rfc-editor.org/rfc/rfc3986#appendix-A for terminology
  1163. var schemeUrlRe = /^[A-Za-z][-.+A-Za-z0-9]*:(\/\/)?([^:/]*)/;
  1164. // A regular expression used to determine if the URL is a TLD match (such as
  1165. // 'google.com', and as opposed to a "scheme match"). This regular
  1166. // expression is used to help parse out the TLD (top-level domain) of the host.
  1167. //
  1168. // See https://www.rfc-editor.org/rfc/rfc3986#appendix-A for terminology
  1169. var tldUrlHostRe = /^(?:\/\/)?([^/#?:]+)/; // optionally prefixed with protocol-relative '//' chars
  1170. /**
  1171. * Determines if the given character code represents a character that may start
  1172. * a scheme (ex: the 'h' in 'http')
  1173. */
  1174. var isSchemeStartChar = isAsciiLetterChar; // Equivalent to checking the RegExp `/[A-Za-z]/`, but aliased for clarity and maintainability
  1175. /**
  1176. * Determines if the given character is a valid character in a scheme (such as
  1177. * 'http' or 'ssh+git'), but only after the start char (which is handled by
  1178. * {@link isSchemeStartChar}.
  1179. */
  1180. function isSchemeChar(charCode) {
  1181. return (isAsciiLetterChar(charCode) ||
  1182. isDigitChar(charCode) ||
  1183. charCode === 43 /* Char.Plus */ || // '+'
  1184. charCode === 45 /* Char.Dash */ || // '-'
  1185. charCode === 46 /* Char.Dot */ // '.'
  1186. );
  1187. }
  1188. /**
  1189. * Determines if the character can begin a domain label, which must be an
  1190. * alphanumeric character and not an underscore or dash.
  1191. *
  1192. * A domain label is a segment of a hostname such as subdomain.google.com.
  1193. */
  1194. var isDomainLabelStartChar = isAlphaNumericOrMarkChar; // alias function for clarity
  1195. /**
  1196. * Determines if the character is part of a domain label (but not a domain label
  1197. * start character).
  1198. *
  1199. * A domain label is a segment of a hostname such as subdomain.google.com.
  1200. */
  1201. function isDomainLabelChar(charCode) {
  1202. return charCode === 95 /* Char.Underscore */ || isDomainLabelStartChar(charCode);
  1203. }
  1204. /**
  1205. * Determines if the character is a path character ("pchar") as defined by
  1206. * https://tools.ietf.org/html/rfc3986#appendix-A
  1207. *
  1208. * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
  1209. *
  1210. * unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
  1211. * pct-encoded = "%" HEXDIG HEXDIG
  1212. * sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
  1213. * / "*" / "+" / "," / ";" / "="
  1214. *
  1215. * Note that this implementation doesn't follow the spec exactly, but rather
  1216. * follows URL path characters found out in the wild (spec might be out of date?)
  1217. */
  1218. function isPathChar(charCode) {
  1219. return (isAlphaNumericOrMarkChar(charCode) ||
  1220. isUrlSuffixAllowedSpecialChar(charCode) ||
  1221. isUrlSuffixNotAllowedAsFinalChar(charCode) // characters in addition to those allowed by isUrlSuffixAllowedSpecialChar()
  1222. );
  1223. }
  1224. /**
  1225. * Determines if the character given may begin the "URL Suffix" section of a
  1226. * URI (i.e. the path, query, or hash section). These are the '/', '?' and '#'
  1227. * characters.
  1228. *
  1229. * See https://tools.ietf.org/html/rfc3986#appendix-A
  1230. */
  1231. function isUrlSuffixStartChar(charCode) {
  1232. return (charCode === 47 /* Char.Slash */ || // '/'
  1233. charCode === 63 /* Char.Question */ || // '?'
  1234. charCode === 35 /* Char.NumberSign */ // '#'
  1235. );
  1236. }
  1237. /**
  1238. * Determines if the top-level domain (TLD) read in the host is a known TLD.
  1239. *
  1240. * Example: 'com' would be a known TLD (for a host of 'google.com'), but
  1241. * 'local' would not (for a domain name of 'my-computer.local').
  1242. */
  1243. function isKnownTld(tld) {
  1244. return tldRegex.test(tld.toLowerCase()); // make sure the tld is lowercase for the regex
  1245. }
  1246. /**
  1247. * Determines if the given `url` is a valid scheme-prefixed URL.
  1248. */
  1249. function isValidSchemeUrl(url) {
  1250. // If the scheme is 'javascript:' or 'vbscript:', these link
  1251. // types can be dangerous. Don't link them.
  1252. if (invalidSchemeRe.test(url)) {
  1253. return false;
  1254. }
  1255. var schemeMatch = url.match(schemeUrlRe);
  1256. if (!schemeMatch) {
  1257. return false;
  1258. }
  1259. var isAuthorityMatch = !!schemeMatch[1];
  1260. var host = schemeMatch[2];
  1261. if (isAuthorityMatch) {
  1262. // Any match that has an authority ('//' chars) after the scheme is
  1263. // valid, such as 'http://anything'
  1264. return true;
  1265. }
  1266. // If there's no authority ('//' chars), check that we have a hostname
  1267. // that looks valid.
  1268. //
  1269. // The host must contain at least one '.' char and have a domain label
  1270. // with at least one letter to be considered valid.
  1271. //
  1272. // Accept:
  1273. // - git:domain.com (scheme followed by a host
  1274. // Do not accept:
  1275. // - git:something ('something' doesn't look like a host)
  1276. // - version:1.0 ('1.0' doesn't look like a host)
  1277. if (host.indexOf('.') === -1 || !/[A-Za-z]/.test(host)) {
  1278. // `letterRe` RegExp checks for a letter anywhere in the host string
  1279. return false;
  1280. }
  1281. return true;
  1282. }
  1283. /**
  1284. * Determines if the given `url` is a match with a valid TLD.
  1285. */
  1286. function isValidTldMatch(url) {
  1287. // TLD URL such as 'google.com', we need to confirm that we have a valid
  1288. // top-level domain
  1289. var tldUrlHostMatch = url.match(tldUrlHostRe);
  1290. if (!tldUrlHostMatch) {
  1291. // At this point, if the URL didn't match our TLD re, it must be invalid
  1292. // (highly unlikely to happen, but just in case)
  1293. return false;
  1294. }
  1295. var host = tldUrlHostMatch[0];
  1296. var hostLabels = host.split('.');
  1297. if (hostLabels.length < 2) {
  1298. // 0 or 1 host label, there's no TLD. Ex: 'localhost'
  1299. return false;
  1300. }
  1301. var tld = hostLabels[hostLabels.length - 1];
  1302. if (!isKnownTld(tld)) {
  1303. return false;
  1304. }
  1305. // TODO: Implement these conditions for TLD matcher:
  1306. // (
  1307. // this.longestDomainLabelLength <= 63 &&
  1308. // this.domainNameLength <= 255
  1309. // );
  1310. return true;
  1311. }
  1312. // Regular expression to confirm a valid IPv4 address (ex: '192.168.0.1')
  1313. // TODO: encode this into the state machine so that we don't need to run this
  1314. // regexp separately to confirm the match
  1315. var ipV4Re = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
  1316. // Regular expression used to split the IPv4 address itself from any port/path/query/hash
  1317. var ipV4PartRe = /[:/?#]/;
  1318. /**
  1319. * Determines if the given URL is a valid IPv4-prefixed URL.
  1320. */
  1321. function isValidIpV4Address(url) {
  1322. // Grab just the IP address
  1323. var ipV4Part = url.split(ipV4PartRe, 1)[0]; // only 1 result needed
  1324. return ipV4Re.test(ipV4Part);
  1325. }
  1326. /**
  1327. * A regular expression used to remove the 'www.' from URLs.
  1328. */
  1329. var wwwPrefixRegex = /^(https?:\/\/)?(?:www\.)?/i;
  1330. /**
  1331. * The regular expression used to remove the protocol-relative '//' from a URL
  1332. * string, for purposes of formatting the anchor text. A protocol-relative URL
  1333. * is, for example, "//yahoo.com"
  1334. */
  1335. var protocolRelativeRegex = /^\/\//;
  1336. /**
  1337. * @class Autolinker.match.Url
  1338. * @extends Autolinker.match.AbstractMatch
  1339. *
  1340. * Represents a Url match found in an input string which should be Autolinked.
  1341. *
  1342. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  1343. */
  1344. var UrlMatch = /** @class */ (function (_super) {
  1345. __extends(UrlMatch, _super);
  1346. /**
  1347. * @method constructor
  1348. * @param {Object} cfg The configuration properties for the Match
  1349. * instance, specified in an Object (map).
  1350. */
  1351. function UrlMatch(cfg) {
  1352. var _this = _super.call(this, cfg) || this;
  1353. /**
  1354. * @public
  1355. * @property {'url'} type
  1356. *
  1357. * A string name for the type of match that this class represents. Can be
  1358. * used in a TypeScript discriminating union to type-narrow from the
  1359. * `Match` type.
  1360. */
  1361. _this.type = 'url';
  1362. /**
  1363. * @cfg {String} url (required)
  1364. *
  1365. * The url that was matched.
  1366. */
  1367. _this.url = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1368. /**
  1369. * @cfg {"scheme"/"www"/"tld"} urlMatchType (required)
  1370. *
  1371. * The type of URL match that this class represents. This helps to determine
  1372. * if the match was made in the original text with a prefixed scheme (ex:
  1373. * 'http://www.google.com'), a prefixed 'www' (ex: 'www.google.com'), or
  1374. * was matched by a known top-level domain (ex: 'google.com').
  1375. */
  1376. _this.urlMatchType = 'scheme'; // default value just to get the above doc comment in the ES5 output and documentation generator
  1377. /**
  1378. * @cfg {Boolean} protocolRelativeMatch (required)
  1379. *
  1380. * `true` if the URL is a protocol-relative match. A protocol-relative match
  1381. * is a URL that starts with '//', and will be either http:// or https://
  1382. * based on the protocol that the site is loaded under.
  1383. */
  1384. _this.protocolRelativeMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  1385. /**
  1386. * @cfg {Object} stripPrefix (required)
  1387. *
  1388. * The Object form of {@link Autolinker#cfg-stripPrefix}.
  1389. */
  1390. _this.stripPrefix = {
  1391. scheme: true,
  1392. www: true,
  1393. }; // default value just to get the above doc comment in the ES5 output and documentation generator
  1394. /**
  1395. * @cfg {Boolean} stripTrailingSlash (required)
  1396. * @inheritdoc Autolinker#cfg-stripTrailingSlash
  1397. */
  1398. _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  1399. /**
  1400. * @cfg {Boolean} decodePercentEncoding (required)
  1401. * @inheritdoc Autolinker#cfg-decodePercentEncoding
  1402. */
  1403. _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  1404. /**
  1405. * @private
  1406. * @property {Boolean} protocolPrepended
  1407. *
  1408. * Will be set to `true` if the 'http://' protocol has been prepended to the {@link #url} (because the
  1409. * {@link #url} did not have a protocol)
  1410. */
  1411. _this.protocolPrepended = false;
  1412. _this.urlMatchType = cfg.urlMatchType;
  1413. _this.url = cfg.url;
  1414. _this.protocolRelativeMatch = cfg.protocolRelativeMatch;
  1415. _this.stripPrefix = cfg.stripPrefix;
  1416. _this.stripTrailingSlash = cfg.stripTrailingSlash;
  1417. _this.decodePercentEncoding = cfg.decodePercentEncoding;
  1418. return _this;
  1419. }
  1420. /**
  1421. * Returns a string name for the type of match that this class represents.
  1422. * For the case of UrlMatch, returns 'url'.
  1423. *
  1424. * @return {String}
  1425. */
  1426. UrlMatch.prototype.getType = function () {
  1427. return 'url';
  1428. };
  1429. /**
  1430. * Returns a string name for the type of URL match that this class
  1431. * represents.
  1432. *
  1433. * This helps to determine if the match was made in the original text with a
  1434. * prefixed scheme (ex: 'http://www.google.com'), a prefixed 'www' (ex:
  1435. * 'www.google.com'), or was matched by a known top-level domain (ex:
  1436. * 'google.com').
  1437. *
  1438. * @return {"scheme"/"www"/"tld"}
  1439. */
  1440. UrlMatch.prototype.getUrlMatchType = function () {
  1441. return this.urlMatchType;
  1442. };
  1443. /**
  1444. * Returns the url that was matched, assuming the protocol to be 'http://' if the original
  1445. * match was missing a protocol.
  1446. *
  1447. * @return {String}
  1448. */
  1449. UrlMatch.prototype.getUrl = function () {
  1450. var url = this.url;
  1451. // if the url string doesn't begin with a scheme, assume 'http://'
  1452. if (!this.protocolRelativeMatch &&
  1453. this.urlMatchType !== 'scheme' &&
  1454. !this.protocolPrepended) {
  1455. url = this.url = 'http://' + url;
  1456. this.protocolPrepended = true;
  1457. }
  1458. return url;
  1459. };
  1460. /**
  1461. * Returns the anchor href that should be generated for the match.
  1462. *
  1463. * @return {String}
  1464. */
  1465. UrlMatch.prototype.getAnchorHref = function () {
  1466. var url = this.getUrl();
  1467. return url.replace(/&amp;/g, '&'); // any &amp;'s in the URL should be converted back to '&' if they were displayed as &amp; in the source html
  1468. };
  1469. /**
  1470. * Returns the anchor text that should be generated for the match.
  1471. *
  1472. * @return {String}
  1473. */
  1474. UrlMatch.prototype.getAnchorText = function () {
  1475. var anchorText = this.getMatchedText();
  1476. if (this.protocolRelativeMatch) {
  1477. // Strip off any protocol-relative '//' from the anchor text
  1478. anchorText = stripProtocolRelativePrefix(anchorText);
  1479. }
  1480. if (this.stripPrefix.scheme) {
  1481. anchorText = stripSchemePrefix(anchorText);
  1482. }
  1483. if (this.stripPrefix.www) {
  1484. anchorText = stripWwwPrefix(anchorText);
  1485. }
  1486. if (this.stripTrailingSlash) {
  1487. anchorText = removeTrailingSlash(anchorText); // remove trailing slash, if there is one
  1488. }
  1489. if (this.decodePercentEncoding) {
  1490. anchorText = removePercentEncoding(anchorText);
  1491. }
  1492. return anchorText;
  1493. };
  1494. return UrlMatch;
  1495. }(AbstractMatch));
  1496. // Utility Functionality
  1497. /**
  1498. * Strips the scheme prefix (such as "http://" or "https://") from the given
  1499. * `url`.
  1500. *
  1501. * @private
  1502. * @param {String} url The text of the anchor that is being generated, for
  1503. * which to strip off the url scheme.
  1504. * @return {String} The `url`, with the scheme stripped.
  1505. */
  1506. function stripSchemePrefix(url) {
  1507. return url.replace(httpSchemePrefixRe, '');
  1508. }
  1509. /**
  1510. * Strips the 'www' prefix from the given `url`.
  1511. *
  1512. * @private
  1513. * @param {String} url The text of the anchor that is being generated, for
  1514. * which to strip off the 'www' if it exists.
  1515. * @return {String} The `url`, with the 'www' stripped.
  1516. */
  1517. function stripWwwPrefix(url) {
  1518. // If the URL doesn't actually include 'www.' in it, skip running the
  1519. // .replace() regexp on it, which is fairly slow even just to check the
  1520. // string for the 'www.'s existence. Most URLs these days do not have 'www.'
  1521. // in it, so most of the time we skip running the .replace(). One other
  1522. // option in the future is to run a state machine on the `url` string
  1523. if (!url.includes('www.')) {
  1524. return url;
  1525. }
  1526. else {
  1527. return url.replace(wwwPrefixRegex, '$1'); // leave any scheme ($1), it one exists
  1528. }
  1529. }
  1530. /**
  1531. * Strips any protocol-relative '//' from the anchor text.
  1532. *
  1533. * @private
  1534. * @param {String} text The text of the anchor that is being generated, for which to strip off the
  1535. * protocol-relative prefix (such as stripping off "//")
  1536. * @return {String} The `anchorText`, with the protocol-relative prefix stripped.
  1537. */
  1538. function stripProtocolRelativePrefix(text) {
  1539. return text.replace(protocolRelativeRegex, '');
  1540. }
  1541. /**
  1542. * Removes any trailing slash from the given `anchorText`, in preparation for the text to be displayed.
  1543. *
  1544. * @private
  1545. * @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing
  1546. * slash ('/') that may exist.
  1547. * @return {String} The `anchorText`, with the trailing slash removed.
  1548. */
  1549. function removeTrailingSlash(anchorText) {
  1550. if (anchorText.charAt(anchorText.length - 1) === '/') {
  1551. anchorText = anchorText.slice(0, -1);
  1552. }
  1553. return anchorText;
  1554. }
  1555. /**
  1556. * Decodes percent-encoded characters from the given `anchorText`, in
  1557. * preparation for the text to be displayed.
  1558. *
  1559. * @private
  1560. * @param {String} anchorText The text of the anchor that is being
  1561. * generated, for which to decode any percent-encoded characters.
  1562. * @return {String} The `anchorText`, with the percent-encoded characters
  1563. * decoded.
  1564. */
  1565. function removePercentEncoding(anchorText) {
  1566. // First, convert a few of the known % encodings to the corresponding
  1567. // HTML entities that could accidentally be interpretted as special
  1568. // HTML characters
  1569. // NOTE: This used to be written as 5 separate .replace() calls, but that
  1570. // was 25% slower than the current form below according to jsperf
  1571. var preProcessedEntityAnchorText = anchorText.replace(/%(?:22|26|27|3C|3E)/gi, function (match) {
  1572. if (match === '%22')
  1573. return '&quot;'; // %22: '"' char
  1574. if (match === '%26')
  1575. return '&amp;'; // %26: '&' char
  1576. if (match === '%27')
  1577. return '&#39;'; // %27: "'" char
  1578. if (match === '%3C' || match === '%3c')
  1579. return '&lt;'; // %3C: '<' char
  1580. /*if (match === '%3E' || match === '%3e')*/ return '&gt;'; // %3E: '>' char
  1581. });
  1582. // Now attempt to URL-decode the rest of the anchor text. However,
  1583. // decodeURIComponent() is a slow function. Only call it if we have
  1584. // remaining %-encoded entities. Adding this check added ~300 ops/sec to
  1585. // benchmark
  1586. if (preProcessedEntityAnchorText.includes('%')) {
  1587. try {
  1588. return decodeURIComponent(preProcessedEntityAnchorText);
  1589. // eslint-disable-next-line @typescript-eslint/no-unused-vars
  1590. }
  1591. catch (error) {
  1592. // Invalid % escape sequence in the anchor text, we'll simply return
  1593. // the preProcessedEntityAnchorText below
  1594. }
  1595. }
  1596. return preProcessedEntityAnchorText;
  1597. }
  1598. /**
  1599. * A regular expression to match a 'mailto:' prefix on an email address.
  1600. */
  1601. var mailtoSchemePrefixRe = /^mailto:/i;
  1602. /**
  1603. * Determines if the given character may start the "local part" of an email
  1604. * address. The local part is the part to the left of the '@' sign.
  1605. *
  1606. * Technically according to the email spec, any of the characters in the
  1607. * {@link emailLocalPartCharRegex} can start an email address (including any of
  1608. * the special characters), but this is so rare in the wild and the
  1609. * implementation is much simpler by only starting an email address with a word
  1610. * character. This is especially important when matching the '{' character which
  1611. * generally starts a brace that isn't part of the email address.
  1612. */
  1613. var isEmailLocalPartStartChar = isAlphaNumericOrMarkChar; // alias for clarity
  1614. /**
  1615. * Determines if the given character can be part of the "local part" of an email
  1616. * address. The local part is the part to the left of the '@' sign.
  1617. *
  1618. * Checking for an email address's start char is handled with {@link #isEmailLocalPartStartChar}
  1619. */
  1620. function isEmailLocalPartChar(charCode) {
  1621. return isEmailLocalPartStartChar(charCode) || isValidEmailLocalPartSpecialChar(charCode);
  1622. }
  1623. /**
  1624. * Determines if the given email address is valid. We consider it valid if it
  1625. * has a valid TLD in its host.
  1626. *
  1627. * @param emailAddress email address
  1628. * @return true is email have valid TLD, false otherwise
  1629. */
  1630. function isValidEmail(emailAddress) {
  1631. var emailAddressTld = emailAddress.split('.').pop(); // as long as we have a valid string (as opposed to null or undefined), we will always get at least one element in the .split('.') array
  1632. return isKnownTld(emailAddressTld);
  1633. }
  1634. /**
  1635. * @class Autolinker.match.Email
  1636. * @extends Autolinker.match.AbstractMatch
  1637. *
  1638. * Represents a Email match found in an input string which should be Autolinked.
  1639. *
  1640. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  1641. */
  1642. var EmailMatch = /** @class */ (function (_super) {
  1643. __extends(EmailMatch, _super);
  1644. /**
  1645. * @method constructor
  1646. * @param {Object} cfg The configuration properties for the Match
  1647. * instance, specified in an Object (map).
  1648. */
  1649. function EmailMatch(cfg) {
  1650. var _this = _super.call(this, cfg) || this;
  1651. /**
  1652. * @public
  1653. * @property {'email'} type
  1654. *
  1655. * A string name for the type of match that this class represents. Can be
  1656. * used in a TypeScript discriminating union to type-narrow from the
  1657. * `Match` type.
  1658. */
  1659. _this.type = 'email';
  1660. /**
  1661. * @cfg {String} email (required)
  1662. *
  1663. * The email address that was matched.
  1664. */
  1665. _this.email = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1666. _this.email = cfg.email;
  1667. return _this;
  1668. }
  1669. /**
  1670. * Returns a string name for the type of match that this class represents.
  1671. * For the case of EmailMatch, returns 'email'.
  1672. *
  1673. * @return {String}
  1674. */
  1675. EmailMatch.prototype.getType = function () {
  1676. return 'email';
  1677. };
  1678. /**
  1679. * Returns the email address that was matched.
  1680. *
  1681. * @return {String}
  1682. */
  1683. EmailMatch.prototype.getEmail = function () {
  1684. return this.email;
  1685. };
  1686. /**
  1687. * Returns the anchor href that should be generated for the match.
  1688. *
  1689. * @return {String}
  1690. */
  1691. EmailMatch.prototype.getAnchorHref = function () {
  1692. return 'mailto:' + this.email;
  1693. };
  1694. /**
  1695. * Returns the anchor text that should be generated for the match.
  1696. *
  1697. * @return {String}
  1698. */
  1699. EmailMatch.prototype.getAnchorText = function () {
  1700. return this.email;
  1701. };
  1702. return EmailMatch;
  1703. }(AbstractMatch));
  1704. /**
  1705. * Determines if the given `char` is a an allowed character in a hashtag. These
  1706. * are underscores or any alphanumeric char.
  1707. */
  1708. function isHashtagTextChar(charCode) {
  1709. return charCode === 95 /* Char.Underscore */ || isAlphaNumericOrMarkChar(charCode);
  1710. }
  1711. /**
  1712. * Determines if a hashtag match is valid.
  1713. */
  1714. function isValidHashtag(hashtag) {
  1715. // Max length of 140 for a hashtag ('#' char + 139 word chars)
  1716. return hashtag.length <= 140;
  1717. }
  1718. var hashtagServices = [
  1719. 'twitter',
  1720. 'facebook',
  1721. 'instagram',
  1722. 'tiktok',
  1723. 'youtube',
  1724. ];
  1725. /**
  1726. * @class Autolinker.match.Hashtag
  1727. * @extends Autolinker.match.AbstractMatch
  1728. *
  1729. * Represents a Hashtag match found in an input string which should be
  1730. * Autolinked.
  1731. *
  1732. * See this class's superclass ({@link Autolinker.match.Match}) for more
  1733. * details.
  1734. */
  1735. var HashtagMatch = /** @class */ (function (_super) {
  1736. __extends(HashtagMatch, _super);
  1737. /**
  1738. * @method constructor
  1739. * @param {Object} cfg The configuration properties for the Match
  1740. * instance, specified in an Object (map).
  1741. */
  1742. function HashtagMatch(cfg) {
  1743. var _this = _super.call(this, cfg) || this;
  1744. /**
  1745. * @public
  1746. * @property {'hashtag'} type
  1747. *
  1748. * A string name for the type of match that this class represents. Can be
  1749. * used in a TypeScript discriminating union to type-narrow from the
  1750. * `Match` type.
  1751. */
  1752. _this.type = 'hashtag';
  1753. /**
  1754. * @cfg {String} serviceName
  1755. *
  1756. * The service to point hashtag matches to. See {@link Autolinker#hashtag}
  1757. * for available values.
  1758. */
  1759. _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
  1760. /**
  1761. * @cfg {String} hashtag (required)
  1762. *
  1763. * The HashtagMatch that was matched, without the '#'.
  1764. */
  1765. _this.hashtag = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1766. _this.serviceName = cfg.serviceName;
  1767. _this.hashtag = cfg.hashtag;
  1768. return _this;
  1769. }
  1770. /**
  1771. * Returns a string name for the type of match that this class represents.
  1772. * For the case of HashtagMatch, returns 'hashtag'.
  1773. *
  1774. * @return {String}
  1775. */
  1776. HashtagMatch.prototype.getType = function () {
  1777. return 'hashtag';
  1778. };
  1779. /**
  1780. * Returns the configured {@link #serviceName} to point the HashtagMatch to.
  1781. * Ex: 'facebook', 'twitter'.
  1782. *
  1783. * @return {String}
  1784. */
  1785. HashtagMatch.prototype.getServiceName = function () {
  1786. return this.serviceName;
  1787. };
  1788. /**
  1789. * Returns the matched hashtag, without the '#' character.
  1790. *
  1791. * @return {String}
  1792. */
  1793. HashtagMatch.prototype.getHashtag = function () {
  1794. return this.hashtag;
  1795. };
  1796. /**
  1797. * Returns the anchor href that should be generated for the match.
  1798. *
  1799. * @return {String}
  1800. */
  1801. HashtagMatch.prototype.getAnchorHref = function () {
  1802. var serviceName = this.serviceName, hashtag = this.hashtag;
  1803. switch (serviceName) {
  1804. case 'twitter':
  1805. return 'https://twitter.com/hashtag/' + hashtag;
  1806. case 'facebook':
  1807. return 'https://www.facebook.com/hashtag/' + hashtag;
  1808. case 'instagram':
  1809. return 'https://instagram.com/explore/tags/' + hashtag;
  1810. case 'tiktok':
  1811. return 'https://www.tiktok.com/tag/' + hashtag;
  1812. case 'youtube':
  1813. return 'https://youtube.com/hashtag/' + hashtag;
  1814. /* istanbul ignore next */
  1815. default:
  1816. // Should never happen because Autolinker's constructor should block any invalid values, but just in case
  1817. assertNever(serviceName);
  1818. }
  1819. };
  1820. /**
  1821. * Returns the anchor text that should be generated for the match.
  1822. *
  1823. * @return {String}
  1824. */
  1825. HashtagMatch.prototype.getAnchorText = function () {
  1826. return '#' + this.hashtag;
  1827. };
  1828. /**
  1829. * Returns the CSS class suffixes that should be used on a tag built with
  1830. * the match. See {@link Autolinker.match.Match#getCssClassSuffixes} for
  1831. * details.
  1832. *
  1833. * @return {String[]}
  1834. */
  1835. HashtagMatch.prototype.getCssClassSuffixes = function () {
  1836. var cssClassSuffixes = _super.prototype.getCssClassSuffixes.call(this), serviceName = this.getServiceName();
  1837. if (serviceName) {
  1838. cssClassSuffixes.push(serviceName);
  1839. }
  1840. return cssClassSuffixes;
  1841. };
  1842. return HashtagMatch;
  1843. }(AbstractMatch));
  1844. var mentionRegexes = {
  1845. twitter: /^@\w{1,15}$/,
  1846. instagram: /^@[_\w]{1,30}$/,
  1847. soundcloud: /^@[-a-z0-9_]{3,25}$/,
  1848. // TikTok usernames are 1-24 characters containing letters, numbers, underscores
  1849. // and periods, but cannot end in a period: https://support.tiktok.com/en/getting-started/setting-up-your-profile/changing-your-username
  1850. tiktok: /^@[.\w]{1,23}[\w]$/,
  1851. // Youtube usernames are 3-30 characters containing letters, numbers, underscores,
  1852. // dashes, or latin middle dots ('·').
  1853. // https://support.google.com/youtube/answer/11585688?hl=en&co=GENIE.Platform%3DAndroid#tns
  1854. youtube: /^@[-.·\w]{3,30}$/,
  1855. };
  1856. /**
  1857. * Determines if the given character can be part of a mention's text characters.
  1858. *
  1859. * Accepts characters that match the RegExp `/[-\w.]/`, which are the possible
  1860. * mention characters for any service.
  1861. *
  1862. * We'll confirm the match based on the user-configured service name after the
  1863. * match is found.
  1864. */
  1865. function isMentionTextChar(charCode) {
  1866. return (charCode === 45 /* Char.Dash */ || // '-'
  1867. charCode === 46 /* Char.Dot */ || // '.'
  1868. charCode === 95 /* Char.Underscore */ || // '_'
  1869. isAsciiLetterChar(charCode) ||
  1870. isDigitChar(charCode));
  1871. }
  1872. /**
  1873. * Determines if the given `mention` text is valid.
  1874. */
  1875. function isValidMention(mention, serviceName) {
  1876. var re = mentionRegexes[serviceName];
  1877. return re.test(mention);
  1878. }
  1879. var mentionServices = [
  1880. 'twitter',
  1881. 'instagram',
  1882. 'soundcloud',
  1883. 'tiktok',
  1884. 'youtube',
  1885. ];
  1886. /**
  1887. * @class Autolinker.match.Mention
  1888. * @extends Autolinker.match.AbstractMatch
  1889. *
  1890. * Represents a Mention match found in an input string which should be Autolinked.
  1891. *
  1892. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  1893. */
  1894. var MentionMatch = /** @class */ (function (_super) {
  1895. __extends(MentionMatch, _super);
  1896. /**
  1897. * @method constructor
  1898. * @param {Object} cfg The configuration properties for the Match
  1899. * instance, specified in an Object (map).
  1900. */
  1901. function MentionMatch(cfg) {
  1902. var _this = _super.call(this, cfg) || this;
  1903. /**
  1904. * @public
  1905. * @property {'mention'} type
  1906. *
  1907. * A string name for the type of match that this class represents. Can be
  1908. * used in a TypeScript discriminating union to type-narrow from the
  1909. * `Match` type.
  1910. */
  1911. _this.type = 'mention';
  1912. /**
  1913. * @cfg {String} serviceName
  1914. *
  1915. * The service to point mention matches to. See {@link Autolinker#mention}
  1916. * for available values.
  1917. */
  1918. _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
  1919. /**
  1920. * @cfg {String} mention (required)
  1921. *
  1922. * The Mention that was matched, without the '@' character.
  1923. */
  1924. _this.mention = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1925. _this.mention = cfg.mention;
  1926. _this.serviceName = cfg.serviceName;
  1927. return _this;
  1928. }
  1929. /**
  1930. * Returns a string name for the type of match that this class represents.
  1931. * For the case of MentionMatch, returns 'mention'.
  1932. *
  1933. * @return {String}
  1934. */
  1935. MentionMatch.prototype.getType = function () {
  1936. return 'mention';
  1937. };
  1938. /**
  1939. * Returns the mention, without the '@' character.
  1940. *
  1941. * @return {String}
  1942. */
  1943. MentionMatch.prototype.getMention = function () {
  1944. return this.mention;
  1945. };
  1946. /**
  1947. * Returns the configured {@link #serviceName} to point the mention to.
  1948. * Ex: 'instagram', 'twitter', 'soundcloud'.
  1949. *
  1950. * @return {String}
  1951. */
  1952. MentionMatch.prototype.getServiceName = function () {
  1953. return this.serviceName;
  1954. };
  1955. /**
  1956. * Returns the anchor href that should be generated for the match.
  1957. *
  1958. * @return {String}
  1959. */
  1960. MentionMatch.prototype.getAnchorHref = function () {
  1961. switch (this.serviceName) {
  1962. case 'twitter':
  1963. return 'https://twitter.com/' + this.mention;
  1964. case 'instagram':
  1965. return 'https://instagram.com/' + this.mention;
  1966. case 'soundcloud':
  1967. return 'https://soundcloud.com/' + this.mention;
  1968. case 'tiktok':
  1969. return 'https://www.tiktok.com/@' + this.mention;
  1970. case 'youtube':
  1971. return 'https://youtube.com/@' + this.mention;
  1972. /* istanbul ignore next */
  1973. default:
  1974. // Should never happen because Autolinker's constructor should block any invalid values, but just in case.
  1975. assertNever(this.serviceName);
  1976. }
  1977. };
  1978. /**
  1979. * Returns the anchor text that should be generated for the match.
  1980. *
  1981. * @return {String}
  1982. */
  1983. MentionMatch.prototype.getAnchorText = function () {
  1984. return '@' + this.mention;
  1985. };
  1986. /**
  1987. * Returns the CSS class suffixes that should be used on a tag built with
  1988. * the match. See {@link Autolinker.match.Match#getCssClassSuffixes} for
  1989. * details.
  1990. *
  1991. * @return {String[]}
  1992. */
  1993. MentionMatch.prototype.getCssClassSuffixes = function () {
  1994. var cssClassSuffixes = _super.prototype.getCssClassSuffixes.call(this), serviceName = this.getServiceName();
  1995. if (serviceName) {
  1996. cssClassSuffixes.push(serviceName);
  1997. }
  1998. return cssClassSuffixes;
  1999. };
  2000. return MentionMatch;
  2001. }(AbstractMatch));
  2002. // Regex that specifies any delimiter char that allows us to treat the number as
  2003. // a phone number rather than just any other number that could appear in text.
  2004. var hasDelimCharsRe = /[-. ()]/;
  2005. // Over the years, many people have added to this regex, but it should have been
  2006. // split up by country. Maybe one day we can break this down.
  2007. var mostPhoneNumbers = /(?:(?:(?:(\+)?\d{1,3}[-. ]?)?\(?\d{3}\)?[-. ]?\d{3}[-. ]?\d{4})|(?:(\+)(?:9[976]\d|8[987530]\d|6[987]\d|5[90]\d|42\d|3[875]\d|2[98654321]\d|9[8543210]|8[6421]|6[6543210]|5[87654321]|4[987654310]|3[9643210]|2[70]|7|1)[-. ]?(?:\d[-. ]?){6,12}\d+))([,;]+[0-9]+#?)*/;
  2008. // Regex for Japanese phone numbers
  2009. var japanesePhoneRe = /(0([1-9]-?[1-9]\d{3}|[1-9]{2}-?\d{3}|[1-9]{2}\d{1}-?\d{2}|[1-9]{2}\d{2}-?\d{1})-?\d{4}|0[789]0-?\d{4}-?\d{4}|050-?\d{4}-?\d{4})/;
  2010. // Combined regex
  2011. var validPhoneNumberRe = new RegExp("^".concat(mostPhoneNumbers.source, "|").concat(japanesePhoneRe.source, "$"));
  2012. /**
  2013. * Determines if the character is a phone number separator character (i.e.
  2014. * '-', '.', or ' ' (space))
  2015. */
  2016. function isPhoneNumberSeparatorChar(charCode) {
  2017. return (charCode === 45 /* Char.Dash */ || // '-'
  2018. charCode === 46 /* Char.Dot */ || // '.'
  2019. charCode === 32 /* Char.Space */ // ' '
  2020. );
  2021. }
  2022. /**
  2023. * Determines if the character is a control character in a phone number. Control
  2024. * characters are as follows:
  2025. *
  2026. * - ',': A 1 second pause. Useful for dialing extensions once the main phone number has been reached
  2027. * - ';': A "wait" that waits for the user to take action (tap something, for instance on a smart phone)
  2028. */
  2029. function isPhoneNumberControlChar(charCode) {
  2030. return (charCode === 44 /* Char.Comma */ || // ','
  2031. charCode === 59 /* Char.SemiColon */ // ';'
  2032. );
  2033. }
  2034. /**
  2035. * Determines if the given phone number text found in a string is a valid phone
  2036. * number.
  2037. *
  2038. * Our state machine parser is simplified to grab anything that looks like a
  2039. * phone number, and this function confirms the match.
  2040. */
  2041. function isValidPhoneNumber(phoneNumberText) {
  2042. // We'll only consider the match as a phone number if there is some kind of
  2043. // delimiter character (a prefixed '+' sign, or separator chars).
  2044. //
  2045. // Accepts:
  2046. // (123) 456-7890
  2047. // +38755233976
  2048. // Does not accept:
  2049. // 1234567890 (no delimiter chars - may just be a random number that's not a phone number)
  2050. var hasDelimiters = phoneNumberText.charAt(0) === '+' || hasDelimCharsRe.test(phoneNumberText);
  2051. return hasDelimiters && validPhoneNumberRe.test(phoneNumberText);
  2052. }
  2053. /**
  2054. * @class Autolinker.match.Phone
  2055. * @extends Autolinker.match.AbstractMatch
  2056. *
  2057. * Represents a Phone number match found in an input string which should be
  2058. * Autolinked.
  2059. *
  2060. * See this class's superclass ({@link Autolinker.match.Match}) for more
  2061. * details.
  2062. */
  2063. var PhoneMatch = /** @class */ (function (_super) {
  2064. __extends(PhoneMatch, _super);
  2065. /**
  2066. * @method constructor
  2067. * @param {Object} cfg The configuration properties for the Match
  2068. * instance, specified in an Object (map).
  2069. */
  2070. function PhoneMatch(cfg) {
  2071. var _this = _super.call(this, cfg) || this;
  2072. /**
  2073. * @public
  2074. * @property {'phone'} type
  2075. *
  2076. * A string name for the type of match that this class represents. Can be
  2077. * used in a TypeScript discriminating union to type-narrow from the
  2078. * `Match` type.
  2079. */
  2080. _this.type = 'phone';
  2081. /**
  2082. * @protected
  2083. * @property {String} number (required)
  2084. *
  2085. * The phone number that was matched, without any delimiter characters.
  2086. *
  2087. * Note: This is a string to allow for prefixed 0's.
  2088. */
  2089. _this.number = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  2090. /**
  2091. * @protected
  2092. * @property {Boolean} plusSign (required)
  2093. *
  2094. * `true` if the matched phone number started with a '+' sign. We'll include
  2095. * it in the `tel:` URL if so, as this is needed for international numbers.
  2096. *
  2097. * Ex: '+1 (123) 456 7879'
  2098. */
  2099. _this.plusSign = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  2100. _this.number = cfg.number;
  2101. _this.plusSign = cfg.plusSign;
  2102. return _this;
  2103. }
  2104. /**
  2105. * Returns a string name for the type of match that this class represents.
  2106. * For the case of PhoneMatch, returns 'phone'.
  2107. *
  2108. * @return {String}
  2109. */
  2110. PhoneMatch.prototype.getType = function () {
  2111. return 'phone';
  2112. };
  2113. /**
  2114. * Returns the phone number that was matched as a string, without any
  2115. * delimiter characters.
  2116. *
  2117. * Note: This is a string to allow for prefixed 0's.
  2118. *
  2119. * @return {String}
  2120. */
  2121. PhoneMatch.prototype.getPhoneNumber = function () {
  2122. return this.number;
  2123. };
  2124. /**
  2125. * Alias of {@link #getPhoneNumber}, returns the phone number that was
  2126. * matched as a string, without any delimiter characters.
  2127. *
  2128. * Note: This is a string to allow for prefixed 0's.
  2129. *
  2130. * @return {String}
  2131. */
  2132. PhoneMatch.prototype.getNumber = function () {
  2133. return this.getPhoneNumber();
  2134. };
  2135. /**
  2136. * Returns the anchor href that should be generated for the match.
  2137. *
  2138. * @return {String}
  2139. */
  2140. PhoneMatch.prototype.getAnchorHref = function () {
  2141. return 'tel:' + (this.plusSign ? '+' : '') + this.number;
  2142. };
  2143. /**
  2144. * Returns the anchor text that should be generated for the match.
  2145. *
  2146. * @return {String}
  2147. */
  2148. PhoneMatch.prototype.getAnchorText = function () {
  2149. return this.matchedText;
  2150. };
  2151. return PhoneMatch;
  2152. }(AbstractMatch));
  2153. // For debugging: search for and uncomment other "For debugging" lines
  2154. // import CliTable from 'cli-table';
  2155. /**
  2156. * Context object containing all the state needed by the state machine functions.
  2157. *
  2158. * ## Historical note
  2159. *
  2160. * In v4.1.1, we used nested functions to handle the context via closures, but
  2161. * this necessitated re-creating the functions for each call to `parseMatches()`,
  2162. * which made them difficult for v8 to JIT optimize. In v4.1.2, we lifted all of
  2163. * the functions to the top-level scope and passed the context object between
  2164. * them, which allows the functions to be JIT compiled once and reused.
  2165. */
  2166. var ParseMatchesContext = /** @class */ (function () {
  2167. function ParseMatchesContext(text, args) {
  2168. this.charIdx = 0; // Current character index being processed
  2169. this.matches = []; // Collection of matches found
  2170. this._stateMachines = []; // Array of active state machines
  2171. this.schemeUrlMachinesCount = 0; // part of an optimization to remove the need to go into a slow code block when unnecessary. Since it's been so long since the initial implementation, not sure that this can ever go above 1, but keeping it as a counter to be safe
  2172. this.text = text;
  2173. this.tagBuilder = args.tagBuilder;
  2174. this.stripPrefix = args.stripPrefix;
  2175. this.stripTrailingSlash = args.stripTrailingSlash;
  2176. this.decodePercentEncoding = args.decodePercentEncoding;
  2177. this.hashtagServiceName = args.hashtagServiceName;
  2178. this.mentionServiceName = args.mentionServiceName;
  2179. }
  2180. Object.defineProperty(ParseMatchesContext.prototype, "stateMachines", {
  2181. get: function () {
  2182. return this._stateMachines;
  2183. },
  2184. enumerable: false,
  2185. configurable: true
  2186. });
  2187. ParseMatchesContext.prototype.addMachine = function (stateMachine) {
  2188. this._stateMachines.push(stateMachine);
  2189. if (isSchemeUrlStateMachine(stateMachine)) {
  2190. this.schemeUrlMachinesCount++;
  2191. }
  2192. };
  2193. ParseMatchesContext.prototype.removeMachine = function (stateMachine) {
  2194. // Performance note: this was originally implemented with Array.prototype.splice()
  2195. // and mutated the array in place. Switching to filter added ~280ops/sec
  2196. // on the benchmark, although likely at the expense of GC time. Perhaps
  2197. // in the future, we implement a rotating array so we never need to move
  2198. // or clean anything up
  2199. this._stateMachines = this._stateMachines.filter(function (m) { return m !== stateMachine; });
  2200. // If we've removed the URL state machine, set the flag to false.
  2201. // This flag is a quick test that helps us skip a slow section of
  2202. // code when there is already a URL state machine present.
  2203. if (isSchemeUrlStateMachine(stateMachine)) {
  2204. this.schemeUrlMachinesCount--;
  2205. }
  2206. };
  2207. ParseMatchesContext.prototype.hasSchemeUrlMachine = function () {
  2208. return this.schemeUrlMachinesCount > 0;
  2209. };
  2210. return ParseMatchesContext;
  2211. }());
  2212. /**
  2213. * Parses URL, email, twitter, mention, and hashtag matches from the given
  2214. * `text`.
  2215. */
  2216. function parseMatches(text, args) {
  2217. // Create the context object that will be passed to all state functions
  2218. var context = new ParseMatchesContext(text, args);
  2219. // For debugging: search for and uncomment other "For debugging" lines
  2220. // const table = new CliTable({
  2221. // head: ['charIdx', 'char', 'code', 'type', 'states', 'startIdx', 'reached accept state'],
  2222. // });
  2223. for (; context.charIdx < context.text.length; context.charIdx++) {
  2224. var char = text.charAt(context.charIdx);
  2225. var charCode = text.charCodeAt(context.charIdx);
  2226. if (context.stateMachines.length === 0) {
  2227. stateNoMatch(context, char, charCode);
  2228. }
  2229. else {
  2230. // Must loop through the state machines backwards for when one
  2231. // is removed
  2232. for (var stateIdx = context.stateMachines.length - 1; stateIdx >= 0; stateIdx--) {
  2233. var stateMachine = context.stateMachines[stateIdx];
  2234. switch (stateMachine.state) {
  2235. // Protocol-relative URL states
  2236. case 11 /* State.ProtocolRelativeSlash1 */:
  2237. stateProtocolRelativeSlash1(context, stateMachine, charCode);
  2238. break;
  2239. case 12 /* State.ProtocolRelativeSlash2 */:
  2240. stateProtocolRelativeSlash2(context, stateMachine, charCode);
  2241. break;
  2242. case 0 /* State.SchemeChar */:
  2243. stateSchemeChar(context, stateMachine, charCode);
  2244. break;
  2245. case 1 /* State.SchemeHyphen */:
  2246. stateSchemeHyphen(context, stateMachine, charCode);
  2247. break;
  2248. case 2 /* State.SchemeColon */:
  2249. stateSchemeColon(context, stateMachine, charCode);
  2250. break;
  2251. case 3 /* State.SchemeSlash1 */:
  2252. stateSchemeSlash1(context, stateMachine, charCode);
  2253. break;
  2254. case 4 /* State.SchemeSlash2 */:
  2255. stateSchemeSlash2(context, stateMachine, char, charCode);
  2256. break;
  2257. case 5 /* State.DomainLabelChar */:
  2258. stateDomainLabelChar(context, stateMachine, charCode);
  2259. break;
  2260. case 6 /* State.DomainHyphen */:
  2261. stateDomainHyphen(context, stateMachine, char, charCode);
  2262. break;
  2263. case 7 /* State.DomainDot */:
  2264. stateDomainDot(context, stateMachine, char, charCode);
  2265. break;
  2266. case 13 /* State.IpV4Digit */:
  2267. stateIpV4Digit(context, stateMachine, charCode);
  2268. break;
  2269. case 14 /* State.IpV4Dot */:
  2270. stateIpV4Dot(context, stateMachine, charCode);
  2271. break;
  2272. case 8 /* State.PortColon */:
  2273. statePortColon(context, stateMachine, charCode);
  2274. break;
  2275. case 9 /* State.PortNumber */:
  2276. statePortNumber(context, stateMachine, charCode);
  2277. break;
  2278. case 10 /* State.Path */:
  2279. statePath(context, stateMachine, charCode);
  2280. break;
  2281. // Email States
  2282. case 15 /* State.EmailMailto_M */:
  2283. stateEmailMailto_M(context, stateMachine, char, charCode);
  2284. break;
  2285. case 16 /* State.EmailMailto_A */:
  2286. stateEmailMailto_A(context, stateMachine, char, charCode);
  2287. break;
  2288. case 17 /* State.EmailMailto_I */:
  2289. stateEmailMailto_I(context, stateMachine, char, charCode);
  2290. break;
  2291. case 18 /* State.EmailMailto_L */:
  2292. stateEmailMailto_L(context, stateMachine, char, charCode);
  2293. break;
  2294. case 19 /* State.EmailMailto_T */:
  2295. stateEmailMailto_T(context, stateMachine, char, charCode);
  2296. break;
  2297. case 20 /* State.EmailMailto_O */:
  2298. stateEmailMailto_O(context, stateMachine, charCode);
  2299. break;
  2300. case 21 /* State.EmailMailto_Colon */:
  2301. stateEmailMailtoColon(context, stateMachine, charCode);
  2302. break;
  2303. case 22 /* State.EmailLocalPart */:
  2304. stateEmailLocalPart(context, stateMachine, charCode);
  2305. break;
  2306. case 23 /* State.EmailLocalPartDot */:
  2307. stateEmailLocalPartDot(context, stateMachine, charCode);
  2308. break;
  2309. case 24 /* State.EmailAtSign */:
  2310. stateEmailAtSign(context, stateMachine, charCode);
  2311. break;
  2312. case 25 /* State.EmailDomainChar */:
  2313. stateEmailDomainChar(context, stateMachine, charCode);
  2314. break;
  2315. case 26 /* State.EmailDomainHyphen */:
  2316. stateEmailDomainHyphen(context, stateMachine, charCode);
  2317. break;
  2318. case 27 /* State.EmailDomainDot */:
  2319. stateEmailDomainDot(context, stateMachine, charCode);
  2320. break;
  2321. // Hashtag states
  2322. case 28 /* State.HashtagHashChar */:
  2323. stateHashtagHashChar(context, stateMachine, charCode);
  2324. break;
  2325. case 29 /* State.HashtagTextChar */:
  2326. stateHashtagTextChar(context, stateMachine, charCode);
  2327. break;
  2328. // Mention states
  2329. case 30 /* State.MentionAtChar */:
  2330. stateMentionAtChar(context, stateMachine, charCode);
  2331. break;
  2332. case 31 /* State.MentionTextChar */:
  2333. stateMentionTextChar(context, stateMachine, charCode);
  2334. break;
  2335. // Phone number states
  2336. case 32 /* State.PhoneNumberOpenParen */:
  2337. statePhoneNumberOpenParen(context, stateMachine, char, charCode);
  2338. break;
  2339. case 33 /* State.PhoneNumberAreaCodeDigit1 */:
  2340. statePhoneNumberAreaCodeDigit1(context, stateMachine, charCode);
  2341. break;
  2342. case 34 /* State.PhoneNumberAreaCodeDigit2 */:
  2343. statePhoneNumberAreaCodeDigit2(context, stateMachine, charCode);
  2344. break;
  2345. case 35 /* State.PhoneNumberAreaCodeDigit3 */:
  2346. statePhoneNumberAreaCodeDigit3(context, stateMachine, charCode);
  2347. break;
  2348. case 36 /* State.PhoneNumberCloseParen */:
  2349. statePhoneNumberCloseParen(context, stateMachine, char, charCode);
  2350. break;
  2351. case 37 /* State.PhoneNumberPlus */:
  2352. statePhoneNumberPlus(context, stateMachine, char, charCode);
  2353. break;
  2354. case 38 /* State.PhoneNumberDigit */:
  2355. statePhoneNumberDigit(context, stateMachine, char, charCode);
  2356. break;
  2357. case 39 /* State.PhoneNumberSeparator */:
  2358. statePhoneNumberSeparator(context, stateMachine, char, charCode);
  2359. break;
  2360. case 40 /* State.PhoneNumberControlChar */:
  2361. statePhoneNumberControlChar(context, stateMachine, charCode);
  2362. break;
  2363. case 41 /* State.PhoneNumberPoundChar */:
  2364. statePhoneNumberPoundChar(context, stateMachine, charCode);
  2365. break;
  2366. /* istanbul ignore next */
  2367. default:
  2368. assertNever(stateMachine.state);
  2369. }
  2370. }
  2371. // Special case for handling a colon (or other non-alphanumeric)
  2372. // when preceded by another character, such as in the text:
  2373. // Link 1:http://google.com
  2374. // In this case, the 'h' character after the colon wouldn't start a
  2375. // new scheme url because we'd be in a ipv4 or tld url and the colon
  2376. // would be interpreted as a port ':' char. Also, only start a new
  2377. // scheme url machine if there isn't currently one so we don't start
  2378. // new ones for colons inside a url
  2379. //
  2380. // TODO: The addition of this snippet (to fix the bug) in 4.0.1 lost
  2381. // us ~500 ops/sec on the benchmarks. Optimizing it with the
  2382. // hasSchemeUrlMachine() flag and optimizing the isSchemeStartChar()
  2383. // method for 4.1.3 got us back about ~400ops/sec. One potential way
  2384. // to improve this even ore is to add this snippet to individual
  2385. // state handler functions where it can occur to prevent running it
  2386. // on every loop interation.
  2387. if (!context.hasSchemeUrlMachine() &&
  2388. context.charIdx > 0 &&
  2389. isSchemeStartChar(charCode)) {
  2390. var prevCharCode = context.text.charCodeAt(context.charIdx - 1);
  2391. if (!isSchemeStartChar(prevCharCode)) {
  2392. context.addMachine(createSchemeUrlStateMachine(context.charIdx, 0 /* State.SchemeChar */));
  2393. }
  2394. }
  2395. }
  2396. // For debugging: search for and uncomment other "For debugging" lines
  2397. // table.push([
  2398. // String(context.charIdx),
  2399. // char,
  2400. // `10: ${char.charCodeAt(0)}\n0x: ${char.charCodeAt(0).toString(16)}\nU+${char.codePointAt(0)}`,
  2401. // context.stateMachines.map(machine => `${StateMachineType[machine.type]}${'matchType' in machine ? ` (${UrlStateMachineMatchType[machine.matchType]})` : ''}`).join('\n') || '(none)',
  2402. // context.stateMachines.map(machine => State[machine.state]).join('\n') || '(none)',
  2403. // context.stateMachines.map(m => m.startIdx).join('\n'),
  2404. // context.stateMachines.map(m => m.acceptStateReached).join('\n'),
  2405. // ]);
  2406. }
  2407. // Capture any valid match at the end of the string
  2408. // Note: this loop must happen in reverse because
  2409. // captureMatchIfValidAndRemove() removes state machines from the array
  2410. // and we'll end up skipping every other one if we remove while looping
  2411. // forward
  2412. for (var i = context.stateMachines.length - 1; i >= 0; i--) {
  2413. context.stateMachines.forEach(function (stateMachine) {
  2414. return captureMatchIfValidAndRemove(context, stateMachine);
  2415. });
  2416. }
  2417. // For debugging: search for and uncomment other "For debugging" lines
  2418. // console.log(`\nRead string:\n ${text}`);
  2419. // console.log(table.toString());
  2420. return context.matches;
  2421. }
  2422. /**
  2423. * Handles the state when we're not in a URL/email/etc. (i.e. when no state machines exist)
  2424. */
  2425. function stateNoMatch(context, char, charCode) {
  2426. var charIdx = context.charIdx;
  2427. if (charCode === 35 /* Char.NumberSign */ /* '#' */) {
  2428. // Hash char, start a Hashtag match
  2429. context.addMachine(createHashtagStateMachine(charIdx, 28 /* State.HashtagHashChar */));
  2430. }
  2431. else if (charCode === 64 /* Char.AtSign */ /* '@' */) {
  2432. // '@' char, start a Mention match
  2433. context.addMachine(createMentionStateMachine(charIdx, 30 /* State.MentionAtChar */));
  2434. }
  2435. else if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2436. // A slash could begin a protocol-relative URL
  2437. context.addMachine(createTldUrlStateMachine(charIdx, 11 /* State.ProtocolRelativeSlash1 */));
  2438. }
  2439. else if (charCode === 43 /* Char.Plus */ /* '+' */) {
  2440. // A '+' char can start a Phone number
  2441. context.addMachine(createPhoneNumberStateMachine(charIdx, 37 /* State.PhoneNumberPlus */));
  2442. }
  2443. else if (charCode === 40 /* Char.OpenParen */ /* '(' */) {
  2444. context.addMachine(createPhoneNumberStateMachine(charIdx, 32 /* State.PhoneNumberOpenParen */));
  2445. }
  2446. else {
  2447. if (isDigitChar(charCode)) {
  2448. // A digit could start a phone number
  2449. context.addMachine(createPhoneNumberStateMachine(charIdx, 38 /* State.PhoneNumberDigit */));
  2450. // A digit could start an IP address
  2451. context.addMachine(createIpV4UrlStateMachine(charIdx, 13 /* State.IpV4Digit */));
  2452. }
  2453. if (isEmailLocalPartStartChar(charCode)) {
  2454. // Any email local part. An 'm' character in particular could
  2455. // start a 'mailto:' match
  2456. var startState = char.toLowerCase() === 'm' ? 15 /* State.EmailMailto_M */ : 22 /* State.EmailLocalPart */;
  2457. context.addMachine(createEmailStateMachine(charIdx, startState));
  2458. }
  2459. if (isSchemeStartChar(charCode)) {
  2460. // An uppercase or lowercase letter may start a scheme match
  2461. context.addMachine(createSchemeUrlStateMachine(charIdx, 0 /* State.SchemeChar */));
  2462. }
  2463. if (isAlphaNumericOrMarkChar(charCode)) {
  2464. // A unicode alpha character or digit could start a domain name
  2465. // label for a TLD match
  2466. context.addMachine(createTldUrlStateMachine(charIdx, 5 /* State.DomainLabelChar */));
  2467. }
  2468. }
  2469. // Anything else, remain in the "non-url" state by not creating any
  2470. // state machines
  2471. }
  2472. // Implements ABNF: ALPHA *( ALPHA / DIGIT / "+" / "-" / "." )
  2473. function stateSchemeChar(context, stateMachine, charCode) {
  2474. if (charCode === 58 /* Char.Colon */ /* ':' */) {
  2475. stateMachine.state = 2 /* State.SchemeColon */;
  2476. }
  2477. else if (charCode === 45 /* Char.Dash */ /* '-' */) {
  2478. stateMachine.state = 1 /* State.SchemeHyphen */;
  2479. }
  2480. else if (isSchemeChar(charCode)) ;
  2481. else {
  2482. // Any other character, not a scheme
  2483. context.removeMachine(stateMachine);
  2484. }
  2485. }
  2486. function stateSchemeHyphen(context, stateMachine, charCode) {
  2487. var charIdx = context.charIdx;
  2488. if (charCode === 45 /* Char.Dash */ /* '-' */) ;
  2489. else if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2490. // Not a valid scheme match, but may be the start of a
  2491. // protocol-relative match (such as //google.com)
  2492. context.removeMachine(stateMachine);
  2493. context.addMachine(createTldUrlStateMachine(charIdx, 11 /* State.ProtocolRelativeSlash1 */));
  2494. }
  2495. else if (isSchemeChar(charCode)) {
  2496. stateMachine.state = 0 /* State.SchemeChar */;
  2497. }
  2498. else {
  2499. // Any other character, not a scheme
  2500. context.removeMachine(stateMachine);
  2501. }
  2502. }
  2503. // https://tools.ietf.org/html/rfc3986#appendix-A
  2504. function stateSchemeColon(context, stateMachine, charCode) {
  2505. var charIdx = context.charIdx;
  2506. if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2507. stateMachine.state = 3 /* State.SchemeSlash1 */;
  2508. }
  2509. else if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2510. // We've read something like 'hello:.' - don't capture
  2511. context.removeMachine(stateMachine);
  2512. }
  2513. else if (isDomainLabelStartChar(charCode)) {
  2514. stateMachine.state = 5 /* State.DomainLabelChar */;
  2515. // It's possible that we read an "introduction" piece of text,
  2516. // and the character after the current colon actually starts an
  2517. // actual scheme. An example of this is:
  2518. // "The link:http://google.com"
  2519. // Hence, start a new machine to capture this match if so
  2520. if (isSchemeStartChar(charCode)) {
  2521. context.addMachine(createSchemeUrlStateMachine(charIdx, 0 /* State.SchemeChar */));
  2522. }
  2523. }
  2524. else {
  2525. context.removeMachine(stateMachine);
  2526. }
  2527. }
  2528. // https://tools.ietf.org/html/rfc3986#appendix-A
  2529. function stateSchemeSlash1(context, stateMachine, charCode) {
  2530. if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2531. stateMachine.state = 4 /* State.SchemeSlash2 */;
  2532. }
  2533. else if (isPathChar(charCode)) {
  2534. stateMachine.state = 10 /* State.Path */;
  2535. stateMachine.acceptStateReached = true;
  2536. }
  2537. else {
  2538. captureMatchIfValidAndRemove(context, stateMachine);
  2539. }
  2540. }
  2541. function stateSchemeSlash2(context, stateMachine, char, charCode) {
  2542. if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2543. // 3rd slash, must be an absolute path (`path-absolute` in the
  2544. // ABNF), such as in "file:///c:/windows/etc". See
  2545. // https://tools.ietf.org/html/rfc3986#appendix-A
  2546. stateMachine.state = 10 /* State.Path */;
  2547. stateMachine.acceptStateReached = true;
  2548. }
  2549. else if (isDomainLabelStartChar(charCode)) {
  2550. // start of "authority" section - see https://tools.ietf.org/html/rfc3986#appendix-A
  2551. stateMachine.state = 5 /* State.DomainLabelChar */;
  2552. stateMachine.acceptStateReached = true;
  2553. }
  2554. else {
  2555. // not valid
  2556. context.removeMachine(stateMachine);
  2557. }
  2558. }
  2559. // Handles after we've read a '/' from the NonUrl state
  2560. function stateProtocolRelativeSlash1(context, stateMachine, charCode) {
  2561. if (charCode === 47 /* Char.Slash */ /* '/' */) {
  2562. stateMachine.state = 12 /* State.ProtocolRelativeSlash2 */;
  2563. }
  2564. else {
  2565. // Anything else, cannot be the start of a protocol-relative
  2566. // URL.
  2567. context.removeMachine(stateMachine);
  2568. }
  2569. }
  2570. // Handles after we've read a second '/', which could start a protocol-relative URL
  2571. function stateProtocolRelativeSlash2(context, stateMachine, charCode) {
  2572. if (isDomainLabelStartChar(charCode)) {
  2573. stateMachine.state = 5 /* State.DomainLabelChar */;
  2574. }
  2575. else {
  2576. // Anything else, not a URL
  2577. context.removeMachine(stateMachine);
  2578. }
  2579. }
  2580. // Handles when we have read a domain label character
  2581. function stateDomainLabelChar(context, stateMachine, charCode) {
  2582. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2583. stateMachine.state = 7 /* State.DomainDot */;
  2584. }
  2585. else if (charCode === 45 /* Char.Dash */ /* '-' */) {
  2586. stateMachine.state = 6 /* State.DomainHyphen */;
  2587. }
  2588. else if (charCode === 58 /* Char.Colon */ /* ':' */) {
  2589. // Beginning of a port number, end the domain name
  2590. stateMachine.state = 8 /* State.PortColon */;
  2591. }
  2592. else if (isUrlSuffixStartChar(charCode)) {
  2593. // '/', '?', or '#'
  2594. stateMachine.state = 10 /* State.Path */;
  2595. }
  2596. else if (isDomainLabelChar(charCode)) ;
  2597. else {
  2598. // Anything else, end the domain name
  2599. captureMatchIfValidAndRemove(context, stateMachine);
  2600. }
  2601. }
  2602. function stateDomainHyphen(context, stateMachine, char, charCode) {
  2603. if (charCode === 45 /* Char.Dash */ /* '-' */) ;
  2604. else if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2605. // Not valid to have a '-.' in a domain label
  2606. captureMatchIfValidAndRemove(context, stateMachine);
  2607. }
  2608. else if (isDomainLabelStartChar(charCode)) {
  2609. stateMachine.state = 5 /* State.DomainLabelChar */;
  2610. }
  2611. else {
  2612. captureMatchIfValidAndRemove(context, stateMachine);
  2613. }
  2614. }
  2615. function stateDomainDot(context, stateMachine, char, charCode) {
  2616. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2617. // domain names cannot have multiple '.'s next to each other.
  2618. // It's possible we've already read a valid domain name though,
  2619. // and that the '..' sequence just forms an ellipsis at the end
  2620. // of a sentence
  2621. captureMatchIfValidAndRemove(context, stateMachine);
  2622. }
  2623. else if (isDomainLabelStartChar(charCode)) {
  2624. stateMachine.state = 5 /* State.DomainLabelChar */;
  2625. stateMachine.acceptStateReached = true; // after hitting a dot, and then another domain label, we've reached an accept state
  2626. }
  2627. else {
  2628. // Anything else, end the domain name
  2629. captureMatchIfValidAndRemove(context, stateMachine);
  2630. }
  2631. }
  2632. function stateIpV4Digit(context, stateMachine, charCode) {
  2633. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2634. stateMachine.state = 14 /* State.IpV4Dot */;
  2635. }
  2636. else if (charCode === 58 /* Char.Colon */ /* ':' */) {
  2637. // Beginning of a port number
  2638. stateMachine.state = 8 /* State.PortColon */;
  2639. }
  2640. else if (isDigitChar(charCode)) ;
  2641. else if (isUrlSuffixStartChar(charCode)) {
  2642. stateMachine.state = 10 /* State.Path */;
  2643. }
  2644. else if (isAlphaNumericOrMarkChar(charCode)) {
  2645. // If we hit an alpha character, must not be an IPv4
  2646. // Example of this: 1.2.3.4abc
  2647. context.removeMachine(stateMachine);
  2648. }
  2649. else {
  2650. captureMatchIfValidAndRemove(context, stateMachine);
  2651. }
  2652. }
  2653. function stateIpV4Dot(context, stateMachine, charCode) {
  2654. if (isDigitChar(charCode)) {
  2655. stateMachine.octetsEncountered++;
  2656. // Once we have encountered 4 octets, it's *potentially* a valid
  2657. // IPv4 address. Our IPv4 regex will confirm the match later
  2658. // though to make sure each octet is in the 0-255 range, and
  2659. // there's exactly 4 octets (not 5 or more)
  2660. if (stateMachine.octetsEncountered === 4) {
  2661. stateMachine.acceptStateReached = true;
  2662. }
  2663. stateMachine.state = 13 /* State.IpV4Digit */;
  2664. }
  2665. else {
  2666. captureMatchIfValidAndRemove(context, stateMachine);
  2667. }
  2668. }
  2669. function statePortColon(context, stateMachine, charCode) {
  2670. if (isDigitChar(charCode)) {
  2671. stateMachine.state = 9 /* State.PortNumber */;
  2672. }
  2673. else {
  2674. captureMatchIfValidAndRemove(context, stateMachine);
  2675. }
  2676. }
  2677. function statePortNumber(context, stateMachine, charCode) {
  2678. if (isDigitChar(charCode)) ;
  2679. else if (isUrlSuffixStartChar(charCode)) {
  2680. // '/', '?', or '#'
  2681. stateMachine.state = 10 /* State.Path */;
  2682. }
  2683. else {
  2684. captureMatchIfValidAndRemove(context, stateMachine);
  2685. }
  2686. }
  2687. function statePath(context, stateMachine, charCode) {
  2688. if (isPathChar(charCode)) ;
  2689. else {
  2690. captureMatchIfValidAndRemove(context, stateMachine);
  2691. }
  2692. }
  2693. // Handles if we're reading a 'mailto:' prefix on the string
  2694. function stateEmailMailto_M(context, stateMachine, char, charCode) {
  2695. if (char.toLowerCase() === 'a') {
  2696. stateMachine.state = 16 /* State.EmailMailto_A */;
  2697. }
  2698. else {
  2699. stateEmailLocalPart(context, stateMachine, charCode);
  2700. }
  2701. }
  2702. function stateEmailMailto_A(context, stateMachine, char, charCode) {
  2703. if (char.toLowerCase() === 'i') {
  2704. stateMachine.state = 17 /* State.EmailMailto_I */;
  2705. }
  2706. else {
  2707. stateEmailLocalPart(context, stateMachine, charCode);
  2708. }
  2709. }
  2710. function stateEmailMailto_I(context, stateMachine, char, charCode) {
  2711. if (char.toLowerCase() === 'l') {
  2712. stateMachine.state = 18 /* State.EmailMailto_L */;
  2713. }
  2714. else {
  2715. stateEmailLocalPart(context, stateMachine, charCode);
  2716. }
  2717. }
  2718. function stateEmailMailto_L(context, stateMachine, char, charCode) {
  2719. if (char.toLowerCase() === 't') {
  2720. stateMachine.state = 19 /* State.EmailMailto_T */;
  2721. }
  2722. else {
  2723. stateEmailLocalPart(context, stateMachine, charCode);
  2724. }
  2725. }
  2726. function stateEmailMailto_T(context, stateMachine, char, charCode) {
  2727. if (char.toLowerCase() === 'o') {
  2728. stateMachine.state = 20 /* State.EmailMailto_O */;
  2729. }
  2730. else {
  2731. stateEmailLocalPart(context, stateMachine, charCode);
  2732. }
  2733. }
  2734. function stateEmailMailto_O(context, stateMachine, charCode) {
  2735. if (charCode === 58 /* Char.Colon */ /* ':' */) {
  2736. stateMachine.state = 21 /* State.EmailMailto_Colon */;
  2737. }
  2738. else {
  2739. stateEmailLocalPart(context, stateMachine, charCode);
  2740. }
  2741. }
  2742. function stateEmailMailtoColon(context, stateMachine, charCode) {
  2743. if (isEmailLocalPartChar(charCode)) {
  2744. stateMachine.state = 22 /* State.EmailLocalPart */;
  2745. }
  2746. else {
  2747. context.removeMachine(stateMachine);
  2748. }
  2749. }
  2750. // Handles the state when we're currently in the "local part" of an
  2751. // email address (as opposed to the "domain part")
  2752. function stateEmailLocalPart(context, stateMachine, charCode) {
  2753. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2754. stateMachine.state = 23 /* State.EmailLocalPartDot */;
  2755. }
  2756. else if (charCode === 64 /* Char.AtSign */ /* '@' */) {
  2757. stateMachine.state = 24 /* State.EmailAtSign */;
  2758. }
  2759. else if (isEmailLocalPartChar(charCode)) {
  2760. // stay in the "local part" of the email address
  2761. // Note: because stateEmailLocalPart() is called from the
  2762. // 'mailto' states (when the 'mailto' prefix itself has been
  2763. // broken), make sure to set the state to EmailLocalPart
  2764. stateMachine.state = 22 /* State.EmailLocalPart */;
  2765. }
  2766. else {
  2767. // not an email address character
  2768. context.removeMachine(stateMachine);
  2769. }
  2770. }
  2771. // Handles the state where we've read a '.' character in the local part of
  2772. // the email address (i.e. the part before the '@' character)
  2773. function stateEmailLocalPartDot(context, stateMachine, charCode) {
  2774. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2775. // We read a second '.' in a row, not a valid email address
  2776. // local part
  2777. context.removeMachine(stateMachine);
  2778. }
  2779. else if (charCode === 64 /* Char.AtSign */ /* '@' */) {
  2780. // We read the '@' character immediately after a dot ('.'), not
  2781. // an email address
  2782. context.removeMachine(stateMachine);
  2783. }
  2784. else if (isEmailLocalPartChar(charCode)) {
  2785. stateMachine.state = 22 /* State.EmailLocalPart */;
  2786. }
  2787. else {
  2788. // Anything else, not an email address
  2789. context.removeMachine(stateMachine);
  2790. }
  2791. }
  2792. function stateEmailAtSign(context, stateMachine, charCode) {
  2793. if (isDomainLabelStartChar(charCode)) {
  2794. stateMachine.state = 25 /* State.EmailDomainChar */;
  2795. }
  2796. else {
  2797. // Anything else, not an email address
  2798. context.removeMachine(stateMachine);
  2799. }
  2800. }
  2801. function stateEmailDomainChar(context, stateMachine, charCode) {
  2802. if (charCode === 46 /* Char.Dot */ /* '.' */) {
  2803. stateMachine.state = 27 /* State.EmailDomainDot */;
  2804. }
  2805. else if (charCode === 45 /* Char.Dash */ /* '-' */) {
  2806. stateMachine.state = 26 /* State.EmailDomainHyphen */;
  2807. }
  2808. else if (isDomainLabelChar(charCode)) ;
  2809. else {
  2810. // Anything else, we potentially matched if the criteria has
  2811. // been met
  2812. captureMatchIfValidAndRemove(context, stateMachine);
  2813. }
  2814. }
  2815. function stateEmailDomainHyphen(context, stateMachine, charCode) {
  2816. if (charCode === 45 /* Char.Dash */ /* '-' */ || charCode === 46 /* Char.Dot */ /* '.' */) {
  2817. // Not valid to have two hyphens ("--") or hypen+dot ("-.")
  2818. captureMatchIfValidAndRemove(context, stateMachine);
  2819. }
  2820. else if (isDomainLabelChar(charCode)) {
  2821. stateMachine.state = 25 /* State.EmailDomainChar */;
  2822. }
  2823. else {
  2824. // Anything else
  2825. captureMatchIfValidAndRemove(context, stateMachine);
  2826. }
  2827. }
  2828. function stateEmailDomainDot(context, stateMachine, charCode) {
  2829. if (charCode === 46 /* Char.Dot */ /* '.' */ || charCode === 45 /* Char.Dash */ /* '-' */) {
  2830. // not valid to have two dots ("..") or dot+hypen (".-")
  2831. captureMatchIfValidAndRemove(context, stateMachine);
  2832. }
  2833. else if (isDomainLabelStartChar(charCode)) {
  2834. stateMachine.state = 25 /* State.EmailDomainChar */;
  2835. // After having read a '.' and then a valid domain character,
  2836. // we now know that the domain part of the email is valid, and
  2837. // we have found at least a partial EmailMatch (however, the
  2838. // email address may have additional characters from this point)
  2839. stateMachine.acceptStateReached = true;
  2840. }
  2841. else {
  2842. // Anything else
  2843. captureMatchIfValidAndRemove(context, stateMachine);
  2844. }
  2845. }
  2846. // Handles the state when we've just encountered a '#' character
  2847. function stateHashtagHashChar(context, stateMachine, charCode) {
  2848. if (isHashtagTextChar(charCode)) {
  2849. // '#' char with valid hash text char following
  2850. stateMachine.state = 29 /* State.HashtagTextChar */;
  2851. stateMachine.acceptStateReached = true;
  2852. }
  2853. else {
  2854. context.removeMachine(stateMachine);
  2855. }
  2856. }
  2857. // Handles the state when we're currently in the hash tag's text chars
  2858. function stateHashtagTextChar(context, stateMachine, charCode) {
  2859. if (isHashtagTextChar(charCode)) ;
  2860. else {
  2861. captureMatchIfValidAndRemove(context, stateMachine);
  2862. }
  2863. }
  2864. // Handles the state when we've just encountered a '@' character
  2865. function stateMentionAtChar(context, stateMachine, charCode) {
  2866. if (isMentionTextChar(charCode)) {
  2867. // '@' char with valid mention text char following
  2868. stateMachine.state = 31 /* State.MentionTextChar */;
  2869. stateMachine.acceptStateReached = true;
  2870. }
  2871. else {
  2872. context.removeMachine(stateMachine);
  2873. }
  2874. }
  2875. // Handles the state when we're currently in the mention's text chars
  2876. function stateMentionTextChar(context, stateMachine, charCode) {
  2877. if (isMentionTextChar(charCode)) ;
  2878. else if (isAlphaNumericOrMarkChar(charCode)) {
  2879. // Char is invalid for a mention text char, not a valid match.
  2880. // Note that ascii alphanumeric chars are okay (which are tested
  2881. // in the previous 'if' statement, but others are not)
  2882. context.removeMachine(stateMachine);
  2883. }
  2884. else {
  2885. captureMatchIfValidAndRemove(context, stateMachine);
  2886. }
  2887. }
  2888. function statePhoneNumberPlus(context, stateMachine, char, charCode) {
  2889. if (isDigitChar(charCode)) {
  2890. stateMachine.state = 38 /* State.PhoneNumberDigit */;
  2891. }
  2892. else {
  2893. context.removeMachine(stateMachine);
  2894. // This character may start a new match. Add states for it
  2895. stateNoMatch(context, char, charCode);
  2896. }
  2897. }
  2898. function statePhoneNumberOpenParen(context, stateMachine, char, charCode) {
  2899. if (isDigitChar(charCode)) {
  2900. stateMachine.state = 33 /* State.PhoneNumberAreaCodeDigit1 */;
  2901. }
  2902. else {
  2903. context.removeMachine(stateMachine);
  2904. }
  2905. // It's also possible that the paren was just an open brace for
  2906. // a piece of text. Start other machines
  2907. stateNoMatch(context, char, charCode);
  2908. }
  2909. function statePhoneNumberAreaCodeDigit1(context, stateMachine, charCode) {
  2910. if (isDigitChar(charCode)) {
  2911. stateMachine.state = 34 /* State.PhoneNumberAreaCodeDigit2 */;
  2912. }
  2913. else {
  2914. context.removeMachine(stateMachine);
  2915. }
  2916. }
  2917. function statePhoneNumberAreaCodeDigit2(context, stateMachine, charCode) {
  2918. if (isDigitChar(charCode)) {
  2919. stateMachine.state = 35 /* State.PhoneNumberAreaCodeDigit3 */;
  2920. }
  2921. else {
  2922. context.removeMachine(stateMachine);
  2923. }
  2924. }
  2925. function statePhoneNumberAreaCodeDigit3(context, stateMachine, charCode) {
  2926. if (charCode === 41 /* Char.CloseParen */ /* ')' */) {
  2927. stateMachine.state = 36 /* State.PhoneNumberCloseParen */;
  2928. }
  2929. else {
  2930. context.removeMachine(stateMachine);
  2931. }
  2932. }
  2933. function statePhoneNumberCloseParen(context, stateMachine, char, charCode) {
  2934. if (isDigitChar(charCode)) {
  2935. stateMachine.state = 38 /* State.PhoneNumberDigit */;
  2936. }
  2937. else if (isPhoneNumberSeparatorChar(charCode)) {
  2938. stateMachine.state = 39 /* State.PhoneNumberSeparator */;
  2939. }
  2940. else {
  2941. context.removeMachine(stateMachine);
  2942. }
  2943. }
  2944. function statePhoneNumberDigit(context, stateMachine, char, charCode) {
  2945. var charIdx = context.charIdx;
  2946. // For now, if we've reached any digits, we'll say that the machine
  2947. // has reached its accept state. The phone regex will confirm the
  2948. // match later.
  2949. // Alternatively, we could count the number of digits to avoid
  2950. // invoking the phone number regex
  2951. stateMachine.acceptStateReached = true;
  2952. if (isPhoneNumberControlChar(charCode)) {
  2953. stateMachine.state = 40 /* State.PhoneNumberControlChar */;
  2954. }
  2955. else if (charCode === 35 /* Char.NumberSign */ /* '#' */) {
  2956. stateMachine.state = 41 /* State.PhoneNumberPoundChar */;
  2957. }
  2958. else if (isDigitChar(charCode)) ;
  2959. else if (charCode === 40 /* Char.OpenParen */ /* '(' */) {
  2960. stateMachine.state = 32 /* State.PhoneNumberOpenParen */;
  2961. }
  2962. else if (isPhoneNumberSeparatorChar(charCode)) {
  2963. stateMachine.state = 39 /* State.PhoneNumberSeparator */;
  2964. }
  2965. else {
  2966. captureMatchIfValidAndRemove(context, stateMachine);
  2967. // The transition from a digit character to a letter can be the
  2968. // start of a new scheme URL match
  2969. if (isSchemeStartChar(charCode)) {
  2970. context.addMachine(createSchemeUrlStateMachine(charIdx, 0 /* State.SchemeChar */));
  2971. }
  2972. }
  2973. }
  2974. function statePhoneNumberSeparator(context, stateMachine, char, charCode) {
  2975. if (isDigitChar(charCode)) {
  2976. stateMachine.state = 38 /* State.PhoneNumberDigit */;
  2977. }
  2978. else if (charCode === 40 /* Char.OpenParen */ /* '(' */) {
  2979. stateMachine.state = 32 /* State.PhoneNumberOpenParen */;
  2980. }
  2981. else {
  2982. captureMatchIfValidAndRemove(context, stateMachine);
  2983. // This character may start a new match. Add states for it
  2984. stateNoMatch(context, char, charCode);
  2985. }
  2986. }
  2987. // The ";" characters is "wait" in a phone number
  2988. // The "," characters is "pause" in a phone number
  2989. function statePhoneNumberControlChar(context, stateMachine, charCode) {
  2990. if (isPhoneNumberControlChar(charCode)) ;
  2991. else if (charCode === 35 /* Char.NumberSign */ /* '#' */) {
  2992. stateMachine.state = 41 /* State.PhoneNumberPoundChar */;
  2993. }
  2994. else if (isDigitChar(charCode)) {
  2995. stateMachine.state = 38 /* State.PhoneNumberDigit */;
  2996. }
  2997. else {
  2998. captureMatchIfValidAndRemove(context, stateMachine);
  2999. }
  3000. }
  3001. // The "#" characters is "pound" in a phone number
  3002. function statePhoneNumberPoundChar(context, stateMachine, charCode) {
  3003. if (isPhoneNumberControlChar(charCode)) {
  3004. stateMachine.state = 40 /* State.PhoneNumberControlChar */;
  3005. }
  3006. else if (isDigitChar(charCode)) {
  3007. // According to some of the older tests, if there's a digit
  3008. // after a '#' sign, the match is invalid. TODO: Revisit if this is true
  3009. context.removeMachine(stateMachine);
  3010. }
  3011. else {
  3012. captureMatchIfValidAndRemove(context, stateMachine);
  3013. }
  3014. }
  3015. /*
  3016. * Captures a match if it is valid (i.e. has a full domain name for a
  3017. * TLD match). If a match is not valid, it is possible that we want to
  3018. * keep reading characters in order to make a full match.
  3019. */
  3020. function captureMatchIfValidAndRemove(context, stateMachine) {
  3021. var matches = context.matches, text = context.text, charIdx = context.charIdx, tagBuilder = context.tagBuilder, stripPrefix = context.stripPrefix, stripTrailingSlash = context.stripTrailingSlash, decodePercentEncoding = context.decodePercentEncoding, hashtagServiceName = context.hashtagServiceName, mentionServiceName = context.mentionServiceName;
  3022. // Remove the state machine first. There are a number of code paths
  3023. // which return out of this function early, so make sure we have
  3024. // this done
  3025. context.removeMachine(stateMachine);
  3026. // Make sure the state machine being checked has actually reached an
  3027. // "accept" state. If it hasn't reach one, it can't be a match
  3028. if (!stateMachine.acceptStateReached) {
  3029. return;
  3030. }
  3031. var startIdx = stateMachine.startIdx;
  3032. var matchedText = text.slice(stateMachine.startIdx, charIdx);
  3033. // Handle any unbalanced braces (parens, square brackets, or curly
  3034. // brackets) inside the URL. This handles situations like:
  3035. // The link (google.com)
  3036. // and
  3037. // Check out this link here (en.wikipedia.org/wiki/IANA_(disambiguation))
  3038. //
  3039. // And also remove any punctuation chars at the end such as:
  3040. // '?', ',', ':', '.', etc.
  3041. matchedText = excludeUnbalancedTrailingBracesAndPunctuation(matchedText);
  3042. switch (stateMachine.type) {
  3043. case 0 /* StateMachineType.Url */: {
  3044. // We don't want to accidentally match a URL that is preceded by an
  3045. // '@' character, which would be an email address
  3046. var charBeforeUrlMatch = text.charCodeAt(stateMachine.startIdx - 1);
  3047. if (charBeforeUrlMatch === 64 /* Char.AtSign */ /* '@' */) {
  3048. return;
  3049. }
  3050. switch (stateMachine.matchType) {
  3051. case 0 /* UrlStateMachineMatchType.Scheme */: {
  3052. // Autolinker accepts many characters in a url's scheme (like `fake://test.com`).
  3053. // However, in cases where a URL is missing whitespace before an obvious link,
  3054. // (for example: `nowhitespacehttp://www.test.com`), we only want the match to start
  3055. // at the http:// part. We will check if the match contains a common scheme and then
  3056. // shift the match to start from there.
  3057. var httpSchemeMatch = httpSchemeRe.exec(matchedText);
  3058. if (httpSchemeMatch) {
  3059. // If we found an overmatched URL, we want to find the index
  3060. // of where the match should start and shift the match to
  3061. // start from the beginning of the common scheme
  3062. startIdx = startIdx + httpSchemeMatch.index;
  3063. matchedText = matchedText.slice(httpSchemeMatch.index);
  3064. }
  3065. if (!isValidSchemeUrl(matchedText)) {
  3066. return; // not a valid match
  3067. }
  3068. break;
  3069. }
  3070. case 1 /* UrlStateMachineMatchType.Tld */: {
  3071. if (!isValidTldMatch(matchedText)) {
  3072. return; // not a valid match
  3073. }
  3074. break;
  3075. }
  3076. case 2 /* UrlStateMachineMatchType.IpV4 */: {
  3077. if (!isValidIpV4Address(matchedText)) {
  3078. return; // not a valid match
  3079. }
  3080. break;
  3081. }
  3082. /* istanbul ignore next */
  3083. default:
  3084. assertNever(stateMachine);
  3085. }
  3086. matches.push(new UrlMatch({
  3087. tagBuilder: tagBuilder,
  3088. matchedText: matchedText,
  3089. offset: startIdx,
  3090. urlMatchType: toUrlMatchType(stateMachine.matchType),
  3091. url: matchedText,
  3092. protocolRelativeMatch: matchedText.slice(0, 2) === '//',
  3093. // TODO: Do these settings need to be passed to the match,
  3094. // or should we handle them here in UrlMatcher?
  3095. stripPrefix: stripPrefix,
  3096. stripTrailingSlash: stripTrailingSlash,
  3097. decodePercentEncoding: decodePercentEncoding,
  3098. }));
  3099. break;
  3100. }
  3101. case 1 /* StateMachineType.Email */: {
  3102. // if the email address has a valid TLD, add it to the list of matches
  3103. if (isValidEmail(matchedText)) {
  3104. matches.push(new EmailMatch({
  3105. tagBuilder: tagBuilder,
  3106. matchedText: matchedText,
  3107. offset: startIdx,
  3108. email: matchedText.replace(mailtoSchemePrefixRe, ''),
  3109. }));
  3110. }
  3111. break;
  3112. }
  3113. case 2 /* StateMachineType.Hashtag */: {
  3114. if (isValidHashtag(matchedText)) {
  3115. matches.push(new HashtagMatch({
  3116. tagBuilder: tagBuilder,
  3117. matchedText: matchedText,
  3118. offset: startIdx,
  3119. serviceName: hashtagServiceName,
  3120. hashtag: matchedText.slice(1),
  3121. }));
  3122. }
  3123. break;
  3124. }
  3125. case 3 /* StateMachineType.Mention */: {
  3126. if (isValidMention(matchedText, mentionServiceName)) {
  3127. matches.push(new MentionMatch({
  3128. tagBuilder: tagBuilder,
  3129. matchedText: matchedText,
  3130. offset: startIdx,
  3131. serviceName: mentionServiceName,
  3132. mention: matchedText.slice(1), // strip off the '@' character at the beginning
  3133. }));
  3134. }
  3135. break;
  3136. }
  3137. case 4 /* StateMachineType.Phone */: {
  3138. // remove any trailing spaces that were considered as "separator"
  3139. // chars by the state machine
  3140. matchedText = matchedText.replace(/ +$/g, '');
  3141. if (isValidPhoneNumber(matchedText)) {
  3142. var cleanNumber = matchedText.replace(/[^0-9,;#]/g, ''); // strip out non-digit characters exclude comma semicolon and #
  3143. matches.push(new PhoneMatch({
  3144. tagBuilder: tagBuilder,
  3145. matchedText: matchedText,
  3146. offset: startIdx,
  3147. number: cleanNumber,
  3148. plusSign: matchedText.charAt(0) === '+',
  3149. }));
  3150. }
  3151. break;
  3152. }
  3153. /* istanbul ignore next */
  3154. default:
  3155. assertNever(stateMachine);
  3156. }
  3157. }
  3158. /**
  3159. * Helper function to convert a UrlStateMachineMatchType value to its
  3160. * UrlMatchType equivalent.
  3161. */
  3162. function toUrlMatchType(stateMachineMatchType) {
  3163. switch (stateMachineMatchType) {
  3164. case 0 /* UrlStateMachineMatchType.Scheme */:
  3165. return 'scheme';
  3166. case 1 /* UrlStateMachineMatchType.Tld */:
  3167. return 'tld';
  3168. case 2 /* UrlStateMachineMatchType.IpV4 */:
  3169. return 'ipV4';
  3170. /* istanbul ignore next */
  3171. default:
  3172. assertNever(stateMachineMatchType);
  3173. }
  3174. }
  3175. var oppositeBrace = {
  3176. ')': '(',
  3177. '}': '{',
  3178. ']': '[',
  3179. };
  3180. /**
  3181. * Determines if a match found has unmatched closing parenthesis,
  3182. * square brackets or curly brackets. If so, these unbalanced symbol(s) will be
  3183. * removed from the URL match itself.
  3184. *
  3185. * A match may have an extra closing parenthesis/square brackets/curly brackets
  3186. * at the end of the match because these are valid URL path characters. For
  3187. * example, "wikipedia.com/something_(disambiguation)" should be auto-linked.
  3188. *
  3189. * However, an extra parenthesis *will* be included when the URL itself is
  3190. * wrapped in parenthesis, such as in the case of:
  3191. *
  3192. * "(wikipedia.com/something_(disambiguation))"
  3193. *
  3194. * In this case, the last closing parenthesis should *not* be part of the
  3195. * URL itself, and this method will exclude it from the returned URL.
  3196. *
  3197. * For square brackets in URLs such as in PHP arrays, the same behavior as
  3198. * parenthesis discussed above should happen:
  3199. *
  3200. * "[http://www.example.com/foo.php?bar[]=1&bar[]=2&bar[]=3]"
  3201. *
  3202. * The very last closing square bracket should not be part of the URL itself,
  3203. * and therefore this method will remove it.
  3204. *
  3205. * @param matchedText The full matched URL/email/hashtag/etc. from the state
  3206. * machine parser.
  3207. * @return The updated matched text with extraneous suffix characters removed.
  3208. */
  3209. function excludeUnbalancedTrailingBracesAndPunctuation(matchedText) {
  3210. var braceCounts = {
  3211. '(': 0,
  3212. '{': 0,
  3213. '[': 0,
  3214. };
  3215. for (var i = 0; i < matchedText.length; i++) {
  3216. var char = matchedText.charAt(i);
  3217. var charCode = matchedText.charCodeAt(i);
  3218. if (isOpenBraceChar(charCode)) {
  3219. braceCounts[char]++;
  3220. }
  3221. else if (isCloseBraceChar(charCode)) {
  3222. braceCounts[oppositeBrace[char]]--;
  3223. }
  3224. }
  3225. var endIdx = matchedText.length - 1;
  3226. while (endIdx >= 0) {
  3227. var char = matchedText.charAt(endIdx);
  3228. var charCode = matchedText.charCodeAt(endIdx);
  3229. if (isCloseBraceChar(charCode)) {
  3230. var oppositeBraceChar = oppositeBrace[char];
  3231. if (braceCounts[oppositeBraceChar] < 0) {
  3232. braceCounts[oppositeBraceChar]++;
  3233. endIdx--;
  3234. }
  3235. else {
  3236. break;
  3237. }
  3238. }
  3239. else if (isUrlSuffixNotAllowedAsFinalChar(charCode)) {
  3240. // Walk back a punctuation char like '?', ',', ':', '.', etc.
  3241. endIdx--;
  3242. }
  3243. else {
  3244. break;
  3245. }
  3246. }
  3247. return matchedText.slice(0, endIdx + 1);
  3248. }
  3249. function createSchemeUrlStateMachine(startIdx, state) {
  3250. return {
  3251. type: 0 /* StateMachineType.Url */,
  3252. startIdx: startIdx,
  3253. state: state,
  3254. acceptStateReached: false,
  3255. matchType: 0 /* UrlStateMachineMatchType.Scheme */,
  3256. };
  3257. }
  3258. function createTldUrlStateMachine(startIdx, state) {
  3259. return {
  3260. type: 0 /* StateMachineType.Url */,
  3261. startIdx: startIdx,
  3262. state: state,
  3263. acceptStateReached: false,
  3264. matchType: 1 /* UrlStateMachineMatchType.Tld */,
  3265. };
  3266. }
  3267. function createIpV4UrlStateMachine(startIdx, state) {
  3268. return {
  3269. type: 0 /* StateMachineType.Url */,
  3270. startIdx: startIdx,
  3271. state: state,
  3272. acceptStateReached: false,
  3273. matchType: 2 /* UrlStateMachineMatchType.IpV4 */,
  3274. octetsEncountered: 1, // starts at 1 because we create this machine when encountering the first octet
  3275. };
  3276. }
  3277. function createEmailStateMachine(startIdx, state) {
  3278. return {
  3279. type: 1 /* StateMachineType.Email */,
  3280. startIdx: startIdx,
  3281. state: state,
  3282. acceptStateReached: false,
  3283. };
  3284. }
  3285. function createHashtagStateMachine(startIdx, state) {
  3286. return {
  3287. type: 2 /* StateMachineType.Hashtag */,
  3288. startIdx: startIdx,
  3289. state: state,
  3290. acceptStateReached: false,
  3291. };
  3292. }
  3293. function createMentionStateMachine(startIdx, state) {
  3294. return {
  3295. type: 3 /* StateMachineType.Mention */,
  3296. startIdx: startIdx,
  3297. state: state,
  3298. acceptStateReached: false,
  3299. };
  3300. }
  3301. function createPhoneNumberStateMachine(startIdx, state) {
  3302. return {
  3303. type: 4 /* StateMachineType.Phone */,
  3304. startIdx: startIdx,
  3305. state: state,
  3306. acceptStateReached: false,
  3307. };
  3308. }
  3309. function isSchemeUrlStateMachine(machine) {
  3310. return (machine.type === 0 /* StateMachineType.Url */ &&
  3311. machine.matchType === 0 /* UrlStateMachineMatchType.Scheme */);
  3312. }
  3313. // For debugging: search for other "For debugging" lines
  3314. // import CliTable from 'cli-table';
  3315. var CurrentTag = /** @class */ (function () {
  3316. function CurrentTag(cfg) {
  3317. if (cfg === void 0) { cfg = {}; }
  3318. this.idx = cfg.idx !== undefined ? cfg.idx : -1;
  3319. this.type = cfg.type || 'tag';
  3320. this.name = cfg.name || '';
  3321. this.isOpening = !!cfg.isOpening;
  3322. this.isClosing = !!cfg.isClosing;
  3323. }
  3324. return CurrentTag;
  3325. }());
  3326. var noCurrentTag = new CurrentTag(); // shared reference for when there is no current tag currently being read
  3327. /**
  3328. * Context object containing all the state needed by the HTML parsing state
  3329. * machine function.
  3330. *
  3331. * ## Historical note
  3332. *
  3333. * In v4.1.5, we used nested functions to handle the context via closures, but
  3334. * this necessitated re-creating the functions for each call to `parseHtml()`,
  3335. * which made them difficult for v8 to JIT optimize. In v4.1.6, we lifted all of
  3336. * the functions to the top-level scope and passed the context object between
  3337. * them, which allows the functions to be JIT compiled once and reused.
  3338. */
  3339. var ParseHtmlContext = /** @class */ (function () {
  3340. function ParseHtmlContext(html, callbacks) {
  3341. this.charIdx = 0; // Current character index being processed
  3342. this.state = 0 /* State.Data */; // begin in the Data state
  3343. this.currentDataIdx = 0; // where the current data start index is
  3344. this.currentTag = noCurrentTag; // describes the current tag that is being read
  3345. this.html = html;
  3346. this.callbacks = callbacks;
  3347. }
  3348. return ParseHtmlContext;
  3349. }());
  3350. /**
  3351. * Parses an HTML string, calling the callbacks to notify of tags and text.
  3352. *
  3353. * ## History
  3354. *
  3355. * This file previously used a regular expression to find html tags in the input
  3356. * text. Unfortunately, we ran into a bunch of catastrophic backtracking issues
  3357. * with certain input text, causing Autolinker to either hang or just take a
  3358. * really long time to parse the string.
  3359. *
  3360. * The current code is intended to be a O(n) algorithm that walks through
  3361. * the string in one pass, and tries to be as cheap as possible. We don't need
  3362. * to implement the full HTML spec, but rather simply determine where the string
  3363. * looks like an HTML tag, and where it looks like text (so that we can autolink
  3364. * that).
  3365. *
  3366. * This state machine parser is intended just to be a simple but performant
  3367. * parser of HTML for the subset of requirements we have. We simply need to:
  3368. *
  3369. * 1. Determine where HTML tags are
  3370. * 2. Determine the tag name (Autolinker specifically only cares about <a>,
  3371. * <script>, and <style> tags, so as not to link any text within them)
  3372. *
  3373. * We don't need to:
  3374. *
  3375. * 1. Create a parse tree
  3376. * 2. Auto-close tags with invalid markup
  3377. * 3. etc.
  3378. *
  3379. * The other intention behind this is that we didn't want to add external
  3380. * dependencies on the Autolinker utility which would increase its size. For
  3381. * instance, adding htmlparser2 adds 125kb to the minified output file,
  3382. * increasing its final size from 47kb to 172kb (at the time of writing). It
  3383. * also doesn't work exactly correctly, treating the string "<3 blah blah blah"
  3384. * as an HTML tag.
  3385. *
  3386. * Reference for HTML spec:
  3387. *
  3388. * https://www.w3.org/TR/html51/syntax.html#sec-tokenization
  3389. *
  3390. * @param {String} html The HTML to parse
  3391. * @param {Object} callbacks
  3392. * @param {Function} callbacks.onOpenTag Callback function to call when an open
  3393. * tag is parsed. Called with the tagName as its argument.
  3394. * @param {Function} callbacks.onCloseTag Callback function to call when a close
  3395. * tag is parsed. Called with the tagName as its argument. If a self-closing
  3396. * tag is found, `onCloseTag` is called immediately after `onOpenTag`.
  3397. * @param {Function} callbacks.onText Callback function to call when text (i.e
  3398. * not an HTML tag) is parsed. Called with the text (string) as its first
  3399. * argument, and offset (number) into the string as its second.
  3400. */
  3401. function parseHtml(html, callbacks) {
  3402. var context = new ParseHtmlContext(html, callbacks);
  3403. // For debugging: search for other "For debugging" lines
  3404. // const table = new CliTable( {
  3405. // head: [ 'charIdx', 'char', 'state', 'currentDataIdx', 'currentOpenTagIdx', 'tag.type' ]
  3406. // } );
  3407. var len = html.length;
  3408. while (context.charIdx < len) {
  3409. var char = html.charAt(context.charIdx);
  3410. var charCode = html.charCodeAt(context.charIdx);
  3411. // For debugging: search for other "For debugging" lines
  3412. // ALSO: Temporarily remove the 'const' keyword on the State enum
  3413. // table.push([
  3414. // String(charIdx),
  3415. // char,
  3416. // State[state],
  3417. // String(currentDataIdx),
  3418. // String(currentTag.idx),
  3419. // currentTag.idx === -1 ? '' : currentTag.type
  3420. // ]);
  3421. switch (context.state) {
  3422. case 0 /* State.Data */:
  3423. stateData(context, char);
  3424. break;
  3425. case 1 /* State.TagOpen */:
  3426. stateTagOpen(context, char, charCode);
  3427. break;
  3428. case 2 /* State.EndTagOpen */:
  3429. stateEndTagOpen(context, char, charCode);
  3430. break;
  3431. case 3 /* State.TagName */:
  3432. stateTagName(context, char, charCode);
  3433. break;
  3434. case 4 /* State.BeforeAttributeName */:
  3435. stateBeforeAttributeName(context, char, charCode);
  3436. break;
  3437. case 5 /* State.AttributeName */:
  3438. stateAttributeName(context, char, charCode);
  3439. break;
  3440. case 6 /* State.AfterAttributeName */:
  3441. stateAfterAttributeName(context, char, charCode);
  3442. break;
  3443. case 7 /* State.BeforeAttributeValue */:
  3444. stateBeforeAttributeValue(context, char, charCode);
  3445. break;
  3446. case 8 /* State.AttributeValueDoubleQuoted */:
  3447. stateAttributeValueDoubleQuoted(context, char);
  3448. break;
  3449. case 9 /* State.AttributeValueSingleQuoted */:
  3450. stateAttributeValueSingleQuoted(context, char);
  3451. break;
  3452. case 10 /* State.AttributeValueUnquoted */:
  3453. stateAttributeValueUnquoted(context, char, charCode);
  3454. break;
  3455. case 11 /* State.AfterAttributeValueQuoted */:
  3456. stateAfterAttributeValueQuoted(context, char, charCode);
  3457. break;
  3458. case 12 /* State.SelfClosingStartTag */:
  3459. stateSelfClosingStartTag(context, char);
  3460. break;
  3461. case 13 /* State.MarkupDeclarationOpenState */:
  3462. stateMarkupDeclarationOpen(context);
  3463. break;
  3464. case 14 /* State.CommentStart */:
  3465. stateCommentStart(context, char);
  3466. break;
  3467. case 15 /* State.CommentStartDash */:
  3468. stateCommentStartDash(context, char);
  3469. break;
  3470. case 16 /* State.Comment */:
  3471. stateComment(context, char);
  3472. break;
  3473. case 17 /* State.CommentEndDash */:
  3474. stateCommentEndDash(context, char);
  3475. break;
  3476. case 18 /* State.CommentEnd */:
  3477. stateCommentEnd(context, char);
  3478. break;
  3479. case 19 /* State.CommentEndBang */:
  3480. stateCommentEndBang(context, char);
  3481. break;
  3482. case 20 /* State.Doctype */:
  3483. stateDoctype(context, char);
  3484. break;
  3485. /* istanbul ignore next */
  3486. default:
  3487. assertNever(context.state);
  3488. }
  3489. // For debugging: search for other "For debugging" lines
  3490. // ALSO: Temporarily remove the 'const' keyword on the State enum
  3491. // table.push([
  3492. // String(context.charIdx),
  3493. // char,
  3494. // State[context.state],
  3495. // String(context.currentDataIdx),
  3496. // String(context.currentTag.idx),
  3497. // context.currentTag.idx === -1 ? '' : context.currentTag.type
  3498. // ]);
  3499. context.charIdx++;
  3500. }
  3501. if (context.currentDataIdx < context.charIdx) {
  3502. emitText(context);
  3503. }
  3504. // For debugging: search for other "For debugging" lines
  3505. // console.log( '\n' + table.toString() );
  3506. }
  3507. // Called when non-tags are being read (i.e. the text around HTML †ags)
  3508. // https://www.w3.org/TR/html51/syntax.html#data-state
  3509. function stateData(context, char) {
  3510. if (char === '<') {
  3511. startNewTag(context);
  3512. }
  3513. }
  3514. // Called after a '<' is read from the Data state
  3515. // https://www.w3.org/TR/html51/syntax.html#tag-open-state
  3516. function stateTagOpen(context, char, charCode) {
  3517. if (char === '!') {
  3518. context.state = 13 /* State.MarkupDeclarationOpenState */;
  3519. }
  3520. else if (char === '/') {
  3521. context.state = 2 /* State.EndTagOpen */;
  3522. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { isClosing: true }));
  3523. }
  3524. else if (char === '<') {
  3525. // start of another tag (ignore the previous, incomplete one)
  3526. startNewTag(context);
  3527. }
  3528. else if (isAsciiLetterChar(charCode)) {
  3529. // tag name start (and no '/' read)
  3530. context.state = 3 /* State.TagName */;
  3531. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { isOpening: true }));
  3532. }
  3533. else {
  3534. // Any other
  3535. context.state = 0 /* State.Data */;
  3536. context.currentTag = noCurrentTag;
  3537. }
  3538. }
  3539. // After a '<x', '</x' sequence is read (where 'x' is a letter character),
  3540. // this is to continue reading the tag name
  3541. // https://www.w3.org/TR/html51/syntax.html#tag-name-state
  3542. function stateTagName(context, char, charCode) {
  3543. if (isWhitespaceChar(charCode)) {
  3544. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { name: captureTagName(context) }));
  3545. context.state = 4 /* State.BeforeAttributeName */;
  3546. }
  3547. else if (char === '<') {
  3548. // start of another tag (ignore the previous, incomplete one)
  3549. startNewTag(context);
  3550. }
  3551. else if (char === '/') {
  3552. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { name: captureTagName(context) }));
  3553. context.state = 12 /* State.SelfClosingStartTag */;
  3554. }
  3555. else if (char === '>') {
  3556. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { name: captureTagName(context) }));
  3557. emitTagAndPreviousTextNode(context); // resets to Data state as well
  3558. }
  3559. else if (!isAsciiLetterChar(charCode) && !isDigitChar(charCode) && char !== ':') {
  3560. // Anything else that does not form an html tag. Note: the colon
  3561. // character is accepted for XML namespaced tags
  3562. resetToDataState(context);
  3563. }
  3564. else ;
  3565. }
  3566. // Called after the '/' is read from a '</' sequence
  3567. // https://www.w3.org/TR/html51/syntax.html#end-tag-open-state
  3568. function stateEndTagOpen(context, char, charCode) {
  3569. if (char === '>') {
  3570. // parse error. Encountered "</>". Skip it without treating as a tag
  3571. resetToDataState(context);
  3572. }
  3573. else if (isAsciiLetterChar(charCode)) {
  3574. context.state = 3 /* State.TagName */;
  3575. }
  3576. else {
  3577. // some other non-tag-like character, don't treat this as a tag
  3578. resetToDataState(context);
  3579. }
  3580. }
  3581. // https://www.w3.org/TR/html51/syntax.html#before-attribute-name-state
  3582. function stateBeforeAttributeName(context, char, charCode) {
  3583. if (isWhitespaceChar(charCode)) ;
  3584. else if (char === '/') {
  3585. context.state = 12 /* State.SelfClosingStartTag */;
  3586. }
  3587. else if (char === '>') {
  3588. emitTagAndPreviousTextNode(context); // resets to Data state as well
  3589. }
  3590. else if (char === '<') {
  3591. // start of another tag (ignore the previous, incomplete one)
  3592. startNewTag(context);
  3593. }
  3594. else if (char === "=" || isQuoteChar(charCode) || isControlChar(charCode)) {
  3595. // "Parse error" characters that, according to the spec, should be
  3596. // appended to the attribute name, but we'll treat these characters
  3597. // as not forming a real HTML tag
  3598. resetToDataState(context);
  3599. }
  3600. else {
  3601. // Any other char, start of a new attribute name
  3602. context.state = 5 /* State.AttributeName */;
  3603. }
  3604. }
  3605. // https://www.w3.org/TR/html51/syntax.html#attribute-name-state
  3606. function stateAttributeName(context, char, charCode) {
  3607. if (isWhitespaceChar(charCode)) {
  3608. context.state = 6 /* State.AfterAttributeName */;
  3609. }
  3610. else if (char === '/') {
  3611. context.state = 12 /* State.SelfClosingStartTag */;
  3612. }
  3613. else if (char === '=') {
  3614. context.state = 7 /* State.BeforeAttributeValue */;
  3615. }
  3616. else if (char === '>') {
  3617. emitTagAndPreviousTextNode(context); // resets to Data state as well
  3618. }
  3619. else if (char === '<') {
  3620. // start of another tag (ignore the previous, incomplete one)
  3621. startNewTag(context);
  3622. }
  3623. else if (isQuoteChar(charCode)) {
  3624. // "Parse error" characters that, according to the spec, should be
  3625. // appended to the attribute name, but we'll treat these characters
  3626. // as not forming a real HTML tag
  3627. resetToDataState(context);
  3628. }
  3629. else ;
  3630. }
  3631. // https://www.w3.org/TR/html51/syntax.html#after-attribute-name-state
  3632. function stateAfterAttributeName(context, char, charCode) {
  3633. if (isWhitespaceChar(charCode)) ;
  3634. else if (char === '/') {
  3635. context.state = 12 /* State.SelfClosingStartTag */;
  3636. }
  3637. else if (char === '=') {
  3638. context.state = 7 /* State.BeforeAttributeValue */;
  3639. }
  3640. else if (char === '>') {
  3641. emitTagAndPreviousTextNode(context);
  3642. }
  3643. else if (char === '<') {
  3644. // start of another tag (ignore the previous, incomplete one)
  3645. startNewTag(context);
  3646. }
  3647. else if (isQuoteChar(charCode)) {
  3648. // "Parse error" characters that, according to the spec, should be
  3649. // appended to the attribute name, but we'll treat these characters
  3650. // as not forming a real HTML tag
  3651. resetToDataState(context);
  3652. }
  3653. else {
  3654. // Any other character, start a new attribute in the current tag
  3655. context.state = 5 /* State.AttributeName */;
  3656. }
  3657. }
  3658. // https://www.w3.org/TR/html51/syntax.html#before-attribute-value-state
  3659. function stateBeforeAttributeValue(context, char, charCode) {
  3660. if (isWhitespaceChar(charCode)) ;
  3661. else if (char === "\"") {
  3662. context.state = 8 /* State.AttributeValueDoubleQuoted */;
  3663. }
  3664. else if (char === "'") {
  3665. context.state = 9 /* State.AttributeValueSingleQuoted */;
  3666. }
  3667. else if (/[>=`]/.test(char)) {
  3668. // Invalid chars after an '=' for an attribute value, don't count
  3669. // the current tag as an HTML tag
  3670. resetToDataState(context);
  3671. }
  3672. else if (char === '<') {
  3673. // start of another tag (ignore the previous, incomplete one)
  3674. startNewTag(context);
  3675. }
  3676. else {
  3677. // Any other character, consider it an unquoted attribute value
  3678. context.state = 10 /* State.AttributeValueUnquoted */;
  3679. }
  3680. }
  3681. // https://www.w3.org/TR/html51/syntax.html#attribute-value-double-quoted-state
  3682. function stateAttributeValueDoubleQuoted(context, char) {
  3683. if (char === "\"") {
  3684. // end the current double-quoted attribute
  3685. context.state = 11 /* State.AfterAttributeValueQuoted */;
  3686. }
  3687. }
  3688. // https://www.w3.org/TR/html51/syntax.html#attribute-value-single-quoted-state
  3689. function stateAttributeValueSingleQuoted(context, char) {
  3690. if (char === "'") {
  3691. // end the current single-quoted attribute
  3692. context.state = 11 /* State.AfterAttributeValueQuoted */;
  3693. }
  3694. }
  3695. // https://www.w3.org/TR/html51/syntax.html#attribute-value-unquoted-state
  3696. function stateAttributeValueUnquoted(context, char, charCode) {
  3697. if (isWhitespaceChar(charCode)) {
  3698. context.state = 4 /* State.BeforeAttributeName */;
  3699. }
  3700. else if (char === '>') {
  3701. emitTagAndPreviousTextNode(context);
  3702. }
  3703. else if (char === '<') {
  3704. // start of another tag (ignore the previous, incomplete one)
  3705. startNewTag(context);
  3706. }
  3707. else ;
  3708. }
  3709. // Called after a double-quoted or single-quoted attribute value is read
  3710. // (i.e. after the closing quote character)
  3711. // https://www.w3.org/TR/html51/syntax.html#after-attribute-value-quoted-state
  3712. function stateAfterAttributeValueQuoted(context, char, charCode) {
  3713. if (isWhitespaceChar(charCode)) {
  3714. context.state = 4 /* State.BeforeAttributeName */;
  3715. }
  3716. else if (char === '/') {
  3717. context.state = 12 /* State.SelfClosingStartTag */;
  3718. }
  3719. else if (char === '>') {
  3720. emitTagAndPreviousTextNode(context);
  3721. }
  3722. else if (char === '<') {
  3723. // start of another tag (ignore the previous, incomplete one)
  3724. startNewTag(context);
  3725. }
  3726. else {
  3727. // Any other character, "parse error". Spec says to switch to the
  3728. // BeforeAttributeState and re-consume the character, as it may be
  3729. // the start of a new attribute name
  3730. context.state = 4 /* State.BeforeAttributeName */;
  3731. reconsumeCurrentChar(context);
  3732. }
  3733. }
  3734. // A '/' has just been read in the current tag (presumably for '/>'), and
  3735. // this handles the next character
  3736. // https://www.w3.org/TR/html51/syntax.html#self-closing-start-tag-state
  3737. function stateSelfClosingStartTag(context, char) {
  3738. if (char === '>') {
  3739. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { isClosing: true }));
  3740. emitTagAndPreviousTextNode(context); // resets to Data state as well
  3741. }
  3742. else {
  3743. // Note: the spec calls for a character after a '/' within a start
  3744. // tag to go back into the BeforeAttributeName state (in order to
  3745. // read more attributes, but for the purposes of Autolinker, this is
  3746. // most likely not a valid HTML tag. For example: "<something / other>"
  3747. // state = State.BeforeAttributeName;
  3748. // Instead, just treat as regular text
  3749. resetToDataState(context);
  3750. }
  3751. }
  3752. // https://www.w3.org/TR/html51/syntax.html#markup-declaration-open-state
  3753. // (HTML Comments or !DOCTYPE)
  3754. function stateMarkupDeclarationOpen(context) {
  3755. var html = context.html, charIdx = context.charIdx;
  3756. if (html.slice(charIdx, charIdx + 2) === '--') {
  3757. // html comment
  3758. context.charIdx++; // "consume" the second '-' character. Next loop iteration will consume the character after the '<!--' sequence
  3759. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { type: 'comment' }));
  3760. context.state = 14 /* State.CommentStart */;
  3761. }
  3762. else if (html.slice(charIdx, charIdx + 7).toUpperCase() === 'DOCTYPE') {
  3763. context.charIdx += 6; // "consume" the characters "OCTYPE" (the current loop iteraction consumed the 'D'). Next loop iteration will consume the character after the '<!DOCTYPE' sequence
  3764. context.currentTag = new CurrentTag(__assign(__assign({}, context.currentTag), { type: 'doctype' }));
  3765. context.state = 20 /* State.Doctype */;
  3766. }
  3767. else {
  3768. // At this point, the spec specifies that the state machine should
  3769. // enter the "bogus comment" state, in which case any character(s)
  3770. // after the '<!' that were read should become an HTML comment up
  3771. // until the first '>' that is read (or EOF). Instead, we'll assume
  3772. // that a user just typed '<!' as part of some piece of non-html
  3773. // text
  3774. resetToDataState(context);
  3775. }
  3776. }
  3777. // Handles after the sequence '<!--' has been read
  3778. // https://www.w3.org/TR/html51/syntax.html#comment-start-state
  3779. function stateCommentStart(context, char) {
  3780. if (char === '-') {
  3781. // We've read the sequence '<!---' at this point (3 dashes)
  3782. context.state = 15 /* State.CommentStartDash */;
  3783. }
  3784. else if (char === '>') {
  3785. // At this point, we'll assume the comment wasn't a real comment
  3786. // so we'll just emit it as data. We basically read the sequence
  3787. // '<!-->'
  3788. resetToDataState(context);
  3789. }
  3790. else {
  3791. // Any other char, take it as part of the comment
  3792. context.state = 16 /* State.Comment */;
  3793. }
  3794. }
  3795. // We've read the sequence '<!---' at this point (3 dashes)
  3796. // https://www.w3.org/TR/html51/syntax.html#comment-start-dash-state
  3797. function stateCommentStartDash(context, char) {
  3798. if (char === '-') {
  3799. // We've read '<!----' (4 dashes) at this point
  3800. context.state = 18 /* State.CommentEnd */;
  3801. }
  3802. else if (char === '>') {
  3803. // At this point, we'll assume the comment wasn't a real comment
  3804. // so we'll just emit it as data. We basically read the sequence
  3805. // '<!--->'
  3806. resetToDataState(context);
  3807. }
  3808. else {
  3809. // Anything else, take it as a valid comment
  3810. context.state = 16 /* State.Comment */;
  3811. }
  3812. }
  3813. // Currently reading the comment's text (data)
  3814. // https://www.w3.org/TR/html51/syntax.html#comment-state
  3815. function stateComment(context, char) {
  3816. if (char === '-') {
  3817. context.state = 17 /* State.CommentEndDash */;
  3818. }
  3819. }
  3820. // When we we've read the first dash inside a comment, it may signal the
  3821. // end of the comment if we read another dash
  3822. // https://www.w3.org/TR/html51/syntax.html#comment-end-dash-state
  3823. function stateCommentEndDash(context, char) {
  3824. if (char === '-') {
  3825. context.state = 18 /* State.CommentEnd */;
  3826. }
  3827. else {
  3828. // Wasn't a dash, must still be part of the comment
  3829. context.state = 16 /* State.Comment */;
  3830. }
  3831. }
  3832. // After we've read two dashes inside a comment, it may signal the end of
  3833. // the comment if we then read a '>' char
  3834. // https://www.w3.org/TR/html51/syntax.html#comment-end-state
  3835. function stateCommentEnd(context, char) {
  3836. if (char === '>') {
  3837. emitTagAndPreviousTextNode(context);
  3838. }
  3839. else if (char === '!') {
  3840. context.state = 19 /* State.CommentEndBang */;
  3841. }
  3842. else if (char === '-') ;
  3843. else {
  3844. // Anything else, switch back to the comment state since we didn't
  3845. // read the full "end comment" sequence (i.e. '-->')
  3846. context.state = 16 /* State.Comment */;
  3847. }
  3848. }
  3849. // We've read the sequence '--!' inside of a comment
  3850. // https://www.w3.org/TR/html51/syntax.html#comment-end-bang-state
  3851. function stateCommentEndBang(context, char) {
  3852. if (char === '-') {
  3853. // We read the sequence '--!-' inside of a comment. The last dash
  3854. // could signify that the comment is going to close
  3855. context.state = 17 /* State.CommentEndDash */;
  3856. }
  3857. else if (char === '>') {
  3858. // End of comment with the sequence '--!>'
  3859. emitTagAndPreviousTextNode(context);
  3860. }
  3861. else {
  3862. // The '--!' was not followed by a '>', continue reading the
  3863. // comment's text
  3864. context.state = 16 /* State.Comment */;
  3865. }
  3866. }
  3867. /**
  3868. * For DOCTYPES in particular, we don't care about the attributes. Just
  3869. * advance to the '>' character and emit the tag, unless we find a '<'
  3870. * character in which case we'll start a new tag.
  3871. *
  3872. * Example doctype tag:
  3873. * <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  3874. *
  3875. * Actual spec: https://www.w3.org/TR/html51/syntax.html#doctype-state
  3876. */
  3877. function stateDoctype(context, char) {
  3878. if (char === '>') {
  3879. emitTagAndPreviousTextNode(context);
  3880. }
  3881. else if (char === '<') {
  3882. startNewTag(context);
  3883. }
  3884. else ;
  3885. }
  3886. /**
  3887. * Resets the state back to the Data state, and removes the current tag.
  3888. *
  3889. * We'll generally run this function whenever a "parse error" is
  3890. * encountered, where the current tag that is being read no longer looks
  3891. * like a real HTML tag.
  3892. */
  3893. function resetToDataState(context) {
  3894. context.state = 0 /* State.Data */;
  3895. context.currentTag = noCurrentTag;
  3896. }
  3897. /**
  3898. * Starts a new HTML tag at the current index, ignoring any previous HTML
  3899. * tag that was being read.
  3900. *
  3901. * We'll generally run this function whenever we read a new '<' character,
  3902. * including when we read a '<' character inside of an HTML tag that we were
  3903. * previously reading.
  3904. */
  3905. function startNewTag(context) {
  3906. context.state = 1 /* State.TagOpen */;
  3907. context.currentTag = new CurrentTag({ idx: context.charIdx });
  3908. }
  3909. /**
  3910. * Once we've decided to emit an open tag, that means we can also emit the
  3911. * text node before it.
  3912. */
  3913. function emitTagAndPreviousTextNode(context) {
  3914. var textBeforeTag = context.html.slice(context.currentDataIdx, context.currentTag.idx);
  3915. if (textBeforeTag) {
  3916. // the html tag was the first element in the html string, or two
  3917. // tags next to each other, in which case we should not emit a text
  3918. // node
  3919. context.callbacks.onText(textBeforeTag, context.currentDataIdx);
  3920. }
  3921. var currentTag = context.currentTag;
  3922. if (currentTag.type === 'comment') {
  3923. context.callbacks.onComment(currentTag.idx);
  3924. }
  3925. else if (currentTag.type === 'doctype') {
  3926. context.callbacks.onDoctype(currentTag.idx);
  3927. }
  3928. else {
  3929. if (currentTag.isOpening) {
  3930. context.callbacks.onOpenTag(currentTag.name, currentTag.idx);
  3931. }
  3932. if (currentTag.isClosing) {
  3933. // note: self-closing tags will emit both opening and closing
  3934. context.callbacks.onCloseTag(currentTag.name, currentTag.idx);
  3935. }
  3936. }
  3937. // Since we just emitted a tag, reset to the data state for the next char
  3938. resetToDataState(context);
  3939. context.currentDataIdx = context.charIdx + 1;
  3940. }
  3941. function emitText(context) {
  3942. var text = context.html.slice(context.currentDataIdx, context.charIdx);
  3943. context.callbacks.onText(text, context.currentDataIdx);
  3944. context.currentDataIdx = context.charIdx + 1;
  3945. }
  3946. /**
  3947. * Captures the tag name from the start of the tag to the current character
  3948. * index, and converts it to lower case
  3949. */
  3950. function captureTagName(context) {
  3951. var startIdx = context.currentTag.idx + (context.currentTag.isClosing ? 2 : 1);
  3952. return context.html.slice(startIdx, context.charIdx).toLowerCase();
  3953. }
  3954. /**
  3955. * Causes the main loop to re-consume the current character, such as after
  3956. * encountering a "parse error" that changed state and needs to reconsume
  3957. * the same character in that new state.
  3958. */
  3959. function reconsumeCurrentChar(context) {
  3960. context.charIdx--;
  3961. }
  3962. /**
  3963. * @class Autolinker
  3964. * @extends Object
  3965. *
  3966. * Utility class used to process a given string of text, and wrap the matches in
  3967. * the appropriate anchor (&lt;a&gt;) tags to turn them into links.
  3968. *
  3969. * Any of the configuration options may be provided in an Object provided
  3970. * to the Autolinker constructor, which will configure how the {@link #link link()}
  3971. * method will process the links.
  3972. *
  3973. * For example:
  3974. *
  3975. * var autolinker = new Autolinker( {
  3976. * newWindow : false,
  3977. * truncate : 30
  3978. * } );
  3979. *
  3980. * var html = autolinker.link( "Joe went to www.yahoo.com" );
  3981. * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
  3982. *
  3983. *
  3984. * The {@link #static-link static link()} method may also be used to inline
  3985. * options into a single call, which may be more convenient for one-off uses.
  3986. * For example:
  3987. *
  3988. * var html = Autolinker.link( "Joe went to www.yahoo.com", {
  3989. * newWindow : false,
  3990. * truncate : 30
  3991. * } );
  3992. * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
  3993. *
  3994. *
  3995. * ## Custom Replacements of Links
  3996. *
  3997. * If the configuration options do not provide enough flexibility, a {@link #replaceFn}
  3998. * may be provided to fully customize the output of Autolinker. This function is
  3999. * called once for each URL/Email/Phone#/Hashtag/Mention (Twitter, Instagram, Soundcloud)
  4000. * match that is encountered.
  4001. *
  4002. * For example:
  4003. *
  4004. * var input = "..."; // string with URLs, Email Addresses, Phone #s, Hashtags, and Mentions (Twitter, Instagram, Soundcloud)
  4005. *
  4006. * var linkedText = Autolinker.link( input, {
  4007. * replaceFn : function( match ) {
  4008. * console.log( "href = ", match.getAnchorHref() );
  4009. * console.log( "text = ", match.getAnchorText() );
  4010. *
  4011. * switch( match.getType() ) {
  4012. * case 'url' :
  4013. * console.log( "url: ", match.getUrl() );
  4014. *
  4015. * if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) {
  4016. * var tag = match.buildTag(); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes
  4017. * tag.setAttr( 'rel', 'nofollow' );
  4018. * tag.addClass( 'external-link' );
  4019. *
  4020. * return tag;
  4021. *
  4022. * } else {
  4023. * return true; // let Autolinker perform its normal anchor tag replacement
  4024. * }
  4025. *
  4026. * case 'email' :
  4027. * var email = match.getEmail();
  4028. * console.log( "email: ", email );
  4029. *
  4030. * if( email === "my@own.address" ) {
  4031. * return false; // don't auto-link this particular email address; leave as-is
  4032. * } else {
  4033. * return; // no return value will have Autolinker perform its normal anchor tag replacement (same as returning `true`)
  4034. * }
  4035. *
  4036. * case 'phone' :
  4037. * var phoneNumber = match.getPhoneNumber();
  4038. * console.log( phoneNumber );
  4039. *
  4040. * return '<a href="http://newplace.to.link.phone.numbers.to/">' + phoneNumber + '</a>';
  4041. *
  4042. * case 'hashtag' :
  4043. * var hashtag = match.getHashtag();
  4044. * console.log( hashtag );
  4045. *
  4046. * return '<a href="http://newplace.to.link.hashtag.handles.to/">' + hashtag + '</a>';
  4047. *
  4048. * case 'mention' :
  4049. * var mention = match.getMention();
  4050. * console.log( mention );
  4051. *
  4052. * return '<a href="http://newplace.to.link.mention.to/">' + mention + '</a>';
  4053. * }
  4054. * }
  4055. * } );
  4056. *
  4057. *
  4058. * The function may return the following values:
  4059. *
  4060. * - `true` (Boolean): Allow Autolinker to replace the match as it normally
  4061. * would.
  4062. * - `false` (Boolean): Do not replace the current match at all - leave as-is.
  4063. * - Any String: If a string is returned from the function, the string will be
  4064. * used directly as the replacement HTML for the match.
  4065. * - An {@link Autolinker.HtmlTag} instance, which can be used to build/modify
  4066. * an HTML tag before writing out its HTML text.
  4067. */
  4068. var Autolinker = /** @class */ (function () {
  4069. /**
  4070. * @method constructor
  4071. * @param {Object} [cfg] The configuration options for the Autolinker instance,
  4072. * specified in an Object (map).
  4073. */
  4074. function Autolinker(cfg) {
  4075. if (cfg === void 0) { cfg = {}; }
  4076. /**
  4077. * The Autolinker version number exposed on the instance itself.
  4078. *
  4079. * Ex: 0.25.1
  4080. *
  4081. * @property {String} version
  4082. */
  4083. this.version = Autolinker.version;
  4084. /**
  4085. * @cfg {Boolean/Object} [urls]
  4086. *
  4087. * `true` if URLs should be automatically linked, `false` if they should not
  4088. * be. Defaults to `true`.
  4089. *
  4090. * Examples:
  4091. *
  4092. * urls: true
  4093. *
  4094. * // or
  4095. *
  4096. * urls: {
  4097. * schemeMatches : true,
  4098. * tldMatches : true,
  4099. * ipV4Matches : true
  4100. * }
  4101. *
  4102. * As shown above, this option also accepts an Object form with 3 properties
  4103. * to allow for more customization of what exactly gets linked. All default
  4104. * to `true`:
  4105. *
  4106. * @cfg {Boolean} [urls.schemeMatches] `true` to match URLs found prefixed
  4107. * with a scheme, i.e. `http://google.com`, or `other+scheme://google.com`,
  4108. * `false` to prevent these types of matches.
  4109. * @cfg {Boolean} [urls.tldMatches] `true` to match URLs with known top
  4110. * level domains (.com, .net, etc.) that are not prefixed with a scheme
  4111. * (such as 'http://'). This option attempts to match anything that looks
  4112. * like a URL in the given text. Ex: `google.com`, `asdf.org/?page=1`, etc.
  4113. * `false` to prevent these types of matches.
  4114. * @cfg {Boolean} [urls.ipV4Matches] `true` to match IPv4 addresses in text
  4115. * that are not prefixed with a scheme (such as 'http://'). This option
  4116. * attempts to match anything that looks like an IPv4 address in text. Ex:
  4117. * `192.168.0.1`, `10.0.0.1/?page=1`, etc. `false` to prevent these types
  4118. * of matches.
  4119. */
  4120. this.urls = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  4121. /**
  4122. * @cfg {Boolean} [email=true]
  4123. *
  4124. * `true` if email addresses should be automatically linked, `false` if they
  4125. * should not be.
  4126. */
  4127. this.email = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  4128. /**
  4129. * @cfg {Boolean} [phone=true]
  4130. *
  4131. * `true` if Phone numbers ("(555)555-5555") should be automatically linked,
  4132. * `false` if they should not be.
  4133. */
  4134. this.phone = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  4135. /**
  4136. * @cfg {Boolean/String} [hashtag=false]
  4137. *
  4138. * A string for the service name to have hashtags (ex: "#myHashtag")
  4139. * auto-linked to. The currently-supported values are:
  4140. *
  4141. * - 'twitter'
  4142. * - 'facebook'
  4143. * - 'instagram'
  4144. * - 'tiktok'
  4145. * - 'youtube'
  4146. *
  4147. * Pass `false` to skip auto-linking of hashtags.
  4148. */
  4149. this.hashtag = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  4150. /**
  4151. * @cfg {String/Boolean} [mention=false]
  4152. *
  4153. * A string for the service name to have mentions (ex: "@myuser")
  4154. * auto-linked to. The currently supported values are:
  4155. *
  4156. * - 'twitter'
  4157. * - 'instagram'
  4158. * - 'soundcloud'
  4159. * - 'tiktok'
  4160. * - 'youtube'
  4161. *
  4162. * Defaults to `false` to skip auto-linking of mentions.
  4163. */
  4164. this.mention = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  4165. /**
  4166. * @cfg {Boolean} [newWindow=true]
  4167. *
  4168. * `true` if the links should open in a new window, `false` otherwise.
  4169. */
  4170. this.newWindow = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  4171. /**
  4172. * @cfg {Boolean/Object} [stripPrefix=true]
  4173. *
  4174. * `true` if 'http://' (or 'https://') and/or the 'www.' should be stripped
  4175. * from the beginning of URL links' text, `false` otherwise. Defaults to
  4176. * `true`.
  4177. *
  4178. * Examples:
  4179. *
  4180. * stripPrefix: true
  4181. *
  4182. * // or
  4183. *
  4184. * stripPrefix: {
  4185. * scheme : true,
  4186. * www : true
  4187. * }
  4188. *
  4189. * As shown above, this option also accepts an Object form with 2 properties
  4190. * to allow for more customization of what exactly is prevented from being
  4191. * displayed. Both default to `true`:
  4192. *
  4193. * @cfg {Boolean} [stripPrefix.scheme] `true` to prevent the scheme part of
  4194. * a URL match from being displayed to the user. Example:
  4195. * `'http://google.com'` will be displayed as `'google.com'`. `false` to
  4196. * not strip the scheme. NOTE: Only an `'http://'` or `'https://'` scheme
  4197. * will be removed, so as not to remove a potentially dangerous scheme
  4198. * (such as `'file://'` or `'javascript:'`)
  4199. * @cfg {Boolean} [stripPrefix.www] www (Boolean): `true` to prevent the
  4200. * `'www.'` part of a URL match from being displayed to the user. Ex:
  4201. * `'www.google.com'` will be displayed as `'google.com'`. `false` to not
  4202. * strip the `'www'`.
  4203. */
  4204. this.stripPrefix = {
  4205. scheme: true,
  4206. www: true,
  4207. }; // default value just to get the above doc comment in the ES5 output and documentation generator
  4208. /**
  4209. * @cfg {Boolean} [stripTrailingSlash=true]
  4210. *
  4211. * `true` to remove the trailing slash from URL matches, `false` to keep
  4212. * the trailing slash.
  4213. *
  4214. * Example when `true`: `http://google.com/` will be displayed as
  4215. * `http://google.com`.
  4216. */
  4217. this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  4218. /**
  4219. * @cfg {Boolean} [decodePercentEncoding=true]
  4220. *
  4221. * `true` to decode percent-encoded characters in URL matches, `false` to keep
  4222. * the percent-encoded characters.
  4223. *
  4224. * Example when `true`: `https://en.wikipedia.org/wiki/San_Jos%C3%A9` will
  4225. * be displayed as `https://en.wikipedia.org/wiki/San_José`.
  4226. */
  4227. this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  4228. /**
  4229. * @cfg {Number/Object} [truncate=0]
  4230. *
  4231. * ## Number Form
  4232. *
  4233. * A number for how many characters matched text should be truncated to
  4234. * inside the text of a link. If the matched text is over this number of
  4235. * characters, it will be truncated to this length by adding a two period
  4236. * ellipsis ('..') to the end of the string.
  4237. *
  4238. * For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file'
  4239. * truncated to 25 characters might look something like this:
  4240. * 'yahoo.com/some/long/pat..'
  4241. *
  4242. * Example Usage:
  4243. *
  4244. * truncate: 25
  4245. *
  4246. *
  4247. * Defaults to `0` for "no truncation."
  4248. *
  4249. *
  4250. * ## Object Form
  4251. *
  4252. * An Object may also be provided with two properties: `length` (Number) and
  4253. * `location` (String). `location` may be one of the following: 'end'
  4254. * (default), 'middle', or 'smart'.
  4255. *
  4256. * Example Usage:
  4257. *
  4258. * truncate: { length: 25, location: 'middle' }
  4259. *
  4260. * @cfg {Number} [truncate.length=0] How many characters to allow before
  4261. * truncation will occur. Defaults to `0` for "no truncation."
  4262. * @cfg {"end"/"middle"/"smart"} [truncate.location="end"]
  4263. *
  4264. * - 'end' (default): will truncate up to the number of characters, and then
  4265. * add an ellipsis at the end. Ex: 'yahoo.com/some/long/pat..'
  4266. * - 'middle': will truncate and add the ellipsis in the middle. Ex:
  4267. * 'yahoo.com/s..th/to/a/file'
  4268. * - 'smart': for URLs where the algorithm attempts to strip out unnecessary
  4269. * parts first (such as the 'www.', then URL scheme, hash, etc.),
  4270. * attempting to make the URL human-readable before looking for a good
  4271. * point to insert the ellipsis if it is still too long. Ex:
  4272. * 'yahoo.com/some..to/a/file'. For more details, see
  4273. * {@link Autolinker.truncate.TruncateSmart}.
  4274. */
  4275. this.truncate = {
  4276. length: 0,
  4277. location: 'end',
  4278. }; // default value just to get the above doc comment in the ES5 output and documentation generator
  4279. /**
  4280. * @cfg {String} className
  4281. *
  4282. * A CSS class name to add to the generated links. This class will be added
  4283. * to all links, as well as this class plus match suffixes for styling
  4284. * url/email/phone/hashtag/mention links differently.
  4285. *
  4286. * For example, if this config is provided as "myLink", then:
  4287. *
  4288. * - URL links will have the CSS classes: "myLink myLink-url"
  4289. * - Email links will have the CSS classes: "myLink myLink-email", and
  4290. * - Phone links will have the CSS classes: "myLink myLink-phone"
  4291. * - Hashtag links will have the CSS classes: "myLink myLink-hashtag"
  4292. * - Mention links will have the CSS classes: "myLink myLink-mention myLink-[type]"
  4293. * where [type] is either "instagram", "twitter" or "soundcloud"
  4294. */
  4295. this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  4296. /**
  4297. * @cfg {Function} replaceFn
  4298. *
  4299. * A function to individually process each match found in the input string.
  4300. *
  4301. * See the class's description for usage.
  4302. *
  4303. * The `replaceFn` can be called with a different context object (`this`
  4304. * reference) using the {@link #context} cfg.
  4305. *
  4306. * This function is called with the following parameter:
  4307. *
  4308. * @cfg {Autolinker.match.Match} replaceFn.match The Match instance which
  4309. * can be used to retrieve information about the match that the `replaceFn`
  4310. * is currently processing. See {@link Autolinker.match.Match} subclasses
  4311. * for details.
  4312. */
  4313. this.replaceFn = null; // default value just to get the above doc comment in the ES5 output and documentation generator
  4314. /**
  4315. * @cfg {Object} context
  4316. *
  4317. * The context object (`this` reference) to call the `replaceFn` with.
  4318. *
  4319. * Defaults to this Autolinker instance.
  4320. */
  4321. this.context = undefined; // default value just to get the above doc comment in the ES5 output and documentation generator
  4322. /**
  4323. * @cfg {Boolean} [sanitizeHtml=false]
  4324. *
  4325. * `true` to HTML-encode the start and end brackets of existing HTML tags found
  4326. * in the input string. This will escape `<` and `>` characters to `&lt;` and
  4327. * `&gt;`, respectively.
  4328. *
  4329. * Setting this to `true` will prevent XSS (Cross-site Scripting) attacks,
  4330. * but will remove the significance of existing HTML tags in the input string. If
  4331. * you would like to maintain the significance of existing HTML tags while also
  4332. * making the output HTML string safe, leave this option as `false` and use a
  4333. * tool like https://github.com/cure53/DOMPurify (or others) on the input string
  4334. * before running Autolinker.
  4335. */
  4336. this.sanitizeHtml = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  4337. /**
  4338. * @private
  4339. * @property {Autolinker.AnchorTagBuilder} tagBuilder
  4340. *
  4341. * The AnchorTagBuilder instance used to build match replacement anchor tags.
  4342. * Note: this is lazily instantiated in the {@link #getTagBuilder} method.
  4343. */
  4344. this.tagBuilder = null;
  4345. // Note: when `this.something` is used in the rhs of these assignments,
  4346. // it refers to the default values set above the constructor
  4347. this.urls = normalizeUrlsCfg(cfg.urls);
  4348. this.email = isBoolean(cfg.email) ? cfg.email : this.email;
  4349. this.phone = isBoolean(cfg.phone) ? cfg.phone : this.phone;
  4350. this.hashtag = cfg.hashtag || this.hashtag;
  4351. this.mention = cfg.mention || this.mention;
  4352. this.newWindow = isBoolean(cfg.newWindow) ? cfg.newWindow : this.newWindow;
  4353. this.stripPrefix = normalizeStripPrefixCfg(cfg.stripPrefix);
  4354. this.stripTrailingSlash = isBoolean(cfg.stripTrailingSlash)
  4355. ? cfg.stripTrailingSlash
  4356. : this.stripTrailingSlash;
  4357. this.decodePercentEncoding = isBoolean(cfg.decodePercentEncoding)
  4358. ? cfg.decodePercentEncoding
  4359. : this.decodePercentEncoding;
  4360. this.sanitizeHtml = cfg.sanitizeHtml || false;
  4361. // Validate the value of the `mention` cfg
  4362. var mention = this.mention;
  4363. if (mention !== false && mentionServices.indexOf(mention) === -1) {
  4364. throw new Error("invalid `mention` cfg '".concat(mention, "' - see docs"));
  4365. }
  4366. // Validate the value of the `hashtag` cfg
  4367. var hashtag = this.hashtag;
  4368. if (hashtag !== false && hashtagServices.indexOf(hashtag) === -1) {
  4369. throw new Error("invalid `hashtag` cfg '".concat(hashtag, "' - see docs"));
  4370. }
  4371. this.truncate = normalizeTruncateCfg(cfg.truncate);
  4372. this.className = cfg.className || this.className;
  4373. this.replaceFn = cfg.replaceFn || this.replaceFn;
  4374. this.context = cfg.context || this;
  4375. }
  4376. /**
  4377. * Automatically links URLs, Email addresses, Phone Numbers, Twitter handles,
  4378. * Hashtags, and Mentions found in the given chunk of HTML. Does not link URLs
  4379. * found within HTML tags.
  4380. *
  4381. * For instance, if given the text: `You should go to http://www.yahoo.com`,
  4382. * then the result will be `You should go to &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
  4383. *
  4384. * Example:
  4385. *
  4386. * var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } );
  4387. * // Produces: "Go to <a href="http://google.com">google.com</a>"
  4388. *
  4389. * @static
  4390. * @param {String} textOrHtml The HTML or text to find matches within (depending
  4391. * on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #mention},
  4392. * {@link #hashtag}, and {@link #mention} options are enabled).
  4393. * @param {Object} [options] Any of the configuration options for the Autolinker
  4394. * class, specified in an Object (map). See the class description for an
  4395. * example call.
  4396. * @return {String} The HTML text, with matches automatically linked.
  4397. */
  4398. Autolinker.link = function (textOrHtml, options) {
  4399. var autolinker = new Autolinker(options);
  4400. return autolinker.link(textOrHtml);
  4401. };
  4402. /**
  4403. * Parses the input `textOrHtml` looking for URLs, email addresses, phone
  4404. * numbers, username handles, and hashtags (depending on the configuration
  4405. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  4406. * objects describing those matches (without making any replacements).
  4407. *
  4408. * Note that if parsing multiple pieces of text, it is slightly more efficient
  4409. * to create an Autolinker instance, and use the instance-level {@link #parse}
  4410. * method.
  4411. *
  4412. * Example:
  4413. *
  4414. * var matches = Autolinker.parse("Hello google.com, I am asdf@asdf.com", {
  4415. * urls: true,
  4416. * email: true
  4417. * });
  4418. *
  4419. * console.log(matches.length); // 2
  4420. * console.log(matches[0].getType()); // 'url'
  4421. * console.log(matches[0].getUrl()); // 'google.com'
  4422. * console.log(matches[1].getType()); // 'email'
  4423. * console.log(matches[1].getEmail()); // 'asdf@asdf.com'
  4424. *
  4425. * @static
  4426. * @param {String} textOrHtml The HTML or text to find matches within
  4427. * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
  4428. * {@link #hashtag}, and {@link #mention} options are enabled).
  4429. * @param {Object} [options] Any of the configuration options for the Autolinker
  4430. * class, specified in an Object (map). See the class description for an
  4431. * example call.
  4432. * @return {Autolinker.match.Match[]} The array of Matches found in the
  4433. * given input `textOrHtml`.
  4434. */
  4435. Autolinker.parse = function (textOrHtml, options) {
  4436. var autolinker = new Autolinker(options);
  4437. return autolinker.parse(textOrHtml);
  4438. };
  4439. /**
  4440. * Parses the input `textOrHtml` looking for URLs, email addresses, phone
  4441. * numbers, username handles, and hashtags (depending on the configuration
  4442. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  4443. * objects describing those matches (without making any replacements).
  4444. *
  4445. * This method is used by the {@link #link} method, but can also be used to
  4446. * simply do parsing of the input in order to discover what kinds of links
  4447. * there are and how many.
  4448. *
  4449. * Example usage:
  4450. *
  4451. * var autolinker = new Autolinker( {
  4452. * urls: true,
  4453. * email: true
  4454. * } );
  4455. *
  4456. * var matches = autolinker.parse( "Hello google.com, I am asdf@asdf.com" );
  4457. *
  4458. * console.log( matches.length ); // 2
  4459. * console.log( matches[ 0 ].getType() ); // 'url'
  4460. * console.log( matches[ 0 ].getUrl() ); // 'google.com'
  4461. * console.log( matches[ 1 ].getType() ); // 'email'
  4462. * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
  4463. *
  4464. * @param {String} textOrHtml The HTML or text to find matches within
  4465. * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
  4466. * {@link #hashtag}, and {@link #mention} options are enabled).
  4467. * @return {Autolinker.match.Match[]} The array of Matches found in the
  4468. * given input `textOrHtml`.
  4469. */
  4470. Autolinker.prototype.parse = function (textOrHtml) {
  4471. var _this = this;
  4472. var skipTagNames = ['a', 'style', 'script'];
  4473. var skipTagsStackCount = 0; // used to only Autolink text outside of anchor/script/style tags. We don't want to autolink something that is already linked inside of an <a> tag, for instance
  4474. var matches = [];
  4475. // Find all matches within the `textOrHtml` (but not matches that are
  4476. // already nested within <a>, <style> and <script> tags)
  4477. parseHtml(textOrHtml, {
  4478. onOpenTag: function (tagName) {
  4479. if (skipTagNames.indexOf(tagName) >= 0) {
  4480. skipTagsStackCount++;
  4481. }
  4482. },
  4483. onText: function (text, offset) {
  4484. // Only process text nodes that are not within an <a>, <style> or <script> tag
  4485. if (skipTagsStackCount === 0) {
  4486. // "Walk around" common HTML entities. An '&nbsp;' (for example)
  4487. // could be at the end of a URL, but we don't want to
  4488. // include the trailing '&' in the URL. See issue #76
  4489. // TODO: Handle HTML entities separately in parseHtml() and
  4490. // don't emit them as "text" except for &amp; entities
  4491. var htmlCharacterEntitiesRegex = /(&nbsp;|&#160;|&lt;|&#60;|&gt;|&#62;|&quot;|&#34;|&#39;)/gi; // NOTE: capturing group is significant to include the split characters in the .split() call below
  4492. var textSplit = text.split(htmlCharacterEntitiesRegex);
  4493. var currentOffset_1 = offset;
  4494. textSplit.forEach(function (splitText, i) {
  4495. // even number matches are text, odd numbers are html entities
  4496. if (i % 2 === 0) {
  4497. var textNodeMatches = _this.parseText(splitText, currentOffset_1);
  4498. matches.push.apply(matches, __spreadArray([], __read(textNodeMatches), false));
  4499. }
  4500. currentOffset_1 += splitText.length;
  4501. });
  4502. }
  4503. },
  4504. onCloseTag: function (tagName) {
  4505. if (skipTagNames.indexOf(tagName) >= 0) {
  4506. skipTagsStackCount = Math.max(skipTagsStackCount - 1, 0); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0
  4507. }
  4508. },
  4509. onComment: function ( /*_offset: number*/) { }, // no need to process comment nodes
  4510. onDoctype: function ( /*_offset: number*/) { }, // no need to process doctype nodes
  4511. });
  4512. // After we have found all matches, remove subsequent matches that
  4513. // overlap with a previous match. This can happen for instance with an
  4514. // email address where the local-part of the email is also a top-level
  4515. // domain, such as in "google.com@aaa.com". In this case, the entire
  4516. // email address should be linked rather than just the 'google.com'
  4517. // part.
  4518. matches = this.compactMatches(matches);
  4519. // And finally, remove matches for match types that have been turned
  4520. // off. We needed to have all match types turned on initially so that
  4521. // things like hashtags could be filtered out if they were really just
  4522. // part of a URL match (for instance, as a named anchor).
  4523. matches = this.removeUnwantedMatches(matches);
  4524. return matches;
  4525. };
  4526. /**
  4527. * After we have found all matches, we need to remove matches that overlap
  4528. * with a previous match. This can happen for instance with an
  4529. * email address where the local-part of the email is also a top-level
  4530. * domain, such as in "google.com@aaa.com". In this case, the entire email
  4531. * address should be linked rather than just the 'google.com' part.
  4532. *
  4533. * @private
  4534. * @param {Autolinker.match.Match[]} matches
  4535. * @return {Autolinker.match.Match[]}
  4536. */
  4537. Autolinker.prototype.compactMatches = function (matches) {
  4538. // First, the matches need to be sorted in order of offset in the input
  4539. // string
  4540. matches.sort(byMatchOffset);
  4541. var i = 0;
  4542. while (i < matches.length - 1) {
  4543. var match = matches[i];
  4544. var offset = match.getOffset();
  4545. var matchedTextLength = match.getMatchedText().length;
  4546. if (i + 1 < matches.length) {
  4547. // Remove subsequent matches that equal offset with current match
  4548. // This can happen when matching the text "google.com@aaa.com"
  4549. // where we have both a URL ('google.com') and an email. We
  4550. // should only keep the email match in this case.
  4551. if (matches[i + 1].getOffset() === offset) {
  4552. // Remove the shorter match
  4553. var removeIdx = matches[i + 1].getMatchedText().length > matchedTextLength ? i : i + 1;
  4554. matches.splice(removeIdx, 1);
  4555. continue;
  4556. }
  4557. // Remove subsequent matches that overlap with the current match
  4558. //
  4559. // NOTE: This was a fundamental snippet of the Autolinker.js v3
  4560. // algorithm where we had multiple regular expressions searching
  4561. // the input string for matches. The regexes would sometimes
  4562. // overlap such as in the case of "google.com/#link", where we
  4563. // would have both a URL match and a hashtag match.
  4564. //
  4565. // However, the Autolinker.js v4 algorithm uses a state machine
  4566. // parser and knows that the '#link' part of 'google.com/#link'
  4567. // is part of the URL that precedes it, so we don't need this
  4568. // piece of code any more. Keeping it here commented for now in
  4569. // case we need to put it back at some point, but none of the
  4570. // test cases are currently able to trigger the need for it.
  4571. // const endIdx = offset + matchedTextLength;
  4572. // if (matches[i + 1].getOffset() < endIdx) {
  4573. // matches.splice(i + 1, 1);
  4574. // continue;
  4575. // }
  4576. }
  4577. i++;
  4578. }
  4579. return matches;
  4580. };
  4581. /**
  4582. * Removes matches for matchers that were turned off in the options. For
  4583. * example, if {@link #hashtag hashtags} were not to be matched, we'll
  4584. * remove them from the `matches` array here.
  4585. *
  4586. * Note: we *must* use all Matchers on the input string, and then filter
  4587. * them out later. For example, if the options were `{ url: false, hashtag: true }`,
  4588. * we wouldn't want to match the text '#link' as a HashTag inside of the text
  4589. * 'google.com/#link'. The way the algorithm works is that we match the full
  4590. * URL first (which prevents the accidental HashTag match), and then we'll
  4591. * simply throw away the URL match.
  4592. *
  4593. * @private
  4594. * @param {Autolinker.match.Match[]} matches The array of matches to remove
  4595. * the unwanted matches from. Note: this array is mutated for the
  4596. * removals.
  4597. * @return {Autolinker.match.Match[]} The mutated input `matches` array.
  4598. */
  4599. Autolinker.prototype.removeUnwantedMatches = function (matches) {
  4600. if (!this.hashtag)
  4601. removeWithPredicate(matches, function (match) {
  4602. return match.getType() === 'hashtag';
  4603. });
  4604. if (!this.email)
  4605. removeWithPredicate(matches, function (match) {
  4606. return match.getType() === 'email';
  4607. });
  4608. if (!this.phone)
  4609. removeWithPredicate(matches, function (match) {
  4610. return match.getType() === 'phone';
  4611. });
  4612. if (!this.mention)
  4613. removeWithPredicate(matches, function (match) {
  4614. return match.getType() === 'mention';
  4615. });
  4616. if (!this.urls.schemeMatches) {
  4617. removeWithPredicate(matches, function (m) {
  4618. return m.getType() === 'url' && m.getUrlMatchType() === 'scheme';
  4619. });
  4620. }
  4621. if (!this.urls.tldMatches) {
  4622. removeWithPredicate(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'tld'; });
  4623. }
  4624. if (!this.urls.ipV4Matches) {
  4625. removeWithPredicate(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'ipV4'; });
  4626. }
  4627. return matches;
  4628. };
  4629. /**
  4630. * Parses the input `text` looking for URLs, email addresses, phone
  4631. * numbers, username handles, and hashtags (depending on the configuration
  4632. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  4633. * objects describing those matches.
  4634. *
  4635. * This method processes a **non-HTML string**, and is used to parse and
  4636. * match within the text nodes of an HTML string. This method is used
  4637. * internally by {@link #parse}.
  4638. *
  4639. * @private
  4640. * @param {String} text The text to find matches within (depending on if the
  4641. * {@link #urls}, {@link #email}, {@link #phone},
  4642. * {@link #hashtag}, and {@link #mention} options are enabled). This must be a non-HTML string.
  4643. * @param {Number} [offset=0] The offset of the text node within the
  4644. * original string. This is used when parsing with the {@link #parse}
  4645. * method to generate correct offsets within the {@link Autolinker.match.Match}
  4646. * instances, but may be omitted if calling this method publicly.
  4647. * @return {Autolinker.match.Match[]} The array of Matches found in the
  4648. * given input `text`.
  4649. */
  4650. Autolinker.prototype.parseText = function (text, offset) {
  4651. offset = offset || 0;
  4652. var matches = parseMatches(text, {
  4653. tagBuilder: this.getTagBuilder(),
  4654. stripPrefix: this.stripPrefix,
  4655. stripTrailingSlash: this.stripTrailingSlash,
  4656. decodePercentEncoding: this.decodePercentEncoding,
  4657. hashtagServiceName: this.hashtag,
  4658. mentionServiceName: this.mention || 'twitter',
  4659. });
  4660. // Correct the offset of each of the matches. They are originally
  4661. // the offset of the match within the provided text node, but we
  4662. // need to correct them to be relative to the original HTML input
  4663. // string (i.e. the one provided to #parse).
  4664. for (var i = 0, numTextMatches = matches.length; i < numTextMatches; i++) {
  4665. matches[i].setOffset(offset + matches[i].getOffset());
  4666. }
  4667. return matches;
  4668. };
  4669. /**
  4670. * Automatically links URLs, Email addresses, Phone numbers, Hashtags,
  4671. * and Mentions (Twitter, Instagram, Soundcloud) found in the given chunk of HTML. Does not link
  4672. * URLs found within HTML tags.
  4673. *
  4674. * For instance, if given the text: `You should go to http://www.yahoo.com`,
  4675. * then the result will be `You should go to
  4676. * &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
  4677. *
  4678. * This method finds the text around any HTML elements in the input
  4679. * `textOrHtml`, which will be the text that is processed. Any original HTML
  4680. * elements will be left as-is, as well as the text that is already wrapped
  4681. * in anchor (&lt;a&gt;) tags.
  4682. *
  4683. * @param {String} textOrHtml The HTML or text to autolink matches within
  4684. * (depending on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #hashtag}, and {@link #mention} options are enabled).
  4685. * @return {String} The HTML, with matches automatically linked.
  4686. */
  4687. Autolinker.prototype.link = function (textOrHtml) {
  4688. if (!textOrHtml) {
  4689. return '';
  4690. } // handle `null` and `undefined` (for JavaScript users that don't have TypeScript support), and nothing to do with an empty string too
  4691. /* We would want to sanitize the start and end characters of a tag
  4692. * before processing the string in order to avoid an XSS scenario.
  4693. * This behaviour can be changed by toggling the sanitizeHtml option.
  4694. */
  4695. if (this.sanitizeHtml) {
  4696. textOrHtml = textOrHtml.replace(/</g, '&lt;').replace(/>/g, '&gt;');
  4697. }
  4698. var matches = this.parse(textOrHtml);
  4699. var newHtml = new Array(matches.length * 2 + 1);
  4700. var lastIndex = 0;
  4701. for (var i = 0, len = matches.length; i < len; i++) {
  4702. var match = matches[i];
  4703. newHtml.push(textOrHtml.substring(lastIndex, match.getOffset()));
  4704. newHtml.push(this.createMatchReturnVal(match));
  4705. lastIndex = match.getOffset() + match.getMatchedText().length;
  4706. }
  4707. newHtml.push(textOrHtml.substring(lastIndex)); // handle the text after the last match
  4708. return newHtml.join('');
  4709. };
  4710. /**
  4711. * Creates the return string value for a given match in the input string.
  4712. *
  4713. * This method handles the {@link #replaceFn}, if one was provided.
  4714. *
  4715. * @private
  4716. * @param {Autolinker.match.Match} match The Match object that represents
  4717. * the match.
  4718. * @return {String} The string that the `match` should be replaced with.
  4719. * This is usually the anchor tag string, but may be the `matchStr` itself
  4720. * if the match is not to be replaced.
  4721. */
  4722. Autolinker.prototype.createMatchReturnVal = function (match) {
  4723. // Handle a custom `replaceFn` being provided
  4724. var replaceFnResult;
  4725. if (this.replaceFn) {
  4726. replaceFnResult = this.replaceFn.call(this.context, match); // Autolinker instance is the context
  4727. }
  4728. if (typeof replaceFnResult === 'string') {
  4729. return replaceFnResult; // `replaceFn` returned a string, use that
  4730. }
  4731. else if (replaceFnResult === false) {
  4732. return match.getMatchedText(); // no replacement for the match
  4733. }
  4734. else if (replaceFnResult instanceof HtmlTag) {
  4735. return replaceFnResult.toAnchorString();
  4736. }
  4737. else {
  4738. // replaceFnResult === true, or no/unknown return value from function
  4739. // Perform Autolinker's default anchor tag generation
  4740. var anchorTag = match.buildTag(); // returns an Autolinker.HtmlTag instance
  4741. return anchorTag.toAnchorString();
  4742. }
  4743. };
  4744. /**
  4745. * Returns the {@link #tagBuilder} instance for this Autolinker instance,
  4746. * lazily instantiating it if it does not yet exist.
  4747. *
  4748. * @private
  4749. * @return {Autolinker.AnchorTagBuilder}
  4750. */
  4751. Autolinker.prototype.getTagBuilder = function () {
  4752. var tagBuilder = this.tagBuilder;
  4753. if (!tagBuilder) {
  4754. tagBuilder = this.tagBuilder = new AnchorTagBuilder({
  4755. newWindow: this.newWindow,
  4756. truncate: this.truncate,
  4757. className: this.className,
  4758. });
  4759. }
  4760. return tagBuilder;
  4761. };
  4762. // NOTE: must be 'export default' here for UMD module
  4763. /**
  4764. * @static
  4765. * @property {String} version
  4766. *
  4767. * The Autolinker version number in the form major.minor.patch
  4768. *
  4769. * Ex: 3.15.0
  4770. */
  4771. Autolinker.version = version;
  4772. return Autolinker;
  4773. }());
  4774. /**
  4775. * Normalizes the {@link #urls} config into an Object with its 2 properties:
  4776. * `schemeMatches` and `tldMatches`, both booleans.
  4777. *
  4778. * See {@link #urls} config for details.
  4779. *
  4780. * @private
  4781. * @param {Boolean/Object} urls
  4782. * @return {Object}
  4783. */
  4784. function normalizeUrlsCfg(urls) {
  4785. if (urls == null)
  4786. urls = true; // default to `true`
  4787. if (isBoolean(urls)) {
  4788. return { schemeMatches: urls, tldMatches: urls, ipV4Matches: urls };
  4789. }
  4790. else {
  4791. // object form
  4792. return {
  4793. schemeMatches: isBoolean(urls.schemeMatches) ? urls.schemeMatches : true,
  4794. tldMatches: isBoolean(urls.tldMatches) ? urls.tldMatches : true,
  4795. ipV4Matches: isBoolean(urls.ipV4Matches) ? urls.ipV4Matches : true,
  4796. };
  4797. }
  4798. }
  4799. /**
  4800. * Normalizes the {@link #stripPrefix} config into an Object with 2
  4801. * properties: `scheme`, and `www` - both Booleans.
  4802. *
  4803. * See {@link #stripPrefix} config for details.
  4804. *
  4805. * @private
  4806. * @param {Boolean/Object} stripPrefix
  4807. * @return {Object}
  4808. */
  4809. function normalizeStripPrefixCfg(stripPrefix) {
  4810. if (stripPrefix == null)
  4811. stripPrefix = true; // default to `true`
  4812. if (isBoolean(stripPrefix)) {
  4813. return { scheme: stripPrefix, www: stripPrefix };
  4814. }
  4815. else {
  4816. // object form
  4817. return {
  4818. scheme: isBoolean(stripPrefix.scheme) ? stripPrefix.scheme : true,
  4819. www: isBoolean(stripPrefix.www) ? stripPrefix.www : true,
  4820. };
  4821. }
  4822. }
  4823. /**
  4824. * Normalizes the {@link #truncate} config into an Object with 2 properties:
  4825. * `length` (Number), and `location` (String).
  4826. *
  4827. * See {@link #truncate} config for details.
  4828. *
  4829. * @private
  4830. * @param {Number/Object} truncate
  4831. * @return {Object}
  4832. */
  4833. function normalizeTruncateCfg(truncate) {
  4834. if (typeof truncate === 'number') {
  4835. return { length: truncate, location: 'end' };
  4836. }
  4837. else {
  4838. // object, or undefined/null
  4839. return __assign({ length: Number.POSITIVE_INFINITY, location: 'end' }, truncate);
  4840. }
  4841. }
  4842. /**
  4843. * Helper function for Array.prototype.sort() to sort the Matches by
  4844. * their offset in the input string.
  4845. */
  4846. function byMatchOffset(a, b) {
  4847. return a.getOffset() - b.getOffset();
  4848. }
  4849. return Autolinker;
  4850. }));
  4851. //# sourceMappingURL=autolinker.js.map