diff --git a/resources/misc/previewInterop.js b/resources/misc/previewInterop.js index b77d2af2f79eaaf6f0ff9052c657b6cca622c496..aadaf9fac5547d9d4d5049bf9b42d400d7c6c570 100644 --- a/resources/misc/previewInterop.js +++ b/resources/misc/previewInterop.js @@ -10,8 +10,9 @@ function getPreviewInfo(messageId, url) { var title = null var description = null var image = null - if (!url.includes("http://") && !url.includes("https://")) { - url = "http://".concat(url) + var u = new URL(url) + if (u.protocol === '') { + url = "https://".concat(url) } var domain = (new URL(url)) fetch(url, { diff --git a/src/libclient/web-chatview/linkify-string.js b/src/libclient/web-chatview/linkify-string.js index 78c83e524902bab8e049f6d0d72eeab1612c7cdb..b7fb03f8991333cda04175c308b5d94cbbb1e0e0 100644 --- a/src/libclient/web-chatview/linkify-string.js +++ b/src/libclient/web-chatview/linkify-string.js @@ -1,118 +1,100 @@ /* - * Copyright (c) 2016 SoapBox Innovations Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. +* Copyright (c) 2021 SoapBox Innovations Inc. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. */ - -'use strict'; - -;(function (window, linkify) { - var linkifyString = function (linkify) { - 'use strict'; - - /** - Convert strings of text into linkable HTML text - */ - - var tokenize = linkify.tokenize; - var options = linkify.options; - var Options = options.Options; - - - function escapeText(text) { - return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); - } - - function escapeAttr(href) { - return href.replace(/"/g, '"'); - } - - function attributesToString(attributes) { - if (!attributes) { - return ''; - } - var result = []; - - for (var attr in attributes) { - var val = attributes[attr] + ''; - result.push(attr + '="' + escapeAttr(val) + '"'); - } - return result.join(' '); - } - - function linkifyStr(str, color='#0645AD') { - var opts = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; - - opts = new Options(opts); - - var tokens = tokenize(str); - var result = []; - - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i]; - - if (token.type === 'nl' && opts.nl2br) { - result.push('<br>\n'); - continue; - } else if (!token.isLink || !opts.check(token)) { - result.push(escapeText(token.toString())); - continue; - } - - var _opts$resolve = opts.resolve(token); - - var formatted = _opts$resolve.formatted; - var formattedHref = _opts$resolve.formattedHref; - var tagName = _opts$resolve.tagName; - var className = _opts$resolve.className; - var target = _opts$resolve.target; - var attributes = _opts$resolve.attributes; - - - var link = '<' + tagName + ' href="' + escapeAttr(formattedHref) + '"'; - - if (className) { - link += ' class="' + escapeAttr(className) + '"'; - } - - if (target) { - link += ' target="' + escapeAttr(target) + '"'; - } - - if (attributes) { - link += ' ' + attributesToString(attributes); - } - - link += ' style="color: '+color+';">' + escapeText(formatted) + '</' + tagName + '>'; - result.push(link); - } - - return result.join(''); - } - - if (!String.prototype.linkify) { - String.prototype.linkify = function (opts) { - return linkifyStr(this, opts); - }; - } - - return linkifyStr; - }(linkify); - window.linkifyStr = linkifyString; -})(window, linkify); +var linkifyStr = (function (linkifyjs) { + 'use strict'; + + /** + Convert strings of text into linkable HTML text + */ + + function escapeText(text) { + return text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); + } + + function escapeAttr(href) { + return href.replace(/"/g, '"'); + } + + function attributesToString(attributes) { + var result = []; + + for (var attr in attributes) { + var val = attributes[attr] + ''; + result.push(attr + "=\"" + escapeAttr(val) + "\""); + } + + return result.join(' '); + } + + function defaultRender(_ref) { + var tagName = _ref.tagName, + attributes = _ref.attributes, + content = _ref.content; + return "<" + tagName + " " + attributesToString(attributes) + ">" + escapeText(content) + "</" + tagName + ">"; + } + /** + * Convert a plan text string to an HTML string with links. Expects that the + * given strings does not contain any HTML entities. Use the linkify-html + * interface if you need to parse HTML entities. + * + * @param {string} str string to linkify + * @param {import('linkifyjs').Opts} [opts] overridable options + * @returns {string} + */ + + + function linkifyStr(str, opts) { + if (opts === void 0) { + opts = {}; + } + + opts = new linkifyjs.Options(opts, defaultRender); + var tokens = linkifyjs.tokenize(str); + var result = []; + + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + + if (token.t === 'nl' && opts.get('nl2br')) { + result.push('<br>\n'); + } else if (!token.isLink || !opts.check(token)) { + result.push(escapeText(token.toString())); + } else { + result.push(opts.render(token)); + } + } + + return result.join(''); + } + + if (!String.prototype.linkify) { + Object.defineProperty(String.prototype, 'linkify', { + writable: false, + value: function linkify(options) { + return linkifyStr(this, options); + } + }); + } + + return linkifyStr; + +})(linkify); \ No newline at end of file diff --git a/src/libclient/web-chatview/linkify.js b/src/libclient/web-chatview/linkify.js index 7666ff8aeb8b2e1960467601bc1a157b98b80f7b..7363cbdb42424d37879257ae390deeb917deeff7 100644 --- a/src/libclient/web-chatview/linkify.js +++ b/src/libclient/web-chatview/linkify.js @@ -1,584 +1,2023 @@ /* - * Copyright (c) 2016 SoapBox Innovations Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. +* Copyright (c) 2021 SoapBox Innovations Inc. +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. */ +var linkify = (function (exports) { + 'use strict'; -;(function () { -'use strict'; - -var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; }; + // THIS FILE IS AUTOMATICALLY GENERATED DO NOT EDIT DIRECTLY + // https://data.iana.org/TLD/tlds-alpha-by-domain.txt + var tlds = 'aaa \ +aarp \ +abarth \ +abb \ +abbott \ +abbvie \ +abc \ +able \ +abogado \ +abudhabi \ +ac \ +academy \ +accenture \ +accountant \ +accountants \ +aco \ +actor \ +ad \ +adac \ +ads \ +adult \ +ae \ +aeg \ +aero \ +aetna \ +af \ +afl \ +africa \ +ag \ +agakhan \ +agency \ +ai \ +aig \ +airbus \ +airforce \ +airtel \ +akdn \ +al \ +alfaromeo \ +alibaba \ +alipay \ +allfinanz \ +allstate \ +ally \ +alsace \ +alstom \ +am \ +amazon \ +americanexpress \ +americanfamily \ +amex \ +amfam \ +amica \ +amsterdam \ +analytics \ +android \ +anquan \ +anz \ +ao \ +aol \ +apartments \ +app \ +apple \ +aq \ +aquarelle \ +ar \ +arab \ +aramco \ +archi \ +army \ +arpa \ +art \ +arte \ +as \ +asda \ +asia \ +associates \ +at \ +athleta \ +attorney \ +au \ +auction \ +audi \ +audible \ +audio \ +auspost \ +author \ +auto \ +autos \ +avianca \ +aw \ +aws \ +ax \ +axa \ +az \ +azure \ +ba \ +baby \ +baidu \ +banamex \ +bananarepublic \ +band \ +bank \ +bar \ +barcelona \ +barclaycard \ +barclays \ +barefoot \ +bargains \ +baseball \ +basketball \ +bauhaus \ +bayern \ +bb \ +bbc \ +bbt \ +bbva \ +bcg \ +bcn \ +bd \ +be \ +beats \ +beauty \ +beer \ +bentley \ +berlin \ +best \ +bestbuy \ +bet \ +bf \ +bg \ +bh \ +bharti \ +bi \ +bible \ +bid \ +bike \ +bing \ +bingo \ +bio \ +biz \ +bj \ +black \ +blackfriday \ +blockbuster \ +blog \ +bloomberg \ +blue \ +bm \ +bms \ +bmw \ +bn \ +bnpparibas \ +bo \ +boats \ +boehringer \ +bofa \ +bom \ +bond \ +boo \ +book \ +booking \ +bosch \ +bostik \ +boston \ +bot \ +boutique \ +box \ +br \ +bradesco \ +bridgestone \ +broadway \ +broker \ +brother \ +brussels \ +bs \ +bt \ +bugatti \ +build \ +builders \ +business \ +buy \ +buzz \ +bv \ +bw \ +by \ +bz \ +bzh \ +ca \ +cab \ +cafe \ +cal \ +call \ +calvinklein \ +cam \ +camera \ +camp \ +cancerresearch \ +canon \ +capetown \ +capital \ +capitalone \ +car \ +caravan \ +cards \ +care \ +career \ +careers \ +cars \ +casa \ +case \ +cash \ +casino \ +cat \ +catering \ +catholic \ +cba \ +cbn \ +cbre \ +cbs \ +cc \ +cd \ +center \ +ceo \ +cern \ +cf \ +cfa \ +cfd \ +cg \ +ch \ +chanel \ +channel \ +charity \ +chase \ +chat \ +cheap \ +chintai \ +christmas \ +chrome \ +church \ +ci \ +cipriani \ +circle \ +cisco \ +citadel \ +citi \ +citic \ +city \ +cityeats \ +ck \ +cl \ +claims \ +cleaning \ +click \ +clinic \ +clinique \ +clothing \ +cloud \ +club \ +clubmed \ +cm \ +cn \ +co \ +coach \ +codes \ +coffee \ +college \ +cologne \ +com \ +comcast \ +commbank \ +community \ +company \ +compare \ +computer \ +comsec \ +condos \ +construction \ +consulting \ +contact \ +contractors \ +cooking \ +cookingchannel \ +cool \ +coop \ +corsica \ +country \ +coupon \ +coupons \ +courses \ +cpa \ +cr \ +credit \ +creditcard \ +creditunion \ +cricket \ +crown \ +crs \ +cruise \ +cruises \ +cu \ +cuisinella \ +cv \ +cw \ +cx \ +cy \ +cymru \ +cyou \ +cz \ +dabur \ +dad \ +dance \ +data \ +date \ +dating \ +datsun \ +day \ +dclk \ +dds \ +de \ +deal \ +dealer \ +deals \ +degree \ +delivery \ +dell \ +deloitte \ +delta \ +democrat \ +dental \ +dentist \ +desi \ +design \ +dev \ +dhl \ +diamonds \ +diet \ +digital \ +direct \ +directory \ +discount \ +discover \ +dish \ +diy \ +dj \ +dk \ +dm \ +dnp \ +do \ +docs \ +doctor \ +dog \ +domains \ +dot \ +download \ +drive \ +dtv \ +dubai \ +dunlop \ +dupont \ +durban \ +dvag \ +dvr \ +dz \ +earth \ +eat \ +ec \ +eco \ +edeka \ +edu \ +education \ +ee \ +eg \ +email \ +emerck \ +energy \ +engineer \ +engineering \ +enterprises \ +epson \ +equipment \ +er \ +ericsson \ +erni \ +es \ +esq \ +estate \ +et \ +etisalat \ +eu \ +eurovision \ +eus \ +events \ +exchange \ +expert \ +exposed \ +express \ +extraspace \ +fage \ +fail \ +fairwinds \ +faith \ +family \ +fan \ +fans \ +farm \ +farmers \ +fashion \ +fast \ +fedex \ +feedback \ +ferrari \ +ferrero \ +fi \ +fiat \ +fidelity \ +fido \ +film \ +final \ +finance \ +financial \ +fire \ +firestone \ +firmdale \ +fish \ +fishing \ +fit \ +fitness \ +fj \ +fk \ +flickr \ +flights \ +flir \ +florist \ +flowers \ +fly \ +fm \ +fo \ +foo \ +food \ +foodnetwork \ +football \ +ford \ +forex \ +forsale \ +forum \ +foundation \ +fox \ +fr \ +free \ +fresenius \ +frl \ +frogans \ +frontdoor \ +frontier \ +ftr \ +fujitsu \ +fun \ +fund \ +furniture \ +futbol \ +fyi \ +ga \ +gal \ +gallery \ +gallo \ +gallup \ +game \ +games \ +gap \ +garden \ +gay \ +gb \ +gbiz \ +gd \ +gdn \ +ge \ +gea \ +gent \ +genting \ +george \ +gf \ +gg \ +ggee \ +gh \ +gi \ +gift \ +gifts \ +gives \ +giving \ +gl \ +glass \ +gle \ +global \ +globo \ +gm \ +gmail \ +gmbh \ +gmo \ +gmx \ +gn \ +godaddy \ +gold \ +goldpoint \ +golf \ +goo \ +goodyear \ +goog \ +google \ +gop \ +got \ +gov \ +gp \ +gq \ +gr \ +grainger \ +graphics \ +gratis \ +green \ +gripe \ +grocery \ +group \ +gs \ +gt \ +gu \ +guardian \ +gucci \ +guge \ +guide \ +guitars \ +guru \ +gw \ +gy \ +hair \ +hamburg \ +hangout \ +haus \ +hbo \ +hdfc \ +hdfcbank \ +health \ +healthcare \ +help \ +helsinki \ +here \ +hermes \ +hgtv \ +hiphop \ +hisamitsu \ +hitachi \ +hiv \ +hk \ +hkt \ +hm \ +hn \ +hockey \ +holdings \ +holiday \ +homedepot \ +homegoods \ +homes \ +homesense \ +honda \ +horse \ +hospital \ +host \ +hosting \ +hot \ +hoteles \ +hotels \ +hotmail \ +house \ +how \ +hr \ +hsbc \ +ht \ +hu \ +hughes \ +hyatt \ +hyundai \ +ibm \ +icbc \ +ice \ +icu \ +id \ +ie \ +ieee \ +ifm \ +ikano \ +il \ +im \ +imamat \ +imdb \ +immo \ +immobilien \ +in \ +inc \ +industries \ +infiniti \ +info \ +ing \ +ink \ +institute \ +insurance \ +insure \ +int \ +international \ +intuit \ +investments \ +io \ +ipiranga \ +iq \ +ir \ +irish \ +is \ +ismaili \ +ist \ +istanbul \ +it \ +itau \ +itv \ +jaguar \ +java \ +jcb \ +je \ +jeep \ +jetzt \ +jewelry \ +jio \ +jll \ +jm \ +jmp \ +jnj \ +jo \ +jobs \ +joburg \ +jot \ +joy \ +jp \ +jpmorgan \ +jprs \ +juegos \ +juniper \ +kaufen \ +kddi \ +ke \ +kerryhotels \ +kerrylogistics \ +kerryproperties \ +kfh \ +kg \ +kh \ +ki \ +kia \ +kids \ +kim \ +kinder \ +kindle \ +kitchen \ +kiwi \ +km \ +kn \ +koeln \ +komatsu \ +kosher \ +kp \ +kpmg \ +kpn \ +kr \ +krd \ +kred \ +kuokgroup \ +kw \ +ky \ +kyoto \ +kz \ +la \ +lacaixa \ +lamborghini \ +lamer \ +lancaster \ +lancia \ +land \ +landrover \ +lanxess \ +lasalle \ +lat \ +latino \ +latrobe \ +law \ +lawyer \ +lb \ +lc \ +lds \ +lease \ +leclerc \ +lefrak \ +legal \ +lego \ +lexus \ +lgbt \ +li \ +lidl \ +life \ +lifeinsurance \ +lifestyle \ +lighting \ +like \ +lilly \ +limited \ +limo \ +lincoln \ +linde \ +link \ +lipsy \ +live \ +living \ +lk \ +llc \ +llp \ +loan \ +loans \ +locker \ +locus \ +loft \ +lol \ +london \ +lotte \ +lotto \ +love \ +lpl \ +lplfinancial \ +lr \ +ls \ +lt \ +ltd \ +ltda \ +lu \ +lundbeck \ +luxe \ +luxury \ +lv \ +ly \ +ma \ +macys \ +madrid \ +maif \ +maison \ +makeup \ +man \ +management \ +mango \ +map \ +market \ +marketing \ +markets \ +marriott \ +marshalls \ +maserati \ +mattel \ +mba \ +mc \ +mckinsey \ +md \ +me \ +med \ +media \ +meet \ +melbourne \ +meme \ +memorial \ +men \ +menu \ +merckmsd \ +mg \ +mh \ +miami \ +microsoft \ +mil \ +mini \ +mint \ +mit \ +mitsubishi \ +mk \ +ml \ +mlb \ +mls \ +mm \ +mma \ +mn \ +mo \ +mobi \ +mobile \ +moda \ +moe \ +moi \ +mom \ +monash \ +money \ +monster \ +mormon \ +mortgage \ +moscow \ +moto \ +motorcycles \ +mov \ +movie \ +mp \ +mq \ +mr \ +ms \ +msd \ +mt \ +mtn \ +mtr \ +mu \ +museum \ +music \ +mutual \ +mv \ +mw \ +mx \ +my \ +mz \ +na \ +nab \ +nagoya \ +name \ +natura \ +navy \ +nba \ +nc \ +ne \ +nec \ +net \ +netbank \ +netflix \ +network \ +neustar \ +new \ +news \ +next \ +nextdirect \ +nexus \ +nf \ +nfl \ +ng \ +ngo \ +nhk \ +ni \ +nico \ +nike \ +nikon \ +ninja \ +nissan \ +nissay \ +nl \ +no \ +nokia \ +northwesternmutual \ +norton \ +now \ +nowruz \ +nowtv \ +np \ +nr \ +nra \ +nrw \ +ntt \ +nu \ +nyc \ +nz \ +obi \ +observer \ +office \ +okinawa \ +olayan \ +olayangroup \ +oldnavy \ +ollo \ +om \ +omega \ +one \ +ong \ +onl \ +online \ +ooo \ +open \ +oracle \ +orange \ +org \ +organic \ +origins \ +osaka \ +otsuka \ +ott \ +ovh \ +pa \ +page \ +panasonic \ +paris \ +pars \ +partners \ +parts \ +party \ +passagens \ +pay \ +pccw \ +pe \ +pet \ +pf \ +pfizer \ +pg \ +ph \ +pharmacy \ +phd \ +philips \ +phone \ +photo \ +photography \ +photos \ +physio \ +pics \ +pictet \ +pictures \ +pid \ +pin \ +ping \ +pink \ +pioneer \ +pizza \ +pk \ +pl \ +place \ +play \ +playstation \ +plumbing \ +plus \ +pm \ +pn \ +pnc \ +pohl \ +poker \ +politie \ +porn \ +post \ +pr \ +pramerica \ +praxi \ +press \ +prime \ +pro \ +prod \ +productions \ +prof \ +progressive \ +promo \ +properties \ +property \ +protection \ +pru \ +prudential \ +ps \ +pt \ +pub \ +pw \ +pwc \ +py \ +qa \ +qpon \ +quebec \ +quest \ +racing \ +radio \ +re \ +read \ +realestate \ +realtor \ +realty \ +recipes \ +red \ +redstone \ +redumbrella \ +rehab \ +reise \ +reisen \ +reit \ +reliance \ +ren \ +rent \ +rentals \ +repair \ +report \ +republican \ +rest \ +restaurant \ +review \ +reviews \ +rexroth \ +rich \ +richardli \ +ricoh \ +ril \ +rio \ +rip \ +ro \ +rocher \ +rocks \ +rodeo \ +rogers \ +room \ +rs \ +rsvp \ +ru \ +rugby \ +ruhr \ +run \ +rw \ +rwe \ +ryukyu \ +sa \ +saarland \ +safe \ +safety \ +sakura \ +sale \ +salon \ +samsclub \ +samsung \ +sandvik \ +sandvikcoromant \ +sanofi \ +sap \ +sarl \ +sas \ +save \ +saxo \ +sb \ +sbi \ +sbs \ +sc \ +sca \ +scb \ +schaeffler \ +schmidt \ +scholarships \ +school \ +schule \ +schwarz \ +science \ +scot \ +sd \ +se \ +search \ +seat \ +secure \ +security \ +seek \ +select \ +sener \ +services \ +ses \ +seven \ +sew \ +sex \ +sexy \ +sfr \ +sg \ +sh \ +shangrila \ +sharp \ +shaw \ +shell \ +shia \ +shiksha \ +shoes \ +shop \ +shopping \ +shouji \ +show \ +showtime \ +si \ +silk \ +sina \ +singles \ +site \ +sj \ +sk \ +ski \ +skin \ +sky \ +skype \ +sl \ +sling \ +sm \ +smart \ +smile \ +sn \ +sncf \ +so \ +soccer \ +social \ +softbank \ +software \ +sohu \ +solar \ +solutions \ +song \ +sony \ +soy \ +spa \ +space \ +sport \ +spot \ +sr \ +srl \ +ss \ +st \ +stada \ +staples \ +star \ +statebank \ +statefarm \ +stc \ +stcgroup \ +stockholm \ +storage \ +store \ +stream \ +studio \ +study \ +style \ +su \ +sucks \ +supplies \ +supply \ +support \ +surf \ +surgery \ +suzuki \ +sv \ +swatch \ +swiss \ +sx \ +sy \ +sydney \ +systems \ +sz \ +tab \ +taipei \ +talk \ +taobao \ +target \ +tatamotors \ +tatar \ +tattoo \ +tax \ +taxi \ +tc \ +tci \ +td \ +tdk \ +team \ +tech \ +technology \ +tel \ +temasek \ +tennis \ +teva \ +tf \ +tg \ +th \ +thd \ +theater \ +theatre \ +tiaa \ +tickets \ +tienda \ +tiffany \ +tips \ +tires \ +tirol \ +tj \ +tjmaxx \ +tjx \ +tk \ +tkmaxx \ +tl \ +tm \ +tmall \ +tn \ +to \ +today \ +tokyo \ +tools \ +top \ +toray \ +toshiba \ +total \ +tours \ +town \ +toyota \ +toys \ +tr \ +trade \ +trading \ +training \ +travel \ +travelchannel \ +travelers \ +travelersinsurance \ +trust \ +trv \ +tt \ +tube \ +tui \ +tunes \ +tushu \ +tv \ +tvs \ +tw \ +tz \ +ua \ +ubank \ +ubs \ +ug \ +uk \ +unicom \ +university \ +uno \ +uol \ +ups \ +us \ +uy \ +uz \ +va \ +vacations \ +vana \ +vanguard \ +vc \ +ve \ +vegas \ +ventures \ +verisign \ +vermögensberater \ +vermögensberatung \ +versicherung \ +vet \ +vg \ +vi \ +viajes \ +video \ +vig \ +viking \ +villas \ +vin \ +vip \ +virgin \ +visa \ +vision \ +viva \ +vivo \ +vlaanderen \ +vn \ +vodka \ +volkswagen \ +volvo \ +vote \ +voting \ +voto \ +voyage \ +vu \ +vuelos \ +wales \ +walmart \ +walter \ +wang \ +wanggou \ +watch \ +watches \ +weather \ +weatherchannel \ +webcam \ +weber \ +website \ +wed \ +wedding \ +weibo \ +weir \ +wf \ +whoswho \ +wien \ +wiki \ +williamhill \ +win \ +windows \ +wine \ +winners \ +wme \ +wolterskluwer \ +woodside \ +work \ +works \ +world \ +wow \ +ws \ +wtc \ +wtf \ +xbox \ +xerox \ +xfinity \ +xihuan \ +xin \ +xxx \ +xyz \ +yachts \ +yahoo \ +yamaxun \ +yandex \ +ye \ +yodobashi \ +yoga \ +yokohama \ +you \ +youtube \ +yt \ +yun \ +za \ +zappos \ +zara \ +zero \ +zip \ +zm \ +zone \ +zuerich \ +zw'.split(' '); // Internationalized domain names containing non-ASCII + + var utlds = 'ελ \ +ευ \ +бг \ +бел \ +дети \ +ею \ +католик \ +ком \ +мкд \ +мон \ +моÑква \ +онлайн \ +орг \ +Ñ€ÑƒÑ \ +рф \ +Ñайт \ +Ñрб \ +укр \ +қаз \ +Õ°Õ¡Õµ \ +ישר×ל \ +×§×•× \ +ابوظبي \ +اتصالات \ +ارامكو \ +الاردن \ +البØرين \ +الجزائر \ +السعودية \ +العليان \ +المغرب \ +امارات \ +ایران \ +بارت \ +بازار \ +بيتك \ +بھارت \ +تونس \ +سودان \ +سورية \ +شبكة \ +عراق \ +عرب \ +عمان \ +Ùلسطين \ +قطر \ +كاثوليك \ +كوم \ +مصر \ +مليسيا \ +موريتانيا \ +موقع \ +همراه \ +پاکستان \ +ڀارت \ +कॉम \ +नेट \ +à¤à¤¾à¤°à¤¤ \ +à¤à¤¾à¤°à¤¤à¤®à¥ \ +à¤à¤¾à¤°à¥‹à¤¤ \ +संगठन \ +বাংলা \ +à¦à¦¾à¦°à¦¤ \ +à¦à¦¾à§°à¦¤ \ +à¨à¨¾à¨°à¨¤ \ +àªàª¾àª°àª¤ \ +à¬à¬¾à¬°à¬¤ \ +இநà¯à®¤à®¿à®¯à®¾ \ +இலஙà¯à®•à¯ˆ \ +சிஙà¯à®•à®ªà¯à®ªà¯‚ர௠\ +à°à°¾à°°à°¤à± \ +à²à²¾à²°à²¤ \ +à´à´¾à´°à´¤à´‚ \ +ලංක෠\ +คà¸à¸¡ \ +ไทย \ +ລາວ \ +გე \ +ã¿ã‚“㪠\ +アマゾン \ +クラウド \ +グーグル \ +コム\ +ストア \ +セール \ +ファッション \ +ãƒã‚¤ãƒ³ãƒˆ \ +世界 \ +ä¸ä¿¡ \ +ä¸å›½ \ +ä¸åœ‹ \ +ä¸æ–‡ç½‘ \ +亚马逊 \ +ä¼ä¸š \ +佛山 \ +ä¿¡æ¯ \ +å¥åº· \ +å…«å¦ \ +å…¬å¸ \ +公益 \ +å°æ¹¾ \ +å°ç£ \ +商城 \ +商店 \ +å•†æ ‡ \ +嘉里 \ +嘉里大酒店 \ +在线 \ +大拿 \ +天主教 \ +å¨±ä¹ \ +家電 \ +广东 \ +å¾®åš \ +慈善 \ +æˆ‘çˆ±ä½ \ +手机 \ +æ‹›è˜ \ +政务 \ +政府 \ +æ–°åŠ å¡ \ +æ–°é—» \ +时尚 \ +æ›¸ç± \ +机构 \ +淡马锡 \ +æ¸¸æˆ \ +澳門 \ +点看 \ +移动 \ +组织机构 \ +ç½‘å€ \ +网店 \ +网站 \ +网络 \ +è”通 \ +诺基亚 \ +è°·æŒ \ +è´ç‰© \ +通販 \ +集团 \ +電訊盈科 \ +飞利浦 \ +é£Ÿå“ \ +é¤åŽ… \ +é¦™æ ¼é‡Œæ‹‰ \ +香港 \ +ë‹·ë„· \ +ë‹·ì»´ \ +삼성 \ +í•œêµ'.split(' '); -(function (exports) { - 'use strict'; + /** + * @template A + * @template B + * @param {A} target + * @param {B} properties + * @return {A & B} + */ + var assign = function assign(target, properties) { + for (var key in properties) { + target[key] = properties[key]; + } + + return target; + }; - function inherits(parent, child) { - var props = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + /** + * Finite State Machine generation utilities + */ + /** + * @template T + * @typedef {{ [group: string]: T[] }} Collections + */ - var extended = Object.create(parent.prototype); - for (var p in props) { - extended[p] = props[p]; - } - extended.constructor = child; - child.prototype = extended; - return child; + /** + * @typedef {{ [group: string]: true }} Flags + */ + // Keys in scanner Collections instances + + var numeric = 'numeric'; + var ascii = 'ascii'; + var alpha = 'alpha'; + var asciinumeric = 'asciinumeric'; + var alphanumeric = 'alphanumeric'; + var domain = 'domain'; + var emoji = 'emoji'; + var scheme = 'scheme'; + var slashscheme = 'slashscheme'; + var whitespace = 'whitespace'; + /** + * @template T + * @param {string} name + * @param {Collections<T>} groups to register in + * @returns {T[]} Current list of tokens in the given collection + */ + + function registerGroup(name, groups) { + if (!(name in groups)) { + groups[name] = []; + } + + return groups[name]; } - - var defaults = { - defaultProtocol: 'http', - events: null, - format: noop, - formatHref: noop, - nl2br: false, - tagName: 'a', - target: typeToTarget, - validate: true, - ignoreTags: [], - attributes: null, - className: 'linkified' }; - - function Options(opts) { - opts = opts || {}; - - this.defaultProtocol = opts.defaultProtocol || defaults.defaultProtocol; - this.events = opts.events || defaults.events; - this.format = opts.format || defaults.format; - this.formatHref = opts.formatHref || defaults.formatHref; - this.nl2br = opts.nl2br || defaults.nl2br; - this.tagName = opts.tagName || defaults.tagName; - this.target = opts.target || defaults.target; - this.validate = opts.validate || defaults.validate; - this.ignoreTags = []; - - // linkAttributes and linkClass is deprecated - this.attributes = opts.attributes || opts.linkAttributes || defaults.attributes; - this.className = opts.className || opts.linkClass || defaults.className; - - // Make all tags names upper case - - var ignoredTags = opts.ignoreTags || defaults.ignoreTags; - for (var i = 0; i < ignoredTags.length; i++) { - this.ignoreTags.push(ignoredTags[i].toUpperCase()); - } + /** + * @template T + * @param {T} t token to add + * @param {Collections<T>} groups + * @param {Flags} flags + */ + + + function addToGroups(t, flags, groups) { + if (flags[numeric]) { + flags[asciinumeric] = true; + flags[alphanumeric] = true; + } + + if (flags[ascii]) { + flags[asciinumeric] = true; + flags[alpha] = true; + } + + if (flags[asciinumeric]) { + flags[alphanumeric] = true; + } + + if (flags[alpha]) { + flags[alphanumeric] = true; + } + + if (flags[alphanumeric]) { + flags[domain] = true; + } + + if (flags[emoji]) { + flags[domain] = true; + } + + for (var k in flags) { + var group = registerGroup(k, groups); + + if (group.indexOf(t) < 0) { + group.push(t); + } + } } - - Options.prototype = { - /** - * Given the token, return all options for how it should be displayed - */ - resolve: function resolve(token) { - var href = token.toHref(this.defaultProtocol); - return { - formatted: this.get('format', token.toString(), token), - formattedHref: this.get('formatHref', href, token), - tagName: this.get('tagName', href, token), - className: this.get('className', href, token), - target: this.get('target', href, token), - events: this.getObject('events', href, token), - attributes: this.getObject('attributes', href, token) - }; - }, - - - /** - * Returns true or false based on whether a token should be displayed as a - * link based on the user options. By default, - */ - check: function check(token) { - return this.get('validate', token.toString(), token); - }, - - - // Private methods - - /** - * Resolve an option's value based on the value of the option and the given - * params. - * @param [String] key Name of option to use - * @param operator will be passed to the target option if it's method - * @param [MultiToken] token The token from linkify.tokenize - */ - get: function get(key, operator, token) { - var option = this[key]; - - if (!option) { - return option; - } - - switch (typeof option === 'undefined' ? 'undefined' : _typeof(option)) { - case 'function': - return option(operator, token.type); - case 'object': - var optionValue = option[token.type] || defaults[key]; - return typeof optionValue === 'function' ? optionValue(operator, token.type) : optionValue; - } - - return option; - }, - getObject: function getObject(key, operator, token) { - var option = this[key]; - return typeof option === 'function' ? option(operator, token.type) : option; - } - }; - /** - * Quick indexOf replacement for checking the ignoreTags option - */ - function contains(arr, value) { - for (var i = 0; i < arr.length; i++) { - if (arr[i] === value) { - return true; - } - } - return false; + * @template T + * @param {T} t token to check + * @param {Collections<T>} groups + * @returns {Flags} group flags that contain this token + */ + + function flagsForToken(t, groups) { + var result = {}; + + for (var c in groups) { + if (groups[c].indexOf(t) >= 0) { + result[c] = true; + } + } + + return result; } + /** + * @template T + * @typedef {null | T } Transition + */ - function noop(val) { - return val; - } + /** + * Define a basic state machine state. j is the list of character transitions, + * jr is the list of regex-match transitions, jd is the default state to + * transition to t is the accepting token type, if any. If this is the terminal + * state, then it does not emit a token. + * + * The template type T represents the type of the token this state accepts. This + * should be a string (such as of the token exports in `text.js`) or a + * MultiToken subclass (from `multi.js`) + * + * @template T + * @param {T} [token] Token that this state emits + */ - function typeToTarget(href, type) { - return type === 'url' ? '_blank' : null; - } - var options = Object.freeze({ - defaults: defaults, - Options: Options, - contains: contains - }); + function State(token) { + if (token === void 0) { + token = null; + } + + // this.n = null; // DEBUG: State name + + /** @type {{ [input: string]: State<T> }} j */ + this.j = {}; // IMPLEMENTATION 1 + // this.j = []; // IMPLEMENTATION 2 + + /** @type {[RegExp, State<T>][]} jr */ - function createStateClass() { - return function (tClass) { - this.j = []; - this.T = tClass || null; - }; + this.jr = []; + /** @type {?State<T>} jd */ + + this.jd = null; + /** @type {?T} t */ + + this.t = token; } + /** + * Scanner token groups + * @type Collections<string> + */ + + State.groups = {}; + State.prototype = { + accepts: function accepts() { + return !!this.t; + }, + + /** + * Follow an existing transition from the given input to the next state. + * Does not mutate. + * @param {string} input character or token type to transition on + * @returns {?State<T>} the next state, if any + */ + go: function go(input) { + var state = this; + var nextState = state.j[input]; + + if (nextState) { + return nextState; + } + + for (var i = 0; i < state.jr.length; i++) { + var regex = state.jr[i][0]; + var _nextState = state.jr[i][1]; // note: might be empty to prevent default jump + + if (_nextState && regex.test(input)) { + return _nextState; + } + } // Nowhere left to jump! Return default, if any + + + return state.jd; + }, + + /** + * Whether the state has a transition for the given input. Set the second + * argument to true to only look for an exact match (and not a default or + * regular-expression-based transition) + * @param {string} input + * @param {boolean} exactOnly + */ + has: function has(input, exactOnly) { + if (exactOnly === void 0) { + exactOnly = false; + } + + return exactOnly ? input in this.j : !!this.go(input); + }, + + /** + * Short for "transition all"; create a transition from the array of items + * in the given list to the same final resulting state. + * @param {string | string[]} inputs Group of inputs to transition on + * @param {Transition<T> | State<T>} [next] Transition options + * @param {Flags} [flags] Collections flags to add token to + * @param {Collections<T>} [groups] Master list of token groups + */ + ta: function ta(inputs, next, flags, groups) { + for (var i = 0; i < inputs.length; i++) { + this.tt(inputs[i], next, flags, groups); + } + }, + + /** + * Short for "take regexp transition"; defines a transition for this state + * when it encounters a token which matches the given regular expression + * @param {RegExp} regexp Regular expression transition (populate first) + * @param {T | State<T>} [next] Transition options + * @param {Flags} [flags] Collections flags to add token to + * @param {Collections<T>} [groups] Master list of token groups + * @returns {State<T>} taken after the given input + */ + tr: function tr(regexp, next, flags, groups) { + groups = groups || State.groups; + var nextState; + + if (next && next.j) { + nextState = next; + } else { + // Token with maybe token groups + nextState = new State(next); + + if (flags && groups) { + addToGroups(next, flags, groups); + } + } + + this.jr.push([regexp, nextState]); + return nextState; + }, + + /** + * Short for "take transitions", will take as many sequential transitions as + * the length of the given input and returns the + * resulting final state. + * @param {string | string[]} input + * @param {T | State<T>} [next] Transition options + * @param {Flags} [flags] Collections flags to add token to + * @param {Collections<T>} [groups] Master list of token groups + * @returns {State<T>} taken after the given input + */ + ts: function ts(input, next, flags, groups) { + var state = this; + var len = input.length; + + if (!len) { + return state; + } + + for (var i = 0; i < len - 1; i++) { + state = state.tt(input[i]); + } + + return state.tt(input[len - 1], next, flags, groups); + }, + + /** + * Short for "take transition", this is a method for building/working with + * state machines. + * + * If a state already exists for the given input, returns it. + * + * If a token is specified, that state will emit that token when reached by + * the linkify engine. + * + * If no state exists, it will be initialized with some default transitions + * that resemble existing default transitions. + * + * If a state is given for the second argument, that state will be + * transitioned to on the given input regardless of what that input + * previously did. + * + * Specify a token group flags to define groups that this token belongs to. + * The token will be added to corresponding entires in the given groups + * object. + * + * @param {string} input character, token type to transition on + * @param {T | State<T>} [next] Transition options + * @param {Flags} [flags] Collections flags to add token to + * @param {Collections<T>} [groups] Master list of groups + * @returns {State<T>} taken after the given input + */ + tt: function tt(input, next, flags, groups) { + groups = groups || State.groups; + var state = this; // Check if existing state given, just a basic transition + + if (next && next.j) { + state.j[input] = next; + return next; + } + + var t = next; // Take the transition with the usual default mechanisms and use that as + // a template for creating the next state + + var nextState, + templateState = state.go(input); + + if (templateState) { + nextState = new State(); + assign(nextState.j, templateState.j); + nextState.jr.push.apply(nextState.jr, templateState.jr); + nextState.jd = templateState.jd; + nextState.t = templateState.t; + } else { + nextState = new State(); + } + + if (t) { + // Ensure newly token is in the same groups as the old token + if (groups) { + if (nextState.t && typeof nextState.t === 'string') { + var allFlags = assign(flagsForToken(nextState.t, groups), flags); + addToGroups(t, allFlags, groups); + } else if (flags) { + addToGroups(t, flags, groups); + } + } + + nextState.t = t; // overwrite anything that was previously there + } + + state.j[input] = nextState; + return nextState; + } + }; // Helper functions to improve minification (not exported outside linkifyjs module) /** - A simple state machine that can emit token classes - - The `j` property in this class refers to state jumps. It's a - multidimensional array where for each element: - - * index [0] is a symbol or class of symbols to transition to. - * index [1] is a State instance which matches - - The type of symbol will depend on the target implementation for this class. - In Linkify, we have a two-stage scanner. Each stage uses this state machine - but with a slighly different (polymorphic) implementation. - - The `T` property refers to the token class. - - TODO: Can the `on` and `next` methods be combined? - - @class BaseState - */ - var BaseState = createStateClass(); - BaseState.prototype = { - defaultTransition: false, - - /** - @method constructor - @param {Class} tClass Pass in the kind of token to emit if there are - no jumps after this state and the state is accepting. - */ - - /** - On the given symbol(s), this machine should go to the given state - @method on - @param {Array|Mixed} symbol - @param {BaseState} state Note that the type of this state should be the - same as the current instance (i.e., don't pass in a different - subclass) - */ - on: function on(symbol, state) { - if (symbol instanceof Array) { - for (var i = 0; i < symbol.length; i++) { - this.j.push([symbol[i], state]); - } - return this; - } - this.j.push([symbol, state]); - return this; - }, - - - /** - Given the next item, returns next state for that item - @method next - @param {Mixed} item Should be an instance of the symbols handled by - this particular machine. - @return {State} state Returns false if no jumps are available - */ - next: function next(item) { - for (var i = 0; i < this.j.length; i++) { - var jump = this.j[i]; - var symbol = jump[0]; // Next item to check for - var state = jump[1]; // State to jump to if items match - - // compare item with symbol - if (this.test(item, symbol)) { - return state; - } - } - - // Nowhere left to jump! - return this.defaultTransition; - }, - - - /** - Does this state accept? - `true` only of `this.T` exists - @method accepts - @return {Boolean} - */ - accepts: function accepts() { - return !!this.T; - }, - - - /** - Determine whether a given item "symbolizes" the symbol, where symbol is - a class of items handled by this state machine. - This method should be overridden in extended classes. - @method test - @param {Mixed} item Does this item match the given symbol? - @param {Mixed} symbol - @return {Boolean} - */ - test: function test(item, symbol) { - return item === symbol; - }, - - - /** - Emit the token for this State (just return it in this case) - If this emits a token, this instance is an accepting state - @method emit - @return {Class} T - */ - emit: function emit() { - return this.T; - } + * @template T + * @param {State<T>} state + * @param {string | string[]} input + * @param {Flags} [flags] + * @param {Collections<T>} [groups] + */ + + var ta = function ta(state, input, next, flags, groups) { + return state.ta(input, next, flags, groups); }; - /** - State machine for string-based input - - @class CharacterState - @extends BaseState - */ - var CharacterState = inherits(BaseState, createStateClass(), { - /** - Does the given character match the given character or regular - expression? - @method test - @param {String} char - @param {String|RegExp} charOrRegExp - @return {Boolean} - */ - test: function test(character, charOrRegExp) { - return character === charOrRegExp || charOrRegExp instanceof RegExp && charOrRegExp.test(character); - } - }); - + * @template T + * @param {State<T>} state + * @param {RegExp} regexp + * @param {T | State<T>} [next] + * @param {Flags} [flags] + * @param {Collections<T>} [groups] + */ + + var tr = function tr(state, regexp, next, flags, groups) { + return state.tr(regexp, next, flags, groups); + }; /** - State machine for input in the form of TextTokens - - @class TokenState - @extends BaseState - */ - var State = inherits(BaseState, createStateClass(), { - - /** - * Similar to `on`, but returns the state the results in the transition from - * the given item - * @method jump - * @param {Mixed} item - * @param {Token} [token] - * @return state - */ - jump: function jump(token) { - var tClass = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; - - var state = this.next(new token('')); // dummy temp token - if (state === this.defaultTransition) { - // Make a new state! - state = new this.constructor(tClass); - this.on(token, state); - } else if (tClass) { - state.T = tClass; - } - return state; - }, - - - /** - Is the given token an instance of the given token class? - @method test - @param {TextToken} token - @param {Class} tokenClass - @return {Boolean} - */ - test: function test(token, tokenClass) { - return token instanceof tokenClass; - } - }); - + * @template T + * @param {State<T>} state + * @param {string | string[]} input + * @param {T | State<T>} [next] + * @param {Flags} [flags] + * @param {Collections<T>} [groups] + */ + + var ts = function ts(state, input, next, flags, groups) { + return state.ts(input, next, flags, groups); + }; /** - Given a non-empty target string, generates states (if required) for each - consecutive substring of characters in str starting from the beginning of - the string. The final state will have a special value, as specified in - options. All other "in between" substrings will have a default end state. - - This turns the state machine into a Trie-like data structure (rather than a - intelligently-designed DFA). - - Note that I haven't really tried these with any strings other than - DOMAIN. - - @param {String} str - @param {CharacterState} start State to jump from the first character - @param {Class} endToken Token class to emit when the given string has been - matched and no more jumps exist. - @param {Class} defaultToken "Filler token", or which token type to emit when - we don't have a full match - @return {Array} list of newly-created states - */ - function stateify(str, start, endToken, defaultToken) { - var i = 0, - len = str.length, - state = start, - newStates = [], - nextState = void 0; - - // Find the next state without a jump to the next character - while (i < len && (nextState = state.next(str[i]))) { - state = nextState; - i++; - } - - if (i >= len) { - return []; - } // no new tokens were added - - while (i < len - 1) { - nextState = new CharacterState(defaultToken); - newStates.push(nextState); - state.on(str[i], nextState); - state = nextState; - i++; - } - - nextState = new CharacterState(endToken); - newStates.push(nextState); - state.on(str[len - 1], nextState); - - return newStates; - } - - function createTokenClass() { - return function (value) { - if (value) { - this.v = value; - } - }; - } + * @template T + * @param {State<T>} state + * @param {string} input + * @param {T | State<T>} [next] + * @param {Collections<T>} [groups] + * @param {Flags} [flags] + */ + + var tt = function tt(state, input, next, flags, groups) { + return state.tt(input, next, flags, groups); + }; /****************************************************************************** - Text Tokens - Tokens composed of strings - ******************************************************************************/ + Text Tokens + Identifiers for token outputs from the regexp scanner + ******************************************************************************/ + // A valid web domain token + var WORD = 'WORD'; // only contains a-z - /** - Abstract class used for manufacturing text tokens. - Pass in the value this token represents - - @class TextToken - @abstract - */ - var TextToken = createTokenClass(); - TextToken.prototype = { - toString: function toString() { - return this.v + ''; - } - }; + var UWORD = 'UWORD'; // contains letters other than a-z, used for IDN + // Special case of word - function inheritsToken(value) { - var props = value ? { v: value } : {}; - return inherits(TextToken, createTokenClass(), props); - } + var LOCALHOST = 'LOCALHOST'; // Valid top-level domain, special case of WORD (see tlds.js) - /** - A valid domain token - @class DOMAIN - @extends TextToken - */ - var DOMAIN = inheritsToken(); + var TLD = 'TLD'; // Valid IDN TLD, special case of UWORD (see tlds.js) - /** - @class AT - @extends TextToken - */ - var AT = inheritsToken('@'); + var UTLD = 'UTLD'; // The scheme portion of a web URI protocol. Supported types include: `mailto`, + // `file`, and user-defined custom protocols. Limited to schemes that contain + // only letters - /** - Represents a single colon `:` character + var SCHEME = 'SCHEME'; // Similar to SCHEME, except makes distinction for schemes that must always be + // followed by `://`, not just `:`. Supported types include `http`, `https`, + // `ftp`, `ftps` - @class COLON - @extends TextToken - */ - var COLON = inheritsToken(':'); + var SLASH_SCHEME = 'SLASH_SCHEME'; // Any sequence of digits 0-9 - /** - @class DOT - @extends TextToken - */ - var DOT = inheritsToken('.'); + var NUM = 'NUM'; // Any number of consecutive whitespace characters that are not newline - /** - A character class that can surround the URL, but which the URL cannot begin - or end with. Does not include certain English punctuation like parentheses. + var WS = 'WS'; // New line (unix style) - @class PUNCTUATION - @extends TextToken - */ - var PUNCTUATION = inheritsToken(); + var NL$1 = 'NL'; // \n + // Opening/closing bracket classes - /** - The word localhost (by itself) - @class LOCALHOST - @extends TextToken - */ - var LOCALHOST = inheritsToken(); + var OPENBRACE = 'OPENBRACE'; // { - /** - Newline token - @class NL - @extends TextToken - */ - var TNL = inheritsToken('\n'); + var OPENBRACKET = 'OPENBRACKET'; // [ - /** - @class NUM - @extends TextToken - */ - var NUM = inheritsToken(); + var OPENANGLEBRACKET = 'OPENANGLEBRACKET'; // < - /** - @class PLUS - @extends TextToken - */ - var PLUS = inheritsToken('+'); + var OPENPAREN = 'OPENPAREN'; // ( - /** - @class POUND - @extends TextToken - */ - var POUND = inheritsToken('#'); + var CLOSEBRACE = 'CLOSEBRACE'; // } - /** - Represents a web URL protocol. Supported types include + var CLOSEBRACKET = 'CLOSEBRACKET'; // ] - * `http:` - * `https:` - * `ftp:` - * `ftps:` - * There's Another super weird one + var CLOSEANGLEBRACKET = 'CLOSEANGLEBRACKET'; // > - @class PROTOCOL - @extends TextToken - */ - var PROTOCOL = inheritsToken(); + var CLOSEPAREN = 'CLOSEPAREN'; // ) + // Various symbols - /** - @class QUERY - @extends TextToken - */ - var QUERY = inheritsToken('?'); + var AMPERSAND = 'AMPERSAND'; // & - /** - @class SLASH - @extends TextToken - */ - var SLASH = inheritsToken('/'); + var APOSTROPHE = 'APOSTROPHE'; // ' - /** - @class UNDERSCORE - @extends TextToken - */ - var UNDERSCORE = inheritsToken('_'); + var ASTERISK = 'ASTERISK'; // * - /** - One ore more non-whitespace symbol. - @class SYM - @extends TextToken - */ - var SYM = inheritsToken(); + var AT = 'AT'; // @ - /** - @class TLD - @extends TextToken - */ - var TLD = inheritsToken(); + var BACKSLASH = 'BACKSLASH'; // \ - /** - Represents a string of consecutive whitespace characters + var BACKTICK = 'BACKTICK'; // ` - @class WS - @extends TextToken - */ - var WS = inheritsToken(); + var CARET = 'CARET'; // ^ - /** - Opening/closing bracket classes - */ - - var OPENBRACE = inheritsToken('{'); - var OPENBRACKET = inheritsToken('['); - var OPENANGLEBRACKET = inheritsToken('<'); - var OPENPAREN = inheritsToken('('); - var CLOSEBRACE = inheritsToken('}'); - var CLOSEBRACKET = inheritsToken(']'); - var CLOSEANGLEBRACKET = inheritsToken('>'); - var CLOSEPAREN = inheritsToken(')'); - - var TOKENS = Object.freeze({ - Base: TextToken, - DOMAIN: DOMAIN, - AT: AT, - COLON: COLON, - DOT: DOT, - PUNCTUATION: PUNCTUATION, + var COLON = 'COLON'; // : + + var COMMA = 'COMMA'; // , + + var DOLLAR = 'DOLLAR'; // $ + + var DOT = 'DOT'; // . + + var EQUALS = 'EQUALS'; // = + + var EXCLAMATION = 'EXCLAMATION'; // ! + + var HYPHEN = 'HYPHEN'; // - + + var PERCENT = 'PERCENT'; // % + + var PIPE = 'PIPE'; // | + + var PLUS = 'PLUS'; // + + + var POUND = 'POUND'; // # + + var QUERY = 'QUERY'; // ? + + var QUOTE = 'QUOTE'; // " + + var SEMI = 'SEMI'; // ; + + var SLASH = 'SLASH'; // / + + var TILDE = 'TILDE'; // ~ + + var UNDERSCORE = 'UNDERSCORE'; // _ + // Emoji symbol + + var EMOJI$1 = 'EMOJI'; // Default token - anything that is not one of the above + + var SYM = 'SYM'; + + var tk = /*#__PURE__*/Object.freeze({ + __proto__: null, + WORD: WORD, + UWORD: UWORD, LOCALHOST: LOCALHOST, - NL: TNL, - NUM: NUM, - PLUS: PLUS, - POUND: POUND, - QUERY: QUERY, - PROTOCOL: PROTOCOL, - SLASH: SLASH, - UNDERSCORE: UNDERSCORE, - SYM: SYM, TLD: TLD, + UTLD: UTLD, + SCHEME: SCHEME, + SLASH_SCHEME: SLASH_SCHEME, + NUM: NUM, WS: WS, + NL: NL$1, OPENBRACE: OPENBRACE, OPENBRACKET: OPENBRACKET, OPENANGLEBRACKET: OPENANGLEBRACKET, @@ -586,686 +2025,1477 @@ var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol CLOSEBRACE: CLOSEBRACE, CLOSEBRACKET: CLOSEBRACKET, CLOSEANGLEBRACKET: CLOSEANGLEBRACKET, - CLOSEPAREN: CLOSEPAREN + CLOSEPAREN: CLOSEPAREN, + AMPERSAND: AMPERSAND, + APOSTROPHE: APOSTROPHE, + ASTERISK: ASTERISK, + AT: AT, + BACKSLASH: BACKSLASH, + BACKTICK: BACKTICK, + CARET: CARET, + COLON: COLON, + COMMA: COMMA, + DOLLAR: DOLLAR, + DOT: DOT, + EQUALS: EQUALS, + EXCLAMATION: EXCLAMATION, + HYPHEN: HYPHEN, + PERCENT: PERCENT, + PIPE: PIPE, + PLUS: PLUS, + POUND: POUND, + QUERY: QUERY, + QUOTE: QUOTE, + SEMI: SEMI, + SLASH: SLASH, + TILDE: TILDE, + UNDERSCORE: UNDERSCORE, + EMOJI: EMOJI$1, + SYM: SYM + }); + + // Note that these two Unicode ones expand into a really big one with Babel + var ASCII_LETTER = /[a-z]/; + var LETTER = /(?:[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\u0560-\u0588\u05D0-\u05EA\u05EF-\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\u0860-\u086A\u0870-\u0887\u0889-\u088E\u08A0-\u08C9\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\u09FC\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\u0C5D\u0C60\u0C61\u0C80\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D04-\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\u0E86-\u0E8A\u0E8C-\u0EA3\u0EA5\u0EA7-\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-\u1711\u171F-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1878\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-\u1B4C\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1C80-\u1C88\u1C90-\u1CBA\u1CBD-\u1CBF\u1CE9-\u1CEC\u1CEE-\u1CF3\u1CF5\u1CF6\u1CFA\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-\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-\u312F\u3131-\u318E\u31A0-\u31BF\u31F0-\u31FF\u3400-\u4DBF\u4E00-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA7CA\uA7D0\uA7D1\uA7D3\uA7D5-\uA7D9\uA7F2-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA8FD\uA8FE\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-\uAB69\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]|\uD800[\uDC00-\uDC0B\uDC0D-\uDC26\uDC28-\uDC3A\uDC3C\uDC3D\uDC3F-\uDC4D\uDC50-\uDC5D\uDC80-\uDCFA\uDE80-\uDE9C\uDEA0-\uDED0\uDF00-\uDF1F\uDF2D-\uDF40\uDF42-\uDF49\uDF50-\uDF75\uDF80-\uDF9D\uDFA0-\uDFC3\uDFC8-\uDFCF]|\uD801[\uDC00-\uDC9D\uDCB0-\uDCD3\uDCD8-\uDCFB\uDD00-\uDD27\uDD30-\uDD63\uDD70-\uDD7A\uDD7C-\uDD8A\uDD8C-\uDD92\uDD94\uDD95\uDD97-\uDDA1\uDDA3-\uDDB1\uDDB3-\uDDB9\uDDBB\uDDBC\uDE00-\uDF36\uDF40-\uDF55\uDF60-\uDF67\uDF80-\uDF85\uDF87-\uDFB0\uDFB2-\uDFBA]|\uD802[\uDC00-\uDC05\uDC08\uDC0A-\uDC35\uDC37\uDC38\uDC3C\uDC3F-\uDC55\uDC60-\uDC76\uDC80-\uDC9E\uDCE0-\uDCF2\uDCF4\uDCF5\uDD00-\uDD15\uDD20-\uDD39\uDD80-\uDDB7\uDDBE\uDDBF\uDE00\uDE10-\uDE13\uDE15-\uDE17\uDE19-\uDE35\uDE60-\uDE7C\uDE80-\uDE9C\uDEC0-\uDEC7\uDEC9-\uDEE4\uDF00-\uDF35\uDF40-\uDF55\uDF60-\uDF72\uDF80-\uDF91]|\uD803[\uDC00-\uDC48\uDC80-\uDCB2\uDCC0-\uDCF2\uDD00-\uDD23\uDE80-\uDEA9\uDEB0\uDEB1\uDF00-\uDF1C\uDF27\uDF30-\uDF45\uDF70-\uDF81\uDFB0-\uDFC4\uDFE0-\uDFF6]|\uD804[\uDC03-\uDC37\uDC71\uDC72\uDC75\uDC83-\uDCAF\uDCD0-\uDCE8\uDD03-\uDD26\uDD44\uDD47\uDD50-\uDD72\uDD76\uDD83-\uDDB2\uDDC1-\uDDC4\uDDDA\uDDDC\uDE00-\uDE11\uDE13-\uDE2B\uDE80-\uDE86\uDE88\uDE8A-\uDE8D\uDE8F-\uDE9D\uDE9F-\uDEA8\uDEB0-\uDEDE\uDF05-\uDF0C\uDF0F\uDF10\uDF13-\uDF28\uDF2A-\uDF30\uDF32\uDF33\uDF35-\uDF39\uDF3D\uDF50\uDF5D-\uDF61]|\uD805[\uDC00-\uDC34\uDC47-\uDC4A\uDC5F-\uDC61\uDC80-\uDCAF\uDCC4\uDCC5\uDCC7\uDD80-\uDDAE\uDDD8-\uDDDB\uDE00-\uDE2F\uDE44\uDE80-\uDEAA\uDEB8\uDF00-\uDF1A\uDF40-\uDF46]|\uD806[\uDC00-\uDC2B\uDCA0-\uDCDF\uDCFF-\uDD06\uDD09\uDD0C-\uDD13\uDD15\uDD16\uDD18-\uDD2F\uDD3F\uDD41\uDDA0-\uDDA7\uDDAA-\uDDD0\uDDE1\uDDE3\uDE00\uDE0B-\uDE32\uDE3A\uDE50\uDE5C-\uDE89\uDE9D\uDEB0-\uDEF8]|\uD807[\uDC00-\uDC08\uDC0A-\uDC2E\uDC40\uDC72-\uDC8F\uDD00-\uDD06\uDD08\uDD09\uDD0B-\uDD30\uDD46\uDD60-\uDD65\uDD67\uDD68\uDD6A-\uDD89\uDD98\uDEE0-\uDEF2\uDFB0]|\uD808[\uDC00-\uDF99]|\uD809[\uDC80-\uDD43]|\uD80B[\uDF90-\uDFF0]|[\uD80C\uD81C-\uD820\uD822\uD840-\uD868\uD86A-\uD86C\uD86F-\uD872\uD874-\uD879\uD880-\uD883][\uDC00-\uDFFF]|\uD80D[\uDC00-\uDC2E]|\uD811[\uDC00-\uDE46]|\uD81A[\uDC00-\uDE38\uDE40-\uDE5E\uDE70-\uDEBE\uDED0-\uDEED\uDF00-\uDF2F\uDF40-\uDF43\uDF63-\uDF77\uDF7D-\uDF8F]|\uD81B[\uDE40-\uDE7F\uDF00-\uDF4A\uDF50\uDF93-\uDF9F\uDFE0\uDFE1\uDFE3]|\uD821[\uDC00-\uDFF7]|\uD823[\uDC00-\uDCD5\uDD00-\uDD08]|\uD82B[\uDFF0-\uDFF3\uDFF5-\uDFFB\uDFFD\uDFFE]|\uD82C[\uDC00-\uDD22\uDD50-\uDD52\uDD64-\uDD67\uDD70-\uDEFB]|\uD82F[\uDC00-\uDC6A\uDC70-\uDC7C\uDC80-\uDC88\uDC90-\uDC99]|\uD835[\uDC00-\uDC54\uDC56-\uDC9C\uDC9E\uDC9F\uDCA2\uDCA5\uDCA6\uDCA9-\uDCAC\uDCAE-\uDCB9\uDCBB\uDCBD-\uDCC3\uDCC5-\uDD05\uDD07-\uDD0A\uDD0D-\uDD14\uDD16-\uDD1C\uDD1E-\uDD39\uDD3B-\uDD3E\uDD40-\uDD44\uDD46\uDD4A-\uDD50\uDD52-\uDEA5\uDEA8-\uDEC0\uDEC2-\uDEDA\uDEDC-\uDEFA\uDEFC-\uDF14\uDF16-\uDF34\uDF36-\uDF4E\uDF50-\uDF6E\uDF70-\uDF88\uDF8A-\uDFA8\uDFAA-\uDFC2\uDFC4-\uDFCB]|\uD837[\uDF00-\uDF1E]|\uD838[\uDD00-\uDD2C\uDD37-\uDD3D\uDD4E\uDE90-\uDEAD\uDEC0-\uDEEB]|\uD839[\uDFE0-\uDFE6\uDFE8-\uDFEB\uDFED\uDFEE\uDFF0-\uDFFE]|\uD83A[\uDC00-\uDCC4\uDD00-\uDD43\uDD4B]|\uD83B[\uDE00-\uDE03\uDE05-\uDE1F\uDE21\uDE22\uDE24\uDE27\uDE29-\uDE32\uDE34-\uDE37\uDE39\uDE3B\uDE42\uDE47\uDE49\uDE4B\uDE4D-\uDE4F\uDE51\uDE52\uDE54\uDE57\uDE59\uDE5B\uDE5D\uDE5F\uDE61\uDE62\uDE64\uDE67-\uDE6A\uDE6C-\uDE72\uDE74-\uDE77\uDE79-\uDE7C\uDE7E\uDE80-\uDE89\uDE8B-\uDE9B\uDEA1-\uDEA3\uDEA5-\uDEA9\uDEAB-\uDEBB]|\uD869[\uDC00-\uDEDF\uDF00-\uDFFF]|\uD86D[\uDC00-\uDF38\uDF40-\uDFFF]|\uD86E[\uDC00-\uDC1D\uDC20-\uDFFF]|\uD873[\uDC00-\uDEA1\uDEB0-\uDFFF]|\uD87A[\uDC00-\uDFE0]|\uD87E[\uDC00-\uDE1D]|\uD884[\uDC00-\uDF4A])/; // Any Unicode character with letter data type + + var EMOJI = /(?:[#\*0-9\xA9\xAE\u203C\u2049\u2122\u2139\u2194-\u2199\u21A9\u21AA\u231A\u231B\u2328\u23CF\u23E9-\u23F3\u23F8-\u23FA\u24C2\u25AA\u25AB\u25B6\u25C0\u25FB-\u25FE\u2600-\u2604\u260E\u2611\u2614\u2615\u2618\u261D\u2620\u2622\u2623\u2626\u262A\u262E\u262F\u2638-\u263A\u2640\u2642\u2648-\u2653\u265F\u2660\u2663\u2665\u2666\u2668\u267B\u267E\u267F\u2692-\u2697\u2699\u269B\u269C\u26A0\u26A1\u26A7\u26AA\u26AB\u26B0\u26B1\u26BD\u26BE\u26C4\u26C5\u26C8\u26CE\u26CF\u26D1\u26D3\u26D4\u26E9\u26EA\u26F0-\u26F5\u26F7-\u26FA\u26FD\u2702\u2705\u2708-\u270D\u270F\u2712\u2714\u2716\u271D\u2721\u2728\u2733\u2734\u2744\u2747\u274C\u274E\u2753-\u2755\u2757\u2763\u2764\u2795-\u2797\u27A1\u27B0\u27BF\u2934\u2935\u2B05-\u2B07\u2B1B\u2B1C\u2B50\u2B55\u3030\u303D\u3297\u3299]|\uD83C[\uDC04\uDCCF\uDD70\uDD71\uDD7E\uDD7F\uDD8E\uDD91-\uDD9A\uDDE6-\uDDFF\uDE01\uDE02\uDE1A\uDE2F\uDE32-\uDE3A\uDE50\uDE51\uDF00-\uDF21\uDF24-\uDF93\uDF96\uDF97\uDF99-\uDF9B\uDF9E-\uDFF0\uDFF3-\uDFF5\uDFF7-\uDFFF]|\uD83D[\uDC00-\uDCFD\uDCFF-\uDD3D\uDD49-\uDD4E\uDD50-\uDD67\uDD6F\uDD70\uDD73-\uDD7A\uDD87\uDD8A-\uDD8D\uDD90\uDD95\uDD96\uDDA4\uDDA5\uDDA8\uDDB1\uDDB2\uDDBC\uDDC2-\uDDC4\uDDD1-\uDDD3\uDDDC-\uDDDE\uDDE1\uDDE3\uDDE8\uDDEF\uDDF3\uDDFA-\uDE4F\uDE80-\uDEC5\uDECB-\uDED2\uDED5-\uDED7\uDEDD-\uDEE5\uDEE9\uDEEB\uDEEC\uDEF0\uDEF3-\uDEFC\uDFE0-\uDFEB\uDFF0]|\uD83E[\uDD0C-\uDD3A\uDD3C-\uDD45\uDD47-\uDDFF\uDE70-\uDE74\uDE78-\uDE7C\uDE80-\uDE86\uDE90-\uDEAC\uDEB0-\uDEBA\uDEC0-\uDEC5\uDED0-\uDED9\uDEE0-\uDEE7\uDEF0-\uDEF6])/; // Any Unicode emoji character + + var EMOJI_VARIATION$1 = /\ufe0f/; + var DIGIT = /\d/; + var SPACE = /\s/; + + var regexp = /*#__PURE__*/Object.freeze({ + __proto__: null, + ASCII_LETTER: ASCII_LETTER, + LETTER: LETTER, + EMOJI: EMOJI, + EMOJI_VARIATION: EMOJI_VARIATION$1, + DIGIT: DIGIT, + SPACE: SPACE }); /** - The scanner provides an interface that takes a string of text as input, and - outputs an array of tokens instances that can be used for easy URL parsing. + The scanner provides an interface that takes a string of text as input, and + outputs an array of tokens instances that can be used for easy URL parsing. + */ + var NL = '\n'; // New line character - @module linkify - @submodule scanner - @main scanner - */ + var EMOJI_VARIATION = "\uFE0F"; // Variation selector, follows heart and others - var tlds = 'aaa|aarp|abb|abbott|abogado|ac|academy|accenture|accountant|accountants|aco|active|actor|ad|adac|ads|adult|ae|aeg|aero|af|afl|ag|agency|ai|aig|airforce|airtel|al|alibaba|alipay|allfinanz|alsace|am|amica|amsterdam|an|analytics|android|ao|apartments|app|apple|aq|aquarelle|ar|aramco|archi|army|arpa|arte|as|asia|associates|at|attorney|au|auction|audi|audio|author|auto|autos|avianca|aw|ax|axa|az|azure|ba|baidu|band|bank|bar|barcelona|barclaycard|barclays|bargains|bauhaus|bayern|bb|bbc|bbva|bcg|bcn|bd|be|beats|beer|bentley|berlin|best|bet|bf|bg|bh|bharti|bi|bible|bid|bike|bing|bingo|bio|biz|bj|black|blackfriday|bloomberg|blue|bm|bms|bmw|bn|bnl|bnpparibas|bo|boats|boehringer|bom|bond|boo|book|boots|bosch|bostik|bot|boutique|br|bradesco|bridgestone|broadway|broker|brother|brussels|bs|bt|budapest|bugatti|build|builders|business|buy|buzz|bv|bw|by|bz|bzh|ca|cab|cafe|cal|call|camera|camp|cancerresearch|canon|capetown|capital|car|caravan|cards|care|career|careers|cars|cartier|casa|cash|casino|cat|catering|cba|cbn|cc|cd|ceb|center|ceo|cern|cf|cfa|cfd|cg|ch|chanel|channel|chase|chat|cheap|chloe|christmas|chrome|church|ci|cipriani|circle|cisco|citic|city|cityeats|ck|cl|claims|cleaning|click|clinic|clinique|clothing|cloud|club|clubmed|cm|cn|co|coach|codes|coffee|college|cologne|com|commbank|community|company|compare|computer|comsec|condos|construction|consulting|contact|contractors|cooking|cool|coop|corsica|country|coupon|coupons|courses|cr|credit|creditcard|creditunion|cricket|crown|crs|cruises|csc|cu|cuisinella|cv|cw|cx|cy|cymru|cyou|cz|dabur|dad|dance|date|dating|datsun|day|dclk|de|dealer|deals|degree|delivery|dell|deloitte|delta|democrat|dental|dentist|desi|design|dev|diamonds|diet|digital|direct|directory|discount|dj|dk|dm|dnp|do|docs|dog|doha|domains|download|drive|dubai|durban|dvag|dz|earth|eat|ec|edeka|edu|education|ee|eg|email|emerck|energy|engineer|engineering|enterprises|epson|equipment|er|erni|es|esq|estate|et|eu|eurovision|eus|events|everbank|exchange|expert|exposed|express|fage|fail|fairwinds|faith|family|fan|fans|farm|fashion|fast|feedback|ferrero|fi|film|final|finance|financial|firestone|firmdale|fish|fishing|fit|fitness|fj|fk|flickr|flights|florist|flowers|flsmidth|fly|fm|fo|foo|football|ford|forex|forsale|forum|foundation|fox|fr|fresenius|frl|frogans|frontier|fund|furniture|futbol|fyi|ga|gal|gallery|gallup|game|garden|gb|gbiz|gd|gdn|ge|gea|gent|genting|gf|gg|ggee|gh|gi|gift|gifts|gives|giving|gl|glass|gle|global|globo|gm|gmail|gmbh|gmo|gmx|gn|gold|goldpoint|golf|goo|goog|google|gop|got|gov|gp|gq|gr|grainger|graphics|gratis|green|gripe|group|gs|gt|gu|gucci|guge|guide|guitars|guru|gw|gy|hamburg|hangout|haus|hdfcbank|health|healthcare|help|helsinki|here|hermes|hiphop|hitachi|hiv|hk|hm|hn|hockey|holdings|holiday|homedepot|homes|honda|horse|host|hosting|hoteles|hotmail|house|how|hr|hsbc|ht|hu|hyundai|ibm|icbc|ice|icu|id|ie|ifm|iinet|il|im|immo|immobilien|in|industries|infiniti|info|ing|ink|institute|insurance|insure|int|international|investments|io|ipiranga|iq|ir|irish|is|iselect|ist|istanbul|it|itau|iwc|jaguar|java|jcb|je|jetzt|jewelry|jlc|jll|jm|jmp|jo|jobs|joburg|jot|joy|jp|jpmorgan|jprs|juegos|kaufen|kddi|ke|kerryhotels|kerrylogistics|kerryproperties|kfh|kg|kh|ki|kia|kim|kinder|kitchen|kiwi|km|kn|koeln|komatsu|kp|kpn|kr|krd|kred|kuokgroup|kw|ky|kyoto|kz|la|lacaixa|lamborghini|lamer|lancaster|land|landrover|lanxess|lasalle|lat|latrobe|law|lawyer|lb|lc|lds|lease|leclerc|legal|lexus|lgbt|li|liaison|lidl|life|lifeinsurance|lifestyle|lighting|like|limited|limo|lincoln|linde|link|live|living|lixil|lk|loan|loans|local|locus|lol|london|lotte|lotto|love|lr|ls|lt|ltd|ltda|lu|lupin|luxe|luxury|lv|ly|ma|madrid|maif|maison|makeup|man|management|mango|market|marketing|markets|marriott|mba|mc|md|me|med|media|meet|melbourne|meme|memorial|men|menu|meo|mg|mh|miami|microsoft|mil|mini|mk|ml|mm|mma|mn|mo|mobi|mobily|moda|moe|moi|mom|monash|money|montblanc|mormon|mortgage|moscow|motorcycles|mov|movie|movistar|mp|mq|mr|ms|mt|mtn|mtpc|mtr|mu|museum|mutuelle|mv|mw|mx|my|mz|na|nadex|nagoya|name|natura|navy|nc|ne|nec|net|netbank|network|neustar|new|news|nexus|nf|ng|ngo|nhk|ni|nico|nikon|ninja|nissan|nl|no|nokia|norton|nowruz|np|nr|nra|nrw|ntt|nu|nyc|nz|obi|office|okinawa|om|omega|one|ong|onl|online|ooo|oracle|orange|org|organic|origins|osaka|otsuka|ovh|pa|page|pamperedchef|panerai|paris|pars|partners|parts|party|passagens|pe|pet|pf|pg|ph|pharmacy|philips|photo|photography|photos|physio|piaget|pics|pictet|pictures|pid|pin|ping|pink|pizza|pk|pl|place|play|playstation|plumbing|plus|pm|pn|pohl|poker|porn|post|pr|praxi|press|pro|prod|productions|prof|promo|properties|property|protection|ps|pt|pub|pw|pwc|py|qa|qpon|quebec|quest|racing|re|read|realtor|realty|recipes|red|redstone|redumbrella|rehab|reise|reisen|reit|ren|rent|rentals|repair|report|republican|rest|restaurant|review|reviews|rexroth|rich|ricoh|rio|rip|ro|rocher|rocks|rodeo|room|rs|rsvp|ru|ruhr|run|rw|rwe|ryukyu|sa|saarland|safe|safety|sakura|sale|salon|samsung|sandvik|sandvikcoromant|sanofi|sap|sapo|sarl|sas|saxo|sb|sbs|sc|sca|scb|schaeffler|schmidt|scholarships|school|schule|schwarz|science|scor|scot|sd|se|seat|security|seek|select|sener|services|seven|sew|sex|sexy|sfr|sg|sh|sharp|shell|shia|shiksha|shoes|show|shriram|si|singles|site|sj|sk|ski|skin|sky|skype|sl|sm|smile|sn|sncf|so|soccer|social|softbank|software|sohu|solar|solutions|song|sony|soy|space|spiegel|spot|spreadbetting|sr|srl|st|stada|star|starhub|statefarm|statoil|stc|stcgroup|stockholm|storage|store|studio|study|style|su|sucks|supplies|supply|support|surf|surgery|suzuki|sv|swatch|swiss|sx|sy|sydney|symantec|systems|sz|tab|taipei|taobao|tatamotors|tatar|tattoo|tax|taxi|tc|tci|td|team|tech|technology|tel|telecity|telefonica|temasek|tennis|tf|tg|th|thd|theater|theatre|tickets|tienda|tiffany|tips|tires|tirol|tj|tk|tl|tm|tmall|tn|to|today|tokyo|tools|top|toray|toshiba|total|tours|town|toyota|toys|tp|tr|trade|trading|training|travel|travelers|travelersinsurance|trust|trv|tt|tube|tui|tunes|tushu|tv|tvs|tw|tz|ua|ubs|ug|uk|unicom|university|uno|uol|us|uy|uz|va|vacations|vana|vc|ve|vegas|ventures|verisign|versicherung|vet|vg|vi|viajes|video|viking|villas|vin|vip|virgin|vision|vista|vistaprint|viva|vlaanderen|vn|vodka|volkswagen|vote|voting|voto|voyage|vu|vuelos|wales|walter|wang|wanggou|watch|watches|weather|weatherchannel|webcam|weber|website|wed|wedding|weir|wf|whoswho|wien|wiki|williamhill|win|windows|wine|wme|wolterskluwer|work|works|world|ws|wtc|wtf|xbox|xerox|xin|xperia|xxx|xyz|yachts|yahoo|yamaxun|yandex|ye|yodobashi|yoga|yokohama|youtube|yt|za|zara|zero|zip|zm|zone|zuerich|zw'.split('|'); // macro, see gulpfile.js + var EMOJI_JOINER = "\u200D"; // zero-width joiner - var NUMBERS = '0123456789'.split(''); - var ALPHANUM = '0123456789abcdefghijklmnopqrstuvwxyz'.split(''); - var WHITESPACE = [' ', '\f', '\r', '\t', '\v', ' ', ' ', 'á Ž']; // excluding line breaks + /** + * Scanner output token: + * - `t` is the token name (e.g., 'NUM', 'EMOJI', 'TLD') + * - `v` is the value of the token (e.g., '123', 'â¤ï¸', 'com') + * - `s` is the start index of the token in the original string + * - `e` is the end index of the token in the original string + * @typedef {{t: string, v: string, s: number, e: number}} Token + */ - var domainStates = []; // states that jump to DOMAIN on /[a-z0-9]/ - var makeState = function makeState(tokenClass) { - return new CharacterState(tokenClass); - }; + /** + * @template T + * @typedef {{ [collection: string]: T[] }} Collections + */ - // Frequently used states - var S_START = makeState(); - var S_NUM = makeState(NUM); - var S_DOMAIN = makeState(DOMAIN); - var S_DOMAIN_HYPHEN = makeState(); // domain followed by 1 or more hyphen characters - var S_WS = makeState(WS); + /** + * Initialize the scanner character-based state machine for the given start + * state + * @param {[string, boolean][]} customSchemes List of custom schemes, where each + * item is a length-2 tuple with the first element set to the string scheme, and + * the second element set to `true` if the `://` after the scheme is optional + */ + + function init$2(customSchemes) { + var _tr, _tr2, _tr3, _tr4, _tt, _tr5; + + if (customSchemes === void 0) { + customSchemes = []; + } + + // Frequently used states (name argument removed during minification) + + /** @type Collections<string> */ + var groups = {}; // of tokens + + State.groups = groups; + /** @type State<string> */ + + var Start = new State(); // States for special URL symbols that accept immediately after start + + tt(Start, "'", APOSTROPHE); + tt(Start, '{', OPENBRACE); + tt(Start, '[', OPENBRACKET); + tt(Start, '<', OPENANGLEBRACKET); + tt(Start, '(', OPENPAREN); + tt(Start, '}', CLOSEBRACE); + tt(Start, ']', CLOSEBRACKET); + tt(Start, '>', CLOSEANGLEBRACKET); + tt(Start, ')', CLOSEPAREN); + tt(Start, '&', AMPERSAND); + tt(Start, '*', ASTERISK); + tt(Start, '@', AT); + tt(Start, '`', BACKTICK); + tt(Start, '^', CARET); + tt(Start, ':', COLON); + tt(Start, ',', COMMA); + tt(Start, '$', DOLLAR); + tt(Start, '.', DOT); + tt(Start, '=', EQUALS); + tt(Start, '!', EXCLAMATION); + tt(Start, '-', HYPHEN); + tt(Start, '%', PERCENT); + tt(Start, '|', PIPE); + tt(Start, '+', PLUS); + tt(Start, '#', POUND); + tt(Start, '?', QUERY); + tt(Start, '"', QUOTE); + tt(Start, '/', SLASH); + tt(Start, ';', SEMI); + tt(Start, '~', TILDE); + tt(Start, '_', UNDERSCORE); + tt(Start, '\\', BACKSLASH); + var Num = tr(Start, DIGIT, NUM, (_tr = {}, _tr[numeric] = true, _tr)); + tr(Num, DIGIT, Num); // State which emits a word token + + var Word = tr(Start, ASCII_LETTER, WORD, (_tr2 = {}, _tr2[ascii] = true, _tr2)); + tr(Word, ASCII_LETTER, Word); // Same as previous, but specific to non-fsm.ascii alphabet words + + var UWord = tr(Start, LETTER, UWORD, (_tr3 = {}, _tr3[alpha] = true, _tr3)); + tr(UWord, ASCII_LETTER); // Non-accepting + + tr(UWord, LETTER, UWord); // Whitespace jumps + // Tokens of only non-newline whitespace are arbitrarily long + // If any whitespace except newline, more whitespace! + + var Ws = tr(Start, SPACE, WS, (_tr4 = {}, _tr4[whitespace] = true, _tr4)); + tt(Start, NL, NL$1, (_tt = {}, _tt[whitespace] = true, _tt)); + tt(Ws, NL); // non-accepting state to avoid mixing whitespaces + + tr(Ws, SPACE, Ws); // Emoji tokens. They are not grouped by the scanner except in cases where a + // zero-width joiner is present + + var Emoji = tr(Start, EMOJI, EMOJI$1, (_tr5 = {}, _tr5[emoji] = true, _tr5)); + tr(Emoji, EMOJI, Emoji); + tt(Emoji, EMOJI_VARIATION, Emoji); // tt(Start, EMOJI_VARIATION, Emoji); // This one is sketchy + + var EmojiJoiner = tt(Emoji, EMOJI_JOINER); + tr(EmojiJoiner, EMOJI, Emoji); // tt(EmojiJoiner, EMOJI_VARIATION, Emoji); // also sketchy + // Generates states for top-level domains + // Note that this is most accurate when tlds are in alphabetical order + + var wordjr = [[ASCII_LETTER, Word]]; + var uwordjr = [[ASCII_LETTER, null], [LETTER, UWord]]; + + for (var i = 0; i < tlds.length; i++) { + fastts(Start, tlds[i], TLD, WORD, wordjr); + } + + for (var _i = 0; _i < utlds.length; _i++) { + fastts(Start, utlds[_i], UTLD, UWORD, uwordjr); + } + + addToGroups(TLD, { + tld: true, + ascii: true + }, groups); + addToGroups(UTLD, { + utld: true, + alpha: true + }, groups); // Collect the states generated by different protocols. NOTE: If any new TLDs + // get added that are also protocols, set the token to be the same as the + // protocol to ensure parsing works as expected. + + fastts(Start, 'file', SCHEME, WORD, wordjr); + fastts(Start, 'mailto', SCHEME, WORD, wordjr); + fastts(Start, 'http', SLASH_SCHEME, WORD, wordjr); + fastts(Start, 'https', SLASH_SCHEME, WORD, wordjr); + fastts(Start, 'ftp', SLASH_SCHEME, WORD, wordjr); + fastts(Start, 'ftps', SLASH_SCHEME, WORD, wordjr); + addToGroups(SCHEME, { + scheme: true, + ascii: true + }, groups); + addToGroups(SLASH_SCHEME, { + slashscheme: true, + ascii: true + }, groups); // Register custom schemes. Assumes each scheme is asciinumeric with hyphens + + customSchemes = customSchemes.sort(function (a, b) { + return a[0] > b[0] ? 1 : -1; + }); + + for (var _i2 = 0; _i2 < customSchemes.length; _i2++) { + var _ref, _ref2; + + var sch = customSchemes[_i2][0]; + var optionalSlashSlash = customSchemes[_i2][1]; + var flags = optionalSlashSlash ? (_ref = {}, _ref[scheme] = true, _ref) : (_ref2 = {}, _ref2[slashscheme] = true, _ref2); + + if (sch.indexOf('-') >= 0) { + flags[domain] = true; + } else if (!ASCII_LETTER.test(sch)) { + flags[numeric] = true; // numbers only + } else if (DIGIT.test(sch)) { + flags[asciinumeric] = true; + } else { + flags[ascii] = true; + } + + ts(Start, sch, sch, flags); + } // Localhost token + + + ts(Start, 'localhost', LOCALHOST, { + ascii: true + }); // Set default transition for start state (some symbol) + + Start.jd = new State(SYM); + return { + start: Start, + tokens: assign({ + groups: groups + }, tk) + }; + } + /** + Given a string, returns an array of TOKEN instances representing the + composition of that string. + + @method run + @param {State<string>} start scanner starting state + @param {string} str input string to scan + @return {Token[]} list of tokens, each with a type and value + */ + + function run$1(start, str) { + // State machine is not case sensitive, so input is tokenized in lowercased + // form (still returns regular case). Uses selective `toLowerCase` because + // lowercasing the entire string causes the length and character position to + // vary in some non-English strings with V8-based runtimes. + var iterable = stringToArray(str.replace(/[A-Z]/g, function (c) { + return c.toLowerCase(); + })); + var charCount = iterable.length; // <= len if there are emojis, etc + + var tokens = []; // return value + // cursor through the string itself, accounting for characters that have + // width with length 2 such as emojis + + var cursor = 0; // Cursor through the array-representation of the string + + var charCursor = 0; // Tokenize the string + + while (charCursor < charCount) { + var state = start; + var nextState = null; + var tokenLength = 0; + var latestAccepting = null; + var sinceAccepts = -1; + var charsSinceAccepts = -1; + + while (charCursor < charCount && (nextState = state.go(iterable[charCursor]))) { + state = nextState; // Keep track of the latest accepting state + + if (state.accepts()) { + sinceAccepts = 0; + charsSinceAccepts = 0; + latestAccepting = state; + } else if (sinceAccepts >= 0) { + sinceAccepts += iterable[charCursor].length; + charsSinceAccepts++; + } + + tokenLength += iterable[charCursor].length; + cursor += iterable[charCursor].length; + charCursor++; + } // Roll back to the latest accepting state + + + cursor -= sinceAccepts; + charCursor -= charsSinceAccepts; + tokenLength -= sinceAccepts; // No more jumps, just make a new token from the last accepting one + + tokens.push({ + t: latestAccepting.t, + // token type/name + v: str.slice(cursor - tokenLength, cursor), + // string value + s: cursor - tokenLength, + // start index + e: cursor // end index (excluding) + + }); + } + + return tokens; + } + /** + * Convert a String to an Array of characters, taking into account that some + * characters like emojis take up two string indexes. + * + * Adapted from core-js (MIT license) + * https://github.com/zloirock/core-js/blob/2d69cf5f99ab3ea3463c395df81e5a15b68f49d9/packages/core-js/internals/string-multibyte.js + * + * @function stringToArray + * @param {string} str + * @returns {string[]} + */ + + function stringToArray(str) { + var result = []; + var len = str.length; + var index = 0; + + while (index < len) { + var first = str.charCodeAt(index); + var second = void 0; + var char = first < 0xd800 || first > 0xdbff || index + 1 === len || (second = str.charCodeAt(index + 1)) < 0xdc00 || second > 0xdfff ? str[index] // single character + : str.slice(index, index + 2); // two-index characters + + result.push(char); + index += char.length; + } + + return result; + } + /** + * Fast version of ts function for when transition defaults are well known + * @param {State<string>} state + * @param {string} input + * @param {string} t + * @param {string} defaultt + * @param {[RegExp, State<string>][]} jr + * @returns {State<string>} + */ + + function fastts(state, input, t, defaultt, jr) { + var next; + var len = input.length; + + for (var i = 0; i < len - 1; i++) { + var char = input[i]; + + if (state.j[char]) { + next = state.j[char]; + } else { + next = new State(defaultt); + next.jr = jr.slice(); + state.j[char] = next; + } + + state = next; + } + + next = new State(t); + next.jr = jr.slice(); + state.j[input[len - 1]] = next; + return next; + } - // States for special URL symbols - S_START.on('@', makeState(AT)).on('.', makeState(DOT)).on('+', makeState(PLUS)).on('#', makeState(POUND)).on('?', makeState(QUERY)).on('/', makeState(SLASH)).on('_', makeState(UNDERSCORE)).on(':', makeState(COLON)).on('{', makeState(OPENBRACE)).on('[', makeState(OPENBRACKET)).on('<', makeState(OPENANGLEBRACKET)).on('(', makeState(OPENPAREN)).on('}', makeState(CLOSEBRACE)).on(']', makeState(CLOSEBRACKET)).on('>', makeState(CLOSEANGLEBRACKET)).on(')', makeState(CLOSEPAREN)).on([',', ';', '!', '"', '\''], makeState(PUNCTUATION)); + function _inheritsLoose(subClass, superClass) { + subClass.prototype = Object.create(superClass.prototype); + subClass.prototype.constructor = subClass; - // Whitespace jumps - // Tokens of only non-newline whitespace are arbitrarily long - S_START.on('\n', makeState(TNL)).on(WHITESPACE, S_WS); + _setPrototypeOf(subClass, superClass); + } - // If any whitespace except newline, more whitespace! - S_WS.on(WHITESPACE, S_WS); + function _setPrototypeOf(o, p) { + _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { + o.__proto__ = p; + return o; + }; - // Generates states for top-level domains - // Note that this is most accurate when tlds are in alphabetical order - for (var i = 0; i < tlds.length; i++) { - var newStates = stateify(tlds[i], S_START, TLD, DOMAIN); - domainStates.push.apply(domainStates, newStates); + return _setPrototypeOf(o, p); } - // Collect the states generated by different protocls - var partialProtocolFileStates = stateify('file', S_START, DOMAIN, DOMAIN); - var partialProtocolFtpStates = stateify('ftp', S_START, DOMAIN, DOMAIN); - var partialProtocolHttpStates = stateify('http', S_START, DOMAIN, DOMAIN); + /** + * An object where each key is a valid DOM Event Name such as `click` or `focus` + * and each value is an event handler function. + * + * https://developer.mozilla.org/en-US/docs/Web/API/Element#events + * @typedef {?{ [event: string]: Function }} EventListeners + */ - // Add the states to the array of DOMAINeric states - domainStates.push.apply(domainStates, partialProtocolFileStates); - domainStates.push.apply(domainStates, partialProtocolFtpStates); - domainStates.push.apply(domainStates, partialProtocolHttpStates); + /** + * All formatted properties required to render a link, including `tagName`, + * `attributes`, `content` and `eventListeners`. + * @typedef {{ tagName: any, attributes: {[attr: string]: any}, content: string, + * eventListeners: EventListeners }} IntermediateRepresentation + */ - // Protocol states - var S_PROTOCOL_FILE = partialProtocolFileStates.pop(); - var S_PROTOCOL_FTP = partialProtocolFtpStates.pop(); - var S_PROTOCOL_HTTP = partialProtocolHttpStates.pop(); - var S_PROTOCOL_SECURE = makeState(DOMAIN); - var S_FULL_PROTOCOL = makeState(PROTOCOL); // Full protocol ends with COLON + /** + * Specify either an object described by the template type `O` or a function. + * + * The function takes a string value (usually the link's href attribute), the + * link type (`'url'`, `'hashtag`', etc.) and an internal token representation + * of the link. It should return an object of the template type `O` + * @template O + * @typedef {O | ((value: string, type: string, token: MultiToken) => O)} OptObj + */ - // Secure protocols (end with 's') - S_PROTOCOL_FTP.on('s', S_PROTOCOL_SECURE).on(':', S_FULL_PROTOCOL); + /** + * Specify either a function described by template type `F` or an object. + * + * Each key in the object should be a link type (`'url'`, `'hashtag`', etc.). Each + * value should be a function with template type `F` that is called when the + * corresponding link type is encountered. + * @template F + * @typedef {F | { [type: string]: F}} OptFn + */ - S_PROTOCOL_HTTP.on('s', S_PROTOCOL_SECURE).on(':', S_FULL_PROTOCOL); + /** + * Specify either a value with template type `V`, a function that returns `V` or + * an object where each value resolves to `V`. + * + * The function takes a string value (usually the link's href attribute), the + * link type (`'url'`, `'hashtag`', etc.) and an internal token representation + * of the link. It should return an object of the template type `V` + * + * For the object, each key should be a link type (`'url'`, `'hashtag`', etc.). + * Each value should either have type `V` or a function that returns V. This + * function similarly takes a string value and a token. + * + * Example valid types for `Opt<string>`: + * + * ```js + * 'hello' + * (value, type, token) => 'world' + * { url: 'hello', email: (value, token) => 'world'} + * ``` + * @template V + * @typedef {V | ((value: string, type: string, token: MultiToken) => V) | { [type: string]: V | ((value: string, token: MultiToken) => V) }} Opt + */ - domainStates.push(S_PROTOCOL_SECURE); + /** + * See available options: https://linkify.js.org/docs/options.html + * @typedef {{ + * defaultProtocol?: string, + * events?: OptObj<EventListeners>, + * format?: Opt<string>, + * formatHref?: Opt<string>, + * nl2br?: boolean, + * tagName?: Opt<any>, + * target?: Opt<string>, + * rel?: Opt<string>, + * validate?: Opt<boolean>, + * truncate?: Opt<number>, + * className?: Opt<string>, + * attributes?: OptObj<({ [attr: string]: any })>, + * ignoreTags?: string[], + * render?: OptFn<((ir: IntermediateRepresentation) => any)> + * }} Opts + */ - // Become protocol tokens after a COLON - S_PROTOCOL_FILE.on(':', S_FULL_PROTOCOL); - S_PROTOCOL_SECURE.on(':', S_FULL_PROTOCOL); + /** + * @type Required<Opts> + */ - // Localhost - var partialLocalhostStates = stateify('localhost', S_START, LOCALHOST, DOMAIN); - domainStates.push.apply(domainStates, partialLocalhostStates); + var defaults = { + defaultProtocol: 'http', + events: null, + format: noop, + formatHref: noop, + nl2br: false, + tagName: 'a', + target: null, + rel: null, + validate: true, + truncate: Infinity, + className: null, + attributes: null, + ignoreTags: [], + render: null + }; + /** + * Utility class for linkify interfaces to apply specified + * {@link Opts formatting and rendering options}. + * + * @param {Opts | Options} [opts] Option value overrides. + * @param {(ir: IntermediateRepresentation) => any} [defaultRender] (For + * internal use) default render function that determines how to generate an + * HTML element based on a link token's derived tagName, attributes and HTML. + * Similar to render option + */ - // Everything else - // DOMAINs make more DOMAINs - // Number and character transitions - S_START.on(NUMBERS, S_NUM); - S_NUM.on('-', S_DOMAIN_HYPHEN).on(NUMBERS, S_NUM).on(ALPHANUM, S_DOMAIN); // number becomes DOMAIN + function Options(opts, defaultRender) { + if (defaultRender === void 0) { + defaultRender = null; + } - S_DOMAIN.on('-', S_DOMAIN_HYPHEN).on(ALPHANUM, S_DOMAIN); + var o = assign({}, defaults); - // All the generated states should have a jump to DOMAIN - for (var _i = 0; _i < domainStates.length; _i++) { - domainStates[_i].on('-', S_DOMAIN_HYPHEN).on(ALPHANUM, S_DOMAIN); - } + if (opts) { + o = assign(o, opts instanceof Options ? opts.o : opts); + } // Ensure all ignored tags are uppercase - S_DOMAIN_HYPHEN.on('-', S_DOMAIN_HYPHEN).on(NUMBERS, S_DOMAIN).on(ALPHANUM, S_DOMAIN); - // Set default transition - S_START.defaultTransition = makeState(SYM); + var ignoredTags = o.ignoreTags; + var uppercaseIgnoredTags = []; - /** - Given a string, returns an array of TOKEN instances representing the - composition of that string. - - @method run - @param {String} str Input string to scan - @return {Array} Array of TOKEN instances - */ - var run = function run(str) { - - // The state machine only looks at lowercase strings. - // This selective `toLowerCase` is used because lowercasing the entire - // string causes the length and character position to vary in some in some - // non-English strings. This happens only on V8-based runtimes. - var lowerStr = str.replace(/[A-Z]/g, function (c) { - return c.toLowerCase(); - }); - var len = str.length; - var tokens = []; // return value - - var cursor = 0; - - // Tokenize the string - while (cursor < len) { - var state = S_START; - var secondState = null; - var nextState = null; - var tokenLength = 0; - var latestAccepting = null; - var sinceAccepts = -1; - - while (cursor < len && (nextState = state.next(lowerStr[cursor]))) { - secondState = null; - state = nextState; - - // Keep track of the latest accepting state - if (state.accepts()) { - sinceAccepts = 0; - latestAccepting = state; - } else if (sinceAccepts >= 0) { - sinceAccepts++; - } - - tokenLength++; - cursor++; - } - - if (sinceAccepts < 0) { - continue; - } // Should never happen - - // Roll back to the latest accepting state - cursor -= sinceAccepts; - tokenLength -= sinceAccepts; - - // Get the class for the new token - var TOKEN = latestAccepting.emit(); // Current token class - - // No more jumps, just make a new token - tokens.push(new TOKEN(str.substr(cursor - tokenLength, tokenLength))); - } - - return tokens; + for (var i = 0; i < ignoredTags.length; i++) { + uppercaseIgnoredTags.push(ignoredTags[i].toUpperCase()); + } + /** @protected */ + + + this.o = o; + + if (defaultRender) { + this.defaultRender = defaultRender; + } + + this.ignoreTags = uppercaseIgnoredTags; + } + Options.prototype = { + o: defaults, + + /** + * @type string[] + */ + ignoreTags: [], + + /** + * @param {IntermediateRepresentation} ir + * @returns {any} + */ + defaultRender: function defaultRender(ir) { + return ir; + }, + + /** + * Returns true or false based on whether a token should be displayed as a + * link based on the user options. + * @param {MultiToken} token + * @returns {boolean} + */ + check: function check(token) { + return this.get('validate', token.toString(), token); + }, + // Private methods + + /** + * Resolve an option's value based on the value of the option and the given + * params. If operator and token are specified and the target option is + * callable, automatically calls the function with the given argument. + * @template {keyof Opts} K + * @param {K} key Name of option to use + * @param {string} [operator] will be passed to the target option if it's a + * function. If not specified, RAW function value gets returned + * @param {MultiToken} [token] The token from linkify.tokenize + * @returns {Opts[K] | any} + */ + get: function get(key, operator, token) { + var isCallable = operator != null; + var option = this.o[key]; + + if (!option) { + return option; + } + + if (typeof option === 'object') { + option = token.t in option ? option[token.t] : defaults[key]; + + if (typeof option === 'function' && isCallable) { + option = option(operator, token); + } + } else if (typeof option === 'function' && isCallable) { + option = option(operator, token.t, token); + } + + return option; + }, + + /** + * @template {keyof Opts} L + * @param {L} key Name of options object to use + * @param {string} [operator] + * @param {MultiToken} [token] + * @returns {Opts[L] | any} + */ + getObj: function getObj(key, operator, token) { + var obj = this.o[key]; + + if (typeof obj === 'function' && operator != null) { + obj = obj(operator, token.t, token); + } + + return obj; + }, + + /** + * Convert the given token to a rendered element that may be added to the + * calling-interface's DOM + * @param {MultiToken} token Token to render to an HTML element + * @returns {any} Render result; e.g., HTML string, DOM element, React + * Component, etc. + */ + render: function render(token) { + var ir = token.render(this); // intermediate representation + + var renderFn = this.get('render', null, token) || this.defaultRender; + return renderFn(ir, token.t, token); + } }; - var start = S_START; + function noop(val) { + return val; + } - var scanner = Object.freeze({ - State: CharacterState, - TOKENS: TOKENS, - run: run, - start: start + var options = /*#__PURE__*/Object.freeze({ + __proto__: null, + defaults: defaults, + Options: Options, + assign: assign }); /****************************************************************************** - Multi-Tokens - Tokens composed of arrays of TextTokens - ******************************************************************************/ - - // Is the given token a valid domain token? - // Should nums be included here? - function isDomainToken(token) { - return token instanceof DOMAIN || token instanceof TLD; + Multi-Tokens + Tokens composed of arrays of TextTokens + ******************************************************************************/ + + /** + * @param {string} value + * @param {Token[]} tokens + */ + + function MultiToken(value, tokens) { + this.t = 'token'; + this.v = value; + this.tk = tokens; } + /** + * Abstract class used for manufacturing tokens of text tokens. That is rather + * than the value for a token being a small string of text, it's value an array + * of text tokens. + * + * Used for grouping together URLs, emails, hashtags, and other potential + * creations. + * @class MultiToken + * @property {string} t + * @property {string} v + * @property {Token[]} tk + * @abstract + */ + MultiToken.prototype = { + isLink: false, + + /** + * Return the string this token represents. + * @return {string} + */ + toString: function toString() { + return this.v; + }, + + /** + * What should the value for this token be in the `href` HTML attribute? + * Returns the `.toString` value by default. + * @param {string} [scheme] + * @return {string} + */ + toHref: function toHref(scheme) { + return this.toString(); + }, + + /** + * @param {Options} options Formatting options + * @returns {string} + */ + toFormattedString: function toFormattedString(options) { + var val = this.toString(); + var truncate = options.get('truncate', val, this); + var formatted = options.get('format', val, this); + return truncate && formatted.length > truncate ? formatted.substring(0, truncate) + '…' : formatted; + }, + + /** + * + * @param {Options} options + * @returns {string} + */ + toFormattedHref: function toFormattedHref(options) { + return options.get('formatHref', this.toHref(options.get('defaultProtocol')), this); + }, + + /** + * The start index of this token in the original input string + * @returns {number} + */ + startIndex: function startIndex() { + return this.tk[0].s; + }, + + /** + * The end index of this token in the original input string (up to this + * index but not including it) + * @returns {number} + */ + endIndex: function endIndex() { + return this.tk[this.tk.length - 1].e; + }, + + /** + Returns an object of relevant values for this token, which includes keys + * type - Kind of token ('url', 'email', etc.) + * value - Original text + * href - The value that should be added to the anchor tag's href + attribute + @method toObject + @param {string} [protocol] `'http'` by default + */ + toObject: function toObject(protocol) { + if (protocol === void 0) { + protocol = defaults.defaultProtocol; + } + + return { + type: this.t, + value: this.toString(), + isLink: this.isLink, + href: this.toHref(protocol), + start: this.startIndex(), + end: this.endIndex() + }; + }, + + /** + * + * @param {Options} options Formatting option + */ + toFormattedObject: function toFormattedObject(options) { + return { + type: this.t, + value: this.toFormattedString(options), + isLink: this.isLink, + href: this.toFormattedHref(options), + start: this.startIndex(), + end: this.endIndex() + }; + }, + + /** + * Whether this token should be rendered as a link according to the given options + * @param {Options} options + * @returns {boolean} + */ + validate: function validate(options) { + return options.get('validate', this.toString(), this); + }, + + /** + * Return an object that represents how this link should be rendered. + * @param {Options} options Formattinng options + */ + render: function render(options) { + var token = this; + var href = this.toFormattedHref(options); + var tagName = options.get('tagName', href, token); + var content = this.toFormattedString(options); + var attributes = {}; + var className = options.get('className', href, token); + var target = options.get('target', href, token); + var rel = options.get('rel', href, token); + var attrs = options.getObj('attributes', href, token); + var eventListeners = options.getObj('events', href, token); + attributes.href = href; + + if (className) { + attributes.class = className; + } + + if (target) { + attributes.target = target; + } + + if (rel) { + attributes.rel = rel; + } + + if (attrs) { + assign(attributes, attrs); + } + + return { + tagName: tagName, + attributes: attributes, + content: content, + eventListeners: eventListeners + }; + } + }; // Base token /** - Abstract class used for manufacturing tokens of text tokens. That is rather - than the value for a token being a small string of text, it's value an array - of text tokens. + * Create a new token that can be emitted by the parser state machine + * @param {string} type readable type of the token + * @param {object} props properties to assign or override, including isLink = true or false + * @returns {new (value: string, tokens: Token[]) => MultiToken} new token class + */ - Used for grouping together URLs, emails, hashtags, and other potential - creations. + function createTokenClass(type, props) { + var Token = /*#__PURE__*/function (_MultiToken) { + _inheritsLoose(Token, _MultiToken); - @class MultiToken - @abstract - */ - var MultiToken = createTokenClass(); + function Token(value, tokens) { + var _this; - MultiToken.prototype = { - /** - String representing the type for this token - @property type - @default 'TOKEN' - */ - type: 'token', - - /** - Is this multitoken a link? - @property isLink - @default false - */ - isLink: false, - - /** - Return the string this token represents. - @method toString - @return {String} - */ - toString: function toString() { - var result = []; - for (var _i2 = 0; _i2 < this.v.length; _i2++) { - result.push(this.v[_i2].toString()); - } - return result.join(''); - }, - - - /** - What should the value for this token be in the `href` HTML attribute? - Returns the `.toString` value by default. - @method toHref - @return {String} - */ - toHref: function toHref() { - return this.toString(); - }, - - - /** - Returns a hash of relevant values for this token, which includes keys - * type - Kind of token ('url', 'email', etc.) - * value - Original text - * href - The value that should be added to the anchor tag's href - attribute - @method toObject - @param {String} [protocol] `'http'` by default - @return {Object} - */ - toObject: function toObject() { - var protocol = arguments.length <= 0 || arguments[0] === undefined ? 'http' : arguments[0]; - - return { - type: this.type, - value: this.toString(), - href: this.toHref(protocol) - }; - } - }; + _this = _MultiToken.call(this, value, tokens) || this; + _this.t = type; + return _this; + } + + return Token; + }(MultiToken); + for (var p in props) { + Token.prototype[p] = props[p]; + } + + Token.t = type; + return Token; + } /** - Represents a list of tokens making up a valid email address - @class EMAIL - @extends MultiToken - */ - var EMAIL = inherits(MultiToken, createTokenClass(), { - type: 'email', - isLink: true, - toHref: function toHref() { - return 'mailto:' + this.toString(); - } + Represents a list of tokens making up a valid email address + */ + + var Email = createTokenClass('email', { + isLink: true, + toHref: function toHref() { + return 'mailto:' + this.toString(); + } }); - /** - Represents some plain text - @class TEXT - @extends MultiToken - */ - var TEXT = inherits(MultiToken, createTokenClass(), { type: 'text' }); + Represents some plain text + */ + var Text = createTokenClass('text'); /** - Multi-linebreak token - represents a line break - @class NL - @extends MultiToken - */ - var NL = inherits(MultiToken, createTokenClass(), { type: 'nl' }); + Multi-linebreak token - represents a line break + @class Nl + */ + var Nl = createTokenClass('nl'); /** - Represents a list of tokens making up a valid URL - @class URL - @extends MultiToken - */ - var URL = inherits(MultiToken, createTokenClass(), { - type: 'url', - isLink: true, - - /** - Lowercases relevant parts of the domain and adds the protocol if - required. Note that this will not escape unsafe HTML characters in the - URL. - @method href - @param {String} protocol - @return {String} - */ - toHref: function toHref() { - var protocol = arguments.length <= 0 || arguments[0] === undefined ? 'http' : arguments[0]; - - var hasProtocol = false; - var hasSlashSlash = false; - var tokens = this.v; - var result = []; - var i = 0; - - // Make the first part of the domain lowercase - // Lowercase protocol - while (tokens[i] instanceof PROTOCOL) { - hasProtocol = true; - result.push(tokens[i].toString().toLowerCase()); - i++; - } - - // Skip slash-slash - while (tokens[i] instanceof SLASH) { - hasSlashSlash = true; - result.push(tokens[i].toString()); - i++; - } - - // Lowercase all other characters in the domain - while (isDomainToken(tokens[i])) { - result.push(tokens[i].toString().toLowerCase()); - i++; - } - - // Leave all other characters as they were written - for (; i < tokens.length; i++) { - result.push(tokens[i].toString()); - } - - result = result.join(''); - - if (!(hasProtocol || hasSlashSlash)) { - result = protocol + '://' + result; - } - - return result; - }, - hasProtocol: function hasProtocol() { - return this.v[0] instanceof PROTOCOL; - } + Represents a list of text tokens making up a valid URL + @class Url + */ + + var Url = createTokenClass('url', { + isLink: true, + + /** + Lowercases relevant parts of the domain and adds the protocol if + required. Note that this will not escape unsafe HTML characters in the + URL. + @param {string} [scheme] default scheme (e.g., 'https') + @return {string} the full href + */ + toHref: function toHref(scheme) { + if (scheme === void 0) { + scheme = defaults.defaultProtocol; + } + + // Check if already has a prefix scheme + return this.hasProtocol() ? this.v : scheme + "://" + this.v; + }, + + /** + * Check whether this URL token has a protocol + * @return {boolean} + */ + hasProtocol: function hasProtocol() { + var tokens = this.tk; + return tokens.length >= 2 && tokens[0].t !== LOCALHOST && tokens[1].t === COLON; + } }); - var TOKENS$1 = Object.freeze({ + var multi = /*#__PURE__*/Object.freeze({ + __proto__: null, + MultiToken: MultiToken, Base: MultiToken, - EMAIL: EMAIL, - NL: NL, - TEXT: TEXT, - URL: URL + createTokenClass: createTokenClass, + Email: Email, + Text: Text, + Nl: Nl, + Url: Url }); /** - Not exactly parser, more like the second-stage scanner (although we can - theoretically hotswap the code here with a real parser in the future... but - for a little URL-finding utility abstract syntax trees may be a little - overkill). - - URL format: http://en.wikipedia.org/wiki/URI_scheme - Email format: http://en.wikipedia.org/wiki/Email_address (links to RFC in - reference) - - @module linkify - @submodule parser - @main parser - */ - - var makeState$1 = function makeState$1(tokenClass) { - return new State(tokenClass); + Not exactly parser, more like the second-stage scanner (although we can + theoretically hotswap the code here with a real parser in the future... but + for a little URL-finding utility abstract syntax trees may be a little + overkill). + + URL format: http://en.wikipedia.org/wiki/URI_scheme + Email format: http://en.wikipedia.org/wiki/EmailAddress (links to RFC in + reference) + + @module linkify + @submodule parser + @main run + */ + + var makeState = function makeState(arg) { + return new State(arg); }; + /** + * Generate the parser multi token-based state machine + * @param {{ groups: Collections<string> }} tokens + */ - // The universal starting state. - var S_START$1 = makeState$1(); - - // Intermediate states for URLs. Note that domains that begin with a protocol - // are treated slighly differently from those that don't. - var S_PROTOCOL = makeState$1(); // e.g., 'http:' - var S_PROTOCOL_SLASH = makeState$1(); // e.g., '/', 'http:/'' - var S_PROTOCOL_SLASH_SLASH = makeState$1(); // e.g., '//', 'http://' - var S_DOMAIN$1 = makeState$1(); // parsed string ends with a potential domain name (A) - var S_DOMAIN_DOT = makeState$1(); // (A) domain followed by DOT - var S_TLD = makeState$1(URL); // (A) Simplest possible URL with no query string - var S_TLD_COLON = makeState$1(); // (A) URL followed by colon (potential port number here) - var S_TLD_PORT = makeState$1(URL); // TLD followed by a port number - var S_URL = makeState$1(URL); // Long URL with optional port and maybe query string - var S_URL_NON_ACCEPTING = makeState$1(); // URL followed by some symbols (will not be part of the final URL) - var S_URL_OPENBRACE = makeState$1(); // URL followed by { - var S_URL_OPENBRACKET = makeState$1(); // URL followed by [ - var S_URL_OPENANGLEBRACKET = makeState$1(); // URL followed by < - var S_URL_OPENPAREN = makeState$1(); // URL followed by ( - var S_URL_OPENBRACE_Q = makeState$1(URL); // URL followed by { and some symbols that the URL can end it - var S_URL_OPENBRACKET_Q = makeState$1(URL); // URL followed by [ and some symbols that the URL can end it - var S_URL_OPENANGLEBRACKET_Q = makeState$1(URL); // URL followed by < and some symbols that the URL can end it - var S_URL_OPENPAREN_Q = makeState$1(URL); // URL followed by ( and some symbols that the URL can end it - var S_URL_OPENBRACE_SYMS = makeState$1(); // S_URL_OPENBRACE_Q followed by some symbols it cannot end it - var S_URL_OPENBRACKET_SYMS = makeState$1(); // S_URL_OPENBRACKET_Q followed by some symbols it cannot end it - var S_URL_OPENANGLEBRACKET_SYMS = makeState$1(); // S_URL_OPENANGLEBRACKET_Q followed by some symbols it cannot end it - var S_URL_OPENPAREN_SYMS = makeState$1(); // S_URL_OPENPAREN_Q followed by some symbols it cannot end it - var S_EMAIL_DOMAIN = makeState$1(); // parsed string starts with local email info + @ with a potential domain name (C) - var S_EMAIL_DOMAIN_DOT = makeState$1(); // (C) domain followed by DOT - var S_EMAIL = makeState$1(EMAIL); // (C) Possible email address (could have more tlds) - var S_EMAIL_COLON = makeState$1(); // (C) URL followed by colon (potential port number here) - var S_EMAIL_PORT = makeState$1(EMAIL); // (C) Email address with a port - var S_LOCALPART = makeState$1(); // Local part of the email address - var S_LOCALPART_AT = makeState$1(); // Local part of the email address plus @ - var S_LOCALPART_DOT = makeState$1(); // Local part of the email address plus '.' (localpart cannot end in .) - var S_NL = makeState$1(NL); // single new line - - // Make path from start to protocol (with '//') - S_START$1.on(TNL, S_NL).on(PROTOCOL, S_PROTOCOL).on(SLASH, S_PROTOCOL_SLASH); - - S_PROTOCOL.on(SLASH, S_PROTOCOL_SLASH); - S_PROTOCOL_SLASH.on(SLASH, S_PROTOCOL_SLASH_SLASH); - - // The very first potential domain name - S_START$1.on(TLD, S_DOMAIN$1).on(DOMAIN, S_DOMAIN$1).on(LOCALHOST, S_TLD).on(NUM, S_DOMAIN$1); - - // Force URL for anything sane followed by protocol - S_PROTOCOL_SLASH_SLASH.on(TLD, S_URL).on(DOMAIN, S_URL).on(NUM, S_URL).on(LOCALHOST, S_URL); - - // Account for dots and hyphens - // hyphens are usually parts of domain names - S_DOMAIN$1.on(DOT, S_DOMAIN_DOT); - S_EMAIL_DOMAIN.on(DOT, S_EMAIL_DOMAIN_DOT); - - // Hyphen can jump back to a domain name - - // After the first domain and a dot, we can find either a URL or another domain - S_DOMAIN_DOT.on(TLD, S_TLD).on(DOMAIN, S_DOMAIN$1).on(NUM, S_DOMAIN$1).on(LOCALHOST, S_DOMAIN$1); - - S_EMAIL_DOMAIN_DOT.on(TLD, S_EMAIL).on(DOMAIN, S_EMAIL_DOMAIN).on(NUM, S_EMAIL_DOMAIN).on(LOCALHOST, S_EMAIL_DOMAIN); - - // S_TLD accepts! But the URL could be longer, try to find a match greedily - // The `run` function should be able to "rollback" to the accepting state - S_TLD.on(DOT, S_DOMAIN_DOT); - S_EMAIL.on(DOT, S_EMAIL_DOMAIN_DOT); - - // Become real URLs after `SLASH` or `COLON NUM SLASH` - // Here PSS and non-PSS converge - S_TLD.on(COLON, S_TLD_COLON).on(SLASH, S_URL); - S_TLD_COLON.on(NUM, S_TLD_PORT); - S_TLD_PORT.on(SLASH, S_URL); - S_EMAIL.on(COLON, S_EMAIL_COLON); - S_EMAIL_COLON.on(NUM, S_EMAIL_PORT); - - // Types of characters the URL can definitely end in - var qsAccepting = [DOMAIN, AT, LOCALHOST, NUM, PLUS, POUND, PROTOCOL, SLASH, TLD, UNDERSCORE, SYM]; - - // Types of tokens that can follow a URL and be part of the query string - // but cannot be the very last characters - // Characters that cannot appear in the URL at all should be excluded - var qsNonAccepting = [COLON, DOT, QUERY, PUNCTUATION, CLOSEBRACE, CLOSEBRACKET, CLOSEANGLEBRACKET, CLOSEPAREN, OPENBRACE, OPENBRACKET, OPENANGLEBRACKET, OPENPAREN]; - - // These states are responsible primarily for determining whether or not to - // include the final round bracket. - - // URL, followed by an opening bracket - S_URL.on(OPENBRACE, S_URL_OPENBRACE).on(OPENBRACKET, S_URL_OPENBRACKET).on(OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(OPENPAREN, S_URL_OPENPAREN); - - // URL with extra symbols at the end, followed by an opening bracket - S_URL_NON_ACCEPTING.on(OPENBRACE, S_URL_OPENBRACE).on(OPENBRACKET, S_URL_OPENBRACKET).on(OPENANGLEBRACKET, S_URL_OPENANGLEBRACKET).on(OPENPAREN, S_URL_OPENPAREN); - - // Closing bracket component. This character WILL be included in the URL - S_URL_OPENBRACE.on(CLOSEBRACE, S_URL); - S_URL_OPENBRACKET.on(CLOSEBRACKET, S_URL); - S_URL_OPENANGLEBRACKET.on(CLOSEANGLEBRACKET, S_URL); - S_URL_OPENPAREN.on(CLOSEPAREN, S_URL); - S_URL_OPENBRACE_Q.on(CLOSEBRACE, S_URL); - S_URL_OPENBRACKET_Q.on(CLOSEBRACKET, S_URL); - S_URL_OPENANGLEBRACKET_Q.on(CLOSEANGLEBRACKET, S_URL); - S_URL_OPENPAREN_Q.on(CLOSEPAREN, S_URL); - S_URL_OPENBRACE_SYMS.on(CLOSEBRACE, S_URL); - S_URL_OPENBRACKET_SYMS.on(CLOSEBRACKET, S_URL); - S_URL_OPENANGLEBRACKET_SYMS.on(CLOSEANGLEBRACKET, S_URL); - S_URL_OPENPAREN_SYMS.on(CLOSEPAREN, S_URL); - - // URL that beings with an opening bracket, followed by a symbols. - // Note that the final state can still be `S_URL_OPENBRACE_Q` (if the URL only - // has a single opening bracket for some reason). - S_URL_OPENBRACE.on(qsAccepting, S_URL_OPENBRACE_Q); - S_URL_OPENBRACKET.on(qsAccepting, S_URL_OPENBRACKET_Q); - S_URL_OPENANGLEBRACKET.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); - S_URL_OPENPAREN.on(qsAccepting, S_URL_OPENPAREN_Q); - S_URL_OPENBRACE.on(qsNonAccepting, S_URL_OPENBRACE_SYMS); - S_URL_OPENBRACKET.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS); - S_URL_OPENANGLEBRACKET.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS); - S_URL_OPENPAREN.on(qsNonAccepting, S_URL_OPENPAREN_SYMS); - - // URL that begins with an opening bracket, followed by some symbols - S_URL_OPENBRACE_Q.on(qsAccepting, S_URL_OPENBRACE_Q); - S_URL_OPENBRACKET_Q.on(qsAccepting, S_URL_OPENBRACKET_Q); - S_URL_OPENANGLEBRACKET_Q.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); - S_URL_OPENPAREN_Q.on(qsAccepting, S_URL_OPENPAREN_Q); - S_URL_OPENBRACE_Q.on(qsNonAccepting, S_URL_OPENBRACE_Q); - S_URL_OPENBRACKET_Q.on(qsNonAccepting, S_URL_OPENBRACKET_Q); - S_URL_OPENANGLEBRACKET_Q.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_Q); - S_URL_OPENPAREN_Q.on(qsNonAccepting, S_URL_OPENPAREN_Q); - - S_URL_OPENBRACE_SYMS.on(qsAccepting, S_URL_OPENBRACE_Q); - S_URL_OPENBRACKET_SYMS.on(qsAccepting, S_URL_OPENBRACKET_Q); - S_URL_OPENANGLEBRACKET_SYMS.on(qsAccepting, S_URL_OPENANGLEBRACKET_Q); - S_URL_OPENPAREN_SYMS.on(qsAccepting, S_URL_OPENPAREN_Q); - S_URL_OPENBRACE_SYMS.on(qsNonAccepting, S_URL_OPENBRACE_SYMS); - S_URL_OPENBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENBRACKET_SYMS); - S_URL_OPENANGLEBRACKET_SYMS.on(qsNonAccepting, S_URL_OPENANGLEBRACKET_SYMS); - S_URL_OPENPAREN_SYMS.on(qsNonAccepting, S_URL_OPENPAREN_SYMS); - - // Account for the query string - S_URL.on(qsAccepting, S_URL); - S_URL_NON_ACCEPTING.on(qsAccepting, S_URL); - - S_URL.on(qsNonAccepting, S_URL_NON_ACCEPTING); - S_URL_NON_ACCEPTING.on(qsNonAccepting, S_URL_NON_ACCEPTING); - - // Email address-specific state definitions - // Note: We are not allowing '/' in email addresses since this would interfere - // with real URLs - - // Tokens allowed in the localpart of the email - var localpartAccepting = [DOMAIN, NUM, PLUS, POUND, QUERY, UNDERSCORE, SYM, TLD]; - - // Some of the tokens in `localpartAccepting` are already accounted for here and - // will not be overwritten (don't worry) - S_DOMAIN$1.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT); - S_TLD.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT); - S_DOMAIN_DOT.on(localpartAccepting, S_LOCALPART); - - // Okay we're on a localpart. Now what? - // TODO: IP addresses and what if the email starts with numbers? - S_LOCALPART.on(localpartAccepting, S_LOCALPART).on(AT, S_LOCALPART_AT) // close to an email address now - .on(DOT, S_LOCALPART_DOT); - S_LOCALPART_DOT.on(localpartAccepting, S_LOCALPART); - S_LOCALPART_AT.on(TLD, S_EMAIL_DOMAIN).on(DOMAIN, S_EMAIL_DOMAIN).on(LOCALHOST, S_EMAIL); - // States following `@` defined above - - var run$1 = function run$1(tokens) { - var len = tokens.length; - var cursor = 0; - var multis = []; - var textTokens = []; - - while (cursor < len) { - var state = S_START$1; - var secondState = null; - var nextState = null; - var multiLength = 0; - var latestAccepting = null; - var sinceAccepts = -1; - - while (cursor < len && !(secondState = state.next(tokens[cursor]))) { - // Starting tokens with nowhere to jump to. - // Consider these to be just plain text - textTokens.push(tokens[cursor++]); - } - - while (cursor < len && (nextState = secondState || state.next(tokens[cursor]))) { - - // Get the next state - secondState = null; - state = nextState; - - // Keep track of the latest accepting state - if (state.accepts()) { - sinceAccepts = 0; - latestAccepting = state; - } else if (sinceAccepts >= 0) { - sinceAccepts++; - } - - cursor++; - multiLength++; - } - - if (sinceAccepts < 0) { - - // No accepting state was found, part of a regular text token - // Add all the tokens we looked at to the text tokens array - for (var _i3 = cursor - multiLength; _i3 < cursor; _i3++) { - textTokens.push(tokens[_i3]); - } - } else { - - // Accepting state! - - // First close off the textTokens (if available) - if (textTokens.length > 0) { - multis.push(new TEXT(textTokens)); - textTokens = []; - } - - // Roll back to the latest accepting state - cursor -= sinceAccepts; - multiLength -= sinceAccepts; - - // Create a new multitoken - var MULTI = latestAccepting.emit(); - multis.push(new MULTI(tokens.slice(cursor - multiLength, cursor))); - } - } - - // Finally close off the textTokens (if available) - if (textTokens.length > 0) { - multis.push(new TEXT(textTokens)); - } - - return multis; - }; - var parser = Object.freeze({ - State: State, - TOKENS: TOKENS$1, - run: run$1, - start: S_START$1 - }); + function init$1(_ref) { + var groups = _ref.groups; + // Types of characters the URL can definitely end in + var qsAccepting = groups.domain.concat([AMPERSAND, ASTERISK, AT, BACKSLASH, BACKTICK, CARET, DOLLAR, EQUALS, HYPHEN, NUM, PERCENT, PIPE, PLUS, POUND, SLASH, SYM, TILDE, UNDERSCORE]); // Types of tokens that can follow a URL and be part of the query string + // but cannot be the very last characters + // Characters that cannot appear in the URL at all should be excluded - if (!Array.isArray) { - Array.isArray = function (arg) { - return Object.prototype.toString.call(arg) === '[object Array]'; - }; - } + var qsNonAccepting = [APOSTROPHE, CLOSEANGLEBRACKET, CLOSEBRACE, CLOSEBRACKET, CLOSEPAREN, COLON, COMMA, DOT, EXCLAMATION, OPENANGLEBRACKET, OPENBRACE, OPENBRACKET, OPENPAREN, QUERY, QUOTE, SEMI]; // For addresses without the mailto prefix + // Tokens allowed in the localpart of the email + + var localpartAccepting = [AMPERSAND, APOSTROPHE, ASTERISK, BACKSLASH, BACKTICK, CARET, CLOSEBRACE, DOLLAR, EQUALS, HYPHEN, NUM, OPENBRACE, PERCENT, PIPE, PLUS, POUND, QUERY, SLASH, SYM, TILDE, UNDERSCORE]; // The universal starting state. + + /** + * @type State<Token> + */ + + var Start = makeState(); + var Localpart = tt(Start, TILDE); // Local part of the email address + + ta(Localpart, localpartAccepting, Localpart); + ta(Localpart, groups.domain, Localpart); + var Domain = makeState(), + Scheme = makeState(), + SlashScheme = makeState(); + ta(Start, groups.domain, Domain); // parsed string ends with a potential domain name (A) + + ta(Start, groups.scheme, Scheme); // e.g., 'mailto' + + ta(Start, groups.slashscheme, SlashScheme); // e.g., 'http' + + ta(Domain, localpartAccepting, Localpart); + ta(Domain, groups.domain, Domain); + var LocalpartAt = tt(Domain, AT); // Local part of the email address plus @ + + tt(Localpart, AT, LocalpartAt); // close to an email address now + + var LocalpartDot = tt(Localpart, DOT); // Local part of the email address plus '.' (localpart cannot end in .) + + ta(LocalpartDot, localpartAccepting, Localpart); + ta(LocalpartDot, groups.domain, Localpart); + var EmailDomain = makeState(); + ta(LocalpartAt, groups.domain, EmailDomain); // parsed string starts with local email info + @ with a potential domain name + + ta(EmailDomain, groups.domain, EmailDomain); + var EmailDomainDot = tt(EmailDomain, DOT); // domain followed by DOT + + ta(EmailDomainDot, groups.domain, EmailDomain); + var Email$1 = makeState(Email); // Possible email address (could have more tlds) + + ta(EmailDomainDot, groups.tld, Email$1); + ta(EmailDomainDot, groups.utld, Email$1); + tt(LocalpartAt, LOCALHOST, Email$1); // Hyphen can jump back to a domain name + + var EmailDomainHyphen = tt(EmailDomain, HYPHEN); // parsed string starts with local email info + @ with a potential domain name + + ta(EmailDomainHyphen, groups.domain, EmailDomain); + ta(Email$1, groups.domain, EmailDomain); + tt(Email$1, DOT, EmailDomainDot); + tt(Email$1, HYPHEN, EmailDomainHyphen); // Final possible email states + + var EmailColon = tt(Email$1, COLON); // URL followed by colon (potential port number here) + + /*const EmailColonPort = */ + + ta(EmailColon, groups.numeric, Email); // URL followed by colon and port numner + // Account for dots and hyphens. Hyphens are usually parts of domain names + // (but not TLDs) + + var DomainHyphen = tt(Domain, HYPHEN); // domain followed by hyphen + + var DomainDot = tt(Domain, DOT); // domain followed by DOT + + ta(DomainHyphen, groups.domain, Domain); + ta(DomainDot, localpartAccepting, Localpart); + ta(DomainDot, groups.domain, Domain); + var DomainDotTld = makeState(Url); // Simplest possible URL with no query string + + ta(DomainDot, groups.tld, DomainDotTld); + ta(DomainDot, groups.utld, DomainDotTld); + ta(DomainDotTld, groups.domain, Domain); + ta(DomainDotTld, localpartAccepting, Localpart); + tt(DomainDotTld, DOT, DomainDot); + tt(DomainDotTld, HYPHEN, DomainHyphen); + tt(DomainDotTld, AT, LocalpartAt); + var DomainDotTldColon = tt(DomainDotTld, COLON); // URL followed by colon (potential port number here) + + var DomainDotTldColonPort = makeState(Url); // TLD followed by a port number + + ta(DomainDotTldColon, groups.numeric, DomainDotTldColonPort); // Long URL with optional port and maybe query string + + var Url$1 = makeState(Url); // URL with extra symbols at the end, followed by an opening bracket + + var UrlNonaccept = makeState(); // URL followed by some symbols (will not be part of the final URL) + // Query strings + + ta(Url$1, qsAccepting, Url$1); + ta(Url$1, qsNonAccepting, UrlNonaccept); + ta(UrlNonaccept, qsAccepting, Url$1); + ta(UrlNonaccept, qsNonAccepting, UrlNonaccept); // Become real URLs after `SLASH` or `COLON NUM SLASH` + // Here works with or without scheme:// prefix + + tt(DomainDotTld, SLASH, Url$1); + tt(DomainDotTldColonPort, SLASH, Url$1); // Note that domains that begin with schemes are treated slighly differently + var UriPrefix = tt(Scheme, COLON); // e.g., 'mailto:' or 'http://' + + var SlashSchemeColon = tt(SlashScheme, COLON); // e.g., 'http:' + + var SlashSchemeColonSlash = tt(SlashSchemeColon, SLASH); // e.g., 'http:/' + + tt(SlashSchemeColonSlash, SLASH, UriPrefix); // Scheme states can transition to domain states + + ta(Scheme, groups.domain, Domain); + tt(Scheme, DOT, DomainDot); + tt(Scheme, HYPHEN, DomainHyphen); + ta(SlashScheme, groups.domain, Domain); + tt(SlashScheme, DOT, DomainDot); + tt(SlashScheme, HYPHEN, DomainHyphen); // Force URL with scheme prefix followed by anything sane + + ta(UriPrefix, groups.domain, Url$1); + tt(UriPrefix, SLASH, Url$1); // URL, followed by an opening bracket + + var UrlOpenbrace = tt(Url$1, OPENBRACE); // URL followed by { + + var UrlOpenbracket = tt(Url$1, OPENBRACKET); // URL followed by [ + + var UrlOpenanglebracket = tt(Url$1, OPENANGLEBRACKET); // URL followed by < + + var UrlOpenparen = tt(Url$1, OPENPAREN); // URL followed by ( + + tt(UrlNonaccept, OPENBRACE, UrlOpenbrace); + tt(UrlNonaccept, OPENBRACKET, UrlOpenbracket); + tt(UrlNonaccept, OPENANGLEBRACKET, UrlOpenanglebracket); + tt(UrlNonaccept, OPENPAREN, UrlOpenparen); // Closing bracket component. This character WILL be included in the URL + + tt(UrlOpenbrace, CLOSEBRACE, Url$1); + tt(UrlOpenbracket, CLOSEBRACKET, Url$1); + tt(UrlOpenanglebracket, CLOSEANGLEBRACKET, Url$1); + tt(UrlOpenparen, CLOSEPAREN, Url$1); + tt(UrlOpenbrace, CLOSEBRACE, Url$1); // URL that beings with an opening bracket, followed by a symbols. + // Note that the final state can still be `UrlOpenbrace` (if the URL only + // has a single opening bracket for some reason). + + var UrlOpenbraceQ = makeState(Url); // URL followed by { and some symbols that the URL can end it + + var UrlOpenbracketQ = makeState(Url); // URL followed by [ and some symbols that the URL can end it + + var UrlOpenanglebracketQ = makeState(Url); // URL followed by < and some symbols that the URL can end it + + var UrlOpenparenQ = makeState(Url); // URL followed by ( and some symbols that the URL can end it + + ta(UrlOpenbrace, qsAccepting, UrlOpenbraceQ); + ta(UrlOpenbracket, qsAccepting, UrlOpenbracketQ); + ta(UrlOpenanglebracket, qsAccepting, UrlOpenanglebracketQ); + ta(UrlOpenparen, qsAccepting, UrlOpenparenQ); + var UrlOpenbraceSyms = makeState(); // UrlOpenbrace followed by some symbols it cannot end it + + var UrlOpenbracketSyms = makeState(); // UrlOpenbracketQ followed by some symbols it cannot end it + + var UrlOpenanglebracketSyms = makeState(); // UrlOpenanglebracketQ followed by some symbols it cannot end it + + var UrlOpenparenSyms = makeState(); // UrlOpenparenQ followed by some symbols it cannot end it + + ta(UrlOpenbrace, qsNonAccepting); + ta(UrlOpenbracket, qsNonAccepting); + ta(UrlOpenanglebracket, qsNonAccepting); + ta(UrlOpenparen, qsNonAccepting); // URL that begins with an opening bracket, followed by some symbols + + ta(UrlOpenbraceQ, qsAccepting, UrlOpenbraceQ); + ta(UrlOpenbracketQ, qsAccepting, UrlOpenbracketQ); + ta(UrlOpenanglebracketQ, qsAccepting, UrlOpenanglebracketQ); + ta(UrlOpenparenQ, qsAccepting, UrlOpenparenQ); + ta(UrlOpenbraceQ, qsNonAccepting, UrlOpenbraceQ); + ta(UrlOpenbracketQ, qsNonAccepting, UrlOpenbracketQ); + ta(UrlOpenanglebracketQ, qsNonAccepting, UrlOpenanglebracketQ); + ta(UrlOpenparenQ, qsNonAccepting, UrlOpenparenQ); + ta(UrlOpenbraceSyms, qsAccepting, UrlOpenbraceSyms); + ta(UrlOpenbracketSyms, qsAccepting, UrlOpenbracketQ); + ta(UrlOpenanglebracketSyms, qsAccepting, UrlOpenanglebracketQ); + ta(UrlOpenparenSyms, qsAccepting, UrlOpenparenQ); + ta(UrlOpenbraceSyms, qsNonAccepting, UrlOpenbraceSyms); + ta(UrlOpenbracketSyms, qsNonAccepting, UrlOpenbracketSyms); + ta(UrlOpenanglebracketSyms, qsNonAccepting, UrlOpenanglebracketSyms); + ta(UrlOpenparenSyms, qsNonAccepting, UrlOpenparenSyms); // Close brace/bracket to become regular URL + + tt(UrlOpenbracketQ, CLOSEBRACKET, Url$1); + tt(UrlOpenanglebracketQ, CLOSEANGLEBRACKET, Url$1); + tt(UrlOpenparenQ, CLOSEPAREN, Url$1); + tt(UrlOpenbraceQ, CLOSEBRACE, Url$1); + tt(UrlOpenbracketSyms, CLOSEBRACKET, Url$1); + tt(UrlOpenanglebracketSyms, CLOSEANGLEBRACKET, Url$1); + tt(UrlOpenparenSyms, CLOSEPAREN, Url$1); + tt(UrlOpenbraceSyms, CLOSEPAREN, Url$1); + tt(Start, LOCALHOST, DomainDotTld); // localhost is a valid URL state + + tt(Start, NL$1, Nl); // single new line + + return { + start: Start, + tokens: tk + }; + } + /** + * Run the parser state machine on a list of scanned string-based tokens to + * create a list of multi tokens, each of which represents a URL, email address, + * plain text, etc. + * + * @param {State<MultiToken>} start parser start state + * @param {string} input the original input used to generate the given tokens + * @param {Token[]} tokens list of scanned tokens + * @returns {MultiToken[]} + */ + + function run(start, input, tokens) { + var len = tokens.length; + var cursor = 0; + var multis = []; + var textTokens = []; + + while (cursor < len) { + var state = start; + var secondState = null; + var nextState = null; + var multiLength = 0; + var latestAccepting = null; + var sinceAccepts = -1; + + while (cursor < len && !(secondState = state.go(tokens[cursor].t))) { + // Starting tokens with nowhere to jump to. + // Consider these to be just plain text + textTokens.push(tokens[cursor++]); + } + + while (cursor < len && (nextState = secondState || state.go(tokens[cursor].t))) { + // Get the next state + secondState = null; + state = nextState; // Keep track of the latest accepting state + + if (state.accepts()) { + sinceAccepts = 0; + latestAccepting = state; + } else if (sinceAccepts >= 0) { + sinceAccepts++; + } + + cursor++; + multiLength++; + } + + if (sinceAccepts < 0) { + // No accepting state was found, part of a regular text token add + // the first text token to the text tokens array and try again from + // the next + cursor -= multiLength; + + if (cursor < len) { + textTokens.push(tokens[cursor]); + cursor++; + } + } else { + // Accepting state! + // First close off the textTokens (if available) + if (textTokens.length > 0) { + multis.push(initMultiToken(Text, input, textTokens)); + textTokens = []; + } // Roll back to the latest accepting state + + + cursor -= sinceAccepts; + multiLength -= sinceAccepts; // Create a new multitoken + + var Multi = latestAccepting.t; + var subtokens = tokens.slice(cursor - multiLength, cursor); + multis.push(initMultiToken(Multi, input, subtokens)); + } + } // Finally close off the textTokens (if available) + + + if (textTokens.length > 0) { + multis.push(initMultiToken(Text, input, textTokens)); + } + + return multis; + } /** - Converts a string into tokens that represent linkable and non-linkable bits - @method tokenize - @param {String} str - @return {Array} tokens - */ - var tokenize = function tokenize(str) { - return run$1(run(str)); + * Utility function for instantiating a new multitoken with all the relevant + * fields during parsing. + * @param {new (value: string, tokens: Token[]) => MultiToken} Multi class to instantiate + * @param {string} input original input string + * @param {Token[]} tokens consecutive tokens scanned from input string + * @returns {MultiToken} + */ + + function initMultiToken(Multi, input, tokens) { + var startIdx = tokens[0].s; + var endIdx = tokens[tokens.length - 1].e; + var value = input.slice(startIdx, endIdx); + return new Multi(value, tokens); + } + + var warn = typeof console !== 'undefined' && console && console.warn || function () {}; + + var warnAdvice = 'To avoid this warning, please register all custom schemes before invoking linkify the first time.'; // Side-effect initialization state + + var INIT = { + scanner: null, + parser: null, + tokenQueue: [], + pluginQueue: [], + customSchemes: [], + initialized: false }; + /** + * @typedef {{ + * start: State<string>, + * tokens: { groups: Collections<string> } & typeof tk + * }} ScannerInit + */ /** - Returns a list of linkable items in the given string. - */ - var find = function find(str) { - var type = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; - - var tokens = tokenize(str); - var filtered = []; - - for (var i = 0; i < tokens.length; i++) { - var token = tokens[i]; - if (token.isLink && (!type || token.type === type)) { - filtered.push(token.toObject()); - } - } - - return filtered; - }; + * @typedef {{ + * start: State<MultiToken>, + * tokens: typeof multi + * }} ParserInit + */ /** - Is the given string valid linkable text of some sort - Note that this does not trim the text for you. + * @typedef {(arg: { scanner: ScannerInit }) => void} TokenPlugin + */ - Optionally pass in a second `type` param, which is the type of link to test - for. + /** + * @typedef {(arg: { scanner: ScannerInit, parser: ParserInit }) => void} Plugin + */ - For example, + /** + * De-register all plugins and reset the internal state-machine. Used for + * testing; not required in practice. + * @private + */ + + function reset() { + State.groups = {}; + INIT.scanner = null; + INIT.parser = null; + INIT.tokenQueue = []; + INIT.pluginQueue = []; + INIT.customSchemes = []; + INIT.initialized = false; + } + /** + * Register a token plugin to allow the scanner to recognize additional token + * types before the parser state machine is constructed from the results. + * @param {string} name of plugin to register + * @param {TokenPlugin} plugin function that accepts the scanner state machine + * and available scanner tokens and collections and extends the state machine to + * recognize additional tokens or groups. + */ + + function registerTokenPlugin(name, plugin) { + if (typeof plugin !== 'function') { + throw new Error("linkifyjs: Invalid token plugin " + plugin + " (expects function)"); + } + + for (var i = 0; i < INIT.tokenQueue.length; i++) { + if (name === INIT.tokenQueue[i][0]) { + warn("linkifyjs: token plugin \"" + name + "\" already registered - will be overwritten"); + INIT.tokenQueue[i] = [name, plugin]; + return; + } + } + + INIT.tokenQueue.push([name, plugin]); + + if (INIT.initialized) { + warn("linkifyjs: already initialized - will not register token plugin \"" + name + "\" until you manually call linkify.init(). " + warnAdvice); + } + } + /** + * Register a linkify plugin + * @param {string} name of plugin to register + * @param {Plugin} plugin function that accepts the parser state machine and + * extends the parser to recognize additional link types + */ + + function registerPlugin(name, plugin) { + if (typeof plugin !== 'function') { + throw new Error("linkifyjs: Invalid plugin " + plugin + " (expects function)"); + } + + for (var i = 0; i < INIT.pluginQueue.length; i++) { + if (name === INIT.pluginQueue[i][0]) { + warn("linkifyjs: plugin \"" + name + "\" already registered - will be overwritten"); + INIT.pluginQueue[i] = [name, plugin]; + return; + } + } + + INIT.pluginQueue.push([name, plugin]); + + if (INIT.initialized) { + warn("linkifyjs: already initialized - will not register plugin \"" + name + "\" until you manually call linkify.init(). " + warnAdvice); + } + } + /** + * Detect URLs with the following additional protocol. Anything with format + * "protocol://..." will be considered a link. If `optionalSlashSlash` is set to + * `true`, anything with format "protocol:..." will be considered a link. + * @param {string} protocol + * @param {boolean} [optionalSlashSlash] + */ + + function registerCustomProtocol(scheme, optionalSlashSlash) { + if (optionalSlashSlash === void 0) { + optionalSlashSlash = false; + } + + if (INIT.initialized) { + warn("linkifyjs: already initialized - will not register custom scheme \"" + scheme + "\" until you manually call linkify.init(). " + warnAdvice); + } + + if (!/^[0-9a-z]+(-[0-9a-z]+)*$/.test(scheme)) { + throw new Error('linkifyjs: incorrect scheme format.\n 1. Must only contain digits, lowercase ASCII letters or "-"\n 2. Cannot start or end with "-"\n 3. "-" cannot repeat'); + } + + INIT.customSchemes.push([scheme, optionalSlashSlash]); + } + /** + * Initialize the linkify state machine. Called automatically the first time + * linkify is called on a string, but may be called manually as well. + */ - test(str, 'email'); + function init() { + // Initialize scanner state machine and plugins + INIT.scanner = init$2(INIT.customSchemes); - Will return `true` if str is a valid email. - */ - var test = function test(str) { - var type = arguments.length <= 1 || arguments[1] === undefined ? null : arguments[1]; + for (var i = 0; i < INIT.tokenQueue.length; i++) { + INIT.tokenQueue[i][1]({ + scanner: INIT.scanner + }); + } // Initialize parser state machine and plugins - var tokens = tokenize(str); - return tokens.length === 1 && tokens[0].isLink && (!type || tokens[0].type === type); - }; + INIT.parser = init$1(INIT.scanner.tokens); + + for (var _i = 0; _i < INIT.pluginQueue.length; _i++) { + INIT.pluginQueue[_i][1]({ + scanner: INIT.scanner, + parser: INIT.parser + }); + } + + INIT.initialized = true; + } + /** + * Parse a string into tokens that represent linkable and non-linkable sub-components + * @param {string} str + * @return {MultiToken[]} tokens + */ + + function tokenize(str) { + if (!INIT.initialized) { + init(); + } + + return run(INIT.parser.start, str, run$1(INIT.scanner.start, str)); + } + /** + * Find a list of linkable items in the given string. + * @param {string} str string to find links in + * @param {string | Opts} [type] either formatting options or specific type of + * links to find, e.g., 'url' or 'email' + * @param {Opts} [opts] formatting options for final output. Cannot be specified + * if opts already provided in `type` argument + */ + + function find(str, type, opts) { + if (type === void 0) { + type = null; + } + + if (opts === void 0) { + opts = null; + } + + if (type && typeof type === 'object') { + if (opts) { + throw Error("linkifyjs: Invalid link type " + type + "; must be a string"); + } + + opts = type; + type = null; + } + + var options = new Options(opts); + var tokens = tokenize(str); + var filtered = []; + + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; + + if (token.isLink && (!type || token.t === type)) { + filtered.push(token.toFormattedObject(options)); + } + } + + return filtered; + } + /** + * Is the given string valid linkable text of some sort. Note that this does not + * trim the text for you. + * + * Optionally pass in a second `type` param, which is the type of link to test + * for. + * + * For example, + * + * linkify.test(str, 'email'); + * + * Returns `true` if str is a valid email. + * @param {string} str string to test for links + * @param {string} [type] optional specific link type to look for + * @returns boolean true/false + */ + + function test(str, type) { + if (type === void 0) { + type = null; + } + + var tokens = tokenize(str); + return tokens.length === 1 && tokens[0].isLink && (!type || tokens[0].t === type); + } + + exports.MultiToken = MultiToken; + exports.Options = Options; + exports.State = State; + exports.createTokenClass = createTokenClass; exports.find = find; - exports.inherits = inherits; + exports.init = init; + exports.multi = multi; exports.options = options; - exports.parser = parser; - exports.scanner = scanner; + exports.regexp = regexp; + exports.registerCustomProtocol = registerCustomProtocol; + exports.registerPlugin = registerPlugin; + exports.registerTokenPlugin = registerTokenPlugin; + exports.reset = reset; + exports.stringToArray = stringToArray; exports.test = test; exports.tokenize = tokenize; -})(window.linkify = window.linkify || {}); -})(); + + Object.defineProperty(exports, '__esModule', { value: true }); + + return exports; + +})({}); \ No newline at end of file