Telegram Web, preconfigured for usage in I2P. http://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4443 lines
128 KiB

11 years ago
/*!
9 years ago
* Webogram v0.5.3 - messaging web application for MTProto
11 years ago
* https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
'use strict';
/* Services */
angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils'])
11 years ago
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiManager, RichTextProcessor, Storage, _) {
var users = {},
10 years ago
usernames = {},
userAccess = {},
cachedPhotoLocations = {},
contactsFillPromise,
contactsList,
contactsIndex = SearchIndexManager.createIndex(),
myID,
serverTimeOffset = 0;
Storage.get('server_time_offset').then(function (to) {
if (to) {
serverTimeOffset = to;
}
});
MtpApiManager.getUserID().then(function (id) {
myID = id;
});
function fillContacts () {
if (contactsFillPromise) {
return contactsFillPromise;
}
return contactsFillPromise = MtpApiManager.invokeApi('contacts.getContacts', {
hash: ''
}).then(function (result) {
var userID, searchText, i;
contactsList = [];
saveApiUsers(result.users);
for (var i = 0; i < result.contacts.length; i++) {
userID = result.contacts[i].user_id;
contactsList.push(userID);
SearchIndexManager.indexObject(userID, getUserSearchText(userID), contactsIndex);
}
return contactsList;
});
}
function getUserSearchText (id) {
var user = users[id];
if (!user) {
return false;
}
return (user.first_name || '') + ' ' + (user.last_name || '') + ' ' + (user.phone || '') + ' ' + (user.username || '');
}
function getContacts (query) {
return fillContacts().then(function (contactsList) {
if (angular.isString(query) && query.length) {
var results = SearchIndexManager.search(query, contactsIndex),
filteredContactsList = [];
for (var i = 0; i < contactsList.length; i++) {
if (results[contactsList[i]]) {
filteredContactsList.push(contactsList[i])
}
}
contactsList = filteredContactsList;
}
return contactsList;
});
};
11 years ago
10 years ago
function resolveUsername (username) {
return usernames[username] || 0;
10 years ago
}
11 years ago
function saveApiUsers (apiUsers) {
angular.forEach(apiUsers, saveApiUser);
};
function saveApiUser (apiUser, noReplace) {
if (!angular.isObject(apiUser) ||
noReplace && angular.isObject(users[apiUser.id]) && users[apiUser.id].first_name) {
11 years ago
return;
}
10 years ago
var userID = apiUser.id;
var result = users[userID];
if (apiUser.pFlags === undefined) {
apiUser.pFlags = {};
}
if (apiUser.pFlags.min) {
if (result !== undefined) {
return;
}
}
10 years ago
if (apiUser.phone) {
apiUser.rPhone = $filter('phoneNumber')(apiUser.phone);
}
10 years ago
apiUser.num = (Math.abs(userID) % 8) + 1;
11 years ago
if (apiUser.first_name) {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.first_name, {noLinks: true, noLinebreaks: true});
apiUser.rFullName = apiUser.last_name ? RichTextProcessor.wrapRichText(apiUser.first_name + ' ' + (apiUser.last_name || ''), {noLinks: true, noLinebreaks: true}) : apiUser.rFirstName;
11 years ago
} else {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || _('user_first_name_deleted');
apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || _('user_name_deleted');
11 years ago
}
10 years ago
if (apiUser.username) {
var searchUsername = SearchIndexManager.cleanUsername(apiUser.username);
usernames[searchUsername] = userID;
10 years ago
}
apiUser.sortName = apiUser.pFlags.deleted ? '' : SearchIndexManager.cleanSearchText(apiUser.first_name + ' ' + (apiUser.last_name || ''));
var nameWords = apiUser.sortName.split(' ');
var firstWord = nameWords.shift();
var lastWord = nameWords.pop();
apiUser.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : firstWord.charAt(1));
if (apiUser.status) {
if (apiUser.status.expires) {
apiUser.status.expires -= serverTimeOffset;
}
if (apiUser.status.was_online) {
apiUser.status.was_online -= serverTimeOffset;
}
}
10 years ago
if (apiUser.pFlags.bot) {
apiUser.sortStatus = -1;
} else {
apiUser.sortStatus = getUserStatusForSort(apiUser.status);
}
11 years ago
10 years ago
var result = users[userID];
if (result === undefined) {
result = users[userID] = apiUser;
11 years ago
} else {
10 years ago
safeReplaceObject(result, apiUser);
}
10 years ago
$rootScope.$broadcast('user_update', userID);
10 years ago
if (cachedPhotoLocations[userID] !== undefined) {
safeReplaceObject(cachedPhotoLocations[userID], apiUser && apiUser.photo && apiUser.photo.photo_small || {empty: true});
11 years ago
}
};
function saveUserAccess (id, accessHash) {
userAccess[id] = accessHash;
}
function getUserStatusForSort(status) {
if (status) {
var expires = status.expires || status.was_online;
if (expires) {
return expires;
}
var timeNow = tsNow(true);
switch (status._) {
case 'userStatusRecently':
9 years ago
return timeNow - 86400 * 3;
case 'userStatusLastWeek':
9 years ago
return timeNow - 86400 * 7;
10 years ago
case 'userStatusLastMonth':
9 years ago
return timeNow - 86400 * 30;
}
}
return 0;
}
11 years ago
function getUser (id) {
if (angular.isObject(id)) {
return id;
}
return users[id] || {id: id, deleted: true, num: 1, access_hash: userAccess[id]};
11 years ago
}
function getSelf() {
return getUser(myID);
}
10 years ago
function isBot(id) {
return users[id] && users[id].pFlags.bot;
}
function hasUser(id, allowMin) {
var user = users[id];
return angular.isObject(user) && (allowMin || !user.pFlags.min);
}
function getUserPhoto(id) {
11 years ago
var user = getUser(id);
if (id == 333000) {
return {
placeholder: 'img/placeholders/DialogListAvatarSystem@2x.png'
}
};
if (cachedPhotoLocations[id] === undefined) {
cachedPhotoLocations[id] = user && user.photo && user.photo.photo_small || {empty: true};
}
11 years ago
return {
num: user.num,
placeholder: 'img/placeholders/UserAvatar' + user.num + '@2x.png',
location: cachedPhotoLocations[id]
11 years ago
};
}
function getUserString (id) {
var user = getUser(id);
return 'u' + id + (user.access_hash ? '_' + user.access_hash : '');
}
function getUserInput (id) {
var user = getUser(id);
10 years ago
if (user.pFlags.self) {
11 years ago
return {_: 'inputUserSelf'};
}
return {
10 years ago
_: 'inputUser',
11 years ago
user_id: id,
access_hash: user.access_hash || 0
};
}
function updateUsersStatuses () {
var timestampNow = tsNow(true);
angular.forEach(users, function (user) {
if (user.status &&
user.status._ == 'userStatusOnline' &&
user.status.expires < timestampNow) {
10 years ago
user.status = user.status.wasStatus ||
{_: 'userStatusOffline', was_online: user.status.expires};
delete user.status.wasStatus;
$rootScope.$broadcast('user_update', user.id);
}
});
}
function forceUserOnline (id) {
10 years ago
if (isBot(id)) {
return;
}
var user = getUser(id);
if (user &&
user.status &&
user.status._ != 'userStatusOnline' &&
user.status._ != 'userStatusEmpty') {
var wasStatus;
if (user.status._ != 'userStatusOffline') {
delete user.status.wasStatus;
wasStatus = angular.copy(user.status);
}
user.status = {
_: 'userStatusOnline',
expires: tsNow(true) + 60,
wasStatus: wasStatus
};
user.sortStatus = getUserStatusForSort(user.status);
$rootScope.$broadcast('user_update', id);
}
}
11 years ago
function wrapForFull (id) {
var user = getUser(id);
return user;
}
function openUser (userID, override) {
11 years ago
var scope = $rootScope.$new();
scope.userID = userID;
scope.override = override || {};
11 years ago
var modalInstance = $modal.open({
templateUrl: templateUrl('user_modal'),
11 years ago
controller: 'UserModalController',
scope: scope,
windowClass: 'user_modal_window mobile_modal',
backdrop: 'single'
11 years ago
});
};
function importContact (phone, firstName, lastName) {
return MtpApiManager.invokeApi('contacts.importContacts', {
contacts: [{
_: 'inputPhoneContact',
client_id: '1',
phone: phone,
first_name: firstName,
last_name: lastName
}],
replace: false
}).then(function (importedContactsResult) {
saveApiUsers(importedContactsResult.users);
var foundUserID = false;
angular.forEach(importedContactsResult.imported, function (importedContact) {
onContactUpdated(foundUserID = importedContact.user_id, true);
});
return foundUserID || false;
});
};
function importContacts (contacts) {
var inputContacts = [],
i, j;
for (i = 0; i < contacts.length; i++) {
for (j = 0; j < contacts[i].phones.length; j++) {
inputContacts.push({
_: 'inputPhoneContact',
client_id: (i << 16 | j).toString(10),
phone: contacts[i].phones[j],
first_name: contacts[i].first_name,
last_name: contacts[i].last_name
});
}
}
return MtpApiManager.invokeApi('contacts.importContacts', {
contacts: inputContacts,
replace: false
}).then(function (importedContactsResult) {
saveApiUsers(importedContactsResult.users);
var result = [];
angular.forEach(importedContactsResult.imported, function (importedContact) {
onContactUpdated(importedContact.user_id, true);
result.push(importedContact.user_id);
});
return result;
});
};
function deleteContacts (userIDs) {
var ids = []
angular.forEach(userIDs, function (userID) {
10 years ago
ids.push(getUserInput(userID))
});
return MtpApiManager.invokeApi('contacts.deleteContacts', {
id: ids
}).then(function () {
angular.forEach(userIDs, function (userID) {
onContactUpdated(userID, false);
});
})
}
function onContactUpdated (userID, isContact) {
if (angular.isArray(contactsList)) {
var curPos = curIsContact = contactsList.indexOf(parseInt(userID)),
curIsContact = curPos != -1;
if (isContact != curIsContact) {
if (isContact) {
contactsList.push(userID);
SearchIndexManager.indexObject(userID, getUserSearchText(userID), contactsIndex);
} else {
contactsList.splice(curPos, 1);
}
$rootScope.$broadcast('contacts_update', userID);
}
}
11 years ago
}
function openImportContact () {
return $modal.open({
templateUrl: templateUrl('import_contact_modal'),
controller: 'ImportContactModalController',
windowClass: 'md_simple_modal_window mobile_modal'
}).result.then(function (foundUserID) {
if (!foundUserID) {
return $q.reject();
}
return foundUserID;
});
};
function setUserStatus (userID, offline) {
10 years ago
if (isBot(userID)) {
return;
}
var user = users[userID];
if (user) {
var status = offline ? {
_: 'userStatusOffline',
was_online: tsNow(true)
} : {
_: 'userStatusOnline',
expires: tsNow(true) + 500
};
user.status = status;
user.sortStatus = getUserStatusForSort(user.status);
$rootScope.$broadcast('user_update', userID);
}
}
11 years ago
$rootScope.$on('apiUpdate', function (e, update) {
// console.log('on apiUpdate', update);
11 years ago
switch (update._) {
case 'updateUserStatus':
var userID = update.user_id,
user = users[userID];
if (user) {
user.status = update.status;
if (user.status) {
if (user.status.expires) {
user.status.expires -= serverTimeOffset;
}
if (user.status.was_online) {
user.status.was_online -= serverTimeOffset;
}
}
user.sortStatus = getUserStatusForSort(user.status);
11 years ago
$rootScope.$broadcast('user_update', userID);
}
break;
case 'updateUserPhoto':
var userID = update.user_id;
var user = users[userID];
if (user) {
forceUserOnline(userID);
if (!user.photo) {
user.photo = update.photo;
} else {
safeReplaceObject(user.photo, update.photo);
}
if (cachedPhotoLocations[userID] !== undefined) {
safeReplaceObject(cachedPhotoLocations[userID], update.photo && update.photo.photo_small || {empty: true});
}
11 years ago
$rootScope.$broadcast('user_update', userID);
}
break;
case 'updateContactLink':
onContactUpdated(update.user_id, update.my_link._ == 'contactLinkContact');
break;
11 years ago
}
});
9 years ago
$rootScope.$on('user_auth', function (e, userAuth) {
myID = userAuth && userAuth.id || 0;
});
setInterval(updateUsersStatuses, 60000);
$rootScope.$on('stateSynchronized', updateUsersStatuses);
11 years ago
return {
getContacts: getContacts,
11 years ago
saveApiUsers: saveApiUsers,
saveApiUser: saveApiUser,
saveUserAccess: saveUserAccess,
11 years ago
getUser: getUser,
getSelf: getSelf,
getUserInput: getUserInput,
setUserStatus: setUserStatus,
forceUserOnline: forceUserOnline,
11 years ago
getUserPhoto: getUserPhoto,
getUserString: getUserString,
getUserSearchText: getUserSearchText,
hasUser: hasUser,
10 years ago
isBot: isBot,
importContact: importContact,
importContacts: importContacts,
deleteContacts: deleteContacts,
11 years ago
wrapForFull: wrapForFull,
openUser: openUser,
10 years ago
resolveUsername: resolveUsername,
openImportContact: openImportContact
11 years ago
}
})
.service('PhonebookContactsService', function ($q, $modal, $sce, FileManager) {
return {
isAvailable: isAvailable,
openPhonebookImport: openPhonebookImport,
getPhonebookContacts: getPhonebookContacts
10 years ago
};
function isAvailable () {
if (Config.Mobile && Config.Navigator.ffos && Config.Modes.packed) {
try {
return navigator.mozContacts && navigator.mozContacts.getAll;
} catch (e) {
console.error(dT(), 'phonebook n/a', e);
return false;
}
}
return false;
}
function openPhonebookImport () {
return $modal.open({
templateUrl: templateUrl('phonebook_modal'),
controller: 'PhonebookModalController',
windowClass: 'phonebook_modal_window mobile_modal'
});
}
function getPhonebookContacts () {
try {
var request = window.navigator.mozContacts.getAll({});
} catch (e) {
return $q.reject(e);
}
var deferred = $q.defer(),
contacts = [],
count = 0;
request.onsuccess = function () {
if (this.result) {
var contact = {
id: count,
first_name: (this.result.givenName || []).join(' '),
last_name: (this.result.familyName || []).join(' '),
phones: []
};
if (this.result.tel != undefined) {
11 years ago
for (var i = 0; i < this.result.tel.length; i++) {
contact.phones.push(this.result.tel[i].value);
}
}
if (this.result.photo && this.result.photo[0]) {
try {
contact.photo = FileManager.getUrl(this.result.photo[0]);
} catch (e) {}
}
if (!contact.photo) {
contact.photo = 'img/placeholders/UserAvatar' + ((Math.abs(count) % 8) + 1) + '@2x.png';
}
contact.photo = $sce.trustAsResourceUrl(contact.photo);
count++;
contacts.push(contact);
}
if (!this.result || count >= 1000) {
deferred.resolve(contacts);
return;
}
this['continue']();
}
request.onerror = function (e) {
console.log('phonebook error', e, e.type, e.message);
deferred.reject(e);
}
return deferred.promise;
}
})
.service('AppChatsManager', function ($q, $rootScope, $modal, _, MtpApiManager, AppUsersManager, AppPhotosManager, RichTextProcessor) {
var chats = {},
usernames = {},
channelAccess = {},
megagroups = {},
cachedPhotoLocations = {};
11 years ago
function saveApiChats (apiChats) {
angular.forEach(apiChats, saveApiChat);
};
function saveApiChat (apiChat) {
if (!angular.isObject(apiChat)) {
return;
}
apiChat.rTitle = RichTextProcessor.wrapRichText(apiChat.title, {noLinks: true, noLinebreaks: true}) || _('chat_title_deleted');
var result = chats[apiChat.id];
var titleWords = SearchIndexManager.cleanSearchText(apiChat.title || '').split(' ');
var firstWord = titleWords.shift();
var lastWord = titleWords.pop();
apiChat.initials = firstWord.charAt(0) + (lastWord ? lastWord.charAt(0) : firstWord.charAt(1));
apiChat.num = (Math.abs(apiChat.id >> 1) % 8) + 1;
if (apiChat.pFlags === undefined) {
apiChat.pFlags = {};
}
if (apiChat.pFlags.min) {
if (result !== undefined) {
return;
}
}
if (apiChat.username) {
var searchUsername = SearchIndexManager.cleanUsername(apiChat.username);
usernames[searchUsername] = apiChat.id;
}
if (result === undefined) {
result = chats[apiChat.id] = apiChat;
11 years ago
} else {
safeReplaceObject(result, apiChat);
$rootScope.$broadcast('chat_update', apiChat.id);
}
if (cachedPhotoLocations[apiChat.id] !== undefined) {
safeReplaceObject(cachedPhotoLocations[apiChat.id], apiChat && apiChat.photo && apiChat.photo.photo_small || {empty: true});
11 years ago
}
};
function getChat (id) {
return chats[id] || {id: id, deleted: true, access_hash: channelAccess[id]};
11 years ago
}
function hasRights (id, action) {
if (chats[id] === undefined) {
return false;
}
var chat = getChat(id);
if (chat._ == 'chatForbidden' ||
chat._ == 'channelForbidden' ||
chat.pFlags.kicked ||
chat.pFlags.left) {
return false;
}
if (chat.pFlags.creator) {
return true;
}
switch (action) {
case 'send':
if (chat._ == 'channel' &&
!chat.pFlags.megagroup &&
!chat.pFlags.editor) {
return false;
}
break;
case 'edit_title':
case 'edit_photo':
case 'invite':
if (chat._ == 'channel') {
if (chat.pFlags.megagroup) {
if (!chat.pFlags.editor &&
!(action == 'invite' && chat.pFlags.democracy)) {
return false;
}
} else {
return false;
}
} else {
if (chat.pFlags.admins_enabled &&
!chat.pFlags.admin) {
return false;
}
}
break;
}
return true;
}
function resolveUsername (username) {
return usernames[username] || 0;
}
function saveChannelAccess (id, accessHash) {
channelAccess[id] = accessHash;
}
function saveIsMegagroup (id) {
megagroups[id] = true;
}
function isChannel (id) {
var chat = chats[id];
if (chat && (chat._ == 'channel' || chat._ == 'channelForbidden') ||
channelAccess[id]) {
return true;
}
return false;
}
function isMegagroup (id) {
if (megagroups[id]) {
return true;
}
var chat = chats[id];
if (chat && chat._ == 'channel' && chat.pFlags.megagroup) {
return true;
}
return false;
}
function getChatInput (id) {
return id || 0;
}
function getChannelInput (id) {
if (!id) {
return {_: 'inputChannelEmpty'};
}
return {
_: 'inputChannel',
channel_id: id,
access_hash: getChat(id).access_hash || channelAccess[id] || 0
}
}
function hasChat (id, allowMin) {
var chat = chats[id];
return angular.isObject(chat) && (allowMin || !chat.pFlags.min);
}
function getChatPhoto(id) {
11 years ago
var chat = getChat(id);
if (cachedPhotoLocations[id] === undefined) {
cachedPhotoLocations[id] = chat && chat.photo && chat.photo.photo_small || {empty: true};
}
11 years ago
return {
placeholder: 'img/placeholders/GroupAvatar' + Math.ceil(chat.num / 2) + '@2x.png',
location: cachedPhotoLocations[id]
11 years ago
};
}
function getChatString (id) {
var chat = getChat(id);
if (isChannel(id)) {
return (isMegagroup(id) ? 's' : 'c') + id + '_' + chat.access_hash;
}
11 years ago
return 'g' + id;
}
function wrapForFull (id, fullChat) {
var chatFull = angular.copy(fullChat),
chat = getChat(id);
if (chatFull.participants && chatFull.participants._ == 'chatParticipants') {
MtpApiManager.getUserID().then(function (myID) {
var isAdmin = chat.pFlags.creator || chat.pFlags.admins_enabled && chat.pFlags.admin;
angular.forEach(chatFull.participants.participants, function(participant){
participant.canLeave = myID == participant.user_id;
participant.canKick = !participant.canLeave && (
chat.pFlags.creator ||
participant._ == 'chatParticipant' && (isAdmin || myID == participant.inviter_id)
);
// just for order by last seen
participant.user = AppUsersManager.getUser(participant.user_id);
});
11 years ago
});
}
if (chatFull.participants && chatFull.participants._ == 'channelParticipants') {
var isAdmin = chat.pFlags.creator || chat.pFlags.editor || chat.pFlags.moderator;
angular.forEach(chatFull.participants.participants, function(participant) {
participant.canLeave = !chat.pFlags.creator && participant._ == 'channelParticipantSelf';
participant.canKick = isAdmin && participant._ == 'channelParticipant';
// just for order by last seen
participant.user = AppUsersManager.getUser(participant.user_id);
});
}
if (chatFull.about) {
chatFull.rAbout = RichTextProcessor.wrapRichText(chatFull.about, {noLinebreaks: true});
}
11 years ago
chatFull.peerString = getChatString(id);
chatFull.chat = chat;
return chatFull;
}
function openChat (chatID, accessHash) {
var scope = $rootScope.$new();
scope.chatID = chatID;
if (isChannel(chatID)) {
var modalInstance = $modal.open({
templateUrl: templateUrl('channel_modal'),
controller: 'ChannelModalController',
scope: scope,
windowClass: 'chat_modal_window channel_modal_window mobile_modal'
});
} else {
var modalInstance = $modal.open({
templateUrl: templateUrl('chat_modal'),
controller: 'ChatModalController',
scope: scope,
windowClass: 'chat_modal_window mobile_modal'
});
}
11 years ago
}
$rootScope.$on('apiUpdate', function (e, update) {
// console.log('on apiUpdate', update);
switch (update._) {
case 'updateChannel':
var channelID = update.channel_id;
$rootScope.$broadcast('channel_settings', {channelID: channelID});
break;
}
});
11 years ago
return {
saveApiChats: saveApiChats,
saveApiChat: saveApiChat,
getChat: getChat,
isChannel: isChannel,
isMegagroup: isMegagroup,
hasRights: hasRights,
saveChannelAccess: saveChannelAccess,
saveIsMegagroup: saveIsMegagroup,
getChatInput: getChatInput,
getChannelInput: getChannelInput,
11 years ago
getChatPhoto: getChatPhoto,
getChatString: getChatString,
resolveUsername: resolveUsername,
hasChat: hasChat,
11 years ago
wrapForFull: wrapForFull,
openChat: openChat
}
})
.service('AppPeersManager', function ($q, qSync, AppUsersManager, AppChatsManager, MtpApiManager) {
11 years ago
function getInputPeer (peerString) {
var firstChar = peerString.charAt(0),
peerParams = peerString.substr(1).split('_');
if (firstChar == 'u') {
AppUsersManager.saveUserAccess(peerParams[0], peerParams[1]);
return {
_: 'inputPeerUser',
user_id: peerParams[0],
access_hash: peerParams[1]
};
}
else if (firstChar == 'c' || firstChar == 's') {
AppChatsManager.saveChannelAccess(peerParams[0], peerParams[1]);
if (firstChar == 's') {
AppChatsManager.saveIsMegagroup(peerParams[0]);
}
return {
_: 'inputPeerChannel',
channel_id: peerParams[0],
access_hash: peerParams[1] || 0
};
}
else {
return {
_: 'inputPeerChat',
chat_id: peerParams[0]
}
}
}
function getInputPeerByID (peerID) {
if (!peerID) {
return {_: 'inputPeerEmpty'};
}
if (peerID < 0) {
var chatID = -peerID;
if (!AppChatsManager.isChannel(chatID)) {
return {
_: 'inputPeerChat',
chat_id: chatID
11 years ago
};
} else {
11 years ago
return {
_: 'inputPeerChannel',
channel_id: chatID,
access_hash: AppChatsManager.getChat(chatID).access_hash || 0
}
11 years ago
}
}
return {
_: 'inputPeerUser',
user_id: peerID,
access_hash: AppUsersManager.getUser(peerID).access_hash || 0
};
}
function getPeerSearchText (peerID) {
var text;
if (peerID > 0) {
text = '%pu ' + AppUsersManager.getUserSearchText(peerID);
} else if (peerID < 0) {
var chat = AppChatsManager.getChat(-peerID);
text = '%pg ' + (chat.title || '');
}
return text;
}
function getPeerString (peerID) {
if (peerID > 0) {
return AppUsersManager.getUserString(peerID);
}
return AppChatsManager.getChatString(-peerID);
}
function getOutputPeer (peerID) {
if (peerID > 0) {
return {_: 'peerUser', user_id: peerID};
}
var chatID = -peerID;
if (AppChatsManager.isChannel(chatID)) {
return {_: 'peerChannel', channel_id: chatID}
}
return {_: 'peerChat', chat_id: chatID}
}
function resolveUsername (username) {
var searchUserName = SearchIndexManager.cleanUsername(username);
var foundUserID, foundChatID, foundPeerID, foundUsername;
if (foundUserID = AppUsersManager.resolveUsername(searchUserName)) {
foundUsername = AppUsersManager.getUser(foundUserID).username;
if (SearchIndexManager.cleanUsername(foundUsername) == searchUserName) {
return qSync.when(foundUserID);
}
}
if (foundChatID = AppChatsManager.resolveUsername(searchUserName)) {
foundUsername = AppChatsManager.getChat(foundChatID).username;
if (SearchIndexManager.cleanUsername(foundUsername) == searchUserName) {
return qSync.when(-foundChatID);
11 years ago
}
}
11 years ago
return MtpApiManager.invokeApi('contacts.resolveUsername', {username: username}).then(function (resolveResult) {
AppUsersManager.saveApiUsers(resolveResult.users);
AppChatsManager.saveApiChats(resolveResult.chats);
return getPeerID(resolveResult.peer);
});
}
function getPeerID (peerString) {
if (angular.isObject(peerString)) {
return peerString.user_id
? peerString.user_id
: -(peerString.channel_id || peerString.chat_id);
11 years ago
}
var isUser = peerString.charAt(0) == 'u',
peerParams = peerString.substr(1).split('_');
return isUser ? peerParams[0] : -peerParams[0] || 0;
}
function getPeer (peerID) {
return peerID > 0
? AppUsersManager.getUser(peerID)
: AppChatsManager.getChat(-peerID);
}
function getPeerPhoto (peerID) {
return peerID > 0
? AppUsersManager.getUserPhoto(peerID)
: AppChatsManager.getChatPhoto(-peerID)
}
function isChannel (peerID) {
return (peerID < 0) && AppChatsManager.isChannel(-peerID);
}
function isMegagroup (peerID) {
return (peerID < 0) && AppChatsManager.isMegagroup(-peerID);
}
function isBot (peerID) {
return (peerID > 0) && AppUsersManager.isBot(peerID);
}
return {
getInputPeer: getInputPeer,
getInputPeerByID: getInputPeerByID,
getPeerSearchText: getPeerSearchText,
getPeerString: getPeerString,
getOutputPeer: getOutputPeer,
getPeerID: getPeerID,
getPeer: getPeer,
getPeerPhoto: getPeerPhoto,
resolveUsername: resolveUsername,
isChannel: isChannel,
isMegagroup: isMegagroup,
isBot: isBot
11 years ago
}
})
.service('AppProfileManager', function ($q, $rootScope, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, NotificationsManager, MtpApiManager, ApiUpdatesManager, RichTextProcessor) {
10 years ago
var botInfos = {};
var chatsFull = {};
var chatFullPromises = {};
10 years ago
function saveBotInfo (botInfo) {
var botID = botInfo && botInfo.user_id;
if (!botID) {
return false;
}
var commands = {};
angular.forEach(botInfo.commands, function (botCommand) {
commands[botCommand.command] = botCommand.description;
})
return botInfos[botID] = {
id: botID,
10 years ago
version: botInfo.version,
shareText: botInfo.share_text,
description: botInfo.description,
commands: commands
};
}
function getProfile (id, override) {
return MtpApiManager.invokeApi('users.getFullUser', {
id: AppUsersManager.getUserInput(id)
}).then(function (userFull) {
if (override && override.phone_number) {
userFull.user.phone = override.phone_number;
if (override.first_name || override.last_name) {
userFull.user.first_name = override.first_name;
userFull.user.last_name = override.last_name;
}
AppUsersManager.saveApiUser(userFull.user);
} else {
AppUsersManager.saveApiUser(userFull.user, true);
}
if (userFull.profile_photo) {
AppPhotosManager.savePhoto(userFull.profile_photo, {
user_id: id
});
}
if (userFull.about !== undefined) {
userFull.rAbout = RichTextProcessor.wrapRichText(userFull.about, {noLinebreaks: true});
}
10 years ago
NotificationsManager.savePeerSettings(id, userFull.notify_settings);
if (userFull.bot_info) {
userFull.bot_info = saveBotInfo(userFull.bot_info);
}
10 years ago
return userFull;
});
}
function getPeerBots (peerID) {
var peerBots = [];
if (peerID >= 0 && !AppUsersManager.isBot(peerID) ||
(AppPeersManager.isChannel(peerID) && !AppPeersManager.isMegagroup(peerID))) {
return $q.when(peerBots);
}
if (peerID >= 0) {
return getProfile(peerID).then(function (userFull) {
var botInfo = userFull.bot_info;
if (botInfo && botInfo._ != 'botInfoEmpty') {
10 years ago
peerBots.push(botInfo);
}
10 years ago
return peerBots;
});
}
return getChatFull(-peerID).then(function (chatFull) {
angular.forEach(chatFull.bot_info, function (botInfo) {
peerBots.push(saveBotInfo(botInfo));
});
return peerBots;
});
}
function getChatFull(id) {
if (AppChatsManager.isChannel(id)) {
return getChannelFull(id);
}
if (chatsFull[id] !== undefined) {
var chat = AppChatsManager.getChat(id);
if (chat.version == chatsFull[id].participants.version ||
chat.pFlags.left) {
return $q.when(chatsFull[id]);
}
}
if (chatFullPromises[id] !== undefined) {
return chatFullPromises[id];
}
9 years ago
console.trace(dT(), 'Get chat full', id, AppChatsManager.getChat(id));
return chatFullPromises[id] = MtpApiManager.invokeApi('messages.getFullChat', {
chat_id: AppChatsManager.getChatInput(id)
}).then(function (result) {
AppChatsManager.saveApiChats(result.chats);
AppUsersManager.saveApiUsers(result.users);
var fullChat = result.full_chat;
if (fullChat && fullChat.chat_photo.id) {
AppPhotosManager.savePhoto(fullChat.chat_photo);
}
NotificationsManager.savePeerSettings(-id, fullChat.notify_settings);
delete chatFullPromises[id];
chatsFull[id] = fullChat;
$rootScope.$broadcast('chat_full_update', id);
return fullChat;
});
}
function getChatInviteLink (id, force) {
return getChatFull(id).then(function (chatFull) {
if (!force &&
chatFull.exported_invite &&
chatFull.exported_invite._ == 'chatInviteExported') {
return chatFull.exported_invite.link;
}
var promise;
if (AppChatsManager.isChannel(id)) {
promise = MtpApiManager.invokeApi('channels.exportInvite', {
channel: AppChatsManager.getChannelInput(id)
});
} else {
promise = MtpApiManager.invokeApi('messages.exportChatInvite', {
chat_id: AppChatsManager.getChatInput(id)
});
}
return promise.then(function (exportedInvite) {
if (chatsFull[id] !== undefined) {
chatsFull[id].exported_invite = exportedInvite;
}
return exportedInvite.link;
});
});
}
function getChannelParticipants (id) {
return MtpApiManager.invokeApi('channels.getParticipants', {
channel: AppChatsManager.getChannelInput(id),
filter: {_: 'channelParticipantsRecent'},
offset: 0,
limit: AppChatsManager.isMegagroup(id) ? 50 : 200
}).then(function (result) {
AppUsersManager.saveApiUsers(result.users);
var participants = result.participants;
var chat = AppChatsManager.getChat(id);
if (!chat.pFlags.kicked && !chat.pFlags.left) {
var myID = AppUsersManager.getSelf().id;
var myIndex = false;
var myParticipant;
for (var i = 0, len = participants.length; i < len; i++) {
if (participants[i].user_id == myID) {
myIndex = i;
break;
}
}
if (myIndex !== false) {
myParticipant = participants[i];
participants.splice(i, 1);
} else {
myParticipant = {_: 'channelParticipantSelf', user_id: myID};
}
participants.unshift(myParticipant);
}
return participants;
});
}
function getChannelFull (id, force) {
if (chatsFull[id] !== undefined && !force) {
return $q.when(chatsFull[id]);
}
if (chatFullPromises[id] !== undefined) {
return chatFullPromises[id];
}
return chatFullPromises[id] = MtpApiManager.invokeApi('channels.getFullChannel', {
channel: AppChatsManager.getChannelInput(id)
}).then(function (result) {
AppChatsManager.saveApiChats(result.chats);
AppUsersManager.saveApiUsers(result.users);
var fullChannel = result.full_chat;
var chat = AppChatsManager.getChat(id);
if (fullChannel && fullChannel.chat_photo.id) {
AppPhotosManager.savePhoto(fullChannel.chat_photo);
}
NotificationsManager.savePeerSettings(-id, fullChannel.notify_settings);
var participantsPromise;
if (fullChannel.flags & 8) {
participantsPromise = getChannelParticipants(id).then(function (participants) {
delete chatFullPromises[id];
fullChannel.participants = {
_: 'channelParticipants',
participants: participants
};
}, function (error) {
error.handled = true;
});
} else {
participantsPromise = $q.when();
}
return participantsPromise.then(function () {
delete chatFullPromises[id];
chatsFull[id] = fullChannel;
$rootScope.$broadcast('chat_full_update', id);
return fullChannel;
});
}, function (error) {
switch (error.type) {
case 'CHANNEL_PRIVATE':
var channel = AppChatsManager.getChat(id);
channel = {_: 'channelForbidden', access_hash: channel.access_hash, title: channel.title};
ApiUpdatesManager.processUpdateMessage({
_: 'updates',
updates: [{
_: 'updateChannel',
channel_id: id
}],
chats: [channel],
users: []
});
break;
}
return $q.reject(error);
});
}
9 years ago
$rootScope.$on('apiUpdate', function (e, update) {
// console.log('on apiUpdate', update);
switch (update._) {
case 'updateChatParticipants':
var participants = update.participants;
var chatFull = chatsFull[participants.id];
if (chatFull !== undefined) {
chatFull.participants = update.participants;
$rootScope.$broadcast('chat_full_update', chatID);
}
break;
case 'updateChatParticipantAdd':
var chatFull = chatsFull[update.chat_id];
if (chatFull !== undefined) {
var participants = chatFull.participants.participants || [];
for (var i = 0, length = participants.length; i < length; i++) {
if (participants[i].user_id == update.user_id) {
return;
}
}
participants.push({
_: 'chatParticipant',
user_id: update.user_id,
inviter_id: update.inviter_id,
date: tsNow(true)
});
chatFull.participants.version = update.version;
$rootScope.$broadcast('chat_full_update', update.chat_id);
}
break;
case 'updateChatParticipantDelete':
var chatFull = chatsFull[update.chat_id];
if (chatFull !== undefined) {
var participants = chatFull.participants.participants || [];
for (var i = 0, length = participants.length; i < length; i++) {
if (participants[i].user_id == update.user_id) {
participants.splice(i, 1);
chatFull.participants.version = update.version;
$rootScope.$broadcast('chat_full_update', update.chat_id);
return;
}
}
}
break;
}
});
$rootScope.$on('chat_update', function (e, chatID) {
var fullChat = chatsFull[chatID];
var chat = AppChatsManager.getChat(chatID);
if (!chat.photo || !fullChat) {
return;
}
9 years ago
var emptyPhoto = chat.photo._ == 'chatPhotoEmpty';
if (emptyPhoto != (fullChat.chat_photo._ == 'photoEmpty')) {
delete chatsFull[chatID];
$rootScope.$broadcast('chat_full_update', chatID);
return;
}
if (emptyPhoto) {
return;
}
var smallUserpic = chat.photo.photo_small;
var smallPhotoSize = AppPhotosManager.choosePhotoSize(fullChat.chat_photo, 0, 0);
if (!angular.equals(smallUserpic, smallPhotoSize.location)) {
delete chatsFull[chatID];
$rootScope.$broadcast('chat_full_update', chatID);
}
});
return {
getPeerBots: getPeerBots,
getProfile: getProfile,
getChatInviteLink: getChatInviteLink,
getChatFull: getChatFull,
getChannelFull: getChannelFull
}
11 years ago
})
.service('AppPhotosManager', function ($modal, $window, $rootScope, MtpApiManager, MtpApiFileManager, AppUsersManager, FileManager) {
var photos = {},
windowW = $(window).width(),
windowH = $(window).height();
11 years ago
function savePhoto (apiPhoto, context) {
if (context) {
angular.extend(apiPhoto, context);
}
11 years ago
photos[apiPhoto.id] = apiPhoto;
11 years ago
angular.forEach(apiPhoto.sizes, function (photoSize) {
if (photoSize._ == 'photoCachedSize') {
MtpApiFileManager.saveSmallFile(photoSize.location, photoSize.bytes);
// Memory
photoSize.size = photoSize.bytes.length;
delete photoSize.bytes;
photoSize._ = 'photoSize';
}
});
};
function choosePhotoSize (photo, width, height) {
if (Config.Navigator.retina) {
width *= 2;
height *= 2;
}
11 years ago
var bestPhotoSize = {_: 'photoSizeEmpty'},
bestDiff = 0xFFFFFF;
angular.forEach(photo.sizes, function (photoSize) {
var diff = Math.abs(photoSize.w * photoSize.h - width * height);
if (diff < bestDiff) {
bestPhotoSize = photoSize;
bestDiff = diff;
}
});
// console.log('choosing', photo, width, height, bestPhotoSize);
11 years ago
return bestPhotoSize;
}
function getUserPhotos (userID, maxID, limit) {
var inputUser = AppUsersManager.getUserInput(userID);
return MtpApiManager.invokeApi('photos.getUserPhotos', {
user_id: inputUser,
offset: 0,
limit: limit || 20,
max_id: maxID || 0
}).then(function (photosResult) {
AppUsersManager.saveApiUsers(photosResult.users);
var photoIDs = [];
var context = {user_id: userID};
for (var i = 0; i < photosResult.photos.length; i++) {
savePhoto(photosResult.photos[i], context);
photoIDs.push(photosResult.photos[i].id)
}
return {
count: photosResult.count || photosResult.photos.length,
photos: photoIDs
};
});
}
function preloadPhoto (photoID) {
if (!photos[photoID]) {
return;
}
var photo = photos[photoID];
var fullWidth = $(window).width() - (Config.Mobile ? 20 : 32);
var fullHeight = $($window).height() - (Config.Mobile ? 150 : 116);
if (fullWidth > 800) {
fullWidth -= 208;
}
var fullPhotoSize = choosePhotoSize(photo, fullWidth, fullHeight);
if (fullPhotoSize && !fullPhotoSize.preloaded) {
fullPhotoSize.preloaded = true;
if (fullPhotoSize.size) {
MtpApiFileManager.downloadFile(fullPhotoSize.location.dc_id, {
_: 'inputFileLocation',
volume_id: fullPhotoSize.location.volume_id,
local_id: fullPhotoSize.location.local_id,
secret: fullPhotoSize.location.secret
}, fullPhotoSize.size);
} else {
MtpApiFileManager.downloadSmallFile(fullPhotoSize.location);
}
}
};
$rootScope.preloadPhoto = preloadPhoto;
function getPhoto (photoID) {
return photos[photoID] || {_: 'photoEmpty'};
}
function wrapForHistory (photoID, options) {
options = options || {};
11 years ago
var photo = angular.copy(photos[photoID]) || {_: 'photoEmpty'},
width = options.website ? 64 : Math.min(windowW - 80, Config.Mobile ? 210 : 260),
height = options.website ? 64 : Math.min(windowH - 100, Config.Mobile ? 210 : 260),
11 years ago
thumbPhotoSize = choosePhotoSize(photo, width, height),
thumb = {
placeholder: 'img/placeholders/PhotoThumbConversation.gif',
11 years ago
width: width,
height: height
};
if (options.website && Config.Mobile) {
width = 50;
height = 50;
}
// console.log('chosen photo size', photoID, thumbPhotoSize);
11 years ago
if (thumbPhotoSize && thumbPhotoSize._ != 'photoSizeEmpty') {
var dim = calcImageInBox(thumbPhotoSize.w, thumbPhotoSize.h, width, height);
thumb.width = dim.w;
thumb.height = dim.h;
11 years ago
thumb.location = thumbPhotoSize.location;
thumb.size = thumbPhotoSize.size;
} else {
thumb.width = 100;
thumb.height = 100;
11 years ago
}
photo.thumb = thumb;
return photo;
}
function wrapForFull (photoID) {
var photo = wrapForHistory(photoID);
var fullWidth = $(window).width() - (Config.Mobile ? 0 : 32);
var fullHeight = $($window).height() - (Config.Mobile ? 0 : 116);
if (!Config.Mobile && fullWidth > 800) {
fullWidth -= 208;
}
var fullPhotoSize = choosePhotoSize(photo, fullWidth, fullHeight);
var full = {
placeholder: 'img/placeholders/PhotoThumbModal.gif'
};
full.width = fullWidth;
full.height = fullHeight;
11 years ago
if (fullPhotoSize && fullPhotoSize._ != 'photoSizeEmpty') {
var wh = calcImageInBox(fullPhotoSize.w, fullPhotoSize.h, fullWidth, fullHeight, true);
full.width = wh.w;
full.height = wh.h;
full.modalWidth = Math.max(full.width, Math.min(400, fullWidth));
11 years ago
full.location = fullPhotoSize.location;
full.size = fullPhotoSize.size;
}
photo.full = full;
return photo;
}
function openPhoto (photoID, list) {
if (!photoID || photoID === '0') {
return false;
}
11 years ago
var scope = $rootScope.$new(true);
11 years ago
scope.photoID = photoID;
var controller = 'PhotoModalController';
if (list && list.p > 0) {
controller = 'UserpicModalController';
scope.userID = list.p;
}
else if (list && list.p < 0) {
controller = 'ChatpicModalController';
scope.chatID = -list.p;
}
else if (list && list.m > 0) {
scope.messageID = list.m;
if (list.w) {
scope.webpageID = list.w;
}
}
11 years ago
var modalInstance = $modal.open({
templateUrl: templateUrl('photo_modal'),
windowTemplateUrl: templateUrl('media_modal_layout'),
controller: controller,
scope: scope,
windowClass: 'photo_modal_window'
11 years ago
});
}
function downloadPhoto (photoID) {
var photo = photos[photoID],
ext = 'jpg',
mimeType = 'image/jpeg',
fileName = 'photo' + photoID + '.' + ext,
fullWidth = Math.max(screen.width || 0, $(window).width() - 36, 800),
fullHeight = Math.max(screen.height || 0, $($window).height() - 150, 800),
fullPhotoSize = choosePhotoSize(photo, fullWidth, fullHeight),
inputFileLocation = {
_: 'inputFileLocation',
volume_id: fullPhotoSize.location.volume_id,
local_id: fullPhotoSize.location.local_id,
secret: fullPhotoSize.location.secret
};
FileManager.chooseSave(fileName, ext, mimeType).then(function (writableFileEntry) {
if (writableFileEntry) {
MtpApiFileManager.downloadFile(
fullPhotoSize.location.dc_id, inputFileLocation, fullPhotoSize.size, {
mime: mimeType,
toFileEntry: writableFileEntry
}).then(function () {
// console.log('file save done');
}, function (e) {
console.log('photo download failed', e);
});
}
}, function () {
var cachedBlob = MtpApiFileManager.getCachedFile(inputFileLocation);
if (cachedBlob) {
return FileManager.download(cachedBlob, mimeType, fileName);
}
MtpApiFileManager.downloadFile(
fullPhotoSize.location.dc_id, inputFileLocation, fullPhotoSize.size, {mime: mimeType}
).then(function (blob) {
FileManager.download(blob, mimeType, fileName);
}, function (e) {
console.log('photo download failed', e);
});
});
};
11 years ago
$rootScope.openPhoto = openPhoto;
return {
savePhoto: savePhoto,
preloadPhoto: preloadPhoto,
getUserPhotos: getUserPhotos,
getPhoto: getPhoto,
choosePhotoSize: choosePhotoSize,
11 years ago
wrapForHistory: wrapForHistory,
wrapForFull: wrapForFull,
openPhoto: openPhoto,
downloadPhoto: downloadPhoto
11 years ago
}
})
.service('AppWebPagesManager', function ($modal, $sce, $window, $rootScope, MtpApiManager, AppPhotosManager, AppDocsManager, RichTextProcessor) {
var webpages = {};
var pendingWebPages = {};
function saveWebPage (apiWebPage, messageID, mediaContext) {
if (apiWebPage.photo && apiWebPage.photo._ === 'photo') {
AppPhotosManager.savePhoto(apiWebPage.photo, mediaContext);
} else {
delete apiWebPage.photo;
}
if (apiWebPage.document && apiWebPage.document._ === 'document') {
AppDocsManager.saveDoc(apiWebPage.document, mediaContext);
} else {
if (apiWebPage.type == 'document') {
delete apiWebPage.type;
}
delete apiWebPage.document;
}
var siteName = apiWebPage.site_name;
var title = apiWebPage.title || apiWebPage.author || siteName;
if (siteName &&
title == siteName) {
delete apiWebPage.site_name;
}
apiWebPage.rTitle = RichTextProcessor.wrapRichText(title, {noLinks: true, noLinebreaks: true});
var contextHashtag = '';
if (siteName == 'GitHub') {
var matches = apiWebPage.url.match(/(https?:\/\/github\.com\/[^\/]+\/[^\/]+)/);
if (matches) {
contextHashtag = matches[0] + '/issues/{1}';
}
}
// delete apiWebPage.description;
apiWebPage.rDescription = RichTextProcessor.wrapRichText(
apiWebPage.description, {
contextSite: siteName || 'external',
contextHashtag: contextHashtag
}
);
if (apiWebPage.type != 'photo' &&
apiWebPage.type != 'video' &&
apiWebPage.type != 'gif' &&
apiWebPage.type != 'document' &&
apiWebPage.type != 'gif' &&
!apiWebPage.description &&
apiWebPage.photo) {
apiWebPage.type = 'photo';
}
if (messageID) {
if (pendingWebPages[apiWebPage.id] === undefined) {
pendingWebPages[apiWebPage.id] = {};
}
pendingWebPages[apiWebPage.id][messageID] = true;
webpages[apiWebPage.id] = apiWebPage;
}
if (webpages[apiWebPage.id] === undefined) {
webpages[apiWebPage.id] = apiWebPage;
} else {
safeReplaceObject(webpages[apiWebPage.id], apiWebPage);
}
if (!messageID &&
pendingWebPages[apiWebPage.id] !== undefined) {
var msgs = [];
angular.forEach(pendingWebPages[apiWebPage.id], function (t, msgID) {
msgs.push(msgID);
});
$rootScope.$broadcast('webpage_updated', {
id: apiWebPage.id,
msgs: msgs
});
}
};
function openEmbed (webpageID, messageID) {
var scope = $rootScope.$new(true);
scope.webpageID = webpageID;
scope.messageID = messageID;
$modal.open({
templateUrl: templateUrl('embed_modal'),
windowTemplateUrl: templateUrl('media_modal_layout'),
controller: 'EmbedModalController',
scope: scope,
windowClass: 'photo_modal_window'
});
}
function wrapForHistory (webPageID) {
var webPage = angular.copy(webpages[webPageID]) || {_: 'webPageEmpty'};
if (webPage.photo && webPage.photo.id) {
webPage.photo = AppPhotosManager.wrapForHistory(webPage.photo.id, {website: webPage.type != 'photo' && webPage.type != 'video'});
}
if (webPage.document && webPage.document.id) {
webPage.document = AppDocsManager.wrapForHistory(webPage.document.id);
}
return webPage;
}
10 years ago
function wrapForFull (webPageID) {
var webPage = wrapForHistory(webPageID);
if (!webPage.embed_url) {
return webPage;
}
var fullWidth = $(window).width() - (Config.Mobile ? 0 : 10);
var fullHeight = $($window).height() - (Config.Mobile ? 92 : 150);
if (!Config.Mobile && fullWidth > 800) {
fullWidth -= 208;
}
var full = {
width: fullWidth,
height: fullHeight,
};
if (!webPage.embed_width || !webPage.embed_height) {
full.height = full.width = Math.min(fullWidth, fullHeight);
} else {
var wh = calcImageInBox(webPage.embed_width, webPage.embed_height, fullWidth, fullHeight);
full.width = wh.w;
full.height = wh.h;
}
var embedTag = Config.Modes.chrome_packed ? 'webview' : 'iframe';
var embedType = webPage.embed_type != 'iframe' ? webPage.embed_type || 'text/html' : 'text/html';
10 years ago
var embedHtml = '<' + embedTag + ' src="' + encodeEntities(webPage.embed_url) + '" type="' + encodeEntities(embedType) + '" frameborder="0" border="0" webkitallowfullscreen mozallowfullscreen allowfullscreen width="' + full.width + '" height="' + full.height + '" style="width: ' + full.width + 'px; height: ' + full.height + 'px;"></' + embedTag + '>';
10 years ago
full.html = $sce.trustAs('html', embedHtml);
webPage.full = full;
return webPage;
}
$rootScope.$on('apiUpdate', function (e, update) {
switch (update._) {
case 'updateWebPage':
saveWebPage(update.webpage);
break;
}
});
return {
saveWebPage: saveWebPage,
openEmbed: openEmbed,
10 years ago
wrapForFull: wrapForFull,
wrapForHistory: wrapForHistory
}
})
9 years ago
.service('AppDocsManager', function ($sce, $rootScope, $modal, $window, $q, $timeout, RichTextProcessor, MtpApiFileManager, FileManager, qSync) {
var docs = {},
docsForHistory = {},
windowW = $(window).width(),
windowH = $(window).height();
11 years ago
function saveDoc (apiDoc, context) {
11 years ago
docs[apiDoc.id] = apiDoc;
if (context) {
angular.extend(apiDoc, context);
}
11 years ago
if (apiDoc.thumb && apiDoc.thumb._ == 'photoCachedSize') {
MtpApiFileManager.saveSmallFile(apiDoc.thumb.location, apiDoc.thumb.bytes);
// Memory
apiDoc.thumb.size = apiDoc.thumb.bytes.length;
delete apiDoc.thumb.bytes;
apiDoc.thumb._ = 'photoSize';
}
if (apiDoc.thumb && apiDoc.thumb._ == 'photoSizeEmpty') {
delete apiDoc.thumb;
}
angular.forEach(apiDoc.attributes, function (attribute) {
switch (attribute._) {
case 'documentAttributeFilename':
apiDoc.file_name = attribute.file_name;
break;
case 'documentAttributeAudio':
10 years ago
apiDoc.duration = attribute.duration;
apiDoc.audioTitle = attribute.title;
apiDoc.audioPerformer = attribute.performer;
apiDoc.type = attribute.pFlags.voice ? 'voice' : 'audio';
10 years ago
break;
case 'documentAttributeVideo':
apiDoc.duration = attribute.duration;
9 years ago
apiDoc.w = attribute.w;
apiDoc.h = attribute.h;
if (apiDoc.thumb) {
apiDoc.type = 'video';
}
break;
case 'documentAttributeSticker':
9 years ago
apiDoc.sticker = true;
if (attribute.alt !== undefined) {
apiDoc.stickerEmojiRaw = attribute.alt;
apiDoc.stickerEmoji = RichTextProcessor.wrapRichText(apiDoc.stickerEmojiRaw, {noLinks: true, noLinebreaks: true});
}
if (attribute.stickerset) {
if (attribute.stickerset._ == 'inputStickerSetEmpty') {
delete attribute.stickerset;
}
else if (attribute.stickerset._ == 'inputStickerSetID') {
apiDoc.stickerSetInput = attribute.stickerset;
}
}
if (apiDoc.thumb && apiDoc.mime_type == 'image/webp') {
apiDoc.type = 'sticker';
}
break;
case 'documentAttributeImageSize':
apiDoc.w = attribute.w;
apiDoc.h = attribute.h;
break;
9 years ago
case 'documentAttributeAnimated':
if ((apiDoc.mime_type == 'image/gif' || apiDoc.mime_type == 'video/mp4') &&
apiDoc.thumb) {
apiDoc.type = 'gif';
}
9 years ago
apiDoc.animated = true;
break;
}
});
if (!apiDoc.mime_type) {
switch (apiDoc.type) {
case 'gif': apiDoc.mime_type = 'video/mp4'; break;
case 'video': apiDoc.mime_type = 'video/mp4'; break;
case 'sticker': apiDoc.mime_type = 'image/webp'; break;
case 'audio': apiDoc.mime_type = 'audio/mpeg'; break;
case 'voice': apiDoc.mime_type = 'audio/ogg'; break;
default: apiDoc.mime_type = 'application/octet-stream'; break;
}
}
9 years ago
if (!apiDoc.file_name) {
apiDoc.file_name = '';
9 years ago
}
if (apiDoc._ == 'documentEmpty') {
apiDoc.size = 0;
}
9 years ago
11 years ago
};
function getDoc (docID) {
return docs[docID] || {_: 'documentEmpty'};
}
function hasDoc (docID) {
return docs[docID] !== undefined;
}
function getFileName(doc) {
if (doc.file_name) {
return doc.file_name;
}
var fileExt = '.' + doc.mime_type.split('/')[1];
if (fileExt == '.octet-stream') {
fileExt = '';
}
return 't_' + (doc.type || 'file') + doc.id + fileExt;
}
11 years ago
function wrapForHistory (docID) {
if (docsForHistory[docID] !== undefined) {
return docsForHistory[docID];
}
11 years ago
var doc = angular.copy(docs[docID]),
thumbPhotoSize = doc.thumb,
inlineImage = false,
boxWidth, boxHeight, thumb, dim;
switch (doc.type) {
case 'video':
boxWidth = Math.min(windowW - 80, Config.Mobile ? 210 : 150),
boxHeight = Math.min(windowH - 100, Config.Mobile ? 210 : 150);
break;
case 'sticker':
inlineImage = true;
boxWidth = Math.min(windowW - 80, Config.Mobile ? 128 : 192);
boxHeight = Math.min(windowH - 100, Config.Mobile ? 128 : 192);
break;
11 years ago
case 'gif':
inlineImage = true;
boxWidth = Math.min(windowW - 80, Config.Mobile ? 210 : 260);
boxHeight = Math.min(windowH - 100, Config.Mobile ? 210 : 260);
break;
default:
boxWidth = boxHeight = 100;
}
if (inlineImage && doc.w && doc.h) {
dim = calcImageInBox(doc.w, doc.h, boxWidth, boxHeight);
}
else if (thumbPhotoSize) {
dim = calcImageInBox(thumbPhotoSize.w, thumbPhotoSize.h, boxWidth, boxHeight);
}
if (dim) {
thumb = {
width: dim.w,
height: dim.h
};
if (thumbPhotoSize) {
thumb.location = thumbPhotoSize.location;
thumb.size = thumbPhotoSize.size;
}
} else {
thumb = false;
11 years ago
}
doc.thumb = thumb;
doc.withPreview = !Config.Mobile && doc.mime_type.match(/^image\/(gif|png|jpeg|jpg|bmp|tiff)/) ? 1 : 0;
return docsForHistory[docID] = doc;
11 years ago
}
function updateDocDownloaded (docID) {
11 years ago
var doc = docs[docID],
historyDoc = docsForHistory[docID] || doc || {},
11 years ago
inputFileLocation = {
_: 'inputDocumentFileLocation',
id: docID,
access_hash: doc.access_hash,
file_name: getFileName(doc)
};
if (historyDoc.downloaded === undefined) {
MtpApiFileManager.getDownloadedFile(inputFileLocation, doc.size).then(function () {
historyDoc.downloaded = true;
}, function () {
historyDoc.downloaded = false;
});
}
}
function downloadDoc (docID, toFileEntry) {
var doc = docs[docID],
historyDoc = docsForHistory[docID] || doc || {},
inputFileLocation = {
_: 'inputDocumentFileLocation',
id: docID,
access_hash: doc.access_hash,
file_name: getFileName(doc)
};
if (doc._ == 'documentEmpty') {
return $q.reject();
}
if (historyDoc.downloaded && !toFileEntry) {
var cachedBlob = MtpApiFileManager.getCachedFile(inputFileLocation);
if (cachedBlob) {
return qSync.when(cachedBlob);
}
}
historyDoc.progress = {enabled: !historyDoc.downloaded, percent: 1, total: doc.size};
var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, {
mime: doc.mime_type || 'application/octet-stream',
toFileEntry: toFileEntry
});
downloadPromise.then(function (blob) {
if (blob) {
FileManager.getFileCorrectUrl(blob, doc.mime_type).then(function (url) {
var trustedUrl = $sce.trustAsResourceUrl(url);
historyDoc.url = trustedUrl;
doc.url = trustedUrl;
})
historyDoc.downloaded = true;
}
9 years ago
historyDoc.progress.percent = 100;
$timeout(function () {
delete historyDoc.progress;
});
console.log('file save done');
}, function (e) {
console.log('document download failed', e);
historyDoc.progress.enabled = false;
}, function (progress) {
console.log('dl progress', progress);
historyDoc.progress.enabled = true;
historyDoc.progress.done = progress.done;
historyDoc.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total));
$rootScope.$broadcast('history_update');
});
historyDoc.progress.cancel = downloadPromise.cancel;
return downloadPromise;
}
function openDoc (docID, messageID) {
var scope = $rootScope.$new(true);
scope.docID = docID;
scope.messageID = messageID;
var modalInstance = $modal.open({
templateUrl: templateUrl('document_modal'),
windowTemplateUrl: templateUrl('media_modal_layout'),
controller: 'DocumentModalController',
scope: scope,
windowClass: 'document_modal_window'
});
}
function saveDocFile (docID) {
var doc = docs[docID],
historyDoc = docsForHistory[docID] || doc || {},
mimeType = doc.mime_type,
fileName = getFileName(doc),
ext = (fileName.split('.', 2) || [])[1] || '';
11 years ago
FileManager.chooseSave(getFileName(doc), ext, doc.mime_type).then(function (writableFileEntry) {
if (writableFileEntry) {
downloadDoc(docID, writableFileEntry);
}
}, function () {
downloadDoc(docID).then(function (blob) {
FileManager.download(blob, doc.mime_type, fileName);
});
});
11 years ago
}
function wrapVideoForFull (docID) {
var doc = wrapForHistory(docID),
fullWidth = Math.min($(window).width() - (Config.Mobile ? 0 : 60), 542),
fullHeight = $(window).height() - (Config.Mobile ? 92 : 150),
full = {
placeholder: 'img/placeholders/docThumbModal.gif',
width: fullWidth,
height: fullHeight,
};
if (!doc.w || !doc.h) {
full.height = full.width = Math.min(fullWidth, fullHeight);
} else {
var dim = calcImageInBox(doc.w, doc.h, fullWidth, fullHeight);
full.width = dim.w;
full.height = dim.h;
}
doc.full = full;
doc.fullThumb = angular.copy(doc.thumb);
doc.fullThumb.width = full.width;
doc.fullThumb.height = full.height;
return doc;
}
function openVideo (docID, messageID) {
var scope = $rootScope.$new(true);
scope.docID = docID;
scope.messageID = messageID;
return $modal.open({
templateUrl: templateUrl('video_modal'),
windowTemplateUrl: templateUrl('media_modal_layout'),
controller: 'VideoModalController',
scope: scope,
windowClass: 'video_modal_window'
});
}
return {
saveDoc: saveDoc,
getDoc: getDoc,
hasDoc: hasDoc,
wrapForHistory: wrapForHistory,
wrapVideoForFull: wrapVideoForFull,
updateDocDownloaded: updateDocDownloaded,
downloadDoc: downloadDoc,
openDoc: openDoc,
openVideo: openVideo,
saveDocFile: saveDocFile
}
})
.service('AppStickersManager', function ($q, $rootScope, $modal, _, FileManager, MtpApiManager, AppDocsManager, Storage, ApiUpdatesManager) {
var started = false;
var applied = false;
var currentStickerSets = [];
9 years ago
$rootScope.$on('apiUpdate', function (e, update) {
if (update._ != 'updateStickerSets' &&
update._ != 'updateNewStickerSet' &&
update._ != 'updateDelStickerSet' &&
update._ != 'updateStickerSetsOrder') {
return false;
}
return Storage.get('all_stickers').then(function (stickers) {
if (!stickers ||
stickers.layer != Config.Schema.API.layer) {
$rootScope.$broadcast('stickers_changed');
}
switch (update._) {
case 'updateNewStickerSet':
var fullSet = update.stickerset;
var set = fullSet.set;
var pos = false;
for (var i = 0, len = stickers.sets.length; i < len; i++) {
if (stickers.sets[i].id == set.id) {
pos = i;
break;
}
}
if (pos !== false) {
stickers.sets.splice(pos, 1);
}
set.pFlags.installed = true;
stickers.sets.unshift(set);
stickers.fullSets[set.id] = fullSet;
break;
case 'updateDelStickerSet':
var set;
for (var i = 0, len = stickers.sets.length; i < len; i++) {
set = stickers.sets[i];
if (set.id == update.id) {
set.pFlags.installed = false;
stickers.sets.splice(i, 1);
break;
}
}
delete stickers.fullSets[update.id];
break;
case 'updateStickerSetsOrder':
var order = update.order;
stickers.sets.sort(function (a, b) {
return order.indexOf(a.id) - order.indexOf(b.id);
});
break;
}
stickers.hash = getStickerSetsHash(stickers.sets);
stickers.date = 0;
Storage.set({all_stickers: stickers}).then(function () {
$rootScope.$broadcast('stickers_changed');
});
});
9 years ago
});
9 years ago
return {
start: start,
getStickers: getStickers,
openStickersetLink: openStickersetLink,
openStickerset: openStickerset,
installStickerset: installStickerset,
pushPopularSticker: pushPopularSticker,
9 years ago
getStickerset: getStickerset
};
function start () {
if (!started) {
started = true;
setTimeout(getStickers, 1000);
}
}
function getStickers (force) {
return Storage.get('all_stickers').then(function (stickers) {
var layer = Config.Schema.API.layer;
if (stickers.layer != layer) {
stickers = false;
}
if (stickers && stickers.date > tsNow(true) && !force) {
return processRawStickers(stickers);
}
return MtpApiManager.invokeApi('messages.getAllStickers', {
hash: stickers && stickers.hash || ''
}).then(function (newStickers) {
var notModified = newStickers._ == 'messages.allStickersNotModified';
if (notModified) {
newStickers = stickers;
}
newStickers.date = tsNow(true) + 3600;
newStickers.layer = layer;
delete newStickers._;
if (notModified) {
Storage.set({all_stickers: newStickers});
return processRawStickers(newStickers);
}
return getStickerSets(newStickers, stickers && stickers.fullSets).then(function () {
Storage.set({all_stickers: newStickers});
return processRawStickers(newStickers);
});
});
})
}
function processRawStickers(stickers) {
if (applied !== stickers.hash) {
applied = stickers.hash;
9 years ago
var i, j, len1, len2, doc, set, docIDs, documents;
currentStickerSets = [];
10 years ago
len1 = stickers.sets.length;
for (i = 0; i < len1; i++) {
10 years ago
set = stickers.sets[i];
9 years ago
if (set.pFlags.disabled) {
continue;
}
documents = stickers.fullSets[set.id].documents;
len2 = documents.length;
docIDs = [];
10 years ago
for (j = 0; j < len2; j++) {
9 years ago
doc = documents[j];
10 years ago
AppDocsManager.saveDoc(doc);
9 years ago
docIDs.push(doc.id);
}
9 years ago
set.docIDs = docIDs;
currentStickerSets.push(set);
}
}
return getPopularStickers().then(function (popularStickers) {
var resultStickersets = currentStickerSets;
if (popularStickers.length) {
resultStickersets = currentStickerSets.slice();
9 years ago
var docIDs = [];
var i, len;
for (i = 0, len = popularStickers.length; i < len; i++) {
9 years ago
docIDs.push(popularStickers[i].id);
}
resultStickersets.unshift({
id: 0,
title: _('im_stickers_tab_recent_raw'),
short_name: '',
9 years ago
docIDs: docIDs
});
}
return resultStickersets;
});
}
function getStickerSets (allStickers, prevCachedSets) {
10 years ago
var promises = [];
var cachedSets = prevCachedSets || allStickers.fullSets || {};
10 years ago
allStickers.fullSets = {};
angular.forEach(allStickers.sets, function (shortSet) {
var fullSet = cachedSets[shortSet.id];
if (fullSet && fullSet.set.hash == shortSet.hash) {
allStickers.fullSets[shortSet.id] = fullSet;
} else {
var promise = MtpApiManager.invokeApi('messages.getStickerSet', {
stickerset: {
_: 'inputStickerSetID',
id: shortSet.id,
access_hash: shortSet.access_hash
}
}).then(function (fullSet) {
allStickers.fullSets[shortSet.id] = fullSet;
});
promises.push(promise);
}
10 years ago
});
return $q.all(promises);
}
function getPopularStickers () {
return Storage.get('stickers_popular').then(function (popStickers) {
var result = [];
var i, len, docID;
if (popStickers && popStickers.length) {
for (i = 0, len = popStickers.length; i < len; i++) {
docID = popStickers[i][0];
if (AppDocsManager.hasDoc(docID)) {
result.push({id: docID, rate: popStickers[i][1]});
}
}
};
return result;
});
}
function pushPopularSticker (id) {
getPopularStickers().then(function (popularStickers) {
var exists = false;
var count = popularStickers.length;
var result = [];
for (var i = 0; i < count; i++) {
if (popularStickers[i].id == id) {
exists = true;
popularStickers[i].rate++;
}
result.push([popularStickers[i].id, popularStickers[i].rate]);
}
if (exists) {
result.sort(function (a, b) {
return b[1] - a[1];
});
} else {
if (result.length > 15) {
result = result.slice(0, 15);
}
result.push([id, 1]);
}
ConfigStorage.set({stickers_popular: result});
});
}
function getStickerset (inputStickerset) {
return MtpApiManager.invokeApi('messages.getStickerSet', {
stickerset: inputStickerset
}).then(function (result) {
for (var i = 0; i < result.documents.length; i++) {
AppDocsManager.saveDoc(result.documents[i]);
}
return result;
});
}
function installStickerset (fullSet, uninstall) {
var method = uninstall
? 'messages.uninstallStickerSet'
: 'messages.installStickerSet';
var inputStickerset = {
_: 'inputStickerSetID',
id: fullSet.set.id,
access_hash: fullSet.set.access_hash
};
return MtpApiManager.invokeApi(method, {
10 years ago
stickerset: inputStickerset,
disabled: false
}).then(function (result) {
var update;
if (uninstall) {
update = {_: 'updateDelStickerSet', id: fullSet.set.id};
} else {
update = {_: 'updateNewStickerSet', stickerset: fullSet};
}
ApiUpdatesManager.processUpdateMessage({
_: 'updateShort',
update: update
});
});
}
function openStickersetLink (shortName) {
return openStickerset({
_: 'inputStickerSetShortName',
short_name: shortName
});
}
function openStickerset (inputStickerset) {
var scope = $rootScope.$new(true);
scope.inputStickerset = inputStickerset;
var modal = $modal.open({
templateUrl: templateUrl('stickerset_modal'),
controller: 'StickersetModalController',
scope: scope,
windowClass: 'stickerset_modal_window mobile_modal'
});
}
function getStickerSetsHash (stickerSets) {
var acc = 0, set;
for (var i = 0; i < stickerSets.length; i++) {
set = stickerSets[i];
if (set.pFlags.disabled || !set.pFlags.installed) {
continue;
}
acc = ((acc * 20261) + 0x80000000 + set.hash) % 0x80000000;
}
return acc;
}
})
.service('AppInlineBotsManager', function (qSync, $q, $rootScope, toaster, Storage, ErrorService, MtpApiManager, AppMessagesManager, AppDocsManager, AppPhotosManager, RichTextProcessor, AppUsersManager, AppPeersManager, PeersSelectService, GeoLocationManager) {
var inlineResults = {};
return {
resolveInlineMention: resolveInlineMention,
getPopularBots: getPopularBots,
sendInlineResult: sendInlineResult,
getInlineResults: getInlineResults,
9 years ago
regroupWrappedResults: regroupWrappedResults,
switchToPM: switchToPM,
9 years ago
checkSwitchReturn: checkSwitchReturn,
switchInlineButtonClick: switchInlineButtonClick,
callbackButtonClick: callbackButtonClick
};
function getPopularBots () {
return Storage.get('inline_bots_popular').then(function (bots) {
var result = [];
var i, len, userID;
if (bots && bots.length) {
var now = tsNow(true);
for (i = 0, len = bots.length; i < len; i++) {
if ((now - bots[i][3]) > 14 * 86400) {
continue;
}
userID = bots[i][0];
if (!AppUsersManager.hasUser(userID)) {
AppUsersManager.saveApiUser(bots[i][1]);
}
result.push({id: userID, rate: bots[i][2], date: bots[i][3]});
}
};
return result;
});
}
function pushPopularBot (id) {
getPopularBots().then(function (bots) {
var exists = false;
var count = bots.length;
var result = [];
for (var i = 0; i < count; i++) {
if (bots[i].id == id) {
exists = true;
bots[i].rate++;
bots[i].date = tsNow(true);
}
var user = AppUsersManager.getUser(bots[i].id);
result.push([bots[i].id, user, bots[i].rate, bots[i].date]);
}
if (exists) {
result.sort(function (a, b) {
return b[2] - a[2];
});
} else {
if (result.length > 15) {
result = result.slice(0, 15);
}
result.push([id, AppUsersManager.getUser(id), 1, tsNow(true)]);
}
ConfigStorage.set({inline_bots_popular: result});
$rootScope.$broadcast('inline_bots_popular');
});
}
function resolveInlineMention (username) {
return AppPeersManager.resolveUsername(username).then(function (peerID) {
if (peerID > 0) {
var bot = AppUsersManager.getUser(peerID);
if (bot.pFlags.bot && bot.bot_inline_placeholder !== undefined) {
var resolvedBot = {
username: username,
id: peerID,
placeholder: bot.bot_inline_placeholder
};
if (bot.pFlags.bot_inline_geo &&
GeoLocationManager.isAvailable()) {
return checkGeoLocationAccess(peerID).then(function () {
return GeoLocationManager.getPosition().then(function (coords) {
resolvedBot.geo = coords;
return qSync.when(resolvedBot);
});
})['catch'](function () {
return qSync.when(resolvedBot);
})
}
return qSync.when(resolvedBot);
}
}
return $q.reject();
}, function (error) {
error.handled = true;
return $q.reject(error);
});
}
function getInlineResults (peerID, botID, query, geo, offset) {
return MtpApiManager.invokeApi('messages.getInlineBotResults', {
flags: 0 | (geo ? 1 : 0),
bot: AppUsersManager.getUserInput(botID),
peer: AppPeersManager.getInputPeerByID(peerID),
query: query,
geo_point: geo && {_: 'inputGeoPoint', lat: geo['lat'], long: geo['long']},
offset: offset
}, {timeout: 1, stopTime: -1, noErrorBox: true}).then(function(botResults) {
var queryID = botResults.query_id;
delete botResults._;
delete botResults.flags;
delete botResults.query_id;
9 years ago
if (botResults.switch_pm) {
botResults.switch_pm.rText = RichTextProcessor.wrapRichText(botResults.switch_pm.text, {noLinebreaks: true, noLinks: true});
}
angular.forEach(botResults.results, function (result) {
var qID = queryID + '_' + result.id;
result.qID = qID;
result.botID = botID;
result.rTitle = RichTextProcessor.wrapRichText(result.title, {noLinebreaks: true, noLinks: true});
result.rDescription = RichTextProcessor.wrapRichText(result.description, {noLinebreaks: true, noLinks: true});
9 years ago
result.initials = (result.url || result.title || result.type || '').substr(0, 1);
9 years ago
if (result.document) {
AppDocsManager.saveDoc(result.document);
}
9 years ago
if (result.photo) {
AppPhotosManager.savePhoto(result.photo);
}
inlineResults[qID] = result;
});
return botResults;
});
}
9 years ago
function regroupWrappedResults (results, rowW, rowH) {
if (!results ||
!results[0] ||
results[0].type != 'photo' && results[0].type != 'gif' && results[0].type != 'sticker') {
9 years ago
return;
}
var ratios = [];
angular.forEach(results, function (result) {
var w, h, doc, photo;
if (result._ == 'botInlineMediaResult') {
if (doc = result.document) {
w = result.document.w;
h = result.document.h;
}
else if (photo = result.photo) {
var photoSize = (photo.sizes || [])[0];
w = photoSize && photoSize.w;
h = photoSize && photoSize.h;
}
9 years ago
}
else {
w = result.w;
h = result.h;
}
if (!w || !h) {
w = h = 1;
}
ratios.push(w / h);
});
var rows = [];
var curCnt = 0;
var curW = 0;
angular.forEach(ratios, function (ratio) {
var w = ratio * rowH;
curW += w;
if (!curCnt || curCnt < 4 && curW < (rowW * 1.1)) {
curCnt++;
} else {
rows.push(curCnt);
curCnt = 1;
curW = w;
}
});
if (curCnt) {
rows.push(curCnt);
}
var i = 0;
var thumbs = [];
var lastRowI = rows.length - 1;
angular.forEach(rows, function (rowCnt, rowI) {
var lastRow = rowI == lastRowI;
var curRatios = ratios.slice(i, i + rowCnt);
var sumRatios = 0;
angular.forEach(curRatios, function (ratio) {
sumRatios += ratio;
});
angular.forEach(curRatios, function (ratio, j) {
var thumbH = rowH;
var thumbW = rowW * ratio / sumRatios;
var realW = thumbH * ratio;
if (lastRow && thumbW > realW) {
thumbW = realW;
}
var result = results[i + j];
result.thumbW = Math.floor(thumbW) - 2;
result.thumbH = Math.floor(thumbH) - 2;
9 years ago
});
i += rowCnt;
});
}
function switchToPM(fromPeerID, botID, startParam) {
var peerString = AppPeersManager.getPeerString(fromPeerID);
var setHash = {};
setHash['inline_switch_pm' + botID] = {peer: peerString, time: tsNow()};
Storage.set(setHash);
$rootScope.$broadcast('history_focus', {peerString: AppPeersManager.getPeerString(botID)});
AppMessagesManager.startBot(botID, 0, startParam);
}
function checkSwitchReturn(botID) {
var bot = AppUsersManager.getUser(botID);
if (!bot || !bot.pFlags.bot || !bot.bot_inline_placeholder) {
return qSync.when(false);
}
var key = 'inline_switch_pm' + botID;
return Storage.get(key).then(function (peerData) {
if (peerData) {
Storage.remove(key);
if (tsNow() - peerData.time < 3600000) {
return peerData.peer;
}
}
return false;
});
}
function switchInlineQuery(botID, toPeerString, query) {
$rootScope.$broadcast('history_focus', {
peerString: toPeerString,
attachment: {
_: 'inline_query',
mention: '@' + AppUsersManager.getUser(botID).username,
query: query
}
});
}
function switchInlineButtonClick(id, button) {
var message = AppMessagesManager.getMessage(id);
var botID = message.fromID;
return checkSwitchReturn(botID).then(function (retPeerString) {
if (retPeerString) {
return switchInlineQuery(botID, retPeerString, button.query);
}
PeersSelectService.selectPeer({
canSend: true
}).then(function (toPeerString) {
return switchInlineQuery(botID, toPeerString, button.query);
});
});
}
function callbackButtonClick(id, button) {
var message = AppMessagesManager.getMessage(id);
var botID = message.fromID;
var peerID = AppMessagesManager.getMessagePeer(message);
return MtpApiManager.invokeApi('messages.getBotCallbackAnswer', {
peer: AppPeersManager.getInputPeerByID(peerID),
msg_id: AppMessagesManager.getMessageLocalID(id),
data: button.data
}).then(function (callbackAnswer) {
if (typeof callbackAnswer.message != 'string' ||
!callbackAnswer.message.length) {
return;
}
if (callbackAnswer.pFlags.alert) {
ErrorService.alert(callbackAnswer.message);
} else {
var html = RichTextProcessor.wrapRichText(callbackAnswer.message, {noLinks: true, noLinebreaks: true}).valueOf();;
toaster.pop({
9 years ago
type: 'info',
// timeout: 100000,
body: html,
bodyOutputType: 'trustedHtml',
showCloseButton: false
});
}
});
}
function sendInlineResult (peerID, qID, options) {
var inlineResult = inlineResults[qID];
if (inlineResult === undefined) {
return false;
}
pushPopularBot(inlineResult.botID);
var splitted = qID.split('_');
var queryID = splitted.shift();
var resultID = splitted.join('_');
options = options || {};
options.viaBotID = inlineResult.botID;
options.queryID = queryID;
options.resultID = resultID;
if (inlineResult.send_message.reply_markup) {
options.reply_markup = inlineResult.send_message.reply_markup;
}
if (inlineResult.send_message._ == 'botInlineMessageText') {
options.entities = inlineResult.send_message.entities;
AppMessagesManager.sendText(peerID, inlineResult.send_message.message, options);
} else {
var caption = '';
var inputMedia = false;
switch (inlineResult.send_message._) {
case 'botInlineMessageMediaAuto':
caption = inlineResult.send_message.caption;
if (inlineResult._ == 'botInlineMediaResult') {
var doc = inlineResult.document;
var photo = inlineResult.photo;
if (doc) {
inputMedia = {
_: 'inputMediaDocument',
id: {_: 'inputDocument', id: doc.id, access_hash: doc.access_hash},
caption: caption
};
} else {
inputMedia = {
_: 'inputMediaPhoto',
id: {_: 'inputPhoto', id: photo.id, access_hash: photo.access_hash},
caption: caption
};
}
}
break;
case 'botInlineMessageMediaGeo':
inputMedia = {
_: 'inputMediaGeoPoint',
geo_point: {
_: 'inputGeoPoint',
'lat': inlineResult.send_message.geo['lat'],
'long': inlineResult.send_message.geo['long']
}
};
break;
case 'botInlineMessageMediaVenue':
inputMedia = {
_: 'inputMediaVenue',
geo_point: {
_: 'inputGeoPoint',
'lat': inlineResult.send_message.geo['lat'],
'long': inlineResult.send_message.geo['long']
},
9 years ago
title: inlineResult.send_message.title,
address: inlineResult.send_message.address,
provider: inlineResult.send_message.provider,
venue_id: inlineResult.send_message.venue_id
};
break;
case 'botInlineMessageMediaContact':
inputMedia = {
_: 'inputMediaContact',
phone_number: inlineResult.send_message.phone_number,
first_name: inlineResult.send_message.first_name,
last_name: inlineResult.send_message.last_name
};
break;
}
if (!inputMedia) {
inputMedia = {
_: 'messageMediaPending',
type: inlineResult.type,
file_name: inlineResult.title || inlineResult.content_url || inlineResult.url,
size: 0,
progress: {percent: 30, total: 0}
};
}
AppMessagesManager.sendOther(peerID, inputMedia, options);
}
}
function checkGeoLocationAccess(botID) {
var key = 'bot_access_geo' + botID;
return Storage.get(key).then(function (geoAccess) {
if (geoAccess && geoAccess.granted) {
return true;
}
return ErrorService.confirm({
9 years ago
type: 'BOT_ACCESS_GEO_INLINE'
}).then(function () {
var setHash = {};
setHash[key] = {granted: true, time: tsNow()};
Storage.set(setHash);
return true;
}, function () {
var setHash = {};
setHash[key] = {denied: true, time: tsNow()};
Storage.set(setHash);
return $q.reject();
});
});
}
})
11 years ago
.service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) {
var updatesState = {
pendingPtsUpdates: [],
pendingSeqUpdates: {},
syncPending: false,
syncLoading: true
};
var channelStates = {};
11 years ago
var myID = 0;
MtpApiManager.getUserID().then(function (id) {
myID = id;
});
function popPendingSeqUpdate () {
var nextSeq = updatesState.seq + 1,
pendingUpdatesData = updatesState.pendingSeqUpdates[nextSeq];
if (!pendingUpdatesData) {
return false;
}
var updates = pendingUpdatesData.updates;
var i, length;
for (var i = 0, length = updates.length; i < length; i++) {
saveUpdate(updates[i]);
}
updatesState.seq = pendingUpdatesData.seq;
if (pendingUpdatesData.date && updatesState.date < pendingUpdatesData.date) {
updatesState.date = pendingUpdatesData.date;
}
delete updatesState.pendingSeqUpdates[nextSeq];
if (!popPendingSeqUpdate() &&
updatesState.syncPending &&
updatesState.syncPending.seqAwaiting &&
updatesState.seq >= updatesState.syncPending.seqAwaiting) {
if (!updatesState.syncPending.ptsAwaiting) {
clearTimeout(updatesState.syncPending.timeout);
updatesState.syncPending = false;
} else {
delete updatesState.syncPending.seqAwaiting;
}
11 years ago
}
return true;
}
function popPendingPtsUpdate (channelID) {
var curState = channelID ? getChannelState(channelID) : updatesState;
if (!curState.pendingPtsUpdates.length) {
return false;
}
curState.pendingPtsUpdates.sort(function (a, b) {
return a.pts - b.pts;
});
11 years ago
var curPts = curState.pts;
var goodPts = false;
var goodIndex = false;
var update;
for (var i = 0, length = curState.pendingPtsUpdates.length; i < length; i++) {
update = curState.pendingPtsUpdates[i];
curPts += update.pts_count;
if (curPts >= update.pts) {
goodPts = update.pts;
goodIndex = i;
11 years ago
}
}
if (!goodPts) {
return false;
}
console.log(dT(), 'pop pending pts updates', goodPts, curState.pendingPtsUpdates.slice(0, goodIndex + 1));
curState.pts = goodPts;
for (i = 0; i <= goodIndex; i++) {
update = curState.pendingPtsUpdates[i];
saveUpdate(update);
}
curState.pendingPtsUpdates.splice(0, goodIndex + 1);
if (!curState.pendingPtsUpdates.length && curState.syncPending) {
if (!curState.syncPending.seqAwaiting) {
clearTimeout(curState.syncPending.timeout);
curState.syncPending = false;
} else {
delete curState.syncPending.ptsAwaiting;
11 years ago
}
}
return true;
}
function forceGetDifference () {
if (!updatesState.syncLoading) {
getDifference();
}
}
function processUpdateMessage (updateMessage) {
var processOpts = {
date: updateMessage.date,
seq: updateMessage.seq,
seqStart: updateMessage.seq_start
};
11 years ago
switch (updateMessage._) {
case 'updatesTooLong':
case 'new_session_created':
forceGetDifference();
11 years ago
break;
case 'updateShort':
processUpdate(updateMessage.update, processOpts);
11 years ago
break;
case 'updateShortMessage':
case 'updateShortChatMessage':
var isOut = updateMessage.flags & 2;
var fromID = updateMessage.from_id || (isOut ? myID : updateMessage.user_id);
var toID = updateMessage.chat_id
? -updateMessage.chat_id
: (isOut ? updateMessage.user_id : myID);
processUpdate({
11 years ago
_: 'updateNewMessage',
message: {
_: 'message',
flags: updateMessage.flags,
pFlags: updateMessage.pFlags,
11 years ago
id: updateMessage.id,
from_id: fromID,
to_id: AppPeersManager.getOutputPeer(toID),
11 years ago
date: updateMessage.date,
message: updateMessage.message,
fwd_from: updateMessage.fwd_from,
reply_to_msg_id: updateMessage.reply_to_msg_id,
entities: updateMessage.entities
11 years ago
},
pts: updateMessage.pts,
pts_count: updateMessage.pts_count
}, processOpts);
11 years ago
break;
case 'updatesCombined':
case 'updates':
AppUsersManager.saveApiUsers(updateMessage.users);
AppChatsManager.saveApiChats(updateMessage.chats);
angular.forEach(updateMessage.updates, function (update) {
processUpdate(update, processOpts);
});
break;
11 years ago
default:
console.warn(dT(), 'Unknown update message', updateMessage);
}
11 years ago
}
function getDifference () {
// console.trace(dT(), 'Get full diff');
if (!updatesState.syncLoading) {
updatesState.syncLoading = true;
updatesState.pendingSeqUpdates = {};
updatesState.pendingPtsUpdates = [];
}
if (updatesState.syncPending) {
clearTimeout(updatesState.syncPending.timeout);
updatesState.syncPending = false;
11 years ago
}
MtpApiManager.invokeApi('updates.getDifference', {pts: updatesState.pts, date: updatesState.date, qts: -1}).then(function (differenceResult) {
11 years ago
if (differenceResult._ == 'updates.differenceEmpty') {
console.log(dT(), 'apply empty diff', differenceResult.seq);
updatesState.date = differenceResult.date;
updatesState.seq = differenceResult.seq;
updatesState.syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
11 years ago
return false;
}
AppUsersManager.saveApiUsers(differenceResult.users);
AppChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
// console.log(dT(), 'applying', differenceResult.other_updates.length, 'other updates');
var channelsUpdates = [];
angular.forEach(differenceResult.other_updates, function(update) {
switch (update._) {
case 'updateChannelTooLong':
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
processUpdate(update);
return;
}
saveUpdate(update);
});
// console.log(dT(), 'applying', differenceResult.new_messages.length, 'new messages');
11 years ago
angular.forEach(differenceResult.new_messages, function (apiMessage) {
saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
pts: updatesState.pts,
pts_count: 0
});
11 years ago
});
var nextState = differenceResult.intermediate_state || differenceResult.state;
updatesState.seq = nextState.seq;
updatesState.pts = nextState.pts;
updatesState.date = nextState.date;
11 years ago
// console.log(dT(), 'apply diff', updatesState.seq, updatesState.pts);
11 years ago
if (differenceResult._ == 'updates.differenceSlice') {
getDifference();
11 years ago
} else {
// console.log(dT(), 'finished get diff');
$rootScope.$broadcast('stateSynchronized');
updatesState.syncLoading = false;
11 years ago
}
});
}
function getChannelDifference (channelID) {
var channelState = getChannelState(channelID);
if (!channelState.syncLoading) {
channelState.syncLoading = true;
channelState.pendingPtsUpdates = [];
}
if (channelState.syncPending) {
clearTimeout(channelState.syncPending.timeout);
channelState.syncPending = false;
}
// console.log(dT(), 'Get channel diff', AppChatsManager.getChat(channelID), channelState.pts);
MtpApiManager.invokeApi('updates.getChannelDifference', {
channel: AppChatsManager.getChannelInput(channelID),
filter: {_: 'channelMessagesFilterEmpty'},
pts: channelState.pts,
limit: 30
}).then(function (differenceResult) {
// console.log(dT(), 'channel diff result', differenceResult);
channelState.pts = differenceResult.pts;
if (differenceResult._ == 'updates.channelDifferenceEmpty') {
console.log(dT(), 'apply channel empty diff', differenceResult);
channelState.syncLoading = false;
$rootScope.$broadcast('stateSynchronized');
return false;
}
if (differenceResult._ == 'updates.channelDifferenceTooLong') {
console.log(dT(), 'channel diff too long', differenceResult);
channelState.syncLoading = false;
delete channelStates[channelID];
saveUpdate({_: 'updateChannelReload', channel_id: channelID});
return false;
}
AppUsersManager.saveApiUsers(differenceResult.users);
AppChatsManager.saveApiChats(differenceResult.chats);
// Should be first because of updateMessageID
console.log(dT(), 'applying', differenceResult.other_updates.length, 'channel other updates');
angular.forEach(differenceResult.other_updates, function(update){
saveUpdate(update);
});
console.log(dT(), 'applying', differenceResult.new_messages.length, 'channel new messages');
angular.forEach(differenceResult.new_messages, function (apiMessage) {
saveUpdate({
_: 'updateNewChannelMessage',
message: apiMessage,
pts: channelState.pts,
pts_count: 0
});
});
console.log(dT(), 'apply channel diff', channelState.pts);
if (differenceResult._ == 'updates.channelDifference' &&
!differenceResult.pFlags['final']) {
getChannelDifference(channelID);
} else {
console.log(dT(), 'finished channel get diff');
$rootScope.$broadcast('stateSynchronized');
channelState.syncLoading = false;
}
});
}
function addChannelState (channelID, pts) {
if (!pts) {
throw new Error('Add channel state without pts ' + channelID);
}
if (channelStates[channelID] === undefined) {
channelStates[channelID] = {
pts: pts,
pendingPtsUpdates: [],
syncPending: false,
syncLoading: false
};
return true;
}
return false;
}
function getChannelState (channelID, pts) {
if (channelStates[channelID] === undefined) {
addChannelState(channelID, pts);
}
return channelStates[channelID];
}
function processUpdate (update, options) {
options = options || {};
var channelID = false;
switch (update._) {
case 'updateNewChannelMessage':
case 'updateEditChannelMessage':
channelID = -AppPeersManager.getPeerID(update.message.to_id);
break;
case 'updateDeleteChannelMessages':
channelID = update.channel_id;
break;
case 'updateChannelTooLong':
channelID = update.channel_id;
if (channelStates[channelID] === undefined) {
return false;
}
break;
}
var curState = channelID ? getChannelState(channelID, update.pts) : updatesState;
// console.log(dT(), 'process', channelID, curState.pts, update);
if (curState.syncLoading) {
return false;
}
if (update._ == 'updateChannelTooLong') {
getChannelDifference(channelID);
return false;
}
if (update._ == 'updateNewMessage' ||
update._ == 'updateEditMessage' ||
update._ == 'updateNewChannelMessage' ||
update._ == 'updateEditChannelMessage') {
var message = update.message;
var toPeerID = AppPeersManager.getPeerID(message.to_id);
var fwdHeader = message.fwdHeader || {};
if (message.from_id && !AppUsersManager.hasUser(message.from_id, message.pFlags.post) ||
fwdHeader.from_id && !AppUsersManager.hasUser(fwdHeader.from_id, !!fwdHeader.channel_id) ||
fwdHeader.channel_id && !AppChatsManager.hasChat(fwdHeader.channel_id) ||
toPeerID > 0 && !AppUsersManager.hasUser(toPeerID) ||
toPeerID < 0 && !AppChatsManager.hasChat(-toPeerID)) {
console.warn(dT(), 'Not enough data for message update', message);
if (channelID && AppChatsManager.hasChat(channelID)) {
getChannelDifference(channelID);
} else {
forceGetDifference();
}
return false;
}
11 years ago
}
else if (channelID && !AppChatsManager.hasChat(channelID)) {
// console.log(dT(), 'skip update, missing channel', channelID, update);
return false;
}
11 years ago
var popPts, popSeq;
if (update.pts) {
var newPts = curState.pts + (update.pts_count || 0);
if (newPts < update.pts) {
console.warn(dT(), 'Pts hole', curState, update, channelID && AppChatsManager.getChat(channelID));
curState.pendingPtsUpdates.push(update);
if (!curState.syncPending) {
curState.syncPending = {
timeout: setTimeout(function () {
if (channelID) {
getChannelDifference(channelID);
} else {
getDifference();
}
}, 5000)
};
}
curState.syncPending.ptsAwaiting = true;
return false;
11 years ago
}
if (update.pts > curState.pts) {
curState.pts = update.pts;
popPts = true;
}
else if (update.pts_count) {
// console.warn(dT(), 'Duplicate update', update);
return false;
}
if (channelID && options.date && updatesState.date < options.date) {
updatesState.date = options.date;
}
11 years ago
}
else if (!channelID && options.seq > 0) {
var seq = options.seq;
var seqStart = options.seqStart || seq;
11 years ago
if (seqStart != curState.seq + 1) {
if (seqStart > curState.seq) {
console.warn(dT(), 'Seq hole', curState, curState.syncPending && curState.syncPending.seqAwaiting);
11 years ago
if (curState.pendingSeqUpdates[seqStart] === undefined) {
curState.pendingSeqUpdates[seqStart] = {seq: seq, date: options.date, updates: []};
}
curState.pendingSeqUpdates[seqStart].updates.push(update);
if (!curState.syncPending) {
curState.syncPending = {
timeout: setTimeout(function () {
getDifference();
}, 5000)
};
}
if (!curState.syncPending.seqAwaiting ||
curState.syncPending.seqAwaiting < seqStart) {
curState.syncPending.seqAwaiting = seqStart;
}
return false;
}
}
if (curState.seq != seq) {
curState.seq = seq;
if (options.date && curState.date < options.date) {
curState.date = options.date;
}
popSeq = true;
}
}
saveUpdate(update);
if (popPts) {
popPendingPtsUpdate(channelID);
}
else if (popSeq) {
popPendingSeqUpdate();
}
}
function saveUpdate (update) {
$rootScope.$broadcast('apiUpdate', update);
11 years ago
}
function attach () {
MtpNetworkerFactory.setUpdatesProcessor(processUpdateMessage);
MtpApiManager.invokeApi('updates.getState', {}, {noErrorBox: true}).then(function (stateResult) {
updatesState.seq = stateResult.seq;
updatesState.pts = stateResult.pts;
updatesState.date = stateResult.date;
setTimeout(function () {
updatesState.syncLoading = false;
}, 1000);
// updatesState.seq = 1;
// updatesState.pts = stateResult.pts - 5000;
// updatesState.date = 1;
// getDifference();
11 years ago
})
}
return {
processUpdateMessage: processUpdateMessage,
addChannelState: addChannelState,
11 years ago
attach: attach
}
})
.service('StatusManager', function ($timeout, $rootScope, MtpApiManager, AppUsersManager, IdleManager) {
var toPromise;
var lastOnlineUpdated = 0;
var started = false;
var myID = 0;
var myOtherDeviceActive = false;
MtpApiManager.getUserID().then(function (id) {
myID = id;
});
$rootScope.$on('apiUpdate', function (e, update) {
if (update._ == 'updateUserStatus' && update.user_id == myID) {
myOtherDeviceActive = tsNow() + (update.status._ == 'userStatusOnline' ? 300000 : 0);
}
});
return {
start: start,
isOtherDeviceActive: isOtherDeviceActive
};
function start() {
if (!started) {
started = true;
$rootScope.$watch('idle.isIDLE', checkIDLE);
11 years ago
$rootScope.$watch('offline', checkIDLE);
}
}
function sendUpdateStatusReq(offline) {
var date = tsNow();
if (offline && !lastOnlineUpdated ||
11 years ago
!offline && (date - lastOnlineUpdated) < 50000 ||
$rootScope.offline) {
return;
}
lastOnlineUpdated = offline ? 0 : date;
AppUsersManager.setUserStatus(myID, offline);
return MtpApiManager.invokeApi('account.updateStatus', {
offline: offline
}, {noErrorBox: true});
}
function checkIDLE() {
toPromise && $timeout.cancel(toPromise);
if ($rootScope.idle.isIDLE) {
toPromise = $timeout(function () {
sendUpdateStatusReq(true);
}, 5000);
} else {
sendUpdateStatusReq(false);
toPromise = $timeout(checkIDLE, 60000);
}
}
function isOtherDeviceActive() {
if (!myOtherDeviceActive) {
return false;
}
if (tsNow() > myOtherDeviceActive) {
myOtherDeviceActive = false;
return false;
}
return true;
}
})
9 years ago
.service('NotificationsManager', function ($rootScope, $window, $interval, $q, _, MtpApiManager, AppPeersManager, IdleManager, Storage, AppRuntimeManager, FileManager) {
navigator.vibrate = navigator.vibrate || navigator.mozVibrate || navigator.webkitVibrate;
var notificationsMsSiteMode = false;
try {
if (window.external && window.external.msIsSiteMode()) {
notificationsMsSiteMode = true;
}
} catch (e) {};
var notificationsUiSupport = notificationsMsSiteMode ||
('Notification' in window) ||
('mozNotification' in navigator);
var notificationsShown = {};
var notificationIndex = 0;
var notificationsCount = 0;
var soundsPlayed = {};
var vibrateSupport = !!navigator.vibrate;
var nextSoundAt = false;
var prevSoundVolume = false;
var peerSettings = {};
10 years ago
var faviconEl = $('link[rel="icon"]:first')[0];
var langNotificationsPluralize = _.pluralize('page_title_pluralize_notifications');
var titleBackup = document.title,
titleChanged = false,
titlePromise;
var prevFavicon;
var stopped = false;
var settings = {};
$rootScope.$watch('idle.deactivated', function (newVal) {
if (newVal) {
stop();
}
});
10 years ago
$rootScope.$watch('idle.isIDLE', function (newVal) {
if (stopped) {
return;
}
10 years ago
if (!newVal) {
notificationsClear();
}
if (!Config.Navigator.mobile) {
$interval.cancel(titlePromise);
if (!newVal) {
titleChanged = false;
10 years ago
document.title = titleBackup;
10 years ago
setFavicon();
10 years ago
} else {
titleBackup = document.title;
titlePromise = $interval(function () {
if (titleChanged || !notificationsCount) {
titleChanged = false;
document.title = titleBackup;
setFavicon();
10 years ago
} else {
titleChanged = true;
10 years ago
document.title = langNotificationsPluralize(notificationsCount);
10 years ago
setFavicon('favicon_unread.ico');
10 years ago
}
}, 1000);
}
10 years ago
}
});
$rootScope.$on('apiUpdate', function (e, update) {
// console.log('on apiUpdate', update);
switch (update._) {
case 'updateNotifySettings':
if (update.peer._ == 'notifyPeer') {
var peerID = AppPeersManager.getPeerID(update.peer.peer);
savePeerSettings(peerID, update.notify_settings);
}
break;
}
});
var registeredDevice = false;
if (window.navigator.mozSetMessageHandler) {
window.navigator.mozSetMessageHandler('push', function(e) {
console.log(dT(), 'received push', e);
$rootScope.$broadcast('push_received');
});
window.navigator.mozSetMessageHandler('push-register', function(e) {
console.log(dT(), 'received push', e);
registeredDevice = false;
registerDevice();
});
}
return {
start: start,
notify: notify,
cancel: notificationCancel,
clear: notificationsClear,
soundReset: notificationSoundReset,
getPeerSettings: getPeerSettings,
getPeerMuted: getPeerMuted,
savePeerSettings: savePeerSettings,
updatePeerSettings: updatePeerSettings,
updateNotifySettings: updateNotifySettings,
getNotifySettings: getNotifySettings,
getVibrateSupport: getVibrateSupport,
testSound: playSound
};
function updateNotifySettings () {
Storage.get('notify_nodesktop', 'notify_volume', 'notify_novibrate', 'notify_nopreview').then(function (updSettings) {
settings.nodesktop = updSettings[0];
settings.volume = updSettings[1] === false
? 0.5
: updSettings[1];
settings.novibrate = updSettings[2];
settings.nopreview = updSettings[3];
});
}
function getNotifySettings () {
return settings;
}
function getPeerSettings (peerID) {
if (peerSettings[peerID] !== undefined) {
return peerSettings[peerID];
}
return peerSettings[peerID] = MtpApiManager.invokeApi('account.getNotifySettings', {
peer: {
_: 'inputNotifyPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
}
});
}
10 years ago
function setFavicon (href) {
href = href || 'favicon.ico';
if (prevFavicon === href) {
return
}
10 years ago
var link = document.createElement('link');
link.rel = 'shortcut icon';
link.type = 'image/x-icon';
link.href = href;
10 years ago
faviconEl.parentNode.replaceChild(link, faviconEl);
faviconEl = link;
prevFavicon = href;
10 years ago
}
function savePeerSettings (peerID, settings) {
// console.trace(dT(), 'peer settings', peerID, settings);
peerSettings[peerID] = $q.when(settings);
$rootScope.$broadcast('notify_settings', {peerID: peerID});
}
function updatePeerSettings (peerID, settings) {
savePeerSettings(peerID, settings);
var inputSettings = angular.copy(settings);
inputSettings._ = 'inputPeerNotifySettings';
return MtpApiManager.invokeApi('account.updateNotifySettings', {
peer: {
_: 'inputNotifyPeer',
peer: AppPeersManager.getInputPeerByID(peerID)
},
settings: inputSettings
});
}
function getPeerMuted (peerID) {
return getPeerSettings(peerID).then(function (peerNotifySettings) {
return peerNotifySettings._ == 'peerNotifySettings' &&
peerNotifySettings.mute_until * 1000 > tsNow();
});
}
function start () {
updateNotifySettings();
$rootScope.$on('settings_changed', updateNotifySettings);
registerDevice();
if (!notificationsUiSupport) {
return false;
}
if ('Notification' in window && Notification.permission !== 'granted' && Notification.permission !== 'denied') {
$($window).on('click', requestPermission);
}
try {
if ('onbeforeunload' in window) {
$($window).on('beforeunload', notificationsClear);
}
} catch (e) {}
}
function stop () {
notificationsClear();
$interval.cancel(titlePromise);
setFavicon();
stopped = true;
}
function requestPermission() {
Notification.requestPermission();
$($window).off('click', requestPermission);
}
function notify (data) {
if (stopped) {
return;
}
// console.log('notify', $rootScope.idle.isIDLE, notificationsUiSupport);
// FFOS Notification blob src bug workaround
9 years ago
if (Config.Navigator.ffos && !Config.Navigator.ffos2p) {
data.image = 'https://telegram.org/img/t_logo.png';
}
else if (data.image && !angular.isString(data.image)) {
if (Config.Navigator.ffos2p) {
FileManager.getDataUrl(data.image, 'image/jpeg').then(function (url) {
data.image = url;
notify(data);
});
return false;
} else {
data.image = FileManager.getUrl(data.image, 'image/jpeg');
}
}
else if (!data.image) {
data.image = 'img/icons/icon60.png';
}
// console.log('notify image', data.image);
notificationsCount++;
var now = tsNow();
if (settings.volume > 0 &&
(
!data.tag ||
!soundsPlayed[data.tag] ||
now > soundsPlayed[data.tag] + 60000
)
) {
playSound(settings.volume);
soundsPlayed[data.tag] = now;
}
if (!notificationsUiSupport ||
'Notification' in window && Notification.permission !== 'granted') {
return false;
}
if (settings.nodesktop) {
if (vibrateSupport && !settings.novibrate) {
navigator.vibrate([200, 100, 200]);
return;
}
return;
}
var idx = ++notificationIndex,
key = data.key || 'k' + idx,
notification;
if ('Notification' in window) {
notification = new Notification(data.title, {
icon: data.image || '',
body: data.message || '',
tag: data.tag || ''
});
}
else if ('mozNotification' in navigator) {
notification = navigator.mozNotification.createNotification(data.title, data.message || '', data.image || '');
}
else if (notificationsMsSiteMode) {
window.external.msSiteModeClearIconOverlay();
window.external.msSiteModeSetIconOverlay('img/icons/icon16.png', data.title);
window.external.msSiteModeActivate();
notification = {
index: idx
};
}
else {
return;
}
notification.onclick = function () {
notification.close();
AppRuntimeManager.focus();
notificationsClear();
if (data.onclick) {
data.onclick();
}
};
notification.onclose = function () {
if (!notification.hidden) {
delete notificationsShown[key];
notificationsClear();
}
};
if (notification.show) {
notification.show();
}
notificationsShown[key] = notification;
if (!Config.Navigator.mobile) {
setTimeout(function () {
notificationHide(key)
}, 8000);
}
};
function playSound (volume) {
var now = tsNow();
if (nextSoundAt && now < nextSoundAt && prevSoundVolume == volume) {
return;
}
nextSoundAt = now + 1000;
prevSoundVolume = volume;
var filename = 'img/sound_a.mp3';
var obj = $('#notify_sound').html('<audio autoplay="autoplay" mozaudiochannel="notification">' +
'<source src="' + filename + '" type="audio/mpeg" />' +
'<embed hidden="true" autostart="true" loop="false" volume="' + (volume * 100) +'" src="' + filename +'" />' +
'</audio>');
obj.find('audio')[0].volume = volume;
}
function notificationCancel (key) {
var notification = notificationsShown[key];
if (notification) {
if (notificationsCount > 0) {
notificationsCount--;
}
try {
if (notification.close) {
notification.close();
}
else if (notificationsMsSiteMode &&
notification.index == notificationIndex) {
window.external.msSiteModeClearIconOverlay();
}
} catch (e) {}
delete notificationsCount[key];
}
}
function notificationHide (key) {
var notification = notificationsShown[key];
if (notification) {
try {
if (notification.close) {
notification.hidden = true;
notification.close();
}
} catch (e) {}
delete notificationsCount[key];
}
}
function notificationSoundReset (tag) {
delete soundsPlayed[tag];
}
function notificationsClear() {
if (notificationsMsSiteMode) {
window.external.msSiteModeClearIconOverlay();
} else {
angular.forEach(notificationsShown, function (notification) {
try {
if (notification.close) {
notification.close()
}
} catch (e) {}
});
}
notificationsShown = {};
notificationsCount = 0;
}
var registerDevicePeriod = 1000,
registerDeviceTO;
function registerDevice () {
if (registeredDevice) {
return false;
}
if (navigator.push && Config.Navigator.ffos && Config.Modes.packed) {
var req = navigator.push.register();
req.onsuccess = function(e) {
clearTimeout(registerDeviceTO);
console.log(dT(), 'Push registered', req.result);
registeredDevice = req.result;
MtpApiManager.invokeApi('account.registerDevice', {
token_type: 4,
token: registeredDevice,
device_model: navigator.userAgent || 'Unknown UserAgent',
system_version: navigator.platform || 'Unknown Platform',
app_version: Config.App.version,
app_sandbox: false,
lang_code: navigator.language || 'en'
});
}
req.onerror = function(e) {
console.error('Push register error', e, e.toString());
registerDeviceTO = setTimeout(registerDevice, registerDevicePeriod);
registerDevicePeriod = Math.min(30000, registerDevicePeriod * 1.5);
}
}
}
function unregisterDevice () {
if (!registeredDevice) {
return false;
}
MtpApiManager.invokeApi('account.unregisterDevice', {
token_type: 4,
token: registeredDevice
}).then(function () {
registeredDevice = false;
})
}
function getVibrateSupport () {
return vibrateSupport;
}
})
.service('PasswordManager', function ($timeout, $q, $rootScope, MtpApiManager, CryptoWorker, MtpSecureRandom) {
10 years ago
return {
check: check,
getState: getState,
requestRecovery: requestRecovery,
recover: recover,
updateSettings: updateSettings
10 years ago
};
function getState (options) {
return MtpApiManager.invokeApi('account.getPassword', {}, options).then(function (result) {
10 years ago
return result;
});
}
function updateSettings (state, settings) {
var currentHashPromise;
var newHashPromise;
var params = {
new_settings: {
_: 'account.passwordInputSettings',
flags: 0,
hint: settings.hint || ''
}
};
if (typeof settings.cur_password === 'string' &&
settings.cur_password.length > 0) {
currentHashPromise = makePasswordHash(state.current_salt, settings.cur_password);
} else {
currentHashPromise = $q.when([]);
}
if (typeof settings.new_password === 'string' &&
settings.new_password.length > 0) {
var saltRandom = new Array(8);
var newSalt = bufferConcat(state.new_salt, saltRandom);
MtpSecureRandom.nextBytes(saltRandom);
newHashPromise = makePasswordHash(newSalt, settings.new_password);
params.new_settings.new_salt = newSalt;
params.new_settings.flags |= 1;
} else {
if (typeof settings.new_password === 'string') {
params.new_settings.flags |= 1;
params.new_settings.new_salt = [];
}
newHashPromise = $q.when([]);
}
if (typeof settings.email === 'string') {
params.new_settings.flags |= 2;
params.new_settings.email = settings.email || '';
}
return $q.all([currentHashPromise, newHashPromise]).then(function (hashes) {
params.current_password_hash = hashes[0];
params.new_settings.new_password_hash = hashes[1];
return MtpApiManager.invokeApi('account.updatePasswordSettings', params);
});
}
function check (state, password, options) {
return makePasswordHash(state.current_salt, password).then(function (passwordHash) {
return MtpApiManager.invokeApi('auth.checkPassword', {
password_hash: passwordHash
}, options);
});
}
function requestRecovery (state, options) {
return MtpApiManager.invokeApi('auth.requestPasswordRecovery', {}, options);
}
function recover (code, options) {
return MtpApiManager.invokeApi('auth.recoverPassword', {
code: code
}, options);
}
function makePasswordHash (salt, password) {
var passwordUTF8 = unescape(encodeURIComponent(password));
var buffer = new ArrayBuffer(passwordUTF8.length);
var byteView = new Uint8Array(buffer);
for (var i = 0, len = passwordUTF8.length; i < len; i++) {
byteView[i] = passwordUTF8.charCodeAt(i);
}
buffer = bufferConcat(bufferConcat(salt, byteView), salt);
return CryptoWorker.sha256Hash(buffer);
}
10 years ago
})
.service('ErrorService', function ($rootScope, $modal, $window) {
var shownBoxes = 0;
11 years ago
function show (params, options) {
if (shownBoxes >= 1) {
console.log('Skip error box, too many open', shownBoxes, params, options);
return false;
}
11 years ago
options = options || {};
var scope = $rootScope.$new();
angular.extend(scope, params);
shownBoxes++;
var modal = $modal.open({
templateUrl: templateUrl('error_modal'),
scope: scope,
11 years ago
windowClass: options.windowClass || 'error_modal_window'
});
modal.result['finally'](function () {
shownBoxes--;
});
return modal;
}
11 years ago
function alert (title, description) {
return show ({
title: title,
description: description
});
};
function confirm (params, options) {
options = options || {};
var scope = $rootScope.$new();
angular.extend(scope, params);
var modal = $modal.open({
templateUrl: templateUrl('confirm_modal'),
scope: scope,
windowClass: options.windowClass || 'confirm_modal_window'
});
return modal.result;
};
$window.safeConfirm = function (params, callback) {
if (typeof params === 'string') {
params = {message: params};
}
confirm(params).then(function (result) {
callback(result || true)
}, function () {
callback(false)
});
};
return {
11 years ago
show: show,
alert: alert,
confirm: confirm
}
})
.service('PeersSelectService', function ($rootScope, $modal) {
function selectPeer (options) {
var scope = $rootScope.$new();
scope.multiSelect = false;
scope.noMessages = true;
if (options) {
angular.extend(scope, options);
}
return $modal.open({
templateUrl: templateUrl('peer_select'),
controller: 'PeerSelectController',
scope: scope,
10 years ago
windowClass: 'peer_select_window mobile_modal',
backdrop: 'single'
}).result;
}
function selectPeers (options) {
if (Config.Mobile) {
return selectPeer(options).then(function (peerString) {
return [peerString];
});
}
var scope = $rootScope.$new();
scope.multiSelect = true;
scope.noMessages = true;
if (options) {
angular.extend(scope, options);
}
return $modal.open({
templateUrl: templateUrl('peer_select'),
controller: 'PeerSelectController',
scope: scope,
10 years ago
windowClass: 'peer_select_window mobile_modal',
backdrop: 'single'
}).result;
}
return {
selectPeer: selectPeer,
selectPeers: selectPeers
}
})
.service('ContactsSelectService', function ($rootScope, $modal) {
function select (multiSelect, options) {
options = options || {};
var scope = $rootScope.$new();
scope.multiSelect = multiSelect;
angular.extend(scope, options);
if (!scope.action && multiSelect) {
scope.action = 'select';
}
return $modal.open({
templateUrl: templateUrl('contacts_modal'),
controller: 'ContactsModalController',
scope: scope,
10 years ago
windowClass: 'contacts_modal_window mobile_modal',
backdrop: 'single'
}).result;
}
return {
selectContacts: function (options) {
return select (true, options);
},
selectContact: function (options) {
return select (false, options);
}
}
})
.service('ChangelogNotifyService', function (Storage, $rootScope, $modal) {
function checkUpdate () {
Storage.get('last_version').then(function (lastVersion) {
if (lastVersion != Config.App.version) {
11 years ago
if (lastVersion) {
showChangelog(lastVersion);
}
Storage.set({last_version: Config.App.version});
}
})
}
function showChangelog (lastVersion) {
var $scope = $rootScope.$new();
$scope.lastVersion = lastVersion;
$modal.open({
controller: 'ChangelogModalController',
templateUrl: templateUrl('changelog_modal'),
scope: $scope,
windowClass: 'changelog_modal_window mobile_modal'
});
}
return {
checkUpdate: checkUpdate,
showChangelog: showChangelog
}
})
.service('HttpsMigrateService', function (ErrorService, Storage) {
var started = false;
function check () {
Storage.get('https_dismiss').then(function (ts) {
if (!ts || tsNow() > ts + 43200000) {
ErrorService.confirm({
type: 'MIGRATE_TO_HTTPS'
}).then(function () {
var popup;
try {
popup = window.open('https://web.telegram.org', '_blank');
} catch (e) {}
if (!popup) {
location.href = 'https://web.telegram.org';
}
}, function () {
Storage.set({https_dismiss: tsNow()});
});
}
});
}
function start () {
if (started ||
location.protocol != 'http:' ||
Config.Modes.http ||
Config.App.domains.indexOf(location.hostname) == -1) {
return;
}
started = true;
setTimeout(check, 120000);
}
return {
start: start,
check: check
}
})
.service('LayoutSwitchService', function (ErrorService, Storage, AppRuntimeManager, $window) {
var started = false;
var confirmShown = false;
function switchLayout(mobile) {
ConfigStorage.noPrefix();
Storage.set({
layout_selected: mobile ? 'mobile' : 'desktop',
layout_width: $(window).width()
}).then(function () {
AppRuntimeManager.reload();
});
}
function layoutCheck (e) {
if (confirmShown) {
return;
}
var width = $(window).width();
var newMobile = width < 600;
if (!width ||
!e && (Config.Navigator.mobile ? width <= 800 : newMobile)) {
return;
}
if (newMobile != Config.Mobile) {
ConfigStorage.noPrefix();
Storage.get('layout_width').then(function (confirmedWidth) {
if (width == confirmedWidth) {
return false;
}
confirmShown = true;
ErrorService.confirm({
type: newMobile ? 'SWITCH_MOBILE_VERSION' : 'SWITCH_DESKTOP_VERSION'
}).then(function () {
switchLayout(newMobile);
}, function () {
ConfigStorage.noPrefix();
Storage.set({layout_width: width});
confirmShown = false;
});
});
}
}
function start () {
if (started || Config.Navigator.mobile) {
return;
}
started = true;
layoutCheck();
$($window).on('resize', layoutCheck);
}
return {
start: start,
switchLayout: switchLayout
}
})
10 years ago
.service('TelegramMeWebService', function (Storage) {
10 years ago
var disabled = Config.Modes.test ||
Config.App.domains.indexOf(location.hostname) == -1 ||
location.protocol != 'http:' && location.protocol != 'https:' ||
location.protocol == 'https:' && location.hostname != 'web.telegram.org';
function sendAsyncRequest (canRedirect) {
if (disabled) {
return false;
}
Storage.get('tgme_sync').then(function (curValue) {
var ts = tsNow(true);
10 years ago
if (canRedirect &&
curValue &&
curValue.canRedirect == canRedirect &&
curValue.ts + 86400 > ts) {
return false;
}
Storage.set({tgme_sync: {canRedirect: canRedirect, ts: ts}});
var script = $('<script>').appendTo('body')
.on('load error', function() {
script.remove();
})
.attr('src', '//telegram.me/_websync_?authed=' + (canRedirect ? '1' : '0'));
});
};
return {
setAuthorized: sendAsyncRequest
};
})
.service('LocationParamsService', function ($rootScope, $routeParams, AppPeersManager, AppUsersManager, AppMessagesManager, PeersSelectService, AppStickersManager, ErrorService) {
10 years ago
var tgAddrRegExp = /^(web\+)?tg:(\/\/)?(.+)/;
function checkLocationTgAddr () {
var tgaddr = $routeParams.tgaddr;
if (tgaddr) {
try {
tgaddr = decodeURIComponent(tgaddr);
} catch (e) {};
10 years ago
var matches = tgaddr.match(tgAddrRegExp);
if (matches) {
handleTgProtoAddr(matches[3]);
}
}
}
10 years ago
function handleTgProtoAddr (url, inner) {
var matches;
if (matches = url.match(/^resolve\?domain=(.+?)(?:&(start|startgroup|post)=(.+))?$/)) {
AppPeersManager.resolveUsername(matches[1]).then(function (peerID) {
if (peerID > 0 && AppUsersManager.isBot(peerID) && matches[2] == 'startgroup') {
PeersSelectService.selectPeer({
confirm_type: 'INVITE_TO_GROUP',
noUsers: true
}).then(function (toPeerString) {
var toPeerID = AppPeersManager.getPeerID(toPeerString);
var toChatID = toPeerID < 0 ? -toPeerID : 0;
AppMessagesManager.startBot(peerID, toChatID, matches[3]).then(function () {
$rootScope.$broadcast('history_focus', {peerString: toPeerString});
});
});
return true;
}
var params = {
peerString: AppPeersManager.getPeerString(peerID)
};
if (matches[2] == 'start') {
params.startParam = matches[3];
} else {
params.messageID = AppMessagesManager.getFullMessageID(parseInt(matches[3]), -peerID);
}
$rootScope.$broadcast('history_focus', params);
});
return true;
10 years ago
}
if (matches = url.match(/^join\?invite=(.+)$/)) {
AppMessagesManager.openChatInviteLink(matches[1]);
return true;
}
if (matches = url.match(/^addstickers\?set=(.+)$/)) {
AppStickersManager.openStickersetLink(matches[1]);
return true;
}
9 years ago
if (matches = url.match(/^msg_url\?url=([^&]+)(?:&text=(.*))?$/)) {
var url = decodeURIComponent(matches[1]);
var text = matches[2] ? decodeURIComponent(matches[2]) : '';
shareUrl(url, text);
return true;
}
if (inner &&
(matches = url.match(/^unsafe_url\?url=([^&]+)/))) {
var url = decodeURIComponent(matches[1]);
ErrorService.confirm({
type: 'JUMP_EXT_URL',
url: url
}).then(function () {
var target = '_blank';
if (url.search('https://telegram.me/') === 0) {
target = '_self';
}
window.open(url, target);
});
return true;
}
9 years ago
if (matches = url.match(/^search_hashtag\?hashtag=(.+?)$/)) {
$rootScope.$broadcast('dialogs_search', {query: '#' + decodeURIComponent(matches[1])});
if (Config.Mobile) {
$rootScope.$broadcast('history_focus', {
peerString: ''
});
}
return true;
}
10 years ago
if (inner &&
(matches = url.match(/^bot_command\?command=(.+?)(?:&bot=(.+))?$/))) {
var peerID = $rootScope.selectedPeerID;
var text = '/' + matches[1];
if (peerID < 0 && matches[2]) {
text += '@' + matches[2];
}
AppMessagesManager.sendText(peerID, text);
$rootScope.$broadcast('history_focus', {
peerString: AppPeersManager.getPeerString(peerID)
10 years ago
});
return true;
10 years ago
}
return false;
}
function handleActivityMessage (name, data) {
console.log(dT(), 'Received activity', name, data);
if (name == 'share' && data.url) {
shareUrl(data.url, '');
}
else if (name == 'view' && data.url) {
var matches = data.url.match(tgAddrRegExp);
if (matches) {
handleTgProtoAddr(matches[3]);
}
}
else if (name == 'webrtc-call' && data.contact) {
var contact = data.contact;
var phones = [];
if (contact.tel != undefined) {
for (var i = 0; i < contact.tel.length; i++) {
phones.push(contact.tel[i].value);
}
}
var firstName = (contact.givenName || []).join(' ');
var lastName = (contact.familyName || []).join(' ');
if (phones.length) {
AppUsersManager.importContact(phones[0], firstName, lastName).then(function (foundUserID) {
if (foundUserID) {
var peerString = AppPeersManager.getPeerString(foundUserID);
$rootScope.$broadcast('history_focus', {peerString: peerString});
} else {
ErrorService.show({
error: {code: 404, type: 'USER_NOT_USING_TELEGRAM'}
});
}
});
}
}
else if (name === 'share' && data.blobs && data.blobs.length > 0) {
PeersSelectService.selectPeers({confirm_type: 'EXT_SHARE_PEER', canSend: true}).then(function (peerStrings) {
angular.forEach(peerStrings, function (peerString) {
var peerID = AppPeersManager.getPeerID(peerString);
angular.forEach(data.blobs, function (blob) {
AppMessagesManager.sendFile(peerID, blob, {isMedia: true});
});
})
if (peerStrings.length == 1) {
$rootScope.$broadcast('history_focus', {peerString: peerStrings[0]});
}
});
}
}
var started = false;
function start () {
if (started) {
return;
}
started = true;
if ('registerProtocolHandler' in navigator) {
try {
navigator.registerProtocolHandler('tg', '#im?tgaddr=%s', 'Telegram Web');
} catch (e) {}
try {
navigator.registerProtocolHandler('web+tg', '#im?tgaddr=%s', 'Telegram Web');
} catch (e) {}
}
if (window.navigator.mozSetMessageHandler) {
console.log(dT(), 'Set activity message handler');
window.navigator.mozSetMessageHandler('activity', function(activityRequest) {
handleActivityMessage(activityRequest.source.name, activityRequest.source.data);
});
}
$(document).on('click', function (event) {
var target = event.target;
if (target &&
target.tagName == 'A' &&
!target.onclick &&
!target.onmousedown) {
var href = $(target).attr('href') || target.href || '';
10 years ago
var match = href.match(tgAddrRegExp);
if (match) {
10 years ago
if (handleTgProtoAddr(match[3], true)) {
return cancelEvent(event);
}
}
}
});
$rootScope.$on('$routeUpdate', checkLocationTgAddr);
checkLocationTgAddr();
};
function shareUrl (url, text) {
PeersSelectService.selectPeer().then(function (toPeerString) {
$rootScope.$broadcast('history_focus', {
peerString: toPeerString,
attachment: {
_: 'share_url',
url: url,
text: text
}
});
});
}
return {
start: start,
shareUrl: shareUrl
};
})