Autolinker.js 205 KB

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