basant307/AI_Governance_Project
048
1'use strict';2 3var uc_micro = require('uc.micro');4 5function reFactory (opts) {6 const re = {};7 opts = opts || {};8 9 re.src_Any = uc_micro.Any.source;10 re.src_Cc = uc_micro.Cc.source;11 re.src_Z = uc_micro.Z.source;12 re.src_P = uc_micro.P.source;13 14 // \p{\Z\P\Cc\CF} (white spaces + control + format + punctuation)15 re.src_ZPCc = [re.src_Z, re.src_P, re.src_Cc].join('|');16 17 // \p{\Z\Cc} (white spaces + control)18 re.src_ZCc = [re.src_Z, re.src_Cc].join('|');19 20 // Experimental. List of chars, completely prohibited in links21 // because can separate it from other part of text22 const text_separators = '[><\uff5c]';23 24 // All possible word characters (everything without punctuation, spaces & controls)25 // Defined via punctuation & spaces to save space26 // Should be something like \p{\L\N\S\M} (\w but without `_`)27 re.src_pseudo_letter = '(?:(?!' + text_separators + '|' + re.src_ZPCc + ')' + re.src_Any + ')';28 // The same as abothe but without [0-9]29 // var src_pseudo_letter_non_d = '(?:(?![0-9]|' + src_ZPCc + ')' + src_Any + ')';30 31 re.src_ip4 =32 33 '(?:(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)';34 35 // Prohibit any of "@/[]()" in user/pass to avoid wrong domain fetch.36 re.src_auth = '(?:(?:(?!' + re.src_ZCc + '|[@/\\[\\]()]).)+@)?';37 38 re.src_port =39 40 '(?::(?:6(?:[0-4]\\d{3}|5(?:[0-4]\\d{2}|5(?:[0-2]\\d|3[0-5])))|[1-5]?\\d{1,4}))?';41 42 re.src_host_terminator =43 44 '(?=$|' + text_separators + '|' + re.src_ZPCc + ')' +45 '(?!' + (opts['---'] ? '-(?!--)|' : '-|') + '_|:\\d|\\.-|\\.(?!$|' + re.src_ZPCc + '))';46 47 re.src_path =48 49 '(?:' +50 '[/?#]' +51 '(?:' +52 '(?!' + re.src_ZCc + '|' + text_separators + '|[()[\\]{}.,"\'?!\\-;]).|' +53 '\\[(?:(?!' + re.src_ZCc + '|\\]).)*\\]|' +54 '\\((?:(?!' + re.src_ZCc + '|[)]).)*\\)|' +55 '\\{(?:(?!' + re.src_ZCc + '|[}]).)*\\}|' +56 '\\"(?:(?!' + re.src_ZCc + '|["]).)+\\"|' +57 "\\'(?:(?!" + re.src_ZCc + "|[']).)+\\'|" +58 59 // allow `I'm_king` if no pair found60 "\\'(?=" + re.src_pseudo_letter + '|[-])|' +61 62 // google has many dots in "google search" links (#66, #81).63 // github has ... in commit range links,64 // Restrict to65 // - english66 // - percent-encoded67 // - parts of file path68 // - params separator69 // until more examples found.70 '\\.{2,}[a-zA-Z0-9%/&]|' +71 72 '\\.(?!' + re.src_ZCc + '|[.]|$)|' +73 (opts['---']74 ? '\\-(?!--(?:[^-]|$))(?:-*)|' // `---` => long dash, terminate75 : '\\-+|'76 ) +77 // allow `,,,` in paths78 ',(?!' + re.src_ZCc + '|$)|' +79 80 // allow `;` if not followed by space-like char81 ';(?!' + re.src_ZCc + '|$)|' +82 83 // allow `!!!` in paths, but not at the end84 '\\!+(?!' + re.src_ZCc + '|[!]|$)|' +85 86 '\\?(?!' + re.src_ZCc + '|[?]|$)' +87 ')+' +88 '|\\/' +89 ')?';90 91 // Allow anything in markdown spec, forbid quote (") at the first position92 // because emails enclosed in quotes are far more common93 re.src_email_name =94 95 '[\\-;:&=\\+\\$,\\.a-zA-Z0-9_][\\-;:&=\\+\\$,\\"\\.a-zA-Z0-9_]*';96 97 re.src_xn =98 99 'xn--[a-z0-9\\-]{1,59}';100 101 // More to read about domain names102 // http://serverfault.com/questions/638260/103 104 re.src_domain_root =105 106 // Allow letters & digits (http://test1)107 '(?:' +108 re.src_xn +109 '|' +110 re.src_pseudo_letter + '{1,63}' +111 ')';112 113 re.src_domain =114 115 '(?:' +116 re.src_xn +117 '|' +118 '(?:' + re.src_pseudo_letter + ')' +119 '|' +120 '(?:' + re.src_pseudo_letter + '(?:-|' + re.src_pseudo_letter + '){0,61}' + re.src_pseudo_letter + ')' +121 ')';122 123 re.src_host =124 125 '(?:' +126 // Don't need IP check, because digits are already allowed in normal domain names127 // src_ip4 +128 // '|' +129 '(?:(?:(?:' + re.src_domain + ')\\.)*' + re.src_domain/* _root */ + ')' +130 ')';131 132 re.tpl_host_fuzzy =133 134 '(?:' +135 re.src_ip4 +136 '|' +137 '(?:(?:(?:' + re.src_domain + ')\\.)+(?:%TLDS%))' +138 ')';139 140 re.tpl_host_no_ip_fuzzy =141 142 '(?:(?:(?:' + re.src_domain + ')\\.)+(?:%TLDS%))';143 144 re.src_host_strict =145 146 re.src_host + re.src_host_terminator;147 148 re.tpl_host_fuzzy_strict =149 150 re.tpl_host_fuzzy + re.src_host_terminator;151 152 re.src_host_port_strict =153 154 re.src_host + re.src_port + re.src_host_terminator;155 156 re.tpl_host_port_fuzzy_strict =157 158 re.tpl_host_fuzzy + re.src_port + re.src_host_terminator;159 160 re.tpl_host_port_no_ip_fuzzy_strict =161 162 re.tpl_host_no_ip_fuzzy + re.src_port + re.src_host_terminator;163 164 //165 // Main rules166 //167 168 // Rude test fuzzy links by host, for quick deny169 re.tpl_host_fuzzy_test =170 171 'localhost|www\\.|\\.\\d{1,3}\\.|(?:\\.(?:%TLDS%)(?:' + re.src_ZPCc + '|>|$))';172 173 re.tpl_email_fuzzy =174 175 '(^|' + text_separators + '|"|\\(|' + re.src_ZCc + ')' +176 '(' + re.src_email_name + '@' + re.tpl_host_fuzzy_strict + ')';177 178 re.tpl_link_fuzzy =179 // Fuzzy link can't be prepended with .:/\- and non punctuation.180 // but can start with > (markdown blockquote)181 '(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|' + re.src_ZPCc + '))' +182 '((?![$+<=>^`|\uff5c])' + re.tpl_host_port_fuzzy_strict + re.src_path + ')';183 184 re.tpl_link_no_ip_fuzzy =185 // Fuzzy link can't be prepended with .:/\- and non punctuation.186 // but can start with > (markdown blockquote)187 '(^|(?![.:/\\-_@])(?:[$+<=>^`|\uff5c]|' + re.src_ZPCc + '))' +188 '((?![$+<=>^`|\uff5c])' + re.tpl_host_port_no_ip_fuzzy_strict + re.src_path + ')';189 190 return re191}192 193//194// Helpers195//196 197// Merge objects198//199function assign (obj /* from1, from2, from3, ... */) {200 const sources = Array.prototype.slice.call(arguments, 1);201 202 sources.forEach(function (source) {203 if (!source) { return }204 205 Object.keys(source).forEach(function (key) {206 obj[key] = source[key];207 });208 });209 210 return obj211}212 213function _class (obj) { return Object.prototype.toString.call(obj) }214function isString (obj) { return _class(obj) === '[object String]' }215function isObject (obj) { return _class(obj) === '[object Object]' }216function isRegExp (obj) { return _class(obj) === '[object RegExp]' }217function isFunction (obj) { return _class(obj) === '[object Function]' }218 219function escapeRE (str) { return str.replace(/[.?*+^$[\]\\(){}|-]/g, '\\$&') }220 221//222 223const defaultOptions = {224 fuzzyLink: true,225 fuzzyEmail: true,226 fuzzyIP: false227};228 229function isOptionsObj (obj) {230 return Object.keys(obj || {}).reduce(function (acc, k) {231 /* eslint-disable-next-line no-prototype-builtins */232 return acc || defaultOptions.hasOwnProperty(k)233 }, false)234}235 236const defaultSchemas = {237 'http:': {238 validate: function (text, pos, self) {239 const tail = text.slice(pos);240 241 if (!self.re.http) {242 // compile lazily, because "host"-containing variables can change on tlds update.243 self.re.http = new RegExp(244 '^\\/\\/' + self.re.src_auth + self.re.src_host_port_strict + self.re.src_path, 'i'245 );246 }247 if (self.re.http.test(tail)) {248 return tail.match(self.re.http)[0].length249 }250 return 0251 }252 },253 'https:': 'http:',254 'ftp:': 'http:',255 '//': {256 validate: function (text, pos, self) {257 const tail = text.slice(pos);258 259 if (!self.re.no_http) {260 // compile lazily, because "host"-containing variables can change on tlds update.261 self.re.no_http = new RegExp(262 '^' +263 self.re.src_auth +264 // Don't allow single-level domains, because of false positives like '//test'265 // with code comments266 '(?:localhost|(?:(?:' + self.re.src_domain + ')\\.)+' + self.re.src_domain_root + ')' +267 self.re.src_port +268 self.re.src_host_terminator +269 self.re.src_path,270 271 'i'272 );273 }274 275 if (self.re.no_http.test(tail)) {276 // should not be `://` & `///`, that protects from errors in protocol name277 if (pos >= 3 && text[pos - 3] === ':') { return 0 }278 if (pos >= 3 && text[pos - 3] === '/') { return 0 }279 return tail.match(self.re.no_http)[0].length280 }281 return 0282 }283 },284 'mailto:': {285 validate: function (text, pos, self) {286 const tail = text.slice(pos);287 288 if (!self.re.mailto) {289 self.re.mailto = new RegExp(290 '^' + self.re.src_email_name + '@' + self.re.src_host_strict, 'i'291 );292 }293 if (self.re.mailto.test(tail)) {294 return tail.match(self.re.mailto)[0].length295 }296 return 0297 }298 }299};300 301// RE pattern for 2-character tlds (autogenerated by ./support/tlds_2char_gen.js)302/* eslint-disable-next-line max-len */303const tlds_2ch_src_re = 'a[cdefgilmnoqrstuwxz]|b[abdefghijmnorstvwyz]|c[acdfghiklmnoruvwxyz]|d[ejkmoz]|e[cegrstu]|f[ijkmor]|g[abdefghilmnpqrstuwy]|h[kmnrtu]|i[delmnoqrst]|j[emop]|k[eghimnprwyz]|l[abcikrstuvy]|m[acdeghklmnopqrstuvwxyz]|n[acefgilopruz]|om|p[aefghklmnrstwy]|qa|r[eosuw]|s[abcdeghijklmnortuvxyz]|t[cdfghjklmnortvwz]|u[agksyz]|v[aceginu]|w[fs]|y[et]|z[amw]';304 305// DON'T try to make PRs with changes. Extend TLDs with LinkifyIt.tlds() instead306const tlds_default = 'biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф'.split('|');307 308function createValidator (re) {309 return function (text, pos) {310 const tail = text.slice(pos);311 312 if (re.test(tail)) {313 return tail.match(re)[0].length314 }315 return 0316 }317}318 319function createNormalizer () {320 return function (match, self) {321 self.normalize(match);322 }323}324 325// Schemas compiler. Build regexps.326//327function compile (self) {328 // Load & clone RE patterns.329 const re = self.re = reFactory(self.__opts__);330 331 // Define dynamic patterns332 const tlds = self.__tlds__.slice();333 334 self.onCompile();335 336 if (!self.__tlds_replaced__) {337 tlds.push(tlds_2ch_src_re);338 }339 tlds.push(re.src_xn);340 341 re.src_tlds = tlds.join('|');342 343 function untpl (tpl) { return tpl.replace('%TLDS%', re.src_tlds) }344 345 re.email_fuzzy = RegExp(untpl(re.tpl_email_fuzzy), 'i');346 re.email_fuzzy_global = RegExp(untpl(re.tpl_email_fuzzy), 'ig');347 re.link_fuzzy = RegExp(untpl(re.tpl_link_fuzzy), 'i');348 re.link_fuzzy_global = RegExp(untpl(re.tpl_link_fuzzy), 'ig');349 re.link_no_ip_fuzzy = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'i');350 re.link_no_ip_fuzzy_global = RegExp(untpl(re.tpl_link_no_ip_fuzzy), 'ig');351 re.host_fuzzy_test = RegExp(untpl(re.tpl_host_fuzzy_test), 'i');352 353 //354 // Compile each schema355 //356 357 const aliases = [];358 359 self.__compiled__ = {}; // Reset compiled data360 361 function schemaError (name, val) {362 throw new Error('(LinkifyIt) Invalid schema "' + name + '": ' + val)363 }364 365 Object.keys(self.__schemas__).forEach(function (name) {366 const val = self.__schemas__[name];367 368 // skip disabled methods369 if (val === null) { return }370 371 const compiled = { validate: null, link: null };372 373 self.__compiled__[name] = compiled;374 375 if (isObject(val)) {376 if (isRegExp(val.validate)) {377 compiled.validate = createValidator(val.validate);378 } else if (isFunction(val.validate)) {379 compiled.validate = val.validate;380 } else {381 schemaError(name, val);382 }383 384 if (isFunction(val.normalize)) {385 compiled.normalize = val.normalize;386 } else if (!val.normalize) {387 compiled.normalize = createNormalizer();388 } else {389 schemaError(name, val);390 }391 392 return393 }394 395 if (isString(val)) {396 aliases.push(name);397 return398 }399 400 schemaError(name, val);401 });402 403 //404 // Compile postponed aliases405 //406 407 aliases.forEach(function (alias) {408 if (!self.__compiled__[self.__schemas__[alias]]) {409 // Silently fail on missed schemas to avoid errons on disable.410 // schemaError(alias, self.__schemas__[alias]);411 return412 }413 414 self.__compiled__[alias].validate =415 self.__compiled__[self.__schemas__[alias]].validate;416 self.__compiled__[alias].normalize =417 self.__compiled__[self.__schemas__[alias]].normalize;418 });419 420 //421 // Fake record for guessed links422 //423 self.__compiled__[''] = { validate: null, normalize: createNormalizer() };424 425 //426 // Build schema condition427 //428 const slist = Object.keys(self.__compiled__)429 .filter(function (name) {430 // Filter disabled & fake schemas431 return name.length > 0 && self.__compiled__[name]432 })433 .map(escapeRE)434 .join('|');435 // (?!_) cause 1.5x slowdown436 self.re.schema_test = RegExp('(^|(?!_)(?:[><\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'i');437 self.re.schema_search = RegExp('(^|(?!_)(?:[><\uff5c]|' + re.src_ZPCc + '))(' + slist + ')', 'ig');438 self.re.schema_at_start = RegExp('^' + self.re.schema_search.source, 'i');439 440 self.re.pretest = RegExp(441 '(' + self.re.schema_test.source + ')|(' + self.re.host_fuzzy_test.source + ')|@',442 'i'443 );444}445 446/**447 * class Match448 *449 * Match result. Single element of array, returned by [[LinkifyIt#match]]450 **/451function Match (text, schema, index, lastIndex) {452 const raw = text.slice(index, lastIndex);453 454 /**455 * Match#schema -> String456 *457 * Prefix (protocol) for matched string.458 **/459 this.schema = schema.toLowerCase();460 /**461 * Match#index -> Number462 *463 * First position of matched string.464 **/465 this.index = index;466 /**467 * Match#lastIndex -> Number468 *469 * Next position after matched string.470 **/471 this.lastIndex = lastIndex;472 /**473 * Match#raw -> String474 *475 * Matched string.476 **/477 this.raw = raw;478 /**479 * Match#text -> String480 *481 * Notmalized text of matched string.482 **/483 this.text = raw;484 /**485 * Match#url -> String486 *487 * Normalized url of matched string.488 **/489 this.url = raw;490}491 492/**493 * class LinkifyIt494 **/495 496/**497 * new LinkifyIt(schemas, options)498 * - schemas (Object): Optional. Additional schemas to validate (prefix/validator)499 * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }500 *501 * Creates new linkifier instance with optional additional schemas.502 * Can be called without `new` keyword for convenience.503 *504 * By default understands:505 *506 * - `http(s)://...` , `ftp://...`, `mailto:...` & `//...` links507 * - "fuzzy" links and emails (example.com, foo@bar.com).508 *509 * `schemas` is an object, where each key/value describes protocol/rule:510 *511 * - __key__ - link prefix (usually, protocol name with `:` at the end, `skype:`512 * for example). `linkify-it` makes shure that prefix is not preceeded with513 * alphanumeric char and symbols. Only whitespaces and punctuation allowed.514 * - __value__ - rule to check tail after link prefix515 * - _String_ - just alias to existing rule516 * - _Object_517 * - _validate_ - validator function (should return matched length on success),518 * or `RegExp`.519 * - _normalize_ - optional function to normalize text & url of matched result520 * (for example, for @twitter mentions).521 *522 * `options`:523 *524 * - __fuzzyLink__ - recognige URL-s without `http(s):` prefix. Default `true`.525 * - __fuzzyIP__ - allow IPs in fuzzy links above. Can conflict with some texts526 * like version numbers. Default `false`.527 * - __fuzzyEmail__ - recognize emails without `mailto:` prefix.528 *529 **/530function LinkifyIt (schemas, options) {531 if (!(this instanceof LinkifyIt)) {532 return new LinkifyIt(schemas, options)533 }534 535 if (!options) {536 if (isOptionsObj(schemas)) {537 options = schemas;538 schemas = {};539 }540 }541 542 this.__opts__ = assign({}, defaultOptions, options);543 544 this.__schemas__ = assign({}, defaultSchemas, schemas);545 this.__compiled__ = {};546 547 this.__tlds__ = tlds_default;548 this.__tlds_replaced__ = false;549 550 this.re = {};551 552 compile(this);553}554 555/** chainable556 * LinkifyIt#add(schema, definition)557 * - schema (String): rule name (fixed pattern prefix)558 * - definition (String|RegExp|Object): schema definition559 *560 * Add new rule definition. See constructor description for details.561 **/562LinkifyIt.prototype.add = function add (schema, definition) {563 this.__schemas__[schema] = definition;564 compile(this);565 return this566};567 568/** chainable569 * LinkifyIt#set(options)570 * - options (Object): { fuzzyLink|fuzzyEmail|fuzzyIP: true|false }571 *572 * Set recognition options for links without schema.573 **/574LinkifyIt.prototype.set = function set (options) {575 this.__opts__ = assign(this.__opts__, options);576 return this577};578 579/**580 * LinkifyIt#test(text) -> Boolean581 *582 * Searches linkifiable pattern and returns `true` on success or `false` on fail.583 **/584LinkifyIt.prototype.test = function test (text) {585 if (!text.length) { return false }586 587 let m, re;588 589 // try to scan for link with schema - that's the most simple rule590 if (this.re.schema_test.test(text)) {591 re = this.re.schema_search;592 re.lastIndex = 0;593 while ((m = re.exec(text)) !== null) {594 if (this.testSchemaAt(text, m[2], re.lastIndex)) { return true }595 }596 }597 598 if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {599 // guess schemaless links600 if (text.search(this.re.host_fuzzy_test) >= 0) {601 if (text.match(this.__opts__.fuzzyIP ? this.re.link_fuzzy : this.re.link_no_ip_fuzzy) !== null) {602 return true603 }604 }605 }606 607 if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {608 // guess schemaless emails609 if (text.indexOf('@') >= 0) {610 // We can't skip this check, because this cases are possible:611 // 192.168.1.1@gmail.com, my.in@example.com612 if (text.match(this.re.email_fuzzy) !== null) { return true }613 }614 }615 616 return false617};618 619/**620 * LinkifyIt#pretest(text) -> Boolean621 *622 * Very quick check, that can give false positives. Returns true if link MAY BE623 * can exists. Can be used for speed optimization, when you need to check that624 * link NOT exists.625 **/626LinkifyIt.prototype.pretest = function pretest (text) {627 return this.re.pretest.test(text)628};629 630/**631 * LinkifyIt#testSchemaAt(text, name, position) -> Number632 * - text (String): text to scan633 * - name (String): rule (schema) name634 * - position (Number): text offset to check from635 *636 * Similar to [[LinkifyIt#test]] but checks only specific protocol tail exactly637 * at given position. Returns length of found pattern (0 on fail).638 **/639LinkifyIt.prototype.testSchemaAt = function testSchemaAt (text, schema, pos) {640 // If not supported schema check requested - terminate641 if (!this.__compiled__[schema.toLowerCase()]) {642 return 0643 }644 return this.__compiled__[schema.toLowerCase()].validate(text, pos, this)645};646 647/**648 * LinkifyIt#match(text) -> Array|null649 *650 * Returns array of found link descriptions or `null` on fail. We strongly651 * recommend to use [[LinkifyIt#test]] first, for best speed.652 *653 * ##### Result match description654 *655 * - __schema__ - link schema, can be empty for fuzzy links, or `//` for656 * protocol-neutral links.657 * - __index__ - offset of matched text658 * - __lastIndex__ - index of next char after mathch end659 * - __raw__ - matched text660 * - __text__ - normalized text661 * - __url__ - link, generated from matched text662 **/663LinkifyIt.prototype.match = function match (text) {664 const result = [];665 const type_schemed = [];666 const type_fuzzy_link = [];667 const type_fuzzy_email = [];668 let m, len, re;669 670 function choose (a, b) {671 if (!a) { return b }672 if (!b) { return a }673 if (a.index !== b.index) { return a.index < b.index ? a : b }674 return a.lastIndex >= b.lastIndex ? a : b675 }676 677 if (!text.length) { return null }678 679 // scan for links with schema680 if (this.re.schema_test.test(text)) {681 re = this.re.schema_search;682 re.lastIndex = 0;683 while ((m = re.exec(text)) !== null) {684 len = this.testSchemaAt(text, m[2], re.lastIndex);685 if (len) {686 type_schemed.push({687 schema: m[2],688 index: m.index + m[1].length,689 lastIndex: m.index + m[0].length + len690 });691 }692 }693 }694 695 if (this.__opts__.fuzzyLink && this.__compiled__['http:']) {696 re = this.__opts__.fuzzyIP ? this.re.link_fuzzy_global : this.re.link_no_ip_fuzzy_global;697 re.lastIndex = 0;698 while ((m = re.exec(text)) !== null) {699 type_fuzzy_link.push({700 schema: '',701 index: m.index + m[1].length,702 lastIndex: m.index + m[0].length703 });704 }705 }706 707 if (this.__opts__.fuzzyEmail && this.__compiled__['mailto:']) {708 re = this.re.email_fuzzy_global;709 re.lastIndex = 0;710 while ((m = re.exec(text)) !== null) {711 type_fuzzy_email.push({712 schema: 'mailto:',713 index: m.index + m[1].length,714 lastIndex: m.index + m[0].length715 });716 }717 }718 719 const indexes = [0, 0, 0];720 let lastIndex = 0;721 722 for (;;) {723 const candidates = [724 type_schemed[indexes[0]],725 type_fuzzy_email[indexes[1]],726 type_fuzzy_link[indexes[2]]727 ];728 729 const candidate = choose(choose(candidates[0], candidates[1]), candidates[2]);730 731 if (!candidate) { break }732 733 if (candidate === candidates[0]) {734 indexes[0]++;735 } else if (candidate === candidates[1]) {736 indexes[1]++;737 } else {738 indexes[2]++;739 }740 741 if (candidate.index < lastIndex) { continue }742 743 const match = new Match(text, candidate.schema, candidate.index, candidate.lastIndex);744 this.__compiled__[match.schema].normalize(match, this);745 result.push(match);746 lastIndex = candidate.lastIndex;747 }748 749 if (result.length) {750 return result751 }752 753 return null754};755 756/**757 * LinkifyIt#matchAtStart(text) -> Match|null758 *759 * Returns fully-formed (not fuzzy) link if it starts at the beginning760 * of the string, and null otherwise.761 **/762LinkifyIt.prototype.matchAtStart = function matchAtStart (text) {763 if (!text.length) return null764 765 const m = this.re.schema_at_start.exec(text);766 if (!m) return null767 768 const len = this.testSchemaAt(text, m[2], m[0].length);769 if (!len) return null770 771 const match = new Match(text, m[2], m.index + m[1].length, m.index + m[0].length + len);772 773 this.__compiled__[match.schema].normalize(match, this);774 return match775};776 777/** chainable778 * LinkifyIt#tlds(list [, keepOld]) -> this779 * - list (Array): list of tlds780 * - keepOld (Boolean): merge with current list if `true` (`false` by default)781 *782 * Load (or merge) new tlds list. Those are user for fuzzy links (without prefix)783 * to avoid false positives. By default this algorythm used:784 *785 * - hostname with any 2-letter root zones are ok.786 * - biz|com|edu|gov|net|org|pro|web|xxx|aero|asia|coop|info|museum|name|shop|рф787 * are ok.788 * - encoded (`xn--...`) root zones are ok.789 *790 * If list is replaced, then exact match for 2-chars root zones will be checked.791 **/792LinkifyIt.prototype.tlds = function tlds (list, keepOld) {793 list = Array.isArray(list) ? list : [list];794 795 if (!keepOld) {796 this.__tlds__ = list.slice();797 this.__tlds_replaced__ = true;798 compile(this);799 return this800 }801 802 this.__tlds__ = this.__tlds__.concat(list)803 .sort()804 .filter(function (el, idx, arr) {805 return el !== arr[idx - 1]806 })807 .reverse();808 809 compile(this);810 return this811};812 813/**814 * LinkifyIt#normalize(match)815 *816 * Default normalizer (if schema does not define it's own).817 **/818LinkifyIt.prototype.normalize = function normalize (match) {819 // Do minimal possible changes by default. Need to collect feedback prior820 // to move forward https://github.com/markdown-it/linkify-it/issues/1821 822 if (!match.schema) { match.url = 'http://' + match.url; }823 824 if (match.schema === 'mailto:' && !/^mailto:/i.test(match.url)) {825 match.url = 'mailto:' + match.url;826 }827};828 829/**830 * LinkifyIt#onCompile()831 *832 * Override to modify basic RegExp-s.833 **/834LinkifyIt.prototype.onCompile = function onCompile () {835};836 837module.exports = LinkifyIt;838 