Fix some entities

Small refactor of RichTextProcessor
This commit is contained in:
morethanwords 2020-10-16 23:03:30 +03:00
parent 7f746738f5
commit ee4d05c9e5
7 changed files with 883 additions and 702 deletions

84
src/layer.d.ts vendored
View File

@ -3795,125 +3795,166 @@ export namespace ReplyMarkup {
/** /**
* @link https://core.telegram.org/type/MessageEntity * @link https://core.telegram.org/type/MessageEntity
*/ */
export type MessageEntity = MessageEntity.messageEntityUnknown | MessageEntity.messageEntityMention | MessageEntity.messageEntityHashtag | MessageEntity.messageEntityBotCommand | MessageEntity.messageEntityUrl | MessageEntity.messageEntityEmail | MessageEntity.messageEntityBold | MessageEntity.messageEntityItalic | MessageEntity.messageEntityCode | MessageEntity.messageEntityPre | MessageEntity.messageEntityTextUrl | MessageEntity.messageEntityMentionName | MessageEntity.inputMessageEntityMentionName | MessageEntity.messageEntityPhone | MessageEntity.messageEntityCashtag | MessageEntity.messageEntityUnderline | MessageEntity.messageEntityStrike | MessageEntity.messageEntityBlockquote | MessageEntity.messageEntityBankCard; export type MessageEntity = MessageEntity.messageEntityUnknown | MessageEntity.messageEntityMention | MessageEntity.messageEntityHashtag | MessageEntity.messageEntityBotCommand | MessageEntity.messageEntityUrl | MessageEntity.messageEntityEmail | MessageEntity.messageEntityBold | MessageEntity.messageEntityItalic | MessageEntity.messageEntityCode | MessageEntity.messageEntityPre | MessageEntity.messageEntityTextUrl | MessageEntity.messageEntityMentionName | MessageEntity.inputMessageEntityMentionName | MessageEntity.messageEntityPhone | MessageEntity.messageEntityCashtag | MessageEntity.messageEntityUnderline | MessageEntity.messageEntityStrike | MessageEntity.messageEntityBlockquote | MessageEntity.messageEntityBankCard | MessageEntity.messageEntityEmoji | MessageEntity.messageEntityHighlight | MessageEntity.messageEntityLinebreak;
export namespace MessageEntity { export namespace MessageEntity {
export type messageEntityUnknown = { export type messageEntityUnknown = {
_: 'messageEntityUnknown', _: 'messageEntityUnknown',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityMention = { export type messageEntityMention = {
_: 'messageEntityMention', _: 'messageEntityMention',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityHashtag = { export type messageEntityHashtag = {
_: 'messageEntityHashtag', _: 'messageEntityHashtag',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityBotCommand = { export type messageEntityBotCommand = {
_: 'messageEntityBotCommand', _: 'messageEntityBotCommand',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityUrl = { export type messageEntityUrl = {
_: 'messageEntityUrl', _: 'messageEntityUrl',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityEmail = { export type messageEntityEmail = {
_: 'messageEntityEmail', _: 'messageEntityEmail',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityBold = { export type messageEntityBold = {
_: 'messageEntityBold', _: 'messageEntityBold',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityItalic = { export type messageEntityItalic = {
_: 'messageEntityItalic', _: 'messageEntityItalic',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityCode = { export type messageEntityCode = {
_: 'messageEntityCode', _: 'messageEntityCode',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityPre = { export type messageEntityPre = {
_: 'messageEntityPre', _: 'messageEntityPre',
offset: number, offset: number,
length: number, length: number,
language: string language: string,
nested?: Array<MessageEntity>
}; };
export type messageEntityTextUrl = { export type messageEntityTextUrl = {
_: 'messageEntityTextUrl', _: 'messageEntityTextUrl',
offset: number, offset: number,
length: number, length: number,
url: string url: string,
nested?: Array<MessageEntity>
}; };
export type messageEntityMentionName = { export type messageEntityMentionName = {
_: 'messageEntityMentionName', _: 'messageEntityMentionName',
offset: number, offset: number,
length: number, length: number,
user_id: number user_id: number,
nested?: Array<MessageEntity>
}; };
export type inputMessageEntityMentionName = { export type inputMessageEntityMentionName = {
_: 'inputMessageEntityMentionName', _: 'inputMessageEntityMentionName',
offset: number, offset: number,
length: number, length: number,
user_id: InputUser user_id: InputUser,
nested?: Array<MessageEntity>
}; };
export type messageEntityPhone = { export type messageEntityPhone = {
_: 'messageEntityPhone', _: 'messageEntityPhone',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityCashtag = { export type messageEntityCashtag = {
_: 'messageEntityCashtag', _: 'messageEntityCashtag',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityUnderline = { export type messageEntityUnderline = {
_: 'messageEntityUnderline', _: 'messageEntityUnderline',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityStrike = { export type messageEntityStrike = {
_: 'messageEntityStrike', _: 'messageEntityStrike',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityBlockquote = { export type messageEntityBlockquote = {
_: 'messageEntityBlockquote', _: 'messageEntityBlockquote',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
}; };
export type messageEntityBankCard = { export type messageEntityBankCard = {
_: 'messageEntityBankCard', _: 'messageEntityBankCard',
offset: number, offset: number,
length: number length: number,
nested?: Array<MessageEntity>
};
export type messageEntityEmoji = {
_: 'messageEntityEmoji',
offset?: number,
length?: number,
unicode?: string,
nested?: Array<MessageEntity>
};
export type messageEntityHighlight = {
_: 'messageEntityHighlight',
offset?: number,
length?: number,
nested?: Array<MessageEntity>
};
export type messageEntityLinebreak = {
_: 'messageEntityLinebreak',
offset?: number,
length?: number,
nested?: Array<MessageEntity>
}; };
} }
@ -8918,6 +8959,9 @@ export interface ConstructorDeclMap {
'messageReplyHeader': MessageReplyHeader.messageReplyHeader, 'messageReplyHeader': MessageReplyHeader.messageReplyHeader,
'messageReplies': MessageReplies.messageReplies, 'messageReplies': MessageReplies.messageReplies,
'peerBlocked': PeerBlocked.peerBlocked, 'peerBlocked': PeerBlocked.peerBlocked,
'messageEntityEmoji': MessageEntity.messageEntityEmoji,
'messageEntityHighlight': MessageEntity.messageEntityHighlight,
'messageEntityLinebreak': MessageEntity.messageEntityLinebreak,
} }
export type InvokeAfterMsg = { export type InvokeAfterMsg = {

View File

@ -674,7 +674,7 @@ export class AppDialogsManager {
let lastMessageText = appMessagesManager.getRichReplyText(lastMessage, ''); let lastMessageText = appMessagesManager.getRichReplyText(lastMessage, '');
let messageText = lastMessage.message; let messageText = lastMessage.message;
let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '), {noLinebreakers: true}); let entities = RichTextProcessor.parseEntities(messageText.replace(/\n/g, ' '));
let regExp = new RegExp(escapeRegExp(highlightWord), 'gi'); let regExp = new RegExp(escapeRegExp(highlightWord), 'gi');
let match: any; let match: any;

View File

@ -2498,7 +2498,7 @@ export class AppMessagesManager {
// * 80 for chatlist in landscape orientation // * 80 for chatlist in landscape orientation
text = limitSymbols(text, 75, 80); text = limitSymbols(text, 75, 80);
let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '), {noLinebreakers: true}); let entities = RichTextProcessor.parseEntities(text.replace(/\n/g, ' '));
messageWrapped = RichTextProcessor.wrapRichText(text, { messageWrapped = RichTextProcessor.wrapRichText(text, {
noLinebreaks: true, noLinebreaks: true,

View File

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

View File

@ -34,7 +34,7 @@ const appSidebarLeft = AppSidebarLeft;
const appMediaViewer = AppMediaViewer; const appMediaViewer = AppMediaViewer;
const appDialogsManager = AppDialogsManager; const appDialogsManager = AppDialogsManager;
(window as any).Services = { const Services = {
appUsersManager, appUsersManager,
appChatsManager, appChatsManager,
apiUpdatesManager, apiUpdatesManager,
@ -53,3 +53,10 @@ const appDialogsManager = AppDialogsManager;
appMediaViewer appMediaViewer
//appSharedMediaManager //appSharedMediaManager
}; };
(window as any).Services = Services;
for(let i in Services) {
// @ts-ignore
(window as any)[i] = Services[i];
}

View File

@ -10,7 +10,12 @@ for(const constructor of additional) {
param.type = 'flags.-1?' + param.type; param.type = 'flags.-1?' + param.type;
}); });
const realConstructor = mtproto.constructors.find(c => c.predicate == constructor.predicate); if(constructor.type) {
mtproto.constructors.push(constructor);
}
const realConstructor = constructor.type ? constructor : mtproto.constructors.find(c => c.predicate == constructor.predicate);
/* constructor.params.forEach(param => { /* constructor.params.forEach(param => {
const index = realConstructor.params.findIndex(_param => _param.predicate == param.predicate); const index = realConstructor.params.findIndex(_param => _param.predicate == param.predicate);
if(index !== -1) { if(index !== -1) {

View File

@ -91,4 +91,124 @@
"params": [ "params": [
{"name": "rAbout", "type": "string"} {"name": "rAbout", "type": "string"}
] ]
}, {
"predicate": "messageEntityEmoji",
"params": [
{"name": "offset", "type": "number"},
{"name": "length", "type": "number"},
{"name": "unicode", "type": "string"},
{"name": "nested", "type": "Array<MessageEntity>"}
],
"type": "MessageEntity"
}, {
"predicate": "messageEntityHighlight",
"params": [
{"name": "offset", "type": "number"},
{"name": "length", "type": "number"},
{"name": "nested", "type": "Array<MessageEntity>"}
],
"type": "MessageEntity"
}, {
"predicate": "messageEntityLinebreak",
"params": [
{"name": "offset", "type": "number"},
{"name": "length", "type": "number"},
{"name": "nested", "type": "Array<MessageEntity>"}
],
"type": "MessageEntity"
}, {
"predicate": "messageEntityUnknown",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityMention",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityHashtag",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityBotCommand",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityUrl",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityEmail",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityBold",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityItalic",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityCode",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityPre",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityTextUrl",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityMentionName",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityPhone",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityCashtag",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityUnderline",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityStrike",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityBlockquote",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "messageEntityBankCard",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}, {
"predicate": "inputMessageEntityMentionName",
"params": [
{"name": "nested", "type": "Array<MessageEntity>"}
]
}] }]