From 2143a72d3da6bca49dc2f6d5c2df7d539d191cab Mon Sep 17 00:00:00 2001 From: Catia Goncalves Date: Thu, 3 Nov 2016 21:27:50 -0700 Subject: [PATCH] twitter-text-1.14.1 js release files --- js/pkg/twitter-text-1.14.1.js | 1324 +++++++++++++++++++++++++++++ js/pkg/twitter-text-1.14.1.min.js | 2 + 2 files changed, 1326 insertions(+) create mode 100644 js/pkg/twitter-text-1.14.1.js create mode 100644 js/pkg/twitter-text-1.14.1.min.js diff --git a/js/pkg/twitter-text-1.14.1.js b/js/pkg/twitter-text-1.14.1.js new file mode 100644 index 000000000..d2e19575b --- /dev/null +++ b/js/pkg/twitter-text-1.14.1.js @@ -0,0 +1,1324 @@ +/*! + * twitter-text-js 1.14.1 + * + * Copyright 2012 Twitter, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this work except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + */ +(function() { + if (typeof twttr === "undefined" || twttr === null) { + var twttr = {}; + } + + twttr.txt = {}; + twttr.txt.regexen = {}; + + var HTML_ENTITIES = { + '&': '&', + '>': '>', + '<': '<', + '"': '"', + "'": ''' + }; + + // HTML escaping + twttr.txt.htmlEscape = function(text) { + return text && text.replace(/[&"'><]/g, function(character) { + return HTML_ENTITIES[character]; + }); + }; + + // Builds a RegExp + function regexSupplant(regex, flags) { + flags = flags || ""; + if (typeof regex !== "string") { + if (regex.global && flags.indexOf("g") < 0) { + flags += "g"; + } + if (regex.ignoreCase && flags.indexOf("i") < 0) { + flags += "i"; + } + if (regex.multiline && flags.indexOf("m") < 0) { + flags += "m"; + } + + regex = regex.source; + } + + return new RegExp(regex.replace(/#\{(\w+)\}/g, function(match, name) { + var newRegex = twttr.txt.regexen[name] || ""; + if (typeof newRegex !== "string") { + newRegex = newRegex.source; + } + return newRegex; + }), flags); + } + + twttr.txt.regexSupplant = regexSupplant; + + // simple string interpolation + function stringSupplant(str, values) { + return str.replace(/#\{(\w+)\}/g, function(match, name) { + return values[name] || ""; + }); + } + + twttr.txt.stringSupplant = stringSupplant; + + twttr.txt.regexen.spaces_group = /\x09-\x0D\x20\x85\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/; + twttr.txt.regexen.spaces = regexSupplant(/[#{spaces_group}]/); + twttr.txt.regexen.invalid_chars_group = /\uFFFE\uFEFF\uFFFF\u202A-\u202E/; + twttr.txt.regexen.invalid_chars = regexSupplant(/[#{invalid_chars_group}]/); + twttr.txt.regexen.punct = /\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$/; + twttr.txt.regexen.rtl_chars = /[\u0600-\u06FF]|[\u0750-\u077F]|[\u0590-\u05FF]|[\uFE70-\uFEFF]/mg; + twttr.txt.regexen.non_bmp_code_pairs = /[\uD800-\uDBFF][\uDC00-\uDFFF]/mg; + + twttr.txt.regexen.latinAccentChars = /\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u024F\u0253\u0254\u0256\u0257\u0259\u025B\u0263\u0268\u026F\u0272\u0289\u028B\u02BB\u0300-\u036F\u1E00-\u1EFF/; + + // Generated from unicode_regex/unicode_regex_groups.scala, same as objective c's \p{L}\p{M} + twttr.txt.regexen.bmpLetterAndMarks = /A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u052f\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07ca-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0-\u08b2\u08e4-\u0963\u0971-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09f0\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a70-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0c00-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c81-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0cf1\u0cf2\u0d01-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u103f\u1050-\u108f\u109a-\u109d\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\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16f1-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u180b-\u180d\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f\u1aa7\u1ab0-\u1abe\u1b00-\u1b4b\u1b6b-\u1b73\u1b80-\u1baf\u1bba-\u1bf3\u1c00-\u1c37\u1c4d-\u1c4f\u1c5a-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1cf8\u1cf9\u1d00-\u1df5\u1dfc-\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\u20d0-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2183\u2184\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005\u3006\u302a-\u302f\u3031-\u3035\u303b\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua672\ua674-\ua67d\ua67f-\ua69d\ua69f-\ua6e5\ua6f0\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c4\ua8e0-\ua8f7\ua8fb\ua90a-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf\ua9e0-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabea\uabec\uabed\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf870-\uf87f\uf882\uf884-\uf89f\uf8b8\uf8c1-\uf8d6\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2d\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc/; + twttr.txt.regexen.astralLetterAndMarks = /\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf30-\udf40\udf42-\udf49\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf]|\ud801[\udc00-\udc9d\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe\uddbf\ude00-\ude03\ude05\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude33\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48]|\ud804[\udc00-\udc46\udc7f-\udcba\udcd0-\udce8\udd00-\udd34\udd50-\udd73\udd76\udd80-\uddc4\uddda\ude00-\ude11\ude13-\ude37\udeb0-\udeea\udf01-\udf03\udf05-\udf0c\udf0f\udf10\udf13-\udf28\udf2a-\udf30\udf32\udf33\udf35-\udf39\udf3c-\udf44\udf47\udf48\udf4b-\udf4d\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc80-\udcc5\udcc7\udd80-\uddb5\uddb8-\uddc0\ude00-\ude40\ude44\ude80-\udeb7]|\ud806[\udca0-\udcdf\udcff\udec0-\udef8]|\ud808[\udc00-\udf98]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\udf00-\udf44\udf50-\udf7e\udf8f-\udf9f]|\ud82c[\udc00\udc01]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\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]|\ud83a[\udc00-\udcc4\udcd0-\udcd6]|\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]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef]/; + + // Generated from unicode_regex/unicode_regex_groups.scala, same as objective c's \p{Nd} + twttr.txt.regexen.bmpNumerals = /0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0de6-\u0def\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\ua9f0-\ua9f9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19/; + twttr.txt.regexen.astralNumerals = /\ud801[\udca0-\udca9]|\ud804[\udc66-\udc6f\udcf0-\udcf9\udd36-\udd3f\uddd0-\uddd9\udef0-\udef9]|\ud805[\udcd0-\udcd9\ude50-\ude59\udec0-\udec9]|\ud806[\udce0-\udce9]|\ud81a[\ude60-\ude69\udf50-\udf59]|\ud835[\udfce-\udfff]/; + + twttr.txt.regexen.hashtagSpecialChars = /_\u200c\u200d\ua67e\u05be\u05f3\u05f4\uff5e\u301c\u309b\u309c\u30a0\u30fb\u3003\u0f0b\u0f0c\xb7/; + + // A hashtag must contain at least one unicode letter or mark, as well as numbers, underscores, and select special characters. + twttr.txt.regexen.hashSigns = /[##]/; + twttr.txt.regexen.hashtagAlpha = regexSupplant(/(?:[#{bmpLetterAndMarks}]|(?=#{non_bmp_code_pairs})(?:#{astralLetterAndMarks}))/); + twttr.txt.regexen.hashtagAlphaNumeric = regexSupplant(/(?:[#{bmpLetterAndMarks}#{bmpNumerals}#{hashtagSpecialChars}]|(?=#{non_bmp_code_pairs})(?:#{astralLetterAndMarks}|#{astralNumerals}))/); + twttr.txt.regexen.endHashtagMatch = regexSupplant(/^(?:#{hashSigns}|:\/\/)/); + twttr.txt.regexen.codePoint = /(?:[^\uD800-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])/; + twttr.txt.regexen.hashtagBoundary = regexSupplant(/(?:^|$|(?!#{hashtagAlphaNumeric}|&)#{codePoint})/); + twttr.txt.regexen.validHashtag = regexSupplant(/(#{hashtagBoundary})(#{hashSigns})(?!\uFE0F|\u20E3)(#{hashtagAlphaNumeric}*#{hashtagAlpha}#{hashtagAlphaNumeric}*)/gi); + + // Mention related regex collection + twttr.txt.regexen.validMentionPrecedingChars = /(?:^|[^a-zA-Z0-9_!#$%&*@@]|(?:^|[^a-zA-Z0-9_+~.-])(?:rt|RT|rT|Rt):?)/; + twttr.txt.regexen.atSigns = /[@@]/; + twttr.txt.regexen.validMentionOrList = regexSupplant( + '(#{validMentionPrecedingChars})' + // $1: Preceding character + '(#{atSigns})' + // $2: At mark + '([a-zA-Z0-9_]{1,20})' + // $3: Screen name + '(\/[a-zA-Z][a-zA-Z0-9_\-]{0,24})?' // $4: List (optional) + , 'g'); + twttr.txt.regexen.validReply = regexSupplant(/^(?:#{spaces})*#{atSigns}([a-zA-Z0-9_]{1,20})/); + twttr.txt.regexen.endMentionMatch = regexSupplant(/^(?:#{atSigns}|[#{latinAccentChars}]|:\/\/)/); + + // URL related regex collection + twttr.txt.regexen.validUrlPrecedingChars = regexSupplant(/(?:[^A-Za-z0-9@@$###{invalid_chars_group}]|^)/); + twttr.txt.regexen.invalidUrlWithoutProtocolPrecedingChars = /[-_.\/]$/; + twttr.txt.regexen.invalidDomainChars = stringSupplant("#{punct}#{spaces_group}#{invalid_chars_group}", twttr.txt.regexen); + twttr.txt.regexen.validDomainChars = regexSupplant(/[^#{invalidDomainChars}]/); + twttr.txt.regexen.validSubdomain = regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/); + twttr.txt.regexen.validDomainName = regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/); + twttr.txt.regexen.validGTLD = regexSupplant(RegExp( +'(?:(?:' + + '삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|' + + '政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|ポイント|' + + 'ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|شبكة|بيتك|بازار|' + + 'العليان|ارامكو|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|дети|zuerich|zone|zippo|zip|zero|zara|' + + 'zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|yahoo|yachts|xyz|xxx|xperia|xin|' + + 'xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|wolterskluwer|wme|winners|wine|' + + 'windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|website|weber|webcam|' + + 'weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|wales|vuelos|voyage|' + + 'voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|vista|vision|visa|' + + 'virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|vermögensberatung|' + + 'vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|university|unicom|' + + 'uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|travelers|' + + 'travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|toray|top|' + + 'tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|tiaa|theatre|' + + 'theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|tci|taxi|tax|' + + 'tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|swiss|swiftcover|' + + 'swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|stream|store|' + + 'storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|stada|srt|srl|' + + 'spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|softbank|social|' + + 'soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|showtime|show|' + + 'shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|sew|seven|ses|' + + 'services|sener|select|seek|security|secure|seat|scot|scor|scjohnson|science|schwarz|schule|' + + 'school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|sarl|sapo|sap|sanofi|' + + 'sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|saarland|ryukyu|rwe|run|' + + 'ruhr|rsvp|room|rogers|rodeo|rocks|rocher|rip|rio|rightathome|ricoh|richardli|rich|rexroth|' + + 'reviews|review|restaurant|rest|republican|report|repair|rentals|rent|ren|reit|reisen|reise|' + + 'rehab|redumbrella|redstone|red|recipes|realty|realtor|realestate|read|raid|radio|racing|qvc|' + + 'quest|quebec|qpon|pwc|pub|prudential|pru|protection|property|properties|promo|progressive|prof|' + + 'productions|prod|pro|prime|press|praxi|pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|' + + 'playstation|play|place|pizza|pioneer|pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|' + + 'photos|photography|photo|philips|pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|' + + 'pars|paris|panerai|panasonic|pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|' + + 'organic|org|orange|oracle|open|ooo|onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|' + + 'olayan|okinawa|office|off|observer|obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|' + + 'northwesternmutual|nokia|nissay|nissan|ninja|nikon|nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|' + + 'news|new|neustar|network|netflix|netbank|net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|' + + 'nab|mutuelle|mutual|museum|mtr|mtpc|mtn|msd|movistar|movie|mov|motorcycles|moscow|mortgage|' + + 'mormon|mopar|montblanc|monster|money|monash|mom|moi|moe|moda|mobily|mobi|mma|mls|mlb|mitsubishi|' + + 'mit|mint|mini|mil|microsoft|miami|metlife|meo|menu|men|memorial|meme|melbourne|meet|media|med|' + + 'mckinsey|mcdonalds|mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|mango|' + + 'management|man|makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|' + + 'lpl|love|lotto|lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|' + + 'lincoln|limo|limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|' + + 'lego|legal|lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|' + + 'land|lancome|lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|' + + 'kpmg|kosher|komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|' + + 'kerryhotels|kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|' + + 'jewelry|jetzt|jeep|jcp|jcb|java|jaguar|iwc|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|' + + 'investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|' + + 'industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|' + + 'hughes|htc|hsbc|how|house|hotmail|hoteles|hot|hosting|host|horse|honeywell|honda|homesense|' + + 'homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|hiphop|hgtv|hermes|' + + 'here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|guru|guitars|guide|' + + 'guge|gucci|guardian|group|gripe|green|gratis|graphics|grainger|gov|got|gop|google|goog|goodyear|' + + 'goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|globo|global|gle|glass|glade|' + + 'giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|garden|gap|games|game|gallup|' + + 'gallo|gallery|gal|fyi|futbol|furniture|fund|fujixerox|fujitsu|ftr|frontier|frontdoor|frogans|' + + 'frl|fresenius|fox|foundation|forum|forsale|forex|ford|football|foodnetwork|foo|fly|flsmidth|' + + 'flowers|florist|flir|flights|flickr|fitness|fit|fishing|fish|firmdale|firestone|fire|financial|' + + 'finance|final|film|fido|fidelity|fiat|ferrero|ferrari|feedback|fedex|fast|fashion|farmers|farm|' + + 'fans|fan|family|faith|fairwinds|fail|fage|extraspace|express|exposed|expert|exchange|everbank|' + + 'events|eus|eurovision|esurance|estate|esq|erni|ericsson|equipment|epson|epost|enterprises|' + + 'engineering|engineer|energy|emerck|email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|' + + 'dupont|duns|dunlop|duck|dubai|dtv|drive|download|dot|doosan|domains|doha|dog|dodge|doctor|docs|' + + 'dnp|diy|dish|discover|discount|directory|direct|digital|diet|diamonds|dhl|dev|design|desi|' + + 'dentist|dental|democrat|delta|deloitte|dell|delivery|degree|deals|dealer|deal|dds|dclk|day|' + + 'datsun|dating|date|dance|dad|dabur|cyou|cymru|cuisinella|csc|cruises|crs|crown|cricket|' + + 'creditunion|creditcard|credit|courses|coupons|coupon|country|corsica|coop|cool|cookingchannel|' + + 'cooking|contractors|contact|consulting|construction|condos|comsec|computer|compare|company|' + + 'community|commbank|comcast|com|cologne|college|coffee|codes|coach|clubmed|club|cloud|clothing|' + + 'clinique|clinic|click|cleaning|claims|cityeats|city|citic|citi|citadel|cisco|circle|cipriani|' + + 'church|chrysler|chrome|christmas|chloe|chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|' + + 'center|ceb|cbs|cbre|cbn|cba|catering|cat|casino|cash|casa|cartier|cars|careers|career|care|' + + 'cards|caravan|car|capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|' + + 'call|cal|cafe|cab|bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|' + + 'broadway|bridgestone|bradesco|boutique|bot|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|' + + 'boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|' + + 'black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|' + + 'beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|bargains|barefoot|barclays|barclaycard|' + + 'barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|autos|auto|' + + 'author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|art|arpa|' + + 'army|archi|aramco|aquarelle|apple|app|apartments|anz|anquan|android|analytics|amsterdam|amica|' + + 'amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|alipay|alibaba|' + + 'alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|afl|afamilycompany|aetna|aero|aeg|' + + 'adult|ads|adac|actor|active|aco|accountants|accountant|accenture|academy|abudhabi|abogado|able|' + + 'abc|abbvie|abbott|abb|abarth|aarp|aaa|onion' + +')(?=[^0-9a-zA-Z@]|$))')); + twttr.txt.regexen.validCCTLD = regexSupplant(RegExp( +'(?:(?:' + + '한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|' + + 'ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|' + + 'بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|' + + 'zw|zm|za|yt|ye|ws|wf|vu|vn|vi|vg|ve|vc|va|uz|uy|us|um|uk|ug|ua|tz|tw|tv|tt|tr|tp|to|tn|tm|tl|tk|' + + 'tj|th|tg|tf|td|tc|sz|sy|sx|sv|su|st|ss|sr|so|sn|sm|sl|sk|sj|si|sh|sg|se|sd|sc|sb|sa|rw|ru|rs|ro|' + + 're|qa|py|pw|pt|ps|pr|pn|pm|pl|pk|ph|pg|pf|pe|pa|om|nz|nu|nr|np|no|nl|ni|ng|nf|ne|nc|na|mz|my|mx|' + + 'mw|mv|mu|mt|ms|mr|mq|mp|mo|mn|mm|ml|mk|mh|mg|mf|me|md|mc|ma|ly|lv|lu|lt|ls|lr|lk|li|lc|lb|la|kz|' + + 'ky|kw|kr|kp|kn|km|ki|kh|kg|ke|jp|jo|jm|je|it|is|ir|iq|io|in|im|il|ie|id|hu|ht|hr|hn|hm|hk|gy|gw|' + + 'gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|' + + 'do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|' + + 'bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac' + +')(?=[^0-9a-zA-Z@]|$))')); + twttr.txt.regexen.validPunycode = /(?:xn--[0-9a-z]+)/; + twttr.txt.regexen.validSpecialCCTLD = /(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/; + twttr.txt.regexen.validDomain = regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/); + twttr.txt.regexen.validAsciiDomain = regexSupplant(/(?:(?:[\-a-z0-9#{latinAccentChars}]+)\.)+(?:#{validGTLD}|#{validCCTLD}|#{validPunycode})/gi); + twttr.txt.regexen.invalidShortDomain = regexSupplant(/^#{validDomainName}#{validCCTLD}$/i); + twttr.txt.regexen.validSpecialShortDomain = regexSupplant(/^#{validDomainName}#{validSpecialCCTLD}$/i); + twttr.txt.regexen.validPortNumber = /[0-9]+/; + twttr.txt.regexen.cyrillicLettersAndMarks = /\u0400-\u04FF/; + twttr.txt.regexen.validGeneralUrlPathChars = regexSupplant(/[a-z#{cyrillicLettersAndMarks}0-9!\*';:=\+,\.\$\/%#\[\]\-_~@\|&#{latinAccentChars}]/i); + // Allow URL paths to contain up to two nested levels of balanced parens + // 1. Used in Wikipedia URLs like /Primer_(film) + // 2. Used in IIS sessions like /S(dfd346)/ + // 3. Used in Rdio URLs like /track/We_Up_(Album_Version_(Edited))/ + twttr.txt.regexen.validUrlBalancedParens = regexSupplant( + '\\(' + + '(?:' + + '#{validGeneralUrlPathChars}+' + + '|' + + // allow one nested level of balanced parentheses + '(?:' + + '#{validGeneralUrlPathChars}*' + + '\\(' + + '#{validGeneralUrlPathChars}+' + + '\\)' + + '#{validGeneralUrlPathChars}*' + + ')' + + ')' + + '\\)' + , 'i'); + // Valid end-of-path chracters (so /foo. does not gobble the period). + // 1. Allow =&# for empty URL parameters and other URL-join artifacts + twttr.txt.regexen.validUrlPathEndingChars = regexSupplant(/[\+\-a-z#{cyrillicLettersAndMarks}0-9=_#\/#{latinAccentChars}]|(?:#{validUrlBalancedParens})/i); + // Allow @ in a url, but only in the middle. Catch things like http://example.com/@user/ + twttr.txt.regexen.validUrlPath = regexSupplant('(?:' + + '(?:' + + '#{validGeneralUrlPathChars}*' + + '(?:#{validUrlBalancedParens}#{validGeneralUrlPathChars}*)*' + + '#{validUrlPathEndingChars}'+ + ')|(?:@#{validGeneralUrlPathChars}+\/)'+ + ')', 'i'); + + twttr.txt.regexen.validUrlQueryChars = /[a-z0-9!?\*'@\(\);:&=\+\$\/%#\[\]\-_\.,~|]/i; + twttr.txt.regexen.validUrlQueryEndingChars = /[a-z0-9_&=#\/]/i; + twttr.txt.regexen.extractUrl = regexSupplant( + '(' + // $1 total match + '(#{validUrlPrecedingChars})' + // $2 Preceeding chracter + '(' + // $3 URL + '(https?:\\/\\/)?' + // $4 Protocol (optional) + '(#{validDomain})' + // $5 Domain(s) + '(?::(#{validPortNumber}))?' + // $6 Port number (optional) + '(\\/#{validUrlPath}*)?' + // $7 URL Path + '(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?' + // $8 Query String + ')' + + ')' + , 'gi'); + + twttr.txt.regexen.validTcoUrl = /^https?:\/\/t\.co\/[a-z0-9]+/i; + twttr.txt.regexen.urlHasProtocol = /^https?:\/\//i; + twttr.txt.regexen.urlHasHttps = /^https:\/\//i; + + // cashtag related regex + twttr.txt.regexen.cashtag = /(?:[a-z]{1,2}[:._-][0-9]{1,6})|(?:[a-z]{1,6}(?:[:._-](?:[a-z]|[0-9]){1,6})?)|(?:[0-9]{1,6}[:._-][a-z]{1,2})/i; + twttr.txt.regexen.validCashtag = regexSupplant('(^|#{spaces})(\\$)(#{cashtag})(?=$|\\s|[#{punct}])', 'gi'); + + // These URL validation pattern strings are based on the ABNF from RFC 3986 + twttr.txt.regexen.validateUrlUnreserved = /[a-z\u0400-\u04FF0-9\-._~]/i; + twttr.txt.regexen.validateUrlPctEncoded = /(?:%[0-9a-f]{2})/i; + twttr.txt.regexen.validateUrlSubDelims = /[!$&'()*+,;=]/i; + twttr.txt.regexen.validateUrlPchar = regexSupplant('(?:' + + '#{validateUrlUnreserved}|' + + '#{validateUrlPctEncoded}|' + + '#{validateUrlSubDelims}|' + + '[:|@]' + + ')', 'i'); + + twttr.txt.regexen.validateUrlScheme = /(?:[a-z][a-z0-9+\-.]*)/i; + twttr.txt.regexen.validateUrlUserinfo = regexSupplant('(?:' + + '#{validateUrlUnreserved}|' + + '#{validateUrlPctEncoded}|' + + '#{validateUrlSubDelims}|' + + ':' + + ')*', 'i'); + + twttr.txt.regexen.validateUrlDecOctet = /(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9]{2})|(?:2[0-4][0-9])|(?:25[0-5]))/i; + twttr.txt.regexen.validateUrlIpv4 = regexSupplant(/(?:#{validateUrlDecOctet}(?:\.#{validateUrlDecOctet}){3})/i); + + // Punting on real IPv6 validation for now + twttr.txt.regexen.validateUrlIpv6 = /(?:\[[a-f0-9:\.]+\])/i; + + // Also punting on IPvFuture for now + twttr.txt.regexen.validateUrlIp = regexSupplant('(?:' + + '#{validateUrlIpv4}|' + + '#{validateUrlIpv6}' + + ')', 'i'); + + // This is more strict than the rfc specifies + twttr.txt.regexen.validateUrlSubDomainSegment = /(?:[a-z0-9](?:[a-z0-9_\-]*[a-z0-9])?)/i; + twttr.txt.regexen.validateUrlDomainSegment = /(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?)/i; + twttr.txt.regexen.validateUrlDomainTld = /(?:[a-z](?:[a-z0-9\-]*[a-z0-9])?)/i; + twttr.txt.regexen.validateUrlDomain = regexSupplant(/(?:(?:#{validateUrlSubDomainSegment]}\.)*(?:#{validateUrlDomainSegment]}\.)#{validateUrlDomainTld})/i); + + twttr.txt.regexen.validateUrlHost = regexSupplant('(?:' + + '#{validateUrlIp}|' + + '#{validateUrlDomain}' + + ')', 'i'); + + // Unencoded internationalized domains - this doesn't check for invalid UTF-8 sequences + twttr.txt.regexen.validateUrlUnicodeSubDomainSegment = /(?:(?:[a-z0-9]|[^\u0000-\u007f])(?:(?:[a-z0-9_\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i; + twttr.txt.regexen.validateUrlUnicodeDomainSegment = /(?:(?:[a-z0-9]|[^\u0000-\u007f])(?:(?:[a-z0-9\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i; + twttr.txt.regexen.validateUrlUnicodeDomainTld = /(?:(?:[a-z]|[^\u0000-\u007f])(?:(?:[a-z0-9\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i; + twttr.txt.regexen.validateUrlUnicodeDomain = regexSupplant(/(?:(?:#{validateUrlUnicodeSubDomainSegment}\.)*(?:#{validateUrlUnicodeDomainSegment}\.)#{validateUrlUnicodeDomainTld})/i); + + twttr.txt.regexen.validateUrlUnicodeHost = regexSupplant('(?:' + + '#{validateUrlIp}|' + + '#{validateUrlUnicodeDomain}' + + ')', 'i'); + + twttr.txt.regexen.validateUrlPort = /[0-9]{1,5}/; + + twttr.txt.regexen.validateUrlUnicodeAuthority = regexSupplant( + '(?:(#{validateUrlUserinfo})@)?' + // $1 userinfo + '(#{validateUrlUnicodeHost})' + // $2 host + '(?::(#{validateUrlPort}))?' //$3 port + , "i"); + + twttr.txt.regexen.validateUrlAuthority = regexSupplant( + '(?:(#{validateUrlUserinfo})@)?' + // $1 userinfo + '(#{validateUrlHost})' + // $2 host + '(?::(#{validateUrlPort}))?' // $3 port + , "i"); + + twttr.txt.regexen.validateUrlPath = regexSupplant(/(\/#{validateUrlPchar}*)*/i); + twttr.txt.regexen.validateUrlQuery = regexSupplant(/(#{validateUrlPchar}|\/|\?)*/i); + twttr.txt.regexen.validateUrlFragment = regexSupplant(/(#{validateUrlPchar}|\/|\?)*/i); + + // Modified version of RFC 3986 Appendix B + twttr.txt.regexen.validateUrlUnencoded = regexSupplant( + '^' + // Full URL + '(?:' + + '([^:/?#]+):\\/\\/' + // $1 Scheme + ')?' + + '([^/?#]*)' + // $2 Authority + '([^?#]*)' + // $3 Path + '(?:' + + '\\?([^#]*)' + // $4 Query + ')?' + + '(?:' + + '#(.*)' + // $5 Fragment + ')?$' + , "i"); + + + // Default CSS class for auto-linked lists (along with the url class) + var DEFAULT_LIST_CLASS = "tweet-url list-slug"; + // Default CSS class for auto-linked usernames (along with the url class) + var DEFAULT_USERNAME_CLASS = "tweet-url username"; + // Default CSS class for auto-linked hashtags (along with the url class) + var DEFAULT_HASHTAG_CLASS = "tweet-url hashtag"; + // Default CSS class for auto-linked cashtags (along with the url class) + var DEFAULT_CASHTAG_CLASS = "tweet-url cashtag"; + // Options which should not be passed as HTML attributes + var OPTIONS_NOT_ATTRIBUTES = {'urlClass':true, 'listClass':true, 'usernameClass':true, 'hashtagClass':true, 'cashtagClass':true, + 'usernameUrlBase':true, 'listUrlBase':true, 'hashtagUrlBase':true, 'cashtagUrlBase':true, + 'usernameUrlBlock':true, 'listUrlBlock':true, 'hashtagUrlBlock':true, 'linkUrlBlock':true, + 'usernameIncludeSymbol':true, 'suppressLists':true, 'suppressNoFollow':true, 'targetBlank':true, + 'suppressDataScreenName':true, 'urlEntities':true, 'symbolTag':true, 'textWithSymbolTag':true, 'urlTarget':true, + 'invisibleTagAttrs':true, 'linkAttributeBlock':true, 'linkTextBlock': true, 'htmlEscapeNonEntities': true + }; + + var BOOLEAN_ATTRIBUTES = {'disabled':true, 'readonly':true, 'multiple':true, 'checked':true}; + + // Simple object cloning function for simple objects + function clone(o) { + var r = {}; + for (var k in o) { + if (o.hasOwnProperty(k)) { + r[k] = o[k]; + } + } + + return r; + } + + twttr.txt.tagAttrs = function(attributes) { + var htmlAttrs = ""; + for (var k in attributes) { + var v = attributes[k]; + if (BOOLEAN_ATTRIBUTES[k]) { + v = v ? k : null; + } + if (v == null) continue; + htmlAttrs += " " + twttr.txt.htmlEscape(k) + "=\"" + twttr.txt.htmlEscape(v.toString()) + "\""; + } + return htmlAttrs; + }; + + twttr.txt.linkToText = function(entity, text, attributes, options) { + if (!options.suppressNoFollow) { + attributes.rel = "nofollow"; + } + // if linkAttributeBlock is specified, call it to modify the attributes + if (options.linkAttributeBlock) { + options.linkAttributeBlock(entity, attributes); + } + // if linkTextBlock is specified, call it to get a new/modified link text + if (options.linkTextBlock) { + text = options.linkTextBlock(entity, text); + } + var d = { + text: text, + attr: twttr.txt.tagAttrs(attributes) + }; + return stringSupplant("#{text}", d); + }; + + twttr.txt.linkToTextWithSymbol = function(entity, symbol, text, attributes, options) { + var taggedSymbol = options.symbolTag ? "<" + options.symbolTag + ">" + symbol + "" : symbol; + text = twttr.txt.htmlEscape(text); + var taggedText = options.textWithSymbolTag ? "<" + options.textWithSymbolTag + ">" + text + "" : text; + + if (options.usernameIncludeSymbol || !symbol.match(twttr.txt.regexen.atSigns)) { + return twttr.txt.linkToText(entity, taggedSymbol + taggedText, attributes, options); + } else { + return taggedSymbol + twttr.txt.linkToText(entity, taggedText, attributes, options); + } + }; + + twttr.txt.linkToHashtag = function(entity, text, options) { + var hash = text.substring(entity.indices[0], entity.indices[0] + 1); + var hashtag = twttr.txt.htmlEscape(entity.hashtag); + var attrs = clone(options.htmlAttrs || {}); + attrs.href = options.hashtagUrlBase + hashtag; + attrs.title = "#" + hashtag; + attrs["class"] = options.hashtagClass; + if (hashtag.charAt(0).match(twttr.txt.regexen.rtl_chars)){ + attrs["class"] += " rtl"; + } + if (options.targetBlank) { + attrs.target = '_blank'; + } + + return twttr.txt.linkToTextWithSymbol(entity, hash, hashtag, attrs, options); + }; + + twttr.txt.linkToCashtag = function(entity, text, options) { + var cashtag = twttr.txt.htmlEscape(entity.cashtag); + var attrs = clone(options.htmlAttrs || {}); + attrs.href = options.cashtagUrlBase + cashtag; + attrs.title = "$" + cashtag; + attrs["class"] = options.cashtagClass; + if (options.targetBlank) { + attrs.target = '_blank'; + } + + return twttr.txt.linkToTextWithSymbol(entity, "$", cashtag, attrs, options); + }; + + twttr.txt.linkToMentionAndList = function(entity, text, options) { + var at = text.substring(entity.indices[0], entity.indices[0] + 1); + var user = twttr.txt.htmlEscape(entity.screenName); + var slashListname = twttr.txt.htmlEscape(entity.listSlug); + var isList = entity.listSlug && !options.suppressLists; + var attrs = clone(options.htmlAttrs || {}); + attrs["class"] = (isList ? options.listClass : options.usernameClass); + attrs.href = isList ? options.listUrlBase + user + slashListname : options.usernameUrlBase + user; + if (!isList && !options.suppressDataScreenName) { + attrs['data-screen-name'] = user; + } + if (options.targetBlank) { + attrs.target = '_blank'; + } + + return twttr.txt.linkToTextWithSymbol(entity, at, isList ? user + slashListname : user, attrs, options); + }; + + twttr.txt.linkToUrl = function(entity, text, options) { + var url = entity.url; + var displayUrl = url; + var linkText = twttr.txt.htmlEscape(displayUrl); + + // If the caller passed a urlEntities object (provided by a Twitter API + // response with include_entities=true), we use that to render the display_url + // for each URL instead of it's underlying t.co URL. + var urlEntity = (options.urlEntities && options.urlEntities[url]) || entity; + if (urlEntity.display_url) { + linkText = twttr.txt.linkTextWithEntity(urlEntity, options); + } + + var attrs = clone(options.htmlAttrs || {}); + + if (!url.match(twttr.txt.regexen.urlHasProtocol)) { + url = "http://" + url; + } + attrs.href = url; + + if (options.targetBlank) { + attrs.target = '_blank'; + } + + // set class only if urlClass is specified. + if (options.urlClass) { + attrs["class"] = options.urlClass; + } + + // set target only if urlTarget is specified. + if (options.urlTarget) { + attrs.target = options.urlTarget; + } + + if (!options.title && urlEntity.display_url) { + attrs.title = urlEntity.expanded_url; + } + + return twttr.txt.linkToText(entity, linkText, attrs, options); + }; + + twttr.txt.linkTextWithEntity = function (entity, options) { + var displayUrl = entity.display_url; + var expandedUrl = entity.expanded_url; + + // Goal: If a user copies and pastes a tweet containing t.co'ed link, the resulting paste + // should contain the full original URL (expanded_url), not the display URL. + // + // Method: Whenever possible, we actually emit HTML that contains expanded_url, and use + // font-size:0 to hide those parts that should not be displayed (because they are not part of display_url). + // Elements with font-size:0 get copied even though they are not visible. + // Note that display:none doesn't work here. Elements with display:none don't get copied. + // + // Additionally, we want to *display* ellipses, but we don't want them copied. To make this happen we + // wrap the ellipses in a tco-ellipsis class and provide an onCopy handler that sets display:none on + // everything with the tco-ellipsis class. + // + // Exception: pic.twitter.com images, for which expandedUrl = "https://twitter.com/#!/username/status/1234/photo/1 + // For those URLs, display_url is not a substring of expanded_url, so we don't do anything special to render the elided parts. + // For a pic.twitter.com URL, the only elided part will be the "https://", so this is fine. + + var displayUrlSansEllipses = displayUrl.replace(/…/g, ""); // We have to disregard ellipses for matching + // Note: we currently only support eliding parts of the URL at the beginning or the end. + // Eventually we may want to elide parts of the URL in the *middle*. If so, this code will + // become more complicated. We will probably want to create a regexp out of display URL, + // replacing every ellipsis with a ".*". + if (expandedUrl.indexOf(displayUrlSansEllipses) != -1) { + var displayUrlIndex = expandedUrl.indexOf(displayUrlSansEllipses); + var v = { + displayUrlSansEllipses: displayUrlSansEllipses, + // Portion of expandedUrl that precedes the displayUrl substring + beforeDisplayUrl: expandedUrl.substr(0, displayUrlIndex), + // Portion of expandedUrl that comes after displayUrl + afterDisplayUrl: expandedUrl.substr(displayUrlIndex + displayUrlSansEllipses.length), + precedingEllipsis: displayUrl.match(/^…/) ? "…" : "", + followingEllipsis: displayUrl.match(/…$/) ? "…" : "" + }; + for (var k in v) { + if (v.hasOwnProperty(k)) { + v[k] = twttr.txt.htmlEscape(v[k]); + } + } + // As an example: The user tweets "hi http://longdomainname.com/foo" + // This gets shortened to "hi http://t.co/xyzabc", with display_url = "…nname.com/foo" + // This will get rendered as: + // + // … + // + // http://longdomai + // + // + // nname.com/foo + // + // + //   + // … + // + v['invisible'] = options.invisibleTagAttrs; + return stringSupplant("#{precedingEllipsis} #{beforeDisplayUrl}#{displayUrlSansEllipses}#{afterDisplayUrl} #{followingEllipsis}", v); + } + return displayUrl; + }; + + twttr.txt.autoLinkEntities = function(text, entities, options) { + options = clone(options || {}); + + options.hashtagClass = options.hashtagClass || DEFAULT_HASHTAG_CLASS; + options.hashtagUrlBase = options.hashtagUrlBase || "https://twitter.com/#!/search?q=%23"; + options.cashtagClass = options.cashtagClass || DEFAULT_CASHTAG_CLASS; + options.cashtagUrlBase = options.cashtagUrlBase || "https://twitter.com/#!/search?q=%24"; + options.listClass = options.listClass || DEFAULT_LIST_CLASS; + options.usernameClass = options.usernameClass || DEFAULT_USERNAME_CLASS; + options.usernameUrlBase = options.usernameUrlBase || "https://twitter.com/"; + options.listUrlBase = options.listUrlBase || "https://twitter.com/"; + options.htmlAttrs = twttr.txt.extractHtmlAttrsFromOptions(options); + options.invisibleTagAttrs = options.invisibleTagAttrs || "style='position:absolute;left:-9999px;'"; + + // remap url entities to hash + var urlEntities, i, len; + if(options.urlEntities) { + urlEntities = {}; + for(i = 0, len = options.urlEntities.length; i < len; i++) { + urlEntities[options.urlEntities[i].url] = options.urlEntities[i]; + } + options.urlEntities = urlEntities; + } + + var result = ""; + var beginIndex = 0; + + // sort entities by start index + entities.sort(function(a,b){ return a.indices[0] - b.indices[0]; }); + + var nonEntity = options.htmlEscapeNonEntities ? twttr.txt.htmlEscape : function(text) { + return text; + }; + + for (var i = 0; i < entities.length; i++) { + var entity = entities[i]; + result += nonEntity(text.substring(beginIndex, entity.indices[0])); + + if (entity.url) { + result += twttr.txt.linkToUrl(entity, text, options); + } else if (entity.hashtag) { + result += twttr.txt.linkToHashtag(entity, text, options); + } else if (entity.screenName) { + result += twttr.txt.linkToMentionAndList(entity, text, options); + } else if (entity.cashtag) { + result += twttr.txt.linkToCashtag(entity, text, options); + } + beginIndex = entity.indices[1]; + } + result += nonEntity(text.substring(beginIndex, text.length)); + return result; + }; + + twttr.txt.autoLinkWithJSON = function(text, json, options) { + // map JSON entity to twitter-text entity + if (json.user_mentions) { + for (var i = 0; i < json.user_mentions.length; i++) { + // this is a @mention + json.user_mentions[i].screenName = json.user_mentions[i].screen_name; + } + } + + if (json.hashtags) { + for (var i = 0; i < json.hashtags.length; i++) { + // this is a #hashtag + json.hashtags[i].hashtag = json.hashtags[i].text; + } + } + + if (json.symbols) { + for (var i = 0; i < json.symbols.length; i++) { + // this is a $CASH tag + json.symbols[i].cashtag = json.symbols[i].text; + } + } + + // concatenate all entities + var entities = []; + for (var key in json) { + entities = entities.concat(json[key]); + } + + // modify indices to UTF-16 + twttr.txt.modifyIndicesFromUnicodeToUTF16(text, entities); + + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.extractHtmlAttrsFromOptions = function(options) { + var htmlAttrs = {}; + for (var k in options) { + var v = options[k]; + if (OPTIONS_NOT_ATTRIBUTES[k]) continue; + if (BOOLEAN_ATTRIBUTES[k]) { + v = v ? k : null; + } + if (v == null) continue; + htmlAttrs[k] = v; + } + return htmlAttrs; + }; + + twttr.txt.autoLink = function(text, options) { + var entities = twttr.txt.extractEntitiesWithIndices(text, {extractUrlsWithoutProtocol: false}); + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.autoLinkUsernamesOrLists = function(text, options) { + var entities = twttr.txt.extractMentionsOrListsWithIndices(text); + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.autoLinkHashtags = function(text, options) { + var entities = twttr.txt.extractHashtagsWithIndices(text); + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.autoLinkCashtags = function(text, options) { + var entities = twttr.txt.extractCashtagsWithIndices(text); + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.autoLinkUrlsCustom = function(text, options) { + var entities = twttr.txt.extractUrlsWithIndices(text, {extractUrlsWithoutProtocol: false}); + return twttr.txt.autoLinkEntities(text, entities, options); + }; + + twttr.txt.removeOverlappingEntities = function(entities) { + entities.sort(function(a,b){ return a.indices[0] - b.indices[0]; }); + + var prev = entities[0]; + for (var i = 1; i < entities.length; i++) { + if (prev.indices[1] > entities[i].indices[0]) { + entities.splice(i, 1); + i--; + } else { + prev = entities[i]; + } + } + }; + + twttr.txt.extractEntitiesWithIndices = function(text, options) { + var entities = twttr.txt.extractUrlsWithIndices(text, options) + .concat(twttr.txt.extractMentionsOrListsWithIndices(text)) + .concat(twttr.txt.extractHashtagsWithIndices(text, {checkUrlOverlap: false})) + .concat(twttr.txt.extractCashtagsWithIndices(text)); + + if (entities.length == 0) { + return []; + } + + twttr.txt.removeOverlappingEntities(entities); + return entities; + }; + + twttr.txt.extractMentions = function(text) { + var screenNamesOnly = [], + screenNamesWithIndices = twttr.txt.extractMentionsWithIndices(text); + + for (var i = 0; i < screenNamesWithIndices.length; i++) { + var screenName = screenNamesWithIndices[i].screenName; + screenNamesOnly.push(screenName); + } + + return screenNamesOnly; + }; + + twttr.txt.extractMentionsWithIndices = function(text) { + var mentions = [], + mentionOrList, + mentionsOrLists = twttr.txt.extractMentionsOrListsWithIndices(text); + + for (var i = 0 ; i < mentionsOrLists.length; i++) { + mentionOrList = mentionsOrLists[i]; + if (mentionOrList.listSlug == '') { + mentions.push({ + screenName: mentionOrList.screenName, + indices: mentionOrList.indices + }); + } + } + + return mentions; + }; + + /** + * Extract list or user mentions. + * (Presence of listSlug indicates a list) + */ + twttr.txt.extractMentionsOrListsWithIndices = function(text) { + if (!text || !text.match(twttr.txt.regexen.atSigns)) { + return []; + } + + var possibleNames = [], + slashListname; + + text.replace(twttr.txt.regexen.validMentionOrList, function(match, before, atSign, screenName, slashListname, offset, chunk) { + var after = chunk.slice(offset + match.length); + if (!after.match(twttr.txt.regexen.endMentionMatch)) { + slashListname = slashListname || ''; + var startPosition = offset + before.length; + var endPosition = startPosition + screenName.length + slashListname.length + 1; + possibleNames.push({ + screenName: screenName, + listSlug: slashListname, + indices: [startPosition, endPosition] + }); + } + }); + + return possibleNames; + }; + + + twttr.txt.extractReplies = function(text) { + if (!text) { + return null; + } + + var possibleScreenName = text.match(twttr.txt.regexen.validReply); + if (!possibleScreenName || + RegExp.rightContext.match(twttr.txt.regexen.endMentionMatch)) { + return null; + } + + return possibleScreenName[1]; + }; + + twttr.txt.extractUrls = function(text, options) { + var urlsOnly = [], + urlsWithIndices = twttr.txt.extractUrlsWithIndices(text, options); + + for (var i = 0; i < urlsWithIndices.length; i++) { + urlsOnly.push(urlsWithIndices[i].url); + } + + return urlsOnly; + }; + + twttr.txt.extractUrlsWithIndices = function(text, options) { + if (!options) { + options = {extractUrlsWithoutProtocol: true}; + } + if (!text || (options.extractUrlsWithoutProtocol ? !text.match(/\./) : !text.match(/:/))) { + return []; + } + + var urls = []; + + while (twttr.txt.regexen.extractUrl.exec(text)) { + var before = RegExp.$2, url = RegExp.$3, protocol = RegExp.$4, domain = RegExp.$5, path = RegExp.$7; + var endPosition = twttr.txt.regexen.extractUrl.lastIndex, + startPosition = endPosition - url.length; + + // if protocol is missing and domain contains non-ASCII characters, + // extract ASCII-only domains. + if (!protocol) { + if (!options.extractUrlsWithoutProtocol + || before.match(twttr.txt.regexen.invalidUrlWithoutProtocolPrecedingChars)) { + continue; + } + var lastUrl = null, + asciiEndPosition = 0; + domain.replace(twttr.txt.regexen.validAsciiDomain, function(asciiDomain) { + var asciiStartPosition = domain.indexOf(asciiDomain, asciiEndPosition); + asciiEndPosition = asciiStartPosition + asciiDomain.length; + lastUrl = { + url: asciiDomain, + indices: [startPosition + asciiStartPosition, startPosition + asciiEndPosition] + }; + if (path + || asciiDomain.match(twttr.txt.regexen.validSpecialShortDomain) + || !asciiDomain.match(twttr.txt.regexen.invalidShortDomain)) { + urls.push(lastUrl); + } + }); + + // no ASCII-only domain found. Skip the entire URL. + if (lastUrl == null) { + continue; + } + + // lastUrl only contains domain. Need to add path and query if they exist. + if (path) { + lastUrl.url = url.replace(domain, lastUrl.url); + lastUrl.indices[1] = endPosition; + } + } else { + // In the case of t.co URLs, don't allow additional path characters. + if (url.match(twttr.txt.regexen.validTcoUrl)) { + url = RegExp.lastMatch; + endPosition = startPosition + url.length; + } + urls.push({ + url: url, + indices: [startPosition, endPosition] + }); + } + } + + return urls; + }; + + twttr.txt.extractHashtags = function(text) { + var hashtagsOnly = [], + hashtagsWithIndices = twttr.txt.extractHashtagsWithIndices(text); + + for (var i = 0; i < hashtagsWithIndices.length; i++) { + hashtagsOnly.push(hashtagsWithIndices[i].hashtag); + } + + return hashtagsOnly; + }; + + twttr.txt.extractHashtagsWithIndices = function(text, options) { + if (!options) { + options = {checkUrlOverlap: true}; + } + + if (!text || !text.match(twttr.txt.regexen.hashSigns)) { + return []; + } + + var tags = []; + + text.replace(twttr.txt.regexen.validHashtag, function(match, before, hash, hashText, offset, chunk) { + var after = chunk.slice(offset + match.length); + if (after.match(twttr.txt.regexen.endHashtagMatch)) + return; + var startPosition = offset + before.length; + var endPosition = startPosition + hashText.length + 1; + tags.push({ + hashtag: hashText, + indices: [startPosition, endPosition] + }); + }); + + if (options.checkUrlOverlap) { + // also extract URL entities + var urls = twttr.txt.extractUrlsWithIndices(text); + if (urls.length > 0) { + var entities = tags.concat(urls); + // remove overlap + twttr.txt.removeOverlappingEntities(entities); + // only push back hashtags + tags = []; + for (var i = 0; i < entities.length; i++) { + if (entities[i].hashtag) { + tags.push(entities[i]); + } + } + } + } + + return tags; + }; + + twttr.txt.extractCashtags = function(text) { + var cashtagsOnly = [], + cashtagsWithIndices = twttr.txt.extractCashtagsWithIndices(text); + + for (var i = 0; i < cashtagsWithIndices.length; i++) { + cashtagsOnly.push(cashtagsWithIndices[i].cashtag); + } + + return cashtagsOnly; + }; + + twttr.txt.extractCashtagsWithIndices = function(text) { + if (!text || text.indexOf("$") == -1) { + return []; + } + + var tags = []; + + text.replace(twttr.txt.regexen.validCashtag, function(match, before, dollar, cashtag, offset, chunk) { + var startPosition = offset + before.length; + var endPosition = startPosition + cashtag.length + 1; + tags.push({ + cashtag: cashtag, + indices: [startPosition, endPosition] + }); + }); + + return tags; + }; + + twttr.txt.modifyIndicesFromUnicodeToUTF16 = function(text, entities) { + twttr.txt.convertUnicodeIndices(text, entities, false); + }; + + twttr.txt.modifyIndicesFromUTF16ToUnicode = function(text, entities) { + twttr.txt.convertUnicodeIndices(text, entities, true); + }; + + twttr.txt.getUnicodeTextLength = function(text) { + return text.replace(twttr.txt.regexen.non_bmp_code_pairs, ' ').length; + }; + + twttr.txt.convertUnicodeIndices = function(text, entities, indicesInUTF16) { + if (entities.length == 0) { + return; + } + + var charIndex = 0; + var codePointIndex = 0; + + // sort entities by start index + entities.sort(function(a,b){ return a.indices[0] - b.indices[0]; }); + var entityIndex = 0; + var entity = entities[0]; + + while (charIndex < text.length) { + if (entity.indices[0] == (indicesInUTF16 ? charIndex : codePointIndex)) { + var len = entity.indices[1] - entity.indices[0]; + entity.indices[0] = indicesInUTF16 ? codePointIndex : charIndex; + entity.indices[1] = entity.indices[0] + len; + + entityIndex++; + if (entityIndex == entities.length) { + // no more entity + break; + } + entity = entities[entityIndex]; + } + + var c = text.charCodeAt(charIndex); + if (0xD800 <= c && c <= 0xDBFF && charIndex < text.length - 1) { + // Found high surrogate char + c = text.charCodeAt(charIndex + 1); + if (0xDC00 <= c && c <= 0xDFFF) { + // Found surrogate pair + charIndex++; + } + } + codePointIndex++; + charIndex++; + } + }; + + // this essentially does text.split(/<|>/) + // except that won't work in IE, where empty strings are ommitted + // so "<>".split(/<|>/) => [] in IE, but is ["", "", ""] in all others + // but "<<".split("<") => ["", "", ""] + twttr.txt.splitTags = function(text) { + var firstSplits = text.split("<"), + secondSplits, + allSplits = [], + split; + + for (var i = 0; i < firstSplits.length; i += 1) { + split = firstSplits[i]; + if (!split) { + allSplits.push(""); + } else { + secondSplits = split.split(">"); + for (var j = 0; j < secondSplits.length; j += 1) { + allSplits.push(secondSplits[j]); + } + } + } + + return allSplits; + }; + + twttr.txt.hitHighlight = function(text, hits, options) { + var defaultHighlightTag = "em"; + + hits = hits || []; + options = options || {}; + + if (hits.length === 0) { + return text; + } + + var tagName = options.tag || defaultHighlightTag, + tags = ["<" + tagName + ">", ""], + chunks = twttr.txt.splitTags(text), + i, + j, + result = "", + chunkIndex = 0, + chunk = chunks[0], + prevChunksLen = 0, + chunkCursor = 0, + startInChunk = false, + chunkChars = chunk, + flatHits = [], + index, + hit, + tag, + placed, + hitSpot; + + for (i = 0; i < hits.length; i += 1) { + for (j = 0; j < hits[i].length; j += 1) { + flatHits.push(hits[i][j]); + } + } + + for (index = 0; index < flatHits.length; index += 1) { + hit = flatHits[index]; + tag = tags[index % 2]; + placed = false; + + while (chunk != null && hit >= prevChunksLen + chunk.length) { + result += chunkChars.slice(chunkCursor); + if (startInChunk && hit === prevChunksLen + chunkChars.length) { + result += tag; + placed = true; + } + + if (chunks[chunkIndex + 1]) { + result += "<" + chunks[chunkIndex + 1] + ">"; + } + + prevChunksLen += chunkChars.length; + chunkCursor = 0; + chunkIndex += 2; + chunk = chunks[chunkIndex]; + chunkChars = chunk; + startInChunk = false; + } + + if (!placed && chunk != null) { + hitSpot = hit - prevChunksLen; + result += chunkChars.slice(chunkCursor, hitSpot) + tag; + chunkCursor = hitSpot; + if (index % 2 === 0) { + startInChunk = true; + } else { + startInChunk = false; + } + } else if(!placed) { + placed = true; + result += tag; + } + } + + if (chunk != null) { + if (chunkCursor < chunkChars.length) { + result += chunkChars.slice(chunkCursor); + } + for (index = chunkIndex + 1; index < chunks.length; index += 1) { + result += (index % 2 === 0 ? chunks[index] : "<" + chunks[index] + ">"); + } + } + + return result; + }; + + var MAX_LENGTH = 140; + + // Returns the length of Tweet text with consideration to t.co URL replacement + // and chars outside the basic multilingual plane that use 2 UTF16 code points + twttr.txt.getTweetLength = function(text, options) { + if (!options) { + options = { + // These come from https://api.twitter.com/1/help/configuration.json + // described by https://dev.twitter.com/docs/api/1/get/help/configuration + short_url_length: 23, + short_url_length_https: 23 + }; + } + var textLength = twttr.txt.getUnicodeTextLength(text), + urlsWithIndices = twttr.txt.extractUrlsWithIndices(text); + twttr.txt.modifyIndicesFromUTF16ToUnicode(text, urlsWithIndices); + + for (var i = 0; i < urlsWithIndices.length; i++) { + // Subtract the length of the original URL + textLength += urlsWithIndices[i].indices[0] - urlsWithIndices[i].indices[1]; + + // Add 23 characters for URL starting with https:// + // http:// URLs still use https://t.co so they are 23 characters as well + if (urlsWithIndices[i].url.toLowerCase().match(twttr.txt.regexen.urlHasHttps)) { + textLength += options.short_url_length_https; + } else { + textLength += options.short_url_length; + } + } + + return textLength; + }; + + // Check the text for any reason that it may not be valid as a Tweet. This is meant as a pre-validation + // before posting to api.twitter.com. There are several server-side reasons for Tweets to fail but this pre-validation + // will allow quicker feedback. + // + // Returns false if this text is valid. Otherwise one of the following strings will be returned: + // + // "too_long": if the text is too long + // "empty": if the text is nil or empty + // "invalid_characters": if the text contains non-Unicode or any of the disallowed Unicode characters + twttr.txt.isInvalidTweet = function(text) { + if (!text) { + return "empty"; + } + + // Determine max length independent of URL length + if (twttr.txt.getTweetLength(text) > MAX_LENGTH) { + return "too_long"; + } + + if (twttr.txt.hasInvalidCharacters(text)) { + return "invalid_characters"; + } + + return false; + }; + + twttr.txt.hasInvalidCharacters = function(text) { + return twttr.txt.regexen.invalid_chars.test(text); + }; + + twttr.txt.isValidTweetText = function(text) { + return !twttr.txt.isInvalidTweet(text); + }; + + twttr.txt.isValidUsername = function(username) { + if (!username) { + return false; + } + + var extracted = twttr.txt.extractMentions(username); + + // Should extract the username minus the @ sign, hence the .slice(1) + return extracted.length === 1 && extracted[0] === username.slice(1); + }; + + var VALID_LIST_RE = regexSupplant(/^#{validMentionOrList}$/); + + twttr.txt.isValidList = function(usernameList) { + var match = usernameList.match(VALID_LIST_RE); + + // Must have matched and had nothing before or after + return !!(match && match[1] == "" && match[4]); + }; + + twttr.txt.isValidHashtag = function(hashtag) { + if (!hashtag) { + return false; + } + + var extracted = twttr.txt.extractHashtags(hashtag); + + // Should extract the hashtag minus the # sign, hence the .slice(1) + return extracted.length === 1 && extracted[0] === hashtag.slice(1); + }; + + twttr.txt.isValidUrl = function(url, unicodeDomains, requireProtocol) { + if (unicodeDomains == null) { + unicodeDomains = true; + } + + if (requireProtocol == null) { + requireProtocol = true; + } + + if (!url) { + return false; + } + + var urlParts = url.match(twttr.txt.regexen.validateUrlUnencoded); + + if (!urlParts || urlParts[0] !== url) { + return false; + } + + var scheme = urlParts[1], + authority = urlParts[2], + path = urlParts[3], + query = urlParts[4], + fragment = urlParts[5]; + + if (!( + (!requireProtocol || (isValidMatch(scheme, twttr.txt.regexen.validateUrlScheme) && scheme.match(/^https?$/i))) && + isValidMatch(path, twttr.txt.regexen.validateUrlPath) && + isValidMatch(query, twttr.txt.regexen.validateUrlQuery, true) && + isValidMatch(fragment, twttr.txt.regexen.validateUrlFragment, true) + )) { + return false; + } + + return (unicodeDomains && isValidMatch(authority, twttr.txt.regexen.validateUrlUnicodeAuthority)) || + (!unicodeDomains && isValidMatch(authority, twttr.txt.regexen.validateUrlAuthority)); + }; + + function isValidMatch(string, regex, optional) { + if (!optional) { + // RegExp["$&"] is the text of the last match + // blank strings are ok, but are falsy, so we check stringiness instead of truthiness + return ((typeof string === "string") && string.match(regex) && RegExp["$&"] === string); + } + + // RegExp["$&"] is the text of the last match + return (!string || (string.match(regex) && RegExp["$&"] === string)); + } + + if (typeof module != 'undefined' && module.exports) { + module.exports = twttr.txt; + } + + if (typeof define == 'function' && define.amd) { + define([], twttr.txt); + } + + if (typeof window != 'undefined') { + if (window.twttr) { + for (var prop in twttr) { + window.twttr[prop] = twttr[prop]; + } + } else { + window.twttr = twttr; + } + } +})(); diff --git a/js/pkg/twitter-text-1.14.1.min.js b/js/pkg/twitter-text-1.14.1.min.js new file mode 100644 index 000000000..9d025bb39 --- /dev/null +++ b/js/pkg/twitter-text-1.14.1.min.js @@ -0,0 +1,2 @@ +(function(){if(typeof twttr==="undefined"||twttr===null){var twttr={}}twttr.txt={};twttr.txt.regexen={};var HTML_ENTITIES={"&":"&",">":">","<":"<",'"':""","'":"'"};twttr.txt.htmlEscape=function(text){return text&&text.replace(/[&"'><]/g,function(character){return HTML_ENTITIES[character]})};function regexSupplant(regex,flags){flags=flags||"";if(typeof regex!=="string"){if(regex.global&&flags.indexOf("g")<0){flags+="g"}if(regex.ignoreCase&&flags.indexOf("i")<0){flags+="i"}if(regex.multiline&&flags.indexOf("m")<0){flags+="m"}regex=regex.source}return new RegExp(regex.replace(/#\{(\w+)\}/g,function(match,name){var newRegex=twttr.txt.regexen[name]||"";if(typeof newRegex!=="string"){newRegex=newRegex.source}return newRegex}),flags)}twttr.txt.regexSupplant=regexSupplant;function stringSupplant(str,values){return str.replace(/#\{(\w+)\}/g,function(match,name){return values[name]||""})}twttr.txt.stringSupplant=stringSupplant;twttr.txt.regexen.spaces_group=/\x09-\x0D\x20\x85\xA0\u1680\u180E\u2000-\u200A\u2028\u2029\u202F\u205F\u3000/;twttr.txt.regexen.spaces=regexSupplant(/[#{spaces_group}]/);twttr.txt.regexen.invalid_chars_group=/\uFFFE\uFEFF\uFFFF\u202A-\u202E/;twttr.txt.regexen.invalid_chars=regexSupplant(/[#{invalid_chars_group}]/);twttr.txt.regexen.punct=/\!'#%&'\(\)*\+,\\\-\.\/:;<=>\?@\[\]\^_{|}~\$/;twttr.txt.regexen.rtl_chars=/[\u0600-\u06FF]|[\u0750-\u077F]|[\u0590-\u05FF]|[\uFE70-\uFEFF]/gm;twttr.txt.regexen.non_bmp_code_pairs=/[\uD800-\uDBFF][\uDC00-\uDFFF]/gm;twttr.txt.regexen.latinAccentChars=/\xC0-\xD6\xD8-\xF6\xF8-\xFF\u0100-\u024F\u0253\u0254\u0256\u0257\u0259\u025B\u0263\u0268\u026F\u0272\u0289\u028B\u02BB\u0300-\u036F\u1E00-\u1EFF/;twttr.txt.regexen.bmpLetterAndMarks=/A-Za-z\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u052f\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u065f\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06ef\u06fa-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07ca-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0-\u08b2\u08e4-\u0963\u0971-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09f0\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a70-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0c00-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c81-\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0cf1\u0cf2\u0d01-\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u103f\u1050-\u108f\u109a-\u109d\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\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16f1-\u16f8\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u180b-\u180d\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191e\u1920-\u192b\u1930-\u193b\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f\u1aa7\u1ab0-\u1abe\u1b00-\u1b4b\u1b6b-\u1b73\u1b80-\u1baf\u1bba-\u1bf3\u1c00-\u1c37\u1c4d-\u1c4f\u1c5a-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1cf8\u1cf9\u1d00-\u1df5\u1dfc-\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\u20d0-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2183\u2184\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005\u3006\u302a-\u302f\u3031-\u3035\u303b\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua672\ua674-\ua67d\ua67f-\ua69d\ua69f-\ua6e5\ua6f0\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua7ad\ua7b0\ua7b1\ua7f7-\ua827\ua840-\ua873\ua880-\ua8c4\ua8e0-\ua8f7\ua8fb\ua90a-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf\ua9e0-\ua9ef\ua9fa-\ua9fe\uaa00-\uaa36\uaa40-\uaa4d\uaa60-\uaa76\uaa7a-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab5a\uab5c-\uab5f\uab64\uab65\uabc0-\uabea\uabec\uabed\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf870-\uf87f\uf882\uf884-\uf89f\uf8b8\uf8c1-\uf8d6\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe2d\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc/;twttr.txt.regexen.astralLetterAndMarks=/\ud800[\udc00-\udc0b\udc0d-\udc26\udc28-\udc3a\udc3c\udc3d\udc3f-\udc4d\udc50-\udc5d\udc80-\udcfa\uddfd\ude80-\ude9c\udea0-\uded0\udee0\udf00-\udf1f\udf30-\udf40\udf42-\udf49\udf50-\udf7a\udf80-\udf9d\udfa0-\udfc3\udfc8-\udfcf]|\ud801[\udc00-\udc9d\udd00-\udd27\udd30-\udd63\ude00-\udf36\udf40-\udf55\udf60-\udf67]|\ud802[\udc00-\udc05\udc08\udc0a-\udc35\udc37\udc38\udc3c\udc3f-\udc55\udc60-\udc76\udc80-\udc9e\udd00-\udd15\udd20-\udd39\udd80-\uddb7\uddbe\uddbf\ude00-\ude03\ude05\ude06\ude0c-\ude13\ude15-\ude17\ude19-\ude33\ude38-\ude3a\ude3f\ude60-\ude7c\ude80-\ude9c\udec0-\udec7\udec9-\udee6\udf00-\udf35\udf40-\udf55\udf60-\udf72\udf80-\udf91]|\ud803[\udc00-\udc48]|\ud804[\udc00-\udc46\udc7f-\udcba\udcd0-\udce8\udd00-\udd34\udd50-\udd73\udd76\udd80-\uddc4\uddda\ude00-\ude11\ude13-\ude37\udeb0-\udeea\udf01-\udf03\udf05-\udf0c\udf0f\udf10\udf13-\udf28\udf2a-\udf30\udf32\udf33\udf35-\udf39\udf3c-\udf44\udf47\udf48\udf4b-\udf4d\udf57\udf5d-\udf63\udf66-\udf6c\udf70-\udf74]|\ud805[\udc80-\udcc5\udcc7\udd80-\uddb5\uddb8-\uddc0\ude00-\ude40\ude44\ude80-\udeb7]|\ud806[\udca0-\udcdf\udcff\udec0-\udef8]|\ud808[\udc00-\udf98]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud81a[\udc00-\ude38\ude40-\ude5e\uded0-\udeed\udef0-\udef4\udf00-\udf36\udf40-\udf43\udf63-\udf77\udf7d-\udf8f]|\ud81b[\udf00-\udf44\udf50-\udf7e\udf8f-\udf9f]|\ud82c[\udc00\udc01]|\ud82f[\udc00-\udc6a\udc70-\udc7c\udc80-\udc88\udc90-\udc99\udc9d\udc9e]|\ud834[\udd65-\udd69\udd6d-\udd72\udd7b-\udd82\udd85-\udd8b\uddaa-\uddad\ude42-\ude44]|\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]|\ud83a[\udc00-\udcc4\udcd0-\udcd6]|\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]|\ud840[\udc00-\udfff]|\ud841[\udc00-\udfff]|\ud842[\udc00-\udfff]|\ud843[\udc00-\udfff]|\ud844[\udc00-\udfff]|\ud845[\udc00-\udfff]|\ud846[\udc00-\udfff]|\ud847[\udc00-\udfff]|\ud848[\udc00-\udfff]|\ud849[\udc00-\udfff]|\ud84a[\udc00-\udfff]|\ud84b[\udc00-\udfff]|\ud84c[\udc00-\udfff]|\ud84d[\udc00-\udfff]|\ud84e[\udc00-\udfff]|\ud84f[\udc00-\udfff]|\ud850[\udc00-\udfff]|\ud851[\udc00-\udfff]|\ud852[\udc00-\udfff]|\ud853[\udc00-\udfff]|\ud854[\udc00-\udfff]|\ud855[\udc00-\udfff]|\ud856[\udc00-\udfff]|\ud857[\udc00-\udfff]|\ud858[\udc00-\udfff]|\ud859[\udc00-\udfff]|\ud85a[\udc00-\udfff]|\ud85b[\udc00-\udfff]|\ud85c[\udc00-\udfff]|\ud85d[\udc00-\udfff]|\ud85e[\udc00-\udfff]|\ud85f[\udc00-\udfff]|\ud860[\udc00-\udfff]|\ud861[\udc00-\udfff]|\ud862[\udc00-\udfff]|\ud863[\udc00-\udfff]|\ud864[\udc00-\udfff]|\ud865[\udc00-\udfff]|\ud866[\udc00-\udfff]|\ud867[\udc00-\udfff]|\ud868[\udc00-\udfff]|\ud869[\udc00-\uded6\udf00-\udfff]|\ud86a[\udc00-\udfff]|\ud86b[\udc00-\udfff]|\ud86c[\udc00-\udfff]|\ud86d[\udc00-\udf34\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud87e[\udc00-\ude1d]|\udb40[\udd00-\uddef]/;twttr.txt.regexen.bmpNumerals=/0-9\u0660-\u0669\u06f0-\u06f9\u07c0-\u07c9\u0966-\u096f\u09e6-\u09ef\u0a66-\u0a6f\u0ae6-\u0aef\u0b66-\u0b6f\u0be6-\u0bef\u0c66-\u0c6f\u0ce6-\u0cef\u0d66-\u0d6f\u0de6-\u0def\u0e50-\u0e59\u0ed0-\u0ed9\u0f20-\u0f29\u1040-\u1049\u1090-\u1099\u17e0-\u17e9\u1810-\u1819\u1946-\u194f\u19d0-\u19d9\u1a80-\u1a89\u1a90-\u1a99\u1b50-\u1b59\u1bb0-\u1bb9\u1c40-\u1c49\u1c50-\u1c59\ua620-\ua629\ua8d0-\ua8d9\ua900-\ua909\ua9d0-\ua9d9\ua9f0-\ua9f9\uaa50-\uaa59\uabf0-\uabf9\uff10-\uff19/;twttr.txt.regexen.astralNumerals=/\ud801[\udca0-\udca9]|\ud804[\udc66-\udc6f\udcf0-\udcf9\udd36-\udd3f\uddd0-\uddd9\udef0-\udef9]|\ud805[\udcd0-\udcd9\ude50-\ude59\udec0-\udec9]|\ud806[\udce0-\udce9]|\ud81a[\ude60-\ude69\udf50-\udf59]|\ud835[\udfce-\udfff]/;twttr.txt.regexen.hashtagSpecialChars=/_\u200c\u200d\ua67e\u05be\u05f3\u05f4\uff5e\u301c\u309b\u309c\u30a0\u30fb\u3003\u0f0b\u0f0c\xb7/;twttr.txt.regexen.hashSigns=/[##]/;twttr.txt.regexen.hashtagAlpha=regexSupplant(/(?:[#{bmpLetterAndMarks}]|(?=#{non_bmp_code_pairs})(?:#{astralLetterAndMarks}))/);twttr.txt.regexen.hashtagAlphaNumeric=regexSupplant(/(?:[#{bmpLetterAndMarks}#{bmpNumerals}#{hashtagSpecialChars}]|(?=#{non_bmp_code_pairs})(?:#{astralLetterAndMarks}|#{astralNumerals}))/);twttr.txt.regexen.endHashtagMatch=regexSupplant(/^(?:#{hashSigns}|:\/\/)/);twttr.txt.regexen.codePoint=/(?:[^\uD800-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF])/;twttr.txt.regexen.hashtagBoundary=regexSupplant(/(?:^|$|(?!#{hashtagAlphaNumeric}|&)#{codePoint})/);twttr.txt.regexen.validHashtag=regexSupplant(/(#{hashtagBoundary})(#{hashSigns})(?!\uFE0F|\u20E3)(#{hashtagAlphaNumeric}*#{hashtagAlpha}#{hashtagAlphaNumeric}*)/gi);twttr.txt.regexen.validMentionPrecedingChars=/(?:^|[^a-zA-Z0-9_!#$%&*@@]|(?:^|[^a-zA-Z0-9_+~.-])(?:rt|RT|rT|Rt):?)/;twttr.txt.regexen.atSigns=/[@@]/;twttr.txt.regexen.validMentionOrList=regexSupplant("(#{validMentionPrecedingChars})"+"(#{atSigns})"+"([a-zA-Z0-9_]{1,20})"+"(/[a-zA-Z][a-zA-Z0-9_-]{0,24})?","g");twttr.txt.regexen.validReply=regexSupplant(/^(?:#{spaces})*#{atSigns}([a-zA-Z0-9_]{1,20})/);twttr.txt.regexen.endMentionMatch=regexSupplant(/^(?:#{atSigns}|[#{latinAccentChars}]|:\/\/)/);twttr.txt.regexen.validUrlPrecedingChars=regexSupplant(/(?:[^A-Za-z0-9@@$###{invalid_chars_group}]|^)/);twttr.txt.regexen.invalidUrlWithoutProtocolPrecedingChars=/[-_.\/]$/;twttr.txt.regexen.invalidDomainChars=stringSupplant("#{punct}#{spaces_group}#{invalid_chars_group}",twttr.txt.regexen);twttr.txt.regexen.validDomainChars=regexSupplant(/[^#{invalidDomainChars}]/);twttr.txt.regexen.validSubdomain=regexSupplant(/(?:(?:#{validDomainChars}(?:[_-]|#{validDomainChars})*)?#{validDomainChars}\.)/);twttr.txt.regexen.validDomainName=regexSupplant(/(?:(?:#{validDomainChars}(?:-|#{validDomainChars})*)?#{validDomainChars}\.)/);twttr.txt.regexen.validGTLD=regexSupplant(RegExp("(?:(?:"+"삼성|닷컴|닷넷|香格里拉|餐厅|食品|飞利浦|電訊盈科|集团|通販|购物|谷歌|诺基亚|联通|网络|网站|网店|网址|组织机构|移动|珠宝|点看|游戏|淡马锡|机构|書籍|时尚|新闻|政府|"+"政务|手表|手机|我爱你|慈善|微博|广东|工行|家電|娱乐|大拿|大众汽车|在线|嘉里大酒店|嘉里|商标|商店|商城|公益|公司|八卦|健康|信息|佛山|企业|中文网|中信|世界|ポイント|"+"ファッション|セール|ストア|コム|グーグル|クラウド|みんな|คอม|संगठन|नेट|कॉम|همراه|موقع|موبايلي|كوم|شبكة|بيتك|بازار|"+"العليان|ارامكو|ابوظبي|קום|сайт|рус|орг|онлайн|москва|ком|дети|zuerich|zone|zippo|zip|zero|zara|"+"zappos|yun|youtube|you|yokohama|yoga|yodobashi|yandex|yamaxun|yahoo|yachts|xyz|xxx|xperia|xin|"+"xihuan|xfinity|xerox|xbox|wtf|wtc|wow|world|works|work|woodside|wolterskluwer|wme|winners|wine|"+"windows|win|williamhill|wiki|wien|whoswho|weir|weibo|wedding|wed|website|weber|webcam|"+"weatherchannel|weather|watches|watch|warman|wanggou|wang|walter|walmart|wales|vuelos|voyage|"+"voto|voting|vote|volvo|volkswagen|vodka|vlaanderen|vivo|viva|vistaprint|vista|vision|visa|"+"virgin|vip|vin|villas|viking|vig|video|viajes|vet|versicherung|vermögensberatung|"+"vermögensberater|verisign|ventures|vegas|vanguard|vana|vacations|ups|uol|uno|university|unicom|"+"uconnect|ubs|ubank|tvs|tushu|tunes|tui|tube|trv|trust|travelersinsurance|travelers|"+"travelchannel|travel|training|trading|trade|toys|toyota|town|tours|total|toshiba|toray|top|"+"tools|tokyo|today|tmall|tkmaxx|tjx|tjmaxx|tirol|tires|tips|tiffany|tienda|tickets|tiaa|theatre|"+"theater|thd|teva|tennis|temasek|telefonica|telecity|tel|technology|tech|team|tdk|tci|taxi|tax|"+"tattoo|tatar|tatamotors|target|taobao|talk|taipei|tab|systems|symantec|sydney|swiss|swiftcover|"+"swatch|suzuki|surgery|surf|support|supply|supplies|sucks|style|study|studio|stream|store|"+"storage|stockholm|stcgroup|stc|statoil|statefarm|statebank|starhub|star|staples|stada|srt|srl|"+"spreadbetting|spot|spiegel|space|soy|sony|song|solutions|solar|sohu|software|softbank|social|"+"soccer|sncf|smile|smart|sling|skype|sky|skin|ski|site|singles|sina|silk|shriram|showtime|show|"+"shouji|shopping|shop|shoes|shiksha|shia|shell|shaw|sharp|shangrila|sfr|sexy|sex|sew|seven|ses|"+"services|sener|select|seek|security|secure|seat|scot|scor|scjohnson|science|schwarz|schule|"+"school|scholarships|schmidt|schaeffler|scb|sca|sbs|sbi|saxo|save|sas|sarl|sapo|sap|sanofi|"+"sandvikcoromant|sandvik|samsung|samsclub|salon|sale|sakura|safety|safe|saarland|ryukyu|rwe|run|"+"ruhr|rsvp|room|rogers|rodeo|rocks|rocher|rip|rio|rightathome|ricoh|richardli|rich|rexroth|"+"reviews|review|restaurant|rest|republican|report|repair|rentals|rent|ren|reit|reisen|reise|"+"rehab|redumbrella|redstone|red|recipes|realty|realtor|realestate|read|raid|radio|racing|qvc|"+"quest|quebec|qpon|pwc|pub|prudential|pru|protection|property|properties|promo|progressive|prof|"+"productions|prod|pro|prime|press|praxi|pramerica|post|porn|politie|poker|pohl|pnc|plus|plumbing|"+"playstation|play|place|pizza|pioneer|pink|ping|pin|pid|pictures|pictet|pics|piaget|physio|"+"photos|photography|photo|philips|pharmacy|pfizer|pet|pccw|pay|passagens|party|parts|partners|"+"pars|paris|panerai|panasonic|pamperedchef|page|ovh|ott|otsuka|osaka|origins|orientexpress|"+"organic|org|orange|oracle|open|ooo|onyourside|online|onl|ong|one|omega|ollo|oldnavy|olayangroup|"+"olayan|okinawa|office|off|observer|obi|nyc|ntt|nrw|nra|nowtv|nowruz|now|norton|"+"northwesternmutual|nokia|nissay|nissan|ninja|nikon|nike|nico|nhk|ngo|nfl|nexus|nextdirect|next|"+"news|new|neustar|network|netflix|netbank|net|nec|nba|navy|natura|nationwide|name|nagoya|nadex|"+"nab|mutuelle|mutual|museum|mtr|mtpc|mtn|msd|movistar|movie|mov|motorcycles|moscow|mortgage|"+"mormon|mopar|montblanc|monster|money|monash|mom|moi|moe|moda|mobily|mobi|mma|mls|mlb|mitsubishi|"+"mit|mint|mini|mil|microsoft|miami|metlife|meo|menu|men|memorial|meme|melbourne|meet|media|med|"+"mckinsey|mcdonalds|mcd|mba|mattel|maserati|marshalls|marriott|markets|marketing|market|mango|"+"management|man|makeup|maison|maif|madrid|macys|luxury|luxe|lupin|lundbeck|ltda|ltd|lplfinancial|"+"lpl|love|lotto|lotte|london|lol|loft|locus|locker|loans|loan|lixil|living|live|lipsy|link|linde|"+"lincoln|limo|limited|lilly|like|lighting|lifestyle|lifeinsurance|life|lidl|liaison|lgbt|lexus|"+"lego|legal|lefrak|leclerc|lease|lds|lawyer|law|latrobe|latino|lat|lasalle|lanxess|landrover|"+"land|lancome|lancia|lancaster|lamer|lamborghini|ladbrokes|lacaixa|kyoto|kuokgroup|kred|krd|kpn|"+"kpmg|kosher|komatsu|koeln|kiwi|kitchen|kindle|kinder|kim|kia|kfh|kerryproperties|kerrylogistics|"+"kerryhotels|kddi|kaufen|juniper|juegos|jprs|jpmorgan|joy|jot|joburg|jobs|jnj|jmp|jll|jlc|"+"jewelry|jetzt|jeep|jcp|jcb|java|jaguar|iwc|itv|itau|istanbul|ist|ismaili|iselect|irish|ipiranga|"+"investments|intuit|international|intel|int|insure|insurance|institute|ink|ing|info|infiniti|"+"industries|immobilien|immo|imdb|imamat|ikano|iinet|ifm|ieee|icu|ice|icbc|ibm|hyundai|hyatt|"+"hughes|htc|hsbc|how|house|hotmail|hoteles|hot|hosting|host|horse|honeywell|honda|homesense|"+"homes|homegoods|homedepot|holiday|holdings|hockey|hkt|hiv|hitachi|hisamitsu|hiphop|hgtv|hermes|"+"here|helsinki|help|healthcare|health|hdfcbank|hdfc|hbo|haus|hangout|hamburg|guru|guitars|guide|"+"guge|gucci|guardian|group|gripe|green|gratis|graphics|grainger|gov|got|gop|google|goog|goodyear|"+"goodhands|goo|golf|goldpoint|gold|godaddy|gmx|gmo|gmbh|gmail|globo|global|gle|glass|glade|"+"giving|gives|gifts|gift|ggee|george|genting|gent|gea|gdn|gbiz|garden|gap|games|game|gallup|"+"gallo|gallery|gal|fyi|futbol|furniture|fund|fujixerox|fujitsu|ftr|frontier|frontdoor|frogans|"+"frl|fresenius|fox|foundation|forum|forsale|forex|ford|football|foodnetwork|foo|fly|flsmidth|"+"flowers|florist|flir|flights|flickr|fitness|fit|fishing|fish|firmdale|firestone|fire|financial|"+"finance|final|film|fido|fidelity|fiat|ferrero|ferrari|feedback|fedex|fast|fashion|farmers|farm|"+"fans|fan|family|faith|fairwinds|fail|fage|extraspace|express|exposed|expert|exchange|everbank|"+"events|eus|eurovision|esurance|estate|esq|erni|ericsson|equipment|epson|epost|enterprises|"+"engineering|engineer|energy|emerck|email|education|edu|edeka|eco|eat|earth|dvr|dvag|durban|"+"dupont|duns|dunlop|duck|dubai|dtv|drive|download|dot|doosan|domains|doha|dog|dodge|doctor|docs|"+"dnp|diy|dish|discover|discount|directory|direct|digital|diet|diamonds|dhl|dev|design|desi|"+"dentist|dental|democrat|delta|deloitte|dell|delivery|degree|deals|dealer|deal|dds|dclk|day|"+"datsun|dating|date|dance|dad|dabur|cyou|cymru|cuisinella|csc|cruises|crs|crown|cricket|"+"creditunion|creditcard|credit|courses|coupons|coupon|country|corsica|coop|cool|cookingchannel|"+"cooking|contractors|contact|consulting|construction|condos|comsec|computer|compare|company|"+"community|commbank|comcast|com|cologne|college|coffee|codes|coach|clubmed|club|cloud|clothing|"+"clinique|clinic|click|cleaning|claims|cityeats|city|citic|citi|citadel|cisco|circle|cipriani|"+"church|chrysler|chrome|christmas|chloe|chintai|cheap|chat|chase|channel|chanel|cfd|cfa|cern|ceo|"+"center|ceb|cbs|cbre|cbn|cba|catering|cat|casino|cash|casa|cartier|cars|careers|career|care|"+"cards|caravan|car|capitalone|capital|capetown|canon|cancerresearch|camp|camera|cam|calvinklein|"+"call|cal|cafe|cab|bzh|buzz|buy|business|builders|build|bugatti|budapest|brussels|brother|broker|"+"broadway|bridgestone|bradesco|boutique|bot|bostik|bosch|boots|booking|book|boo|bond|bom|bofa|"+"boehringer|boats|bnpparibas|bnl|bmw|bms|blue|bloomberg|blog|blockbuster|blanco|blackfriday|"+"black|biz|bio|bingo|bing|bike|bid|bible|bharti|bet|bestbuy|best|berlin|bentley|beer|beauty|"+"beats|bcn|bcg|bbva|bbt|bbc|bayern|bauhaus|basketball|bargains|barefoot|barclays|barclaycard|"+"barcelona|bar|bank|band|bananarepublic|banamex|baidu|baby|azure|axa|aws|avianca|autos|auto|"+"author|auspost|audio|audible|audi|auction|attorney|athleta|associates|asia|asda|arte|art|arpa|"+"army|archi|aramco|aquarelle|apple|app|apartments|anz|anquan|android|analytics|amsterdam|amica|"+"amfam|amex|americanfamily|americanexpress|alstom|alsace|ally|allstate|allfinanz|alipay|alibaba|"+"alfaromeo|akdn|airtel|airforce|airbus|aigo|aig|agency|agakhan|afl|afamilycompany|aetna|aero|aeg|"+"adult|ads|adac|actor|active|aco|accountants|accountant|accenture|academy|abudhabi|abogado|able|"+"abc|abbvie|abbott|abb|abarth|aarp|aaa|onion"+")(?=[^0-9a-zA-Z@]|$))"));twttr.txt.regexen.validCCTLD=regexSupplant(RegExp("(?:(?:"+"한국|香港|澳門|新加坡|台灣|台湾|中國|中国|გე|ไทย|ලංකා|ഭാരതം|ಭಾರತ|భారత్|சிங்கப்பூர்|இலங்கை|இந்தியா|ଭାରତ|ભારત|ਭਾਰਤ|"+"ভাৰত|ভারত|বাংলা|भारोत|भारतम्|भारत|ڀارت|پاکستان|مليسيا|مصر|قطر|فلسطين|عمان|عراق|سورية|سودان|تونس|"+"بھارت|بارت|ایران|امارات|المغرب|السعودية|الجزائر|الاردن|հայ|қаз|укр|срб|рф|мон|мкд|ею|бел|бг|ελ|"+"zw|zm|za|yt|ye|ws|wf|vu|vn|vi|vg|ve|vc|va|uz|uy|us|um|uk|ug|ua|tz|tw|tv|tt|tr|tp|to|tn|tm|tl|tk|"+"tj|th|tg|tf|td|tc|sz|sy|sx|sv|su|st|ss|sr|so|sn|sm|sl|sk|sj|si|sh|sg|se|sd|sc|sb|sa|rw|ru|rs|ro|"+"re|qa|py|pw|pt|ps|pr|pn|pm|pl|pk|ph|pg|pf|pe|pa|om|nz|nu|nr|np|no|nl|ni|ng|nf|ne|nc|na|mz|my|mx|"+"mw|mv|mu|mt|ms|mr|mq|mp|mo|mn|mm|ml|mk|mh|mg|mf|me|md|mc|ma|ly|lv|lu|lt|ls|lr|lk|li|lc|lb|la|kz|"+"ky|kw|kr|kp|kn|km|ki|kh|kg|ke|jp|jo|jm|je|it|is|ir|iq|io|in|im|il|ie|id|hu|ht|hr|hn|hm|hk|gy|gw|"+"gu|gt|gs|gr|gq|gp|gn|gm|gl|gi|gh|gg|gf|ge|gd|gb|ga|fr|fo|fm|fk|fj|fi|eu|et|es|er|eh|eg|ee|ec|dz|"+"do|dm|dk|dj|de|cz|cy|cx|cw|cv|cu|cr|co|cn|cm|cl|ck|ci|ch|cg|cf|cd|cc|ca|bz|by|bw|bv|bt|bs|br|bq|"+"bo|bn|bm|bl|bj|bi|bh|bg|bf|be|bd|bb|ba|az|ax|aw|au|at|as|ar|aq|ao|an|am|al|ai|ag|af|ae|ad|ac"+")(?=[^0-9a-zA-Z@]|$))"));twttr.txt.regexen.validPunycode=/(?:xn--[0-9a-z]+)/;twttr.txt.regexen.validSpecialCCTLD=/(?:(?:co|tv)(?=[^0-9a-zA-Z@]|$))/;twttr.txt.regexen.validDomain=regexSupplant(/(?:#{validSubdomain}*#{validDomainName}(?:#{validGTLD}|#{validCCTLD}|#{validPunycode}))/);twttr.txt.regexen.validAsciiDomain=regexSupplant(/(?:(?:[\-a-z0-9#{latinAccentChars}]+)\.)+(?:#{validGTLD}|#{validCCTLD}|#{validPunycode})/gi);twttr.txt.regexen.invalidShortDomain=regexSupplant(/^#{validDomainName}#{validCCTLD}$/i);twttr.txt.regexen.validSpecialShortDomain=regexSupplant(/^#{validDomainName}#{validSpecialCCTLD}$/i);twttr.txt.regexen.validPortNumber=/[0-9]+/;twttr.txt.regexen.cyrillicLettersAndMarks=/\u0400-\u04FF/;twttr.txt.regexen.validGeneralUrlPathChars=regexSupplant(/[a-z#{cyrillicLettersAndMarks}0-9!\*';:=\+,\.\$\/%#\[\]\-_~@\|&#{latinAccentChars}]/i);twttr.txt.regexen.validUrlBalancedParens=regexSupplant("\\("+"(?:"+"#{validGeneralUrlPathChars}+"+"|"+"(?:"+"#{validGeneralUrlPathChars}*"+"\\("+"#{validGeneralUrlPathChars}+"+"\\)"+"#{validGeneralUrlPathChars}*"+")"+")"+"\\)","i");twttr.txt.regexen.validUrlPathEndingChars=regexSupplant(/[\+\-a-z#{cyrillicLettersAndMarks}0-9=_#\/#{latinAccentChars}]|(?:#{validUrlBalancedParens})/i);twttr.txt.regexen.validUrlPath=regexSupplant("(?:"+"(?:"+"#{validGeneralUrlPathChars}*"+"(?:#{validUrlBalancedParens}#{validGeneralUrlPathChars}*)*"+"#{validUrlPathEndingChars}"+")|(?:@#{validGeneralUrlPathChars}+/)"+")","i");twttr.txt.regexen.validUrlQueryChars=/[a-z0-9!?\*'@\(\);:&=\+\$\/%#\[\]\-_\.,~|]/i;twttr.txt.regexen.validUrlQueryEndingChars=/[a-z0-9_&=#\/]/i;twttr.txt.regexen.extractUrl=regexSupplant("("+"(#{validUrlPrecedingChars})"+"("+"(https?:\\/\\/)?"+"(#{validDomain})"+"(?::(#{validPortNumber}))?"+"(\\/#{validUrlPath}*)?"+"(\\?#{validUrlQueryChars}*#{validUrlQueryEndingChars})?"+")"+")","gi");twttr.txt.regexen.validTcoUrl=/^https?:\/\/t\.co\/[a-z0-9]+/i;twttr.txt.regexen.urlHasProtocol=/^https?:\/\//i;twttr.txt.regexen.urlHasHttps=/^https:\/\//i;twttr.txt.regexen.cashtag=/(?:[a-z]{1,2}[:._-][0-9]{1,6})|(?:[a-z]{1,6}(?:[:._-](?:[a-z]|[0-9]){1,6})?)|(?:[0-9]{1,6}[:._-][a-z]{1,2})/i;twttr.txt.regexen.validCashtag=regexSupplant("(^|#{spaces})(\\$)(#{cashtag})(?=$|\\s|[#{punct}])","gi");twttr.txt.regexen.validateUrlUnreserved=/[a-z\u0400-\u04FF0-9\-._~]/i;twttr.txt.regexen.validateUrlPctEncoded=/(?:%[0-9a-f]{2})/i;twttr.txt.regexen.validateUrlSubDelims=/[!$&'()*+,;=]/i;twttr.txt.regexen.validateUrlPchar=regexSupplant("(?:"+"#{validateUrlUnreserved}|"+"#{validateUrlPctEncoded}|"+"#{validateUrlSubDelims}|"+"[:|@]"+")","i");twttr.txt.regexen.validateUrlScheme=/(?:[a-z][a-z0-9+\-.]*)/i;twttr.txt.regexen.validateUrlUserinfo=regexSupplant("(?:"+"#{validateUrlUnreserved}|"+"#{validateUrlPctEncoded}|"+"#{validateUrlSubDelims}|"+":"+")*","i");twttr.txt.regexen.validateUrlDecOctet=/(?:[0-9]|(?:[1-9][0-9])|(?:1[0-9]{2})|(?:2[0-4][0-9])|(?:25[0-5]))/i;twttr.txt.regexen.validateUrlIpv4=regexSupplant(/(?:#{validateUrlDecOctet}(?:\.#{validateUrlDecOctet}){3})/i);twttr.txt.regexen.validateUrlIpv6=/(?:\[[a-f0-9:\.]+\])/i;twttr.txt.regexen.validateUrlIp=regexSupplant("(?:"+"#{validateUrlIpv4}|"+"#{validateUrlIpv6}"+")","i");twttr.txt.regexen.validateUrlSubDomainSegment=/(?:[a-z0-9](?:[a-z0-9_\-]*[a-z0-9])?)/i;twttr.txt.regexen.validateUrlDomainSegment=/(?:[a-z0-9](?:[a-z0-9\-]*[a-z0-9])?)/i;twttr.txt.regexen.validateUrlDomainTld=/(?:[a-z](?:[a-z0-9\-]*[a-z0-9])?)/i;twttr.txt.regexen.validateUrlDomain=regexSupplant(/(?:(?:#{validateUrlSubDomainSegment]}\.)*(?:#{validateUrlDomainSegment]}\.)#{validateUrlDomainTld})/i);twttr.txt.regexen.validateUrlHost=regexSupplant("(?:"+"#{validateUrlIp}|"+"#{validateUrlDomain}"+")","i");twttr.txt.regexen.validateUrlUnicodeSubDomainSegment=/(?:(?:[a-z0-9]|[^\u0000-\u007f])(?:(?:[a-z0-9_\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i;twttr.txt.regexen.validateUrlUnicodeDomainSegment=/(?:(?:[a-z0-9]|[^\u0000-\u007f])(?:(?:[a-z0-9\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i;twttr.txt.regexen.validateUrlUnicodeDomainTld=/(?:(?:[a-z]|[^\u0000-\u007f])(?:(?:[a-z0-9\-]|[^\u0000-\u007f])*(?:[a-z0-9]|[^\u0000-\u007f]))?)/i;twttr.txt.regexen.validateUrlUnicodeDomain=regexSupplant(/(?:(?:#{validateUrlUnicodeSubDomainSegment}\.)*(?:#{validateUrlUnicodeDomainSegment}\.)#{validateUrlUnicodeDomainTld})/i);twttr.txt.regexen.validateUrlUnicodeHost=regexSupplant("(?:"+"#{validateUrlIp}|"+"#{validateUrlUnicodeDomain}"+")","i");twttr.txt.regexen.validateUrlPort=/[0-9]{1,5}/;twttr.txt.regexen.validateUrlUnicodeAuthority=regexSupplant("(?:(#{validateUrlUserinfo})@)?"+"(#{validateUrlUnicodeHost})"+"(?::(#{validateUrlPort}))?","i");twttr.txt.regexen.validateUrlAuthority=regexSupplant("(?:(#{validateUrlUserinfo})@)?"+"(#{validateUrlHost})"+"(?::(#{validateUrlPort}))?","i");twttr.txt.regexen.validateUrlPath=regexSupplant(/(\/#{validateUrlPchar}*)*/i);twttr.txt.regexen.validateUrlQuery=regexSupplant(/(#{validateUrlPchar}|\/|\?)*/i);twttr.txt.regexen.validateUrlFragment=regexSupplant(/(#{validateUrlPchar}|\/|\?)*/i);twttr.txt.regexen.validateUrlUnencoded=regexSupplant("^"+"(?:"+"([^:/?#]+):\\/\\/"+")?"+"([^/?#]*)"+"([^?#]*)"+"(?:"+"\\?([^#]*)"+")?"+"(?:"+"#(.*)"+")?$","i");var DEFAULT_LIST_CLASS="tweet-url list-slug";var DEFAULT_USERNAME_CLASS="tweet-url username";var DEFAULT_HASHTAG_CLASS="tweet-url hashtag";var DEFAULT_CASHTAG_CLASS="tweet-url cashtag";var OPTIONS_NOT_ATTRIBUTES={urlClass:true,listClass:true,usernameClass:true,hashtagClass:true,cashtagClass:true,usernameUrlBase:true,listUrlBase:true,hashtagUrlBase:true,cashtagUrlBase:true,usernameUrlBlock:true,listUrlBlock:true,hashtagUrlBlock:true,linkUrlBlock:true,usernameIncludeSymbol:true,suppressLists:true,suppressNoFollow:true,targetBlank:true,suppressDataScreenName:true,urlEntities:true,symbolTag:true,textWithSymbolTag:true,urlTarget:true,invisibleTagAttrs:true,linkAttributeBlock:true,linkTextBlock:true,htmlEscapeNonEntities:true};var BOOLEAN_ATTRIBUTES={disabled:true,readonly:true,multiple:true,checked:true};function clone(o){var r={};for(var k in o){if(o.hasOwnProperty(k)){r[k]=o[k]}}return r}twttr.txt.tagAttrs=function(attributes){var htmlAttrs="";for(var k in attributes){var v=attributes[k];if(BOOLEAN_ATTRIBUTES[k]){v=v?k:null}if(v==null)continue;htmlAttrs+=" "+twttr.txt.htmlEscape(k)+'="'+twttr.txt.htmlEscape(v.toString())+'"'}return htmlAttrs};twttr.txt.linkToText=function(entity,text,attributes,options){if(!options.suppressNoFollow){attributes.rel="nofollow"}if(options.linkAttributeBlock){options.linkAttributeBlock(entity,attributes)}if(options.linkTextBlock){text=options.linkTextBlock(entity,text)}var d={text:text,attr:twttr.txt.tagAttrs(attributes)};return stringSupplant("#{text}",d)};twttr.txt.linkToTextWithSymbol=function(entity,symbol,text,attributes,options){var taggedSymbol=options.symbolTag?"<"+options.symbolTag+">"+symbol+"":symbol;text=twttr.txt.htmlEscape(text);var taggedText=options.textWithSymbolTag?"<"+options.textWithSymbolTag+">"+text+"":text;if(options.usernameIncludeSymbol||!symbol.match(twttr.txt.regexen.atSigns)){return twttr.txt.linkToText(entity,taggedSymbol+taggedText,attributes,options)}else{return taggedSymbol+twttr.txt.linkToText(entity,taggedText,attributes,options)}};twttr.txt.linkToHashtag=function(entity,text,options){var hash=text.substring(entity.indices[0],entity.indices[0]+1);var hashtag=twttr.txt.htmlEscape(entity.hashtag);var attrs=clone(options.htmlAttrs||{});attrs.href=options.hashtagUrlBase+hashtag;attrs.title="#"+hashtag;attrs["class"]=options.hashtagClass;if(hashtag.charAt(0).match(twttr.txt.regexen.rtl_chars)){attrs["class"]+=" rtl"}if(options.targetBlank){attrs.target="_blank"}return twttr.txt.linkToTextWithSymbol(entity,hash,hashtag,attrs,options)};twttr.txt.linkToCashtag=function(entity,text,options){var cashtag=twttr.txt.htmlEscape(entity.cashtag);var attrs=clone(options.htmlAttrs||{});attrs.href=options.cashtagUrlBase+cashtag;attrs.title="$"+cashtag;attrs["class"]=options.cashtagClass;if(options.targetBlank){attrs.target="_blank"}return twttr.txt.linkToTextWithSymbol(entity,"$",cashtag,attrs,options)};twttr.txt.linkToMentionAndList=function(entity,text,options){var at=text.substring(entity.indices[0],entity.indices[0]+1);var user=twttr.txt.htmlEscape(entity.screenName);var slashListname=twttr.txt.htmlEscape(entity.listSlug);var isList=entity.listSlug&&!options.suppressLists;var attrs=clone(options.htmlAttrs||{});attrs["class"]=isList?options.listClass:options.usernameClass;attrs.href=isList?options.listUrlBase+user+slashListname:options.usernameUrlBase+user;if(!isList&&!options.suppressDataScreenName){attrs["data-screen-name"]=user}if(options.targetBlank){attrs.target="_blank"}return twttr.txt.linkToTextWithSymbol(entity,at,isList?user+slashListname:user,attrs,options)};twttr.txt.linkToUrl=function(entity,text,options){var url=entity.url;var displayUrl=url;var linkText=twttr.txt.htmlEscape(displayUrl);var urlEntity=options.urlEntities&&options.urlEntities[url]||entity;if(urlEntity.display_url){linkText=twttr.txt.linkTextWithEntity(urlEntity,options)}var attrs=clone(options.htmlAttrs||{});if(!url.match(twttr.txt.regexen.urlHasProtocol)){url="http://"+url}attrs.href=url;if(options.targetBlank){attrs.target="_blank"}if(options.urlClass){attrs["class"]=options.urlClass}if(options.urlTarget){attrs.target=options.urlTarget}if(!options.title&&urlEntity.display_url){attrs.title=urlEntity.expanded_url}return twttr.txt.linkToText(entity,linkText,attrs,options)};twttr.txt.linkTextWithEntity=function(entity,options){var displayUrl=entity.display_url;var expandedUrl=entity.expanded_url;var displayUrlSansEllipses=displayUrl.replace(/…/g,"");if(expandedUrl.indexOf(displayUrlSansEllipses)!=-1){var displayUrlIndex=expandedUrl.indexOf(displayUrlSansEllipses);var v={displayUrlSansEllipses:displayUrlSansEllipses, +beforeDisplayUrl:expandedUrl.substr(0,displayUrlIndex),afterDisplayUrl:expandedUrl.substr(displayUrlIndex+displayUrlSansEllipses.length),precedingEllipsis:displayUrl.match(/^…/)?"…":"",followingEllipsis:displayUrl.match(/…$/)?"…":""};for(var k in v){if(v.hasOwnProperty(k)){v[k]=twttr.txt.htmlEscape(v[k])}}v["invisible"]=options.invisibleTagAttrs;return stringSupplant("#{precedingEllipsis} #{beforeDisplayUrl}#{displayUrlSansEllipses}#{afterDisplayUrl} #{followingEllipsis}",v)}return displayUrl};twttr.txt.autoLinkEntities=function(text,entities,options){options=clone(options||{});options.hashtagClass=options.hashtagClass||DEFAULT_HASHTAG_CLASS;options.hashtagUrlBase=options.hashtagUrlBase||"https://twitter.com/#!/search?q=%23";options.cashtagClass=options.cashtagClass||DEFAULT_CASHTAG_CLASS;options.cashtagUrlBase=options.cashtagUrlBase||"https://twitter.com/#!/search?q=%24";options.listClass=options.listClass||DEFAULT_LIST_CLASS;options.usernameClass=options.usernameClass||DEFAULT_USERNAME_CLASS;options.usernameUrlBase=options.usernameUrlBase||"https://twitter.com/";options.listUrlBase=options.listUrlBase||"https://twitter.com/";options.htmlAttrs=twttr.txt.extractHtmlAttrsFromOptions(options);options.invisibleTagAttrs=options.invisibleTagAttrs||"style='position:absolute;left:-9999px;'";var urlEntities,i,len;if(options.urlEntities){urlEntities={};for(i=0,len=options.urlEntities.length;ientities[i].indices[0]){entities.splice(i,1);i--}else{prev=entities[i]}}};twttr.txt.extractEntitiesWithIndices=function(text,options){var entities=twttr.txt.extractUrlsWithIndices(text,options).concat(twttr.txt.extractMentionsOrListsWithIndices(text)).concat(twttr.txt.extractHashtagsWithIndices(text,{checkUrlOverlap:false})).concat(twttr.txt.extractCashtagsWithIndices(text));if(entities.length==0){return[]}twttr.txt.removeOverlappingEntities(entities);return entities};twttr.txt.extractMentions=function(text){var screenNamesOnly=[],screenNamesWithIndices=twttr.txt.extractMentionsWithIndices(text);for(var i=0;i0){var entities=tags.concat(urls);twttr.txt.removeOverlappingEntities(entities);tags=[];for(var i=0;i");for(var j=0;j",""],chunks=twttr.txt.splitTags(text),i,j,result="",chunkIndex=0,chunk=chunks[0],prevChunksLen=0,chunkCursor=0,startInChunk=false,chunkChars=chunk,flatHits=[],index,hit,tag,placed,hitSpot;for(i=0;i=prevChunksLen+chunk.length){result+=chunkChars.slice(chunkCursor);if(startInChunk&&hit===prevChunksLen+chunkChars.length){result+=tag;placed=true}if(chunks[chunkIndex+1]){result+="<"+chunks[chunkIndex+1]+">"}prevChunksLen+=chunkChars.length;chunkCursor=0;chunkIndex+=2;chunk=chunks[chunkIndex];chunkChars=chunk;startInChunk=false}if(!placed&&chunk!=null){hitSpot=hit-prevChunksLen;result+=chunkChars.slice(chunkCursor,hitSpot)+tag;chunkCursor=hitSpot;if(index%2===0){startInChunk=true}else{startInChunk=false}}else if(!placed){placed=true;result+=tag}}if(chunk!=null){if(chunkCursor"}}return result};var MAX_LENGTH=140;twttr.txt.getTweetLength=function(text,options){if(!options){options={short_url_length:23,short_url_length_https:23}}var textLength=twttr.txt.getUnicodeTextLength(text),urlsWithIndices=twttr.txt.extractUrlsWithIndices(text);twttr.txt.modifyIndicesFromUTF16ToUnicode(text,urlsWithIndices);for(var i=0;iMAX_LENGTH){return"too_long"}if(twttr.txt.hasInvalidCharacters(text)){return"invalid_characters"}return false};twttr.txt.hasInvalidCharacters=function(text){return twttr.txt.regexen.invalid_chars.test(text)};twttr.txt.isValidTweetText=function(text){return!twttr.txt.isInvalidTweet(text)};twttr.txt.isValidUsername=function(username){if(!username){return false}var extracted=twttr.txt.extractMentions(username);return extracted.length===1&&extracted[0]===username.slice(1)};var VALID_LIST_RE=regexSupplant(/^#{validMentionOrList}$/);twttr.txt.isValidList=function(usernameList){var match=usernameList.match(VALID_LIST_RE);return!!(match&&match[1]==""&&match[4])};twttr.txt.isValidHashtag=function(hashtag){if(!hashtag){return false}var extracted=twttr.txt.extractHashtags(hashtag);return extracted.length===1&&extracted[0]===hashtag.slice(1)};twttr.txt.isValidUrl=function(url,unicodeDomains,requireProtocol){if(unicodeDomains==null){unicodeDomains=true}if(requireProtocol==null){requireProtocol=true}if(!url){return false}var urlParts=url.match(twttr.txt.regexen.validateUrlUnencoded);if(!urlParts||urlParts[0]!==url){return false}var scheme=urlParts[1],authority=urlParts[2],path=urlParts[3],query=urlParts[4],fragment=urlParts[5];if(!((!requireProtocol||isValidMatch(scheme,twttr.txt.regexen.validateUrlScheme)&&scheme.match(/^https?$/i))&&isValidMatch(path,twttr.txt.regexen.validateUrlPath)&&isValidMatch(query,twttr.txt.regexen.validateUrlQuery,true)&&isValidMatch(fragment,twttr.txt.regexen.validateUrlFragment,true))){return false}return unicodeDomains&&isValidMatch(authority,twttr.txt.regexen.validateUrlUnicodeAuthority)||!unicodeDomains&&isValidMatch(authority,twttr.txt.regexen.validateUrlAuthority)};function isValidMatch(string,regex,optional){if(!optional){return typeof string==="string"&&string.match(regex)&&RegExp["$&"]===string}return!string||string.match(regex)&&RegExp["$&"]===string}if(typeof module!="undefined"&&module.exports){module.exports=twttr.txt}if(typeof define=="function"&&define.amd){define([],twttr.txt)}if(typeof window!="undefined"){if(window.twttr){for(var prop in twttr){window.twttr[prop]=twttr[prop]}}else{window.twttr=twttr}}})(); \ No newline at end of file