|
|
|
@ -4,18 +4,17 @@ import Config from './config';
@@ -4,18 +4,17 @@ import Config from './config';
|
|
|
|
|
import emojiRegExp from '../emoji/regex'; |
|
|
|
|
import { encodeEmoji } from '../emoji'; |
|
|
|
|
import { MOUNT_CLASS_TO } from './mtproto/mtproto_config'; |
|
|
|
|
import { MessageEntity } from '../layer'; |
|
|
|
|
|
|
|
|
|
var EmojiHelper = { |
|
|
|
|
const EmojiHelper = { |
|
|
|
|
emojiMap: (code: string) => { return code; }, |
|
|
|
|
shortcuts: [] as any, |
|
|
|
|
emojis: [] as any |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var emojiData = Config.Emoji; |
|
|
|
|
var emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1/* && false *//* || true */, |
|
|
|
|
emojiCode; |
|
|
|
|
const emojiData = Config.Emoji; |
|
|
|
|
|
|
|
|
|
var alphaCharsRegExp = 'a-z' + |
|
|
|
|
const alphaCharsRegExp = 'a-z' + |
|
|
|
|
'\\u00c0-\\u00d6\\u00d8-\\u00f6\\u00f8-\\u00ff' + // Latin-1
|
|
|
|
|
'\\u0100-\\u024f' + // Latin Extended A and B
|
|
|
|
|
'\\u0253\\u0254\\u0256\\u0257\\u0259\\u025b\\u0263\\u0268\\u026f\\u0272\\u0289\\u028b' + // IPA Extensions
|
|
|
|
@ -40,10 +39,10 @@ var alphaCharsRegExp = 'a-z' +
@@ -40,10 +39,10 @@ var alphaCharsRegExp = 'a-z' +
|
|
|
|
|
'\\uff21-\\uff3a\\uff41-\\uff5a' + // full width Alphabet
|
|
|
|
|
'\\uff66-\\uff9f' + // half width Katakana
|
|
|
|
|
'\\uffa1-\\uffdc'; // half width Hangul (Korean)
|
|
|
|
|
var alphaNumericRegExp = '0-9\_' + alphaCharsRegExp; |
|
|
|
|
var domainAddChars = '\u00b7'; |
|
|
|
|
const alphaNumericRegExp = '0-9\_' + alphaCharsRegExp; |
|
|
|
|
const domainAddChars = '\u00b7'; |
|
|
|
|
// Based on Regular Expression for URL validation by Diego Perini
|
|
|
|
|
var urlRegExp = '((?:https?|ftp)://|mailto:)?' + |
|
|
|
|
const urlRegExp = '((?:https?|ftp)://|mailto:)?' + |
|
|
|
|
// user:pass authentication
|
|
|
|
|
'(?:\\S{1,64}(?::\\S{0,64})?@)?' + |
|
|
|
|
'(?:' + |
|
|
|
@ -61,32 +60,35 @@ var urlRegExp = '((?:https?|ftp)://|mailto:)?' +
@@ -61,32 +60,35 @@ var urlRegExp = '((?:https?|ftp)://|mailto:)?' +
|
|
|
|
|
'(?::\\d{2,5})?' + |
|
|
|
|
// resource path
|
|
|
|
|
'(?:/(?:\\S{0,255}[^\\s.;,(\\[\\]{}<>"\'])?)?'; |
|
|
|
|
var usernameRegExp = '[a-zA-Z\\d_]{5,32}'; |
|
|
|
|
var botCommandRegExp = '\\/([a-zA-Z\\d_]{1,32})(?:@(' + usernameRegExp + '))?(\\b|$)'; |
|
|
|
|
var fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i') |
|
|
|
|
var emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; |
|
|
|
|
var markdownTestRegExp = /[`_*@]/; |
|
|
|
|
var markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s)(`|\*\*|__)([^\n]+?)\7([\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)/m; |
|
|
|
|
var siteHashtags: any = { |
|
|
|
|
const usernameRegExp = '[a-zA-Z\\d_]{5,32}'; |
|
|
|
|
const botCommandRegExp = '\\/([a-zA-Z\\d_]{1,32})(?:@(' + usernameRegExp + '))?(\\b|$)'; |
|
|
|
|
const fullRegExp = new RegExp('(^| )(@)(' + usernameRegExp + ')|(' + urlRegExp + ')|(\\n)|(' + emojiRegExp + ')|(^|[\\s\\(\\]])(#[' + alphaNumericRegExp + ']{2,64})|(^|\\s)' + botCommandRegExp, 'i') |
|
|
|
|
const emailRegExp = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; |
|
|
|
|
const markdownTestRegExp = /[`_*@]/; |
|
|
|
|
const markdownRegExp = /(^|\s|\n)(````?)([\s\S]+?)(````?)([\s\n\.,:?!;]|$)|(^|\s)(`|\*\*|__)([^\n]+?)\7([\s\.,:?!;]|$)|@(\d+)\s*\((.+?)\)/m; |
|
|
|
|
const siteHashtags: {[siteName: string]: string} = { |
|
|
|
|
Telegram: 'tg://search_hashtag?hashtag={1}', |
|
|
|
|
Twitter: 'https://twitter.com/hashtag/{1}', |
|
|
|
|
Instagram: 'https://instagram.com/explore/tags/{1}/', |
|
|
|
|
'Google Plus': 'https://plus.google.com/explore/{1}' |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
var siteMentions: any = { |
|
|
|
|
const siteMentions: {[siteName: string]: string} = { |
|
|
|
|
Telegram: '#/im?p=%40{1}', |
|
|
|
|
Twitter: 'https://twitter.com/{1}', |
|
|
|
|
Instagram: 'https://instagram.com/{1}/', |
|
|
|
|
GitHub: 'https://github.com/{1}' |
|
|
|
|
}; |
|
|
|
|
var markdownEntities = { |
|
|
|
|
const markdownEntities = { |
|
|
|
|
'`': 'messageEntityCode', |
|
|
|
|
'**': 'messageEntityBold', |
|
|
|
|
'__': 'messageEntityItalic' |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
function getEmojiSpritesheetCoords(emojiCode: string) { |
|
|
|
|
namespace RichTextProcessor { |
|
|
|
|
export const emojiSupported = navigator.userAgent.search(/OS X|iPhone|iPad|iOS/i) != -1/* && false *//* || true */; |
|
|
|
|
|
|
|
|
|
export function getEmojiSpritesheetCoords(emojiCode: string) { |
|
|
|
|
let unified = encodeEmoji(emojiCode)/* .replace(/(-fe0f|fe0f)/g, '') */; |
|
|
|
|
|
|
|
|
|
if(unified == '1f441-200d-1f5e8') { |
|
|
|
@ -100,11 +102,13 @@ function getEmojiSpritesheetCoords(emojiCode: string) {
@@ -100,11 +102,13 @@ function getEmojiSpritesheetCoords(emojiCode: string) {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return unified.replace(/(-fe0f|fe0f)/g, ''); |
|
|
|
|
} |
|
|
|
|
function parseEntities(text: string, options = {}) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function parseEntities(text: string) { |
|
|
|
|
var match; |
|
|
|
|
var raw = text, url; |
|
|
|
|
var entities = [], matchIndex; |
|
|
|
|
const entities: MessageEntity[] = []; |
|
|
|
|
let matchIndex; |
|
|
|
|
var rawOffset = 0; |
|
|
|
|
// var start = tsNow()
|
|
|
|
|
while((match = raw.match(fullRegExp))) { |
|
|
|
@ -173,11 +177,11 @@ function parseEntities(text: string, options = {}) {
@@ -173,11 +177,11 @@ function parseEntities(text: string, options = {}) {
|
|
|
|
|
unicode: emojiCoords |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} else if(match[10]) { // Hashtag
|
|
|
|
|
} else if(match[11]) { // Hashtag
|
|
|
|
|
entities.push({ |
|
|
|
|
_: 'messageEntityHashtag', |
|
|
|
|
offset: matchIndex + (match[9] ? match[9].length : 0), |
|
|
|
|
length: match[10].length |
|
|
|
|
offset: matchIndex + (match[10] ? match[10].length : 0), |
|
|
|
|
length: match[11].length |
|
|
|
|
}); |
|
|
|
|
} else if(match[12]) { // Bot command
|
|
|
|
|
entities.push({ |
|
|
|
@ -194,9 +198,10 @@ function parseEntities(text: string, options = {}) {
@@ -194,9 +198,10 @@ function parseEntities(text: string, options = {}) {
|
|
|
|
|
// if (entities.length) {
|
|
|
|
|
// console.log('parse entities', text, entities.slice())
|
|
|
|
|
// }
|
|
|
|
|
return entities |
|
|
|
|
} |
|
|
|
|
function parseEmojis(text: string) { |
|
|
|
|
return entities; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* export function parseEmojis(text: string) { |
|
|
|
|
return text.replace(/:([a-z0-9\-\+\*_]+?):/gi, function (all, shortcut) { |
|
|
|
|
var emojiCode = EmojiHelper.shortcuts[shortcut] |
|
|
|
|
if (emojiCode !== undefined) { |
|
|
|
@ -204,9 +209,10 @@ function parseEmojis(text: string) {
@@ -204,9 +209,10 @@ function parseEmojis(text: string) {
|
|
|
|
|
} |
|
|
|
|
return all |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
function parseMarkdown(text: string, entities: any[], noTrim?: any) { |
|
|
|
|
if(!markdownTestRegExp.test(text)) { |
|
|
|
|
} */ |
|
|
|
|
|
|
|
|
|
export function parseMarkdown(text: string, entities: MessageEntity[], noTrim?: any) { |
|
|
|
|
if(!markdownTestRegExp.test(text)) { |
|
|
|
|
return noTrim ? text : text.trim(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -251,7 +257,7 @@ function parseMarkdown(text: string, entities: any[], noTrim?: any) {
@@ -251,7 +257,7 @@ function parseMarkdown(text: string, entities: any[], noTrim?: any) {
|
|
|
|
|
newText.push(text) |
|
|
|
|
entities.push({ |
|
|
|
|
_: 'messageEntityMentionName', |
|
|
|
|
user_id: match[10], |
|
|
|
|
user_id: +match[10], |
|
|
|
|
offset: matchIndex, |
|
|
|
|
length: text.length |
|
|
|
|
}) |
|
|
|
@ -270,34 +276,27 @@ function parseMarkdown(text: string, entities: any[], noTrim?: any) {
@@ -270,34 +276,27 @@ function parseMarkdown(text: string, entities: any[], noTrim?: any) {
|
|
|
|
|
newText = newText.trim() |
|
|
|
|
} |
|
|
|
|
return newText |
|
|
|
|
} |
|
|
|
|
function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any) { |
|
|
|
|
var totalEntities = newEntities.slice(); |
|
|
|
|
var i; |
|
|
|
|
var len = currentEntities.length; |
|
|
|
|
var j; |
|
|
|
|
var len2 = newEntities.length; |
|
|
|
|
var startJ = 0; |
|
|
|
|
var curEntity; |
|
|
|
|
var newEntity; |
|
|
|
|
var start, end; |
|
|
|
|
var cStart, cEnd; |
|
|
|
|
var bad; |
|
|
|
|
for(i = 0; i < len; i++) { |
|
|
|
|
curEntity = currentEntities[i]; |
|
|
|
|
if (fromApi && |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function mergeEntities(currentEntities: MessageEntity[], newEntities: MessageEntity[], fromApi?: boolean) { |
|
|
|
|
const totalEntities = newEntities.slice(); |
|
|
|
|
const newLength = newEntities.length; |
|
|
|
|
let startJ = 0; |
|
|
|
|
for(let i = 0, length = currentEntities.length; i < length; i++) { |
|
|
|
|
const curEntity = currentEntities[i]; |
|
|
|
|
/* if(fromApi && |
|
|
|
|
curEntity._ != 'messageEntityLinebreak' && |
|
|
|
|
curEntity._ != 'messageEntityEmoji') { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
} */ |
|
|
|
|
// console.log('s', curEntity, newEntities);
|
|
|
|
|
start = curEntity.offset; |
|
|
|
|
end = start + curEntity.length; |
|
|
|
|
bad = false; |
|
|
|
|
for(j = startJ; j < len2; j++) { |
|
|
|
|
newEntity = newEntities[j]; |
|
|
|
|
cStart = newEntity.offset; |
|
|
|
|
cEnd = cStart + newEntity.length; |
|
|
|
|
const start = curEntity.offset; |
|
|
|
|
const end = start + curEntity.length; |
|
|
|
|
let bad = false; |
|
|
|
|
for(let j = startJ; j < newLength; j++) { |
|
|
|
|
const newEntity = newEntities[j]; |
|
|
|
|
const cStart = newEntity.offset; |
|
|
|
|
const cEnd = cStart + newEntity.length; |
|
|
|
|
if(cStart <= start) { |
|
|
|
|
startJ = j; |
|
|
|
|
} |
|
|
|
@ -305,8 +304,7 @@ function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any)
@@ -305,8 +304,7 @@ function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any)
|
|
|
|
|
if(start >= cStart && start < cEnd || |
|
|
|
|
end > cStart && end <= cEnd) { |
|
|
|
|
// console.log('bad', curEntity, newEntity)
|
|
|
|
|
if(fromApi && |
|
|
|
|
start >= cStart && end <= cEnd) { |
|
|
|
|
if(fromApi && start >= cStart && end <= cEnd) { |
|
|
|
|
if(newEntity.nested === undefined) { |
|
|
|
|
newEntity.nested = []; |
|
|
|
|
} |
|
|
|
@ -336,17 +334,19 @@ function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any)
@@ -336,17 +334,19 @@ function mergeEntities(currentEntities: any[], newEntities: any[], fromApi: any)
|
|
|
|
|
}); |
|
|
|
|
// console.log('merge', currentEntities, newEntities, totalEntities)
|
|
|
|
|
return totalEntities; |
|
|
|
|
} |
|
|
|
|
function wrapRichNestedText(text: string, nested: any, options: any) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function wrapRichNestedText(text: string, nested: MessageEntity[], options: any) { |
|
|
|
|
if(nested === undefined) { |
|
|
|
|
return encodeEntities(text); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
options.hasNested = true; |
|
|
|
|
return wrapRichText(text, {entities: nested, nested: true}); |
|
|
|
|
} |
|
|
|
|
function wrapRichText(text: string, options: Partial<{ |
|
|
|
|
entities: any, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function wrapRichText(text: string, options: Partial<{ |
|
|
|
|
entities: MessageEntity[], |
|
|
|
|
contextSite: string, |
|
|
|
|
highlightUsername: string, |
|
|
|
|
noLinks: boolean, |
|
|
|
@ -356,29 +356,20 @@ function wrapRichText(text: string, options: Partial<{
@@ -356,29 +356,20 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
noTextFormat: boolean, |
|
|
|
|
nested?: boolean, |
|
|
|
|
contextHashtag?: string |
|
|
|
|
}> = {}) { |
|
|
|
|
}> = {}) { |
|
|
|
|
if(!text || !text.length) { |
|
|
|
|
return '' |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var entities = options.entities; |
|
|
|
|
var contextSite = options.contextSite || 'Telegram'; |
|
|
|
|
var contextExternal = contextSite != 'Telegram'; |
|
|
|
|
var emojiFound = false; |
|
|
|
|
if(entities === undefined) { |
|
|
|
|
entities = parseEntities(text, options); |
|
|
|
|
} |
|
|
|
|
const entities = options.entities || parseEntities(text); |
|
|
|
|
const contextSite = options.contextSite || 'Telegram'; |
|
|
|
|
const contextExternal = contextSite != 'Telegram'; |
|
|
|
|
|
|
|
|
|
//console.log('wrapRichText got entities:', text, entities);
|
|
|
|
|
var len = entities.length; |
|
|
|
|
var entity; |
|
|
|
|
var entityText; |
|
|
|
|
var skipEntity; |
|
|
|
|
var url; |
|
|
|
|
var html = []; |
|
|
|
|
var lastOffset = 0; |
|
|
|
|
for(var i = 0; i < len; i++) { |
|
|
|
|
entity = entities[i]; |
|
|
|
|
const html: string[] = []; |
|
|
|
|
let lastOffset = 0; |
|
|
|
|
for(let i = 0, len = entities.length; i < len; i++) { |
|
|
|
|
const entity = entities[i]; |
|
|
|
|
if(entity.offset > lastOffset) { |
|
|
|
|
html.push( |
|
|
|
|
encodeEntities(text.substr(lastOffset, entity.offset - lastOffset)) |
|
|
|
@ -387,8 +378,8 @@ function wrapRichText(text: string, options: Partial<{
@@ -387,8 +378,8 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
skipEntity = false; |
|
|
|
|
entityText = text.substr(entity.offset, entity.length); |
|
|
|
|
let skipEntity = false; |
|
|
|
|
const entityText = text.substr(entity.offset, entity.length); |
|
|
|
|
switch(entity._) { |
|
|
|
|
case 'messageEntityMention': |
|
|
|
|
var contextUrl = !options.noLinks && siteMentions[contextSite] |
|
|
|
@ -412,27 +403,31 @@ function wrapRichText(text: string, options: Partial<{
@@ -412,27 +403,31 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityMentionName': |
|
|
|
|
if (options.noLinks) { |
|
|
|
|
skipEntity = true |
|
|
|
|
break |
|
|
|
|
if(options.noLinks) { |
|
|
|
|
skipEntity = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
html.push( |
|
|
|
|
'<a href="#/im?p=u', |
|
|
|
|
encodeURIComponent(entity.user_id), |
|
|
|
|
'">', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityHashtag': |
|
|
|
|
var contextUrl = !options.noLinks && siteHashtags[contextSite] |
|
|
|
|
if (!contextUrl) { |
|
|
|
|
skipEntity = true |
|
|
|
|
break |
|
|
|
|
var contextUrl = !options.noLinks && siteHashtags[contextSite]; |
|
|
|
|
if(!contextUrl) { |
|
|
|
|
skipEntity = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
var hashtag = entityText.substr(1) |
|
|
|
|
|
|
|
|
|
var hashtag = entityText.substr(1); |
|
|
|
|
html.push( |
|
|
|
|
'<a ', |
|
|
|
|
contextExternal ? ' target="_blank" rel="noopener noreferrer" ' : '', |
|
|
|
@ -442,33 +437,38 @@ function wrapRichText(text: string, options: Partial<{
@@ -442,33 +437,38 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'">', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityEmail': |
|
|
|
|
if (options.noLinks) { |
|
|
|
|
skipEntity = true |
|
|
|
|
break |
|
|
|
|
if(options.noLinks) { |
|
|
|
|
skipEntity = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
html.push( |
|
|
|
|
'<a href="', |
|
|
|
|
encodeEntities('mailto:' + entityText), |
|
|
|
|
'" target="_blank" rel="noopener noreferrer">', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityUrl': |
|
|
|
|
case 'messageEntityTextUrl': |
|
|
|
|
var inner |
|
|
|
|
let inner: string; |
|
|
|
|
let url: string; |
|
|
|
|
if (entity._ == 'messageEntityTextUrl') { |
|
|
|
|
url = entity.url |
|
|
|
|
url = wrapUrl(url, true) |
|
|
|
|
inner = wrapRichNestedText(entityText, entity.nested, options) |
|
|
|
|
url = (entity as MessageEntity.messageEntityTextUrl).url; |
|
|
|
|
url = wrapUrl(url, true); |
|
|
|
|
inner = wrapRichNestedText(entityText, entity.nested, options); |
|
|
|
|
} else { |
|
|
|
|
url = wrapUrl(entityText, false) |
|
|
|
|
inner = encodeEntities(replaceUrlEncodings(entityText)) |
|
|
|
|
url = wrapUrl(entityText, false); |
|
|
|
|
inner = encodeEntities(replaceUrlEncodings(entityText)); |
|
|
|
|
} |
|
|
|
|
if (options.noLinks) { |
|
|
|
|
|
|
|
|
|
if(options.noLinks) { |
|
|
|
|
html.push(inner); |
|
|
|
|
} else { |
|
|
|
|
html.push( |
|
|
|
@ -477,41 +477,45 @@ function wrapRichText(text: string, options: Partial<{
@@ -477,41 +477,45 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'" target="_blank" rel="noopener noreferrer">', |
|
|
|
|
inner, |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityLinebreak': |
|
|
|
|
html.push(options.noLinebreaks ? ' ' : '<br/>') |
|
|
|
|
break |
|
|
|
|
html.push(options.noLinebreaks ? ' ' : '<br/>'); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityEmoji': |
|
|
|
|
html.push(emojiSupported ? |
|
|
|
|
`<span class="emoji" contenteditable="false">${encodeEntities(entityText)}</span>` : |
|
|
|
|
`<img src="assets/img/emoji/${entity.unicode}.png" alt="${encodeEntities(entityText)}" class="emoji">`); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
emojiFound = true; |
|
|
|
|
break |
|
|
|
|
case 'messageEntityBotCommand': |
|
|
|
|
if (options.noLinks || options.noCommands || contextExternal) { |
|
|
|
|
skipEntity = true |
|
|
|
|
break |
|
|
|
|
if(options.noLinks || options.noCommands || contextExternal) { |
|
|
|
|
skipEntity = true; |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
var command = entityText.substr(1) |
|
|
|
|
var bot |
|
|
|
|
var atPos |
|
|
|
|
|
|
|
|
|
var command = entityText.substr(1); |
|
|
|
|
var bot; |
|
|
|
|
var atPos; |
|
|
|
|
if ((atPos = command.indexOf('@')) != -1) { |
|
|
|
|
bot = command.substr(atPos + 1) |
|
|
|
|
command = command.substr(0, atPos) |
|
|
|
|
bot = command.substr(atPos + 1); |
|
|
|
|
command = command.substr(0, atPos); |
|
|
|
|
} else { |
|
|
|
|
bot = options.fromBot |
|
|
|
|
bot = options.fromBot; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
html.push( |
|
|
|
|
'<a href="', |
|
|
|
|
encodeEntities('tg://bot_command?command=' + encodeURIComponent(command) + (bot ? '&bot=' + encodeURIComponent(bot) : '')), |
|
|
|
|
'">', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</a>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityBold': |
|
|
|
|
if(options.noTextFormat) { |
|
|
|
|
html.push(wrapRichNestedText(entityText, entity.nested, options)); |
|
|
|
@ -522,8 +526,9 @@ function wrapRichText(text: string, options: Partial<{
@@ -522,8 +526,9 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'<strong>', |
|
|
|
|
wrapRichNestedText(entityText, entity.nested, options), |
|
|
|
|
'</strong>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityItalic': |
|
|
|
|
if(options.noTextFormat) { |
|
|
|
|
html.push(wrapRichNestedText(entityText, entity.nested, options)); |
|
|
|
@ -534,15 +539,25 @@ function wrapRichText(text: string, options: Partial<{
@@ -534,15 +539,25 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'<em>', |
|
|
|
|
wrapRichNestedText(entityText, entity.nested, options), |
|
|
|
|
'</em>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityHighlight': |
|
|
|
|
html.push( |
|
|
|
|
'<i>', |
|
|
|
|
wrapRichNestedText(entityText, entity.nested, options), |
|
|
|
|
'</i>' |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityStrike': |
|
|
|
|
html.push( |
|
|
|
|
'<del>', |
|
|
|
|
wrapRichNestedText(entityText, entity.nested, options), |
|
|
|
|
'</del>' |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityCode': |
|
|
|
|
if(options.noTextFormat) { |
|
|
|
|
html.push(encodeEntities(entityText)); |
|
|
|
@ -553,8 +568,9 @@ function wrapRichText(text: string, options: Partial<{
@@ -553,8 +568,9 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'<code>', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</code>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
case 'messageEntityPre': |
|
|
|
|
if(options.noTextFormat) { |
|
|
|
|
html.push(encodeEntities(entityText)); |
|
|
|
@ -565,33 +581,31 @@ function wrapRichText(text: string, options: Partial<{
@@ -565,33 +581,31 @@ function wrapRichText(text: string, options: Partial<{
|
|
|
|
|
'<pre><code', (entity.language ? ' class="language-' + encodeEntities(entity.language) + '"' : ''), '>', |
|
|
|
|
encodeEntities(entityText), |
|
|
|
|
'</code></pre>' |
|
|
|
|
) |
|
|
|
|
break |
|
|
|
|
); |
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
default: |
|
|
|
|
skipEntity = true |
|
|
|
|
skipEntity = true; |
|
|
|
|
} |
|
|
|
|
lastOffset = entity.offset + (skipEntity ? 0 : entity.length) |
|
|
|
|
|
|
|
|
|
lastOffset = entity.offset + (skipEntity ? 0 : entity.length); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
html.push(encodeEntities(text.substr(lastOffset))); // may be empty string
|
|
|
|
|
//console.log(html);
|
|
|
|
|
text = html.join('')//$sanitize(html.join(''))
|
|
|
|
|
/* if (!options.nested && (emojiFound || options.hasNested)) { |
|
|
|
|
text = text.replace(/\ufe0f|️|�|‍/g, '', text) |
|
|
|
|
var emojiSizeClass = curEmojiSize == 18 ? '' : (' emoji-w' + curEmojiSize) |
|
|
|
|
text = text.replace(/<span((?: [^>]*)?) class="emoji emoji-(\d)-(\d+)-(\d+)"(.+?)<\/span>/g, |
|
|
|
|
'<span$1 class="emoji ' + emojiSizeClass + ' emoji-spritesheet-$2" style="background-position: -$3px -$4px;" $5</span>') |
|
|
|
|
} */ |
|
|
|
|
return text;//$sce.trustAs('html', text)
|
|
|
|
|
} |
|
|
|
|
function wrapDraftText (text: string, options: any = {}) { |
|
|
|
|
text = html.join(''); |
|
|
|
|
|
|
|
|
|
return text; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function wrapDraftText(text: string, options: any = {}) { |
|
|
|
|
if(!text || !text.length) { |
|
|
|
|
return ''; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
var entities = options.entities; |
|
|
|
|
if(entities === undefined) { |
|
|
|
|
entities = parseEntities(text, options); |
|
|
|
|
entities = parseEntities(text); |
|
|
|
|
} |
|
|
|
|
var i = 0; |
|
|
|
|
var len = entities.length; |
|
|
|
@ -658,8 +672,9 @@ function wrapDraftText (text: string, options: any = {}) {
@@ -658,8 +672,9 @@ function wrapDraftText (text: string, options: any = {}) {
|
|
|
|
|
} |
|
|
|
|
code.push(text.substr(lastOffset)); |
|
|
|
|
return code.join(''); |
|
|
|
|
} |
|
|
|
|
function checkBrackets(url: string) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function checkBrackets(url: string) { |
|
|
|
|
var urlLength = url.length; |
|
|
|
|
var urlOpenBrackets = url.split('(').length - 1; |
|
|
|
|
var urlCloseBrackets = url.split(')').length - 1; |
|
|
|
@ -673,9 +688,9 @@ function checkBrackets(url: string) {
@@ -673,9 +688,9 @@ function checkBrackets(url: string) {
|
|
|
|
|
url = url.replace(/\)+$/, ''); |
|
|
|
|
} |
|
|
|
|
return url; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function replaceUrlEncodings(urlWithEncoded: string) { |
|
|
|
|
export function replaceUrlEncodings(urlWithEncoded: string) { |
|
|
|
|
return urlWithEncoded.replace(/(%[A-Z\d]{2})+/g, (str) => { |
|
|
|
|
try { |
|
|
|
|
return decodeURIComponent(str); |
|
|
|
@ -683,9 +698,9 @@ function replaceUrlEncodings(urlWithEncoded: string) {
@@ -683,9 +698,9 @@ function replaceUrlEncodings(urlWithEncoded: string) {
|
|
|
|
|
return str; |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function wrapPlainText(text: any, options: any = {}) { |
|
|
|
|
export function wrapPlainText(text: any) { |
|
|
|
|
if(emojiSupported) { |
|
|
|
|
return text; |
|
|
|
|
} |
|
|
|
@ -703,7 +718,8 @@ function wrapPlainText(text: any, options: any = {}) {
@@ -703,7 +718,8 @@ function wrapPlainText(text: any, options: any = {}) {
|
|
|
|
|
text.push(raw.substr(0, match.index)) |
|
|
|
|
if(match[8]) { |
|
|
|
|
// @ts-ignore
|
|
|
|
|
if((emojiCode = EmojiHelper.emojiMap[match[8]]) && |
|
|
|
|
const emojiCode = EmojiHelper.emojiMap[match[8]]; |
|
|
|
|
if(emojiCode && |
|
|
|
|
// @ts-ignore
|
|
|
|
|
(emojiTitle = emojiData[emojiCode][1][0])) { |
|
|
|
|
text.push(':' + emojiTitle + ':'); |
|
|
|
@ -718,14 +734,16 @@ function wrapPlainText(text: any, options: any = {}) {
@@ -718,14 +734,16 @@ function wrapPlainText(text: any, options: any = {}) {
|
|
|
|
|
} |
|
|
|
|
text.push(raw); |
|
|
|
|
return text.join(''); |
|
|
|
|
} |
|
|
|
|
function wrapEmojiText(text: string) { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function wrapEmojiText(text: string) { |
|
|
|
|
if(!text) return ''; |
|
|
|
|
|
|
|
|
|
let entities = parseEntities(text).filter(e => e._ == 'messageEntityEmoji'); |
|
|
|
|
return wrapRichText(text, {entities}); |
|
|
|
|
} |
|
|
|
|
function wrapUrl(url: string, unsafe: number | boolean): string { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
export function wrapUrl(url: string, unsafe: number | boolean): string { |
|
|
|
|
if(!url.match(/^https?:\/\//i)) { |
|
|
|
|
url = 'http://' + url; |
|
|
|
|
} |
|
|
|
@ -775,28 +793,15 @@ function wrapUrl(url: string, unsafe: number | boolean): string {
@@ -775,28 +793,15 @@ function wrapUrl(url: string, unsafe: number | boolean): string {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return url; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
function matchUrl(text: string) { |
|
|
|
|
export function matchUrl(text: string) { |
|
|
|
|
return text.match(urlRegExp); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let RichTextProcessor = { |
|
|
|
|
wrapRichText, |
|
|
|
|
wrapPlainText, |
|
|
|
|
wrapDraftText, |
|
|
|
|
wrapUrl, |
|
|
|
|
wrapEmojiText, |
|
|
|
|
parseEntities, |
|
|
|
|
parseMarkdown, |
|
|
|
|
parseEmojis, |
|
|
|
|
mergeEntities, |
|
|
|
|
getEmojiSpritesheetCoords, |
|
|
|
|
emojiSupported, |
|
|
|
|
matchUrl |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
MOUNT_CLASS_TO && (MOUNT_CLASS_TO.RichTextProcessor = RichTextProcessor); |
|
|
|
|
|
|
|
|
|
export {RichTextProcessor}; |
|
|
|
|
export default RichTextProcessor; |
|
|
|
|
|
|
|
|
|