Autolinker.js 195 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297
  1. /* This file is automatically rebuilt by the Cesium build process. */
  2. /**
  3. * Assigns (shallow copies) the properties of `src` onto `dest`, if the
  4. * corresponding property on `dest` === `undefined`.
  5. *
  6. * @param {Object} dest The destination object.
  7. * @param {Object} src The source object.
  8. * @return {Object} The destination object (`dest`)
  9. */
  10. function defaults(dest, src) {
  11. for (var prop in src) {
  12. if (src.hasOwnProperty(prop) && dest[prop] === undefined) {
  13. dest[prop] = src[prop];
  14. }
  15. }
  16. return dest;
  17. }
  18. /**
  19. * Truncates the `str` at `len - ellipsisChars.length`, and adds the `ellipsisChars` to the
  20. * end of the string (by default, two periods: '..'). If the `str` length does not exceed
  21. * `len`, the string will be returned unchanged.
  22. *
  23. * @param {String} str The string to truncate and add an ellipsis to.
  24. * @param {Number} truncateLen The length to truncate the string at.
  25. * @param {String} [ellipsisChars=...] The ellipsis character(s) to add to the end of `str`
  26. * when truncated. Defaults to '...'
  27. */
  28. function ellipsis(str, truncateLen, ellipsisChars) {
  29. var ellipsisLength;
  30. if (str.length > truncateLen) {
  31. if (ellipsisChars == null) {
  32. ellipsisChars = '…';
  33. ellipsisLength = 3;
  34. }
  35. else {
  36. ellipsisLength = ellipsisChars.length;
  37. }
  38. str = str.substring(0, truncateLen - ellipsisLength) + ellipsisChars;
  39. }
  40. return str;
  41. }
  42. /**
  43. * Supports `Array.prototype.indexOf()` functionality for old IE (IE8 and below).
  44. *
  45. * @param {Array} arr The array to find an element of.
  46. * @param {*} element The element to find in the array, and return the index of.
  47. * @return {Number} The index of the `element`, or -1 if it was not found.
  48. */
  49. function indexOf(arr, element) {
  50. // @ts-ignore - As far as TypeScript is concerned, this method will always
  51. // exist (lowest "lib" in TS is "ES5"). Hence we need to ignore this error
  52. // to support IE8 which only implements ES3 and doesn't have this method
  53. if (Array.prototype.indexOf) {
  54. return arr.indexOf(element);
  55. }
  56. else {
  57. for (var i = 0, len = arr.length; i < len; i++) {
  58. if (arr[i] === element)
  59. return i;
  60. }
  61. return -1;
  62. }
  63. }
  64. /**
  65. * Removes array elements based on a filtering function. Mutates the input
  66. * array.
  67. *
  68. * Using this instead of the ES5 Array.prototype.filter() function, to allow
  69. * Autolinker compatibility with IE8, and also to prevent creating many new
  70. * arrays in memory for filtering.
  71. *
  72. * @param {Array} arr The array to remove elements from. This array is
  73. * mutated.
  74. * @param {Function} fn A function which should return `true` to
  75. * remove an element.
  76. * @return {Array} The mutated input `arr`.
  77. */
  78. function remove(arr, fn) {
  79. for (var i = arr.length - 1; i >= 0; i--) {
  80. if (fn(arr[i]) === true) {
  81. arr.splice(i, 1);
  82. }
  83. }
  84. }
  85. /**
  86. * Performs the functionality of what modern browsers do when `String.prototype.split()` is called
  87. * with a regular expression that contains capturing parenthesis.
  88. *
  89. * For example:
  90. *
  91. * // Modern browsers:
  92. * "a,b,c".split( /(,)/ ); // --> [ 'a', ',', 'b', ',', 'c' ]
  93. *
  94. * // Old IE (including IE8):
  95. * "a,b,c".split( /(,)/ ); // --> [ 'a', 'b', 'c' ]
  96. *
  97. * This method emulates the functionality of modern browsers for the old IE case.
  98. *
  99. * @param {String} str The string to split.
  100. * @param {RegExp} splitRegex The regular expression to split the input `str` on. The splitting
  101. * character(s) will be spliced into the array, as in the "modern browsers" example in the
  102. * description of this method.
  103. * Note #1: the supplied regular expression **must** have the 'g' flag specified.
  104. * Note #2: for simplicity's sake, the regular expression does not need
  105. * to contain capturing parenthesis - it will be assumed that any match has them.
  106. * @return {String[]} The split array of strings, with the splitting character(s) included.
  107. */
  108. function splitAndCapture(str, splitRegex) {
  109. if (!splitRegex.global)
  110. throw new Error("`splitRegex` must have the 'g' flag set");
  111. var result = [], lastIdx = 0, match;
  112. while (match = splitRegex.exec(str)) {
  113. result.push(str.substring(lastIdx, match.index));
  114. result.push(match[0]); // push the splitting char(s)
  115. lastIdx = match.index + match[0].length;
  116. }
  117. result.push(str.substring(lastIdx));
  118. return result;
  119. }
  120. /**
  121. * Function that should never be called but is used to check that every
  122. * enum value is handled using TypeScript's 'never' type.
  123. */
  124. function throwUnhandledCaseError(theValue) {
  125. throw new Error("Unhandled case for value: '".concat(theValue, "'"));
  126. }
  127. /**
  128. * @class Autolinker.HtmlTag
  129. * @extends Object
  130. *
  131. * Represents an HTML tag, which can be used to easily build/modify HTML tags programmatically.
  132. *
  133. * Autolinker uses this abstraction to create HTML tags, and then write them out as strings. You may also use
  134. * this class in your code, especially within a {@link Autolinker#replaceFn replaceFn}.
  135. *
  136. * ## Examples
  137. *
  138. * Example instantiation:
  139. *
  140. * var tag = new Autolinker.HtmlTag( {
  141. * tagName : 'a',
  142. * attrs : { 'href': 'http://google.com', 'class': 'external-link' },
  143. * innerHtml : 'Google'
  144. * } );
  145. *
  146. * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
  147. *
  148. * // Individual accessor methods
  149. * tag.getTagName(); // 'a'
  150. * tag.getAttr( 'href' ); // 'http://google.com'
  151. * tag.hasClass( 'external-link' ); // true
  152. *
  153. *
  154. * Using mutator methods (which may be used in combination with instantiation config properties):
  155. *
  156. * var tag = new Autolinker.HtmlTag();
  157. * tag.setTagName( 'a' );
  158. * tag.setAttr( 'href', 'http://google.com' );
  159. * tag.addClass( 'external-link' );
  160. * tag.setInnerHtml( 'Google' );
  161. *
  162. * tag.getTagName(); // 'a'
  163. * tag.getAttr( 'href' ); // 'http://google.com'
  164. * tag.hasClass( 'external-link' ); // true
  165. *
  166. * tag.toAnchorString(); // <a href="http://google.com" class="external-link">Google</a>
  167. *
  168. *
  169. * ## Example use within a {@link Autolinker#replaceFn replaceFn}
  170. *
  171. * var html = Autolinker.link( "Test google.com", {
  172. * replaceFn : function( match ) {
  173. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance, configured with the Match's href and anchor text
  174. * tag.setAttr( 'rel', 'nofollow' );
  175. *
  176. * return tag;
  177. * }
  178. * } );
  179. *
  180. * // generated html:
  181. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  182. *
  183. *
  184. * ## Example use with a new tag for the replacement
  185. *
  186. * var html = Autolinker.link( "Test google.com", {
  187. * replaceFn : function( match ) {
  188. * var tag = new Autolinker.HtmlTag( {
  189. * tagName : 'button',
  190. * attrs : { 'title': 'Load URL: ' + match.getAnchorHref() },
  191. * innerHtml : 'Load URL: ' + match.getAnchorText()
  192. * } );
  193. *
  194. * return tag;
  195. * }
  196. * } );
  197. *
  198. * // generated html:
  199. * // Test <button title="Load URL: http://google.com">Load URL: google.com</button>
  200. */
  201. var HtmlTag = /** @class */ (function () {
  202. /**
  203. * @method constructor
  204. * @param {Object} [cfg] The configuration properties for this class, in an Object (map)
  205. */
  206. function HtmlTag(cfg) {
  207. if (cfg === void 0) { cfg = {}; }
  208. /**
  209. * @cfg {String} tagName
  210. *
  211. * The tag name. Ex: 'a', 'button', etc.
  212. *
  213. * Not required at instantiation time, but should be set using {@link #setTagName} before {@link #toAnchorString}
  214. * is executed.
  215. */
  216. this.tagName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  217. /**
  218. * @cfg {Object.<String, String>} attrs
  219. *
  220. * An key/value Object (map) of attributes to create the tag with. The keys are the attribute names, and the
  221. * values are the attribute values.
  222. */
  223. this.attrs = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  224. /**
  225. * @cfg {String} innerHTML
  226. *
  227. * The inner HTML for the tag.
  228. */
  229. this.innerHTML = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  230. /**
  231. * @protected
  232. * @property {RegExp} whitespaceRegex
  233. *
  234. * Regular expression used to match whitespace in a string of CSS classes.
  235. */
  236. this.whitespaceRegex = /\s+/; // default value just to get the above doc comment in the ES5 output and documentation generator
  237. this.tagName = cfg.tagName || '';
  238. this.attrs = cfg.attrs || {};
  239. this.innerHTML = cfg.innerHtml || cfg.innerHTML || ''; // accept either the camelCased form or the fully capitalized acronym as in the DOM
  240. }
  241. /**
  242. * Sets the tag name that will be used to generate the tag with.
  243. *
  244. * @param {String} tagName
  245. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  246. */
  247. HtmlTag.prototype.setTagName = function (tagName) {
  248. this.tagName = tagName;
  249. return this;
  250. };
  251. /**
  252. * Retrieves the tag name.
  253. *
  254. * @return {String}
  255. */
  256. HtmlTag.prototype.getTagName = function () {
  257. return this.tagName || '';
  258. };
  259. /**
  260. * Sets an attribute on the HtmlTag.
  261. *
  262. * @param {String} attrName The attribute name to set.
  263. * @param {String} attrValue The attribute value to set.
  264. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  265. */
  266. HtmlTag.prototype.setAttr = function (attrName, attrValue) {
  267. var tagAttrs = this.getAttrs();
  268. tagAttrs[attrName] = attrValue;
  269. return this;
  270. };
  271. /**
  272. * Retrieves an attribute from the HtmlTag. If the attribute does not exist, returns `undefined`.
  273. *
  274. * @param {String} attrName The attribute name to retrieve.
  275. * @return {String} The attribute's value, or `undefined` if it does not exist on the HtmlTag.
  276. */
  277. HtmlTag.prototype.getAttr = function (attrName) {
  278. return this.getAttrs()[attrName];
  279. };
  280. /**
  281. * Sets one or more attributes on the HtmlTag.
  282. *
  283. * @param {Object.<String, String>} attrs A key/value Object (map) of the attributes to set.
  284. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  285. */
  286. HtmlTag.prototype.setAttrs = function (attrs) {
  287. Object.assign(this.getAttrs(), attrs);
  288. return this;
  289. };
  290. /**
  291. * Retrieves the attributes Object (map) for the HtmlTag.
  292. *
  293. * @return {Object.<String, String>} A key/value object of the attributes for the HtmlTag.
  294. */
  295. HtmlTag.prototype.getAttrs = function () {
  296. return this.attrs || (this.attrs = {});
  297. };
  298. /**
  299. * Sets the provided `cssClass`, overwriting any current CSS classes on the HtmlTag.
  300. *
  301. * @param {String} cssClass One or more space-separated CSS classes to set (overwrite).
  302. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  303. */
  304. HtmlTag.prototype.setClass = function (cssClass) {
  305. return this.setAttr('class', cssClass);
  306. };
  307. /**
  308. * Convenience method to add one or more CSS classes to the HtmlTag. Will not add duplicate CSS classes.
  309. *
  310. * @param {String} cssClass One or more space-separated CSS classes to add.
  311. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  312. */
  313. HtmlTag.prototype.addClass = function (cssClass) {
  314. var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = (!classAttr) ? [] : classAttr.split(whitespaceRegex), newClasses = cssClass.split(whitespaceRegex), newClass;
  315. while (newClass = newClasses.shift()) {
  316. if (indexOf(classes, newClass) === -1) {
  317. classes.push(newClass);
  318. }
  319. }
  320. this.getAttrs()['class'] = classes.join(" ");
  321. return this;
  322. };
  323. /**
  324. * Convenience method to remove one or more CSS classes from the HtmlTag.
  325. *
  326. * @param {String} cssClass One or more space-separated CSS classes to remove.
  327. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  328. */
  329. HtmlTag.prototype.removeClass = function (cssClass) {
  330. var classAttr = this.getClass(), whitespaceRegex = this.whitespaceRegex, classes = (!classAttr) ? [] : classAttr.split(whitespaceRegex), removeClasses = cssClass.split(whitespaceRegex), removeClass;
  331. while (classes.length && (removeClass = removeClasses.shift())) {
  332. var idx = indexOf(classes, removeClass);
  333. if (idx !== -1) {
  334. classes.splice(idx, 1);
  335. }
  336. }
  337. this.getAttrs()['class'] = classes.join(" ");
  338. return this;
  339. };
  340. /**
  341. * Convenience method to retrieve the CSS class(es) for the HtmlTag, which will each be separated by spaces when
  342. * there are multiple.
  343. *
  344. * @return {String}
  345. */
  346. HtmlTag.prototype.getClass = function () {
  347. return this.getAttrs()['class'] || "";
  348. };
  349. /**
  350. * Convenience method to check if the tag has a CSS class or not.
  351. *
  352. * @param {String} cssClass The CSS class to check for.
  353. * @return {Boolean} `true` if the HtmlTag has the CSS class, `false` otherwise.
  354. */
  355. HtmlTag.prototype.hasClass = function (cssClass) {
  356. return (' ' + this.getClass() + ' ').indexOf(' ' + cssClass + ' ') !== -1;
  357. };
  358. /**
  359. * Sets the inner HTML for the tag.
  360. *
  361. * @param {String} html The inner HTML to set.
  362. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  363. */
  364. HtmlTag.prototype.setInnerHTML = function (html) {
  365. this.innerHTML = html;
  366. return this;
  367. };
  368. /**
  369. * Backwards compatibility method name.
  370. *
  371. * @param {String} html The inner HTML to set.
  372. * @return {Autolinker.HtmlTag} This HtmlTag instance, so that method calls may be chained.
  373. */
  374. HtmlTag.prototype.setInnerHtml = function (html) {
  375. return this.setInnerHTML(html);
  376. };
  377. /**
  378. * Retrieves the inner HTML for the tag.
  379. *
  380. * @return {String}
  381. */
  382. HtmlTag.prototype.getInnerHTML = function () {
  383. return this.innerHTML || "";
  384. };
  385. /**
  386. * Backward compatibility method name.
  387. *
  388. * @return {String}
  389. */
  390. HtmlTag.prototype.getInnerHtml = function () {
  391. return this.getInnerHTML();
  392. };
  393. /**
  394. * Override of superclass method used to generate the HTML string for the tag.
  395. *
  396. * @return {String}
  397. */
  398. HtmlTag.prototype.toAnchorString = function () {
  399. var tagName = this.getTagName(), attrsStr = this.buildAttrsStr();
  400. attrsStr = (attrsStr) ? ' ' + attrsStr : ''; // prepend a space if there are actually attributes
  401. return ['<', tagName, attrsStr, '>', this.getInnerHtml(), '</', tagName, '>'].join("");
  402. };
  403. /**
  404. * Support method for {@link #toAnchorString}, returns the string space-separated key="value" pairs, used to populate
  405. * the stringified HtmlTag.
  406. *
  407. * @protected
  408. * @return {String} Example return: `attr1="value1" attr2="value2"`
  409. */
  410. HtmlTag.prototype.buildAttrsStr = function () {
  411. if (!this.attrs)
  412. return ""; // no `attrs` Object (map) has been set, return empty string
  413. var attrs = this.getAttrs(), attrsArr = [];
  414. for (var prop in attrs) {
  415. if (attrs.hasOwnProperty(prop)) {
  416. attrsArr.push(prop + '="' + attrs[prop] + '"');
  417. }
  418. }
  419. return attrsArr.join(" ");
  420. };
  421. return HtmlTag;
  422. }());
  423. /**
  424. * Date: 2015-10-05
  425. * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
  426. *
  427. * A truncation feature, where the ellipsis will be placed at a section within
  428. * the URL making it still somewhat human readable.
  429. *
  430. * @param {String} url A URL.
  431. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  432. * @param {String} ellipsisChars The characters to place within the url, e.g. "...".
  433. * @return {String} The truncated URL.
  434. */
  435. function truncateSmart(url, truncateLen, ellipsisChars) {
  436. var ellipsisLengthBeforeParsing;
  437. var ellipsisLength;
  438. if (ellipsisChars == null) {
  439. ellipsisChars = '&hellip;';
  440. ellipsisLength = 3;
  441. ellipsisLengthBeforeParsing = 8;
  442. }
  443. else {
  444. ellipsisLength = ellipsisChars.length;
  445. ellipsisLengthBeforeParsing = ellipsisChars.length;
  446. }
  447. var parse_url = function (url) {
  448. var urlObj = {};
  449. var urlSub = url;
  450. var match = urlSub.match(/^([a-z]+):\/\//i);
  451. if (match) {
  452. urlObj.scheme = match[1];
  453. urlSub = urlSub.substr(match[0].length);
  454. }
  455. match = urlSub.match(/^(.*?)(?=(\?|#|\/|$))/i);
  456. if (match) {
  457. urlObj.host = match[1];
  458. urlSub = urlSub.substr(match[0].length);
  459. }
  460. match = urlSub.match(/^\/(.*?)(?=(\?|#|$))/i);
  461. if (match) {
  462. urlObj.path = match[1];
  463. urlSub = urlSub.substr(match[0].length);
  464. }
  465. match = urlSub.match(/^\?(.*?)(?=(#|$))/i);
  466. if (match) {
  467. urlObj.query = match[1];
  468. urlSub = urlSub.substr(match[0].length);
  469. }
  470. match = urlSub.match(/^#(.*?)$/i);
  471. if (match) {
  472. urlObj.fragment = match[1];
  473. //urlSub = urlSub.substr(match[0].length); -- not used. Uncomment if adding another block.
  474. }
  475. return urlObj;
  476. };
  477. var buildUrl = function (urlObj) {
  478. var url = "";
  479. if (urlObj.scheme && urlObj.host) {
  480. url += urlObj.scheme + "://";
  481. }
  482. if (urlObj.host) {
  483. url += urlObj.host;
  484. }
  485. if (urlObj.path) {
  486. url += "/" + urlObj.path;
  487. }
  488. if (urlObj.query) {
  489. url += "?" + urlObj.query;
  490. }
  491. if (urlObj.fragment) {
  492. url += "#" + urlObj.fragment;
  493. }
  494. return url;
  495. };
  496. var buildSegment = function (segment, remainingAvailableLength) {
  497. var remainingAvailableLengthHalf = remainingAvailableLength / 2, startOffset = Math.ceil(remainingAvailableLengthHalf), endOffset = (-1) * Math.floor(remainingAvailableLengthHalf), end = "";
  498. if (endOffset < 0) {
  499. end = segment.substr(endOffset);
  500. }
  501. return segment.substr(0, startOffset) + ellipsisChars + end;
  502. };
  503. if (url.length <= truncateLen) {
  504. return url;
  505. }
  506. var availableLength = truncateLen - ellipsisLength;
  507. var urlObj = parse_url(url);
  508. // Clean up the URL
  509. if (urlObj.query) {
  510. var matchQuery = urlObj.query.match(/^(.*?)(?=(\?|\#))(.*?)$/i);
  511. if (matchQuery) {
  512. // Malformed URL; two or more "?". Removed any content behind the 2nd.
  513. urlObj.query = urlObj.query.substr(0, matchQuery[1].length);
  514. url = buildUrl(urlObj);
  515. }
  516. }
  517. if (url.length <= truncateLen) {
  518. return url;
  519. }
  520. if (urlObj.host) {
  521. urlObj.host = urlObj.host.replace(/^www\./, "");
  522. url = buildUrl(urlObj);
  523. }
  524. if (url.length <= truncateLen) {
  525. return url;
  526. }
  527. // Process and build the URL
  528. var str = "";
  529. if (urlObj.host) {
  530. str += urlObj.host;
  531. }
  532. if (str.length >= availableLength) {
  533. if (urlObj.host.length == truncateLen) {
  534. return (urlObj.host.substr(0, (truncateLen - ellipsisLength)) + ellipsisChars).substr(0, availableLength + ellipsisLengthBeforeParsing);
  535. }
  536. return buildSegment(str, availableLength).substr(0, availableLength + ellipsisLengthBeforeParsing);
  537. }
  538. var pathAndQuery = "";
  539. if (urlObj.path) {
  540. pathAndQuery += "/" + urlObj.path;
  541. }
  542. if (urlObj.query) {
  543. pathAndQuery += "?" + urlObj.query;
  544. }
  545. if (pathAndQuery) {
  546. if ((str + pathAndQuery).length >= availableLength) {
  547. if ((str + pathAndQuery).length == truncateLen) {
  548. return (str + pathAndQuery).substr(0, truncateLen);
  549. }
  550. var remainingAvailableLength = availableLength - str.length;
  551. return (str + buildSegment(pathAndQuery, remainingAvailableLength)).substr(0, availableLength + ellipsisLengthBeforeParsing);
  552. }
  553. else {
  554. str += pathAndQuery;
  555. }
  556. }
  557. if (urlObj.fragment) {
  558. var fragment = "#" + urlObj.fragment;
  559. if ((str + fragment).length >= availableLength) {
  560. if ((str + fragment).length == truncateLen) {
  561. return (str + fragment).substr(0, truncateLen);
  562. }
  563. var remainingAvailableLength2 = availableLength - str.length;
  564. return (str + buildSegment(fragment, remainingAvailableLength2)).substr(0, availableLength + ellipsisLengthBeforeParsing);
  565. }
  566. else {
  567. str += fragment;
  568. }
  569. }
  570. if (urlObj.scheme && urlObj.host) {
  571. var scheme = urlObj.scheme + "://";
  572. if ((str + scheme).length < availableLength) {
  573. return (scheme + str).substr(0, truncateLen);
  574. }
  575. }
  576. if (str.length <= truncateLen) {
  577. return str;
  578. }
  579. var end = "";
  580. if (availableLength > 0) {
  581. end = str.substr((-1) * Math.floor(availableLength / 2));
  582. }
  583. return (str.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
  584. }
  585. /**
  586. * Date: 2015-10-05
  587. * Author: Kasper Søfren <soefritz@gmail.com> (https://github.com/kafoso)
  588. *
  589. * A truncation feature, where the ellipsis will be placed in the dead-center of the URL.
  590. *
  591. * @param {String} url A URL.
  592. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  593. * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
  594. * @return {String} The truncated URL.
  595. */
  596. function truncateMiddle(url, truncateLen, ellipsisChars) {
  597. if (url.length <= truncateLen) {
  598. return url;
  599. }
  600. var ellipsisLengthBeforeParsing;
  601. var ellipsisLength;
  602. if (ellipsisChars == null) {
  603. ellipsisChars = '&hellip;';
  604. ellipsisLengthBeforeParsing = 8;
  605. ellipsisLength = 3;
  606. }
  607. else {
  608. ellipsisLengthBeforeParsing = ellipsisChars.length;
  609. ellipsisLength = ellipsisChars.length;
  610. }
  611. var availableLength = truncateLen - ellipsisLength;
  612. var end = "";
  613. if (availableLength > 0) {
  614. end = url.substr((-1) * Math.floor(availableLength / 2));
  615. }
  616. return (url.substr(0, Math.ceil(availableLength / 2)) + ellipsisChars + end).substr(0, availableLength + ellipsisLengthBeforeParsing);
  617. }
  618. /**
  619. * A truncation feature where the ellipsis will be placed at the end of the URL.
  620. *
  621. * @param {String} anchorText
  622. * @param {Number} truncateLen The maximum length of the truncated output URL string.
  623. * @param {String} ellipsisChars The characters to place within the url, e.g. "..".
  624. * @return {String} The truncated URL.
  625. */
  626. function truncateEnd(anchorText, truncateLen, ellipsisChars) {
  627. return ellipsis(anchorText, truncateLen, ellipsisChars);
  628. }
  629. /**
  630. * @protected
  631. * @class Autolinker.AnchorTagBuilder
  632. * @extends Object
  633. *
  634. * Builds anchor (&lt;a&gt;) tags for the Autolinker utility when a match is
  635. * found.
  636. *
  637. * Normally this class is instantiated, configured, and used internally by an
  638. * {@link Autolinker} instance, but may actually be used indirectly in a
  639. * {@link Autolinker#replaceFn replaceFn} to create {@link Autolinker.HtmlTag HtmlTag}
  640. * instances which may be modified before returning from the
  641. * {@link Autolinker#replaceFn replaceFn}. For example:
  642. *
  643. * var html = Autolinker.link( "Test google.com", {
  644. * replaceFn : function( match ) {
  645. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
  646. * tag.setAttr( 'rel', 'nofollow' );
  647. *
  648. * return tag;
  649. * }
  650. * } );
  651. *
  652. * // generated html:
  653. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  654. */
  655. var AnchorTagBuilder = /** @class */ (function () {
  656. /**
  657. * @method constructor
  658. * @param {Object} [cfg] The configuration options for the AnchorTagBuilder instance, specified in an Object (map).
  659. */
  660. function AnchorTagBuilder(cfg) {
  661. if (cfg === void 0) { cfg = {}; }
  662. /**
  663. * @cfg {Boolean} newWindow
  664. * @inheritdoc Autolinker#newWindow
  665. */
  666. this.newWindow = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  667. /**
  668. * @cfg {Object} truncate
  669. * @inheritdoc Autolinker#truncate
  670. */
  671. this.truncate = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  672. /**
  673. * @cfg {String} className
  674. * @inheritdoc Autolinker#className
  675. */
  676. this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  677. this.newWindow = cfg.newWindow || false;
  678. this.truncate = cfg.truncate || {};
  679. this.className = cfg.className || '';
  680. }
  681. /**
  682. * Generates the actual anchor (&lt;a&gt;) tag to use in place of the
  683. * matched text, via its `match` object.
  684. *
  685. * @param {Autolinker.match.Match} match The Match instance to generate an
  686. * anchor tag from.
  687. * @return {Autolinker.HtmlTag} The HtmlTag instance for the anchor tag.
  688. */
  689. AnchorTagBuilder.prototype.build = function (match) {
  690. return new HtmlTag({
  691. tagName: 'a',
  692. attrs: this.createAttrs(match),
  693. innerHtml: this.processAnchorText(match.getAnchorText())
  694. });
  695. };
  696. /**
  697. * Creates the Object (map) of the HTML attributes for the anchor (&lt;a&gt;)
  698. * tag being generated.
  699. *
  700. * @protected
  701. * @param {Autolinker.match.Match} match The Match instance to generate an
  702. * anchor tag from.
  703. * @return {Object} A key/value Object (map) of the anchor tag's attributes.
  704. */
  705. AnchorTagBuilder.prototype.createAttrs = function (match) {
  706. var attrs = {
  707. 'href': match.getAnchorHref() // we'll always have the `href` attribute
  708. };
  709. var cssClass = this.createCssClass(match);
  710. if (cssClass) {
  711. attrs['class'] = cssClass;
  712. }
  713. if (this.newWindow) {
  714. attrs['target'] = "_blank";
  715. attrs['rel'] = "noopener noreferrer"; // Issue #149. See https://mathiasbynens.github.io/rel-noopener/
  716. }
  717. if (this.truncate) {
  718. if (this.truncate.length && this.truncate.length < match.getAnchorText().length) {
  719. attrs['title'] = match.getAnchorHref();
  720. }
  721. }
  722. return attrs;
  723. };
  724. /**
  725. * Creates the CSS class that will be used for a given anchor tag, based on
  726. * the `matchType` and the {@link #className} config.
  727. *
  728. * Example returns:
  729. *
  730. * - "" // no {@link #className}
  731. * - "myLink myLink-url" // url match
  732. * - "myLink myLink-email" // email match
  733. * - "myLink myLink-phone" // phone match
  734. * - "myLink myLink-hashtag" // hashtag match
  735. * - "myLink myLink-mention myLink-twitter" // mention match with Twitter service
  736. *
  737. * @protected
  738. * @param {Autolinker.match.Match} match The Match instance to generate an
  739. * anchor tag from.
  740. * @return {String} The CSS class string for the link. Example return:
  741. * "myLink myLink-url". If no {@link #className} was configured, returns
  742. * an empty string.
  743. */
  744. AnchorTagBuilder.prototype.createCssClass = function (match) {
  745. var className = this.className;
  746. if (!className) {
  747. return "";
  748. }
  749. else {
  750. var returnClasses = [className], cssClassSuffixes = match.getCssClassSuffixes();
  751. for (var i = 0, len = cssClassSuffixes.length; i < len; i++) {
  752. returnClasses.push(className + '-' + cssClassSuffixes[i]);
  753. }
  754. return returnClasses.join(' ');
  755. }
  756. };
  757. /**
  758. * Processes the `anchorText` by truncating the text according to the
  759. * {@link #truncate} config.
  760. *
  761. * @private
  762. * @param {String} anchorText The anchor tag's text (i.e. what will be
  763. * displayed).
  764. * @return {String} The processed `anchorText`.
  765. */
  766. AnchorTagBuilder.prototype.processAnchorText = function (anchorText) {
  767. anchorText = this.doTruncate(anchorText);
  768. return anchorText;
  769. };
  770. /**
  771. * Performs the truncation of the `anchorText` based on the {@link #truncate}
  772. * option. If the `anchorText` is longer than the length specified by the
  773. * {@link #truncate} option, the truncation is performed based on the
  774. * `location` property. See {@link #truncate} for details.
  775. *
  776. * @private
  777. * @param {String} anchorText The anchor tag's text (i.e. what will be
  778. * displayed).
  779. * @return {String} The truncated anchor text.
  780. */
  781. AnchorTagBuilder.prototype.doTruncate = function (anchorText) {
  782. var truncate = this.truncate;
  783. if (!truncate || !truncate.length)
  784. return anchorText;
  785. var truncateLength = truncate.length, truncateLocation = truncate.location;
  786. if (truncateLocation === 'smart') {
  787. return truncateSmart(anchorText, truncateLength);
  788. }
  789. else if (truncateLocation === 'middle') {
  790. return truncateMiddle(anchorText, truncateLength);
  791. }
  792. else {
  793. return truncateEnd(anchorText, truncateLength);
  794. }
  795. };
  796. return AnchorTagBuilder;
  797. }());
  798. /**
  799. * @abstract
  800. * @class Autolinker.match.Match
  801. *
  802. * Represents a match found in an input string which should be Autolinked. A Match object is what is provided in a
  803. * {@link Autolinker#replaceFn replaceFn}, and may be used to query for details about the match.
  804. *
  805. * For example:
  806. *
  807. * var input = "..."; // string with URLs, Email Addresses, and Mentions (Twitter, Instagram, Soundcloud)
  808. *
  809. * var linkedText = Autolinker.link( input, {
  810. * replaceFn : function( match ) {
  811. * console.log( "href = ", match.getAnchorHref() );
  812. * console.log( "text = ", match.getAnchorText() );
  813. *
  814. * switch( match.getType() ) {
  815. * case 'url' :
  816. * console.log( "url: ", match.getUrl() );
  817. *
  818. * case 'email' :
  819. * console.log( "email: ", match.getEmail() );
  820. *
  821. * case 'mention' :
  822. * console.log( "mention: ", match.getMention() );
  823. * }
  824. * }
  825. * } );
  826. *
  827. * See the {@link Autolinker} class for more details on using the {@link Autolinker#replaceFn replaceFn}.
  828. */
  829. var Match = /** @class */ (function () {
  830. /**
  831. * @member Autolinker.match.Match
  832. * @method constructor
  833. * @param {Object} cfg The configuration properties for the Match
  834. * instance, specified in an Object (map).
  835. */
  836. function Match(cfg) {
  837. /**
  838. * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required)
  839. *
  840. * Reference to the AnchorTagBuilder instance to use to generate an anchor
  841. * tag for the Match.
  842. */
  843. this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator
  844. /**
  845. * @cfg {String} matchedText (required)
  846. *
  847. * The original text that was matched by the {@link Autolinker.matcher.Matcher}.
  848. */
  849. this.matchedText = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  850. /**
  851. * @cfg {Number} offset (required)
  852. *
  853. * The offset of where the match was made in the input string.
  854. */
  855. this.offset = 0; // default value just to get the above doc comment in the ES5 output and documentation generator
  856. this.tagBuilder = cfg.tagBuilder;
  857. this.matchedText = cfg.matchedText;
  858. this.offset = cfg.offset;
  859. }
  860. /**
  861. * Returns the original text that was matched.
  862. *
  863. * @return {String}
  864. */
  865. Match.prototype.getMatchedText = function () {
  866. return this.matchedText;
  867. };
  868. /**
  869. * Sets the {@link #offset} of where the match was made in the input string.
  870. *
  871. * A {@link Autolinker.matcher.Matcher} will be fed only HTML text nodes,
  872. * and will therefore set an original offset that is relative to the HTML
  873. * text node itself. However, we want this offset to be relative to the full
  874. * HTML input string, and thus if using {@link Autolinker#parse} (rather
  875. * than calling a {@link Autolinker.matcher.Matcher} directly), then this
  876. * offset is corrected after the Matcher itself has done its job.
  877. *
  878. * @param {Number} offset
  879. */
  880. Match.prototype.setOffset = function (offset) {
  881. this.offset = offset;
  882. };
  883. /**
  884. * Returns the offset of where the match was made in the input string. This
  885. * is the 0-based index of the match.
  886. *
  887. * @return {Number}
  888. */
  889. Match.prototype.getOffset = function () {
  890. return this.offset;
  891. };
  892. /**
  893. * Returns the CSS class suffix(es) for this match.
  894. *
  895. * A CSS class suffix is appended to the {@link Autolinker#className} in
  896. * the {@link Autolinker.AnchorTagBuilder} when a match is translated into
  897. * an anchor tag.
  898. *
  899. * For example, if {@link Autolinker#className} was configured as 'myLink',
  900. * and this method returns `[ 'url' ]`, the final class name of the element
  901. * will become: 'myLink myLink-url'.
  902. *
  903. * The match may provide multiple CSS class suffixes to be appended to the
  904. * {@link Autolinker#className} in order to facilitate better styling
  905. * options for different match criteria. See {@link Autolinker.match.Mention}
  906. * for an example.
  907. *
  908. * By default, this method returns a single array with the match's
  909. * {@link #getType type} name, but may be overridden by subclasses.
  910. *
  911. * @return {String[]}
  912. */
  913. Match.prototype.getCssClassSuffixes = function () {
  914. return [this.getType()];
  915. };
  916. /**
  917. * Builds and returns an {@link Autolinker.HtmlTag} instance based on the
  918. * Match.
  919. *
  920. * This can be used to easily generate anchor tags from matches, and either
  921. * return their HTML string, or modify them before doing so.
  922. *
  923. * Example Usage:
  924. *
  925. * var tag = match.buildTag();
  926. * tag.addClass( 'cordova-link' );
  927. * tag.setAttr( 'target', '_system' );
  928. *
  929. * tag.toAnchorString(); // <a href="http://google.com" class="cordova-link" target="_system">Google</a>
  930. *
  931. * Example Usage in {@link Autolinker#replaceFn}:
  932. *
  933. * var html = Autolinker.link( "Test google.com", {
  934. * replaceFn : function( match ) {
  935. * var tag = match.buildTag(); // returns an {@link Autolinker.HtmlTag} instance
  936. * tag.setAttr( 'rel', 'nofollow' );
  937. *
  938. * return tag;
  939. * }
  940. * } );
  941. *
  942. * // generated html:
  943. * // Test <a href="http://google.com" target="_blank" rel="nofollow">google.com</a>
  944. */
  945. Match.prototype.buildTag = function () {
  946. return this.tagBuilder.build(this);
  947. };
  948. return Match;
  949. }());
  950. /******************************************************************************
  951. Copyright (c) Microsoft Corporation.
  952. Permission to use, copy, modify, and/or distribute this software for any
  953. purpose with or without fee is hereby granted.
  954. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
  955. REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  956. AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
  957. INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  958. LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
  959. OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  960. PERFORMANCE OF THIS SOFTWARE.
  961. ***************************************************************************** */
  962. /* global Reflect, Promise */
  963. var extendStatics = function(d, b) {
  964. extendStatics = Object.setPrototypeOf ||
  965. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  966. function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
  967. return extendStatics(d, b);
  968. };
  969. function __extends(d, b) {
  970. if (typeof b !== "function" && b !== null)
  971. throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
  972. extendStatics(d, b);
  973. function __() { this.constructor = d; }
  974. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  975. }
  976. var __assign = function() {
  977. __assign = Object.assign || function __assign(t) {
  978. for (var s, i = 1, n = arguments.length; i < n; i++) {
  979. s = arguments[i];
  980. for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
  981. }
  982. return t;
  983. };
  984. return __assign.apply(this, arguments);
  985. };
  986. /**
  987. * @class Autolinker.match.Email
  988. * @extends Autolinker.match.Match
  989. *
  990. * Represents a Email match found in an input string which should be Autolinked.
  991. *
  992. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  993. */
  994. var EmailMatch = /** @class */ (function (_super) {
  995. __extends(EmailMatch, _super);
  996. /**
  997. * @method constructor
  998. * @param {Object} cfg The configuration properties for the Match
  999. * instance, specified in an Object (map).
  1000. */
  1001. function EmailMatch(cfg) {
  1002. var _this = _super.call(this, cfg) || this;
  1003. /**
  1004. * @cfg {String} email (required)
  1005. *
  1006. * The email address that was matched.
  1007. */
  1008. _this.email = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1009. _this.email = cfg.email;
  1010. return _this;
  1011. }
  1012. /**
  1013. * Returns a string name for the type of match that this class represents.
  1014. * For the case of EmailMatch, returns 'email'.
  1015. *
  1016. * @return {String}
  1017. */
  1018. EmailMatch.prototype.getType = function () {
  1019. return 'email';
  1020. };
  1021. /**
  1022. * Returns the email address that was matched.
  1023. *
  1024. * @return {String}
  1025. */
  1026. EmailMatch.prototype.getEmail = function () {
  1027. return this.email;
  1028. };
  1029. /**
  1030. * Returns the anchor href that should be generated for the match.
  1031. *
  1032. * @return {String}
  1033. */
  1034. EmailMatch.prototype.getAnchorHref = function () {
  1035. return 'mailto:' + this.email;
  1036. };
  1037. /**
  1038. * Returns the anchor text that should be generated for the match.
  1039. *
  1040. * @return {String}
  1041. */
  1042. EmailMatch.prototype.getAnchorText = function () {
  1043. return this.email;
  1044. };
  1045. return EmailMatch;
  1046. }(Match));
  1047. /**
  1048. * @class Autolinker.match.Hashtag
  1049. * @extends Autolinker.match.Match
  1050. *
  1051. * Represents a Hashtag match found in an input string which should be
  1052. * Autolinked.
  1053. *
  1054. * See this class's superclass ({@link Autolinker.match.Match}) for more
  1055. * details.
  1056. */
  1057. var HashtagMatch = /** @class */ (function (_super) {
  1058. __extends(HashtagMatch, _super);
  1059. /**
  1060. * @method constructor
  1061. * @param {Object} cfg The configuration properties for the Match
  1062. * instance, specified in an Object (map).
  1063. */
  1064. function HashtagMatch(cfg) {
  1065. var _this = _super.call(this, cfg) || this;
  1066. /**
  1067. * @cfg {String} serviceName
  1068. *
  1069. * The service to point hashtag matches to. See {@link Autolinker#hashtag}
  1070. * for available values.
  1071. */
  1072. _this.serviceName = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1073. /**
  1074. * @cfg {String} hashtag (required)
  1075. *
  1076. * The HashtagMatch that was matched, without the '#'.
  1077. */
  1078. _this.hashtag = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1079. _this.serviceName = cfg.serviceName;
  1080. _this.hashtag = cfg.hashtag;
  1081. return _this;
  1082. }
  1083. /**
  1084. * Returns a string name for the type of match that this class represents.
  1085. * For the case of HashtagMatch, returns 'hashtag'.
  1086. *
  1087. * @return {String}
  1088. */
  1089. HashtagMatch.prototype.getType = function () {
  1090. return 'hashtag';
  1091. };
  1092. /**
  1093. * Returns the configured {@link #serviceName} to point the HashtagMatch to.
  1094. * Ex: 'facebook', 'twitter'.
  1095. *
  1096. * @return {String}
  1097. */
  1098. HashtagMatch.prototype.getServiceName = function () {
  1099. return this.serviceName;
  1100. };
  1101. /**
  1102. * Returns the matched hashtag, without the '#' character.
  1103. *
  1104. * @return {String}
  1105. */
  1106. HashtagMatch.prototype.getHashtag = function () {
  1107. return this.hashtag;
  1108. };
  1109. /**
  1110. * Returns the anchor href that should be generated for the match.
  1111. *
  1112. * @return {String}
  1113. */
  1114. HashtagMatch.prototype.getAnchorHref = function () {
  1115. var serviceName = this.serviceName, hashtag = this.hashtag;
  1116. switch (serviceName) {
  1117. case 'twitter':
  1118. return 'https://twitter.com/hashtag/' + hashtag;
  1119. case 'facebook':
  1120. return 'https://www.facebook.com/hashtag/' + hashtag;
  1121. case 'instagram':
  1122. return 'https://instagram.com/explore/tags/' + hashtag;
  1123. case 'tiktok':
  1124. return 'https://www.tiktok.com/tag/' + hashtag;
  1125. default: // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case.
  1126. throw new Error('Unknown service name to point hashtag to: ' + serviceName);
  1127. }
  1128. };
  1129. /**
  1130. * Returns the anchor text that should be generated for the match.
  1131. *
  1132. * @return {String}
  1133. */
  1134. HashtagMatch.prototype.getAnchorText = function () {
  1135. return '#' + this.hashtag;
  1136. };
  1137. return HashtagMatch;
  1138. }(Match));
  1139. /**
  1140. * @class Autolinker.match.Mention
  1141. * @extends Autolinker.match.Match
  1142. *
  1143. * Represents a Mention match found in an input string which should be Autolinked.
  1144. *
  1145. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  1146. */
  1147. var MentionMatch = /** @class */ (function (_super) {
  1148. __extends(MentionMatch, _super);
  1149. /**
  1150. * @method constructor
  1151. * @param {Object} cfg The configuration properties for the Match
  1152. * instance, specified in an Object (map).
  1153. */
  1154. function MentionMatch(cfg) {
  1155. var _this = _super.call(this, cfg) || this;
  1156. /**
  1157. * @cfg {String} serviceName
  1158. *
  1159. * The service to point mention matches to. See {@link Autolinker#mention}
  1160. * for available values.
  1161. */
  1162. _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
  1163. /**
  1164. * @cfg {String} mention (required)
  1165. *
  1166. * The Mention that was matched, without the '@' character.
  1167. */
  1168. _this.mention = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1169. _this.mention = cfg.mention;
  1170. _this.serviceName = cfg.serviceName;
  1171. return _this;
  1172. }
  1173. /**
  1174. * Returns a string name for the type of match that this class represents.
  1175. * For the case of MentionMatch, returns 'mention'.
  1176. *
  1177. * @return {String}
  1178. */
  1179. MentionMatch.prototype.getType = function () {
  1180. return 'mention';
  1181. };
  1182. /**
  1183. * Returns the mention, without the '@' character.
  1184. *
  1185. * @return {String}
  1186. */
  1187. MentionMatch.prototype.getMention = function () {
  1188. return this.mention;
  1189. };
  1190. /**
  1191. * Returns the configured {@link #serviceName} to point the mention to.
  1192. * Ex: 'instagram', 'twitter', 'soundcloud'.
  1193. *
  1194. * @return {String}
  1195. */
  1196. MentionMatch.prototype.getServiceName = function () {
  1197. return this.serviceName;
  1198. };
  1199. /**
  1200. * Returns the anchor href that should be generated for the match.
  1201. *
  1202. * @return {String}
  1203. */
  1204. MentionMatch.prototype.getAnchorHref = function () {
  1205. switch (this.serviceName) {
  1206. case 'twitter':
  1207. return 'https://twitter.com/' + this.mention;
  1208. case 'instagram':
  1209. return 'https://instagram.com/' + this.mention;
  1210. case 'soundcloud':
  1211. return 'https://soundcloud.com/' + this.mention;
  1212. case 'tiktok':
  1213. return 'https://www.tiktok.com/@' + this.mention;
  1214. default: // Shouldn't happen because Autolinker's constructor should block any invalid values, but just in case.
  1215. throw new Error('Unknown service name to point mention to: ' + this.serviceName);
  1216. }
  1217. };
  1218. /**
  1219. * Returns the anchor text that should be generated for the match.
  1220. *
  1221. * @return {String}
  1222. */
  1223. MentionMatch.prototype.getAnchorText = function () {
  1224. return '@' + this.mention;
  1225. };
  1226. /**
  1227. * Returns the CSS class suffixes that should be used on a tag built with
  1228. * the match. See {@link Autolinker.match.Match#getCssClassSuffixes} for
  1229. * details.
  1230. *
  1231. * @return {String[]}
  1232. */
  1233. MentionMatch.prototype.getCssClassSuffixes = function () {
  1234. var cssClassSuffixes = _super.prototype.getCssClassSuffixes.call(this), serviceName = this.getServiceName();
  1235. if (serviceName) {
  1236. cssClassSuffixes.push(serviceName);
  1237. }
  1238. return cssClassSuffixes;
  1239. };
  1240. return MentionMatch;
  1241. }(Match));
  1242. /**
  1243. * @class Autolinker.match.Phone
  1244. * @extends Autolinker.match.Match
  1245. *
  1246. * Represents a Phone number match found in an input string which should be
  1247. * Autolinked.
  1248. *
  1249. * See this class's superclass ({@link Autolinker.match.Match}) for more
  1250. * details.
  1251. */
  1252. var PhoneMatch = /** @class */ (function (_super) {
  1253. __extends(PhoneMatch, _super);
  1254. /**
  1255. * @method constructor
  1256. * @param {Object} cfg The configuration properties for the Match
  1257. * instance, specified in an Object (map).
  1258. */
  1259. function PhoneMatch(cfg) {
  1260. var _this = _super.call(this, cfg) || this;
  1261. /**
  1262. * @protected
  1263. * @property {String} number (required)
  1264. *
  1265. * The phone number that was matched, without any delimiter characters.
  1266. *
  1267. * Note: This is a string to allow for prefixed 0's.
  1268. */
  1269. _this.number = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1270. /**
  1271. * @protected
  1272. * @property {Boolean} plusSign (required)
  1273. *
  1274. * `true` if the matched phone number started with a '+' sign. We'll include
  1275. * it in the `tel:` URL if so, as this is needed for international numbers.
  1276. *
  1277. * Ex: '+1 (123) 456 7879'
  1278. */
  1279. _this.plusSign = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  1280. _this.number = cfg.number;
  1281. _this.plusSign = cfg.plusSign;
  1282. return _this;
  1283. }
  1284. /**
  1285. * Returns a string name for the type of match that this class represents.
  1286. * For the case of PhoneMatch, returns 'phone'.
  1287. *
  1288. * @return {String}
  1289. */
  1290. PhoneMatch.prototype.getType = function () {
  1291. return 'phone';
  1292. };
  1293. /**
  1294. * Returns the phone number that was matched as a string, without any
  1295. * delimiter characters.
  1296. *
  1297. * Note: This is a string to allow for prefixed 0's.
  1298. *
  1299. * @return {String}
  1300. */
  1301. PhoneMatch.prototype.getPhoneNumber = function () {
  1302. return this.number;
  1303. };
  1304. /**
  1305. * Alias of {@link #getPhoneNumber}, returns the phone number that was
  1306. * matched as a string, without any delimiter characters.
  1307. *
  1308. * Note: This is a string to allow for prefixed 0's.
  1309. *
  1310. * @return {String}
  1311. */
  1312. PhoneMatch.prototype.getNumber = function () {
  1313. return this.getPhoneNumber();
  1314. };
  1315. /**
  1316. * Returns the anchor href that should be generated for the match.
  1317. *
  1318. * @return {String}
  1319. */
  1320. PhoneMatch.prototype.getAnchorHref = function () {
  1321. return 'tel:' + (this.plusSign ? '+' : '') + this.number;
  1322. };
  1323. /**
  1324. * Returns the anchor text that should be generated for the match.
  1325. *
  1326. * @return {String}
  1327. */
  1328. PhoneMatch.prototype.getAnchorText = function () {
  1329. return this.matchedText;
  1330. };
  1331. return PhoneMatch;
  1332. }(Match));
  1333. /**
  1334. * @class Autolinker.match.Url
  1335. * @extends Autolinker.match.Match
  1336. *
  1337. * Represents a Url match found in an input string which should be Autolinked.
  1338. *
  1339. * See this class's superclass ({@link Autolinker.match.Match}) for more details.
  1340. */
  1341. var UrlMatch = /** @class */ (function (_super) {
  1342. __extends(UrlMatch, _super);
  1343. /**
  1344. * @method constructor
  1345. * @param {Object} cfg The configuration properties for the Match
  1346. * instance, specified in an Object (map).
  1347. */
  1348. function UrlMatch(cfg) {
  1349. var _this = _super.call(this, cfg) || this;
  1350. /**
  1351. * @cfg {String} url (required)
  1352. *
  1353. * The url that was matched.
  1354. */
  1355. _this.url = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  1356. /**
  1357. * @cfg {"scheme"/"www"/"tld"} urlMatchType (required)
  1358. *
  1359. * The type of URL match that this class represents. This helps to determine
  1360. * if the match was made in the original text with a prefixed scheme (ex:
  1361. * 'http://www.google.com'), a prefixed 'www' (ex: 'www.google.com'), or
  1362. * was matched by a known top-level domain (ex: 'google.com').
  1363. */
  1364. _this.urlMatchType = 'scheme'; // default value just to get the above doc comment in the ES5 output and documentation generator
  1365. /**
  1366. * @cfg {Boolean} protocolUrlMatch (required)
  1367. *
  1368. * `true` if the URL is a match which already has a protocol (i.e.
  1369. * 'http://'), `false` if the match was from a 'www' or known TLD match.
  1370. */
  1371. _this.protocolUrlMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  1372. /**
  1373. * @cfg {Boolean} protocolRelativeMatch (required)
  1374. *
  1375. * `true` if the URL is a protocol-relative match. A protocol-relative match
  1376. * is a URL that starts with '//', and will be either http:// or https://
  1377. * based on the protocol that the site is loaded under.
  1378. */
  1379. _this.protocolRelativeMatch = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  1380. /**
  1381. * @cfg {Object} stripPrefix (required)
  1382. *
  1383. * The Object form of {@link Autolinker#cfg-stripPrefix}.
  1384. */
  1385. _this.stripPrefix = { scheme: true, www: true }; // default value just to get the above doc comment in the ES5 output and documentation generator
  1386. /**
  1387. * @cfg {Boolean} stripTrailingSlash (required)
  1388. * @inheritdoc Autolinker#cfg-stripTrailingSlash
  1389. */
  1390. _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  1391. /**
  1392. * @cfg {Boolean} decodePercentEncoding (required)
  1393. * @inheritdoc Autolinker#cfg-decodePercentEncoding
  1394. */
  1395. _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  1396. /**
  1397. * @private
  1398. * @property {RegExp} schemePrefixRegex
  1399. *
  1400. * A regular expression used to remove the 'http://' or 'https://' from
  1401. * URLs.
  1402. */
  1403. _this.schemePrefixRegex = /^(https?:\/\/)?/i;
  1404. /**
  1405. * @private
  1406. * @property {RegExp} wwwPrefixRegex
  1407. *
  1408. * A regular expression used to remove the 'www.' from URLs.
  1409. */
  1410. _this.wwwPrefixRegex = /^(https?:\/\/)?(www\.)?/i;
  1411. /**
  1412. * @private
  1413. * @property {RegExp} protocolRelativeRegex
  1414. *
  1415. * The regular expression used to remove the protocol-relative '//' from the {@link #url} string, for purposes
  1416. * of {@link #getAnchorText}. A protocol-relative URL is, for example, "//yahoo.com"
  1417. */
  1418. _this.protocolRelativeRegex = /^\/\//;
  1419. /**
  1420. * @private
  1421. * @property {Boolean} protocolPrepended
  1422. *
  1423. * Will be set to `true` if the 'http://' protocol has been prepended to the {@link #url} (because the
  1424. * {@link #url} did not have a protocol)
  1425. */
  1426. _this.protocolPrepended = false;
  1427. _this.urlMatchType = cfg.urlMatchType;
  1428. _this.url = cfg.url;
  1429. _this.protocolUrlMatch = cfg.protocolUrlMatch;
  1430. _this.protocolRelativeMatch = cfg.protocolRelativeMatch;
  1431. _this.stripPrefix = cfg.stripPrefix;
  1432. _this.stripTrailingSlash = cfg.stripTrailingSlash;
  1433. _this.decodePercentEncoding = cfg.decodePercentEncoding;
  1434. return _this;
  1435. }
  1436. /**
  1437. * Returns a string name for the type of match that this class represents.
  1438. * For the case of UrlMatch, returns 'url'.
  1439. *
  1440. * @return {String}
  1441. */
  1442. UrlMatch.prototype.getType = function () {
  1443. return 'url';
  1444. };
  1445. /**
  1446. * Returns a string name for the type of URL match that this class
  1447. * represents.
  1448. *
  1449. * This helps to determine if the match was made in the original text with a
  1450. * prefixed scheme (ex: 'http://www.google.com'), a prefixed 'www' (ex:
  1451. * 'www.google.com'), or was matched by a known top-level domain (ex:
  1452. * 'google.com').
  1453. *
  1454. * @return {"scheme"/"www"/"tld"}
  1455. */
  1456. UrlMatch.prototype.getUrlMatchType = function () {
  1457. return this.urlMatchType;
  1458. };
  1459. /**
  1460. * Returns the url that was matched, assuming the protocol to be 'http://' if the original
  1461. * match was missing a protocol.
  1462. *
  1463. * @return {String}
  1464. */
  1465. UrlMatch.prototype.getUrl = function () {
  1466. var url = this.url;
  1467. // if the url string doesn't begin with a protocol, assume 'http://'
  1468. if (!this.protocolRelativeMatch && !this.protocolUrlMatch && !this.protocolPrepended) {
  1469. url = this.url = 'http://' + url;
  1470. this.protocolPrepended = true;
  1471. }
  1472. return url;
  1473. };
  1474. /**
  1475. * Returns the anchor href that should be generated for the match.
  1476. *
  1477. * @return {String}
  1478. */
  1479. UrlMatch.prototype.getAnchorHref = function () {
  1480. var url = this.getUrl();
  1481. 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
  1482. };
  1483. /**
  1484. * Returns the anchor text that should be generated for the match.
  1485. *
  1486. * @return {String}
  1487. */
  1488. UrlMatch.prototype.getAnchorText = function () {
  1489. var anchorText = this.getMatchedText();
  1490. if (this.protocolRelativeMatch) {
  1491. // Strip off any protocol-relative '//' from the anchor text
  1492. anchorText = this.stripProtocolRelativePrefix(anchorText);
  1493. }
  1494. if (this.stripPrefix.scheme) {
  1495. anchorText = this.stripSchemePrefix(anchorText);
  1496. }
  1497. if (this.stripPrefix.www) {
  1498. anchorText = this.stripWwwPrefix(anchorText);
  1499. }
  1500. if (this.stripTrailingSlash) {
  1501. anchorText = this.removeTrailingSlash(anchorText); // remove trailing slash, if there is one
  1502. }
  1503. if (this.decodePercentEncoding) {
  1504. anchorText = this.removePercentEncoding(anchorText);
  1505. }
  1506. return anchorText;
  1507. };
  1508. // ---------------------------------------
  1509. // Utility Functionality
  1510. /**
  1511. * Strips the scheme prefix (such as "http://" or "https://") from the given
  1512. * `url`.
  1513. *
  1514. * @private
  1515. * @param {String} url The text of the anchor that is being generated, for
  1516. * which to strip off the url scheme.
  1517. * @return {String} The `url`, with the scheme stripped.
  1518. */
  1519. UrlMatch.prototype.stripSchemePrefix = function (url) {
  1520. return url.replace(this.schemePrefixRegex, '');
  1521. };
  1522. /**
  1523. * Strips the 'www' prefix from the given `url`.
  1524. *
  1525. * @private
  1526. * @param {String} url The text of the anchor that is being generated, for
  1527. * which to strip off the 'www' if it exists.
  1528. * @return {String} The `url`, with the 'www' stripped.
  1529. */
  1530. UrlMatch.prototype.stripWwwPrefix = function (url) {
  1531. return url.replace(this.wwwPrefixRegex, '$1'); // leave any scheme ($1), it one exists
  1532. };
  1533. /**
  1534. * Strips any protocol-relative '//' from the anchor text.
  1535. *
  1536. * @private
  1537. * @param {String} text The text of the anchor that is being generated, for which to strip off the
  1538. * protocol-relative prefix (such as stripping off "//")
  1539. * @return {String} The `anchorText`, with the protocol-relative prefix stripped.
  1540. */
  1541. UrlMatch.prototype.stripProtocolRelativePrefix = function (text) {
  1542. return text.replace(this.protocolRelativeRegex, '');
  1543. };
  1544. /**
  1545. * Removes any trailing slash from the given `anchorText`, in preparation for the text to be displayed.
  1546. *
  1547. * @private
  1548. * @param {String} anchorText The text of the anchor that is being generated, for which to remove any trailing
  1549. * slash ('/') that may exist.
  1550. * @return {String} The `anchorText`, with the trailing slash removed.
  1551. */
  1552. UrlMatch.prototype.removeTrailingSlash = function (anchorText) {
  1553. if (anchorText.charAt(anchorText.length - 1) === '/') {
  1554. anchorText = anchorText.slice(0, -1);
  1555. }
  1556. return anchorText;
  1557. };
  1558. /**
  1559. * Decodes percent-encoded characters from the given `anchorText`, in
  1560. * preparation for the text to be displayed.
  1561. *
  1562. * @private
  1563. * @param {String} anchorText The text of the anchor that is being
  1564. * generated, for which to decode any percent-encoded characters.
  1565. * @return {String} The `anchorText`, with the percent-encoded characters
  1566. * decoded.
  1567. */
  1568. UrlMatch.prototype.removePercentEncoding = function (anchorText) {
  1569. // First, convert a few of the known % encodings to the corresponding
  1570. // HTML entities that could accidentally be interpretted as special
  1571. // HTML characters
  1572. var preProcessedEntityAnchorText = anchorText
  1573. .replace(/%22/gi, '&quot;') // " char
  1574. .replace(/%26/gi, '&amp;') // & char
  1575. .replace(/%27/gi, '&#39;') // ' char
  1576. .replace(/%3C/gi, '&lt;') // < char
  1577. .replace(/%3E/gi, '&gt;'); // > char
  1578. try {
  1579. // Now attempt to decode the rest of the anchor text
  1580. return decodeURIComponent(preProcessedEntityAnchorText);
  1581. }
  1582. catch (e) { // Invalid % escape sequence in the anchor text
  1583. return preProcessedEntityAnchorText;
  1584. }
  1585. };
  1586. return UrlMatch;
  1587. }(Match));
  1588. /**
  1589. * @abstract
  1590. * @class Autolinker.matcher.Matcher
  1591. *
  1592. * An abstract class and interface for individual matchers to find matches in
  1593. * an input string with linkified versions of them.
  1594. *
  1595. * Note that Matchers do not take HTML into account - they must be fed the text
  1596. * nodes of any HTML string, which is handled by {@link Autolinker#parse}.
  1597. */
  1598. var Matcher = /** @class */ (function () {
  1599. /**
  1600. * @method constructor
  1601. * @param {Object} cfg The configuration properties for the Matcher
  1602. * instance, specified in an Object (map).
  1603. */
  1604. function Matcher(cfg) {
  1605. /**
  1606. * @cfg {Autolinker.AnchorTagBuilder} tagBuilder (required)
  1607. *
  1608. * Reference to the AnchorTagBuilder instance to use to generate HTML tags
  1609. * for {@link Autolinker.match.Match Matches}.
  1610. */
  1611. this.__jsduckDummyDocProp = null; // property used just to get the above doc comment into the ES5 output and documentation generator
  1612. this.tagBuilder = cfg.tagBuilder;
  1613. }
  1614. return Matcher;
  1615. }());
  1616. /*
  1617. * This file builds and stores a library of the common regular expressions used
  1618. * by the Autolinker utility.
  1619. *
  1620. * Other regular expressions may exist ad-hoc, but these are generally the
  1621. * regular expressions that are shared between source files.
  1622. */
  1623. /**
  1624. * Regular expression to match upper and lowercase ASCII letters
  1625. */
  1626. var letterRe = /[A-Za-z]/;
  1627. /**
  1628. * Regular expression to match ASCII digits
  1629. */
  1630. var digitRe = /[\d]/;
  1631. /**
  1632. * Regular expression to match everything *except* ASCII digits
  1633. */
  1634. var nonDigitRe = /[\D]/;
  1635. /**
  1636. * Regular expression to match whitespace
  1637. */
  1638. var whitespaceRe = /\s/;
  1639. /**
  1640. * Regular expression to match quote characters
  1641. */
  1642. var quoteRe = /['"]/;
  1643. /**
  1644. * Regular expression to match the range of ASCII control characters (0-31), and
  1645. * the backspace char (127)
  1646. */
  1647. var controlCharsRe = /[\x00-\x1F\x7F]/;
  1648. /**
  1649. * The string form of a regular expression that would match all of the
  1650. * alphabetic ("letter") chars in the unicode character set when placed in a
  1651. * RegExp character class (`[]`). This includes all international alphabetic
  1652. * characters.
  1653. *
  1654. * These would be the characters matched by unicode regex engines `\p{L}`
  1655. * escape ("all letters").
  1656. *
  1657. * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
  1658. * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Letter'
  1659. * regex's bmp
  1660. *
  1661. * VERY IMPORTANT: This set of characters is defined inside of a Regular
  1662. * Expression literal rather than a string literal to prevent UglifyJS from
  1663. * compressing the unicode escape sequences into their actual unicode
  1664. * characters. If Uglify compresses these into the unicode characters
  1665. * themselves, this results in the error "Range out of order in character
  1666. * class" when these characters are used inside of a Regular Expression
  1667. * character class (`[]`). See usages of this const. Alternatively, we can set
  1668. * the UglifyJS option `ascii_only` to true for the build, but that doesn't
  1669. * help others who are pulling in Autolinker into their own build and running
  1670. * UglifyJS themselves.
  1671. */
  1672. var alphaCharsStr = /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/
  1673. .source; // see note in above variable description
  1674. /**
  1675. * The string form of a regular expression that would match all emoji characters
  1676. * Based on the emoji regex defined in this article: https://thekevinscott.com/emojis-in-javascript/
  1677. */
  1678. var emojiStr = /\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/
  1679. .source;
  1680. /**
  1681. * The string form of a regular expression that would match all of the
  1682. * combining mark characters in the unicode character set when placed in a
  1683. * RegExp character class (`[]`).
  1684. *
  1685. * These would be the characters matched by unicode regex engines `\p{M}`
  1686. * escape ("all marks").
  1687. *
  1688. * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
  1689. * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Mark'
  1690. * regex's bmp
  1691. *
  1692. * VERY IMPORTANT: This set of characters is defined inside of a Regular
  1693. * Expression literal rather than a string literal to prevent UglifyJS from
  1694. * compressing the unicode escape sequences into their actual unicode
  1695. * characters. If Uglify compresses these into the unicode characters
  1696. * themselves, this results in the error "Range out of order in character
  1697. * class" when these characters are used inside of a Regular Expression
  1698. * character class (`[]`). See usages of this const. Alternatively, we can set
  1699. * the UglifyJS option `ascii_only` to true for the build, but that doesn't
  1700. * help others who are pulling in Autolinker into their own build and running
  1701. * UglifyJS themselves.
  1702. */
  1703. var marksStr = /\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-\uFE2F/
  1704. .source; // see note in above variable description
  1705. /**
  1706. * The string form of a regular expression that would match all of the
  1707. * alphabetic ("letter") chars, emoji, and combining marks in the unicode character set
  1708. * when placed in a RegExp character class (`[]`). This includes all
  1709. * international alphabetic characters.
  1710. *
  1711. * These would be the characters matched by unicode regex engines `\p{L}\p{M}`
  1712. * escapes and emoji characters.
  1713. */
  1714. var alphaCharsAndMarksStr = alphaCharsStr + emojiStr + marksStr;
  1715. /**
  1716. * The string form of a regular expression that would match all of the
  1717. * decimal number chars in the unicode character set when placed in a RegExp
  1718. * character class (`[]`).
  1719. *
  1720. * These would be the characters matched by unicode regex engines `\p{Nd}`
  1721. * escape ("all decimal numbers")
  1722. *
  1723. * Taken from the XRegExp library: http://xregexp.com/ (thanks @https://github.com/slevithan)
  1724. * Specifically: http://xregexp.com/v/3.2.0/xregexp-all.js, the 'Decimal_Number'
  1725. * regex's bmp
  1726. *
  1727. * VERY IMPORTANT: This set of characters is defined inside of a Regular
  1728. * Expression literal rather than a string literal to prevent UglifyJS from
  1729. * compressing the unicode escape sequences into their actual unicode
  1730. * characters. If Uglify compresses these into the unicode characters
  1731. * themselves, this results in the error "Range out of order in character
  1732. * class" when these characters are used inside of a Regular Expression
  1733. * character class (`[]`). See usages of this const. Alternatively, we can set
  1734. * the UglifyJS option `ascii_only` to true for the build, but that doesn't
  1735. * help others who are pulling in Autolinker into their own build and running
  1736. * UglifyJS themselves.
  1737. */
  1738. var decimalNumbersStr = /0-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/
  1739. .source; // see note in above variable description
  1740. /**
  1741. * The string form of a regular expression that would match all of the
  1742. * letters and decimal number chars in the unicode character set when placed in
  1743. * a RegExp character class (`[]`).
  1744. *
  1745. * These would be the characters matched by unicode regex engines
  1746. * `[\p{L}\p{Nd}]` escape ("all letters and decimal numbers")
  1747. */
  1748. var alphaNumericCharsStr = alphaCharsAndMarksStr + decimalNumbersStr;
  1749. /**
  1750. * The string form of a regular expression that would match all of the
  1751. * letters, combining marks, and decimal number chars in the unicode character
  1752. * set when placed in a RegExp character class (`[]`).
  1753. *
  1754. * These would be the characters matched by unicode regex engines
  1755. * `[\p{L}\p{M}\p{Nd}]` escape ("all letters, combining marks, and decimal
  1756. * numbers")
  1757. */
  1758. var alphaNumericAndMarksCharsStr = alphaCharsAndMarksStr + decimalNumbersStr;
  1759. // Simplified IP regular expression
  1760. var ipStr = '(?:[' + decimalNumbersStr + ']{1,3}\\.){3}[' + decimalNumbersStr + ']{1,3}';
  1761. // Protected domain label which do not allow "-" character on the beginning and the end of a single label
  1762. var domainLabelStr = '[' + alphaNumericAndMarksCharsStr + '](?:[' + alphaNumericAndMarksCharsStr + '\\-]{0,61}[' + alphaNumericAndMarksCharsStr + '])?';
  1763. var getDomainLabelStr = function (group) {
  1764. return '(?=(' + domainLabelStr + '))\\' + group;
  1765. };
  1766. /**
  1767. * A function to match domain names of a URL or email address.
  1768. * Ex: 'google', 'yahoo', 'some-other-company', etc.
  1769. */
  1770. var getDomainNameStr = function (group) {
  1771. return '(?:' + getDomainLabelStr(group) + '(?:\\.' + getDomainLabelStr(group + 1) + '){0,126}|' + ipStr + ')';
  1772. };
  1773. /**
  1774. * A regular expression that is simply the character class of the characters
  1775. * that may be used in a domain name, minus the '-' or '.'
  1776. */
  1777. var domainNameCharRegex = new RegExp("[".concat(alphaNumericAndMarksCharsStr, "]"));
  1778. // NOTE: THIS IS A GENERATED FILE
  1779. // To update with the latest TLD list, run `npm run update-tld-regex` or `yarn update-tld-regex` (depending on which you have installed)
  1780. var tldRegex = /(?:xn--vermgensberatung-pwb|xn--vermgensberater-ctb|xn--clchc0ea0b2g2a9gcd|xn--w4r85el8fhu5dnra|northwesternmutual|travelersinsurance|vermögensberatung|xn--3oq18vl8pn36a|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--mgbaakc7dvf|xn--mgbc0a9azcg|xn--nqv7fs00ema|afamilycompany|americanfamily|bananarepublic|cancerresearch|cookingchannel|kerrylogistics|weatherchannel|xn--54b7fta0cc|xn--6qq986b3xl|xn--80aqecdr1a|xn--b4w605ferd|xn--fiq228c5hs|xn--h2breg3eve|xn--jlq480n2rg|xn--jlq61u9w7b|xn--mgba3a3ejt|xn--mgbaam7a8h|xn--mgbayh7gpa|xn--mgbbh1a71e|xn--mgbca7dzdo|xn--mgbi4ecexp|xn--mgbx4cd0ab|xn--rvc1e0am3e|international|lifeinsurance|travelchannel|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|foodnetwork|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|swiftcover|tatamotors|technology|university|vlaanderen|volkswagen|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|alfaromeo|allfinanz|amsterdam|analytics|aquarelle|barcelona|bloomberg|christmas|community|directory|education|equipment|fairwinds|financial|firestone|fresenius|frontdoor|furniture|goldpoint|hisamitsu|homedepot|homegoods|homesense|institute|insurance|kuokgroup|lancaster|landrover|lifestyle|marketing|marshalls|melbourne|microsoft|panasonic|passagens|pramerica|richardli|scjohnson|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|budapest|builders|business|capetown|catering|catholic|cipriani|cityeats|cleaning|clinique|clothing|commbank|computer|delivery|deloitte|democrat|diamonds|discount|discover|download|engineer|ericsson|etisalat|exchange|feedback|fidelity|firmdale|football|frontier|goodyear|grainger|graphics|guardian|hdfcbank|helsinki|holdings|hospital|infiniti|ipiranga|istanbul|jpmorgan|lighting|lundbeck|marriott|maserati|mckinsey|memorial|merckmsd|mortgage|observer|partners|pharmacy|pictures|plumbing|property|redstone|reliance|saarland|samsclub|security|services|shopping|showtime|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|avianca|banamex|bauhaus|bentley|bestbuy|booking|brother|bugatti|capital|caravan|careers|channel|charity|chintai|citadel|clubmed|college|cologne|comcast|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|hoteles|hotmail|hyundai|ismaili|jewelry|juniper|kitchen|komatsu|lacaixa|lanxess|lasalle|latrobe|leclerc|limited|lincoln|markets|monster|netbank|netflix|network|neustar|okinawa|oldnavy|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|tiffany|toshiba|trading|walmart|wanggou|watches|weather|website|wedding|whoswho|windows|winners|xfinity|yamaxun|youtube|zuerich|католик|اتصالات|البحرين|الجزائر|العليان|پاکستان|كاثوليك|இந்தியா|abarth|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|kinder|kindle|kosher|lancia|latino|lawyer|lefrak|living|locker|london|luxury|madrid|maison|makeup|market|mattel|mobile|monash|mormon|moscow|museum|mutual|nagoya|natura|nissan|nissay|norton|nowruz|office|olayan|online|oracle|orange|otsuka|pfizer|photos|physio|pictet|quebec|racing|realty|reisen|repair|report|review|rocher|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|vuelos|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|dabur|dance|deals|delta|drive|dubai|earth|edeka|email|epson|faith|fedex|final|forex|forum|gallo|games|gifts|gives|glade|glass|globo|gmail|green|gripe|group|gucci|guide|homes|honda|horse|house|hyatt|ikano|irish|jetzt|koeln|kyoto|lamer|lease|legal|lexus|lilly|linde|lipsy|lixil|loans|locus|lotte|lotto|macys|mango|media|miami|money|movie|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|adac|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|duck|dvag|erni|fage|fail|fans|farm|fast|fiat|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|hgtv|host|hsbc|icbc|ieee|imdb|immo|info|itau|java|jeep|jobs|jprs|kddi|kiwi|kpmg|kred|land|lego|lgbt|lidl|life|like|limo|link|live|loan|loft|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|raid|read|reit|rent|rest|rich|rmit|room|rsvp|ruhr|safe|sale|sarl|save|saxo|scot|seat|seek|sexy|shaw|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|cbs|ceo|cfa|cfd|com|cpa|crs|csc|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|off|one|ong|onl|ooo|org|ott|ovh|pay|pet|phd|pid|pin|pnc|pro|pru|pub|pwc|qvc|red|ren|ril|rio|rip|run|rwe|sap|sas|sbi|sbs|sca|scb|ses|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|ελ|ευ|бг|ею|рф|გე|닷넷|닷컴|삼성|한국|コム|世界|中信|中国|中國|企业|佛山|信息|健康|八卦|公司|公益|台湾|台灣|商城|商店|商标|嘉里|在线|大拿|娱乐|家電|广东|微博|慈善|手机|招聘|政务|政府|新闻|时尚|書籍|机构|游戏|澳門|点看|移动|网址|网店|网站|网络|联通|谷歌|购物|通販|集团|食品|餐厅|香港)/;
  1781. // For debugging: search for other "For debugging" lines
  1782. // import CliTable from 'cli-table';
  1783. // RegExp objects which are shared by all instances of EmailMatcher. These are
  1784. // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
  1785. // called multiple times, thus instantiating EmailMatcher and its RegExp
  1786. // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
  1787. // See descriptions of the properties where they are used for details about them
  1788. var localPartCharRegex = new RegExp("[".concat(alphaNumericAndMarksCharsStr, "!#$%&'*+/=?^_`{|}~-]"));
  1789. var strictTldRegex = new RegExp("^".concat(tldRegex.source, "$"));
  1790. /**
  1791. * @class Autolinker.matcher.Email
  1792. * @extends Autolinker.matcher.Matcher
  1793. *
  1794. * Matcher to find email matches in an input string.
  1795. *
  1796. * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details.
  1797. */
  1798. var EmailMatcher = /** @class */ (function (_super) {
  1799. __extends(EmailMatcher, _super);
  1800. function EmailMatcher() {
  1801. var _this = _super !== null && _super.apply(this, arguments) || this;
  1802. /**
  1803. * Valid characters that can be used in the "local" part of an email address,
  1804. * i.e. the "name" part of "name@site.com"
  1805. */
  1806. _this.localPartCharRegex = localPartCharRegex;
  1807. /**
  1808. * Stricter TLD regex which adds a beginning and end check to ensure
  1809. * the string is a valid TLD
  1810. */
  1811. _this.strictTldRegex = strictTldRegex;
  1812. return _this;
  1813. }
  1814. /**
  1815. * @inheritdoc
  1816. */
  1817. EmailMatcher.prototype.parseMatches = function (text) {
  1818. var tagBuilder = this.tagBuilder, localPartCharRegex = this.localPartCharRegex, strictTldRegex = this.strictTldRegex, matches = [], len = text.length, noCurrentEmailMatch = new CurrentEmailMatch();
  1819. // for matching a 'mailto:' prefix
  1820. var mailtoTransitions = {
  1821. 'm': 'a',
  1822. 'a': 'i',
  1823. 'i': 'l',
  1824. 'l': 't',
  1825. 't': 'o',
  1826. 'o': ':',
  1827. };
  1828. var charIdx = 0, state = 0 /* NonEmailMatch */, currentEmailMatch = noCurrentEmailMatch;
  1829. // For debugging: search for other "For debugging" lines
  1830. // const table = new CliTable( {
  1831. // head: [ 'charIdx', 'char', 'state', 'charIdx', 'currentEmailAddress.idx', 'hasDomainDot' ]
  1832. // } );
  1833. while (charIdx < len) {
  1834. var char = text.charAt(charIdx);
  1835. // For debugging: search for other "For debugging" lines
  1836. // table.push(
  1837. // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
  1838. // );
  1839. switch (state) {
  1840. case 0 /* NonEmailMatch */:
  1841. stateNonEmailAddress(char);
  1842. break;
  1843. case 1 /* Mailto */:
  1844. stateMailTo(text.charAt(charIdx - 1), char);
  1845. break;
  1846. case 2 /* LocalPart */:
  1847. stateLocalPart(char);
  1848. break;
  1849. case 3 /* LocalPartDot */:
  1850. stateLocalPartDot(char);
  1851. break;
  1852. case 4 /* AtSign */:
  1853. stateAtSign(char);
  1854. break;
  1855. case 5 /* DomainChar */:
  1856. stateDomainChar(char);
  1857. break;
  1858. case 6 /* DomainHyphen */:
  1859. stateDomainHyphen(char);
  1860. break;
  1861. case 7 /* DomainDot */:
  1862. stateDomainDot(char);
  1863. break;
  1864. default:
  1865. throwUnhandledCaseError(state);
  1866. }
  1867. // For debugging: search for other "For debugging" lines
  1868. // table.push(
  1869. // [ charIdx, char, State[ state ], charIdx, currentEmailAddress.idx, currentEmailAddress.hasDomainDot ]
  1870. // );
  1871. charIdx++;
  1872. }
  1873. // Capture any valid match at the end of the string
  1874. captureMatchIfValidAndReset();
  1875. // For debugging: search for other "For debugging" lines
  1876. //console.log( '\n' + table.toString() );
  1877. return matches;
  1878. // Handles the state when we're not in an email address
  1879. function stateNonEmailAddress(char) {
  1880. if (char === 'm') {
  1881. beginEmailMatch(1 /* Mailto */);
  1882. }
  1883. else if (localPartCharRegex.test(char)) {
  1884. beginEmailMatch();
  1885. }
  1886. else ;
  1887. }
  1888. // Handles if we're reading a 'mailto:' prefix on the string
  1889. function stateMailTo(prevChar, char) {
  1890. if (prevChar === ':') {
  1891. // We've reached the end of the 'mailto:' prefix
  1892. if (localPartCharRegex.test(char)) {
  1893. state = 2 /* LocalPart */;
  1894. currentEmailMatch = new CurrentEmailMatch(__assign(__assign({}, currentEmailMatch), { hasMailtoPrefix: true }));
  1895. }
  1896. else {
  1897. // we've matched 'mailto:' but didn't get anything meaningful
  1898. // immediately afterwards (for example, we encountered a
  1899. // space character, or an '@' character which formed 'mailto:@'
  1900. resetToNonEmailMatchState();
  1901. }
  1902. }
  1903. else if (mailtoTransitions[prevChar] === char) ;
  1904. else if (localPartCharRegex.test(char)) {
  1905. // We we're reading a prefix of 'mailto:', but encountered a
  1906. // different character that didn't continue the prefix
  1907. state = 2 /* LocalPart */;
  1908. }
  1909. else if (char === '.') {
  1910. // We we're reading a prefix of 'mailto:', but encountered a
  1911. // dot character
  1912. state = 3 /* LocalPartDot */;
  1913. }
  1914. else if (char === '@') {
  1915. // We we're reading a prefix of 'mailto:', but encountered a
  1916. // an @ character
  1917. state = 4 /* AtSign */;
  1918. }
  1919. else {
  1920. // not an email address character, return to "NonEmailAddress" state
  1921. resetToNonEmailMatchState();
  1922. }
  1923. }
  1924. // Handles the state when we're currently in the "local part" of an
  1925. // email address (as opposed to the "domain part")
  1926. function stateLocalPart(char) {
  1927. if (char === '.') {
  1928. state = 3 /* LocalPartDot */;
  1929. }
  1930. else if (char === '@') {
  1931. state = 4 /* AtSign */;
  1932. }
  1933. else if (localPartCharRegex.test(char)) ;
  1934. else {
  1935. // not an email address character, return to "NonEmailAddress" state
  1936. resetToNonEmailMatchState();
  1937. }
  1938. }
  1939. // Handles the state where we've read
  1940. function stateLocalPartDot(char) {
  1941. if (char === '.') {
  1942. // We read a second '.' in a row, not a valid email address
  1943. // local part
  1944. resetToNonEmailMatchState();
  1945. }
  1946. else if (char === '@') {
  1947. // We read the '@' character immediately after a dot ('.'), not
  1948. // an email address
  1949. resetToNonEmailMatchState();
  1950. }
  1951. else if (localPartCharRegex.test(char)) {
  1952. state = 2 /* LocalPart */;
  1953. }
  1954. else {
  1955. // Anything else, not an email address
  1956. resetToNonEmailMatchState();
  1957. }
  1958. }
  1959. function stateAtSign(char) {
  1960. if (domainNameCharRegex.test(char)) {
  1961. state = 5 /* DomainChar */;
  1962. }
  1963. else {
  1964. // Anything else, not an email address
  1965. resetToNonEmailMatchState();
  1966. }
  1967. }
  1968. function stateDomainChar(char) {
  1969. if (char === '.') {
  1970. state = 7 /* DomainDot */;
  1971. }
  1972. else if (char === '-') {
  1973. state = 6 /* DomainHyphen */;
  1974. }
  1975. else if (domainNameCharRegex.test(char)) ;
  1976. else {
  1977. // Anything else, we potentially matched if the criteria has
  1978. // been met
  1979. captureMatchIfValidAndReset();
  1980. }
  1981. }
  1982. function stateDomainHyphen(char) {
  1983. if (char === '-' || char === '.') {
  1984. // Not valid to have two hyphens ("--") or hypen+dot ("-.")
  1985. captureMatchIfValidAndReset();
  1986. }
  1987. else if (domainNameCharRegex.test(char)) {
  1988. state = 5 /* DomainChar */;
  1989. }
  1990. else {
  1991. // Anything else
  1992. captureMatchIfValidAndReset();
  1993. }
  1994. }
  1995. function stateDomainDot(char) {
  1996. if (char === '.' || char === '-') {
  1997. // not valid to have two dots ("..") or dot+hypen (".-")
  1998. captureMatchIfValidAndReset();
  1999. }
  2000. else if (domainNameCharRegex.test(char)) {
  2001. state = 5 /* DomainChar */;
  2002. // After having read a '.' and then a valid domain character,
  2003. // we now know that the domain part of the email is valid, and
  2004. // we have found at least a partial EmailMatch (however, the
  2005. // email address may have additional characters from this point)
  2006. currentEmailMatch = new CurrentEmailMatch(__assign(__assign({}, currentEmailMatch), { hasDomainDot: true }));
  2007. }
  2008. else {
  2009. // Anything else
  2010. captureMatchIfValidAndReset();
  2011. }
  2012. }
  2013. function beginEmailMatch(newState) {
  2014. if (newState === void 0) { newState = 2 /* LocalPart */; }
  2015. state = newState;
  2016. currentEmailMatch = new CurrentEmailMatch({ idx: charIdx });
  2017. }
  2018. function resetToNonEmailMatchState() {
  2019. state = 0 /* NonEmailMatch */;
  2020. currentEmailMatch = noCurrentEmailMatch;
  2021. }
  2022. /*
  2023. * Captures the current email address as an EmailMatch if it's valid,
  2024. * and resets the state to read another email address.
  2025. */
  2026. function captureMatchIfValidAndReset() {
  2027. if (currentEmailMatch.hasDomainDot) { // we need at least one dot in the domain to be considered a valid email address
  2028. var matchedText = text.slice(currentEmailMatch.idx, charIdx);
  2029. // If we read a '.' or '-' char that ended the email address
  2030. // (valid domain name characters, but only valid email address
  2031. // characters if they are followed by something else), strip
  2032. // it off now
  2033. if (/[-.]$/.test(matchedText)) {
  2034. matchedText = matchedText.slice(0, -1);
  2035. }
  2036. var emailAddress = currentEmailMatch.hasMailtoPrefix
  2037. ? matchedText.slice('mailto:'.length)
  2038. : matchedText;
  2039. // if the email address has a valid TLD, add it to the list of matches
  2040. if (doesEmailHaveValidTld(emailAddress)) {
  2041. matches.push(new EmailMatch({
  2042. tagBuilder: tagBuilder,
  2043. matchedText: matchedText,
  2044. offset: currentEmailMatch.idx,
  2045. email: emailAddress
  2046. }));
  2047. }
  2048. }
  2049. resetToNonEmailMatchState();
  2050. /**
  2051. * Determines if the given email address has a valid TLD or not
  2052. * @param {string} emailAddress - email address
  2053. * @return {Boolean} - true is email have valid TLD, false otherwise
  2054. */
  2055. function doesEmailHaveValidTld(emailAddress) {
  2056. var emailAddressTld = emailAddress.split('.').pop() || '';
  2057. var emailAddressNormalized = emailAddressTld.toLowerCase();
  2058. var isValidTld = strictTldRegex.test(emailAddressNormalized);
  2059. return isValidTld;
  2060. }
  2061. }
  2062. };
  2063. return EmailMatcher;
  2064. }(Matcher));
  2065. var CurrentEmailMatch = /** @class */ (function () {
  2066. function CurrentEmailMatch(cfg) {
  2067. if (cfg === void 0) { cfg = {}; }
  2068. this.idx = cfg.idx !== undefined ? cfg.idx : -1;
  2069. this.hasMailtoPrefix = !!cfg.hasMailtoPrefix;
  2070. this.hasDomainDot = !!cfg.hasDomainDot;
  2071. }
  2072. return CurrentEmailMatch;
  2073. }());
  2074. /**
  2075. * @private
  2076. * @class Autolinker.matcher.UrlMatchValidator
  2077. * @singleton
  2078. *
  2079. * Used by Autolinker to filter out false URL positives from the
  2080. * {@link Autolinker.matcher.Url UrlMatcher}.
  2081. *
  2082. * Due to the limitations of regular expressions (including the missing feature
  2083. * of look-behinds in JS regular expressions), we cannot always determine the
  2084. * validity of a given match. This class applies a bit of additional logic to
  2085. * filter out any false positives that have been matched by the
  2086. * {@link Autolinker.matcher.Url UrlMatcher}.
  2087. */
  2088. var UrlMatchValidator = /** @class */ (function () {
  2089. function UrlMatchValidator() {
  2090. }
  2091. /**
  2092. * Determines if a given URL match found by the {@link Autolinker.matcher.Url UrlMatcher}
  2093. * is valid. Will return `false` for:
  2094. *
  2095. * 1) URL matches which do not have at least have one period ('.') in the
  2096. * domain name (effectively skipping over matches like "abc:def").
  2097. * However, URL matches with a protocol will be allowed (ex: 'http://localhost')
  2098. * 2) URL matches which do not have at least one word character in the
  2099. * domain name (effectively skipping over matches like "git:1.0").
  2100. * However, URL matches with a protocol will be allowed (ex: 'intra-net://271219.76')
  2101. * 3) A protocol-relative url match (a URL beginning with '//') whose
  2102. * previous character is a word character (effectively skipping over
  2103. * strings like "abc//google.com")
  2104. *
  2105. * Otherwise, returns `true`.
  2106. *
  2107. * @param {String} urlMatch The matched URL, if there was one. Will be an
  2108. * empty string if the match is not a URL match.
  2109. * @param {String} protocolUrlMatch The match URL string for a protocol
  2110. * match. Ex: 'http://yahoo.com'. This is used to match something like
  2111. * 'http://localhost', where we won't double check that the domain name
  2112. * has at least one '.' in it.
  2113. * @return {Boolean} `true` if the match given is valid and should be
  2114. * processed, or `false` if the match is invalid and/or should just not be
  2115. * processed.
  2116. */
  2117. UrlMatchValidator.isValid = function (urlMatch, protocolUrlMatch) {
  2118. if ((protocolUrlMatch && !this.isValidUriScheme(protocolUrlMatch)) ||
  2119. this.urlMatchDoesNotHaveProtocolOrDot(urlMatch, protocolUrlMatch) || // At least one period ('.') must exist in the URL match for us to consider it an actual URL, *unless* it was a full protocol match (like 'http://localhost')
  2120. (this.urlMatchDoesNotHaveAtLeastOneWordChar(urlMatch, protocolUrlMatch) && // At least one letter character must exist in the domain name after a protocol match. Ex: skip over something like "git:1.0"
  2121. !this.isValidIpAddress(urlMatch)) || // Except if it's an IP address
  2122. this.containsMultipleDots(urlMatch)) {
  2123. return false;
  2124. }
  2125. return true;
  2126. };
  2127. UrlMatchValidator.isValidIpAddress = function (uriSchemeMatch) {
  2128. var newRegex = new RegExp(this.hasFullProtocolRegex.source + this.ipRegex.source);
  2129. var uriScheme = uriSchemeMatch.match(newRegex);
  2130. return uriScheme !== null;
  2131. };
  2132. UrlMatchValidator.containsMultipleDots = function (urlMatch) {
  2133. var stringBeforeSlash = urlMatch;
  2134. if (this.hasFullProtocolRegex.test(urlMatch)) {
  2135. stringBeforeSlash = urlMatch.split('://')[1];
  2136. }
  2137. return stringBeforeSlash.split('/')[0].indexOf("..") > -1;
  2138. };
  2139. /**
  2140. * Determines if the URI scheme is a valid scheme to be autolinked. Returns
  2141. * `false` if the scheme is 'javascript:' or 'vbscript:'
  2142. *
  2143. * @private
  2144. * @param {String} uriSchemeMatch The match URL string for a full URI scheme
  2145. * match. Ex: 'http://yahoo.com' or 'mailto:a@a.com'.
  2146. * @return {Boolean} `true` if the scheme is a valid one, `false` otherwise.
  2147. */
  2148. UrlMatchValidator.isValidUriScheme = function (uriSchemeMatch) {
  2149. var uriSchemeMatchArr = uriSchemeMatch.match(this.uriSchemeRegex), uriScheme = uriSchemeMatchArr && uriSchemeMatchArr[0].toLowerCase();
  2150. return (uriScheme !== 'javascript:' && uriScheme !== 'vbscript:');
  2151. };
  2152. /**
  2153. * Determines if a URL match does not have either:
  2154. *
  2155. * a) a full protocol (i.e. 'http://'), or
  2156. * b) at least one dot ('.') in the domain name (for a non-full-protocol
  2157. * match).
  2158. *
  2159. * Either situation is considered an invalid URL (ex: 'git:d' does not have
  2160. * either the '://' part, or at least one dot in the domain name. If the
  2161. * match was 'git:abc.com', we would consider this valid.)
  2162. *
  2163. * @private
  2164. * @param {String} urlMatch The matched URL, if there was one. Will be an
  2165. * empty string if the match is not a URL match.
  2166. * @param {String} protocolUrlMatch The match URL string for a protocol
  2167. * match. Ex: 'http://yahoo.com'. This is used to match something like
  2168. * 'http://localhost', where we won't double check that the domain name
  2169. * has at least one '.' in it.
  2170. * @return {Boolean} `true` if the URL match does not have a full protocol,
  2171. * or at least one dot ('.') in a non-full-protocol match.
  2172. */
  2173. UrlMatchValidator.urlMatchDoesNotHaveProtocolOrDot = function (urlMatch, protocolUrlMatch) {
  2174. return (!!urlMatch && (!protocolUrlMatch || !this.hasFullProtocolRegex.test(protocolUrlMatch)) && urlMatch.indexOf('.') === -1);
  2175. };
  2176. /**
  2177. * Determines if a URL match does not have either:
  2178. *
  2179. * a) a full protocol (i.e. 'http://'), or
  2180. * b) at least one word character after the protocol (i.e. in the domain name)
  2181. *
  2182. * At least one letter character must exist in the domain name after a
  2183. * protocol match. Ex: skip over something like "git:1.0"
  2184. *
  2185. * @private
  2186. * @param {String} urlMatch The matched URL, if there was one. Will be an
  2187. * empty string if the match is not a URL match.
  2188. * @param {String} protocolUrlMatch The match URL string for a protocol
  2189. * match. Ex: 'http://yahoo.com'. This is used to know whether or not we
  2190. * have a protocol in the URL string, in order to check for a word
  2191. * character after the protocol separator (':').
  2192. * @return {Boolean} `true` if the URL match does not have a full protocol, or
  2193. * at least one word character in it, `false` otherwise.
  2194. */
  2195. UrlMatchValidator.urlMatchDoesNotHaveAtLeastOneWordChar = function (urlMatch, protocolUrlMatch) {
  2196. if (urlMatch && protocolUrlMatch) {
  2197. return !this.hasFullProtocolRegex.test(protocolUrlMatch) && !this.hasWordCharAfterProtocolRegex.test(urlMatch);
  2198. }
  2199. else {
  2200. return false;
  2201. }
  2202. };
  2203. /**
  2204. * Regex to test for a full protocol, with the two trailing slashes. Ex: 'http://'
  2205. *
  2206. * @private
  2207. * @property {RegExp} hasFullProtocolRegex
  2208. */
  2209. UrlMatchValidator.hasFullProtocolRegex = /^[A-Za-z][-.+A-Za-z0-9]*:\/\//;
  2210. /**
  2211. * Regex to find the URI scheme, such as 'mailto:'.
  2212. *
  2213. * This is used to filter out 'javascript:' and 'vbscript:' schemes.
  2214. *
  2215. * @private
  2216. * @property {RegExp} uriSchemeRegex
  2217. */
  2218. UrlMatchValidator.uriSchemeRegex = /^[A-Za-z][-.+A-Za-z0-9]*:/;
  2219. /**
  2220. * Regex to determine if at least one word char exists after the protocol (i.e. after the ':')
  2221. *
  2222. * @private
  2223. * @property {RegExp} hasWordCharAfterProtocolRegex
  2224. */
  2225. UrlMatchValidator.hasWordCharAfterProtocolRegex = new RegExp(":[^\\s]*?[" + alphaCharsStr + "]");
  2226. /**
  2227. * Regex to determine if the string is a valid IP address
  2228. *
  2229. * @private
  2230. * @property {RegExp} ipRegex
  2231. */
  2232. UrlMatchValidator.ipRegex = /[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?\.[0-9][0-9]?[0-9]?(:[0-9]*)?\/?$/;
  2233. return UrlMatchValidator;
  2234. }());
  2235. // RegExp objects which are shared by all instances of UrlMatcher. These are
  2236. // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
  2237. // called multiple times, thus instantiating UrlMatcher and its RegExp
  2238. // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
  2239. // See descriptions of the properties where they are used for details about them
  2240. var matcherRegex$1 = (function () {
  2241. var schemeRegex = /(?:[A-Za-z][-.+A-Za-z0-9]{0,63}:(?![A-Za-z][-.+A-Za-z0-9]{0,63}:\/\/)(?!\d+\/?)(?:\/\/)?)/, // match protocol, allow in format "http://" or "mailto:". However, do not match the first part of something like 'link:http://www.google.com' (i.e. don't match "link:"). Also, make sure we don't interpret 'google.com:8000' as if 'google.com' was a protocol here (i.e. ignore a trailing port number in this regex)
  2242. wwwRegex = /(?:www\.)/, // starting with 'www.'
  2243. // Allow optional path, query string, and hash anchor, not ending in the following characters: "?!:,.;"
  2244. // http://blog.codinghorror.com/the-problem-with-urls/
  2245. urlSuffixRegex = new RegExp('[/?#](?:[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]{}?!:,.;^\u2713]*[' + alphaNumericAndMarksCharsStr + '\\-+&@#/%=~_()|\'$*\\[\\]{}\u2713])?');
  2246. return new RegExp([
  2247. '(?:',
  2248. '(',
  2249. schemeRegex.source,
  2250. getDomainNameStr(2),
  2251. ')',
  2252. '|',
  2253. '(',
  2254. '(//)?',
  2255. wwwRegex.source,
  2256. getDomainNameStr(6),
  2257. ')',
  2258. '|',
  2259. '(',
  2260. '(//)?',
  2261. getDomainNameStr(10) + '\\.',
  2262. tldRegex.source,
  2263. '(?![-' + alphaNumericCharsStr + '])',
  2264. ')',
  2265. ')',
  2266. '(?::[0-9]+)?',
  2267. '(?:' + urlSuffixRegex.source + ')?' // match for path, query string, and/or hash anchor - optional
  2268. ].join(""), 'gi');
  2269. })();
  2270. var wordCharRegExp = new RegExp('[' + alphaNumericAndMarksCharsStr + ']');
  2271. /**
  2272. * @class Autolinker.matcher.Url
  2273. * @extends Autolinker.matcher.Matcher
  2274. *
  2275. * Matcher to find URL matches in an input string.
  2276. *
  2277. * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more details.
  2278. */
  2279. var UrlMatcher = /** @class */ (function (_super) {
  2280. __extends(UrlMatcher, _super);
  2281. /**
  2282. * @method constructor
  2283. * @param {Object} cfg The configuration properties for the Match instance,
  2284. * specified in an Object (map).
  2285. */
  2286. function UrlMatcher(cfg) {
  2287. var _this = _super.call(this, cfg) || this;
  2288. /**
  2289. * @cfg {Object} stripPrefix (required)
  2290. *
  2291. * The Object form of {@link Autolinker#cfg-stripPrefix}.
  2292. */
  2293. _this.stripPrefix = { scheme: true, www: true }; // default value just to get the above doc comment in the ES5 output and documentation generator
  2294. /**
  2295. * @cfg {Boolean} stripTrailingSlash (required)
  2296. * @inheritdoc Autolinker#stripTrailingSlash
  2297. */
  2298. _this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  2299. /**
  2300. * @cfg {Boolean} decodePercentEncoding (required)
  2301. * @inheritdoc Autolinker#decodePercentEncoding
  2302. */
  2303. _this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  2304. /**
  2305. * @protected
  2306. * @property {RegExp} matcherRegex
  2307. *
  2308. * The regular expression to match URLs with an optional scheme, port
  2309. * number, path, query string, and hash anchor.
  2310. *
  2311. * Example matches:
  2312. *
  2313. * http://google.com
  2314. * www.google.com
  2315. * google.com/path/to/file?q1=1&q2=2#myAnchor
  2316. *
  2317. *
  2318. * This regular expression will have the following capturing groups:
  2319. *
  2320. * 1. Group that matches a scheme-prefixed URL (i.e. 'http://google.com').
  2321. * This is used to match scheme URLs with just a single word, such as
  2322. * 'http://localhost', where we won't double check that the domain name
  2323. * has at least one dot ('.') in it.
  2324. * 2. Group that matches a 'www.' prefixed URL. This is only matched if the
  2325. * 'www.' text was not prefixed by a scheme (i.e.: not prefixed by
  2326. * 'http://', 'ftp:', etc.)
  2327. * 3. A protocol-relative ('//') match for the case of a 'www.' prefixed
  2328. * URL. Will be an empty string if it is not a protocol-relative match.
  2329. * We need to know the character before the '//' in order to determine
  2330. * if it is a valid match or the // was in a string we don't want to
  2331. * auto-link.
  2332. * 4. Group that matches a known TLD (top level domain), when a scheme
  2333. * or 'www.'-prefixed domain is not matched.
  2334. * 5. A protocol-relative ('//') match for the case of a known TLD prefixed
  2335. * URL. Will be an empty string if it is not a protocol-relative match.
  2336. * See #3 for more info.
  2337. */
  2338. _this.matcherRegex = matcherRegex$1;
  2339. /**
  2340. * A regular expression to use to check the character before a protocol-relative
  2341. * URL match. We don't want to match a protocol-relative URL if it is part
  2342. * of another word.
  2343. *
  2344. * For example, we want to match something like "Go to: //google.com",
  2345. * but we don't want to match something like "abc//google.com"
  2346. *
  2347. * This regular expression is used to test the character before the '//'.
  2348. *
  2349. * @protected
  2350. * @type {RegExp} wordCharRegExp
  2351. */
  2352. _this.wordCharRegExp = wordCharRegExp;
  2353. _this.stripPrefix = cfg.stripPrefix;
  2354. _this.stripTrailingSlash = cfg.stripTrailingSlash;
  2355. _this.decodePercentEncoding = cfg.decodePercentEncoding;
  2356. return _this;
  2357. }
  2358. /**
  2359. * @inheritdoc
  2360. */
  2361. UrlMatcher.prototype.parseMatches = function (text) {
  2362. var matcherRegex = this.matcherRegex, stripPrefix = this.stripPrefix, stripTrailingSlash = this.stripTrailingSlash, decodePercentEncoding = this.decodePercentEncoding, tagBuilder = this.tagBuilder, matches = [], match;
  2363. var _loop_1 = function () {
  2364. var matchStr = match[0], schemeUrlMatch = match[1], wwwUrlMatch = match[4], wwwProtocolRelativeMatch = match[5],
  2365. //tldUrlMatch = match[ 8 ], -- not needed at the moment
  2366. tldProtocolRelativeMatch = match[9], offset = match.index, protocolRelativeMatch = wwwProtocolRelativeMatch || tldProtocolRelativeMatch, prevChar = text.charAt(offset - 1);
  2367. if (!UrlMatchValidator.isValid(matchStr, schemeUrlMatch)) {
  2368. return "continue";
  2369. }
  2370. // If the match is preceded by an '@' character, then it is either
  2371. // an email address or a username. Skip these types of matches.
  2372. if (offset > 0 && prevChar === '@') {
  2373. return "continue";
  2374. }
  2375. // If it's a protocol-relative '//' match, but the character before the '//'
  2376. // was a word character (i.e. a letter/number), then we found the '//' in the
  2377. // middle of another word (such as "asdf//asdf.com"). In this case, skip the
  2378. // match.
  2379. if (offset > 0 && protocolRelativeMatch && this_1.wordCharRegExp.test(prevChar)) {
  2380. return "continue";
  2381. }
  2382. // If the URL ends with a question mark, don't include the question
  2383. // mark as part of the URL. We'll assume the question mark was the
  2384. // end of a sentence, such as: "Going to google.com?"
  2385. if (/\?$/.test(matchStr)) {
  2386. matchStr = matchStr.substr(0, matchStr.length - 1);
  2387. }
  2388. // Handle a closing parenthesis or square bracket at the end of the
  2389. // match, and exclude it if there is not a matching open parenthesis
  2390. // or square bracket in the match itself.
  2391. if (this_1.matchHasUnbalancedClosingParen(matchStr)) {
  2392. matchStr = matchStr.substr(0, matchStr.length - 1); // remove the trailing ")"
  2393. }
  2394. else {
  2395. // Handle an invalid character after the TLD
  2396. var pos = this_1.matchHasInvalidCharAfterTld(matchStr, schemeUrlMatch);
  2397. if (pos > -1) {
  2398. matchStr = matchStr.substr(0, pos); // remove the trailing invalid chars
  2399. }
  2400. }
  2401. // The autolinker accepts many characters in a url's scheme (like `fake://test.com`).
  2402. // However, in cases where a URL is missing whitespace before an obvious link,
  2403. // (for example: `nowhitespacehttp://www.test.com`), we only want the match to start
  2404. // at the http:// part. We will check if the match contains a common scheme and then
  2405. // shift the match to start from there.
  2406. var foundCommonScheme = ['http://', 'https://'].find(function (commonScheme) { return !!schemeUrlMatch && schemeUrlMatch.indexOf(commonScheme) !== -1; });
  2407. if (foundCommonScheme) {
  2408. // If we found an overmatched URL, we want to find the index
  2409. // of where the match should start and shift the match to
  2410. // start from the beginning of the common scheme
  2411. var indexOfSchemeStart = matchStr.indexOf(foundCommonScheme);
  2412. matchStr = matchStr.substr(indexOfSchemeStart);
  2413. schemeUrlMatch = schemeUrlMatch.substr(indexOfSchemeStart);
  2414. offset = offset + indexOfSchemeStart;
  2415. }
  2416. var urlMatchType = schemeUrlMatch ? 'scheme' : (wwwUrlMatch ? 'www' : 'tld'), protocolUrlMatch = !!schemeUrlMatch;
  2417. matches.push(new UrlMatch({
  2418. tagBuilder: tagBuilder,
  2419. matchedText: matchStr,
  2420. offset: offset,
  2421. urlMatchType: urlMatchType,
  2422. url: matchStr,
  2423. protocolUrlMatch: protocolUrlMatch,
  2424. protocolRelativeMatch: !!protocolRelativeMatch,
  2425. stripPrefix: stripPrefix,
  2426. stripTrailingSlash: stripTrailingSlash,
  2427. decodePercentEncoding: decodePercentEncoding,
  2428. }));
  2429. };
  2430. var this_1 = this;
  2431. while ((match = matcherRegex.exec(text)) !== null) {
  2432. _loop_1();
  2433. }
  2434. return matches;
  2435. };
  2436. /**
  2437. * Determines if a match found has an unmatched closing parenthesis,
  2438. * square bracket or curly bracket. If so, the symbol will be removed
  2439. * from the match itself, and appended after the generated anchor tag.
  2440. *
  2441. * A match may have an extra closing parenthesis at the end of the match
  2442. * because the regular expression must include parenthesis for URLs such as
  2443. * "wikipedia.com/something_(disambiguation)", which should be auto-linked.
  2444. *
  2445. * However, an extra parenthesis *will* be included when the URL itself is
  2446. * wrapped in parenthesis, such as in the case of:
  2447. * "(wikipedia.com/something_(disambiguation))"
  2448. * In this case, the last closing parenthesis should *not* be part of the
  2449. * URL itself, and this method will return `true`.
  2450. *
  2451. * For square brackets in URLs such as in PHP arrays, the same behavior as
  2452. * parenthesis discussed above should happen:
  2453. * "[http://www.example.com/foo.php?bar[]=1&bar[]=2&bar[]=3]"
  2454. * The closing square bracket should not be part of the URL itself, and this
  2455. * method will return `true`.
  2456. *
  2457. * @protected
  2458. * @param {String} matchStr The full match string from the {@link #matcherRegex}.
  2459. * @return {Boolean} `true` if there is an unbalanced closing parenthesis or
  2460. * square bracket at the end of the `matchStr`, `false` otherwise.
  2461. */
  2462. UrlMatcher.prototype.matchHasUnbalancedClosingParen = function (matchStr) {
  2463. var endChar = matchStr.charAt(matchStr.length - 1);
  2464. var startChar;
  2465. if (endChar === ')') {
  2466. startChar = '(';
  2467. }
  2468. else if (endChar === ']') {
  2469. startChar = '[';
  2470. }
  2471. else if (endChar === '}') {
  2472. startChar = '{';
  2473. }
  2474. else {
  2475. return false; // not a close parenthesis or square bracket
  2476. }
  2477. // Find if there are the same number of open braces as close braces in
  2478. // the URL string, minus the last character (which we have already
  2479. // determined to be either ')', ']' or '}'
  2480. var numOpenBraces = 0;
  2481. for (var i = 0, len = matchStr.length - 1; i < len; i++) {
  2482. var char = matchStr.charAt(i);
  2483. if (char === startChar) {
  2484. numOpenBraces++;
  2485. }
  2486. else if (char === endChar) {
  2487. numOpenBraces = Math.max(numOpenBraces - 1, 0);
  2488. }
  2489. }
  2490. // If the number of open braces matches the number of close braces in
  2491. // the URL minus the last character, then the match has *unbalanced*
  2492. // braces because of the last character. Example of unbalanced braces
  2493. // from the regex match:
  2494. // "http://example.com?a[]=1]"
  2495. if (numOpenBraces === 0) {
  2496. return true;
  2497. }
  2498. return false;
  2499. };
  2500. /**
  2501. * Determine if there's an invalid character after the TLD in a URL. Valid
  2502. * characters after TLD are ':/?#'. Exclude scheme matched URLs from this
  2503. * check.
  2504. *
  2505. * @protected
  2506. * @param {String} urlMatch The matched URL, if there was one. Will be an
  2507. * empty string if the match is not a URL match.
  2508. * @param {String} schemeUrlMatch The match URL string for a scheme
  2509. * match. Ex: 'http://yahoo.com'. This is used to match something like
  2510. * 'http://localhost', where we won't double check that the domain name
  2511. * has at least one '.' in it.
  2512. * @return {Number} the position where the invalid character was found. If
  2513. * no such character was found, returns -1
  2514. */
  2515. UrlMatcher.prototype.matchHasInvalidCharAfterTld = function (urlMatch, schemeUrlMatch) {
  2516. if (!urlMatch) {
  2517. return -1;
  2518. }
  2519. var offset = 0;
  2520. if (schemeUrlMatch) {
  2521. offset = urlMatch.indexOf(':');
  2522. urlMatch = urlMatch.slice(offset);
  2523. }
  2524. var re = new RegExp("^((.?\/\/)?[-." + alphaNumericAndMarksCharsStr + "]*[-" + alphaNumericAndMarksCharsStr + "]\\.[-" + alphaNumericAndMarksCharsStr + "]+)");
  2525. var res = re.exec(urlMatch);
  2526. if (res === null) {
  2527. return -1;
  2528. }
  2529. offset += res[1].length;
  2530. urlMatch = urlMatch.slice(res[1].length);
  2531. if (/^[^-.A-Za-z0-9:\/?#]/.test(urlMatch)) {
  2532. return offset;
  2533. }
  2534. return -1;
  2535. };
  2536. return UrlMatcher;
  2537. }(Matcher));
  2538. // RegExp objects which are shared by all instances of HashtagMatcher. These are
  2539. // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
  2540. // called multiple times, thus instantiating HashtagMatcher and its RegExp
  2541. // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
  2542. // See descriptions of the properties where they are used for details about them
  2543. var matcherRegex = new RegExp("#[_".concat(alphaNumericAndMarksCharsStr, "]{1,139}(?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 139 characters
  2544. var nonWordCharRegex$1 = new RegExp('[^' + alphaNumericAndMarksCharsStr + ']');
  2545. /**
  2546. * @class Autolinker.matcher.Hashtag
  2547. * @extends Autolinker.matcher.Matcher
  2548. *
  2549. * Matcher to find HashtagMatch matches in an input string.
  2550. */
  2551. var HashtagMatcher = /** @class */ (function (_super) {
  2552. __extends(HashtagMatcher, _super);
  2553. /**
  2554. * @method constructor
  2555. * @param {Object} cfg The configuration properties for the Match instance,
  2556. * specified in an Object (map).
  2557. */
  2558. function HashtagMatcher(cfg) {
  2559. var _this = _super.call(this, cfg) || this;
  2560. /**
  2561. * @cfg {String} serviceName
  2562. *
  2563. * The service to point hashtag matches to. See {@link Autolinker#hashtag}
  2564. * for available values.
  2565. */
  2566. _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
  2567. /**
  2568. * The regular expression to match Hashtags. Example match:
  2569. *
  2570. * #asdf
  2571. *
  2572. * @protected
  2573. * @property {RegExp} matcherRegex
  2574. */
  2575. _this.matcherRegex = matcherRegex;
  2576. /**
  2577. * The regular expression to use to check the character before a username match to
  2578. * make sure we didn't accidentally match an email address.
  2579. *
  2580. * For example, the string "asdf@asdf.com" should not match "@asdf" as a username.
  2581. *
  2582. * @protected
  2583. * @property {RegExp} nonWordCharRegex
  2584. */
  2585. _this.nonWordCharRegex = nonWordCharRegex$1;
  2586. _this.serviceName = cfg.serviceName;
  2587. return _this;
  2588. }
  2589. /**
  2590. * @inheritdoc
  2591. */
  2592. HashtagMatcher.prototype.parseMatches = function (text) {
  2593. var matcherRegex = this.matcherRegex, nonWordCharRegex = this.nonWordCharRegex, serviceName = this.serviceName, tagBuilder = this.tagBuilder, matches = [], match;
  2594. while ((match = matcherRegex.exec(text)) !== null) {
  2595. var offset = match.index, prevChar = text.charAt(offset - 1);
  2596. // If we found the match at the beginning of the string, or we found the match
  2597. // and there is a whitespace char in front of it (meaning it is not a '#' char
  2598. // in the middle of a word), then it is a hashtag match.
  2599. if (offset === 0 || nonWordCharRegex.test(prevChar)) {
  2600. var matchedText = match[0], hashtag = match[0].slice(1); // strip off the '#' character at the beginning
  2601. matches.push(new HashtagMatch({
  2602. tagBuilder: tagBuilder,
  2603. matchedText: matchedText,
  2604. offset: offset,
  2605. serviceName: serviceName,
  2606. hashtag: hashtag
  2607. }));
  2608. }
  2609. }
  2610. return matches;
  2611. };
  2612. return HashtagMatcher;
  2613. }(Matcher));
  2614. // RegExp objects which are shared by all instances of PhoneMatcher. These are
  2615. // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
  2616. // called multiple times, thus instantiating PhoneMatcher and its RegExp
  2617. // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
  2618. // See descriptions of the properties where they are used for details about them
  2619. // Over the years, many people have added to this regex, but it should have been
  2620. // split up by country. Maybe one day we can break this down.
  2621. var mostPhoneNumbers = /(?:(?:(?:(\+)?\d{1,3}[-\040.]?)?\(?\d{3}\)?[-\040.]?\d{3}[-\040.]?\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)[-\040.]?(?:\d[-\040.]?){6,12}\d+))([,;]+[0-9]+#?)*/;
  2622. // Regex for Japanese phone numbers
  2623. var japanesePhoneRe = /(0([1-9]{1}-?[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})/;
  2624. // Combined regex
  2625. var phoneMatcherRegex = new RegExp("".concat(mostPhoneNumbers.source, "|").concat(japanesePhoneRe.source), 'g');
  2626. /**
  2627. * @class Autolinker.matcher.Phone
  2628. * @extends Autolinker.matcher.Matcher
  2629. *
  2630. * Matcher to find Phone number matches in an input string.
  2631. *
  2632. * See this class's superclass ({@link Autolinker.matcher.Matcher}) for more
  2633. * details.
  2634. */
  2635. var PhoneMatcher = /** @class */ (function (_super) {
  2636. __extends(PhoneMatcher, _super);
  2637. function PhoneMatcher() {
  2638. var _this = _super !== null && _super.apply(this, arguments) || this;
  2639. /**
  2640. * The regular expression to match Phone numbers. Example matches:
  2641. *
  2642. * (123) 456-7890
  2643. * 123 456 7890
  2644. * 123-456-7890
  2645. * +18004441234,,;,10226420346#
  2646. * +1 (800) 444 1234
  2647. * 10226420346#
  2648. * 1-800-444-1234,1022,64,20346#
  2649. *
  2650. * This regular expression has the following capturing groups:
  2651. *
  2652. * 1 or 2. The prefixed '+' sign, if there is one.
  2653. *
  2654. * @protected
  2655. * @property {RegExp} matcherRegex
  2656. */
  2657. _this.matcherRegex = phoneMatcherRegex;
  2658. return _this;
  2659. }
  2660. /**
  2661. * @inheritdoc
  2662. */
  2663. PhoneMatcher.prototype.parseMatches = function (text) {
  2664. var matcherRegex = this.matcherRegex, tagBuilder = this.tagBuilder, matches = [], match;
  2665. while ((match = matcherRegex.exec(text)) !== null) {
  2666. // Remove non-numeric values from phone number string
  2667. var matchedText = match[0], cleanNumber = matchedText.replace(/[^0-9,;#]/g, ''), // strip out non-digit characters exclude comma semicolon and #
  2668. plusSign = !!(match[1] || match[2]), // match[ 1 ] or match[ 2 ] is the prefixed plus sign, if there is one
  2669. before = match.index == 0 ? '' : text.substr(match.index - 1, 1), after = text.substr(match.index + matchedText.length, 1), contextClear = !before.match(/\d/) && !after.match(/\d/);
  2670. if (this.testMatch(match[3]) && this.testMatch(matchedText) && contextClear) {
  2671. matches.push(new PhoneMatch({
  2672. tagBuilder: tagBuilder,
  2673. matchedText: matchedText,
  2674. offset: match.index,
  2675. number: cleanNumber,
  2676. plusSign: plusSign
  2677. }));
  2678. }
  2679. }
  2680. return matches;
  2681. };
  2682. PhoneMatcher.prototype.testMatch = function (text) {
  2683. return nonDigitRe.test(text);
  2684. };
  2685. return PhoneMatcher;
  2686. }(Matcher));
  2687. // RegExp objects which are shared by all instances of MentionMatcher. These are
  2688. // here to avoid re-instantiating the RegExp objects if `Autolinker.link()` is
  2689. // called multiple times, thus instantiating MentionMatcher and its RegExp
  2690. // objects each time (which is very expensive - see https://github.com/gregjacobs/Autolinker.js/issues/314).
  2691. // See descriptions of the properties where they are used for details about them
  2692. var twitterRegex = new RegExp("@[_".concat(alphaNumericAndMarksCharsStr, "]{1,50}(?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 50 characters
  2693. var instagramRegex = new RegExp("@[_.".concat(alphaNumericAndMarksCharsStr, "]{1,30}(?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 30 characters
  2694. var soundcloudRegex = new RegExp("@[-_.".concat(alphaNumericAndMarksCharsStr, "]{1,50}(?![-_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 50 characters
  2695. // TikTok usernames are 1-24 characters containing letters, numbers, underscores
  2696. // and periods, but cannot end in a period: https://support.tiktok.com/en/getting-started/setting-up-your-profile/changing-your-username
  2697. var tiktokRegex = new RegExp("@[_.".concat(alphaNumericAndMarksCharsStr, "]{1,23}[_").concat(alphaNumericAndMarksCharsStr, "](?![_").concat(alphaNumericAndMarksCharsStr, "])"), 'g'); // lookahead used to make sure we don't match something above 24 characters
  2698. var nonWordCharRegex = new RegExp('[^' + alphaNumericAndMarksCharsStr + ']');
  2699. /**
  2700. * @class Autolinker.matcher.Mention
  2701. * @extends Autolinker.matcher.Matcher
  2702. *
  2703. * Matcher to find/replace username matches in an input string.
  2704. */
  2705. var MentionMatcher = /** @class */ (function (_super) {
  2706. __extends(MentionMatcher, _super);
  2707. /**
  2708. * @method constructor
  2709. * @param {Object} cfg The configuration properties for the Match instance,
  2710. * specified in an Object (map).
  2711. */
  2712. function MentionMatcher(cfg) {
  2713. var _this = _super.call(this, cfg) || this;
  2714. /**
  2715. * @cfg {'twitter'/'instagram'/'soundcloud'} protected
  2716. *
  2717. * The name of service to link @mentions to.
  2718. *
  2719. * Valid values are: 'twitter', 'instagram', 'soundcloud', or 'tiktok'
  2720. */
  2721. _this.serviceName = 'twitter'; // default value just to get the above doc comment in the ES5 output and documentation generator
  2722. /**
  2723. * Hash of regular expression to match username handles. Example match:
  2724. *
  2725. * @asdf
  2726. *
  2727. * @private
  2728. * @property {Object} matcherRegexes
  2729. */
  2730. _this.matcherRegexes = {
  2731. 'twitter': twitterRegex,
  2732. 'instagram': instagramRegex,
  2733. 'soundcloud': soundcloudRegex,
  2734. 'tiktok': tiktokRegex
  2735. };
  2736. /**
  2737. * The regular expression to use to check the character before a username match to
  2738. * make sure we didn't accidentally match an email address.
  2739. *
  2740. * For example, the string "asdf@asdf.com" should not match "@asdf" as a username.
  2741. *
  2742. * @private
  2743. * @property {RegExp} nonWordCharRegex
  2744. */
  2745. _this.nonWordCharRegex = nonWordCharRegex;
  2746. _this.serviceName = cfg.serviceName;
  2747. return _this;
  2748. }
  2749. /**
  2750. * @inheritdoc
  2751. */
  2752. MentionMatcher.prototype.parseMatches = function (text) {
  2753. var serviceName = this.serviceName, matcherRegex = this.matcherRegexes[this.serviceName], nonWordCharRegex = this.nonWordCharRegex, tagBuilder = this.tagBuilder, matches = [], match;
  2754. if (!matcherRegex) {
  2755. return matches;
  2756. }
  2757. while ((match = matcherRegex.exec(text)) !== null) {
  2758. var offset = match.index, prevChar = text.charAt(offset - 1);
  2759. // If we found the match at the beginning of the string, or we found the match
  2760. // and there is a whitespace char in front of it (meaning it is not an email
  2761. // address), then it is a username match.
  2762. if (offset === 0 || nonWordCharRegex.test(prevChar)) {
  2763. var matchedText = match[0].replace(/\.+$/g, ''), // strip off trailing .
  2764. mention = matchedText.slice(1); // strip off the '@' character at the beginning
  2765. matches.push(new MentionMatch({
  2766. tagBuilder: tagBuilder,
  2767. matchedText: matchedText,
  2768. offset: offset,
  2769. serviceName: serviceName,
  2770. mention: mention
  2771. }));
  2772. }
  2773. }
  2774. return matches;
  2775. };
  2776. return MentionMatcher;
  2777. }(Matcher));
  2778. // For debugging: search for other "For debugging" lines
  2779. // import CliTable from 'cli-table';
  2780. /**
  2781. * Parses an HTML string, calling the callbacks to notify of tags and text.
  2782. *
  2783. * ## History
  2784. *
  2785. * This file previously used a regular expression to find html tags in the input
  2786. * text. Unfortunately, we ran into a bunch of catastrophic backtracking issues
  2787. * with certain input text, causing Autolinker to either hang or just take a
  2788. * really long time to parse the string.
  2789. *
  2790. * The current code is intended to be a O(n) algorithm that walks through
  2791. * the string in one pass, and tries to be as cheap as possible. We don't need
  2792. * to implement the full HTML spec, but rather simply determine where the string
  2793. * looks like an HTML tag, and where it looks like text (so that we can autolink
  2794. * that).
  2795. *
  2796. * This state machine parser is intended just to be a simple but performant
  2797. * parser of HTML for the subset of requirements we have. We simply need to:
  2798. *
  2799. * 1. Determine where HTML tags are
  2800. * 2. Determine the tag name (Autolinker specifically only cares about <a>,
  2801. * <script>, and <style> tags, so as not to link any text within them)
  2802. *
  2803. * We don't need to:
  2804. *
  2805. * 1. Create a parse tree
  2806. * 2. Auto-close tags with invalid markup
  2807. * 3. etc.
  2808. *
  2809. * The other intention behind this is that we didn't want to add external
  2810. * dependencies on the Autolinker utility which would increase its size. For
  2811. * instance, adding htmlparser2 adds 125kb to the minified output file,
  2812. * increasing its final size from 47kb to 172kb (at the time of writing). It
  2813. * also doesn't work exactly correctly, treating the string "<3 blah blah blah"
  2814. * as an HTML tag.
  2815. *
  2816. * Reference for HTML spec:
  2817. *
  2818. * https://www.w3.org/TR/html51/syntax.html#sec-tokenization
  2819. *
  2820. * @param {String} html The HTML to parse
  2821. * @param {Object} callbacks
  2822. * @param {Function} callbacks.onOpenTag Callback function to call when an open
  2823. * tag is parsed. Called with the tagName as its argument.
  2824. * @param {Function} callbacks.onCloseTag Callback function to call when a close
  2825. * tag is parsed. Called with the tagName as its argument. If a self-closing
  2826. * tag is found, `onCloseTag` is called immediately after `onOpenTag`.
  2827. * @param {Function} callbacks.onText Callback function to call when text (i.e
  2828. * not an HTML tag) is parsed. Called with the text (string) as its first
  2829. * argument, and offset (number) into the string as its second.
  2830. */
  2831. function parseHtml(html, _a) {
  2832. var onOpenTag = _a.onOpenTag, onCloseTag = _a.onCloseTag, onText = _a.onText, onComment = _a.onComment, onDoctype = _a.onDoctype;
  2833. var noCurrentTag = new CurrentTag();
  2834. var charIdx = 0, len = html.length, state = 0 /* Data */, currentDataIdx = 0, // where the current data start index is
  2835. currentTag = noCurrentTag; // describes the current tag that is being read
  2836. // For debugging: search for other "For debugging" lines
  2837. // const table = new CliTable( {
  2838. // head: [ 'charIdx', 'char', 'state', 'currentDataIdx', 'currentOpenTagIdx', 'tag.type' ]
  2839. // } );
  2840. while (charIdx < len) {
  2841. var char = html.charAt(charIdx);
  2842. // For debugging: search for other "For debugging" lines
  2843. // ALSO: Temporarily remove the 'const' keyword on the State enum
  2844. // table.push(
  2845. // [ charIdx, char, State[ state ], currentDataIdx, currentTag.idx, currentTag.idx === -1 ? '' : currentTag.type ]
  2846. // );
  2847. switch (state) {
  2848. case 0 /* Data */:
  2849. stateData(char);
  2850. break;
  2851. case 1 /* TagOpen */:
  2852. stateTagOpen(char);
  2853. break;
  2854. case 2 /* EndTagOpen */:
  2855. stateEndTagOpen(char);
  2856. break;
  2857. case 3 /* TagName */:
  2858. stateTagName(char);
  2859. break;
  2860. case 4 /* BeforeAttributeName */:
  2861. stateBeforeAttributeName(char);
  2862. break;
  2863. case 5 /* AttributeName */:
  2864. stateAttributeName(char);
  2865. break;
  2866. case 6 /* AfterAttributeName */:
  2867. stateAfterAttributeName(char);
  2868. break;
  2869. case 7 /* BeforeAttributeValue */:
  2870. stateBeforeAttributeValue(char);
  2871. break;
  2872. case 8 /* AttributeValueDoubleQuoted */:
  2873. stateAttributeValueDoubleQuoted(char);
  2874. break;
  2875. case 9 /* AttributeValueSingleQuoted */:
  2876. stateAttributeValueSingleQuoted(char);
  2877. break;
  2878. case 10 /* AttributeValueUnquoted */:
  2879. stateAttributeValueUnquoted(char);
  2880. break;
  2881. case 11 /* AfterAttributeValueQuoted */:
  2882. stateAfterAttributeValueQuoted(char);
  2883. break;
  2884. case 12 /* SelfClosingStartTag */:
  2885. stateSelfClosingStartTag(char);
  2886. break;
  2887. case 13 /* MarkupDeclarationOpenState */:
  2888. stateMarkupDeclarationOpen();
  2889. break;
  2890. case 14 /* CommentStart */:
  2891. stateCommentStart(char);
  2892. break;
  2893. case 15 /* CommentStartDash */:
  2894. stateCommentStartDash(char);
  2895. break;
  2896. case 16 /* Comment */:
  2897. stateComment(char);
  2898. break;
  2899. case 17 /* CommentEndDash */:
  2900. stateCommentEndDash(char);
  2901. break;
  2902. case 18 /* CommentEnd */:
  2903. stateCommentEnd(char);
  2904. break;
  2905. case 19 /* CommentEndBang */:
  2906. stateCommentEndBang(char);
  2907. break;
  2908. case 20 /* Doctype */:
  2909. stateDoctype(char);
  2910. break;
  2911. default:
  2912. throwUnhandledCaseError(state);
  2913. }
  2914. // For debugging: search for other "For debugging" lines
  2915. // ALSO: Temporarily remove the 'const' keyword on the State enum
  2916. // table.push(
  2917. // [ charIdx, char, State[ state ], currentDataIdx, currentTag.idx, currentTag.idx === -1 ? '' : currentTag.type ]
  2918. // );
  2919. charIdx++;
  2920. }
  2921. if (currentDataIdx < charIdx) {
  2922. emitText();
  2923. }
  2924. // For debugging: search for other "For debugging" lines
  2925. // console.log( '\n' + table.toString() );
  2926. // Called when non-tags are being read (i.e. the text around HTML †ags)
  2927. // https://www.w3.org/TR/html51/syntax.html#data-state
  2928. function stateData(char) {
  2929. if (char === '<') {
  2930. startNewTag();
  2931. }
  2932. }
  2933. // Called after a '<' is read from the Data state
  2934. // https://www.w3.org/TR/html51/syntax.html#tag-open-state
  2935. function stateTagOpen(char) {
  2936. if (char === '!') {
  2937. state = 13 /* MarkupDeclarationOpenState */;
  2938. }
  2939. else if (char === '/') {
  2940. state = 2 /* EndTagOpen */;
  2941. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isClosing: true }));
  2942. }
  2943. else if (char === '<') {
  2944. // start of another tag (ignore the previous, incomplete one)
  2945. startNewTag();
  2946. }
  2947. else if (letterRe.test(char)) {
  2948. // tag name start (and no '/' read)
  2949. state = 3 /* TagName */;
  2950. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isOpening: true }));
  2951. }
  2952. else {
  2953. // Any other
  2954. state = 0 /* Data */;
  2955. currentTag = noCurrentTag;
  2956. }
  2957. }
  2958. // After a '<x', '</x' sequence is read (where 'x' is a letter character),
  2959. // this is to continue reading the tag name
  2960. // https://www.w3.org/TR/html51/syntax.html#tag-name-state
  2961. function stateTagName(char) {
  2962. if (whitespaceRe.test(char)) {
  2963. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
  2964. state = 4 /* BeforeAttributeName */;
  2965. }
  2966. else if (char === '<') {
  2967. // start of another tag (ignore the previous, incomplete one)
  2968. startNewTag();
  2969. }
  2970. else if (char === '/') {
  2971. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
  2972. state = 12 /* SelfClosingStartTag */;
  2973. }
  2974. else if (char === '>') {
  2975. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { name: captureTagName() }));
  2976. emitTagAndPreviousTextNode(); // resets to Data state as well
  2977. }
  2978. else if (!letterRe.test(char) && !digitRe.test(char) && char !== ':') {
  2979. // Anything else that does not form an html tag. Note: the colon
  2980. // character is accepted for XML namespaced tags
  2981. resetToDataState();
  2982. }
  2983. else ;
  2984. }
  2985. // Called after the '/' is read from a '</' sequence
  2986. // https://www.w3.org/TR/html51/syntax.html#end-tag-open-state
  2987. function stateEndTagOpen(char) {
  2988. if (char === '>') { // parse error. Encountered "</>". Skip it without treating as a tag
  2989. resetToDataState();
  2990. }
  2991. else if (letterRe.test(char)) {
  2992. state = 3 /* TagName */;
  2993. }
  2994. else {
  2995. // some other non-tag-like character, don't treat this as a tag
  2996. resetToDataState();
  2997. }
  2998. }
  2999. // https://www.w3.org/TR/html51/syntax.html#before-attribute-name-state
  3000. function stateBeforeAttributeName(char) {
  3001. if (whitespaceRe.test(char)) ;
  3002. else if (char === '/') {
  3003. state = 12 /* SelfClosingStartTag */;
  3004. }
  3005. else if (char === '>') {
  3006. emitTagAndPreviousTextNode(); // resets to Data state as well
  3007. }
  3008. else if (char === '<') {
  3009. // start of another tag (ignore the previous, incomplete one)
  3010. startNewTag();
  3011. }
  3012. else if (char === "=" || quoteRe.test(char) || controlCharsRe.test(char)) {
  3013. // "Parse error" characters that, according to the spec, should be
  3014. // appended to the attribute name, but we'll treat these characters
  3015. // as not forming a real HTML tag
  3016. resetToDataState();
  3017. }
  3018. else {
  3019. // Any other char, start of a new attribute name
  3020. state = 5 /* AttributeName */;
  3021. }
  3022. }
  3023. // https://www.w3.org/TR/html51/syntax.html#attribute-name-state
  3024. function stateAttributeName(char) {
  3025. if (whitespaceRe.test(char)) {
  3026. state = 6 /* AfterAttributeName */;
  3027. }
  3028. else if (char === '/') {
  3029. state = 12 /* SelfClosingStartTag */;
  3030. }
  3031. else if (char === '=') {
  3032. state = 7 /* BeforeAttributeValue */;
  3033. }
  3034. else if (char === '>') {
  3035. emitTagAndPreviousTextNode(); // resets to Data state as well
  3036. }
  3037. else if (char === '<') {
  3038. // start of another tag (ignore the previous, incomplete one)
  3039. startNewTag();
  3040. }
  3041. else if (quoteRe.test(char)) {
  3042. // "Parse error" characters that, according to the spec, should be
  3043. // appended to the attribute name, but we'll treat these characters
  3044. // as not forming a real HTML tag
  3045. resetToDataState();
  3046. }
  3047. else ;
  3048. }
  3049. // https://www.w3.org/TR/html51/syntax.html#after-attribute-name-state
  3050. function stateAfterAttributeName(char) {
  3051. if (whitespaceRe.test(char)) ;
  3052. else if (char === '/') {
  3053. state = 12 /* SelfClosingStartTag */;
  3054. }
  3055. else if (char === '=') {
  3056. state = 7 /* BeforeAttributeValue */;
  3057. }
  3058. else if (char === '>') {
  3059. emitTagAndPreviousTextNode();
  3060. }
  3061. else if (char === '<') {
  3062. // start of another tag (ignore the previous, incomplete one)
  3063. startNewTag();
  3064. }
  3065. else if (quoteRe.test(char)) {
  3066. // "Parse error" characters that, according to the spec, should be
  3067. // appended to the attribute name, but we'll treat these characters
  3068. // as not forming a real HTML tag
  3069. resetToDataState();
  3070. }
  3071. else {
  3072. // Any other character, start a new attribute in the current tag
  3073. state = 5 /* AttributeName */;
  3074. }
  3075. }
  3076. // https://www.w3.org/TR/html51/syntax.html#before-attribute-value-state
  3077. function stateBeforeAttributeValue(char) {
  3078. if (whitespaceRe.test(char)) ;
  3079. else if (char === "\"") {
  3080. state = 8 /* AttributeValueDoubleQuoted */;
  3081. }
  3082. else if (char === "'") {
  3083. state = 9 /* AttributeValueSingleQuoted */;
  3084. }
  3085. else if (/[>=`]/.test(char)) {
  3086. // Invalid chars after an '=' for an attribute value, don't count
  3087. // the current tag as an HTML tag
  3088. resetToDataState();
  3089. }
  3090. else if (char === '<') {
  3091. // start of another tag (ignore the previous, incomplete one)
  3092. startNewTag();
  3093. }
  3094. else {
  3095. // Any other character, consider it an unquoted attribute value
  3096. state = 10 /* AttributeValueUnquoted */;
  3097. }
  3098. }
  3099. // https://www.w3.org/TR/html51/syntax.html#attribute-value-double-quoted-state
  3100. function stateAttributeValueDoubleQuoted(char) {
  3101. if (char === "\"") { // end the current double-quoted attribute
  3102. state = 11 /* AfterAttributeValueQuoted */;
  3103. }
  3104. }
  3105. // https://www.w3.org/TR/html51/syntax.html#attribute-value-single-quoted-state
  3106. function stateAttributeValueSingleQuoted(char) {
  3107. if (char === "'") { // end the current single-quoted attribute
  3108. state = 11 /* AfterAttributeValueQuoted */;
  3109. }
  3110. }
  3111. // https://www.w3.org/TR/html51/syntax.html#attribute-value-unquoted-state
  3112. function stateAttributeValueUnquoted(char) {
  3113. if (whitespaceRe.test(char)) {
  3114. state = 4 /* BeforeAttributeName */;
  3115. }
  3116. else if (char === '>') {
  3117. emitTagAndPreviousTextNode();
  3118. }
  3119. else if (char === '<') {
  3120. // start of another tag (ignore the previous, incomplete one)
  3121. startNewTag();
  3122. }
  3123. else ;
  3124. }
  3125. // https://www.w3.org/TR/html51/syntax.html#after-attribute-value-quoted-state
  3126. function stateAfterAttributeValueQuoted(char) {
  3127. if (whitespaceRe.test(char)) {
  3128. state = 4 /* BeforeAttributeName */;
  3129. }
  3130. else if (char === '/') {
  3131. state = 12 /* SelfClosingStartTag */;
  3132. }
  3133. else if (char === '>') {
  3134. emitTagAndPreviousTextNode();
  3135. }
  3136. else if (char === '<') {
  3137. // start of another tag (ignore the previous, incomplete one)
  3138. startNewTag();
  3139. }
  3140. else {
  3141. // Any other character, "parse error". Spec says to switch to the
  3142. // BeforeAttributeState and re-consume the character, as it may be
  3143. // the start of a new attribute name
  3144. state = 4 /* BeforeAttributeName */;
  3145. reconsumeCurrentCharacter();
  3146. }
  3147. }
  3148. // A '/' has just been read in the current tag (presumably for '/>'), and
  3149. // this handles the next character
  3150. // https://www.w3.org/TR/html51/syntax.html#self-closing-start-tag-state
  3151. function stateSelfClosingStartTag(char) {
  3152. if (char === '>') {
  3153. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { isClosing: true }));
  3154. emitTagAndPreviousTextNode(); // resets to Data state as well
  3155. }
  3156. else {
  3157. state = 4 /* BeforeAttributeName */;
  3158. }
  3159. }
  3160. // https://www.w3.org/TR/html51/syntax.html#markup-declaration-open-state
  3161. // (HTML Comments or !DOCTYPE)
  3162. function stateMarkupDeclarationOpen(char) {
  3163. if (html.substr(charIdx, 2) === '--') { // html comment
  3164. charIdx += 2; // "consume" characters
  3165. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { type: 'comment' }));
  3166. state = 14 /* CommentStart */;
  3167. }
  3168. else if (html.substr(charIdx, 7).toUpperCase() === 'DOCTYPE') {
  3169. charIdx += 7; // "consume" characters
  3170. currentTag = new CurrentTag(__assign(__assign({}, currentTag), { type: 'doctype' }));
  3171. state = 20 /* Doctype */;
  3172. }
  3173. else {
  3174. // At this point, the spec specifies that the state machine should
  3175. // enter the "bogus comment" state, in which case any character(s)
  3176. // after the '<!' that were read should become an HTML comment up
  3177. // until the first '>' that is read (or EOF). Instead, we'll assume
  3178. // that a user just typed '<!' as part of text data
  3179. resetToDataState();
  3180. }
  3181. }
  3182. // Handles after the sequence '<!--' has been read
  3183. // https://www.w3.org/TR/html51/syntax.html#comment-start-state
  3184. function stateCommentStart(char) {
  3185. if (char === '-') {
  3186. // We've read the sequence '<!---' at this point (3 dashes)
  3187. state = 15 /* CommentStartDash */;
  3188. }
  3189. else if (char === '>') {
  3190. // At this point, we'll assume the comment wasn't a real comment
  3191. // so we'll just emit it as data. We basically read the sequence
  3192. // '<!-->'
  3193. resetToDataState();
  3194. }
  3195. else {
  3196. // Any other char, take it as part of the comment
  3197. state = 16 /* Comment */;
  3198. }
  3199. }
  3200. // We've read the sequence '<!---' at this point (3 dashes)
  3201. // https://www.w3.org/TR/html51/syntax.html#comment-start-dash-state
  3202. function stateCommentStartDash(char) {
  3203. if (char === '-') {
  3204. // We've read '<!----' (4 dashes) at this point
  3205. state = 18 /* CommentEnd */;
  3206. }
  3207. else if (char === '>') {
  3208. // At this point, we'll assume the comment wasn't a real comment
  3209. // so we'll just emit it as data. We basically read the sequence
  3210. // '<!--->'
  3211. resetToDataState();
  3212. }
  3213. else {
  3214. // Anything else, take it as a valid comment
  3215. state = 16 /* Comment */;
  3216. }
  3217. }
  3218. // Currently reading the comment's text (data)
  3219. // https://www.w3.org/TR/html51/syntax.html#comment-state
  3220. function stateComment(char) {
  3221. if (char === '-') {
  3222. state = 17 /* CommentEndDash */;
  3223. }
  3224. }
  3225. // When we we've read the first dash inside a comment, it may signal the
  3226. // end of the comment if we read another dash
  3227. // https://www.w3.org/TR/html51/syntax.html#comment-end-dash-state
  3228. function stateCommentEndDash(char) {
  3229. if (char === '-') {
  3230. state = 18 /* CommentEnd */;
  3231. }
  3232. else {
  3233. // Wasn't a dash, must still be part of the comment
  3234. state = 16 /* Comment */;
  3235. }
  3236. }
  3237. // After we've read two dashes inside a comment, it may signal the end of
  3238. // the comment if we then read a '>' char
  3239. // https://www.w3.org/TR/html51/syntax.html#comment-end-state
  3240. function stateCommentEnd(char) {
  3241. if (char === '>') {
  3242. emitTagAndPreviousTextNode();
  3243. }
  3244. else if (char === '!') {
  3245. state = 19 /* CommentEndBang */;
  3246. }
  3247. else if (char === '-') ;
  3248. else {
  3249. // Anything else, switch back to the comment state since we didn't
  3250. // read the full "end comment" sequence (i.e. '-->')
  3251. state = 16 /* Comment */;
  3252. }
  3253. }
  3254. // We've read the sequence '--!' inside of a comment
  3255. // https://www.w3.org/TR/html51/syntax.html#comment-end-bang-state
  3256. function stateCommentEndBang(char) {
  3257. if (char === '-') {
  3258. // We read the sequence '--!-' inside of a comment. The last dash
  3259. // could signify that the comment is going to close
  3260. state = 17 /* CommentEndDash */;
  3261. }
  3262. else if (char === '>') {
  3263. // End of comment with the sequence '--!>'
  3264. emitTagAndPreviousTextNode();
  3265. }
  3266. else {
  3267. // The '--!' was not followed by a '>', continue reading the
  3268. // comment's text
  3269. state = 16 /* Comment */;
  3270. }
  3271. }
  3272. /**
  3273. * For DOCTYPES in particular, we don't care about the attributes. Just
  3274. * advance to the '>' character and emit the tag, unless we find a '<'
  3275. * character in which case we'll start a new tag.
  3276. *
  3277. * Example doctype tag:
  3278. * <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
  3279. *
  3280. * Actual spec: https://www.w3.org/TR/html51/syntax.html#doctype-state
  3281. */
  3282. function stateDoctype(char) {
  3283. if (char === '>') {
  3284. emitTagAndPreviousTextNode();
  3285. }
  3286. else if (char === '<') {
  3287. startNewTag();
  3288. }
  3289. else ;
  3290. }
  3291. /**
  3292. * Resets the state back to the Data state, and removes the current tag.
  3293. *
  3294. * We'll generally run this function whenever a "parse error" is
  3295. * encountered, where the current tag that is being read no longer looks
  3296. * like a real HTML tag.
  3297. */
  3298. function resetToDataState() {
  3299. state = 0 /* Data */;
  3300. currentTag = noCurrentTag;
  3301. }
  3302. /**
  3303. * Starts a new HTML tag at the current index, ignoring any previous HTML
  3304. * tag that was being read.
  3305. *
  3306. * We'll generally run this function whenever we read a new '<' character,
  3307. * including when we read a '<' character inside of an HTML tag that we were
  3308. * previously reading.
  3309. */
  3310. function startNewTag() {
  3311. state = 1 /* TagOpen */;
  3312. currentTag = new CurrentTag({ idx: charIdx });
  3313. }
  3314. /**
  3315. * Once we've decided to emit an open tag, that means we can also emit the
  3316. * text node before it.
  3317. */
  3318. function emitTagAndPreviousTextNode() {
  3319. var textBeforeTag = html.slice(currentDataIdx, currentTag.idx);
  3320. if (textBeforeTag) {
  3321. // the html tag was the first element in the html string, or two
  3322. // tags next to each other, in which case we should not emit a text
  3323. // node
  3324. onText(textBeforeTag, currentDataIdx);
  3325. }
  3326. if (currentTag.type === 'comment') {
  3327. onComment(currentTag.idx);
  3328. }
  3329. else if (currentTag.type === 'doctype') {
  3330. onDoctype(currentTag.idx);
  3331. }
  3332. else {
  3333. if (currentTag.isOpening) {
  3334. onOpenTag(currentTag.name, currentTag.idx);
  3335. }
  3336. if (currentTag.isClosing) { // note: self-closing tags will emit both opening and closing
  3337. onCloseTag(currentTag.name, currentTag.idx);
  3338. }
  3339. }
  3340. // Since we just emitted a tag, reset to the data state for the next char
  3341. resetToDataState();
  3342. currentDataIdx = charIdx + 1;
  3343. }
  3344. function emitText() {
  3345. var text = html.slice(currentDataIdx, charIdx);
  3346. onText(text, currentDataIdx);
  3347. currentDataIdx = charIdx + 1;
  3348. }
  3349. /**
  3350. * Captures the tag name from the start of the tag to the current character
  3351. * index, and converts it to lower case
  3352. */
  3353. function captureTagName() {
  3354. var startIdx = currentTag.idx + (currentTag.isClosing ? 2 : 1);
  3355. return html.slice(startIdx, charIdx).toLowerCase();
  3356. }
  3357. /**
  3358. * Causes the main loop to re-consume the current character, such as after
  3359. * encountering a "parse error" that changed state and needs to reconsume
  3360. * the same character in that new state.
  3361. */
  3362. function reconsumeCurrentCharacter() {
  3363. charIdx--;
  3364. }
  3365. }
  3366. var CurrentTag = /** @class */ (function () {
  3367. function CurrentTag(cfg) {
  3368. if (cfg === void 0) { cfg = {}; }
  3369. this.idx = cfg.idx !== undefined ? cfg.idx : -1;
  3370. this.type = cfg.type || 'tag';
  3371. this.name = cfg.name || '';
  3372. this.isOpening = !!cfg.isOpening;
  3373. this.isClosing = !!cfg.isClosing;
  3374. }
  3375. return CurrentTag;
  3376. }());
  3377. /**
  3378. * @class Autolinker
  3379. * @extends Object
  3380. *
  3381. * Utility class used to process a given string of text, and wrap the matches in
  3382. * the appropriate anchor (&lt;a&gt;) tags to turn them into links.
  3383. *
  3384. * Any of the configuration options may be provided in an Object provided
  3385. * to the Autolinker constructor, which will configure how the {@link #link link()}
  3386. * method will process the links.
  3387. *
  3388. * For example:
  3389. *
  3390. * var autolinker = new Autolinker( {
  3391. * newWindow : false,
  3392. * truncate : 30
  3393. * } );
  3394. *
  3395. * var html = autolinker.link( "Joe went to www.yahoo.com" );
  3396. * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
  3397. *
  3398. *
  3399. * The {@link #static-link static link()} method may also be used to inline
  3400. * options into a single call, which may be more convenient for one-off uses.
  3401. * For example:
  3402. *
  3403. * var html = Autolinker.link( "Joe went to www.yahoo.com", {
  3404. * newWindow : false,
  3405. * truncate : 30
  3406. * } );
  3407. * // produces: 'Joe went to <a href="http://www.yahoo.com">yahoo.com</a>'
  3408. *
  3409. *
  3410. * ## Custom Replacements of Links
  3411. *
  3412. * If the configuration options do not provide enough flexibility, a {@link #replaceFn}
  3413. * may be provided to fully customize the output of Autolinker. This function is
  3414. * called once for each URL/Email/Phone#/Hashtag/Mention (Twitter, Instagram, Soundcloud)
  3415. * match that is encountered.
  3416. *
  3417. * For example:
  3418. *
  3419. * var input = "..."; // string with URLs, Email Addresses, Phone #s, Hashtags, and Mentions (Twitter, Instagram, Soundcloud)
  3420. *
  3421. * var linkedText = Autolinker.link( input, {
  3422. * replaceFn : function( match ) {
  3423. * console.log( "href = ", match.getAnchorHref() );
  3424. * console.log( "text = ", match.getAnchorText() );
  3425. *
  3426. * switch( match.getType() ) {
  3427. * case 'url' :
  3428. * console.log( "url: ", match.getUrl() );
  3429. *
  3430. * if( match.getUrl().indexOf( 'mysite.com' ) === -1 ) {
  3431. * var tag = match.buildTag(); // returns an `Autolinker.HtmlTag` instance, which provides mutator methods for easy changes
  3432. * tag.setAttr( 'rel', 'nofollow' );
  3433. * tag.addClass( 'external-link' );
  3434. *
  3435. * return tag;
  3436. *
  3437. * } else {
  3438. * return true; // let Autolinker perform its normal anchor tag replacement
  3439. * }
  3440. *
  3441. * case 'email' :
  3442. * var email = match.getEmail();
  3443. * console.log( "email: ", email );
  3444. *
  3445. * if( email === "my@own.address" ) {
  3446. * return false; // don't auto-link this particular email address; leave as-is
  3447. * } else {
  3448. * return; // no return value will have Autolinker perform its normal anchor tag replacement (same as returning `true`)
  3449. * }
  3450. *
  3451. * case 'phone' :
  3452. * var phoneNumber = match.getPhoneNumber();
  3453. * console.log( phoneNumber );
  3454. *
  3455. * return '<a href="http://newplace.to.link.phone.numbers.to/">' + phoneNumber + '</a>';
  3456. *
  3457. * case 'hashtag' :
  3458. * var hashtag = match.getHashtag();
  3459. * console.log( hashtag );
  3460. *
  3461. * return '<a href="http://newplace.to.link.hashtag.handles.to/">' + hashtag + '</a>';
  3462. *
  3463. * case 'mention' :
  3464. * var mention = match.getMention();
  3465. * console.log( mention );
  3466. *
  3467. * return '<a href="http://newplace.to.link.mention.to/">' + mention + '</a>';
  3468. * }
  3469. * }
  3470. * } );
  3471. *
  3472. *
  3473. * The function may return the following values:
  3474. *
  3475. * - `true` (Boolean): Allow Autolinker to replace the match as it normally
  3476. * would.
  3477. * - `false` (Boolean): Do not replace the current match at all - leave as-is.
  3478. * - Any String: If a string is returned from the function, the string will be
  3479. * used directly as the replacement HTML for the match.
  3480. * - An {@link Autolinker.HtmlTag} instance, which can be used to build/modify
  3481. * an HTML tag before writing out its HTML text.
  3482. */
  3483. var Autolinker = /** @class */ (function () {
  3484. /**
  3485. * @method constructor
  3486. * @param {Object} [cfg] The configuration options for the Autolinker instance,
  3487. * specified in an Object (map).
  3488. */
  3489. function Autolinker(cfg) {
  3490. if (cfg === void 0) { cfg = {}; }
  3491. /**
  3492. * The Autolinker version number exposed on the instance itself.
  3493. *
  3494. * Ex: 0.25.1
  3495. */
  3496. this.version = Autolinker.version;
  3497. /**
  3498. * @cfg {Boolean/Object} [urls]
  3499. *
  3500. * `true` if URLs should be automatically linked, `false` if they should not
  3501. * be. Defaults to `true`.
  3502. *
  3503. * Examples:
  3504. *
  3505. * urls: true
  3506. *
  3507. * // or
  3508. *
  3509. * urls: {
  3510. * schemeMatches : true,
  3511. * wwwMatches : true,
  3512. * tldMatches : true
  3513. * }
  3514. *
  3515. * As shown above, this option also accepts an Object form with 3 properties
  3516. * to allow for more customization of what exactly gets linked. All default
  3517. * to `true`:
  3518. *
  3519. * @cfg {Boolean} [urls.schemeMatches] `true` to match URLs found prefixed
  3520. * with a scheme, i.e. `http://google.com`, or `other+scheme://google.com`,
  3521. * `false` to prevent these types of matches.
  3522. * @cfg {Boolean} [urls.wwwMatches] `true` to match urls found prefixed with
  3523. * `'www.'`, i.e. `www.google.com`. `false` to prevent these types of
  3524. * matches. Note that if the URL had a prefixed scheme, and
  3525. * `schemeMatches` is true, it will still be linked.
  3526. * @cfg {Boolean} [urls.tldMatches] `true` to match URLs with known top
  3527. * level domains (.com, .net, etc.) that are not prefixed with a scheme or
  3528. * `'www.'`. This option attempts to match anything that looks like a URL
  3529. * in the given text. Ex: `google.com`, `asdf.org/?page=1`, etc. `false`
  3530. * to prevent these types of matches.
  3531. */
  3532. this.urls = {}; // default value just to get the above doc comment in the ES5 output and documentation generator
  3533. /**
  3534. * @cfg {Boolean} [email=true]
  3535. *
  3536. * `true` if email addresses should be automatically linked, `false` if they
  3537. * should not be.
  3538. */
  3539. this.email = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  3540. /**
  3541. * @cfg {Boolean} [phone=true]
  3542. *
  3543. * `true` if Phone numbers ("(555)555-5555") should be automatically linked,
  3544. * `false` if they should not be.
  3545. */
  3546. this.phone = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  3547. /**
  3548. * @cfg {Boolean/String} [hashtag=false]
  3549. *
  3550. * A string for the service name to have hashtags (ex: "#myHashtag")
  3551. * auto-linked to. The currently-supported values are:
  3552. *
  3553. * - 'twitter'
  3554. * - 'facebook'
  3555. * - 'instagram'
  3556. *
  3557. * Pass `false` to skip auto-linking of hashtags.
  3558. */
  3559. this.hashtag = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  3560. /**
  3561. * @cfg {String/Boolean} [mention=false]
  3562. *
  3563. * A string for the service name to have mentions (ex: "@myuser")
  3564. * auto-linked to. The currently supported values are:
  3565. *
  3566. * - 'twitter'
  3567. * - 'instagram'
  3568. * - 'soundcloud'
  3569. *
  3570. * Defaults to `false` to skip auto-linking of mentions.
  3571. */
  3572. this.mention = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  3573. /**
  3574. * @cfg {Boolean} [newWindow=true]
  3575. *
  3576. * `true` if the links should open in a new window, `false` otherwise.
  3577. */
  3578. this.newWindow = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  3579. /**
  3580. * @cfg {Boolean/Object} [stripPrefix=true]
  3581. *
  3582. * `true` if 'http://' (or 'https://') and/or the 'www.' should be stripped
  3583. * from the beginning of URL links' text, `false` otherwise. Defaults to
  3584. * `true`.
  3585. *
  3586. * Examples:
  3587. *
  3588. * stripPrefix: true
  3589. *
  3590. * // or
  3591. *
  3592. * stripPrefix: {
  3593. * scheme : true,
  3594. * www : true
  3595. * }
  3596. *
  3597. * As shown above, this option also accepts an Object form with 2 properties
  3598. * to allow for more customization of what exactly is prevented from being
  3599. * displayed. Both default to `true`:
  3600. *
  3601. * @cfg {Boolean} [stripPrefix.scheme] `true` to prevent the scheme part of
  3602. * a URL match from being displayed to the user. Example:
  3603. * `'http://google.com'` will be displayed as `'google.com'`. `false` to
  3604. * not strip the scheme. NOTE: Only an `'http://'` or `'https://'` scheme
  3605. * will be removed, so as not to remove a potentially dangerous scheme
  3606. * (such as `'file://'` or `'javascript:'`)
  3607. * @cfg {Boolean} [stripPrefix.www] www (Boolean): `true` to prevent the
  3608. * `'www.'` part of a URL match from being displayed to the user. Ex:
  3609. * `'www.google.com'` will be displayed as `'google.com'`. `false` to not
  3610. * strip the `'www'`.
  3611. */
  3612. this.stripPrefix = { scheme: true, www: true }; // default value just to get the above doc comment in the ES5 output and documentation generator
  3613. /**
  3614. * @cfg {Boolean} [stripTrailingSlash=true]
  3615. *
  3616. * `true` to remove the trailing slash from URL matches, `false` to keep
  3617. * the trailing slash.
  3618. *
  3619. * Example when `true`: `http://google.com/` will be displayed as
  3620. * `http://google.com`.
  3621. */
  3622. this.stripTrailingSlash = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  3623. /**
  3624. * @cfg {Boolean} [decodePercentEncoding=true]
  3625. *
  3626. * `true` to decode percent-encoded characters in URL matches, `false` to keep
  3627. * the percent-encoded characters.
  3628. *
  3629. * Example when `true`: `https://en.wikipedia.org/wiki/San_Jos%C3%A9` will
  3630. * be displayed as `https://en.wikipedia.org/wiki/San_José`.
  3631. */
  3632. this.decodePercentEncoding = true; // default value just to get the above doc comment in the ES5 output and documentation generator
  3633. /**
  3634. * @cfg {Number/Object} [truncate=0]
  3635. *
  3636. * ## Number Form
  3637. *
  3638. * A number for how many characters matched text should be truncated to
  3639. * inside the text of a link. If the matched text is over this number of
  3640. * characters, it will be truncated to this length by adding a two period
  3641. * ellipsis ('..') to the end of the string.
  3642. *
  3643. * For example: A url like 'http://www.yahoo.com/some/long/path/to/a/file'
  3644. * truncated to 25 characters might look something like this:
  3645. * 'yahoo.com/some/long/pat..'
  3646. *
  3647. * Example Usage:
  3648. *
  3649. * truncate: 25
  3650. *
  3651. *
  3652. * Defaults to `0` for "no truncation."
  3653. *
  3654. *
  3655. * ## Object Form
  3656. *
  3657. * An Object may also be provided with two properties: `length` (Number) and
  3658. * `location` (String). `location` may be one of the following: 'end'
  3659. * (default), 'middle', or 'smart'.
  3660. *
  3661. * Example Usage:
  3662. *
  3663. * truncate: { length: 25, location: 'middle' }
  3664. *
  3665. * @cfg {Number} [truncate.length=0] How many characters to allow before
  3666. * truncation will occur. Defaults to `0` for "no truncation."
  3667. * @cfg {"end"/"middle"/"smart"} [truncate.location="end"]
  3668. *
  3669. * - 'end' (default): will truncate up to the number of characters, and then
  3670. * add an ellipsis at the end. Ex: 'yahoo.com/some/long/pat..'
  3671. * - 'middle': will truncate and add the ellipsis in the middle. Ex:
  3672. * 'yahoo.com/s..th/to/a/file'
  3673. * - 'smart': for URLs where the algorithm attempts to strip out unnecessary
  3674. * parts first (such as the 'www.', then URL scheme, hash, etc.),
  3675. * attempting to make the URL human-readable before looking for a good
  3676. * point to insert the ellipsis if it is still too long. Ex:
  3677. * 'yahoo.com/some..to/a/file'. For more details, see
  3678. * {@link Autolinker.truncate.TruncateSmart}.
  3679. */
  3680. this.truncate = { length: 0, location: 'end' }; // default value just to get the above doc comment in the ES5 output and documentation generator
  3681. /**
  3682. * @cfg {String} className
  3683. *
  3684. * A CSS class name to add to the generated links. This class will be added
  3685. * to all links, as well as this class plus match suffixes for styling
  3686. * url/email/phone/hashtag/mention links differently.
  3687. *
  3688. * For example, if this config is provided as "myLink", then:
  3689. *
  3690. * - URL links will have the CSS classes: "myLink myLink-url"
  3691. * - Email links will have the CSS classes: "myLink myLink-email", and
  3692. * - Phone links will have the CSS classes: "myLink myLink-phone"
  3693. * - Hashtag links will have the CSS classes: "myLink myLink-hashtag"
  3694. * - Mention links will have the CSS classes: "myLink myLink-mention myLink-[type]"
  3695. * where [type] is either "instagram", "twitter" or "soundcloud"
  3696. */
  3697. this.className = ''; // default value just to get the above doc comment in the ES5 output and documentation generator
  3698. /**
  3699. * @cfg {Function} replaceFn
  3700. *
  3701. * A function to individually process each match found in the input string.
  3702. *
  3703. * See the class's description for usage.
  3704. *
  3705. * The `replaceFn` can be called with a different context object (`this`
  3706. * reference) using the {@link #context} cfg.
  3707. *
  3708. * This function is called with the following parameter:
  3709. *
  3710. * @cfg {Autolinker.match.Match} replaceFn.match The Match instance which
  3711. * can be used to retrieve information about the match that the `replaceFn`
  3712. * is currently processing. See {@link Autolinker.match.Match} subclasses
  3713. * for details.
  3714. */
  3715. this.replaceFn = null; // default value just to get the above doc comment in the ES5 output and documentation generator
  3716. /**
  3717. * @cfg {Object} context
  3718. *
  3719. * The context object (`this` reference) to call the `replaceFn` with.
  3720. *
  3721. * Defaults to this Autolinker instance.
  3722. */
  3723. this.context = undefined; // default value just to get the above doc comment in the ES5 output and documentation generator
  3724. /**
  3725. * @cfg {Boolean} [sanitizeHtml=false]
  3726. *
  3727. * `true` to HTML-encode the start and end brackets of existing HTML tags found
  3728. * in the input string. This will escape `<` and `>` characters to `&lt;` and
  3729. * `&gt;`, respectively.
  3730. *
  3731. * Setting this to `true` will prevent XSS (Cross-site Scripting) attacks,
  3732. * but will remove the significance of existing HTML tags in the input string. If
  3733. * you would like to maintain the significance of existing HTML tags while also
  3734. * making the output HTML string safe, leave this option as `false` and use a
  3735. * tool like https://github.com/cure53/DOMPurify (or others) on the input string
  3736. * before running Autolinker.
  3737. */
  3738. this.sanitizeHtml = false; // default value just to get the above doc comment in the ES5 output and documentation generator
  3739. /**
  3740. * @private
  3741. * @property {Autolinker.matcher.Matcher[]} matchers
  3742. *
  3743. * The {@link Autolinker.matcher.Matcher} instances for this Autolinker
  3744. * instance.
  3745. *
  3746. * This is lazily created in {@link #getMatchers}.
  3747. */
  3748. this.matchers = null;
  3749. /**
  3750. * @private
  3751. * @property {Autolinker.AnchorTagBuilder} tagBuilder
  3752. *
  3753. * The AnchorTagBuilder instance used to build match replacement anchor tags.
  3754. * Note: this is lazily instantiated in the {@link #getTagBuilder} method.
  3755. */
  3756. this.tagBuilder = null;
  3757. // Note: when `this.something` is used in the rhs of these assignments,
  3758. // it refers to the default values set above the constructor
  3759. this.urls = this.normalizeUrlsCfg(cfg.urls);
  3760. this.email = typeof cfg.email === 'boolean' ? cfg.email : this.email;
  3761. this.phone = typeof cfg.phone === 'boolean' ? cfg.phone : this.phone;
  3762. this.hashtag = cfg.hashtag || this.hashtag;
  3763. this.mention = cfg.mention || this.mention;
  3764. this.newWindow = typeof cfg.newWindow === 'boolean' ? cfg.newWindow : this.newWindow;
  3765. this.stripPrefix = this.normalizeStripPrefixCfg(cfg.stripPrefix);
  3766. this.stripTrailingSlash = typeof cfg.stripTrailingSlash === 'boolean' ? cfg.stripTrailingSlash : this.stripTrailingSlash;
  3767. this.decodePercentEncoding = typeof cfg.decodePercentEncoding === 'boolean' ? cfg.decodePercentEncoding : this.decodePercentEncoding;
  3768. this.sanitizeHtml = cfg.sanitizeHtml || false;
  3769. // Validate the value of the `mention` cfg
  3770. var mention = this.mention;
  3771. if (mention !== false && ['twitter', 'instagram', 'soundcloud', 'tiktok'].indexOf(mention) === -1) {
  3772. throw new Error("invalid `mention` cfg '".concat(mention, "' - see docs"));
  3773. }
  3774. // Validate the value of the `hashtag` cfg
  3775. var hashtag = this.hashtag;
  3776. if (hashtag !== false && ['twitter', 'facebook', 'instagram', 'tiktok'].indexOf(hashtag) === -1) {
  3777. throw new Error("invalid `hashtag` cfg '".concat(hashtag, "' - see docs"));
  3778. }
  3779. this.truncate = this.normalizeTruncateCfg(cfg.truncate);
  3780. this.className = cfg.className || this.className;
  3781. this.replaceFn = cfg.replaceFn || this.replaceFn;
  3782. this.context = cfg.context || this;
  3783. }
  3784. /**
  3785. * Automatically links URLs, Email addresses, Phone Numbers, Twitter handles,
  3786. * Hashtags, and Mentions found in the given chunk of HTML. Does not link URLs
  3787. * found within HTML tags.
  3788. *
  3789. * For instance, if given the text: `You should go to http://www.yahoo.com`,
  3790. * then the result will be `You should go to &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
  3791. *
  3792. * Example:
  3793. *
  3794. * var linkedText = Autolinker.link( "Go to google.com", { newWindow: false } );
  3795. * // Produces: "Go to <a href="http://google.com">google.com</a>"
  3796. *
  3797. * @static
  3798. * @param {String} textOrHtml The HTML or text to find matches within (depending
  3799. * on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #mention},
  3800. * {@link #hashtag}, and {@link #mention} options are enabled).
  3801. * @param {Object} [options] Any of the configuration options for the Autolinker
  3802. * class, specified in an Object (map). See the class description for an
  3803. * example call.
  3804. * @return {String} The HTML text, with matches automatically linked.
  3805. */
  3806. Autolinker.link = function (textOrHtml, options) {
  3807. var autolinker = new Autolinker(options);
  3808. return autolinker.link(textOrHtml);
  3809. };
  3810. /**
  3811. * Parses the input `textOrHtml` looking for URLs, email addresses, phone
  3812. * numbers, username handles, and hashtags (depending on the configuration
  3813. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  3814. * objects describing those matches (without making any replacements).
  3815. *
  3816. * Note that if parsing multiple pieces of text, it is slightly more efficient
  3817. * to create an Autolinker instance, and use the instance-level {@link #parse}
  3818. * method.
  3819. *
  3820. * Example:
  3821. *
  3822. * var matches = Autolinker.parse( "Hello google.com, I am asdf@asdf.com", {
  3823. * urls: true,
  3824. * email: true
  3825. * } );
  3826. *
  3827. * console.log( matches.length ); // 2
  3828. * console.log( matches[ 0 ].getType() ); // 'url'
  3829. * console.log( matches[ 0 ].getUrl() ); // 'google.com'
  3830. * console.log( matches[ 1 ].getType() ); // 'email'
  3831. * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
  3832. *
  3833. * @static
  3834. * @param {String} textOrHtml The HTML or text to find matches within
  3835. * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
  3836. * {@link #hashtag}, and {@link #mention} options are enabled).
  3837. * @param {Object} [options] Any of the configuration options for the Autolinker
  3838. * class, specified in an Object (map). See the class description for an
  3839. * example call.
  3840. * @return {Autolinker.match.Match[]} The array of Matches found in the
  3841. * given input `textOrHtml`.
  3842. */
  3843. Autolinker.parse = function (textOrHtml, options) {
  3844. var autolinker = new Autolinker(options);
  3845. return autolinker.parse(textOrHtml);
  3846. };
  3847. /**
  3848. * Normalizes the {@link #urls} config into an Object with 3 properties:
  3849. * `schemeMatches`, `wwwMatches`, and `tldMatches`, all Booleans.
  3850. *
  3851. * See {@link #urls} config for details.
  3852. *
  3853. * @private
  3854. * @param {Boolean/Object} urls
  3855. * @return {Object}
  3856. */
  3857. Autolinker.prototype.normalizeUrlsCfg = function (urls) {
  3858. if (urls == null)
  3859. urls = true; // default to `true`
  3860. if (typeof urls === 'boolean') {
  3861. return { schemeMatches: urls, wwwMatches: urls, tldMatches: urls };
  3862. }
  3863. else { // object form
  3864. return {
  3865. schemeMatches: typeof urls.schemeMatches === 'boolean' ? urls.schemeMatches : true,
  3866. wwwMatches: typeof urls.wwwMatches === 'boolean' ? urls.wwwMatches : true,
  3867. tldMatches: typeof urls.tldMatches === 'boolean' ? urls.tldMatches : true
  3868. };
  3869. }
  3870. };
  3871. /**
  3872. * Normalizes the {@link #stripPrefix} config into an Object with 2
  3873. * properties: `scheme`, and `www` - both Booleans.
  3874. *
  3875. * See {@link #stripPrefix} config for details.
  3876. *
  3877. * @private
  3878. * @param {Boolean/Object} stripPrefix
  3879. * @return {Object}
  3880. */
  3881. Autolinker.prototype.normalizeStripPrefixCfg = function (stripPrefix) {
  3882. if (stripPrefix == null)
  3883. stripPrefix = true; // default to `true`
  3884. if (typeof stripPrefix === 'boolean') {
  3885. return { scheme: stripPrefix, www: stripPrefix };
  3886. }
  3887. else { // object form
  3888. return {
  3889. scheme: typeof stripPrefix.scheme === 'boolean' ? stripPrefix.scheme : true,
  3890. www: typeof stripPrefix.www === 'boolean' ? stripPrefix.www : true
  3891. };
  3892. }
  3893. };
  3894. /**
  3895. * Normalizes the {@link #truncate} config into an Object with 2 properties:
  3896. * `length` (Number), and `location` (String).
  3897. *
  3898. * See {@link #truncate} config for details.
  3899. *
  3900. * @private
  3901. * @param {Number/Object} truncate
  3902. * @return {Object}
  3903. */
  3904. Autolinker.prototype.normalizeTruncateCfg = function (truncate) {
  3905. if (typeof truncate === 'number') {
  3906. return { length: truncate, location: 'end' };
  3907. }
  3908. else { // object, or undefined/null
  3909. return defaults(truncate || {}, {
  3910. length: Number.POSITIVE_INFINITY,
  3911. location: 'end'
  3912. });
  3913. }
  3914. };
  3915. /**
  3916. * Parses the input `textOrHtml` looking for URLs, email addresses, phone
  3917. * numbers, username handles, and hashtags (depending on the configuration
  3918. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  3919. * objects describing those matches (without making any replacements).
  3920. *
  3921. * This method is used by the {@link #link} method, but can also be used to
  3922. * simply do parsing of the input in order to discover what kinds of links
  3923. * there are and how many.
  3924. *
  3925. * Example usage:
  3926. *
  3927. * var autolinker = new Autolinker( {
  3928. * urls: true,
  3929. * email: true
  3930. * } );
  3931. *
  3932. * var matches = autolinker.parse( "Hello google.com, I am asdf@asdf.com" );
  3933. *
  3934. * console.log( matches.length ); // 2
  3935. * console.log( matches[ 0 ].getType() ); // 'url'
  3936. * console.log( matches[ 0 ].getUrl() ); // 'google.com'
  3937. * console.log( matches[ 1 ].getType() ); // 'email'
  3938. * console.log( matches[ 1 ].getEmail() ); // 'asdf@asdf.com'
  3939. *
  3940. * @param {String} textOrHtml The HTML or text to find matches within
  3941. * (depending on if the {@link #urls}, {@link #email}, {@link #phone},
  3942. * {@link #hashtag}, and {@link #mention} options are enabled).
  3943. * @return {Autolinker.match.Match[]} The array of Matches found in the
  3944. * given input `textOrHtml`.
  3945. */
  3946. Autolinker.prototype.parse = function (textOrHtml) {
  3947. var _this = this;
  3948. var skipTagNames = ['a', 'style', 'script'], 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
  3949. matches = [];
  3950. // Find all matches within the `textOrHtml` (but not matches that are
  3951. // already nested within <a>, <style> and <script> tags)
  3952. parseHtml(textOrHtml, {
  3953. onOpenTag: function (tagName) {
  3954. if (skipTagNames.indexOf(tagName) >= 0) {
  3955. skipTagsStackCount++;
  3956. }
  3957. },
  3958. onText: function (text, offset) {
  3959. // Only process text nodes that are not within an <a>, <style> or <script> tag
  3960. if (skipTagsStackCount === 0) {
  3961. // "Walk around" common HTML entities. An '&nbsp;' (for example)
  3962. // could be at the end of a URL, but we don't want to
  3963. // include the trailing '&' in the URL. See issue #76
  3964. // TODO: Handle HTML entities separately in parseHtml() and
  3965. // don't emit them as "text" except for &amp; entities
  3966. var htmlCharacterEntitiesRegex = /(&nbsp;|&#160;|&lt;|&#60;|&gt;|&#62;|&quot;|&#34;|&#39;)/gi;
  3967. var textSplit = splitAndCapture(text, htmlCharacterEntitiesRegex);
  3968. var currentOffset_1 = offset;
  3969. textSplit.forEach(function (splitText, i) {
  3970. // even number matches are text, odd numbers are html entities
  3971. if (i % 2 === 0) {
  3972. var textNodeMatches = _this.parseText(splitText, currentOffset_1);
  3973. matches.push.apply(matches, textNodeMatches);
  3974. }
  3975. currentOffset_1 += splitText.length;
  3976. });
  3977. }
  3978. },
  3979. onCloseTag: function (tagName) {
  3980. if (skipTagNames.indexOf(tagName) >= 0) {
  3981. skipTagsStackCount = Math.max(skipTagsStackCount - 1, 0); // attempt to handle extraneous </a> tags by making sure the stack count never goes below 0
  3982. }
  3983. },
  3984. onComment: function (offset) { },
  3985. onDoctype: function (offset) { }, // no need to process doctype nodes
  3986. });
  3987. // After we have found all matches, remove subsequent matches that
  3988. // overlap with a previous match. This can happen for instance with URLs,
  3989. // where the url 'google.com/#link' would match '#link' as a hashtag.
  3990. matches = this.compactMatches(matches);
  3991. // And finally, remove matches for match types that have been turned
  3992. // off. We needed to have all match types turned on initially so that
  3993. // things like hashtags could be filtered out if they were really just
  3994. // part of a URL match (for instance, as a named anchor).
  3995. matches = this.removeUnwantedMatches(matches);
  3996. return matches;
  3997. };
  3998. /**
  3999. * After we have found all matches, we need to remove matches that overlap
  4000. * with a previous match. This can happen for instance with URLs, where the
  4001. * url 'google.com/#link' would match '#link' as a hashtag. Because the
  4002. * '#link' part is contained in a larger match that comes before the HashTag
  4003. * match, we'll remove the HashTag match.
  4004. *
  4005. * @private
  4006. * @param {Autolinker.match.Match[]} matches
  4007. * @return {Autolinker.match.Match[]}
  4008. */
  4009. Autolinker.prototype.compactMatches = function (matches) {
  4010. // First, the matches need to be sorted in order of offset
  4011. matches.sort(function (a, b) { return a.getOffset() - b.getOffset(); });
  4012. var i = 0;
  4013. while (i < matches.length - 1) {
  4014. var match = matches[i], offset = match.getOffset(), matchedTextLength = match.getMatchedText().length, endIdx = offset + matchedTextLength;
  4015. if (i + 1 < matches.length) {
  4016. // Remove subsequent matches that equal offset with current match
  4017. if (matches[i + 1].getOffset() === offset) {
  4018. var removeIdx = matches[i + 1].getMatchedText().length > matchedTextLength ? i : i + 1;
  4019. matches.splice(removeIdx, 1);
  4020. continue;
  4021. }
  4022. // Remove subsequent matches that overlap with the current match
  4023. if (matches[i + 1].getOffset() < endIdx) {
  4024. matches.splice(i + 1, 1);
  4025. continue;
  4026. }
  4027. }
  4028. i++;
  4029. }
  4030. return matches;
  4031. };
  4032. /**
  4033. * Removes matches for matchers that were turned off in the options. For
  4034. * example, if {@link #hashtag hashtags} were not to be matched, we'll
  4035. * remove them from the `matches` array here.
  4036. *
  4037. * Note: we *must* use all Matchers on the input string, and then filter
  4038. * them out later. For example, if the options were `{ url: false, hashtag: true }`,
  4039. * we wouldn't want to match the text '#link' as a HashTag inside of the text
  4040. * 'google.com/#link'. The way the algorithm works is that we match the full
  4041. * URL first (which prevents the accidental HashTag match), and then we'll
  4042. * simply throw away the URL match.
  4043. *
  4044. * @private
  4045. * @param {Autolinker.match.Match[]} matches The array of matches to remove
  4046. * the unwanted matches from. Note: this array is mutated for the
  4047. * removals.
  4048. * @return {Autolinker.match.Match[]} The mutated input `matches` array.
  4049. */
  4050. Autolinker.prototype.removeUnwantedMatches = function (matches) {
  4051. if (!this.hashtag)
  4052. remove(matches, function (match) { return match.getType() === 'hashtag'; });
  4053. if (!this.email)
  4054. remove(matches, function (match) { return match.getType() === 'email'; });
  4055. if (!this.phone)
  4056. remove(matches, function (match) { return match.getType() === 'phone'; });
  4057. if (!this.mention)
  4058. remove(matches, function (match) { return match.getType() === 'mention'; });
  4059. if (!this.urls.schemeMatches) {
  4060. remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'scheme'; });
  4061. }
  4062. if (!this.urls.wwwMatches) {
  4063. remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'www'; });
  4064. }
  4065. if (!this.urls.tldMatches) {
  4066. remove(matches, function (m) { return m.getType() === 'url' && m.getUrlMatchType() === 'tld'; });
  4067. }
  4068. return matches;
  4069. };
  4070. /**
  4071. * Parses the input `text` looking for URLs, email addresses, phone
  4072. * numbers, username handles, and hashtags (depending on the configuration
  4073. * of the Autolinker instance), and returns an array of {@link Autolinker.match.Match}
  4074. * objects describing those matches.
  4075. *
  4076. * This method processes a **non-HTML string**, and is used to parse and
  4077. * match within the text nodes of an HTML string. This method is used
  4078. * internally by {@link #parse}.
  4079. *
  4080. * @private
  4081. * @param {String} text The text to find matches within (depending on if the
  4082. * {@link #urls}, {@link #email}, {@link #phone},
  4083. * {@link #hashtag}, and {@link #mention} options are enabled). This must be a non-HTML string.
  4084. * @param {Number} [offset=0] The offset of the text node within the
  4085. * original string. This is used when parsing with the {@link #parse}
  4086. * method to generate correct offsets within the {@link Autolinker.match.Match}
  4087. * instances, but may be omitted if calling this method publicly.
  4088. * @return {Autolinker.match.Match[]} The array of Matches found in the
  4089. * given input `text`.
  4090. */
  4091. Autolinker.prototype.parseText = function (text, offset) {
  4092. if (offset === void 0) { offset = 0; }
  4093. offset = offset || 0;
  4094. var matchers = this.getMatchers(), matches = [];
  4095. for (var i = 0, numMatchers = matchers.length; i < numMatchers; i++) {
  4096. var textMatches = matchers[i].parseMatches(text);
  4097. // Correct the offset of each of the matches. They are originally
  4098. // the offset of the match within the provided text node, but we
  4099. // need to correct them to be relative to the original HTML input
  4100. // string (i.e. the one provided to #parse).
  4101. for (var j = 0, numTextMatches = textMatches.length; j < numTextMatches; j++) {
  4102. textMatches[j].setOffset(offset + textMatches[j].getOffset());
  4103. }
  4104. matches.push.apply(matches, textMatches);
  4105. }
  4106. return matches;
  4107. };
  4108. /**
  4109. * Automatically links URLs, Email addresses, Phone numbers, Hashtags,
  4110. * and Mentions (Twitter, Instagram, Soundcloud) found in the given chunk of HTML. Does not link
  4111. * URLs found within HTML tags.
  4112. *
  4113. * For instance, if given the text: `You should go to http://www.yahoo.com`,
  4114. * then the result will be `You should go to
  4115. * &lt;a href="http://www.yahoo.com"&gt;http://www.yahoo.com&lt;/a&gt;`
  4116. *
  4117. * This method finds the text around any HTML elements in the input
  4118. * `textOrHtml`, which will be the text that is processed. Any original HTML
  4119. * elements will be left as-is, as well as the text that is already wrapped
  4120. * in anchor (&lt;a&gt;) tags.
  4121. *
  4122. * @param {String} textOrHtml The HTML or text to autolink matches within
  4123. * (depending on if the {@link #urls}, {@link #email}, {@link #phone}, {@link #hashtag}, and {@link #mention} options are enabled).
  4124. * @return {String} The HTML, with matches automatically linked.
  4125. */
  4126. Autolinker.prototype.link = function (textOrHtml) {
  4127. if (!textOrHtml) {
  4128. return "";
  4129. } // handle `null` and `undefined` (for JavaScript users that don't have TypeScript support)
  4130. /* We would want to sanitize the start and end characters of a tag
  4131. * before processing the string in order to avoid an XSS scenario.
  4132. * This behaviour can be changed by toggling the sanitizeHtml option.
  4133. */
  4134. if (this.sanitizeHtml) {
  4135. textOrHtml = textOrHtml
  4136. .replace(/</g, '&lt;')
  4137. .replace(/>/g, '&gt;');
  4138. }
  4139. var matches = this.parse(textOrHtml), newHtml = [], lastIndex = 0;
  4140. for (var i = 0, len = matches.length; i < len; i++) {
  4141. var match = matches[i];
  4142. newHtml.push(textOrHtml.substring(lastIndex, match.getOffset()));
  4143. newHtml.push(this.createMatchReturnVal(match));
  4144. lastIndex = match.getOffset() + match.getMatchedText().length;
  4145. }
  4146. newHtml.push(textOrHtml.substring(lastIndex)); // handle the text after the last match
  4147. return newHtml.join('');
  4148. };
  4149. /**
  4150. * Creates the return string value for a given match in the input string.
  4151. *
  4152. * This method handles the {@link #replaceFn}, if one was provided.
  4153. *
  4154. * @private
  4155. * @param {Autolinker.match.Match} match The Match object that represents
  4156. * the match.
  4157. * @return {String} The string that the `match` should be replaced with.
  4158. * This is usually the anchor tag string, but may be the `matchStr` itself
  4159. * if the match is not to be replaced.
  4160. */
  4161. Autolinker.prototype.createMatchReturnVal = function (match) {
  4162. // Handle a custom `replaceFn` being provided
  4163. var replaceFnResult;
  4164. if (this.replaceFn) {
  4165. replaceFnResult = this.replaceFn.call(this.context, match); // Autolinker instance is the context
  4166. }
  4167. if (typeof replaceFnResult === 'string') {
  4168. return replaceFnResult; // `replaceFn` returned a string, use that
  4169. }
  4170. else if (replaceFnResult === false) {
  4171. return match.getMatchedText(); // no replacement for the match
  4172. }
  4173. else if (replaceFnResult instanceof HtmlTag) {
  4174. return replaceFnResult.toAnchorString();
  4175. }
  4176. else { // replaceFnResult === true, or no/unknown return value from function
  4177. // Perform Autolinker's default anchor tag generation
  4178. var anchorTag = match.buildTag(); // returns an Autolinker.HtmlTag instance
  4179. return anchorTag.toAnchorString();
  4180. }
  4181. };
  4182. /**
  4183. * Lazily instantiates and returns the {@link Autolinker.matcher.Matcher}
  4184. * instances for this Autolinker instance.
  4185. *
  4186. * @private
  4187. * @return {Autolinker.matcher.Matcher[]}
  4188. */
  4189. Autolinker.prototype.getMatchers = function () {
  4190. if (!this.matchers) {
  4191. var tagBuilder = this.getTagBuilder();
  4192. var matchers = [
  4193. new HashtagMatcher({ tagBuilder: tagBuilder, serviceName: this.hashtag }),
  4194. new EmailMatcher({ tagBuilder: tagBuilder }),
  4195. new PhoneMatcher({ tagBuilder: tagBuilder }),
  4196. new MentionMatcher({ tagBuilder: tagBuilder, serviceName: this.mention }),
  4197. new UrlMatcher({ tagBuilder: tagBuilder, stripPrefix: this.stripPrefix, stripTrailingSlash: this.stripTrailingSlash, decodePercentEncoding: this.decodePercentEncoding })
  4198. ];
  4199. return (this.matchers = matchers);
  4200. }
  4201. else {
  4202. return this.matchers;
  4203. }
  4204. };
  4205. /**
  4206. * Returns the {@link #tagBuilder} instance for this Autolinker instance,
  4207. * lazily instantiating it if it does not yet exist.
  4208. *
  4209. * @private
  4210. * @return {Autolinker.AnchorTagBuilder}
  4211. */
  4212. Autolinker.prototype.getTagBuilder = function () {
  4213. var tagBuilder = this.tagBuilder;
  4214. if (!tagBuilder) {
  4215. tagBuilder = this.tagBuilder = new AnchorTagBuilder({
  4216. newWindow: this.newWindow,
  4217. truncate: this.truncate,
  4218. className: this.className
  4219. });
  4220. }
  4221. return tagBuilder;
  4222. };
  4223. /**
  4224. * @static
  4225. * @property {String} version
  4226. *
  4227. * The Autolinker version number in the form major.minor.patch
  4228. *
  4229. * Ex: 0.25.1
  4230. */
  4231. Autolinker.version = '3.15.0';
  4232. /**
  4233. * For backwards compatibility with Autolinker 1.x, the AnchorTagBuilder
  4234. * class is provided as a static on the Autolinker class.
  4235. */
  4236. Autolinker.AnchorTagBuilder = AnchorTagBuilder;
  4237. /**
  4238. * For backwards compatibility with Autolinker 1.x, the HtmlTag class is
  4239. * provided as a static on the Autolinker class.
  4240. */
  4241. Autolinker.HtmlTag = HtmlTag;
  4242. /**
  4243. * For backwards compatibility with Autolinker 1.x, the Matcher classes are
  4244. * provided as statics on the Autolinker class.
  4245. */
  4246. Autolinker.matcher = {
  4247. Email: EmailMatcher,
  4248. Hashtag: HashtagMatcher,
  4249. Matcher: Matcher,
  4250. Mention: MentionMatcher,
  4251. Phone: PhoneMatcher,
  4252. Url: UrlMatcher
  4253. };
  4254. /**
  4255. * For backwards compatibility with Autolinker 1.x, the Match classes are
  4256. * provided as statics on the Autolinker class.
  4257. */
  4258. Autolinker.match = {
  4259. Email: EmailMatch,
  4260. Hashtag: HashtagMatch,
  4261. Match: Match,
  4262. Mention: MentionMatch,
  4263. Phone: PhoneMatch,
  4264. Url: UrlMatch
  4265. };
  4266. return Autolinker;
  4267. }());
  4268. export { Autolinker as default };