diff --git a/src/components/appForward.ts b/src/components/appForward.ts index d8643d93..0242a492 100644 --- a/src/components/appForward.ts +++ b/src/components/appForward.ts @@ -4,7 +4,7 @@ import { putPreloader } from "./misc"; import { AppSelectPeers } from "./appSelectPeers"; class AppForward { - private container = document.getElementById('forward-container') as HTMLDivElement; + public container = document.getElementById('forward-container') as HTMLDivElement; private closeBtn = this.container.querySelector('.sidebar-close-button') as HTMLButtonElement; private sendBtn = this.container.querySelector('.btn-circle') as HTMLButtonElement; @@ -12,10 +12,7 @@ class AppForward { private msgIDs: number[] = []; constructor() { - this.closeBtn.addEventListener('click', () => { - this.cleanup(); - this.container.classList.remove('active'); - }); + this.closeBtn.addEventListener('click', this.close.bind(this)); this.sendBtn.addEventListener('click', () => { let peerIDs = this.selector.getSelected(); @@ -45,6 +42,11 @@ class AppForward { }); } + public close() { + this.cleanup(); + this.container.classList.remove('active'); + } + public cleanup() { if(this.selector) { this.selector.container.remove(); diff --git a/src/components/appSelectPeers.ts b/src/components/appSelectPeers.ts index 6f7c9fb0..306e44f7 100644 --- a/src/components/appSelectPeers.ts +++ b/src/components/appSelectPeers.ts @@ -1,5 +1,5 @@ import Scrollable from "./scrollable_new"; -import appMessagesManager from "../lib/appManagers/appMessagesManager"; +import appMessagesManager, { Dialog } from "../lib/appManagers/appMessagesManager"; import { $rootScope, cancelEvent, findUpTag, findUpClassName } from "../lib/utils"; import appDialogsManager from "../lib/appManagers/appDialogsManager"; import appChatsManager from "../lib/appManagers/appChatsManager"; @@ -24,7 +24,7 @@ export class AppSelectPeers { private myID = $rootScope.myID; private offsetIndex = 0; - private promise: Promise; + private promise: Promise; private query = ''; private cachedContacts: number[]; @@ -47,6 +47,7 @@ export class AppSelectPeers { this.chatsContainer.classList.add('chats-container'); this.chatsContainer.append(this.list); this.scrollable = new Scrollable(this.chatsContainer); + this.scrollable.setVirtualContainer(this.list); this.list.addEventListener('click', (e) => { let target = e.target as HTMLElement; @@ -89,9 +90,11 @@ export class AppSelectPeers { if(this.query != value) { if(this.peerType == 'contacts') { this.cachedContacts = null; - this.promise = null; + } else { + this.offsetIndex = 0; } + this.promise = null; this.list.innerHTML = ''; this.query = value; @@ -115,26 +118,35 @@ export class AppSelectPeers { } } - private getMoreDialogs() { + private async getMoreDialogs() { + if(this.promise) return this.promise; + // в десктопе - сначала без группы, потом архивные, потом контакты без сообщений - let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; - return appMessagesManager.getConversations(this.offsetIndex, pageCount, 0).then(value => { - let dialogs = value.dialogs; - - let newOffsetIndex = dialogs[value.dialogs.length - 1].index || 0; - - dialogs = dialogs.filter(d => d.peerID != this.myID); - if(!this.offsetIndex) { - dialogs.unshift({ - peerID: this.myID, - pFlags: {} - } as any); - } + const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; - this.offsetIndex = newOffsetIndex; + this.promise = appMessagesManager.getConversations(this.query, this.offsetIndex, pageCount, 0); + const value = await this.promise; - this.renderResults(dialogs.map(dialog => dialog.peerID)); - }); + let dialogs = value.dialogs as Dialog[]; + if(!dialogs.length) { + return; + } + + const newOffsetIndex = dialogs[dialogs.length - 1].index || 0; + + dialogs = dialogs.filter(d => d.peerID != this.myID); + if(!this.offsetIndex) { + dialogs.unshift({ + peerID: this.myID, + pFlags: {} + } as any); + } + + this.offsetIndex = newOffsetIndex; + + this.renderResults(dialogs.map(dialog => dialog.peerID)); + + this.promise = null; } private async getMoreContacts() { @@ -148,8 +160,8 @@ export class AppSelectPeers { } if(this.cachedContacts.length) { - let pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; - let arr = this.cachedContacts.splice(0, pageCount); + const pageCount = appPhotosManager.windowH / 72 * 1.25 | 0; + const arr = this.cachedContacts.splice(0, pageCount); this.renderResults(arr); } } @@ -163,8 +175,9 @@ export class AppSelectPeers { } private renderResults(peerIDs: number[]) { + console.log('will renderResults:', peerIDs); peerIDs.forEach(peerID => { - let {dom} = appDialogsManager.addDialog(peerID, this.list, false, false); + const {dom} = appDialogsManager.addDialog(peerID, this.scrollable, false, false); dom.containerEl.insertAdjacentHTML('afterbegin', '
'); let subtitle = ''; @@ -184,14 +197,14 @@ export class AppSelectPeers { } private add(peerID: number) { - let div = document.createElement('div'); + const div = document.createElement('div'); div.classList.add('selector-user', 'scale-in'); div.dataset.peerID = '' + peerID; this.selected[peerID] = div; - let title = appPeersManager.getPeerTitle(peerID, false, true); + const title = appPeersManager.getPeerTitle(peerID, false, true); - let avatarEl = document.createElement('avatar-element'); + const avatarEl = document.createElement('avatar-element'); avatarEl.classList.add('selector-user-avatar', 'tgico'); avatarEl.setAttribute('dialog', '1'); avatarEl.setAttribute('peer', '' + peerID); @@ -205,7 +218,7 @@ export class AppSelectPeers { } private remove(peerID: number) { - let div = this.selected[peerID]; + const div = this.selected[peerID]; div.classList.remove('scale-in'); void div.offsetWidth; div.classList.add('scale-out'); diff --git a/src/components/emoticonsDropdown.ts b/src/components/emoticonsDropdown.ts index b2dd336e..a415a069 100644 --- a/src/components/emoticonsDropdown.ts +++ b/src/components/emoticonsDropdown.ts @@ -254,7 +254,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, stickersDiv.classList.add('stickers-categories'); contentStickersDiv.append(stickersDiv); - stickersDiv.addEventListener('mouseover', (e) => { + /* stickersDiv.addEventListener('mouseover', (e) => { let target = e.target as HTMLElement; if(target.tagName == 'CANVAS') { // turn on sticker @@ -269,7 +269,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, } } } - }); + }); */ stickersDiv.addEventListener('click', onMediaClick); @@ -397,7 +397,7 @@ const initEmoticonsDropdown = (pageEl: HTMLDivElement, const text = e.srcElement.result; let json = await apiManager.gzipUncompress(text, true); - let animation = await lottieLoader.loadAnimation({ + let animation = await lottieLoader.loadAnimationWorker({ container: li, loop: true, autoplay: false, diff --git a/src/components/misc.ts b/src/components/misc.ts index 121e10b7..40382e42 100644 --- a/src/components/misc.ts +++ b/src/components/misc.ts @@ -341,10 +341,10 @@ export function formatPhoneNumber(str: string) { return {formatted: str, country}; } -export function parseMenuButtonsTo(to: {[name: string]: HTMLButtonElement}, elements: HTMLCollection) { +export function parseMenuButtonsTo(to: {[name: string]: HTMLElement}, elements: HTMLCollection | NodeListOf) { Array.from(elements).forEach(el => { - let name = el.className.match(/ menu-(.+?) /)[1]; - to[name] = el as HTMLButtonElement; + let name = el.className.match(/(?:^|\s)menu-(.+?)(?:$|\s)/)[1]; + to[name] = el as HTMLElement; }); } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index e802c57a..32c20eb3 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -697,7 +697,8 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o let stickerType = doc.sticker; if(stickerType == 2 && !LottieLoader.loaded) { - LottieLoader.loadLottie(); + //LottieLoader.loadLottie(); + LottieLoader.loadLottieWorkers(); } if(!stickerType) { @@ -759,7 +760,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o div.append(img); } - lazyLoadQueue && !downloaded ? lazyLoadQueue.push({div, load}) : load(); + lazyLoadQueue && !downloaded ? lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}) : load(); } } @@ -779,7 +780,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o let downloaded = doc.downloaded; let load = () => appDocsManager.downloadDoc(doc.id).then(blob => { - //console.log('loaded sticker:', blob, div); + //console.log('loaded sticker:', doc, div); if(middleware && !middleware()) return; //return; @@ -796,21 +797,26 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o //console.timeEnd('decompress sticker' + doc.id); - let animation = await LottieLoader.loadAnimation({ + /* if(doc.id == '1860749763008266301') { + console.log('loaded sticker:', doc, div); + } */ + + let animation = await LottieLoader.loadAnimationWorker/* loadAnimation */({ container: div, - loop: false, - autoplay: false, - animationData: JSON.parse(json), - renderer: 'svg' + loop: !emoji, + autoplay: true, + animationData: JSON.parse(json) }, group, toneIndex); + animation.addListener('ready', () => { + if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') { + div.firstElementChild.remove(); + } + }); + //console.timeEnd('render sticker' + doc.id); - if(div.firstElementChild && div.firstElementChild.tagName == 'IMG') { - div.firstElementChild.remove(); - } - - div.addEventListener('mouseover', (e) => { + /* div.addEventListener('mouseover', (e) => { let animation = LottieLoader.getAnimation(div, group); if(animation) { @@ -833,9 +839,9 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o } }); - if(play) { + if(play && false) { animation.play(); - } + } */ }); reader.readAsArrayBuffer(blob); @@ -862,7 +868,7 @@ export function wrapSticker({doc, div, middleware, lazyLoadQueue, group, play, o } }); - return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat'}), Promise.resolve()) : load(); + return lazyLoadQueue && (!doc.downloaded || stickerType == 2) ? (lazyLoadQueue.push({div, load, wasSeen: group == 'chat' && stickerType != 2}), Promise.resolve()) : load(); } export function wrapReply(title: string, subtitle: string, message?: any) { diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index 194cd2dc..a210ff22 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -1,9 +1,10 @@ -import { $rootScope, isObject, SearchIndexManager, safeReplaceObject, copy, numberWithCommas } from "../utils"; +import { $rootScope, isObject, safeReplaceObject, copy, numberWithCommas } from "../utils"; import { RichTextProcessor } from "../richtextprocessor"; import appUsersManager from "./appUsersManager"; import apiManager from '../mtproto/mtprotoworker'; import apiUpdatesManager from "./apiUpdatesManager"; import appProfileManager from "./appProfileManager"; +import searchIndexManager from "../searchIndexManager"; type Channel = { _: 'channel', @@ -72,7 +73,7 @@ export class AppChatsManager { let oldChat = this.chats[apiChat.id]; - let titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '', false).split(' '); + let titleWords = searchIndexManager.cleanSearchText(apiChat.title || '', false).split(' '); let firstWord = titleWords.shift(); let lastWord = titleWords.pop(); apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : ''); @@ -95,7 +96,7 @@ export class AppChatsManager { } if(apiChat.username) { - let searchUsername = SearchIndexManager.cleanUsername(apiChat.username); + let searchUsername = searchIndexManager.cleanUsername(apiChat.username); this.usernames[searchUsername] = apiChat.id; } @@ -297,10 +298,10 @@ export class AppChatsManager { } public getChatMembersString(id: number) { - let chat = this.getChat(id); + const chat = this.getChat(id); - let isChannel = this.isChannel(id) && !this.isMegagroup(id); - let participants_count = chat.participants_count || chat.participants.participants.length; + const isChannel = this.isBroadcast(id); + const participants_count = chat.participants_count || chat.participants?.participants.length || 0; return numberWithCommas(participants_count) + ' ' + (isChannel ? 'subscribers' : 'members'); } diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 1c239385..baa8f425 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -489,7 +489,7 @@ export class AppDialogsManager { console.time('getDialogs time'); let loadCount = 50/*this.chatsLoadCount */; - this.loadDialogsPromise = appMessagesManager.getConversations(offsetIndex, loadCount, +archived); + this.loadDialogsPromise = appMessagesManager.getConversations('', offsetIndex, loadCount, +archived); let result = await this.loadDialogsPromise; @@ -792,7 +792,7 @@ export class AppDialogsManager { return this.doms[peerID] || this.domsArchived[peerID]; } - public addDialog(_dialog: Dialog | number, container?: HTMLUListElement, drawStatus = true, rippleEnabled = true, onlyFirstName = false) { + public addDialog(_dialog: Dialog | number, container?: HTMLUListElement | Scrollable, drawStatus = true, rippleEnabled = true, onlyFirstName = false) { let dialog: Dialog; if(typeof(_dialog) === 'number') { diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 0c6c7087..62b64374 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -260,13 +260,6 @@ export class AppImManager { private scrolledAllDown: boolean; public contextMenu = new ChatContextMenu(this.bubblesContainer); - - private popupDeleteMessage: { - popupEl?: HTMLDivElement, - deleteBothBtn?: HTMLButtonElement, - deleteMeBtn?: HTMLButtonElement, - cancelBtn?: HTMLButtonElement - } = {}; private setPeerPromise: Promise = null; @@ -288,9 +281,12 @@ export class AppImManager { private peerChanged: boolean; private firstUnreadBubble: HTMLDivElement = null; + private attachedUnreadBubble: boolean; private stickyIntersector: StickyIntersector = null; + private cleanupID = 0; + constructor() { /* if(!lottieLoader.loaded) { lottieLoader.loadLottie(); @@ -301,12 +297,7 @@ export class AppImManager { this.chatInputC = new ChatInput(); this.preloader = new ProgressivePreloader(null, false); - - this.popupDeleteMessage.popupEl = this.pageEl.querySelector('.popup-delete-message') as HTMLDivElement; - this.popupDeleteMessage.deleteBothBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-both') as HTMLButtonElement; - this.popupDeleteMessage.deleteMeBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-delete-me') as HTMLButtonElement; - this.popupDeleteMessage.cancelBtn = this.popupDeleteMessage.popupEl.querySelector('.popup-close') as HTMLButtonElement; - + apiManager.getUserID().then((id) => { this.myID = $rootScope.myID = id; }); @@ -657,8 +648,16 @@ export class AppImManager { return; } - if(e.key == 'Escape' && this.peerID != 0) { // hide current dialog - this.setPeer(0); + if(e.key == 'Escape') { + if(appMediaViewer.wholeDiv.classList.contains('active')) { + appMediaViewer.buttons.close.click(); + } else if(appForward.container.classList.contains('active')) { + appForward.close(); + } else if(this.chatInputC.replyElements.container.classList.contains('active')) { + this.chatInputC.replyElements.cancelBtn.click(); + } else if(this.peerID != 0) { // hide current dialog + this.setPeer(0); + } } else if(e.key == 'Meta' || e.key == 'Control') { return; } else if(e.key == 'c' && (e.ctrlKey || e.metaKey) && target.tagName != 'INPUT') { @@ -672,16 +671,6 @@ export class AppImManager { }; document.body.addEventListener('keydown', onKeyDown); - - this.popupDeleteMessage.deleteBothBtn.addEventListener('click', () => { - appMessagesManager.deleteMessages([this.contextMenu.msgID], true); - this.popupDeleteMessage.cancelBtn.click(); - }); - - this.popupDeleteMessage.deleteMeBtn.addEventListener('click', () => { - appMessagesManager.deleteMessages([this.contextMenu.msgID], false); - this.popupDeleteMessage.cancelBtn.click(); - }); this.goDownBtn.addEventListener('click', () => { let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0]; @@ -939,6 +928,7 @@ export class AppImManager { this.peerChanged = false; this.firstUnreadBubble = null; + this.attachedUnreadBubble = false; this.messagesQueue.length = 0; this.messagesQueuePromise = null; @@ -954,6 +944,8 @@ export class AppImManager { this.loadedTopTimes = this.loadedBottomTimes = 0; + this.cleanupID++; + ////console.timeEnd('appImManager cleanup'); } @@ -1094,7 +1086,8 @@ export class AppImManager { this.log('scrolledAllDown:', this.scrolledAllDown); - if(!this.unreaded.length && dialog) { // lol + //if(!this.unreaded.length && dialog) { // lol + if(this.scrolledAllDown && dialog) { // lol appMessagesManager.readHistory(peerID, dialog.top_message); } @@ -1401,6 +1394,13 @@ export class AppImManager { }); } } + + private getMiddleware() { + let cleanupID = this.cleanupID; + return () => { + return this.cleanupID == cleanupID; + }; + } // reverse means top public renderMessage(message: any, reverse = false, multipleRender = false, bubble: HTMLDivElement = null, updatePosition = true) { @@ -1766,16 +1766,12 @@ export class AppImManager { wrapAlbum({ groupID: message.grouped_id, attachmentDiv, - middleware: () => { - return this.peerID == peerID; - }, + middleware: this.getMiddleware(), isOut: our, lazyLoadQueue: this.lazyLoadQueue }); } else { - wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, isOut, this.lazyLoadQueue, () => { - return this.peerID == peerID; - }); + wrapPhoto(photo.id, message, attachmentDiv, undefined, undefined, true, isOut, this.lazyLoadQueue, this.getMiddleware()); } break; @@ -1827,9 +1823,7 @@ export class AppImManager { boxWidth: 380, boxHeight: 300, lazyLoadQueue: this.lazyLoadQueue, - middleware: () => { - return this.peerID == peerID; - }, + middleware: this.getMiddleware(), isOut }); //} @@ -1841,9 +1835,14 @@ export class AppImManager { if(webpage.photo && !doc) { bubble.classList.add('photo'); - wrapPhoto(webpage.photo.id, message, preview, 380, 300, false, null, this.lazyLoadQueue, () => { - return this.peerID == peerID; - }); + const size = webpage.photo.sizes[webpage.photo.sizes.length - 1]; + if(size.w == size.h) { + bubble.classList.add('is-square-photo'); + } else if(size.h > size.w) { + bubble.classList.add('is-vertical-photo'); + } + + wrapPhoto(webpage.photo.id, message, preview, 380, 300, false, null, this.lazyLoadQueue, this.getMiddleware()); } if(preview) { @@ -1861,8 +1860,13 @@ export class AppImManager { if(webpage.title) { titleDiv.innerHTML = RichTextProcessor.wrapRichText(webpage.title); } + + let quoteTextDiv = document.createElement('div'); + quoteTextDiv.classList.add('quote-text'); + quoteTextDiv.append(nameEl, titleDiv, textDiv); + + quote.append(quoteTextDiv); - quote.append(nameEl, titleDiv, textDiv); box.append(quote); //bubble.prepend(box); @@ -1894,14 +1898,7 @@ export class AppImManager { wrapSticker({ doc, div: attachmentDiv, - middleware: () => { - if(this.peerID != peerID) { - this.log.warn('peer changed, canceling sticker attach'); - return false; - } - - return true; - }, + middleware: this.getMiddleware(), lazyLoadQueue: this.lazyLoadQueue, group: 'chat', play: !!message.pending || !multipleRender, @@ -1923,9 +1920,7 @@ export class AppImManager { wrapAlbum({ groupID: message.grouped_id, attachmentDiv, - middleware: () => { - return this.peerID == peerID; - }, + middleware: this.getMiddleware(), isOut: our, lazyLoadQueue: this.lazyLoadQueue }); @@ -1939,9 +1934,7 @@ export class AppImManager { withTail: doc.type != 'round', isOut: isOut, lazyLoadQueue: this.lazyLoadQueue, - middleware: () => { - return this.peerID == peerID; - } + middleware: this.getMiddleware() }); } @@ -2325,6 +2318,10 @@ export class AppImManager { } public setUnreadDelimiter() { + if(this.attachedUnreadBubble) { + return; + } + let dialog = appMessagesManager.getDialogByPeerID(this.peerID)[0]; if(!dialog?.unread_count) return; @@ -2343,6 +2340,7 @@ export class AppImManager { } this.firstUnreadBubble = bubble; + this.attachedUnreadBubble = true; } } diff --git a/src/lib/appManagers/appMediaViewer.ts b/src/lib/appManagers/appMediaViewer.ts index 1705b50e..0ca8b0cc 100644 --- a/src/lib/appManagers/appMediaViewer.ts +++ b/src/lib/appManagers/appMediaViewer.ts @@ -4,35 +4,27 @@ import appMessagesManager from "./appMessagesManager"; import { RichTextProcessor } from "../richtextprocessor"; import { logger } from "../polyfill"; import ProgressivePreloader from "../../components/preloader"; -import { findUpClassName, $rootScope, generatePathData, fillPropertyValue } from "../utils"; +import { findUpClassName, $rootScope, generatePathData, fillPropertyValue, cancelEvent } from "../utils"; import appDocsManager from "./appDocsManager"; import VideoPlayer from "../mediaPlayer"; -import { renderImageFromUrl } from "../../components/misc"; +import { renderImageFromUrl, parseMenuButtonsTo } from "../../components/misc"; import AvatarElement from "../../components/avatar"; import LazyLoadQueue from "../../components/lazyLoadQueue"; import appForward from "../../components/appForward"; export class AppMediaViewer { - private overlaysDiv = document.querySelector('.overlays') as HTMLDivElement; - private mediaViewerDiv = this.overlaysDiv.firstElementChild as HTMLDivElement; + public wholeDiv = document.querySelector('.media-viewer-whole') as HTMLDivElement; + private overlaysDiv = this.wholeDiv.firstElementChild as HTMLDivElement; private author = { avatarEl: this.overlaysDiv.querySelector('.media-viewer-userpic') as AvatarElement, nameEl: this.overlaysDiv.querySelector('.media-viewer-name') as HTMLDivElement, date: this.overlaysDiv.querySelector('.media-viewer-date') as HTMLDivElement }; - private buttons = { - delete: this.overlaysDiv.querySelector('.media-viewer-delete-button') as HTMLDivElement, - forward: this.overlaysDiv.querySelector('.media-viewer-forward-button') as HTMLDivElement, - download: this.overlaysDiv.querySelector('.media-viewer-download-button') as HTMLDivElement, - close: this.overlaysDiv.querySelector('.media-viewer-close-button') as HTMLDivElement, - prev: this.overlaysDiv.querySelector('.media-viewer-switcher-left') as HTMLDivElement, - next: this.overlaysDiv.querySelector('.media-viewer-switcher-right') as HTMLDivElement, - }; - private content = { - container: this.overlaysDiv.querySelector('.media-viewer-media') as HTMLDivElement, - caption: this.overlaysDiv.querySelector('.media-viewer-caption') as HTMLDivElement, - //mover: this.overlaysDiv.querySelector('.media-viewer-mover') as HTMLDivElement - mover: document.querySelector('.media-viewer-mover') as HTMLDivElement + public buttons: {[k in 'delete' | 'forward' | 'download' | 'close' | 'prev' | 'next']: HTMLElement} = {} as any; + private content: {[k in 'container' | 'caption' | 'mover']: HTMLDivElement} = { + container: this.overlaysDiv.querySelector('.media-viewer-media'), + caption: this.overlaysDiv.querySelector('.media-viewer-caption'), + mover: null }; public currentMessageID = 0; @@ -71,9 +63,12 @@ export class AppMediaViewer { this.preloader = new ProgressivePreloader(); this.lazyLoadQueue = new LazyLoadQueue(5, false); + parseMenuButtonsTo(this.buttons, this.wholeDiv.querySelectorAll(`[class*='menu']`) as NodeListOf); + this.onKeyDownBinded = this.onKeyDown.bind(this); - this.buttons.close.addEventListener('click', () => { + this.buttons.close.addEventListener('click', (e) => { + cancelEvent(e); //this.overlaysDiv.classList.remove('active'); this.content.container.innerHTML = ''; if(this.content.container.firstElementChild) { @@ -92,10 +87,13 @@ export class AppMediaViewer { this.loadedAllMediaUp = this.loadedAllMediaDown = false; this.loadMediaPromiseUp = this.loadMediaPromiseDown = null; + appForward.close(); + window.removeEventListener('keydown', this.onKeyDownBinded); }); - this.buttons.prev.addEventListener('click', () => { + this.buttons.prev.addEventListener('click', (e) => { + cancelEvent(e); if(this.setMoverPromise) return; let target = this.prevTargets.pop(); @@ -107,7 +105,8 @@ export class AppMediaViewer { } }); - this.buttons.next.addEventListener('click', () => { + this.buttons.next.addEventListener('click', (e) => { + cancelEvent(e); if(this.setMoverPromise) return; let target = this.nextTargets.shift(); @@ -141,6 +140,7 @@ export class AppMediaViewer { }); this.onClickBinded = (e: MouseEvent) => { + cancelEvent(e); let target = e.target as HTMLElement; let mover: HTMLDivElement = null; @@ -156,9 +156,10 @@ export class AppMediaViewer { } }; - this.overlaysDiv.addEventListener('click', this.onClickBinded); - this.content.mover.addEventListener('click', this.onClickBinded); + this.wholeDiv.addEventListener('click', this.onClickBinded); + //this.content.mover.addEventListener('click', this.onClickBinded); //this.content.mover.append(this.buttons.prev, this.buttons.next); + this.setNewMover(); } public onKeyDown(e: KeyboardEvent) { @@ -176,7 +177,7 @@ export class AppMediaViewer { if(!closing) { mover.innerHTML = ''; - mover.append(this.buttons.prev, this.buttons.next); + //mover.append(this.buttons.prev, this.buttons.next); } let wasActive = fromRight !== 0; @@ -357,7 +358,7 @@ export class AppMediaViewer { mediaElement.src = src; } }); - } else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && (mediaElement.firstElementChild as HTMLSourceElement).src) { + } else if(mediaElement instanceof HTMLVideoElement && mediaElement.firstElementChild && ((mediaElement.firstElementChild as HTMLSourceElement).src || src)) { await new Promise((resolve, reject) => { mediaElement.addEventListener('loadeddata', resolve); @@ -386,7 +387,7 @@ export class AppMediaViewer { } setTimeout(() => { - this.overlaysDiv.classList.remove('active'); + this.wholeDiv.classList.remove('active'); }, 0); setTimeout(() => { @@ -409,6 +410,8 @@ export class AppMediaViewer { //await new Promise((resolve) => setTimeout(resolve, 0)); await new Promise((resolve) => window.requestAnimationFrame(resolve)); + //throw ''; + mover.style.transform = `translate(${containerRect.left}px,${containerRect.top}px) scale(1,1)`; if(aspecter) { @@ -427,10 +430,12 @@ export class AppMediaViewer { mover.classList.remove('moving'); if(aspecter) { // всё из-за видео, элементы управления скейлятся, так бы можно было этого не делать - mover.classList.remove('active'); - //aspecter.style.cssText = ''; - void mover.offsetLeft; // reflow - + if(mover.querySelector('video')) { + mover.classList.remove('active'); + aspecter.style.cssText = ''; + void mover.offsetLeft; // reflow + } + aspecter.classList.remove('disable-hover'); } @@ -453,9 +458,9 @@ export class AppMediaViewer { } let {width, height} = rect; - if(proportion == 1) { + /* if(proportion == 1) { aspecter.style.cssText = ''; - } else { + } else { */ if(proportion > 0) { width = height * proportion; } else { @@ -465,7 +470,7 @@ export class AppMediaViewer { //this.log('will set style aspecter:', `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`); aspecter.style.cssText = `width: ${width}px; height: ${height}px; transform: scale(${containerRect.width / width}, ${containerRect.height / height});`; - } + //} } public sizeTailPath(path: SVGPathElement, rect: DOMRect, scaleX: number, delay: number, upscale: boolean, isOut: boolean, borderRadius: string) { @@ -528,8 +533,12 @@ export class AppMediaViewer { let newMover = document.createElement('div'); newMover.classList.add('media-viewer-mover'); - let oldMover = this.content.mover; - oldMover.parentElement.append(newMover); + if(this.content.mover) { + let oldMover = this.content.mover; + oldMover.parentElement.append(newMover); + } else { + this.wholeDiv.append(newMover); + } newMover.addEventListener('click', this.onClickBinded); @@ -716,7 +725,7 @@ export class AppMediaViewer { this.setNewMover(); } else { window.addEventListener('keydown', this.onKeyDownBinded); - this.overlaysDiv.classList.add('active'); + this.wholeDiv.classList.add('active'); } ////////this.log('wasActive:', wasActive); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 5dffb0de..63a1d64b 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -22,6 +22,7 @@ import apiManager from '../mtproto/mtprotoworker'; import appWebPagesManager from "./appWebPagesManager"; import { CancellablePromise, deferredPromise } from "../polyfill"; import appPollsManager from "./appPollsManager"; +import searchIndexManager from '../searchIndexManager'; const APITIMEOUT = 0; @@ -107,6 +108,17 @@ export class AppMessagesManager { public loaded: Promise = null; + private dialogsIndex = searchIndexManager.createIndex(); + private cachedResults: { + query: string, + count: number, + dialogs: Dialog[] + } = { + query: '', + count: 0, + dialogs: [] + }; + constructor() { $rootScope.$on('apiUpdate', (e: CustomEvent) => { let update: any = e.detail; @@ -1226,9 +1238,33 @@ export class AppMessagesManager { return false; } - public getConversations(offsetIndex?: number, limit = 20, folderID = 0) { + public getConversations(query = '', offsetIndex?: number, limit = 20, folderID = 0) { let curDialogStorage = this.dialogsStorage[folderID] ?? (this.dialogsStorage[folderID] = []); + if(query) { + if(!limit || this.cachedResults.query !== query) { + this.cachedResults.query = query + + const results = searchIndexManager.search(query, this.dialogsIndex); + + this.cachedResults.dialogs = []; + for(const folderID in this.dialogsStorage) { + const dialogs = this.dialogsStorage[folderID]; + dialogs.forEach(dialog => { + if(results[dialog.peerID]) { + this.cachedResults.dialogs.push(dialog); + } + }); + } + + this.cachedResults.count = this.cachedResults.dialogs.length; + } + + curDialogStorage = this.cachedResults.dialogs; + } else { + this.cachedResults.query = ''; + } + let offset = 0; if(offsetIndex > 0) { for(; offset < curDialogStorage.length; offset++) { @@ -1238,15 +1274,15 @@ export class AppMessagesManager { } } - if(this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) { + if(query || this.allDialogsLoaded[folderID] || curDialogStorage.length >= offset + limit) { return Promise.resolve({ dialogs: curDialogStorage.slice(offset, offset + limit), count: curDialogStorage.length }); } - return this.getTopMessages(limit, folderID).then(count => { - let curDialogStorage = this.dialogsStorage[folderID]; + return this.getTopMessages(limit, folderID).then(totalCount => { + //const curDialogStorage = this.dialogsStorage[folderID]; offset = 0; if(offsetIndex > 0) { @@ -1261,7 +1297,7 @@ export class AppMessagesManager { return { dialogs: curDialogStorage.slice(offset, offset + limit), - count: count + count: totalCount }; }); } @@ -1277,15 +1313,13 @@ export class AppMessagesManager { if(this.dialogsOffsetDate[folderID]) { offsetDate = this.dialogsOffsetDate[folderID] + serverTimeManager.serverTimeOffset; offsetIndex = this.dialogsOffsetDate[folderID] * 0x10000; - flags |= 1; + //flags |= 1; // means pinned already loaded } - if(folderID > 0) { - flags |= 1; + //if(folderID > 0) { + //flags |= 1; flags |= 2; - } - - let hash = 0; + //} return apiManager.invokeApi('messages.getDialogs', { flags: flags, @@ -1294,7 +1328,7 @@ export class AppMessagesManager { offset_id: appMessagesIDsManager.getMessageLocalID(offsetID), offset_peer: appPeersManager.getInputPeerByID(offsetPeerID), limit: limit, - hash: hash + hash: 0 }, { timeout: APITIMEOUT }).then((dialogsResult: any) => { @@ -1828,9 +1862,11 @@ export class AppMessagesManager { let messageText = ''; if(message.media) { - switch(message.media._) { + if(message.grouped_id) { + messageText += 'Album' + (message.message ? ', ' : '') + ''; + } else switch(message.media._) { case 'messageMediaPhoto': - messageText += '' + (message.grouped_id ? 'Album' : 'Photo') + (message.message ? ', ' : '') + ''; + messageText += 'Photo' + (message.message ? ', ' : '') + ''; break; case 'messageMediaGeo': messageText += 'Geolocation'; @@ -1869,26 +1905,33 @@ export class AppMessagesManager { if(message.action) { let action = message.action; - console.log('message action:', action); - - let suffix = ''; - let _ = action._; - if(_ == "messageActionPhoneCall") { - _ += '.' + action.type; - - let duration = action.duration; - if(duration) { - let d = []; - - d.push(duration % 60 + ' s'); - if(duration >= 60) d.push((duration / 60 | 0) + ' min'); - //if(duration >= 3600) d.push((duration / 3600 | 0) + ' h'); - suffix = ' (' + d.reverse().join(' ') + ')'; + let str = ''; + if(action.message) { + str = RichTextProcessor.wrapRichText(action.message, {noLinebreaks: true}); + } else { + let suffix = ''; + let _ = action._; + if(_ == "messageActionPhoneCall") { + _ += '.' + action.type; + + let duration = action.duration; + if(duration) { + let d = []; + + d.push(duration % 60 + ' s'); + if(duration >= 60) d.push((duration / 60 | 0) + ' min'); + //if(duration >= 3600) d.push((duration / 3600 | 0) + ' h'); + suffix = ' (' + d.reverse().join(' ') + ')'; + } } + + // @ts-ignore + str = langPack[_] + suffix; } - // @ts-ignore - messageText = '' + langPack[_] + suffix + ''; + console.log('message action:', action); + + messageText = '' + str + ''; } let messageWrapped = ''; @@ -2098,18 +2141,21 @@ export class AppMessagesManager { } public saveConversation(dialog: Dialog) { - var peerID = appPeersManager.getPeerID(dialog.peer); + const peerID = appPeersManager.getPeerID(dialog.peer); if(!peerID) { return false; } - var channelID = appPeersManager.isChannel(peerID) ? -peerID : 0; + const channelID = appPeersManager.isChannel(peerID) ? -peerID : 0; + const peerText = appPeersManager.getPeerSearchText(peerID); + searchIndexManager.indexObject(peerID, peerText, this.dialogsIndex); + let mid: number, message; if(dialog.top_message) { - var mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID); - var message = this.getMessage(mid); + mid = appMessagesIDsManager.getFullMessageID(dialog.top_message, channelID); + message = this.getMessage(mid); } else { - var mid = this.tempID--; - var message: any = { + mid = this.tempID--; + message = { _: 'message', id: mid, mid: mid, @@ -2120,14 +2166,14 @@ export class AppMessagesManager { pFlags: {unread: false, out: true}, date: 0, message: '' - } + }; this.saveMessages([message]); } if(!channelID && peerID < 0) { - var chat = appChatsManager.getChat(-peerID) + const chat = appChatsManager.getChat(-peerID); if(chat && chat.migrated_to && chat.pFlags.deactivated) { - var migratedToPeer = appPeersManager.getPeerID(chat.migrated_to) + const migratedToPeer = appPeersManager.getPeerID(chat.migrated_to); this.migratedFromTo[peerID] = migratedToPeer; this.migratedToFrom[migratedToPeer] = peerID; return; @@ -2151,7 +2197,7 @@ export class AppMessagesManager { } if(this.historiesStorage[peerID] === undefined/* && !message.deleted */) { // warning - let historyStorage: HistoryStorage = {count: null, history: [], pending: []}; + const historyStorage: HistoryStorage = {count: null, history: [], pending: []}; historyStorage[mid > 0 ? 'history' : 'pending'].push(mid); if(mid < 0 && message.pFlags.unread) { dialog.unread_count++; diff --git a/src/lib/appManagers/appPollsManager.ts b/src/lib/appManagers/appPollsManager.ts index 01da304d..5b5eb95d 100644 --- a/src/lib/appManagers/appPollsManager.ts +++ b/src/lib/appManagers/appPollsManager.ts @@ -148,4 +148,6 @@ class AppPollsManager { } } -export default new AppPollsManager(); \ No newline at end of file +const appPollsManager = new AppPollsManager(); +(window as any).appPollsManager = appPollsManager; +export default appPollsManager; \ No newline at end of file diff --git a/src/lib/appManagers/appSidebarRight.ts b/src/lib/appManagers/appSidebarRight.ts index 8b7b51a5..90faef1f 100644 --- a/src/lib/appManagers/appSidebarRight.ts +++ b/src/lib/appManagers/appSidebarRight.ts @@ -15,6 +15,7 @@ import LazyLoadQueue from "../../components/lazyLoadQueue"; import { wrapDocument, wrapAudio } from "../../components/wrappers"; import AppSearch, { SearchGroup } from "../../components/appSearch"; import AvatarElement from "../../components/avatar"; +import appForward from "../../components/appForward"; const testScroll = false; @@ -250,6 +251,11 @@ class AppSidebarRight { (item.element.firstElementChild as HTMLElement).style.display = ''; } + if(enable == false || (this.sidebarEl.classList.contains('active') && enable == undefined)) { + appForward.close(); + this.searchCloseBtn.click(); + } + resolve(); }, 200); }); diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index cb54a09d..17b0beb7 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -1,10 +1,11 @@ -import { SearchIndexManager, safeReplaceObject, isObject, tsNow, copy, $rootScope } from "../utils"; +import { safeReplaceObject, isObject, tsNow, copy, $rootScope } from "../utils"; import { RichTextProcessor } from "../richtextprocessor"; import appChatsManager from "./appChatsManager"; //import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/mtprotoworker'; import serverTimeManager from "../mtproto/serverTimeManager"; import { formatPhoneNumber } from "../../components/misc"; +import searchIndexManager from "../searchIndexManager"; export type User = { _: 'user', @@ -39,7 +40,7 @@ export class AppUsersManager { public usernames: {[username: string]: number} = {}; public userAccess: {[userID: number]: string} = {}; public cachedPhotoLocations: any = {}; - public contactsIndex = SearchIndexManager.createIndex(); + public contactsIndex = searchIndexManager.createIndex(); public contactsFillPromise: Promise; public contactsList: number[]; public myID: number; @@ -118,14 +119,14 @@ export class AppUsersManager { return this.contactsFillPromise = apiManager.invokeApi('contacts.getContacts', { hash: 0 }).then((result: any) => { - var userID; + let userID: number; this.contactsList = []; this.saveApiUsers(result.users); result.contacts.forEach((contact: any) => { userID = contact.user_id; this.contactsList.push(userID); - SearchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex); + searchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex); }); return this.contactsList; @@ -133,15 +134,12 @@ export class AppUsersManager { } public getUserSearchText(id: number) { - var user = this.users[id]; + const user = this.users[id]; if(!user) { - return false; - } - var serviceText = ''; - if(user.pFlags.self) { - serviceText = 'user_name_saved_msgs_raw'; + return ''; } + const serviceText = user.pFlags.self ? 'user_name_saved_msgs_raw' : ''; return (user.first_name || '') + ' ' + (user.last_name || '') + ' ' + (user.phone || '') + @@ -152,7 +150,7 @@ export class AppUsersManager { public getContacts(query?: string) { return this.fillContacts().then(contactsList => { if(query) { - const results: any = SearchIndexManager.search(query, this.contactsIndex); + const results: any = searchIndexManager.search(query, this.contactsIndex); const filteredContactsList = contactsList.filter(id => !!results[id]); contactsList = filteredContactsList; @@ -219,11 +217,11 @@ export class AppUsersManager { } if(apiUser.username) { - var searchUsername = SearchIndexManager.cleanUsername(apiUser.username); + var searchUsername = searchIndexManager.cleanUsername(apiUser.username); this.usernames[searchUsername] = userID; } - apiUser.sortName = apiUser.pFlags.deleted ? '' : SearchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || ''), false); + apiUser.sortName = apiUser.pFlags.deleted ? '' : searchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || ''), false); var nameWords = apiUser.sortName.split(' '); var firstWord = nameWords.shift(); @@ -544,7 +542,7 @@ export class AppUsersManager { if(isContact != curIsContact) { if(isContact) { this.contactsList.push(userID) - SearchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex); + searchIndexManager.indexObject(userID, this.getUserSearchText(userID), this.contactsIndex); } else { this.contactsList.splice(curPos, 1); } diff --git a/src/lib/config.ts b/src/lib/config.ts index 5b635fc9..906c708c 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -23,7 +23,7 @@ var Countries = [{"phoneCode":"7 840","code":"AB","name":"Abkhazia","pattern":"" var TLD = ['abogado', 'ac', 'academy', 'accountants', 'active', 'actor', 'ad', 'adult', 'ae', 'aero', 'af', 'ag', 'agency', 'ai', 'airforce', 'al', 'allfinanz', 'alsace', 'am', 'amsterdam', 'an', 'android', 'ao', 'apartments', 'aq', 'aquarelle', 'ar', 'archi', 'army', 'arpa', 'as', 'asia', 'associates', 'at', 'attorney', 'au', 'auction', 'audio', 'autos', 'aw', 'ax', 'axa', 'az', 'ba', 'band', 'bank', 'bar', 'barclaycard', 'barclays', 'bargains', 'bayern', 'bb', 'bd', 'be', 'beer', 'berlin', 'best', 'bf', 'bg', 'bh', 'bi', 'bid', 'bike', 'bingo', 'bio', 'biz', 'bj', 'black', 'blackfriday', 'bloomberg', 'blue', 'bm', 'bmw', 'bn', 'bnpparibas', 'bo', 'boo', 'boutique', 'br', 'brussels', 'bs', 'bt', 'budapest', 'build', 'builders', 'business', 'buzz', 'bv', 'bw', 'by', 'bz', 'bzh', 'ca', 'cab', 'cal', 'camera', 'camp', 'cancerresearch', 'canon', 'capetown', 'capital', 'caravan', 'cards', 'care', 'career', 'careers', 'cartier', 'casa', 'cash', 'cat', 'catering', 'cc', 'cd', 'center', 'ceo', 'cern', 'cf', 'cg', 'ch', 'channel', 'chat', 'cheap', 'christmas', 'chrome', 'church', 'ci', 'citic', 'city', 'ck', 'cl', 'claims', 'cleaning', 'click', 'clinic', 'clothing', 'club', 'cm', 'cn', 'co', 'coach', 'codes', 'coffee', 'college', 'cologne', 'com', 'community', 'company', 'computer', 'condos', 'construction', 'consulting', 'contractors', 'cooking', 'cool', 'coop', 'country', 'cr', 'credit', 'creditcard', 'cricket', 'crs', 'cruises', 'cu', 'cuisinella', 'cv', 'cw', 'cx', 'cy', 'cymru', 'cz', 'dabur', 'dad', 'dance', 'dating', 'day', 'dclk', 'de', 'deals', 'degree', 'delivery', 'democrat', 'dental', 'dentist', 'desi', 'design', 'dev', 'diamonds', 'diet', 'digital', 'direct', 'directory', 'discount', 'dj', 'dk', 'dm', 'dnp', 'do', 'docs', 'domains', 'doosan', 'durban', 'dvag', 'dz', 'eat', 'ec', 'edu', 'education', 'ee', 'eg', 'email', 'emerck', 'energy', 'engineer', 'engineering', 'enterprises', 'equipment', 'er', 'es', 'esq', 'estate', 'et', 'eu', 'eurovision', 'eus', 'events', 'everbank', 'exchange', 'expert', 'exposed', 'fail', 'farm', 'fashion', 'feedback', 'fi', 'finance', 'financial', 'firmdale', 'fish', 'fishing', 'fit', 'fitness', 'fj', 'fk', 'flights', 'florist', 'flowers', 'flsmidth', 'fly', 'fm', 'fo', 'foo', 'forsale', 'foundation', 'fr', 'frl', 'frogans', 'fund', 'furniture', 'futbol', 'ga', 'gal', 'gallery', 'garden', 'gb', 'gbiz', 'gd', 'ge', 'gent', 'gf', 'gg', 'ggee', 'gh', 'gi', 'gift', 'gifts', 'gives', 'gl', 'glass', 'gle', 'global', 'globo', 'gm', 'gmail', 'gmo', 'gmx', 'gn', 'goog', 'google', 'gop', 'gov', 'gp', 'gq', 'gr', 'graphics', 'gratis', 'green', 'gripe', 'gs', 'gt', 'gu', 'guide', 'guitars', 'guru', 'gw', 'gy', 'hamburg', 'hangout', 'haus', 'healthcare', 'help', 'here', 'hermes', 'hiphop', 'hiv', 'hk', 'hm', 'hn', 'holdings', 'holiday', 'homes', 'horse', 'host', 'hosting', 'house', 'how', 'hr', 'ht', 'hu', 'ibm', 'id', 'ie', 'ifm', 'il', 'im', 'immo', 'immobilien', 'in', 'industries', 'info', 'ing', 'ink', 'institute', 'insure', 'int', 'international', 'investments', 'io', 'iq', 'ir', 'irish', 'is', 'it', 'iwc', 'jcb', 'je', 'jetzt', 'jm', 'jo', 'jobs', 'joburg', 'jp', 'juegos', 'kaufen', 'kddi', 'ke', 'kg', 'kh', 'ki', 'kim', 'kitchen', 'kiwi', 'km', 'kn', 'koeln', 'kp', 'kr', 'krd', 'kred', 'kw', 'ky', 'kyoto', 'kz', 'la', 'lacaixa', 'land', 'lat', 'latrobe', 'lawyer', 'lb', 'lc', 'lds', 'lease', 'legal', 'lgbt', 'li', 'lidl', 'life', 'lighting', 'limited', 'limo', 'link', 'lk', 'loans', 'london', 'lotte', 'lotto', 'lr', 'ls', 'lt', 'ltda', 'lu', 'luxe', 'luxury', 'lv', 'ly', 'ma', 'madrid', 'maison', 'management', 'mango', 'market', 'marketing', 'marriott', 'mc', 'md', 'me', 'media', 'meet', 'melbourne', 'meme', 'memorial', 'menu', 'mg', 'mh', 'miami', 'mil', 'mini', 'mk', 'ml', 'mm', 'mn', 'mo', 'mobi', 'moda', 'moe', 'monash', 'money', 'mormon', 'mortgage', 'moscow', 'motorcycles', 'mov', 'mp', 'mq', 'mr', 'ms', 'mt', 'mu', 'museum', 'mv', 'mw', 'mx', 'my', 'mz', 'na', 'nagoya', 'name', 'navy', 'nc', 'ne', 'net', 'network', 'neustar', 'new', 'nexus', 'nf', 'ng', 'ngo', 'nhk', 'ni', 'nico', 'ninja', 'nl', 'no', 'np', 'nr', 'nra', 'nrw', 'ntt', 'nu', 'nyc', 'nz', 'okinawa', 'om', 'one', 'ong', 'onl', 'ooo', 'org', 'organic', 'osaka', 'otsuka', 'ovh', 'pa', 'paris', 'partners', 'parts', 'party', 'pe', 'pf', 'pg', 'ph', 'pharmacy', 'photo', 'photography', 'photos', 'physio', 'pics', 'pictures', 'pink', 'pizza', 'pk', 'pl', 'place', 'plumbing', 'pm', 'pn', 'pohl', 'poker', 'porn', 'post', 'pr', 'praxi', 'press', 'pro', 'prod', 'productions', 'prof', 'properties', 'property', 'ps', 'pt', 'pub', 'pw', 'py', 'qa', 'qpon', 'quebec', 're', 'realtor', 'recipes', 'red', 'rehab', 'reise', 'reisen', 'reit', 'ren', 'rentals', 'repair', 'report', 'republican', 'rest', 'restaurant', 'reviews', 'rich', 'rio', 'rip', 'ro', 'rocks', 'rodeo', 'rs', 'rsvp', 'ru', 'ruhr', 'rw', 'ryukyu', 'sa', 'saarland', 'sale', 'samsung', 'sarl', 'saxo', 'sb', 'sc', 'sca', 'scb', 'schmidt', 'schule', 'schwarz', 'science', 'scot', 'sd', 'se', 'services', 'sew', 'sexy', 'sg', 'sh', 'shiksha', 'shoes', 'shriram', 'si', 'singles', 'sj', 'sk', 'sky', 'sl', 'sm', 'sn', 'so', 'social', 'software', 'sohu', 'solar', 'solutions', 'soy', 'space', 'spiegel', 'sr', 'st', 'style', 'su', 'supplies', 'supply', 'support', 'surf', 'surgery', 'suzuki', 'sv', 'sx', 'sy', 'sydney', 'systems', 'sz', 'taipei', 'tatar', 'tattoo', 'tax', 'tc', 'td', 'technology', 'tel', 'temasek', 'tennis', 'tf', 'tg', 'th', 'tienda', 'tips', 'tires', 'tirol', 'tj', 'tk', 'tl', 'tm', 'tn', 'to', 'today', 'tokyo', 'tools', 'top', 'toshiba', 'town', 'toys', 'tp', 'tr', 'trade', 'training', 'travel', 'trust', 'tt', 'tui', 'tv', 'tw', 'tz', 'ua', 'ug', 'uk', 'university', 'uno', 'uol', 'us', 'uy', 'uz', 'va', 'vacations', 'vc', 've', 'vegas', 'ventures', 'versicherung', 'vet', 'vg', 'vi', 'viajes', 'video', 'villas', 'vision', 'vlaanderen', 'vn', 'vodka', 'vote', 'voting', 'voto', 'voyage', 'vu', 'wales', 'wang', 'watch', 'webcam', 'website', 'wed', 'wedding', 'wf', 'whoswho', 'wien', 'wiki', 'williamhill', 'wme', 'work', 'works', 'world', 'ws', 'wtc', 'wtf', '佛山', '集团', '在线', '한국', 'ভারত', '八卦', 'موقع', '公益', '公司', '移动', '我爱你', 'москва', 'қаз', 'онлайн', 'сайт', 'срб', '淡马锡', 'орг', '삼성', 'சிங்கப்பூர்', '商标', '商店', '商城', 'дети', 'мкд', '中文网', '中信', '中国', '中國', '谷歌', 'భారత్', 'ලංකා', 'ભારત', 'भारत', '网店', 'संगठन', '网络', 'укр', '香港', '台湾', '台灣', '手机', 'мон', 'الجزائر', 'عمان', 'ایران', 'امارات', 'بازار', 'الاردن', 'بھارت', 'المغرب', 'السعودية', 'مليسيا', 'شبكة', 'გე', '机构', '组织机构', 'ไทย', 'سورية', 'рус', 'рф', 'تونس', 'みんな', 'グーグル', '世界', 'ਭਾਰਤ', '网址', '游戏', 'vermögensberater', 'vermögensberatung', '企业', 'مصر', 'قطر', '广东', 'இலங்கை', 'இந்தியா', '新加坡', 'فلسطين', '政务', 'xxx', 'xyz', 'yachts', 'yandex', 'ye', 'yoga', 'yokohama', 'youtube', 'yt', 'za', 'zip', 'zm', 'zone', 'zuerich', 'zw']; // From https://raw.githubusercontent.com/FGRibreau/latenize/master/latinize_map.js -var LatinizeMap = {'Á': 'A','Ă': 'A','Ắ': 'A','Ặ': 'A','Ằ': 'A','Ẳ': 'A','Ẵ': 'A','Ǎ': 'A','Â': 'A','Ấ': 'A','Ậ': 'A','Ầ': 'A','Ẩ': 'A','Ẫ': 'A','Ä': 'A','Ǟ': 'A','Ȧ': 'A','Ǡ': 'A','Ạ': 'A','Ȁ': 'A','À': 'A','Ả': 'A','Ȃ': 'A','Ā': 'A','Ą': 'A','Å': 'A','Ǻ': 'A','Ḁ': 'A','Ⱥ': 'A','Ã': 'A','Ꜳ': 'AA','Æ': 'AE','Ǽ': 'AE','Ǣ': 'AE','Ꜵ': 'AO','Ꜷ': 'AU','Ꜹ': 'AV','Ꜻ': 'AV','Ꜽ': 'AY','Ḃ': 'B','Ḅ': 'B','Ɓ': 'B','Ḇ': 'B','Ƀ': 'B','Ƃ': 'B','Ć': 'C','Č': 'C','Ç': 'C','Ḉ': 'C','Ĉ': 'C','Ċ': 'C','Ƈ': 'C','Ȼ': 'C','Ď': 'D','Ḑ': 'D','Ḓ': 'D','Ḋ': 'D','Ḍ': 'D','Ɗ': 'D','Ḏ': 'D','Dz': 'D','Dž': 'D','Đ': 'D','Ƌ': 'D','DZ': 'DZ','DŽ': 'DZ','É': 'E','Ĕ': 'E','Ě': 'E','Ȩ': 'E','Ḝ': 'E','Ê': 'E','Ế': 'E','Ệ': 'E','Ề': 'E','Ể': 'E','Ễ': 'E','Ḙ': 'E','Ë': 'E','Ė': 'E','Ẹ': 'E','Ȅ': 'E','È': 'E','Ẻ': 'E','Ȇ': 'E','Ē': 'E','Ḗ': 'E','Ḕ': 'E','Ę': 'E','Ɇ': 'E','Ẽ': 'E','Ḛ': 'E','Ꝫ': 'ET','Ḟ': 'F','Ƒ': 'F','Ǵ': 'G','Ğ': 'G','Ǧ': 'G','Ģ': 'G','Ĝ': 'G','Ġ': 'G','Ɠ': 'G','Ḡ': 'G','Ǥ': 'G','Ḫ': 'H','Ȟ': 'H','Ḩ': 'H','Ĥ': 'H','Ⱨ': 'H','Ḧ': 'H','Ḣ': 'H','Ḥ': 'H','Ħ': 'H','Í': 'I','Ĭ': 'I','Ǐ': 'I','Î': 'I','Ï': 'I','Ḯ': 'I','İ': 'I','Ị': 'I','Ȉ': 'I','Ì': 'I','Ỉ': 'I','Ȋ': 'I','Ī': 'I','Į': 'I','Ɨ': 'I','Ĩ': 'I','Ḭ': 'I','Ꝺ': 'D','Ꝼ': 'F','Ᵹ': 'G','Ꞃ': 'R','Ꞅ': 'S','Ꞇ': 'T','Ꝭ': 'IS','Ĵ': 'J','Ɉ': 'J','Ḱ': 'K','Ǩ': 'K','Ķ': 'K','Ⱪ': 'K','Ꝃ': 'K','Ḳ': 'K','Ƙ': 'K','Ḵ': 'K','Ꝁ': 'K','Ꝅ': 'K','Ĺ': 'L','Ƚ': 'L','Ľ': 'L','Ļ': 'L','Ḽ': 'L','Ḷ': 'L','Ḹ': 'L','Ⱡ': 'L','Ꝉ': 'L','Ḻ': 'L','Ŀ': 'L','Ɫ': 'L','Lj': 'L','Ł': 'L','LJ': 'LJ','Ḿ': 'M','Ṁ': 'M','Ṃ': 'M','Ɱ': 'M','Ń': 'N','Ň': 'N','Ņ': 'N','Ṋ': 'N','Ṅ': 'N','Ṇ': 'N','Ǹ': 'N','Ɲ': 'N','Ṉ': 'N','Ƞ': 'N','Nj': 'N','Ñ': 'N','NJ': 'NJ','Ó': 'O','Ŏ': 'O','Ǒ': 'O','Ô': 'O','Ố': 'O','Ộ': 'O','Ồ': 'O','Ổ': 'O','Ỗ': 'O','Ö': 'O','Ȫ': 'O','Ȯ': 'O','Ȱ': 'O','Ọ': 'O','Ő': 'O','Ȍ': 'O','Ò': 'O','Ỏ': 'O','Ơ': 'O','Ớ': 'O','Ợ': 'O','Ờ': 'O','Ở': 'O','Ỡ': 'O','Ȏ': 'O','Ꝋ': 'O','Ꝍ': 'O','Ō': 'O','Ṓ': 'O','Ṑ': 'O','Ɵ': 'O','Ǫ': 'O','Ǭ': 'O','Ø': 'O','Ǿ': 'O','Õ': 'O','Ṍ': 'O','Ṏ': 'O','Ȭ': 'O','Ƣ': 'OI','Ꝏ': 'OO','Ɛ': 'E','Ɔ': 'O','Ȣ': 'OU','Ṕ': 'P','Ṗ': 'P','Ꝓ': 'P','Ƥ': 'P','Ꝕ': 'P','Ᵽ': 'P','Ꝑ': 'P','Ꝙ': 'Q','Ꝗ': 'Q','Ŕ': 'R','Ř': 'R','Ŗ': 'R','Ṙ': 'R','Ṛ': 'R','Ṝ': 'R','Ȑ': 'R','Ȓ': 'R','Ṟ': 'R','Ɍ': 'R','Ɽ': 'R','Ꜿ': 'C','Ǝ': 'E','Ś': 'S','Ṥ': 'S','Š': 'S','Ṧ': 'S','Ş': 'S','Ŝ': 'S','Ș': 'S','Ṡ': 'S','Ṣ': 'S','Ṩ': 'S','ẞ': 'SS','Ť': 'T','Ţ': 'T','Ṱ': 'T','Ț': 'T','Ⱦ': 'T','Ṫ': 'T','Ṭ': 'T','Ƭ': 'T','Ṯ': 'T','Ʈ': 'T','Ŧ': 'T','Ɐ': 'A','Ꞁ': 'L','Ɯ': 'M','Ʌ': 'V','Ꜩ': 'TZ','Ú': 'U','Ŭ': 'U','Ǔ': 'U','Û': 'U','Ṷ': 'U','Ü': 'U','Ǘ': 'U','Ǚ': 'U','Ǜ': 'U','Ǖ': 'U','Ṳ': 'U','Ụ': 'U','Ű': 'U','Ȕ': 'U','Ù': 'U','Ủ': 'U','Ư': 'U','Ứ': 'U','Ự': 'U','Ừ': 'U','Ử': 'U','Ữ': 'U','Ȗ': 'U','Ū': 'U','Ṻ': 'U','Ų': 'U','Ů': 'U','Ũ': 'U','Ṹ': 'U','Ṵ': 'U','Ꝟ': 'V','Ṿ': 'V','Ʋ': 'V','Ṽ': 'V','Ꝡ': 'VY','Ẃ': 'W','Ŵ': 'W','Ẅ': 'W','Ẇ': 'W','Ẉ': 'W','Ẁ': 'W','Ⱳ': 'W','Ẍ': 'X','Ẋ': 'X','Ý': 'Y','Ŷ': 'Y','Ÿ': 'Y','Ẏ': 'Y','Ỵ': 'Y','Ỳ': 'Y','Ƴ': 'Y','Ỷ': 'Y','Ỿ': 'Y','Ȳ': 'Y','Ɏ': 'Y','Ỹ': 'Y','Ź': 'Z','Ž': 'Z','Ẑ': 'Z','Ⱬ': 'Z','Ż': 'Z','Ẓ': 'Z','Ȥ': 'Z','Ẕ': 'Z','Ƶ': 'Z','IJ': 'IJ','Œ': 'OE','ᴀ': 'A','ᴁ': 'AE','ʙ': 'B','ᴃ': 'B','ᴄ': 'C','ᴅ': 'D','ᴇ': 'E','ꜰ': 'F','ɢ': 'G','ʛ': 'G','ʜ': 'H','ɪ': 'I','ʁ': 'R','ᴊ': 'J','ᴋ': 'K','ʟ': 'L','ᴌ': 'L','ᴍ': 'M','ɴ': 'N','ᴏ': 'O','ɶ': 'OE','ᴐ': 'O','ᴕ': 'OU','ᴘ': 'P','ʀ': 'R','ᴎ': 'N','ᴙ': 'R','ꜱ': 'S','ᴛ': 'T','ⱻ': 'E','ᴚ': 'R','ᴜ': 'U','ᴠ': 'V','ᴡ': 'W','ʏ': 'Y','ᴢ': 'Z','á': 'a','ă': 'a','ắ': 'a','ặ': 'a','ằ': 'a','ẳ': 'a','ẵ': 'a','ǎ': 'a','â': 'a','ấ': 'a','ậ': 'a','ầ': 'a','ẩ': 'a','ẫ': 'a','ä': 'a','ǟ': 'a','ȧ': 'a','ǡ': 'a','ạ': 'a','ȁ': 'a','à': 'a','ả': 'a','ȃ': 'a','ā': 'a','ą': 'a','ᶏ': 'a','ẚ': 'a','å': 'a','ǻ': 'a','ḁ': 'a','ⱥ': 'a','ã': 'a','ꜳ': 'aa','æ': 'ae','ǽ': 'ae','ǣ': 'ae','ꜵ': 'ao','ꜷ': 'au','ꜹ': 'av','ꜻ': 'av','ꜽ': 'ay','ḃ': 'b','ḅ': 'b','ɓ': 'b','ḇ': 'b','ᵬ': 'b','ᶀ': 'b','ƀ': 'b','ƃ': 'b','ɵ': 'o','ć': 'c','č': 'c','ç': 'c','ḉ': 'c','ĉ': 'c','ɕ': 'c','ċ': 'c','ƈ': 'c','ȼ': 'c','ď': 'd','ḑ': 'd','ḓ': 'd','ȡ': 'd','ḋ': 'd','ḍ': 'd','ɗ': 'd','ᶑ': 'd','ḏ': 'd','ᵭ': 'd','ᶁ': 'd','đ': 'd','ɖ': 'd','ƌ': 'd','ı': 'i','ȷ': 'j','ɟ': 'j','ʄ': 'j','dz': 'dz','dž': 'dz','é': 'e','ĕ': 'e','ě': 'e','ȩ': 'e','ḝ': 'e','ê': 'e','ế': 'e','ệ': 'e','ề': 'e','ể': 'e','ễ': 'e','ḙ': 'e','ë': 'e','ė': 'e','ẹ': 'e','ȅ': 'e','è': 'e','ẻ': 'e','ȇ': 'e','ē': 'e','ḗ': 'e','ḕ': 'e','ⱸ': 'e','ę': 'e','ᶒ': 'e','ɇ': 'e','ẽ': 'e','ḛ': 'e','ꝫ': 'et','ḟ': 'f','ƒ': 'f','ᵮ': 'f','ᶂ': 'f','ǵ': 'g','ğ': 'g','ǧ': 'g','ģ': 'g','ĝ': 'g','ġ': 'g','ɠ': 'g','ḡ': 'g','ᶃ': 'g','ǥ': 'g','ḫ': 'h','ȟ': 'h','ḩ': 'h','ĥ': 'h','ⱨ': 'h','ḧ': 'h','ḣ': 'h','ḥ': 'h','ɦ': 'h','ẖ': 'h','ħ': 'h','ƕ': 'hv','í': 'i','ĭ': 'i','ǐ': 'i','î': 'i','ï': 'i','ḯ': 'i','ị': 'i','ȉ': 'i','ì': 'i','ỉ': 'i','ȋ': 'i','ī': 'i','į': 'i','ᶖ': 'i','ɨ': 'i','ĩ': 'i','ḭ': 'i','ꝺ': 'd','ꝼ': 'f','ᵹ': 'g','ꞃ': 'r','ꞅ': 's','ꞇ': 't','ꝭ': 'is','ǰ': 'j','ĵ': 'j','ʝ': 'j','ɉ': 'j','ḱ': 'k','ǩ': 'k','ķ': 'k','ⱪ': 'k','ꝃ': 'k','ḳ': 'k','ƙ': 'k','ḵ': 'k','ᶄ': 'k','ꝁ': 'k','ꝅ': 'k','ĺ': 'l','ƚ': 'l','ɬ': 'l','ľ': 'l','ļ': 'l','ḽ': 'l','ȴ': 'l','ḷ': 'l','ḹ': 'l','ⱡ': 'l','ꝉ': 'l','ḻ': 'l','ŀ': 'l','ɫ': 'l','ᶅ': 'l','ɭ': 'l','ł': 'l','lj': 'lj','ſ': 's','ẜ': 's','ẛ': 's','ẝ': 's','ḿ': 'm','ṁ': 'm','ṃ': 'm','ɱ': 'm','ᵯ': 'm','ᶆ': 'm','ń': 'n','ň': 'n','ņ': 'n','ṋ': 'n','ȵ': 'n','ṅ': 'n','ṇ': 'n','ǹ': 'n','ɲ': 'n','ṉ': 'n','ƞ': 'n','ᵰ': 'n','ᶇ': 'n','ɳ': 'n','ñ': 'n','nj': 'nj','ó': 'o','ŏ': 'o','ǒ': 'o','ô': 'o','ố': 'o','ộ': 'o','ồ': 'o','ổ': 'o','ỗ': 'o','ö': 'o','ȫ': 'o','ȯ': 'o','ȱ': 'o','ọ': 'o','ő': 'o','ȍ': 'o','ò': 'o','ỏ': 'o','ơ': 'o','ớ': 'o','ợ': 'o','ờ': 'o','ở': 'o','ỡ': 'o','ȏ': 'o','ꝋ': 'o','ꝍ': 'o','ⱺ': 'o','ō': 'o','ṓ': 'o','ṑ': 'o','ǫ': 'o','ǭ': 'o','ø': 'o','ǿ': 'o','õ': 'o','ṍ': 'o','ṏ': 'o','ȭ': 'o','ƣ': 'oi','ꝏ': 'oo','ɛ': 'e','ᶓ': 'e','ɔ': 'o','ᶗ': 'o','ȣ': 'ou','ṕ': 'p','ṗ': 'p','ꝓ': 'p','ƥ': 'p','ᵱ': 'p','ᶈ': 'p','ꝕ': 'p','ᵽ': 'p','ꝑ': 'p','ꝙ': 'q','ʠ': 'q','ɋ': 'q','ꝗ': 'q','ŕ': 'r','ř': 'r','ŗ': 'r','ṙ': 'r','ṛ': 'r','ṝ': 'r','ȑ': 'r','ɾ': 'r','ᵳ': 'r','ȓ': 'r','ṟ': 'r','ɼ': 'r','ᵲ': 'r','ᶉ': 'r','ɍ': 'r','ɽ': 'r','ↄ': 'c','ꜿ': 'c','ɘ': 'e','ɿ': 'r','ś': 's','ṥ': 's','š': 's','ṧ': 's','ş': 's','ŝ': 's','ș': 's','ṡ': 's','ṣ': 's','ṩ': 's','ʂ': 's','ᵴ': 's','ᶊ': 's','ȿ': 's','ɡ': 'g','ß': 'ss','ᴑ': 'o','ᴓ': 'o','ᴝ': 'u','ť': 't','ţ': 't','ṱ': 't','ț': 't','ȶ': 't','ẗ': 't','ⱦ': 't','ṫ': 't','ṭ': 't','ƭ': 't','ṯ': 't','ᵵ': 't','ƫ': 't','ʈ': 't','ŧ': 't','ᵺ': 'th','ɐ': 'a','ᴂ': 'ae','ǝ': 'e','ᵷ': 'g','ɥ': 'h','ʮ': 'h','ʯ': 'h','ᴉ': 'i','ʞ': 'k','ꞁ': 'l','ɯ': 'm','ɰ': 'm','ᴔ': 'oe','ɹ': 'r','ɻ': 'r','ɺ': 'r','ⱹ': 'r','ʇ': 't','ʌ': 'v','ʍ': 'w','ʎ': 'y','ꜩ': 'tz','ú': 'u','ŭ': 'u','ǔ': 'u','û': 'u','ṷ': 'u','ü': 'u','ǘ': 'u','ǚ': 'u','ǜ': 'u','ǖ': 'u','ṳ': 'u','ụ': 'u','ű': 'u','ȕ': 'u','ù': 'u','ủ': 'u','ư': 'u','ứ': 'u','ự': 'u','ừ': 'u','ử': 'u','ữ': 'u','ȗ': 'u','ū': 'u','ṻ': 'u','ų': 'u','ᶙ': 'u','ů': 'u','ũ': 'u','ṹ': 'u','ṵ': 'u','ᵫ': 'ue','ꝸ': 'um','ⱴ': 'v','ꝟ': 'v','ṿ': 'v','ʋ': 'v','ᶌ': 'v','ⱱ': 'v','ṽ': 'v','ꝡ': 'vy','ẃ': 'w','ŵ': 'w','ẅ': 'w','ẇ': 'w','ẉ': 'w','ẁ': 'w','ⱳ': 'w','ẘ': 'w','ẍ': 'x','ẋ': 'x','ᶍ': 'x','ý': 'y','ŷ': 'y','ÿ': 'y','ẏ': 'y','ỵ': 'y','ỳ': 'y','ƴ': 'y','ỷ': 'y','ỿ': 'y','ȳ': 'y','ẙ': 'y','ɏ': 'y','ỹ': 'y','ź': 'z','ž': 'z','ẑ': 'z','ʑ': 'z','ⱬ': 'z','ż': 'z','ẓ': 'z','ȥ': 'z','ẕ': 'z','ᵶ': 'z','ᶎ': 'z','ʐ': 'z','ƶ': 'z','ɀ': 'z','ff': 'ff','ffi': 'ffi','ffl': 'ffl','fi': 'fi','fl': 'fl','ij': 'ij','œ': 'oe','st': 'st','ₐ': 'a','ₑ': 'e','ᵢ': 'i','ⱼ': 'j','ₒ': 'o','ᵣ': 'r','ᵤ': 'u','ᵥ': 'v','ₓ': 'x','Ё': 'YO', 'Й': 'I', 'Ц': 'TS', 'У': 'U', 'К': 'K', 'Е': 'E', 'Н': 'N', 'Г': 'G', 'Ш': 'SH', 'Щ': 'SCH', 'З': 'Z', 'Х': 'H', 'Ъ': '', 'ё': 'yo', 'й': 'i', 'ц': 'ts', 'у': 'u', 'к': 'k', 'е': 'e', 'н': 'n', 'г': 'g', 'ш': 'sh', 'щ': 'sch', 'з': 'z', 'х': 'h', 'ъ': '', 'Ф': 'F', 'Ы': 'I', 'В': 'V', 'А': 'A', 'П': 'P', 'Р': 'R', 'О': 'O', 'Л': 'L', 'Д': 'D', 'Ж': 'ZH', 'Э': 'E', 'ф': 'f', 'ы': 'i', 'в': 'v', 'а': 'a', 'п': 'p', 'р': 'r', 'о': 'o', 'л': 'l', 'д': 'd', 'ж': 'zh', 'э': 'e', 'Я': 'Ya', 'Ч': 'CH', 'С': 'S', 'М': 'M', 'И': 'I', 'Т': 'T', 'Ь': '', 'Б': 'B', 'Ю': 'YU', 'я': 'ya', 'ч': 'ch', 'с': 's', 'м': 'm', 'и': 'i', 'т': 't', 'ь': '', 'б': 'b', 'ю': 'yu'} +var LatinizeMap: {[k: string]: string} = {'Á': 'A','Ă': 'A','Ắ': 'A','Ặ': 'A','Ằ': 'A','Ẳ': 'A','Ẵ': 'A','Ǎ': 'A','Â': 'A','Ấ': 'A','Ậ': 'A','Ầ': 'A','Ẩ': 'A','Ẫ': 'A','Ä': 'A','Ǟ': 'A','Ȧ': 'A','Ǡ': 'A','Ạ': 'A','Ȁ': 'A','À': 'A','Ả': 'A','Ȃ': 'A','Ā': 'A','Ą': 'A','Å': 'A','Ǻ': 'A','Ḁ': 'A','Ⱥ': 'A','Ã': 'A','Ꜳ': 'AA','Æ': 'AE','Ǽ': 'AE','Ǣ': 'AE','Ꜵ': 'AO','Ꜷ': 'AU','Ꜹ': 'AV','Ꜻ': 'AV','Ꜽ': 'AY','Ḃ': 'B','Ḅ': 'B','Ɓ': 'B','Ḇ': 'B','Ƀ': 'B','Ƃ': 'B','Ć': 'C','Č': 'C','Ç': 'C','Ḉ': 'C','Ĉ': 'C','Ċ': 'C','Ƈ': 'C','Ȼ': 'C','Ď': 'D','Ḑ': 'D','Ḓ': 'D','Ḋ': 'D','Ḍ': 'D','Ɗ': 'D','Ḏ': 'D','Dz': 'D','Dž': 'D','Đ': 'D','Ƌ': 'D','DZ': 'DZ','DŽ': 'DZ','É': 'E','Ĕ': 'E','Ě': 'E','Ȩ': 'E','Ḝ': 'E','Ê': 'E','Ế': 'E','Ệ': 'E','Ề': 'E','Ể': 'E','Ễ': 'E','Ḙ': 'E','Ë': 'E','Ė': 'E','Ẹ': 'E','Ȅ': 'E','È': 'E','Ẻ': 'E','Ȇ': 'E','Ē': 'E','Ḗ': 'E','Ḕ': 'E','Ę': 'E','Ɇ': 'E','Ẽ': 'E','Ḛ': 'E','Ꝫ': 'ET','Ḟ': 'F','Ƒ': 'F','Ǵ': 'G','Ğ': 'G','Ǧ': 'G','Ģ': 'G','Ĝ': 'G','Ġ': 'G','Ɠ': 'G','Ḡ': 'G','Ǥ': 'G','Ḫ': 'H','Ȟ': 'H','Ḩ': 'H','Ĥ': 'H','Ⱨ': 'H','Ḧ': 'H','Ḣ': 'H','Ḥ': 'H','Ħ': 'H','Í': 'I','Ĭ': 'I','Ǐ': 'I','Î': 'I','Ï': 'I','Ḯ': 'I','İ': 'I','Ị': 'I','Ȉ': 'I','Ì': 'I','Ỉ': 'I','Ȋ': 'I','Ī': 'I','Į': 'I','Ɨ': 'I','Ĩ': 'I','Ḭ': 'I','Ꝺ': 'D','Ꝼ': 'F','Ᵹ': 'G','Ꞃ': 'R','Ꞅ': 'S','Ꞇ': 'T','Ꝭ': 'IS','Ĵ': 'J','Ɉ': 'J','Ḱ': 'K','Ǩ': 'K','Ķ': 'K','Ⱪ': 'K','Ꝃ': 'K','Ḳ': 'K','Ƙ': 'K','Ḵ': 'K','Ꝁ': 'K','Ꝅ': 'K','Ĺ': 'L','Ƚ': 'L','Ľ': 'L','Ļ': 'L','Ḽ': 'L','Ḷ': 'L','Ḹ': 'L','Ⱡ': 'L','Ꝉ': 'L','Ḻ': 'L','Ŀ': 'L','Ɫ': 'L','Lj': 'L','Ł': 'L','LJ': 'LJ','Ḿ': 'M','Ṁ': 'M','Ṃ': 'M','Ɱ': 'M','Ń': 'N','Ň': 'N','Ņ': 'N','Ṋ': 'N','Ṅ': 'N','Ṇ': 'N','Ǹ': 'N','Ɲ': 'N','Ṉ': 'N','Ƞ': 'N','Nj': 'N','Ñ': 'N','NJ': 'NJ','Ó': 'O','Ŏ': 'O','Ǒ': 'O','Ô': 'O','Ố': 'O','Ộ': 'O','Ồ': 'O','Ổ': 'O','Ỗ': 'O','Ö': 'O','Ȫ': 'O','Ȯ': 'O','Ȱ': 'O','Ọ': 'O','Ő': 'O','Ȍ': 'O','Ò': 'O','Ỏ': 'O','Ơ': 'O','Ớ': 'O','Ợ': 'O','Ờ': 'O','Ở': 'O','Ỡ': 'O','Ȏ': 'O','Ꝋ': 'O','Ꝍ': 'O','Ō': 'O','Ṓ': 'O','Ṑ': 'O','Ɵ': 'O','Ǫ': 'O','Ǭ': 'O','Ø': 'O','Ǿ': 'O','Õ': 'O','Ṍ': 'O','Ṏ': 'O','Ȭ': 'O','Ƣ': 'OI','Ꝏ': 'OO','Ɛ': 'E','Ɔ': 'O','Ȣ': 'OU','Ṕ': 'P','Ṗ': 'P','Ꝓ': 'P','Ƥ': 'P','Ꝕ': 'P','Ᵽ': 'P','Ꝑ': 'P','Ꝙ': 'Q','Ꝗ': 'Q','Ŕ': 'R','Ř': 'R','Ŗ': 'R','Ṙ': 'R','Ṛ': 'R','Ṝ': 'R','Ȑ': 'R','Ȓ': 'R','Ṟ': 'R','Ɍ': 'R','Ɽ': 'R','Ꜿ': 'C','Ǝ': 'E','Ś': 'S','Ṥ': 'S','Š': 'S','Ṧ': 'S','Ş': 'S','Ŝ': 'S','Ș': 'S','Ṡ': 'S','Ṣ': 'S','Ṩ': 'S','ẞ': 'SS','Ť': 'T','Ţ': 'T','Ṱ': 'T','Ț': 'T','Ⱦ': 'T','Ṫ': 'T','Ṭ': 'T','Ƭ': 'T','Ṯ': 'T','Ʈ': 'T','Ŧ': 'T','Ɐ': 'A','Ꞁ': 'L','Ɯ': 'M','Ʌ': 'V','Ꜩ': 'TZ','Ú': 'U','Ŭ': 'U','Ǔ': 'U','Û': 'U','Ṷ': 'U','Ü': 'U','Ǘ': 'U','Ǚ': 'U','Ǜ': 'U','Ǖ': 'U','Ṳ': 'U','Ụ': 'U','Ű': 'U','Ȕ': 'U','Ù': 'U','Ủ': 'U','Ư': 'U','Ứ': 'U','Ự': 'U','Ừ': 'U','Ử': 'U','Ữ': 'U','Ȗ': 'U','Ū': 'U','Ṻ': 'U','Ų': 'U','Ů': 'U','Ũ': 'U','Ṹ': 'U','Ṵ': 'U','Ꝟ': 'V','Ṿ': 'V','Ʋ': 'V','Ṽ': 'V','Ꝡ': 'VY','Ẃ': 'W','Ŵ': 'W','Ẅ': 'W','Ẇ': 'W','Ẉ': 'W','Ẁ': 'W','Ⱳ': 'W','Ẍ': 'X','Ẋ': 'X','Ý': 'Y','Ŷ': 'Y','Ÿ': 'Y','Ẏ': 'Y','Ỵ': 'Y','Ỳ': 'Y','Ƴ': 'Y','Ỷ': 'Y','Ỿ': 'Y','Ȳ': 'Y','Ɏ': 'Y','Ỹ': 'Y','Ź': 'Z','Ž': 'Z','Ẑ': 'Z','Ⱬ': 'Z','Ż': 'Z','Ẓ': 'Z','Ȥ': 'Z','Ẕ': 'Z','Ƶ': 'Z','IJ': 'IJ','Œ': 'OE','ᴀ': 'A','ᴁ': 'AE','ʙ': 'B','ᴃ': 'B','ᴄ': 'C','ᴅ': 'D','ᴇ': 'E','ꜰ': 'F','ɢ': 'G','ʛ': 'G','ʜ': 'H','ɪ': 'I','ʁ': 'R','ᴊ': 'J','ᴋ': 'K','ʟ': 'L','ᴌ': 'L','ᴍ': 'M','ɴ': 'N','ᴏ': 'O','ɶ': 'OE','ᴐ': 'O','ᴕ': 'OU','ᴘ': 'P','ʀ': 'R','ᴎ': 'N','ᴙ': 'R','ꜱ': 'S','ᴛ': 'T','ⱻ': 'E','ᴚ': 'R','ᴜ': 'U','ᴠ': 'V','ᴡ': 'W','ʏ': 'Y','ᴢ': 'Z','á': 'a','ă': 'a','ắ': 'a','ặ': 'a','ằ': 'a','ẳ': 'a','ẵ': 'a','ǎ': 'a','â': 'a','ấ': 'a','ậ': 'a','ầ': 'a','ẩ': 'a','ẫ': 'a','ä': 'a','ǟ': 'a','ȧ': 'a','ǡ': 'a','ạ': 'a','ȁ': 'a','à': 'a','ả': 'a','ȃ': 'a','ā': 'a','ą': 'a','ᶏ': 'a','ẚ': 'a','å': 'a','ǻ': 'a','ḁ': 'a','ⱥ': 'a','ã': 'a','ꜳ': 'aa','æ': 'ae','ǽ': 'ae','ǣ': 'ae','ꜵ': 'ao','ꜷ': 'au','ꜹ': 'av','ꜻ': 'av','ꜽ': 'ay','ḃ': 'b','ḅ': 'b','ɓ': 'b','ḇ': 'b','ᵬ': 'b','ᶀ': 'b','ƀ': 'b','ƃ': 'b','ɵ': 'o','ć': 'c','č': 'c','ç': 'c','ḉ': 'c','ĉ': 'c','ɕ': 'c','ċ': 'c','ƈ': 'c','ȼ': 'c','ď': 'd','ḑ': 'd','ḓ': 'd','ȡ': 'd','ḋ': 'd','ḍ': 'd','ɗ': 'd','ᶑ': 'd','ḏ': 'd','ᵭ': 'd','ᶁ': 'd','đ': 'd','ɖ': 'd','ƌ': 'd','ı': 'i','ȷ': 'j','ɟ': 'j','ʄ': 'j','dz': 'dz','dž': 'dz','é': 'e','ĕ': 'e','ě': 'e','ȩ': 'e','ḝ': 'e','ê': 'e','ế': 'e','ệ': 'e','ề': 'e','ể': 'e','ễ': 'e','ḙ': 'e','ë': 'e','ė': 'e','ẹ': 'e','ȅ': 'e','è': 'e','ẻ': 'e','ȇ': 'e','ē': 'e','ḗ': 'e','ḕ': 'e','ⱸ': 'e','ę': 'e','ᶒ': 'e','ɇ': 'e','ẽ': 'e','ḛ': 'e','ꝫ': 'et','ḟ': 'f','ƒ': 'f','ᵮ': 'f','ᶂ': 'f','ǵ': 'g','ğ': 'g','ǧ': 'g','ģ': 'g','ĝ': 'g','ġ': 'g','ɠ': 'g','ḡ': 'g','ᶃ': 'g','ǥ': 'g','ḫ': 'h','ȟ': 'h','ḩ': 'h','ĥ': 'h','ⱨ': 'h','ḧ': 'h','ḣ': 'h','ḥ': 'h','ɦ': 'h','ẖ': 'h','ħ': 'h','ƕ': 'hv','í': 'i','ĭ': 'i','ǐ': 'i','î': 'i','ï': 'i','ḯ': 'i','ị': 'i','ȉ': 'i','ì': 'i','ỉ': 'i','ȋ': 'i','ī': 'i','į': 'i','ᶖ': 'i','ɨ': 'i','ĩ': 'i','ḭ': 'i','ꝺ': 'd','ꝼ': 'f','ᵹ': 'g','ꞃ': 'r','ꞅ': 's','ꞇ': 't','ꝭ': 'is','ǰ': 'j','ĵ': 'j','ʝ': 'j','ɉ': 'j','ḱ': 'k','ǩ': 'k','ķ': 'k','ⱪ': 'k','ꝃ': 'k','ḳ': 'k','ƙ': 'k','ḵ': 'k','ᶄ': 'k','ꝁ': 'k','ꝅ': 'k','ĺ': 'l','ƚ': 'l','ɬ': 'l','ľ': 'l','ļ': 'l','ḽ': 'l','ȴ': 'l','ḷ': 'l','ḹ': 'l','ⱡ': 'l','ꝉ': 'l','ḻ': 'l','ŀ': 'l','ɫ': 'l','ᶅ': 'l','ɭ': 'l','ł': 'l','lj': 'lj','ſ': 's','ẜ': 's','ẛ': 's','ẝ': 's','ḿ': 'm','ṁ': 'm','ṃ': 'm','ɱ': 'm','ᵯ': 'm','ᶆ': 'm','ń': 'n','ň': 'n','ņ': 'n','ṋ': 'n','ȵ': 'n','ṅ': 'n','ṇ': 'n','ǹ': 'n','ɲ': 'n','ṉ': 'n','ƞ': 'n','ᵰ': 'n','ᶇ': 'n','ɳ': 'n','ñ': 'n','nj': 'nj','ó': 'o','ŏ': 'o','ǒ': 'o','ô': 'o','ố': 'o','ộ': 'o','ồ': 'o','ổ': 'o','ỗ': 'o','ö': 'o','ȫ': 'o','ȯ': 'o','ȱ': 'o','ọ': 'o','ő': 'o','ȍ': 'o','ò': 'o','ỏ': 'o','ơ': 'o','ớ': 'o','ợ': 'o','ờ': 'o','ở': 'o','ỡ': 'o','ȏ': 'o','ꝋ': 'o','ꝍ': 'o','ⱺ': 'o','ō': 'o','ṓ': 'o','ṑ': 'o','ǫ': 'o','ǭ': 'o','ø': 'o','ǿ': 'o','õ': 'o','ṍ': 'o','ṏ': 'o','ȭ': 'o','ƣ': 'oi','ꝏ': 'oo','ɛ': 'e','ᶓ': 'e','ɔ': 'o','ᶗ': 'o','ȣ': 'ou','ṕ': 'p','ṗ': 'p','ꝓ': 'p','ƥ': 'p','ᵱ': 'p','ᶈ': 'p','ꝕ': 'p','ᵽ': 'p','ꝑ': 'p','ꝙ': 'q','ʠ': 'q','ɋ': 'q','ꝗ': 'q','ŕ': 'r','ř': 'r','ŗ': 'r','ṙ': 'r','ṛ': 'r','ṝ': 'r','ȑ': 'r','ɾ': 'r','ᵳ': 'r','ȓ': 'r','ṟ': 'r','ɼ': 'r','ᵲ': 'r','ᶉ': 'r','ɍ': 'r','ɽ': 'r','ↄ': 'c','ꜿ': 'c','ɘ': 'e','ɿ': 'r','ś': 's','ṥ': 's','š': 's','ṧ': 's','ş': 's','ŝ': 's','ș': 's','ṡ': 's','ṣ': 's','ṩ': 's','ʂ': 's','ᵴ': 's','ᶊ': 's','ȿ': 's','ɡ': 'g','ß': 'ss','ᴑ': 'o','ᴓ': 'o','ᴝ': 'u','ť': 't','ţ': 't','ṱ': 't','ț': 't','ȶ': 't','ẗ': 't','ⱦ': 't','ṫ': 't','ṭ': 't','ƭ': 't','ṯ': 't','ᵵ': 't','ƫ': 't','ʈ': 't','ŧ': 't','ᵺ': 'th','ɐ': 'a','ᴂ': 'ae','ǝ': 'e','ᵷ': 'g','ɥ': 'h','ʮ': 'h','ʯ': 'h','ᴉ': 'i','ʞ': 'k','ꞁ': 'l','ɯ': 'm','ɰ': 'm','ᴔ': 'oe','ɹ': 'r','ɻ': 'r','ɺ': 'r','ⱹ': 'r','ʇ': 't','ʌ': 'v','ʍ': 'w','ʎ': 'y','ꜩ': 'tz','ú': 'u','ŭ': 'u','ǔ': 'u','û': 'u','ṷ': 'u','ü': 'u','ǘ': 'u','ǚ': 'u','ǜ': 'u','ǖ': 'u','ṳ': 'u','ụ': 'u','ű': 'u','ȕ': 'u','ù': 'u','ủ': 'u','ư': 'u','ứ': 'u','ự': 'u','ừ': 'u','ử': 'u','ữ': 'u','ȗ': 'u','ū': 'u','ṻ': 'u','ų': 'u','ᶙ': 'u','ů': 'u','ũ': 'u','ṹ': 'u','ṵ': 'u','ᵫ': 'ue','ꝸ': 'um','ⱴ': 'v','ꝟ': 'v','ṿ': 'v','ʋ': 'v','ᶌ': 'v','ⱱ': 'v','ṽ': 'v','ꝡ': 'vy','ẃ': 'w','ŵ': 'w','ẅ': 'w','ẇ': 'w','ẉ': 'w','ẁ': 'w','ⱳ': 'w','ẘ': 'w','ẍ': 'x','ẋ': 'x','ᶍ': 'x','ý': 'y','ŷ': 'y','ÿ': 'y','ẏ': 'y','ỵ': 'y','ỳ': 'y','ƴ': 'y','ỷ': 'y','ỿ': 'y','ȳ': 'y','ẙ': 'y','ɏ': 'y','ỹ': 'y','ź': 'z','ž': 'z','ẑ': 'z','ʑ': 'z','ⱬ': 'z','ż': 'z','ẓ': 'z','ȥ': 'z','ẕ': 'z','ᵶ': 'z','ᶎ': 'z','ʐ': 'z','ƶ': 'z','ɀ': 'z','ff': 'ff','ffi': 'ffi','ffl': 'ffl','fi': 'fi','fl': 'fl','ij': 'ij','œ': 'oe','st': 'st','ₐ': 'a','ₑ': 'e','ᵢ': 'i','ⱼ': 'j','ₒ': 'o','ᵣ': 'r','ᵤ': 'u','ᵥ': 'v','ₓ': 'x','Ё': 'YO', 'Й': 'I', 'Ц': 'TS', 'У': 'U', 'К': 'K', 'Е': 'E', 'Н': 'N', 'Г': 'G', 'Ш': 'SH', 'Щ': 'SCH', 'З': 'Z', 'Х': 'H', 'Ъ': '', 'ё': 'yo', 'й': 'i', 'ц': 'ts', 'у': 'u', 'к': 'k', 'е': 'e', 'н': 'n', 'г': 'g', 'ш': 'sh', 'щ': 'sch', 'з': 'z', 'х': 'h', 'ъ': '', 'Ф': 'F', 'Ы': 'I', 'В': 'V', 'А': 'A', 'П': 'P', 'Р': 'R', 'О': 'O', 'Л': 'L', 'Д': 'D', 'Ж': 'ZH', 'Э': 'E', 'ф': 'f', 'ы': 'i', 'в': 'v', 'а': 'a', 'п': 'p', 'р': 'r', 'о': 'o', 'л': 'l', 'д': 'd', 'ж': 'zh', 'э': 'e', 'Я': 'Ya', 'Ч': 'CH', 'С': 'S', 'М': 'M', 'И': 'I', 'Т': 'T', 'Ь': '', 'Б': 'B', 'Ю': 'YU', 'я': 'ya', 'ч': 'ch', 'с': 's', 'м': 'm', 'и': 'i', 'т': 't', 'ь': '', 'б': 'b', 'ю': 'yu'} const Config = { Emoji, diff --git a/src/lib/lottieLoader copy.ts b/src/lib/lottieLoader copy.ts new file mode 100644 index 00000000..399ca602 --- /dev/null +++ b/src/lib/lottieLoader copy.ts @@ -0,0 +1,461 @@ +import { isInDOM } from "./utils"; + +let convert = (value: number) => { + return Math.round(Math.min(Math.max(value, 0), 1) * 255); +}; + +type RLottiePlayerListeners = 'firstFrame' | 'enterFrame'; + +export class RLottiePlayer { + public static reqId = 0; + + public reqId = 0; + public curFrame: number; + public worker: QueryableWorker; + public el: HTMLElement; + public width: number; + public height: number; + + public listeners: Partial<{ + [k in RLottiePlayerListeners]: (res: any) => void + }> = {}; + public listenerResults: Partial<{ + [k in RLottiePlayerListeners]: any + }> = {}; + + public canvas: HTMLCanvasElement; + public context: CanvasRenderingContext2D; + + public paused = false; + public direction = 1; + public speed = 1; + public autoplay = true; + + constructor({el, width, height, worker}: { + el: HTMLElement, + width: number, + height: number, + worker: QueryableWorker + }) { + this.reqId = ++RLottiePlayer['reqId']; + this.el = el; + this.width = width; + this.height = height; + this.worker = worker; + } + + public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void) { + if(this.listenerResults.hasOwnProperty(name)) return Promise.resolve(this.listenerResults[name]); + this.listeners[name] = callback; + } + + public setListenerResult(name: RLottiePlayerListeners, value?: any) { + this.listenerResults[name] = value; + if(this.listeners[name]) { + this.listeners[name](value); + } + } + + private sendQuery(methodName: string, ...args: any[]) { + this.worker.sendQuery(methodName, this.reqId, ...args); + } + + public loadFromData(json: any) { + this.sendQuery('loadFromData', json, this.width, this.height, { + paused: this.paused, + direction: this.direction, + speed: this.speed + }); + } + + public play() { + this.sendQuery('play'); + this.paused = false; + } + + public pause() { + this.sendQuery('pause'); + this.paused = true; + } + + public stop() { + this.sendQuery('stop'); + this.paused = true; + } + + public restart() { + this.sendQuery('restart'); + } + + public setSpeed(speed: number) { + this.sendQuery('setSpeed', speed); + } + + public setDirection(direction: number) { + this.direction = direction; + this.sendQuery('setDirection', direction); + } + + public destroy() { + lottieLoader.onDestroy(this.reqId); + this.sendQuery('destroy'); + } + + private attachPlayer() { + this.canvas = document.createElement('canvas'); + this.canvas.width = this.width; + this.canvas.height = this.height; + + //this.el.appendChild(this.canvas); + this.context = this.canvas.getContext('2d'); + } + + public renderFrame(frame: Uint8ClampedArray, frameNo: number) { + if(!this.listenerResults.hasOwnProperty('firstFrame')) { + this.attachPlayer(); + this.el.appendChild(this.canvas); + + this.setListenerResult('firstFrame'); + } + + this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0); + this.setListenerResult('enterFrame', frameNo); + } +} + +class QueryableWorker { + private worker: Worker; + private listeners: {[name: string]: (...args: any[]) => void} = {}; + + constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) { + this.worker = new Worker(url); + if(onError) { + this.worker.onerror = onError; + } + + this.worker.onmessage = (event) => { + if(event.data instanceof Object && + event.data.hasOwnProperty('queryMethodListener') && + event.data.hasOwnProperty('queryMethodArguments')) { + this.listeners[event.data.queryMethodListener].apply(this, event.data.queryMethodArguments); + } else { + this.defaultListener.call(this, event.data); + } + } + } + + public postMessage(message: any) { + this.worker.postMessage(message); + } + + public terminate() { + this.worker.terminate(); + } + + public addListener(name: string, listener: (...args: any[]) => void) { + this.listeners[name] = listener; + } + + public removeListener(name: string) { + delete this.listeners[name]; + } + + public sendQuery(queryMethod: string, ...args: any[]) { + this.worker.postMessage({ + 'queryMethod': queryMethod, + 'queryMethodArguments': args + }); + } +} + +class LottieLoader { + public loadPromise: Promise; + public loaded = false; + + private static COLORREPLACEMENTS = [ + [ + [0xf77e41, 0xca907a], + [0xffb139, 0xedc5a5], + [0xffd140, 0xf7e3c3], + [0xffdf79, 0xfbefd6], + ], + + [ + [0xf77e41, 0xaa7c60], + [0xffb139, 0xc8a987], + [0xffd140, 0xddc89f], + [0xffdf79, 0xe6d6b2], + ], + + [ + [0xf77e41, 0x8c6148], + [0xffb139, 0xad8562], + [0xffd140, 0xc49e76], + [0xffdf79, 0xd4b188], + ], + + [ + [0xf77e41, 0x6e3c2c], + [0xffb139, 0x925a34], + [0xffd140, 0xa16e46], + [0xffdf79, 0xac7a52], + ] + ]; + + private workersLimit = 4; + private players: {[reqId: number]: RLottiePlayer} = {}; + private byGroups: {[group: string]: RLottiePlayer[]} = {}; + + private workers: QueryableWorker[] = []; + private curWorkerNum = 0; + + private observer: IntersectionObserver; + private visible: Set = new Set(); + + private debug = true; + + constructor() { + this.observer = new IntersectionObserver((entries) => { + for(const entry of entries) { + const target = entry.target; + + for(const group in this.byGroups) { + const player = this.byGroups[group].find(p => p.el == target); + if(player) { + if(entry.isIntersecting) { + this.visible.add(player); + + if(player.paused) { + player.play(); + } + } else { + this.visible.delete(player); + + if(!player.paused) { + player.pause(); + } + } + + break; + } + } + } + }); + } + + public loadLottieWorkers() { + if(this.loadPromise) return this.loadPromise; + + const onFrame = this.onFrame.bind(this); + + return this.loadPromise = new Promise((resolve, reject) => { + let remain = this.workersLimit; + for(let i = 0; i < this.workersLimit; ++i) { + const worker = this.workers[i] = new QueryableWorker('rlottie.worker.js'); + + worker.addListener('ready', () => { + console.log('worker #' + i + ' ready'); + + worker.addListener('frame', onFrame); + + --remain; + if(!remain) { + console.log('workers ready'); + resolve(); + this.loaded = true; + } + }); + } + }); + } + + private applyReplacements(object: any, toneIndex: number) { + const replacements = LottieLoader.COLORREPLACEMENTS[toneIndex - 2]; + + const iterateIt = (it: any) => { + for(let smth of it) { + switch(smth.ty) { + case 'st': + case 'fl': + let k = smth.c.k; + let color = convert(k[2]) | (convert(k[1]) << 8) | (convert(k[0]) << 16); + + let foundReplacement = replacements.find(p => p[0] == color); + if(foundReplacement) { + k[0] = ((foundReplacement[1] >> 16) & 255) / 255; + k[1] = ((foundReplacement[1] >> 8) & 255) / 255; + k[2] = (foundReplacement[1] & 255) / 255; + } + + //console.log('foundReplacement!', foundReplacement, color.toString(16), k); + break; + } + + if(smth.hasOwnProperty('it')) { + iterateIt(smth.it); + } + } + }; + + for(let layer of object.layers) { + if(!layer.shapes) continue; + + for(let shape of layer.shapes) { + iterateIt(shape.it); + } + } + } + + public async loadAnimationWorker(params: { + container: HTMLElement, + autoplay?: boolean, + animationData: any, + loop?: boolean, + renderer?: string, + width?: number, + height?: number + }, group = '', toneIndex = -1) { + //params.autoplay = false; + + if(toneIndex >= 1 && toneIndex <= 5) { + this.applyReplacements(params.animationData, toneIndex); + } + + if(!this.loaded) { + await this.loadLottieWorkers(); + } + + this.observer.observe(params.container); + + const width = params.width || parseInt(params.container.style.width); + const height = params.height || parseInt(params.container.style.height); + + const player = this.initPlayer(params.container, params.animationData, width, height); + for(let i in params) { + // @ts-ignore + if(player.hasOwnProperty(i)) { + // @ts-ignore + player[i] = params[i]; + } + } + + (this.byGroups[group] ?? (this.byGroups[group] = [])).push(player); + + return player; + } + + public checkAnimations(blurred?: boolean, group?: string, destroy = false) { + const groups = group && false ? [group] : Object.keys(this.byGroups); + + if(group && !this.byGroups[group]) { + console.warn('no animation group:', group); + this.byGroups[group] = []; + //return; + } + + for(const group of groups) { + const animations = this.byGroups[group]; + + const length = animations.length; + for(let i = length - 1; i >= 0; --i) { + const player = animations[i]; + + if(destroy || (!isInDOM(player.el) && player.listenerResults.hasOwnProperty('firstFrame'))) { + //console.log('destroy animation'); + player.destroy(); + continue; + } + + if(blurred) { + if(!player.paused) { + this.debug && console.log('pause animation', player); + player.pause(); + } + } else if(player.paused && this.visible.has(player)) { + this.debug && console.log('play animation', player); + player.play(); + } + + /* if(canvas) { + let c = container.firstElementChild as HTMLCanvasElement; + if(!c) { + console.warn('no canvas element for check!', container, animations[i]); + continue; + } + + if(!c.height && !c.width && isElementInViewport(container)) { + //console.log('lottie need resize'); + animation.resize(); + } + } */ + + //if(!autoplay) continue; + + /* if(blurred || !isElementInViewport(container)) { + if(!paused) { + this.debug && console.log('pause animation', isElementInViewport(container), container); + animation.pause(); + animations[i].paused = true; + } + } else if(paused) { + this.debug && console.log('play animation', container); + animation.play(); + animations[i].paused = false; + } */ + } + } + } + + private onFrame(reqId: number, frameNo: number, frame: Uint8ClampedArray, width: number, height: number) { + const rlPlayer = this.players[reqId]; + if(!rlPlayer) { + this.debug && console.warn('onFrame on destroyed player:', reqId, frameNo); + return; + } + + rlPlayer.renderFrame(frame, frameNo); + } + + public onDestroy(reqId: number) { + let player = this.players[reqId]; + for(let group in this.byGroups) { + this.byGroups[group].findAndSplice(p => p == player); + } + + delete this.players[player.reqId]; + this.observer.unobserve(player.el); + this.visible.delete(player); + } + + public destroyWorkers() { + this.workers.forEach((worker, idx) => { + worker.terminate(); + console.log('worker #' + idx + ' terminated'); + }); + + console.log('workers destroyed'); + this.workers.length = 0; + } + + private initPlayer(el: HTMLElement, json: any, width: number, height: number) { + const rlPlayer = new RLottiePlayer({ + el, + width, + height, + worker: this.workers[this.curWorkerNum++] + }); + + this.players[rlPlayer.reqId] = rlPlayer; + if(this.curWorkerNum >= this.workers.length) { + this.curWorkerNum = 0; + } + + rlPlayer.loadFromData(json); + + return rlPlayer; + } +} + +const lottieLoader = new LottieLoader(); +(window as any).LottieLoader = lottieLoader; +export default lottieLoader; diff --git a/src/lib/lottieLoader.ts b/src/lib/lottieLoader.ts index 97b48f66..6d14f013 100644 --- a/src/lib/lottieLoader.ts +++ b/src/lib/lottieLoader.ts @@ -1,25 +1,253 @@ -//import { isInDOM } from "./utils"; -import LottiePlayer, { AnimationConfigWithPath, AnimationConfigWithData, AnimationItem } from "lottie-web/build/player/lottie.d"; +import { isInDOM } from "./utils"; let convert = (value: number) => { return Math.round(Math.min(Math.max(value, 0), 1) * 255); }; +type RLottiePlayerListeners = 'enterFrame' | 'ready'; + +export class RLottiePlayer { + public static reqId = 0; + + public reqId = 0; + public curFrame: number; + public frameCount: number; + public fps: number; + + public worker: QueryableWorker; + + public width: number; + public height: number; + + public listeners: Partial<{ + [k in RLottiePlayerListeners]: (res: any) => void + }> = {}; + public listenerResults: Partial<{ + [k in RLottiePlayerListeners]: any + }> = {}; + + public el: HTMLElement; + public canvas: HTMLCanvasElement; + public context: CanvasRenderingContext2D; + + public paused = true; + public direction = 1; + public speed = 1; + public autoplay = true; + public loop = true; + + private frInterval: number; + private frThen: number; + private rafId: number; + + private playedTimes = 0; + + constructor({el, width, height, worker}: { + el: HTMLElement, + width: number, + height: number, + worker: QueryableWorker + }) { + this.reqId = ++RLottiePlayer['reqId']; + this.el = el; + this.width = width; + this.height = height; + this.worker = worker; + + this.canvas = document.createElement('canvas'); + this.canvas.width = this.width; + this.canvas.height = this.height; + this.context = this.canvas.getContext('2d'); + } + + public addListener(name: RLottiePlayerListeners, callback: (res?: any) => void) { + if(this.listenerResults.hasOwnProperty(name)) return Promise.resolve(this.listenerResults[name]); + this.listeners[name] = callback; + } + + public setListenerResult(name: RLottiePlayerListeners, value?: any) { + this.listenerResults[name] = value; + if(this.listeners[name]) { + this.listeners[name](value); + } + } + + public sendQuery(methodName: string, ...args: any[]) { + this.worker.sendQuery(methodName, this.reqId, ...args); + } + + public loadFromData(json: any) { + this.sendQuery('loadFromData', json, this.width, this.height, { + paused: this.paused, + direction: this.direction, + speed: this.speed + }); + } + + public play() { + if(!this.paused) return; + + this.paused = false; + this.setMainLoop(); + } + + public pause() { + if(this.paused) return; + + this.paused = true; + window.cancelAnimationFrame(this.rafId); + } + + public stop() { + this.pause(); + + this.curFrame = this.direction == 1 ? 0 : this.frameCount; + this.sendQuery('renderFrame', this.curFrame); + } + + public restart() { + this.stop(); + this.play(); + } + + public setSpeed(speed: number) { + this.speed = speed; + + if(!this.paused) { + this.setMainLoop(); + } + } + + public setDirection(direction: number) { + this.direction = direction; + + if(!this.paused) { + this.setMainLoop(); + } + } + + public destroy() { + lottieLoader.onDestroy(this.reqId); + this.pause(); + this.sendQuery('destroy'); + } + + public renderFrame(frame: Uint8ClampedArray, frameNo: number) { + this.context.putImageData(new ImageData(frame, this.width, this.height), 0, 0); + this.setListenerResult('enterFrame', frameNo); + } + + private mainLoop(method: RLottiePlayer['mainLoopForwards'] | RLottiePlayer['mainLoopBackwards']) { + let r = () => { + if(this.paused) { + return; + } + + const now = Date.now(), delta = now - this.frThen; + if(delta > this.frInterval) { + this.frThen = now - (delta % this.frInterval); + + const canContinue = method(); + if(!canContinue && !this.loop && this.autoplay) { + this.autoplay = false; + } + } + + this.rafId = window.requestAnimationFrame(r); + }; + + //this.rafId = window.requestAnimationFrame(r); + r(); + } + + private mainLoopForwards() { + this.sendQuery('renderFrame', this.curFrame++); + if(this.curFrame >= this.frameCount) { + this.playedTimes++; + + if(!this.loop) return false; + + this.curFrame = 0; + } + + return true; + }; + + private mainLoopBackwards() { + this.sendQuery('renderFrame', this.curFrame--); + if(this.curFrame < 0) { + this.playedTimes++; + + if(!this.loop) return false; + + this.curFrame = this.frameCount - 1; + } + + return true; + }; + + public setMainLoop() { + window.cancelAnimationFrame(this.rafId); + + this.frInterval = 1000 / this.fps / this.speed; + this.frThen = Date.now(); + + //console.trace('setMainLoop', this.frInterval, this.direction, this); + + const method = (this.direction == 1 ? this.mainLoopForwards : this.mainLoopBackwards).bind(this); + this.mainLoop(method); + }; +} + +class QueryableWorker { + private worker: Worker; + private listeners: {[name: string]: (...args: any[]) => void} = {}; + + constructor(url: string, private defaultListener: (data: any) => void = () => {}, onError?: (error: any) => void) { + this.worker = new Worker(url); + if(onError) { + this.worker.onerror = onError; + } + + this.worker.onmessage = (event) => { + if(event.data instanceof Object && + event.data.hasOwnProperty('queryMethodListener') && + event.data.hasOwnProperty('queryMethodArguments')) { + this.listeners[event.data.queryMethodListener].apply(this, event.data.queryMethodArguments); + } else { + this.defaultListener.call(this, event.data); + } + } + } + + public postMessage(message: any) { + this.worker.postMessage(message); + } + + public terminate() { + this.worker.terminate(); + } + + public addListener(name: string, listener: (...args: any[]) => void) { + this.listeners[name] = listener; + } + + public removeListener(name: string) { + delete this.listeners[name]; + } + + public sendQuery(queryMethod: string, ...args: any[]) { + this.worker.postMessage({ + 'queryMethod': queryMethod, + 'queryMethodArguments': args + }); + } +} + class LottieLoader { - public lottie: /* any */ typeof LottiePlayer = null; - private animations: { - [group: string]: { - animation: /* any */AnimationItem, - container: HTMLDivElement, - paused: boolean, - autoplay: boolean, - canvas: boolean - }[] - } = {}; - private debug = false; - public loaded: Promise; - private lastTimeLoad = 0; - private waitingTimeouts = 0; + public loadPromise: Promise; + public loaded = false; + private static COLORREPLACEMENTS = [ [ [0xf77e41, 0xca907a], @@ -48,78 +276,69 @@ class LottieLoader { [0xffd140, 0xa16e46], [0xffdf79, 0xac7a52], ] - ]; - - public loadLottie() { - if(this.loaded) return this.loaded; + ]; + + private workersLimit = 4; + private players: {[reqId: number]: RLottiePlayer} = {}; + private byGroups: {[group: string]: RLottiePlayer[]} = {}; + + private workers: QueryableWorker[] = []; + private curWorkerNum = 0; + + private observer: IntersectionObserver; + private visible: Set = new Set(); + + private debug = true; + + constructor() { + this.observer = new IntersectionObserver((entries) => { + for(const entry of entries) { + const target = entry.target; + + for(const group in this.byGroups) { + const player = this.byGroups[group].find(p => p.el == target); + if(player) { + if(entry.isIntersecting) { + this.visible.add(player); + this.checkAnimation(player, false); + } else { + this.visible.delete(player); + this.checkAnimation(player, true); + } - return this.loaded = new Promise((resolve, reject) => { - (window as any).lottieLoaded = () => { - console.log('lottie loaded'); - this.lottie = (window as any).lottie; - resolve(); - }; - - let sc = document.createElement('script'); - sc.src = 'npm.lottie-web.chunk.js'; - sc.async = true; - sc.onload = (window as any).lottieLoaded; - - document.body.appendChild(sc); + break; + } + } + } }); } - public checkAnimations(blurred?: boolean, group?: string, destroy = false) { - let groups = group ? [group] : Object.keys(this.animations); + public loadLottieWorkers() { + if(this.loadPromise) return this.loadPromise; - if(group && !this.animations[group]) { - console.warn('no animation group:', group); - this.animations[group] = []; - //return; - } + const onFrame = this.onFrame.bind(this); + const onPlayerLoaded = this.onPlayerLoaded.bind(this); - for(let group of groups) { - let animations = this.animations[group]; + return this.loadPromise = new Promise((resolve, reject) => { + let remain = this.workersLimit; + for(let i = 0; i < this.workersLimit; ++i) { + const worker = this.workers[i] = new QueryableWorker('rlottie.worker.js'); - let length = animations.length; - for(let i = length - 1; i >= 0; --i) { - let {animation, container, paused, autoplay, canvas} = animations[i]; + worker.addListener('ready', () => { + console.log('worker #' + i + ' ready'); - if(destroy && !container.parentElement/* !isInDOM(container) */) { - this.debug && console.log('destroy animation'); - animation.destroy(); - animations.splice(i, 1); - continue; - } + worker.addListener('frame', onFrame); + worker.addListener('loaded', onPlayerLoaded); - /* if(canvas) { - let c = container.firstElementChild as HTMLCanvasElement; - if(!c) { - console.warn('no canvas element for check!', container, animations[i]); - continue; - } - - if(!c.height && !c.width && isElementInViewport(container)) { - //console.log('lottie need resize'); - animation.resize(); - } - } */ - - if(!autoplay) continue; - - /* if(blurred || !isElementInViewport(container)) { - if(!paused) { - this.debug && console.log('pause animation', isElementInViewport(container), container); - animation.pause(); - animations[i].paused = true; + --remain; + if(!remain) { + console.log('workers ready'); + resolve(); + this.loaded = true; } - } else if(paused) { - this.debug && console.log('play animation', container); - animation.play(); - animations[i].paused = false; - } */ + }); } - } + }); } private applyReplacements(object: any, toneIndex: number) { @@ -159,81 +378,156 @@ class LottieLoader { } } - public async loadAnimation(params: /* any */AnimationConfigWithPath & AnimationConfigWithData, group = '', toneIndex = -1) { - //params.autoplay = false; - //if(group != 'auth') { - //params.renderer = 'canvas'; - params.renderer = 'svg'; - //} + public async loadAnimationWorker(params: { + container: HTMLElement, + autoplay?: boolean, + animationData: any, + loop?: boolean, + width?: number, + height?: number + }, group = '', toneIndex = -1) { + params.autoplay = true; if(toneIndex >= 1 && toneIndex <= 5) { this.applyReplacements(params.animationData, toneIndex); } - - let rendererSettings = { - //context: context, // the canvas context - //preserveAspectRatio: 'xMinYMin slice', // Supports the same options as the svg element's preserveAspectRatio property - clearCanvas: true, - progressiveLoad: true, // Boolean, only svg renderer, loads dom elements when needed. Might speed up initialization for large number of elements. - hideOnTransparent: true, //Boolean, only svg renderer, hides elements when opacity reaches 0 (defaults to true), - }; - if(params.rendererSettings) { - params.rendererSettings = Object.assign(params.rendererSettings, rendererSettings); - } else { - params.rendererSettings = rendererSettings; + if(!this.loaded) { + await this.loadLottieWorkers(); + } + + const width = params.width || parseInt(params.container.style.width); + const height = params.height || parseInt(params.container.style.height); + + const player = this.initPlayer(params.container, params.animationData, width, height); + for(let i in params) { + // @ts-ignore + if(player.hasOwnProperty(i)) { + // @ts-ignore + player[i] = params[i]; + } + } + + (this.byGroups[group] ?? (this.byGroups[group] = [])).push(player); + + return player; + } + + public checkAnimations(blurred?: boolean, group?: string, destroy = false) { + const groups = group && false ? [group] : Object.keys(this.byGroups); + + if(group && !this.byGroups[group]) { + console.warn('no animation group:', group); + this.byGroups[group] = []; + //return; + } + + for(const group of groups) { + const animations = this.byGroups[group]; + + animations.forEach(player => { + this.checkAnimation(player, blurred, destroy); + + //if(!autoplay) continue; + + /* if(blurred || !isElementInViewport(container)) { + if(!paused) { + this.debug && console.log('pause animation', isElementInViewport(container), container); + animation.pause(); + animations[i].paused = true; + } + } else if(paused) { + this.debug && console.log('play animation', container); + animation.play(); + animations[i].paused = false; + } */ + }); } + } - if(!this.lottie) { - if(!this.loaded) this.loadLottie(); - await this.loaded; + public checkAnimation(player: RLottiePlayer, blurred = false, destroy = false) { + if(destroy || (!isInDOM(player.el) && player.listenerResults.hasOwnProperty('ready'))) { + //console.log('destroy animation'); + player.destroy(); + return; + } - this.lottie.setQuality('low'); - //this.lottie.setQuality(10); + if(blurred) { + if(!player.paused) { + this.debug && console.log('pause animation', player); + player.pause(); + } + } else if(player.paused && this.visible.has(player) && player.autoplay) { + this.debug && console.log('play animation', player); + player.play(); } + } - let time = Date.now(); - let diff = time - this.lastTimeLoad; - let delay = 150; - if(diff < delay) { - delay *= ++this.waitingTimeouts; - console.log('lottieloader delay:', delay); - //await new Promise((resolve) => setTimeout(resolve, delay)); - this.waitingTimeouts--; + private onPlayerLoaded(reqId: number, frameCount: number, fps: number) { + const rlPlayer = this.players[reqId]; + if(!rlPlayer) { + this.debug && console.warn('onPlayerLoaded on destroyed player:', reqId, frameCount); + return; } - let animation = this.lottie.loadAnimation(params); + rlPlayer.el.appendChild(rlPlayer.canvas); + + rlPlayer.curFrame = rlPlayer.direction == 1 ? 0 : frameCount - 1; + rlPlayer.frameCount = frameCount; + rlPlayer.fps = fps; + rlPlayer.sendQuery('renderFrame', 0); + rlPlayer.setListenerResult('ready'); + + this.observer.observe(rlPlayer.el); + } - this.lastTimeLoad = Date.now(); + private onFrame(reqId: number, frameNo: number, frame: Uint8ClampedArray) { + const rlPlayer = this.players[reqId]; + if(!rlPlayer) { + this.debug && console.warn('onFrame on destroyed player:', reqId, frameNo); + return; + } - if(!this.animations[group]) this.animations[group] = []; - this.animations[group].push({ - animation, - container: params.container as HTMLDivElement, - paused: !params.autoplay, - autoplay: params.autoplay, - canvas: false//params.renderer == 'canvas' - }); + rlPlayer.renderFrame(frame, frameNo); + } - if(params.autoplay) { - this.checkAnimations(); + public onDestroy(reqId: number) { + let player = this.players[reqId]; + for(let group in this.byGroups) { + this.byGroups[group].findAndSplice(p => p == player); } - return animation; + delete this.players[player.reqId]; + this.observer.unobserve(player.el); + this.visible.delete(player); + } + + public destroyWorkers() { + this.workers.forEach((worker, idx) => { + worker.terminate(); + console.log('worker #' + idx + ' terminated'); + }); + + console.log('workers destroyed'); + this.workers.length = 0; } - public getAnimation(el: HTMLElement, group = '') { - let groups = group ? [group] : Object.keys(this.animations); - //console.log('getAnimation', groups, this.animations); - for(let group of groups) { - let animations = this.animations[group]; + private initPlayer(el: HTMLElement, json: any, width: number, height: number) { + const rlPlayer = new RLottiePlayer({ + el, + width, + height, + worker: this.workers[this.curWorkerNum++] + }); - - let animation = animations.find(a => a.container === el); - if(animation) return animation.animation; + this.players[rlPlayer.reqId] = rlPlayer; + if(this.curWorkerNum >= this.workers.length) { + this.curWorkerNum = 0; } - return null; + rlPlayer.loadFromData(json); + + return rlPlayer; } } diff --git a/src/lib/searchIndexManager.ts b/src/lib/searchIndexManager.ts new file mode 100644 index 00000000..9e63e006 --- /dev/null +++ b/src/lib/searchIndexManager.ts @@ -0,0 +1,110 @@ +import Config from './config'; + +class SearchIndexManager { + public static badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g; + public static trimRe = /^\s+|\s$/g; + + public createIndex() { + return { + shortIndexes: {}, + fullTexts: {} + }; + } + + public cleanSearchText(text: string, latinize = true) { + const hasTag = text.charAt(0) == '%'; + text = text.replace(SearchIndexManager['badCharsRe'], '').replace(SearchIndexManager['trimRe'], ''); + if(latinize) { + text = text.replace(/[^A-Za-z0-9]/g, (ch) => { + const latinizeCh = Config.LatinizeMap[ch]; + return latinizeCh !== undefined ? latinizeCh : ch; + }); + } + + text = text.toLowerCase(); + if(hasTag) { + text = '%' + text; + } + + return text; + } + + public cleanUsername(username: string) { + return username && username.toLowerCase() || ''; + } + + public indexObject(id: number, searchText: string, searchIndex: any) { + if(searchIndex.fullTexts[id] !== undefined) { + return false; + } + + searchText = this.cleanSearchText(searchText); + + if(!searchText.length) { + return false; + } + + const shortIndexes = searchIndex.shortIndexes; + + searchIndex.fullTexts[id] = searchText; + + searchText.split(' ').forEach((searchWord) => { + let len = Math.min(searchWord.length, 3), + wordPart, i; + for(i = 1; i <= len; i++) { + wordPart = searchWord.substr(0, i); + if(shortIndexes[wordPart] === undefined) { + shortIndexes[wordPart] = [id]; + } else { + shortIndexes[wordPart].push(id); + } + } + }); + } + + public search(query: string, searchIndex: any) { + const shortIndexes = searchIndex.shortIndexes; + const fullTexts = searchIndex.fullTexts; + + query = this.cleanSearchText(query); + + const queryWords = query.split(' '); + let foundObjs: any = false, + newFoundObjs: any, i: number; + let j: number, searchText: string; + let found: boolean; + + for(i = 0; i < queryWords.length; i++) { + newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; + if(!newFoundObjs) { + foundObjs = []; + break; + } + + if(foundObjs === false || foundObjs.length > newFoundObjs.length) { + foundObjs = newFoundObjs; + } + } + + newFoundObjs = {}; + + for(j = 0; j < foundObjs.length; j++) { + found = true; + searchText = fullTexts[foundObjs[j]]; + for(i = 0; i < queryWords.length; i++) { + if(searchText.indexOf(queryWords[i]) == -1) { + found = false; + break; + } + } + + if(found) { + newFoundObjs[foundObjs[j]] = true; + } + } + + return newFoundObjs; + } +} + +export default new SearchIndexManager(); \ No newline at end of file diff --git a/src/lib/utils.js b/src/lib/utils.js index 91dff8e2..167af3db 100644 --- a/src/lib/utils.js +++ b/src/lib/utils.js @@ -9,18 +9,6 @@ export function dT () { return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'; } -export function checkClick(e, noprevent) { - if(e.which == 1 && (e.ctrlKey || e.metaKey) || e.which == 2) { - return true; - } - - if(!noprevent) { - e.preventDefault(); - } - - return false; -} - export function isInDOM(element, parentNode) { if(!element) { return false; @@ -62,60 +50,6 @@ export function cancelEvent (event) { return false; } -export function setFieldSelection (field, from, to) { - field = $(field)[0] - try { - field.focus() - if (from === undefined || from === false) { - from = field.value.length - } - if (to === undefined || to === false) { - to = from - } - if (field.createTextRange) { - var range = field.createTextRange() - range.collapse(true) - range.moveEnd('character', to) - range.moveStart('character', from) - range.select() - } - else if (field.setSelectionRange) { - field.setSelectionRange(from, to) - } - } catch(e) {} -} - -export function getFieldSelection (field) { - if (field.selectionStart) { - return field.selectionStart - } - else if (!document.selection) { - return 0 - } - - var c = '\x01' - var sel = document.selection.createRange() - var txt = sel.text - var dup = sel.duplicate() - var len = 0 - - try { - dup.moveToElementText(field) - } catch(e) { - return 0 - } - - sel.text = txt + c - len = dup.text.indexOf(c) - sel.moveStart('character', -1) - sel.text = '' - - // if (browser.msie && len == -1) { - // return field.value.length - // } - return len -} - export function getRichValue (field) { if (!field) { return '' @@ -152,42 +86,6 @@ export function placeCaretAtEnd(el) { } } -export function getRichValueWithCaret (field) { - if (!field) { - return [] - } - var lines = [] - var line = [] - - var sel = window.getSelection ? window.getSelection() : false - var selNode - var selOffset - if (sel && sel.rangeCount) { - var range = sel.getRangeAt(0) - /* if (range.startContainer && - range.startContainer == range.endContainer && - range.startOffset == range.endOffset) { */ - selNode = range.startContainer - selOffset = range.startOffset - //} - } - - getRichElementValue(field, lines, line, selNode, selOffset) - - if (line.length) { - lines.push(line.join('')) - } - - var value = lines.join('\n') - var caretPos = value.indexOf('\x01') - if (caretPos != -1) { - value = value.substr(0, caretPos) + value.substr(caretPos + 1) - } - value = value.replace(/\u00A0/g, ' ') - - return [value, caretPos] -} - export function getRichElementValue (node, lines, line, selNode, selOffset) { if (node.nodeType == 3) { // TEXT if (selNode === node) { @@ -230,50 +128,6 @@ export function getRichElementValue (node, lines, line, selNode, selOffset) { } } -export function setRichFocus (field, selectNode, noCollapse) { - field.focus() - if (selectNode && - selectNode.parentNode == field && - !selectNode.nextSibling && - !noCollapse) { - field.removeChild(selectNode) - selectNode = null - } - if (window.getSelection && document.createRange) { - var range = document.createRange() - if (selectNode) { - range.selectNode(selectNode) - } else { - range.selectNodeContents(field) - } - if (!noCollapse) { - range.collapse(false) - } - - var sel = window.getSelection() - sel.removeAllRanges() - sel.addRange(range) - } - else if (document.body.createTextRange !== undefined) { - var textRange = document.body.createTextRange() - textRange.moveToElementText(selectNode || field) - if (!noCollapse) { - textRange.collapse(false) - } - textRange.select() - } -} - -export function getSelectedText() { - var sel = ( - window.getSelection && window.getSelection() || - document.getSelection && document.getSelection() || - document.selection && document.selection.createRange().text || '' - ).toString().replace(/^\s+|\s+$/g, '') - - return sel -} - /* if (Config.Modes.animations && typeof window.requestAnimationFrame == 'function') { window.onAnimationFrameCallback = function (cb) { @@ -528,20 +382,6 @@ export function listMergeSorted (list1, list2) { return result } -export function listUniqSorted (list) { - list = list || [] - var resultList = [] - var prev = false - for (var i = 0; i < list.length; i++) { - if (list[i] !== prev) { - resultList.push(list[i]) - } - prev = list[i] - } - - return resultList -} - // credits to https://github.com/sindresorhus/escape-string-regexp/blob/master/index.js export function escapeRegExp(str) { return str @@ -657,119 +497,3 @@ export function getEmojiToneIndex(input) { let match = input.match(/[\uDFFB-\uDFFF]/); return match ? 5 - (57343 - match[0].charCodeAt(0)) : 0; } - - //var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, - var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<]+/g, - trimRe = /^\s+|\s$/g - - function createIndex () { - return { - shortIndexes: {}, - fullTexts: {} - } - } - - function cleanSearchText(text, latinize = true) { - var hasTag = text.charAt(0) == '%'; - text = text.replace(badCharsRe, '').replace(trimRe, ''); - if(latinize) { - text = text.replace(/[^A-Za-z0-9]/g, (ch) => { - var latinizeCh = Config.LatinizeMap[ch]; - return latinizeCh !== undefined ? latinizeCh : ch; - }); - } - - text = text.toLowerCase(); - if(hasTag) { - text = '%' + text; - } - - return text; - } - - function cleanUsername(username) { - return username && username.toLowerCase() || ''; - } - - function indexObject(id, searchText, searchIndex) { - if(searchIndex.fullTexts[id] !== undefined) { - return false; - } - - searchText = cleanSearchText(searchText); - - if(!searchText.length) { - return false; - } - - var shortIndexes = searchIndex.shortIndexes; - - searchIndex.fullTexts[id] = searchText; - - searchText.split(' ').forEach((searchWord) => { - var len = Math.min(searchWord.length, 3), - wordPart, i; - for(i = 1; i <= len; i++) { - wordPart = searchWord.substr(0, i); - if(shortIndexes[wordPart] === undefined) { - shortIndexes[wordPart] = [id]; - } else { - shortIndexes[wordPart].push(id); - } - } - }); - } - - function search(query, searchIndex) { - var shortIndexes = searchIndex.shortIndexes; - var fullTexts = searchIndex.fullTexts; - - query = cleanSearchText(query); - - var queryWords = query.split(' '); - var foundObjs = false, - newFoundObjs, i; - var j, searchText; - var found; - - for(i = 0; i < queryWords.length; i++) { - newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; - if(!newFoundObjs) { - foundObjs = []; - break; - } - if(foundObjs === false || foundObjs.length > newFoundObjs.length) { - foundObjs = newFoundObjs; - } - } - - newFoundObjs = {}; - - for(j = 0; j < foundObjs.length; j++) { - found = true; - searchText = fullTexts[foundObjs[j]]; - for(i = 0; i < queryWords.length; i++) { - if(searchText.indexOf(queryWords[i]) == -1) { - found = false; - break; - } - } - if(found) { - newFoundObjs[foundObjs[j]] = true; - } - } - - return newFoundObjs; - } - - let SearchIndexManager = { - createIndex: createIndex, - indexObject: indexObject, - cleanSearchText: cleanSearchText, - cleanUsername: cleanUsername, - search: search - }; - //window.SearchIndexManager = SearchIndexManager; - - export {SearchIndexManager}; -//})(window) diff --git a/src/pages/pageAuthCode.ts b/src/pages/pageAuthCode.ts index b3d62ffd..c3c2e4e4 100644 --- a/src/pages/pageAuthCode.ts +++ b/src/pages/pageAuthCode.ts @@ -2,7 +2,7 @@ import pageSignIn from './pageSignIn'; import pageSignUp from './pageSignUp'; import pageIm from './pageIm'; import pagePassword from './pagePassword'; -import LottieLoader from '../lib/lottieLoader'; +import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader'; //import CryptoWorker from '../lib/crypto/cryptoworker'; //import apiManager from '../lib/mtproto/apiManager'; import apiManager from '../lib/mtproto/mtprotoworker'; @@ -29,11 +29,8 @@ let sentTypeElement: HTMLParagraphElement = null; let onFirstMount = (): Promise => { let needFrame = 0, lastLength = 0; - let animation: /* AnimationItem */any; - let idleAnimation: any; - - let mTrackingSvg: SVGSVGElement; - let mIdleSvg: SVGSVGElement; + let animation: RLottiePlayer; + let idleAnimation: RLottiePlayer; const CODELENGTH = authCode.type.length; @@ -41,6 +38,8 @@ let onFirstMount = (): Promise => { const codeInputLabel = codeInput.nextElementSibling as HTMLLabelElement; const editButton = page.pageEl.querySelector('.phone-edit') as HTMLElement; + codeInput.focus(); + if(EDITONSAMEPAGE) { let editable = false; let changePhonePromise: Promise; @@ -209,17 +208,17 @@ let onFirstMount = (): Promise => { let frame: number; if(length) { - frame = Math.round((length > max ? max : length) * (165 / max) + 11.33); + frame = Math.round(Math.min(max, length) * (165 / max) + 11.33); - mIdleSvg.style.display = 'none'; - mTrackingSvg.style.display = ''; + idleAnimation.canvas.style.display = 'none'; + animation.canvas.style.display = ''; } else { frame = 0; } //animation.playSegments([1, 2]); let direction = needFrame > frame ? -1 : 1; - //console.log('keydown', length, frame, direction); + console.log('keydown', length, frame, direction); animation.setDirection(direction); if(needFrame != 0 && frame == 0) { @@ -235,69 +234,64 @@ let onFirstMount = (): Promise => { //animation.goToAndStop(length / max * ); }); - let imageDiv = page.pageEl.querySelector('.auth-image'); + let imageDiv = page.pageEl.querySelector('.auth-image') as HTMLDivElement; return Promise.all([ - LottieLoader.loadLottie(), + LottieLoader.loadLottieWorkers(), fetch('assets/img/TwoFactorSetupMonkeyIdle.tgs') .then(res => res.arrayBuffer()) .then(data => apiManager.gzipUncompress(data, true)) - .then(str => LottieLoader.loadAnimation({ + .then(str => LottieLoader.loadAnimationWorker({ container: imageDiv, - renderer: 'svg', loop: true, autoplay: true, animationData: JSON.parse(str), - rendererSettings: { - className: 'monkey-idle' - } + width: 166, + height: 166 })) - .then(_animation => { - idleAnimation = _animation; - - mIdleSvg = imageDiv.querySelector('.monkey-idle'); + .then(animation => { + idleAnimation = animation; }), - fetch('assets/img/TwoFactorSetupMonkeyTracking.tgs') + /* false && */fetch('assets/img/TwoFactorSetupMonkeyTracking.tgs') .then(res => res.arrayBuffer()) .then(data => apiManager.gzipUncompress(data, true)) - .then(str => LottieLoader.loadAnimation({ + .then(str => LottieLoader.loadAnimationWorker({ container: imageDiv, - renderer: 'svg', loop: false, autoplay: false, animationData: JSON.parse(str), - rendererSettings: { - className: 'monkey-tracking' - } + width: 166, + height: 166 })) .then(_animation => { animation = _animation; - animation.setSpeed(1); - //console.log(animation.getDuration(), animation.getDuration(true)); - mTrackingSvg = imageDiv.querySelector('.monkey-tracking'); if(!codeInput.value.length) { - mTrackingSvg.style.display = 'none'; + animation.canvas.style.display = 'none'; } - animation.addEventListener('enterFrame', (e: any) => { - //console.log('enterFrame', e, needFrame); - let currentFrame = Math.round(e.currentTime); + animation.addListener('enterFrame', currentFrame => { + console.log('enterFrame', currentFrame, needFrame); + //let currentFrame = Math.round(e.currentTime); - if((e.direction == 1 && currentFrame >= needFrame) || - (e.direction == -1 && currentFrame <= needFrame)) { + if((animation.direction == 1 && currentFrame >= needFrame) || + (animation.direction == -1 && currentFrame <= needFrame)) { animation.setSpeed(1); animation.pause(); } - if(currentFrame == 0 && needFrame == 0 && mIdleSvg) { - mTrackingSvg.style.display = 'none'; - mIdleSvg.style.display = ''; - idleAnimation.stop(); - idleAnimation.play(); + if(currentFrame == 0 && needFrame == 0) { + animation.curFrame = 0; + + if(idleAnimation) { + animation.canvas.style.display = 'none'; + idleAnimation.canvas.style.display = ''; + idleAnimation.restart(); + } } }); + //console.log(animation.getDuration(), animation.getDuration(true)); }) ]); }; diff --git a/src/pages/pagePassword.ts b/src/pages/pagePassword.ts index ab1ffee7..9d0841db 100644 --- a/src/pages/pagePassword.ts +++ b/src/pages/pagePassword.ts @@ -3,14 +3,14 @@ import pageIm from './pageIm'; //import apiManager from '../lib/mtproto/apiManager'; import { putPreloader } from '../components/misc'; -import LottieLoader from '../lib/lottieLoader'; +import LottieLoader, { RLottiePlayer } from '../lib/lottieLoader'; //import passwordManager from '../lib/mtproto/passwordManager'; import apiManager from '../lib/mtproto/mtprotoworker'; import Page from './page'; let onFirstMount = (): Promise => { let needFrame = 0; - let animation: /* AnimationItem */any = undefined; + let animation: RLottiePlayer; let passwordVisible = false; @@ -91,29 +91,29 @@ let onFirstMount = (): Promise => { }); */ return Promise.all([ - LottieLoader.loadLottie(), + LottieLoader.loadLottieWorkers(), fetch('assets/img/TwoFactorSetupMonkeyClose.tgs') .then(res => res.arrayBuffer()) .then(data => apiManager.gzipUncompress(data, true)) - .then(str => LottieLoader.loadAnimation({ + .then(str => LottieLoader.loadAnimationWorker({ container: page.pageEl.querySelector('.auth-image'), - renderer: 'svg', loop: false, autoplay: false, - animationData: JSON.parse(str) + animationData: JSON.parse(str), + width: 166, + height: 166 })) .then(_animation => { animation = _animation; - animation.addEventListener('enterFrame', (e: any) => { + animation.addListener('enterFrame', currentFrame => { //console.log('enterFrame', e, needFrame); - let currentFrame = Math.round(e.currentTime); - - if((e.direction == 1 && currentFrame >= needFrame) || - (e.direction == -1 && currentFrame <= needFrame)) { + + if((animation.direction == 1 && currentFrame >= needFrame) || + (animation.direction == -1 && currentFrame <= needFrame)) { animation.setSpeed(1); animation.pause(); - } + } }); needFrame = 49; diff --git a/src/pages/pageSignQR.ts b/src/pages/pageSignQR.ts index 2ba6a2d3..849a6eb2 100644 --- a/src/pages/pageSignQR.ts +++ b/src/pages/pageSignQR.ts @@ -56,6 +56,7 @@ let onFirstMount = async() => { page.pageEl.querySelector('.a-qr').addEventListener('click', () => { pageSignIn.mount(); + stop = true; }); const results = await Promise.all([ @@ -66,98 +67,112 @@ let onFirstMount = async() => { let stop = false; document.addEventListener('user_auth', () => { stop = true; + cachedPromise = null; }, {once: true}); - let options: {dcID?: number} = {}; + let options: {dcID?: number, ignoreErrors: true} = {ignoreErrors: true}; let prevToken: Uint8Array; - do { - if(stop) { - break; - } - - try { - let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', { - api_id: App.id, - api_hash: App.hash, - except_ids: [] - }/* , options */); - - if(loginToken._ == 'auth.loginTokenMigrateTo') { - if(!options.dcID) { - options.dcID = loginToken.dc_id; - apiManager.setBaseDcID(loginToken.dc_id); - //continue; - } - - loginToken = await apiManager.invokeApi('auth.importLoginToken', { - token: loginToken.token - }, options) as LoginToken; - } + return async() => { + stop = false; - if(loginToken._ == 'auth.loginTokenSuccess') { - let authorization = loginToken.authorization; - apiManager.setUserAuth({ - id: authorization.user.id - }); - pageIm.mount(); + do { + if(stop) { break; } - - /* // to base64 - var decoder = new TextDecoder('utf8'); - var b64encoded = btoa(String.fromCharCode.apply(null, [...loginToken.token])); */ - - if(!prevToken || !bytesCmp(prevToken, loginToken.token)) { - prevToken = loginToken.token; - - let encoded = bytesToBase64(loginToken.token); - let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, ""); - imageDiv.innerHTML = ''; - const qrCode = new QRCodeStyling({ - width: 166, - height: 166, - data: url, - image: "assets/img/logo_padded.svg", - dotsOptions: { - color: "#000000", - type: "rounded" - }, - imageOptions: { - imageSize: .75 - }, - backgroundOptions: { - color: "#ffffff" - }, - qrOptions: { - errorCorrectionLevel: "L" + try { + let loginToken: LoginToken | LoginTokenMigrateTo | LoginTokenSuccess = await apiManager.invokeApi('auth.exportLoginToken', { + api_id: App.id, + api_hash: App.hash, + except_ids: [] + }, {ignoreErrors: true}); + + if(loginToken._ == 'auth.loginTokenMigrateTo') { + if(!options.dcID) { + options.dcID = loginToken.dc_id; + apiManager.setBaseDcID(loginToken.dc_id); + //continue; } - }); - qrCode.append(imageDiv); - } - - let timestamp = Date.now() / 1000; - let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset; - - await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0)); - } catch(err) { - switch(err.type) { - case 'SESSION_PASSWORD_NEEDED': - console.warn('pageSignQR: SESSION_PASSWORD_NEEDED'); - err.handled = true; - pagePassword.mount(); - break; - default: - console.error('pageSignQR: default error:', err); + + loginToken = await apiManager.invokeApi('auth.importLoginToken', { + token: loginToken.token + }, options) as LoginToken; + } + + if(loginToken._ == 'auth.loginTokenSuccess') { + let authorization = loginToken.authorization; + apiManager.setUserAuth({ + id: authorization.user.id + }); + pageIm.mount(); break; + } + + /* // to base64 + var decoder = new TextDecoder('utf8'); + var b64encoded = btoa(String.fromCharCode.apply(null, [...loginToken.token])); */ + + if(!prevToken || !bytesCmp(prevToken, loginToken.token)) { + prevToken = loginToken.token; + + let encoded = bytesToBase64(loginToken.token); + let url = "tg://login?token=" + encoded.replace(/\+/g, "-").replace(/\//g, "_").replace(/\=+$/, ""); + + imageDiv.innerHTML = ''; + const qrCode = new QRCodeStyling({ + width: 166, + height: 166, + data: url, + image: "assets/img/logo_padded.svg", + dotsOptions: { + color: "#000000", + type: "rounded" + }, + imageOptions: { + imageSize: .75 + }, + backgroundOptions: { + color: "#ffffff" + }, + qrOptions: { + errorCorrectionLevel: "L" + } + }); + qrCode.append(imageDiv); + } + + let timestamp = Date.now() / 1000; + let diff = loginToken.expires - timestamp - serverTimeManager.serverTimeOffset; + + await new Promise((resolve, reject) => setTimeout(resolve, diff > 5 ? 5e3 : 1e3 * diff | 0)); + } catch(err) { + switch(err.type) { + case 'SESSION_PASSWORD_NEEDED': + console.warn('pageSignQR: SESSION_PASSWORD_NEEDED'); + err.handled = true; + pagePassword.mount(); + stop = true; + cachedPromise = null; + break; + default: + console.error('pageSignQR: default error:', err); + break; + } } - } - } while(true); + } while(true); + }; }; +let cachedPromise: Promise<() => Promise>; const page = new Page('page-signQR', true, () => { - onFirstMount(); + return cachedPromise; +}, () => { + console.log('onMount'); + if(!cachedPromise) cachedPromise = onFirstMount(); + cachedPromise.then(func => { + func(); + }); }); export default page; diff --git a/src/pages/pagesManager.ts b/src/pages/pagesManager.ts index 0282efdb..2e97a193 100644 --- a/src/pages/pagesManager.ts +++ b/src/pages/pagesManager.ts @@ -23,12 +23,8 @@ class PagesManager { this.selectTab(id); - // это нужно чтобы ресайзнуть канвас (из-за скрытого рендера будет 0х0) - if(this.pageID != -1) { - lottieLoader.loadLottie().then(() => { - // @ts-ignore - lottieLoader.lottie.resize(); - }); + if(this.pageID != -1 && id > 1) { + lottieLoader.loadLottieWorkers(); } this.pageID = id; diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index 80795344..82a03f00 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -433,10 +433,10 @@ max-width: 451px; max-height: none; - > div { + .album-item { background-color: #000; background-size: cover; - background-position: center center; + /* background-position: center center; */ /* flex: 1 0 auto; */ max-width: 100%; cursor: pointer; @@ -555,6 +555,38 @@ } } + &.is-square-photo { + .bubble__container { + width: fit-content; + } + + .box.web { + .quote { + display: flex; + } + + .preview { + -webkit-box-ordinal-group: 3; + order: 2; + flex-shrink: 0; + max-width: 5rem; + max-height: 5rem; + } + + .quote-text { + -webkit-box-ordinal-group: 2; + order: 1; + padding-right: 1rem; + } + } + } + + &.is-vertical-photo { + .bubble__container { + width: fit-content; + } + } + .reply { max-width: 300px; margin-bottom: 6px; @@ -908,7 +940,7 @@ } &:not(.sticker):not(.emoji-big) { - &.hide-name, &:not(.is-group-first) { + &.hide-name, &:not(.is-group-first), &.is-out { .reply { margin-top: 6px; } diff --git a/src/scss/partials/_ckin.scss b/src/scss/partials/_ckin.scss index adc70508..74712f3c 100644 --- a/src/scss/partials/_ckin.scss +++ b/src/scss/partials/_ckin.scss @@ -33,6 +33,7 @@ video { max-height: none; max-width: none; + object-fit: contain; } } diff --git a/src/scss/partials/_mediaViewer.scss b/src/scss/partials/_mediaViewer.scss index 0a00b5ee..2c338573 100644 --- a/src/scss/partials/_mediaViewer.scss +++ b/src/scss/partials/_mediaViewer.scss @@ -209,4 +209,52 @@ $move-duration: .35s; &-mover.active &-aspecter { transition: $open-duration all; } + + &-whole { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: fixed!important; + display: block; + z-index: 4; + visibility: hidden; + transition: visibility 0s $open-duration; + + &.active { + visibility: visible; + transition-delay: 0s; + + .overlays { + opacity: 1; + visibility: visible; + -webkit-transition: opacity $open-duration 0s, visibility 0s 0s; + -moz-transition: opacity $open-duration 0s, visibility 0s 0s; + transition: opacity $open-duration 0s, visibility 0s 0s; + } + } + } + + &-switchers { + position: relative; + width: $large-screen; + height: 100%; + margin: 0 auto; + } +} + +.overlays { + top: 0; + left: 0; + width: 100%; + height: 100%; + position: fixed!important; + background-color: rgba(0, 0, 0, .2); + z-index: 4; + //display: none; + opacity: 0; + visibility: hidden; + -webkit-transition: opacity $open-duration 0s, visibility 0s $open-duration; + -moz-transition: opacity $open-duration 0s, visibility 0s $open-duration; + transition: opacity $open-duration 0s, visibility 0s $open-duration; } diff --git a/src/scss/style.scss b/src/scss/style.scss index 9f338fa1..a01319fa 100644 --- a/src/scss/style.scss +++ b/src/scss/style.scss @@ -1336,30 +1336,6 @@ img.emoji { } } -.overlays { - top: 0; - left: 0; - width: 100%; - height: 100%; - position: fixed!important; - background-color: rgba(0, 0, 0, .2); - z-index: 4; - //display: none; - opacity: 0; - visibility: hidden; - -webkit-transition: opacity 0.2s 0s, visibility 0s 0.2s; - -moz-transition: opacity 0.2s 0s, visibility 0s 0.2s; - transition: opacity 0.2s 0s, visibility 0s 0.2s; - - &.active { - opacity: 1; - visibility: visible; - -webkit-transition: opacity 0.2s 0s, visibility 0s 0s; - -moz-transition: opacity 0.2s 0s, visibility 0s 0s; - transition: opacity 0.2s 0s, visibility 0s 0s; - } -} - [contenteditable] { -webkit-user-select: text; user-select: text; diff --git a/webpack.common.js b/webpack.common.js index 0bf3a72c..232761a0 100644 --- a/webpack.common.js +++ b/webpack.common.js @@ -1,7 +1,7 @@ const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); -let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111', '192.168.0.105']; +let allowedIPs = ['195.66.140.39', '192.168.31.144', '127.0.0.1', '192.168.31.1', '192.168.31.192', '192.168.0.111', '192.168.0.105', '192.168.0.108']; const opts = { MTPROTO_WORKER: true, @@ -66,8 +66,8 @@ module.exports = { //entry: './src/index.ts', entry: { index: './src/index.ts', - webp: './src/lib/webp.ts', - lottie: './src/lib/lottie.ts' + webp: './src/lib/webp.ts'/* , + lottie: './src/lib/lottie.ts' */ }, /* entry: { index: './src/index.ts', @@ -129,7 +129,7 @@ module.exports = { minifyURLs: true }, */ chunks: "all", - excludeChunks: ['npm.webp-hero', 'npm.lottie-web'] + excludeChunks: ['npm.webp-hero'/* , 'npm.lottie-web' */] }) ], }; diff --git a/webpack.prod.js b/webpack.prod.js index 5785e64b..42069969 100644 --- a/webpack.prod.js +++ b/webpack.prod.js @@ -59,7 +59,13 @@ module.exports = merge(common, { files.forEach(file => { //console.log('to unlink 1:', file); - if(file.includes('mitm.') || file.includes('sw.js') || file.includes('.xml') || file.includes('.webmanifest')) return; + if(file.includes('mitm.') + || file.includes('sw.js') + || file.includes('.xml') + || file.includes('.webmanifest') + || file.includes('.wasm') + || file.includes('rlottie') + || file.includes('pako')) return; let p = path.resolve(buildDir + file); if(!newlyCreatedAssets[file] && ['.gz', '.js'].find(ext => file.endsWith(ext)) !== undefined) {