Browse Source

merged with master

gh-pages
Igor Zhukov 11 years ago
parent
commit
0d19e2d534
  1. 259
      css/app.css
  2. BIN
      img/emojisprite_0.png
  3. BIN
      img/emojisprite_1.png
  4. BIN
      img/emojisprite_2.png
  5. BIN
      img/emojisprite_3.png
  6. BIN
      img/emojisprite_4.png
  7. BIN
      img/icons/EmojiTabSheet.png
  8. 11
      index.html
  9. 26
      js/app.js
  10. 2
      js/background.js
  11. 290
      js/controllers.js
  12. 74
      js/directives.js
  13. 2
      js/filters.js
  14. 2
      js/lib/aes_worker.js
  15. 4
      js/lib/config.js
  16. 28
      js/lib/mtproto.js
  17. 2
      js/lib/pq_worker.js
  18. 2
      js/lib/sha1_worker.js
  19. 106
      js/services.js
  20. 7
      js/util.js
  21. 2
      manifest.json
  22. 2
      manifest.webapp
  23. 30
      partials/chat_create_modal.html
  24. 7
      partials/chat_modal.html
  25. 53
      partials/contacts_modal.html
  26. 2
      partials/dialog.html
  27. 11
      partials/head.html
  28. 1
      partials/im.html
  29. 28
      partials/peer_select.html
  30. 28
      partials/settings_modal.html
  31. 113
      vendor/jquery.emojiarea/jquery.emojiarea.js

259
css/app.css

@ -109,6 +109,7 @@ fieldset[disabled] .btn-tg:active,
.btn-tg.disabled.active, .btn-tg.disabled.active,
.btn-tg[disabled].active, .btn-tg[disabled].active,
fieldset[disabled] .btn-tg.active { fieldset[disabled] .btn-tg.active {
color: #DDD;
background-color: #999; background-color: #999;
border-color: #999; border-color: #999;
} }
@ -145,6 +146,19 @@ fieldset[disabled] .btn-tg.active {
line-height: 0; line-height: 0;
height: auto; height: auto;
} }
.navbar-offline {
max-width: 250px;
margin: 0 auto;
float: none;
}
.navbar-offline-text {
padding: 12px 15px;
/*color: #FFF;*/
color: #b9cfe3;
font-size: 13px;
display: block;
line-height: 20px;
}
.tg_head_logo { .tg_head_logo {
@ -279,6 +293,10 @@ fieldset[disabled] .btn-tg.active {
opacity: 1; opacity: 1;
} }
.text-invisible {
visibility: hidden;
}
.modal-header { .modal-header {
padding: 12px 0 4px 3px; padding: 12px 0 4px 3px;
border-bottom: 2px solid #E1E1E1; border-bottom: 2px solid #E1E1E1;
@ -438,7 +456,6 @@ fieldset[disabled] .btn-tg.active {
/* Dialogs list */ /* Dialogs list */
.im_dialogs_col { .im_dialogs_col {
/*min-width: 315px;*/
margin-right: -7px; margin-right: -7px;
} }
.im_dialogs_col .nano > .pane { .im_dialogs_col .nano > .pane {
@ -588,6 +605,9 @@ a.im_dialog:hover .im_dialog_message_text {
color: #428bca; color: #428bca;
background-color: #fff; background-color: #fff;
} }
.im_dialogs_modal_col .im_dialog_badge {
display: none;
}
.im_dialog_unread { .im_dialog_unread {
background: #c1d6e5; background: #c1d6e5;
@ -649,11 +669,13 @@ a.im_dialog:hover .im_dialog_date {
.im_history_col { .im_history_col {
} }
.im_history_col .nano > .pane { .im_history_col .nano > .pane,
.contacts_modal_col .nano > .pane,
.im_dialogs_modal_col .nano > .pane {
background : rgba(3,36,64,0.08); background : rgba(3,36,64,0.08);
width : 9px; width : 9px;
top: 10px; right: 0;
right: 8px; top: 0;
-webkit-transition : .2s; -webkit-transition : .2s;
-moz-transition : .2s; -moz-transition : .2s;
-o-transition : .2s; -o-transition : .2s;
@ -662,7 +684,14 @@ a.im_dialog:hover .im_dialog_date {
-webkit-border-radius : 2px; -webkit-border-radius : 2px;
border-radius : 2px; border-radius : 2px;
} }
.im_history_col .nano > .pane > .slider {
.im_history_col .nano > .pane {
top: 10px;
right: 8px;
}
.im_history_col .nano > .pane > .slider,
.contacts_modal_col .nano > .pane > .slider,
.im_dialogs_modal_col .nano > .pane > .slider {
background : rgba(3,46,79,0.22); background : rgba(3,46,79,0.22);
margin: 0; margin: 0;
-moz-border-radius : 2px; -moz-border-radius : 2px;
@ -1103,7 +1132,8 @@ div.im_message_video_thumb {
.im_content_message_wrap { .im_content_message_wrap {
margin: 10px 0px 5px 16px; /*margin: 10px 0px 5px 16px;*/
margin: 8px 0px 8px 16px
} }
.icon-message-status { .icon-message-status {
background: #43A4DB; background: #43A4DB;
@ -1150,6 +1180,7 @@ div.im_message_video_thumb {
.im_message_date { .im_message_date {
color: #adadad; color: #adadad;
font-size: 0.85em; font-size: 0.85em;
padding-bottom: 20px;
} }
div.im_message_author, div.im_message_author,
div.im_message_body { div.im_message_body {
@ -1411,6 +1442,49 @@ img.img_fullsize {
.user_modal_wrap .modal-body { .user_modal_wrap .modal-body {
padding: 23px 25px 30px; padding: 23px 25px 30px;
} }
.settings_profile_photo_wrap {
width: 120px;
margin-right: 22px;
}
.settings_profile_photo {
position: relative;
overflow: hidden;
}
.settings_profile_photo_change_wrap {
background: rgba(0,0,0,0.6);
padding: 2px 5px;
position: absolute;
opacity: 0;
bottom: -30px;
-webkit-transition: all ease-in-out 0.2s;
transition: all ease-in-out 0.2s;
width: 120px;
}
.settings_profile_photo:hover .settings_profile_photo_change_wrap {
bottom: 0;
opacity: 1;
}
.settings_profile_photo_update_link,
.settings_profile_photo_delete_link,
.settings_profile_photo_loading {
display: block;
color: rgba(255,255,255,0.8);
text-align: center;
padding: 2px 0;
}
.settings_profile_photo_update_link:hover,
.settings_profile_photo_delete_link:hover {
color: #FFF;
text-decoration: none;
}
.settings_profile_photo_update_link {
position: relative;
overflow: hidden;
}
.user_modal_image_wrap { .user_modal_image_wrap {
width: 120px; width: 120px;
margin-right: 22px; margin-right: 22px;
@ -1498,6 +1572,10 @@ img.img_fullsize {
padding: 8px 7px; padding: 8px 7px;
border-top: 1px solid #F0F0F0; border-top: 1px solid #F0F0F0;
} }
.chat_modal_participant_kick {
padding: 11px 0;
display: block;
}
.chat_modal_participant_name { .chat_modal_participant_name {
display: block; display: block;
color: #3C6E97; color: #3C6E97;
@ -1564,7 +1642,7 @@ img.img_fullsize {
z-index: 999; z-index: 999;
width: 180px; width: 180px;
margin-left: -91px; margin-left: -91px;
margin-top: -232px; margin-top: -260px;
overflow: hidden; overflow: hidden;
} }
.emoji-items-wrap1 { .emoji-items-wrap1 {
@ -1574,12 +1652,62 @@ img.img_fullsize {
-moz-border-radius: 3px; -moz-border-radius: 3px;
border-radius: 3px; border-radius: 3px;
} }
.emoji-items-wrap1 .emoji-menu-tabs {
width: 100%;
}
.emoji-items-wrap1 .emoji-menu-tabs td {
text-align: center;
color: white;
}
.emoji-menu-tabs .emoji-menu-tab {
display: inline-block;
width: 24px;
height: 24px;
background: url(../img/icons/EmojiTabSheet.png) no-repeat;
background-size: 120px 48px;
}
.icon-smile {
background-position: 0 0 !important;
}
.icon-smile-selected {
background-position: 0 -24px !important;
border-bottom: 1px solid white;
}
.icon-flower {
background-position: -24px 0 !important;
}
.icon-flower-selected {
background-position: -24px -24px !important;
border-bottom: 1px solid white;
}
.icon-bell {
background-position: -48px 0 !important;
}
.icon-bell-selected {
background-position: -48px -24px !important;
border-bottom: 1px solid white;
}
.icon-car {
background-position: -72px 0 !important;
}
.icon-car-selected {
background-position: -72px -24px !important;
border-bottom: 1px solid white;
}
.icon-grid {
background-position: -96px 0 !important;
}
.icon-grid-selected {
background-position: -96px -24px !important;
border-bottom: 1px solid white;
}
.emoji-menu .emoji-items-wrap { .emoji-menu .emoji-items-wrap {
position: relative; position: relative;
height: 190px; height: 190px;
} }
.emoji-menu .emoji-items { .emoji-menu .emoji-items {
padding-right: 8px; padding-right: 8px;
outline: 0 !important;
} }
.emoji-menu img { .emoji-menu img {
width: 20px; width: 20px;
@ -1587,7 +1715,7 @@ img.img_fullsize {
vertical-align: middle; vertical-align: middle;
border: 0 none; border: 0 none;
} }
.emoji-menu a { .emoji-menu .emoji-items a {
margin: -1px 0 0 -1px; margin: -1px 0 0 -1px;
/*border: 1px solid #f2f2f2;*/ /*border: 1px solid #f2f2f2;*/
padding: 4px; padding: 4px;
@ -1595,7 +1723,7 @@ img.img_fullsize {
float: left; float: left;
border-radius: 2px; border-radius: 2px;
} }
.emoji-menu a:hover { .emoji-menu .emoji-items a:hover {
background-color: rgba(0,0,0, 0.5); background-color: rgba(0,0,0, 0.5);
/*background-color: #fffae7;*/ /*background-color: #fffae7;*/
} }
@ -1680,11 +1808,21 @@ img.img_fullsize {
.settings_profile_edit_form { .settings_profile_edit_form {
margin-bottom: 15px; margin-bottom: 15px;
} }
.settings_profile_image_wrap {
float: left;
width: 120px;
margin-right: 22px;
}
.settings_profile_first_name, .settings_profile_first_name,
.settings_profile_last_name { .settings_profile_last_name {
width: 180px; width: 180px;
margin-bottom: 10px;
float: left; float: left;
} }
.settings_profile_first_name label,
.settings_profile_last_name label {
margin-bottom: 3px;
}
.settings_profile_first_name { .settings_profile_first_name {
margin-right: 22px; margin-right: 22px;
} }
@ -1777,13 +1915,38 @@ img.img_fullsize {
opacity: 1; opacity: 1;
} }
.contacts_modal_contact_wrap { .contacts_modal_col {
padding: 8px 7px; margin-right: -17px;
border-top: 1px solid #F0F0F0; }
.contacts_scrollable_wrap {
padding: 0 17px 0 0;
outline: none ! important;
}
.contacts_modal_members_list .contacts_modal_contact_wrap {
/*margin-top: 0;*/
} }
.contacts_modal_contact_wrap:first-child { .contacts_modal_members_list a.contacts_modal_contact {
border-top: 0; clear: both;
overflow: hidden;
color: #000;
padding: 8px 9px;
border-radius: 0;
}
.contacts_modal_members_list a.contacts_modal_contact:hover {
border-radius: 2px;
background: #f2f6fa;
} }
.contacts_modal_members_list .active a.contacts_modal_contact {
border-radius: 2px;
background-color: #6490b1;
}
.contacts_modal_members_list .active a.contacts_modal_contact:hover {
background-color: #6490b1;
}
.contacts_modal_contact_name { .contacts_modal_contact_name {
display: block; display: block;
color: #3C6E97; color: #3C6E97;
@ -1793,9 +1956,6 @@ img.img_fullsize {
.non_osx .contacts_modal_contact_name { .non_osx .contacts_modal_contact_name {
font-size: 12px; font-size: 12px;
} }
.contacts_modal_contact_status {
color: #999;
}
.contacts_modal_contact_photo { .contacts_modal_contact_photo {
width: 40px; width: 40px;
height: 40px; height: 40px;
@ -1803,6 +1963,50 @@ img.img_fullsize {
overflow: hidden; overflow: hidden;
} }
.contacts_modal_contact_status {
color: #999;
}
a.contacts_modal_contact:hover .contacts_modal_contact_status {
color: #91a6ba;
}
.contacts_modal_members_list .active .contacts_modal_contact_name,
.contacts_modal_members_list .active a.contacts_modal_contact .contacts_modal_contact_status {
color: #FFF;
}
.icon-contact-tick {
position: absolute;
right: 10px;
top: 22px;
width: 17px;
height: 15px;
/*margin: 10px 0 0 -75px;*/
background: url(../img/icons/IconsetW.png?1) -13px -343px no-repeat;
background-size: 42px 460px;
}
.is_1x .icon-contact-tick {
background-image: url(../img/icons/IconsetW_1x.png?2);
}
.contacts_modal_members_list .active .icon-contact-tick,
.contacts_modal_members_list a.contacts_modal_contact:hover .icon-contact-tick {
background-position: -13px -367px;
opacity: 0.5;
}
.contacts_modal_members_list .active .icon-contact-tick {
opacity: 1 !important;
}
.contacts_modal_panel {
padding-top: 10px;
}
.contacts_modal_actions {
padding-top: 10px;
}
/* Messages edit panel */ /* Messages edit panel */
.im_edit_panel_wrap { .im_edit_panel_wrap {
@ -1816,6 +2020,7 @@ img.img_fullsize {
width: 100%; width: 100%;
} }
.im_edit_delete_link, .im_edit_delete_link,
.im_edit_forward_link,
.im_edit_cancel_link { .im_edit_cancel_link {
display: block; display: block;
padding: 8px 17px; padding: 8px 17px;
@ -1823,11 +2028,13 @@ img.img_fullsize {
.im_edit_cancel_link { .im_edit_cancel_link {
float: left; float: left;
} }
.im_edit_delete_link { .im_edit_delete_link,
.im_edit_forward_link {
float: right; float: right;
} }
.im_edit_delete_link:hover, .im_edit_delete_link:hover,
.im_edit_forward_link:hover,
.im_edit_cancel_link:hover { .im_edit_cancel_link:hover {
background: #f2f6fa; background: #f2f6fa;
text-decoration: none; text-decoration: none;
@ -1898,8 +2105,11 @@ img.img_fullsize {
.is_1x .icon-select-tick { .is_1x .icon-select-tick {
background-image: url(../img/icons/IconsetW_1x.png?2); background-image: url(../img/icons/IconsetW_1x.png?2);
} }
.im_history_selectable .icon-select-tick {
display: inline-block; @media (min-width: 1024px) {
.im_history_selectable .icon-select-tick {
display: inline-block;
}
} }
.im_message_selected .icon-select-tick, .im_message_selected .icon-select-tick,
@ -2067,4 +2277,13 @@ img.img_fullsize {
.im_emoji_btn { .im_emoji_btn {
display: none; display: none;
} }
}
/* Dialogs modal */
.peer_select_window .modal-dialog {
max-width: 506px;
}
.peer_select_modal_wrap .modal-body {
padding: 10px 10px 15px;
} }

BIN
img/emojisprite_0.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 722 KiB

BIN
img/emojisprite_1.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 531 KiB

BIN
img/emojisprite_2.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 944 KiB

BIN
img/emojisprite_3.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 402 KiB

BIN
img/emojisprite_4.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 470 KiB

BIN
img/icons/EmojiTabSheet.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

11
index.html

@ -47,16 +47,5 @@
<script type="text/javascript" src="js/filters.js?4"></script> <script type="text/javascript" src="js/filters.js?4"></script>
<script type="text/javascript" src="js/directives.js?20"></script> <script type="text/javascript" src="js/directives.js?20"></script>
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-45099287-2');
ga('send', 'pageview');
</script>
</body> </body>
</html> </html>

26
js/app.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -38,21 +38,23 @@ angular.module('myApp', [
]). ]).
config(['$locationProvider', '$routeProvider', '$compileProvider', function($locationProvider, $routeProvider, $compileProvider) { config(['$locationProvider', '$routeProvider', '$compileProvider', function($locationProvider, $routeProvider, $compileProvider) {
var icons = {}, reverseIcons = {}, i, j, hex, name, dataItem, var icons = {}, reverseIcons = {}, i, j, hex, name, dataItem, row, column, totalColumns;
ranges = [[0x1f600, 0x1f637], [0x261d, 0x263f], [0x270a, 0x270c], [0x1f446, 0x1f450]];
for (j in ranges) { for (j = 0; j < Config.EmojiCategories.length; j++) {
for (i = ranges[j][0]; i <= ranges[j][1]; i++) { totalColumns = Config.EmojiCategorySpritesheetDimens[j][1];
hex = i.toString(16); for (i = 0; i < Config.EmojiCategories[j].length; i++) {
if (dataItem = Config.Emoji[hex]) { dataItem = Config.Emoji[Config.EmojiCategories[j][i]];
name = dataItem[1][0]; name = dataItem[1][0];
icons[':' + name + ':'] = hex + '.png'; row = Math.floor(i / totalColumns);
reverseIcons[name] = dataItem[0]; column = (i % totalColumns);
} icons[':' + name + ':'] = [j, row, column, ':'+name+':'];
reverseIcons[name] = dataItem[0];
} }
} }
$.emojiarea.path = 'vendor/gemoji/images'; $.emojiarea.spritesheetPath = 'img/emojisprite_!.png';
$.emojiarea.spritesheetDimens = Config.EmojiCategorySpritesheetDimens;
$.emojiarea.iconSize = 20;
$.emojiarea.icons = icons; $.emojiarea.icons = icons;
$.emojiarea.reverseIcons = reverseIcons; $.emojiarea.reverseIcons = reverseIcons;

2
js/background.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE

290
js/controllers.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -22,6 +22,12 @@ angular.module('myApp.controllers', [])
}) })
.controller('AppLoginController', function ($scope, $location, $timeout, MtpApiManager, ErrorService) { .controller('AppLoginController', function ($scope, $location, $timeout, MtpApiManager, ErrorService) {
MtpApiManager.getUserID().then(function (id) {
if (id) {
$location.url('/im');
return;
}
});
var options = {dcID: 1}; var options = {dcID: 1};
$scope.credentials = {}; $scope.credentials = {};
@ -145,7 +151,7 @@ angular.module('myApp.controllers', [])
}; };
}) })
.controller('AppIMController', function ($scope, $location, $routeParams, $modal, $rootScope, $modalStack, MtpApiManager) { .controller('AppIMController', function ($scope, $location, $routeParams, $modal, $rootScope, $modalStack, MtpApiManager, AppUsersManager, ContactsSelectService) {
$scope.$on('$routeUpdate', updateCurDialog); $scope.$on('$routeUpdate', updateCurDialog);
@ -170,14 +176,35 @@ angular.module('myApp.controllers', [])
} }
$scope.openContacts = function () { $scope.openContacts = function () {
$modal.open({ ContactsSelectService.selectContact().then(function (userID) {
templateUrl: 'partials/contacts_modal.html?3', $scope.dialogSelect(AppUsersManager.getUserString(userID));
controller: 'ContactsModalController', });
scope: $rootScope.$new(), }
windowClass: 'contacts_modal_window'
$scope.openGroup = function () {
ContactsSelectService.selectContacts().then(function (userIDs) {
if (userIDs.length == 1) {
$scope.dialogSelect(AppUsersManager.getUserString(userIDs[0]));
} else if (userIDs.length > 1) {
var scope = $rootScope.$new();
scope.userIDs = userIDs;
$modal.open({
templateUrl: 'partials/chat_create_modal.html?3',
controller: 'ChatCreateModalController',
scope: scope,
windowClass: 'contacts_modal_window'
});
}
}); });
} }
$scope.dialogSelect = function (peerString) {
$rootScope.$broadcast('history_focus', {peerString: peerString});
};
updateCurDialog(); updateCurDialog();
function updateCurDialog() { function updateCurDialog() {
@ -294,11 +321,11 @@ angular.module('myApp.controllers', [])
$scope.$broadcast('ui_dialogs_append'); $scope.$broadcast('ui_dialogs_append');
}); });
} };
}) })
.controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, IdleManager, StatusManager) { .controller('AppImHistoryController', function ($scope, $location, $timeout, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, PeersSelectService, IdleManager, StatusManager) {
$scope.$watch('curDialog.peer', applyDialogSelect); $scope.$watch('curDialog.peer', applyDialogSelect);
@ -318,6 +345,7 @@ angular.module('myApp.controllers', [])
$scope.toggleMessage = toggleMessage; $scope.toggleMessage = toggleMessage;
$scope.selectedDelete = selectedDelete; $scope.selectedDelete = selectedDelete;
$scope.selectedForward = selectedForward;
$scope.selectedCancel = selectedCancel; $scope.selectedCancel = selectedCancel;
$scope.toggleEdit = toggleEdit; $scope.toggleEdit = toggleEdit;
$scope.toggleMedia = toggleMedia; $scope.toggleMedia = toggleMedia;
@ -385,16 +413,14 @@ angular.module('myApp.controllers', [])
if (!hasMore || !offset) { if (!hasMore || !offset) {
return; return;
} }
// console.trace('load history'); console.trace('load history');
var inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]}, var inputMediaFilter = $scope.mediaType && {_: inputMediaFilters[$scope.mediaType]},
getMessagesPromise = inputMediaFilter getMessagesPromise = inputMediaFilter
? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID, startLimit) ? AppMessagesManager.getSearch($scope.curDialog.inputPeer, '', inputMediaFilter, maxID, limit)
: AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, startLimit); : AppMessagesManager.getHistory($scope.curDialog.inputPeer, maxID, limit);
getMessagesPromise.then(function (historyResult) { getMessagesPromise.then(function (historyResult) {
console.log('got', maxID, historyResult);
offset += limit; offset += limit;
hasMore = offset < historyResult.count; hasMore = offset < historyResult.count;
maxID = historyResult.history[historyResult.history.length - 1]; maxID = historyResult.history[historyResult.history.length - 1];
@ -491,6 +517,24 @@ angular.module('myApp.controllers', [])
} }
} }
function selectedForward () {
if ($scope.selectedCount > 0) {
var selectedMessageIDs = [];
angular.forEach($scope.selectedMsgs, function (t, messageID) {
selectedMessageIDs.push(messageID);
});
PeersSelectService.selectPeer().then(function (peerString) {
var inputPeer = AppPeersManager.getInputPeer(peerString);
AppMessagesManager.forwardMessages(selectedMessageIDs, inputPeer).then(function () {
selectedCancel();
});
});
}
}
function toggleEdit () { function toggleEdit () {
if ($scope.selectActions) { if ($scope.selectActions) {
selectedCancel(); selectedCancel();
@ -751,7 +795,7 @@ angular.module('myApp.controllers', [])
}; };
}) })
.controller('ChatModalController', function ($scope, $timeout, $rootScope, AppUsersManager, AppChatsManager, MtpApiManager, NotificationsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager) { .controller('ChatModalController', function ($scope, $timeout, $rootScope, AppUsersManager, AppChatsManager, MtpApiManager, NotificationsManager, AppMessagesManager, AppPeersManager, ApiUpdatesManager, ContactsSelectService) {
$scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, {}); $scope.chatFull = AppChatsManager.wrapForFull($scope.chatID, {});
@ -820,6 +864,73 @@ angular.module('myApp.controllers', [])
}; };
$scope.inviteToGroup = function () {
var disabled = [];
angular.forEach($scope.chatFull.participants.participants, function(participant){
disabled.push(participant.user_id);
});
ContactsSelectService.selectContacts({disabled: disabled}).then(function (userIDs) {
angular.forEach(userIDs, function (userID) {
MtpApiManager.invokeApi('messages.addChatUser', {
chat_id: $scope.chatID,
user_id: {_: 'inputUserContact', user_id: userID},
fwd_limit: 100
}).then(function (addResult) {
AppUsersManager.saveApiUsers(addResult.users);
AppChatsManager.saveApiChats(addResult.chats);
if (ApiUpdatesManager.saveSeq(addResult.seq)) {
ApiUpdatesManager.saveUpdate({
_: 'updateNewMessage',
message: addResult.message,
pts: addResult.pts
});
}
});
});
$rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString});
});
MtpApiManager.invokeApi('messages.addChatUser', {
chat_id: $scope.chatID,
user_id: {_: 'inputUserSelf'}
}).then(function (result) {
if (ApiUpdatesManager.saveSeq(result.seq)) {
ApiUpdatesManager.saveUpdate({
_: 'updateNewMessage',
message: result.message,
pts: result.pts
});
}
$rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString});
});
};
$scope.kickFromGroup = function (userID) {
var user = AppUsersManager.getUser(userID);
console.log({_: 'inputUserForeign', user_id: userID, access_hash: user.access_hash || '0'}, user);
MtpApiManager.invokeApi('messages.deleteChatUser', {
chat_id: $scope.chatID,
user_id: {_: 'inputUserForeign', user_id: userID, access_hash: user.access_hash || '0'}
}).then(function (result) {
if (ApiUpdatesManager.saveSeq(result.seq)) {
ApiUpdatesManager.saveUpdate({
_: 'updateNewMessage',
message: result.message,
pts: result.pts
});
}
$rootScope.$broadcast('history_focus', {peerString: $scope.chatFull.peerString});
});
};
$scope.flushHistory = function () { $scope.flushHistory = function () {
if (confirm('Are you sure? This can not be undone!') !== true) { if (confirm('Are you sure? This can not be undone!') !== true) {
@ -833,7 +944,7 @@ angular.module('myApp.controllers', [])
}) })
.controller('SettingsModalController', function ($rootScope, $scope, $timeout, AppUsersManager, AppChatsManager, MtpApiManager, AppConfigManager, NotificationsManager) { .controller('SettingsModalController', function ($rootScope, $scope, $timeout, AppUsersManager, AppChatsManager, MtpApiManager, AppConfigManager, NotificationsManager, MtpApiFileManager, ApiUpdatesManager) {
$scope.profile = {}; $scope.profile = {};
@ -841,6 +952,7 @@ angular.module('myApp.controllers', [])
var user = AppUsersManager.getUser(id); var user = AppUsersManager.getUser(id);
$scope.profile.first_name = user.first_name; $scope.profile.first_name = user.first_name;
$scope.profile.last_name = user.last_name; $scope.profile.last_name = user.last_name;
$scope.profile.photo = AppUsersManager.getUserPhoto(id, 'User');
$scope.phone = user.phone; $scope.phone = user.phone;
}); });
@ -848,6 +960,58 @@ angular.module('myApp.controllers', [])
$scope.notify = {}; $scope.notify = {};
$scope.send = {}; $scope.send = {};
$scope.photo = {};
$scope.$watch('photo.file', onPhotoSelected);
function onPhotoSelected (photo) {
if (!photo || !photo.hasOwnProperty('name')) {
return;
}
$scope.photo.updating = true;
MtpApiFileManager.uploadFile(photo).then(function (inputFile) {
MtpApiManager.invokeApi('photos.uploadProfilePhoto', {
file: inputFile,
caption: '',
geo_point: {_: 'inputGeoPointEmpty'},
crop: {_: 'inputPhotoCropAuto'}
}).then(function (updateResult) {
AppUsersManager.saveApiUsers(updateResult.users);
MtpApiManager.getUserID().then(function (id) {
ApiUpdatesManager.saveUpdate({
_: 'updateUserPhoto',
user_id: id,
date: tsNow(true),
photo: AppUsersManager.getUser(id).photo,
previous: true
});
$scope.profile.photo = AppUsersManager.getUserPhoto(id, 'User');
});
$scope.photo.updating = false;
});
});
};
$scope.deletePhoto = function () {
$scope.photo.updating = true;
MtpApiManager.invokeApi('photos.updateProfilePhoto', {
id: {_: 'inputPhotoEmpty'},
crop: {_: 'inputPhotoCropAuto'}
}).then(function (updateResult) {
MtpApiManager.getUserID().then(function (id) {
ApiUpdatesManager.saveUpdate({
_: 'updateUserPhoto',
user_id: id,
date: tsNow(true),
photo: updateResult,
previous: true
});
$scope.profile.photo = AppUsersManager.getUserPhoto(id, 'User');
});
$scope.photo.updating = false;
});
};
AppConfigManager.get('notify_nodesktop', 'notify_nosound', 'send_ctrlenter').then(function (settings) { AppConfigManager.get('notify_nodesktop', 'notify_nosound', 'send_ctrlenter').then(function (settings) {
$scope.notify.desktop = !settings[0]; $scope.notify.desktop = !settings[0];
$scope.notify.sound = !settings[1]; $scope.notify.sound = !settings[1];
@ -925,9 +1089,28 @@ angular.module('myApp.controllers', [])
} }
}) })
.controller('ContactsModalController', function ($scope, AppUsersManager) { .controller('ContactsModalController', function ($scope, $modalInstance, AppUsersManager) {
$scope.contacts = []; $scope.contacts = [];
$scope.search = []; $scope.search = {};
$scope.selectedContacts = {};
$scope.disabledContacts = {};
$scope.selectedCount = 0;
if ($scope.disabled) {
for (var i = 0; i < $scope.disabled.length; i++) {
$scope.disabledContacts[$scope.disabled[i]] = true;
}
}
if ($scope.selected) {
for (var i = 0; i < $scope.selected.length; i++) {
if (!$scope.selectedContacts[$scope.selected[i]]) {
$scope.selectedContacts[$scope.selected[i]] = true;
$scope.selectedCount++;
}
}
}
$scope.$watch('search.query', function (newValue) { $scope.$watch('search.query', function (newValue) {
AppUsersManager.getContacts(newValue).then(function (contactsList) { AppUsersManager.getContacts(newValue).then(function (contactsList) {
@ -940,10 +1123,79 @@ angular.module('myApp.controllers', [])
} }
$scope.contacts.push(contact); $scope.contacts.push(contact);
}); });
$scope.$broadcast('contacts_change');
}); });
}) });
$scope.contactSelect = function (userID) {
if ($scope.disabledContacts[userID]) {
return false;
}
if (!$scope.multiSelect) {
return $modalInstance.close(userID);
}
if ($scope.selectedContacts[userID]) {
delete $scope.selectedContacts[userID];
$scope.selectedCount--;
} else {
$scope.selectedContacts[userID] = true;
$scope.selectedCount++;
}
};
$scope.submitSelected = function () {
if ($scope.selectedCount > 0) {
var selectedUserIDs = [];
angular.forEach($scope.selectedContacts, function (t, userID) {
selectedUserIDs.push(userID);
});
return $modalInstance.close(selectedUserIDs);
}
}
})
.controller('PeerSelectController', function ($scope, $modalInstance) {
$scope.dialogSelect = function (peerString) {
$modalInstance.close(peerString);
};
}) })
.controller('ChatCreateModalController', function ($scope, $modalInstance, $rootScope, MtpApiManager, AppUsersManager, AppChatsManager, ApiUpdatesManager) {
$scope.group = {name: ''};
$scope.createGroup = function () {
if (!$scope.group.name) {
return;
}
var inputUsers = [];
angular.forEach($scope.userIDs, function(userID) {
inputUsers.push({_: 'inputUserContact', user_id: userID});
});
return MtpApiManager.invokeApi('messages.createChat', {
title: $scope.group.name,
users: inputUsers
}).then(function (createdResult) {
AppUsersManager.saveApiUsers(createdResult.users);
AppChatsManager.saveApiChats(createdResult.chats);
if (ApiUpdatesManager.saveSeq(createdResult.seq)) {
ApiUpdatesManager.saveUpdate({
_: 'updateNewMessage',
message: createdResult.message,
pts: createdResult.pts
});
}
var peerString = AppChatsManager.getChatString(createdResult.message.to_id.chat_id);
$rootScope.$broadcast('history_focus', {peerString: peerString});
});
};
$scope.back = function () {
$modalInstance.dismiss();
};
})

74
js/directives.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -81,9 +81,18 @@ angular.module('myApp.directives', ['myApp.filters'])
function updateSizes () { function updateSizes () {
if (attrs.modal) {
$(element).css({
height: $($window).height() - 200
});
updateScroller();
return;
}
$(element).css({ $(element).css({
height: $($window).height() - footer.offsetHeight - (headWrap ? headWrap.offsetHeight : 50) - 72 height: $($window).height() - footer.offsetHeight - (headWrap ? headWrap.offsetHeight : 50) - 72
}); });
updateScroller();
if (!headWrap) { if (!headWrap) {
headWrap = $('.tg_page_head')[0]; headWrap = $('.tg_page_head')[0];
} }
@ -96,6 +105,38 @@ angular.module('myApp.directives', ['myApp.filters'])
}) })
.directive('myContactsList', function($window, $timeout) {
return {
link: link
};
function link (scope, element, attrs) {
var searchWrap = $('.contacts_modal_search')[0],
panelWrap = $('.contacts_modal_panel')[0],
contactsWrap = $('.contacts_wrap', element)[0];
onContentLoaded(function () {
$(contactsWrap).nanoScroller({preventPageScrolling: true, tabIndex: -1, iOSNativeScrolling: true});
updateSizes();
});
function updateSizes () {
$(element).css({
height: $($window).height() - (panelWrap && panelWrap.offsetHeight || 0) - (searchWrap && searchWrap.offsetHeight || 0) - 200
});
$(contactsWrap).nanoScroller();
}
$($window).on('resize', updateSizes);
scope.$on('contacts_change', function () {
onContentLoaded(updateSizes)
});
};
})
.directive('myHistory', function ($window, $timeout) { .directive('myHistory', function ($window, $timeout) {
return { return {
@ -479,7 +520,9 @@ angular.module('myApp.directives', ['myApp.filters'])
return; return;
} }
element.attr('src', scope.thumb.placeholder || 'img/blank.gif'); if (!element.attr('src')) {
element.attr('src', scope.thumb.placeholder || 'img/blank.gif');
}
MtpApiFileManager.downloadSmallFile(scope.thumb.location, scope.thumb.size).then(function (url) { MtpApiFileManager.downloadSmallFile(scope.thumb.location, scope.thumb.size).then(function (url) {
if (counterSaved == counter) { if (counterSaved == counter) {
@ -711,7 +754,11 @@ angular.module('myApp.directives', ['myApp.filters'])
} else if (time % 1000 <= 600) { } else if (time % 1000 <= 600) {
cnt = 2; cnt = 2;
} }
element.html((new Array(cnt + 1)).join('.'));
var text = '...',
html = text.substr(0, cnt + 1) + (cnt < 2 ? ('<span class="text-invisible">' + text.substr(cnt + 1) + '</span>') : '');
element.html(html);
}, 200); }, 200);
scope.$on('$destroy', function cleanup() { scope.$on('$destroy', function cleanup() {
@ -749,4 +796,25 @@ angular.module('myApp.directives', ['myApp.filters'])
}, 100); }, 100);
} }
}; };
})
.directive('myFileUpload', function(){
return {
link: link
};
function link(scope, element, attrs) {
element.on('change', function () {
var self = this;
scope.$apply(function () {
scope.photo.file = self.files[0];
setTimeout(function () {
try {
self.value = '';
} catch (e) {};
}, 1000);
});
});
};
}); });

2
js/filters.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE

2
js/lib/aes_worker.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE

4
js/lib/config.js

File diff suppressed because one or more lines are too long

28
js/lib/mtproto.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -893,12 +893,12 @@ factory('MtpDcConfigurator', function () {
var dcOptions = window._testMode var dcOptions = window._testMode
? [ ? [
{id: 1, host: '173.240.5.253', port: 80}, {id: 1, host: '173.240.5.253', port: 80},
{id: 2, host: '95.142.192.65', port: 80}, {id: 2, host: '109.239.131.195', port: 80},
{id: 3, host: '174.140.142.5', port: 80} {id: 3, host: '174.140.142.5', port: 80}
] ]
: [ : [
{id: 1, host: '173.240.5.1', port: 80}, {id: 1, host: '173.240.5.1', port: 80},
{id: 2, host: '95.142.192.66', port: 80}, {id: 2, host: '109.239.131.193', port: 80},
{id: 3, host: '174.140.142.6', port: 80}, {id: 3, host: '174.140.142.6', port: 80},
{id: 4, host: '31.210.235.12', port: 80}, {id: 4, host: '31.210.235.12', port: 80},
{id: 5, host: '116.51.22.2', port: 80}, {id: 5, host: '116.51.22.2', port: 80},
@ -1500,11 +1500,18 @@ factory('MtpSha1Service', function ($q) {
} }
}). }).
factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerator, MtpSecureRandom, MtpSha1Service, MtpAesService, AppConfigManager, $http, $q, $timeout, $interval) { factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerator, MtpSecureRandom, MtpSha1Service, MtpAesService, AppConfigManager, $http, $q, $timeout, $interval, $rootScope) {
var updatesProcessor, var updatesProcessor,
iii = 0, iii = 0,
offline = false; offline;
$rootScope.offline = true;
$rootScope.offlineConnecting = true;
$rootScope.retryOnline = function () {
$(document.body).trigger('online');
}
function MtpNetworker(dcID, authKey, serverSalt, options) { function MtpNetworker(dcID, authKey, serverSalt, options) {
options = options || {}; options = options || {};
@ -1759,6 +1766,8 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
}; };
MtpNetworker.prototype.checkConnection = function(event) { MtpNetworker.prototype.checkConnection = function(event) {
$rootScope.offlineConnecting = true;
console.log('check connection', event); console.log('check connection', event);
$timeout.cancel(this.checkConnectionPromise); $timeout.cancel(this.checkConnectionPromise);
@ -1775,21 +1784,27 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var self = this; var self = this;
this.sendEncryptedRequest(pingMessage).then(function (result) { this.sendEncryptedRequest(pingMessage).then(function (result) {
delete $rootScope.offlineConnecting;
self.toggleOffline(false); self.toggleOffline(false);
}, function () { }, function () {
console.log('delay ', self.checkConnectionPeriod * 1000); console.log('delay ', self.checkConnectionPeriod * 1000);
self.checkConnectionPromise = $timeout(self.checkConnection.bind(self), parseInt(self.checkConnectionPeriod * 1000)); self.checkConnectionPromise = $timeout(self.checkConnection.bind(self), parseInt(self.checkConnectionPeriod * 1000));
self.checkConnectionPeriod = Math.min(60, self.checkConnectionPeriod * 1.5); self.checkConnectionPeriod = Math.min(60, self.checkConnectionPeriod * 1.5);
$timeout(function () {
delete $rootScope.offlineConnecting;
}, 1000);
}) })
}; };
MtpNetworker.prototype.toggleOffline = function(enabled) { MtpNetworker.prototype.toggleOffline = function(enabled) {
console.log('toggle ', enabled, this.dcID, this.iii); console.log('toggle ', enabled, this.dcID, this.iii);
if (this.offline == enabled) { if (this.offline !== undefined && this.offline == enabled) {
return false; return false;
} }
this.offline = enabled; this.offline = enabled;
$rootScope.offline = enabled;
$rootScope.offlineConnecting = false;
if (this.offline) { if (this.offline) {
$timeout.cancel(this.nextReqPromise); $timeout.cancel(this.nextReqPromise);
@ -1912,6 +1927,7 @@ factory('MtpNetworkerFactory', function (MtpDcConfigurator, MtpMessageIdGenerato
var self = this; var self = this;
this.sendEncryptedRequest(message).then(function (result) { this.sendEncryptedRequest(message).then(function (result) {
self.toggleOffline(false);
self.parseResponse(result.data).then(function (response) { self.parseResponse(result.data).then(function (response) {
if (window._debugMode) { if (window._debugMode) {
console.log('Server response', self.dcID, response); console.log('Server response', self.dcID, response);

2
js/lib/pq_worker.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE

2
js/lib/sha1_worker.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE

106
js/services.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -118,7 +118,7 @@ angular.module('myApp.services', [])
}; };
}) })
.service('AppUsersManager', function ($rootScope, $modal, $modalStack, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager) { .service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager) {
var users = {}, var users = {},
contactsFillPromise, contactsFillPromise,
contactsIndex = SearchIndexManager.createIndex(); contactsIndex = SearchIndexManager.createIndex();
@ -180,12 +180,16 @@ angular.module('myApp.services', [])
return; return;
} }
if (apiUser.phone) {
apiUser.rPhone = $filter('phoneNumber')(apiUser.phone);
}
if (apiUser.first_name) { if (apiUser.first_name) {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.first_name, {noLinks: true, noLinebreaks: true}); apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.first_name, {noLinks: true, noLinebreaks: true});
apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.first_name + ' ' + (apiUser.last_name || ''), {noLinks: true, noLinebreaks: true}); apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.first_name + ' ' + (apiUser.last_name || ''), {noLinks: true, noLinebreaks: true});
} else { } else {
apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || 'DELETED'; apiUser.rFirstName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || 'DELETED';
apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || 'DELETED'; apiUser.rFullName = RichTextProcessor.wrapRichText(apiUser.last_name, {noLinks: true, noLinebreaks: true}) || apiUser.rPhone || 'DELETED';
} }
apiUser.sortName = $.trim((apiUser.last_name || '') + ' ' + apiUser.first_name); apiUser.sortName = $.trim((apiUser.last_name || '') + ' ' + apiUser.first_name);
apiUser.sortStatus = apiUser.status && (apiUser.status.expires || apiUser.status.was_online) || 0; apiUser.sortStatus = apiUser.status && (apiUser.status.expires || apiUser.status.was_online) || 0;
@ -361,11 +365,15 @@ angular.module('myApp.services', [])
var chatFull = angular.copy(fullChat), var chatFull = angular.copy(fullChat),
chat = getChat(id); chat = getChat(id);
if (chatFull.participants && chatFull.participants._ == 'chatParticipants') { if (chatFull.participants && chatFull.participants._ == 'chatParticipants') {
angular.forEach(chatFull.participants.participants, function(participant){ MtpApiManager.getUserID().then(function (myID) {
participant.user = AppUsersManager.getUser(participant.user_id); angular.forEach(chatFull.participants.participants, function(participant){
participant.userPhoto = AppUsersManager.getUserPhoto(participant.user_id, 'User'); participant.user = AppUsersManager.getUser(participant.user_id);
participant.inviter = AppUsersManager.getUser(participant.inviter_id); participant.userPhoto = AppUsersManager.getUserPhoto(participant.user_id, 'User');
participant.inviter = AppUsersManager.getUser(participant.inviter_id);
participant.canKick = myID != participant.user_id && (myID == chat.admin_id || myID == participant.inviter_id);
});
}); });
} }
@ -1126,6 +1134,28 @@ angular.module('myApp.services', [])
pendingByRandomID[randomIDS] = [peerID, messageID]; pendingByRandomID[randomIDS] = [peerID, messageID];
} }
function forwardMessages (msgIDs, inputPeer) {
return MtpApiManager.invokeApi('messages.forwardMessages', {
peer: inputPeer,
id: msgIDs
}).then(function (forwardResult) {
AppUsersManager.saveApiUsers(forwardResult.users);
AppChatsManager.saveApiChats(forwardResult.chats);
if (ApiUpdatesManager.saveSeq(forwardResult.seq)) {
angular.forEach(forwardResult.messages, function(apiMessage) {
ApiUpdatesManager.saveUpdate({
_: 'updateNewMessage',
message: apiMessage,
pts: forwardResult.pts
});
});
}
});
};
function finalizePendingMessage(randomID, finalMessage) { function finalizePendingMessage(randomID, finalMessage) {
var pendingData = pendingByRandomID[randomID]; var pendingData = pendingByRandomID[randomID];
@ -1178,7 +1208,7 @@ angular.module('myApp.services', [])
if (toID < 0) { if (toID < 0) {
return toID; return toID;
} else if (message.out) { } else if (message.out) {
return toID return toID;
} }
return message.from_id; return message.from_id;
} }
@ -1533,6 +1563,7 @@ angular.module('myApp.services', [])
saveMessages: saveMessages, saveMessages: saveMessages,
sendText: sendText, sendText: sendText,
sendFile: sendFile, sendFile: sendFile,
forwardMessages: forwardMessages,
getMessagePeer: getMessagePeer, getMessagePeer: getMessagePeer,
wrapForDialog: wrapForDialog, wrapForDialog: wrapForDialog,
wrapForHistory: wrapForHistory wrapForHistory: wrapForHistory
@ -2231,7 +2262,7 @@ angular.module('myApp.services', [])
'<a href="', '<a href="',
encodeEntities(match[2] + '://' + match[4]), encodeEntities(match[2] + '://' + match[4]),
'" target="_blank">', '" target="_blank">',
encodeEntities((match[2] != 'http' ? match[2] + '://' : '') + match[4]), encodeEntities(match[2] + '://' + match[4]),
'</a>' '</a>'
); );
} }
@ -2345,13 +2376,15 @@ angular.module('myApp.services', [])
if (!started) { if (!started) {
started = true; started = true;
$rootScope.$watch('idle.isIDLE', checkIDLE); $rootScope.$watch('idle.isIDLE', checkIDLE);
$rootScope.$watch('offline', checkIDLE);
} }
} }
function sendUpdateStatusReq(offline) { function sendUpdateStatusReq(offline) {
var date = tsNow(); var date = tsNow();
if (offline && !lastOnlineUpdated || if (offline && !lastOnlineUpdated ||
!offline && (date - lastOnlineUpdated) < 50000) { !offline && (date - lastOnlineUpdated) < 50000 ||
$rootScope.offline) {
return; return;
} }
lastOnlineUpdated = offline ? 0 : date; lastOnlineUpdated = offline ? 0 : date;
@ -2591,3 +2624,54 @@ angular.module('myApp.services', [])
showSimpleError: showSimpleError showSimpleError: showSimpleError
} }
}) })
.service('PeersSelectService', function ($rootScope, $modal) {
function selectPeer () {
var scope = $rootScope.$new();
// angular.extend(scope, params);
return $modal.open({
templateUrl: 'partials/peer_select.html',
controller: 'PeerSelectController',
scope: scope,
windowClass: 'peer_select_window'
}).result;
}
return {
selectPeer: selectPeer
}
})
.service('ContactsSelectService', function ($rootScope, $modal) {
function select (multiSelect, options) {
options = options || {};
var scope = $rootScope.$new();
scope.multiSelect = multiSelect;
angular.extend(scope, options);
return $modal.open({
templateUrl: 'partials/contacts_modal.html',
controller: 'ContactsModalController',
scope: scope,
windowClass: 'contacts_modal_window'
}).result;
}
return {
selectContacts: function (options) {
return select (true, options);
},
selectContact: function (options) {
return select (false, options);
},
}
})

7
js/util.js

@ -1,5 +1,5 @@
/*! /*!
* Webogram v0.0.18 - messaging web application for MTProto * Webogram v0.0.19 - messaging web application for MTProto
* https://github.com/zhukov/webogram * https://github.com/zhukov/webogram
* Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com> * Copyright (C) 2014 Igor Zhukov <igor.beatle@gmail.com>
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
@ -54,8 +54,9 @@ function onContentLoaded (cb) {
setTimeout(cb, 0); setTimeout(cb, 0);
}; };
function tsNow () { function tsNow (seconds) {
return +new Date(); var t = +new Date();
return seconds ? Math.floor(t / 1000) : t;
} }
function safeReplaceObject (wasObject, newObject) { function safeReplaceObject (wasObject, newObject) {

2
manifest.json

@ -1,6 +1,6 @@
{ {
"name": "Telegram UNOFFICIAL", "name": "Telegram UNOFFICIAL",
"version": "0.0.18", "version": "0.0.19",
"short_name": "Webogram", "short_name": "Webogram",
"manifest_version": 2, "manifest_version": 2,
"app": { "app": {

2
manifest.webapp

@ -1,7 +1,7 @@
{ {
"name": "Webogram", "name": "Webogram",
"description": "Webogram – UNOFFICIAL Telegram Web App.\nMore info & source code here: https://github.com/zhukov/webogram", "description": "Webogram – UNOFFICIAL Telegram Web App.\nMore info & source code here: https://github.com/zhukov/webogram",
"version": "0.0.18", "version": "0.0.19",
"launch_path": "/index.html", "launch_path": "/index.html",
"developer": { "developer": {
"name": "Igor Zhukov", "name": "Igor Zhukov",

30
partials/chat_create_modal.html

@ -0,0 +1,30 @@
<div class="contacts_modal_wrap">
<div class="modal-header">
<a class="modal-close-link" ng-click="$close()">Close</a>
<h4 class="modal-title">Create Chat</h4>
</div>
<div class="modal-body">
<form ng-submit="createGroup()">
<div class="contacts_modal_group_title">
<input class="form-control" my-focused type="text" placeholder="Group name" ng-model="group.name"/>
</div>
<div class="contacts_modal_panel clearfix">
<div class="contacts_modal_actions pull-right">
<button class="btn btn-default" ng-click="$dismiss()"> Cancel </button>
<button class="btn btn-tg" type="submit"> Create </button>
</div>
</div>
</form>
</div>
</div>

7
partials/chat_modal.html

@ -41,14 +41,19 @@
<div class="chat_modal_leave" ng-if="chatFull.chat.left"> <div class="chat_modal_leave" ng-if="chatFull.chat.left">
<a href="" ng-click="returnToGroup()">Return to group</a> <a href="" ng-click="returnToGroup()">Return to group</a>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<h5 class="chat_modal_members_header">Members</h5> <h5 class="chat_modal_members_header">
<a href="" ng-click="inviteToGroup()" ng-if="!chatFull.chat.left &amp;&amp; chatFull.participants.participants.length" class="pull-right">Add participant</a>
Members
</h5>
<div class="chat_modal_members_list"> <div class="chat_modal_members_list">
<div class="chat_modal_participant_wrap clearfix" ng-repeat="participant in chatFull.participants.participants | orderBy:'-user.sortStatus'"> <div class="chat_modal_participant_wrap clearfix" ng-repeat="participant in chatFull.participants.participants | orderBy:'-user.sortStatus'">
<a ng-if="participant.canKick" ng-click="kickFromGroup(participant.user_id)" class="chat_modal_participant_kick pull-right">Kick</a>
<a ng-click="openUser(participant.user_id)" class="chat_modal_participant_photo pull-left"> <a ng-click="openUser(participant.user_id)" class="chat_modal_participant_photo pull-left">
<img <img
class="chat_modal_participant_photo" class="chat_modal_participant_photo"

53
partials/contacts_modal.html

@ -12,25 +12,50 @@
<a class="contacts_modal_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a> <a class="contacts_modal_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a>
</div> </div>
<div class="contacts_modal_members_list">
<div my-contacts-list class="contacts_modal_col">
<div class="contacts_modal_contact_wrap clearfix" ng-repeat="contact in contacts | orderBy:'user.sortName' track by contact.userID">
<a ng-click="openUser(contact.userID)" class="contacts_modal_contact_photo pull-left"> <div class="contacts_wrap nano">
<img <div class="contacts_scrollable_wrap content">
class="contacts_modal_contact_photo"
my-load-thumb <ul class="contacts_modal_members_list nav nav-pills nav-stacked">
thumb="contact.userPhoto"
/> <li class="contacts_modal_contact_wrap clearfix" ng-repeat="contact in contacts | orderBy:'user.sortName' track by contact.userID" ng-class="{active: selectedContacts[contact.userID], disabled: disabledContacts[contact.userID]}">
<i class="icon status_online" ng-show="contact.user.status._ == 'userStatusOnline'"></i> <a class="contacts_modal_contact" ng-click="contactSelect(contact.userID)">
</a>
<div class="contacts_modal_contact_name"> <i ng-if="multiSelect" class="icon icon-contact-tick"></i>
<a ng-click="openUser(contact.user.id)" ng-bind-html="contact.user.rFullName"></a>
<div class="contacts_modal_contact_photo pull-left">
<img
class="contacts_modal_contact_photo"
my-load-thumb
thumb="contact.userPhoto"
/>
<i class="icon status_online" ng-show="contact.user.status._ == 'userStatusOnline'"></i>
</div>
<div class="contacts_modal_contact_name" ng-bind-html="contact.user.rFullName"></div>
<div class="contacts_modal_contact_status">{{contact.user | userStatus}}</div>
</a>
</li>
</ul>
</div> </div>
<div class="contacts_modal_contact_status">{{contact.user | userStatus}}</div>
</div> </div>
</div> </div>
<div ng-if="multiSelect" class="contacts_modal_panel">
<button class="btn btn-tg btn-block" ng-class="{disabled: !selectedCount}" ng-disabled="!selectedCount" ng-click="submitSelected()" type="submit">
Next »
</button>
</div>
</div> </div>
</div> </div>

2
partials/dialog.html

@ -1,4 +1,4 @@
<a class="im_dialog" href="#/im?p={{dialogMessage.peerString}}"> <a class="im_dialog" ng-click="dialogSelect(dialogMessage.peerString)">
<div class="im_dialog_meta pull-right text-right"> <div class="im_dialog_meta pull-right text-right">
<div class="im_dialog_date"> <div class="im_dialog_date">

11
partials/head.html

@ -20,11 +20,20 @@
<div class="navbar-collapse" collapse="navbarCollapsed"> <div class="navbar-collapse" collapse="navbarCollapsed">
<ul class="nav navbar-nav navbar-right">
<ul ng-if="offline" class="nav navbar-nav navbar-offline">
<li ng-show="!offlineConnecting"><span class="navbar-offline-text">Waiting for network<span my-typing-dots></span></span></li>
<li ng-show="!offlineConnecting"><a href="" ng-click="retryOnline()">Retry</a></li>
<li ng-show="offlineConnecting"><span class="navbar-offline-text">Connecting<span my-typing-dots></span></span></li>
</ul>
<ul ng-if="!offline" class="nav navbar-nav navbar-right">
<li ng-if="isLoggedIn"><a href="" ng-click="openGroup()">New Chat</a></li>
<li ng-if="isLoggedIn"><a href="" ng-click="openContacts()">Contacts</a></li> <li ng-if="isLoggedIn"><a href="" ng-click="openContacts()">Contacts</a></li>
<li ng-if="isLoggedIn"><a href="" ng-click="openSettings()">Settings</a></li> <li ng-if="isLoggedIn"><a href="" ng-click="openSettings()">Settings</a></li>
<li><a href="https://github.com/zhukov/webogram" target="_blank">About</a></li> <li><a href="https://github.com/zhukov/webogram" target="_blank">About</a></li>
</ul> </ul>
</div> </div>
</div> </div>
</div> </div>

1
partials/im.html

@ -79,6 +79,7 @@
<div class="im_edit_panel_wrap clearfix" ng-show="selectActions"> <div class="im_edit_panel_wrap clearfix" ng-show="selectActions">
<a class="im_edit_delete_link" ng-click="selectedDelete()"><i class="icon icon-delete"></i></a> <a class="im_edit_delete_link" ng-click="selectedDelete()"><i class="icon icon-delete"></i></a>
<a class="im_edit_forward_link" ng-click="selectedForward()">Forward</a>
<a class="im_edit_cancel_link" ng-click="selectedCancel()">Cancel</a> <a class="im_edit_cancel_link" ng-click="selectedCancel()">Cancel</a>
<h4 class="im_edit_panel_title"> <h4 class="im_edit_panel_title">
<ng-pluralize count="selectedCount" <ng-pluralize count="selectedCount"

28
partials/peer_select.html

@ -0,0 +1,28 @@
<div class="peer_select_modal_wrap">
<div class="modal-header">
<a class="modal-close-link" ng-click="$close()">Close</a>
<h4 class="modal-title">Select conversation</h4>
</div>
<div class="modal-body">
<div class="im_dialogs_modal_col_wrap" ng-controller="AppImDialogsController">
<div class="im_dialogs_search">
<input class="form-control im_dialogs_search_field" type="search" placeholder="Search" ng-model="search.query"/>
<a class="im_dialogs_search_clear" ng-click="search.query = ''" ng-show="search.query.length"></a>
</div>
<div my-dialogs-list modal="true" class="im_dialogs_modal_col">
<div class="im_dialogs_wrap nano">
<div class="im_dialogs_scrollable_wrap content">
<ul class="nav nav-pills nav-stacked">
<li class="im_dialog_wrap" my-dialog dialog-message="dialogMessage" ng-repeat="dialogMessage in dialogs track by dialogMessage.peerID"></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>

28
partials/settings_modal.html

@ -7,7 +7,30 @@
<div class="modal-body"> <div class="modal-body">
<form name="profileForm" class="settings_profile_edit_form clearfix"> <div class="settings_profile_photo_wrap pull-left">
<div class="settings_profile_photo">
<img
class="user_modal_image"
my-load-thumb
thumb="profile.photo"
/>
<div class="settings_profile_photo_change_wrap">
<div ng-if="photo.updating" class="settings_profile_photo_loading">Updating<span my-typing-dots></span></div>
<div ng-if="!photo.updating">
<div class="settings_profile_photo_update_link">
<input my-file-upload type="file" multiple="false" class="im_attach_input" size="120" multiple="false" accept="image/x-png, image/png, image/gif, image/jpeg" />
Update photo
</div>
<a ng-if="profile.photo.location" href="" ng-click="deletePhoto()" class="settings_profile_photo_delete_link">Delete photo</a>
</div>
</div>
</div>
</div>
<form my-settings-form name="profileForm" class="settings_profile_edit_form clearfix">
<div class="form-group settings_profile_first_name" ng-class="{'has-error': error.field == 'first_name'}"> <div class="form-group settings_profile_first_name" ng-class="{'has-error': error.field == 'first_name'}">
<label class="control-label" for="first_name"> <label class="control-label" for="first_name">
<span ng-if="error.field == 'first_name'">Invalid First Name</span> <span ng-if="error.field == 'first_name'">Invalid First Name</span>
@ -27,6 +50,7 @@
<div class="settings_profile_save"> <div class="settings_profile_save">
<button class="btn btn-link settings_profile_save_btn" ng-click="save(profileForm)" ng-disabled="profileForm.$invalid || (!profileForm.last_name.$dirty &amp;&amp; !profileForm.first_name.$dirty)">Save</button> <button class="btn btn-link settings_profile_save_btn" ng-click="save(profileForm)" ng-disabled="profileForm.$invalid || (!profileForm.last_name.$dirty &amp;&amp; !profileForm.first_name.$dirty)">Save</button>
</div> </div>
</form> </form>
<form class="settings_send_choose_form"> <form class="settings_send_choose_form">
@ -61,7 +85,7 @@
<p> <p>
<strong>Version: </strong> <strong>Version: </strong>
<span class="settings_version">alpha 0.0.18</span> <span class="settings_version">alpha 0.0.19</span>
</p> </p>
<hr/> <hr/>

113
vendor/jquery.emojiarea/jquery.emojiarea.js vendored

@ -29,8 +29,14 @@
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
/*! MODIFICATION START
Options 'spritesheetPath', 'spritesheetDimens', 'iconSize' added by Andre Staltz.
*/
$.emojiarea = { $.emojiarea = {
path: '', path: '',
spritesheetPath: '',
spritesheetDimens: [],
iconSize: 20,
icons: {}, icons: {},
defaults: { defaults: {
button: null, button: null,
@ -38,6 +44,7 @@
buttonPosition: 'after' buttonPosition: 'after'
} }
}; };
/*! MODIFICATION END */
$.fn.emojiarea = function(options) { $.fn.emojiarea = function(options) {
options = $.extend({}, $.emojiarea.defaults, options); options = $.extend({}, $.emojiarea.defaults, options);
@ -198,14 +205,28 @@
this.$button = $button; this.$button = $button;
}; };
/*! MODIFICATION START
This function was modified by Andre Staltz so that the icon is created from a spritesheet.
*/
EmojiArea.createIcon = function(emoji) { EmojiArea.createIcon = function(emoji) {
var filename = $.emojiarea.icons[emoji]; var category = emoji[0];
var path = $.emojiarea.path || ''; var row = emoji[1];
if (path.length && path.charAt(path.length - 1) !== '/') { var column = emoji[2];
path += '/'; var name = emoji[3];
} var filename = $.emojiarea.spritesheetPath;
return '<img src="' + path + filename + '" alt="' + util.htmlEntities(emoji) + '">'; var xoffset = -($.emojiarea.iconSize * column);
var yoffset = -($.emojiarea.iconSize * row);
var scaledWidth = ($.emojiarea.spritesheetDimens[category][1] * $.emojiarea.iconSize);
var scaledHeight = ($.emojiarea.spritesheetDimens[category][0] * $.emojiarea.iconSize);
var style = 'display:inline-block;';
style += 'width:' + $.emojiarea.iconSize + 'px;';
style += 'height:' + $.emojiarea.iconSize + 'px;';
style += 'background:url(\'' + filename.replace('!',category) + '\') ' + xoffset + 'px ' + yoffset + 'px no-repeat;';
style += 'background-size:' + scaledWidth + 'px ' + scaledHeight + 'px;';
return '<img src="img/blank.gif" class="img" style="'+ style +'" alt="' + util.htmlEntities(name) + '">';
}; };
/*! MODIFICATION END */
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -272,7 +293,8 @@
var emojis = $.emojiarea.icons; var emojis = $.emojiarea.icons;
for (var key in emojis) { for (var key in emojis) {
if (emojis.hasOwnProperty(key)) { if (emojis.hasOwnProperty(key)) {
html = html.replace(new RegExp(util.escapeRegex(key), 'g'), EmojiArea.createIcon(key)); /* MODIFICATION: Following line was modified by Andre Staltz, to use new implementation of createIcon function.*/
html = html.replace(new RegExp(util.escapeRegex(key), 'g'), EmojiArea.createIcon(emojis[key]));
} }
} }
this.$editor.html(html); this.$editor.html(html);
@ -293,17 +315,17 @@
Following code was modified by Igor Zhukov, in order to improve rich text paste Following code was modified by Igor Zhukov, in order to improve rich text paste
*/ */
EmojiArea_WYSIWYG.prototype.onPaste = function(e) { EmojiArea_WYSIWYG.prototype.onPaste = function(e) {
var text = (e.originalEvent || e).clipboardData.getData('text/plain'), var text = (e.originalEvent || e).clipboardData.getData('text/plain'),
self = this; self = this;
setTimeout(function () { setTimeout(function () {
self.onChange(); self.onChange();
}, 0); }, 0);
if (text.length) { if (text.length) {
document.execCommand('insertText', false, text); document.execCommand('insertText', false, text);
return cancelEvent(e); return cancelEvent(e);
} }
return true; return true;
} };
/*! MODIFICATION END */ /*! MODIFICATION END */
EmojiArea_WYSIWYG.prototype.onChange = function(e) { EmojiArea_WYSIWYG.prototype.onChange = function(e) {
@ -312,7 +334,8 @@
EmojiArea_WYSIWYG.prototype.insert = function(emoji) { EmojiArea_WYSIWYG.prototype.insert = function(emoji) {
var content; var content;
var $img = $(EmojiArea.createIcon(emoji)); /* MODIFICATION: Following line was modified by Andre Staltz, to use new implementation of createIcon function.*/
var $img = $(EmojiArea.createIcon($.emojiarea.icons[emoji]));
if ($img[0].attachEvent) { if ($img[0].attachEvent) {
$img[0].attachEvent('onresizestart', function(e) { e.returnValue = false; }, false); $img[0].attachEvent('onresizestart', function(e) { e.returnValue = false; }, false);
} }
@ -403,8 +426,16 @@
/*! MODIFICATION START /*! MODIFICATION START
Following code was modified by Igor Zhukov, in order to add scrollbars and tail to EmojiMenu Following code was modified by Igor Zhukov, in order to add scrollbars and tail to EmojiMenu
Also modified by Andre Staltz, to include tabs for categories, on the menu header.
*/ */
this.$itemsTailWrap = $('<div class="emoji-items-wrap1"></div>').appendTo(this.$menu); this.$itemsTailWrap = $('<div class="emoji-items-wrap1"></div>').appendTo(this.$menu);
this.$categoryTabs = $('<table class="emoji-menu-tabs"><tr>' +
'<td><a class="emoji-menu-tab icon-smile" ></a></td>' +
'<td><a class="emoji-menu-tab icon-flower"></a></td>' +
'<td><a class="emoji-menu-tab icon-bell"></a></td>' +
'<td><a class="emoji-menu-tab icon-car"></a></td>' +
'<td><a class="emoji-menu-tab icon-grid"></a></td>' +
'</tr></table>').appendTo(this.$itemsTailWrap);
this.$itemsWrap = $('<div class="emoji-items-wrap nano"></div>').appendTo(this.$itemsTailWrap); this.$itemsWrap = $('<div class="emoji-items-wrap nano"></div>').appendTo(this.$itemsTailWrap);
this.$items = $('<div class="emoji-items content">').appendTo(this.$itemsWrap); this.$items = $('<div class="emoji-items content">').appendTo(this.$itemsWrap);
$('<div class="emoji-menu-tail">').appendTo(this.$menu); $('<div class="emoji-menu-tail">').appendTo(this.$menu);
@ -447,6 +478,16 @@
}); });
this.$menu.on('click', 'a', function(e) { this.$menu.on('click', 'a', function(e) {
/*! MODIFICATION START
Following code was modified by Andre Staltz, to capture clicks on category tabs and change the category selection.
*/
if ($(this).hasClass('emoji-menu-tab')) {
if (self.getTabIndex(this) !== self.currentCategory) {
self.selectCategory(self.getTabIndex(this));
}
return false;
}
/*! MODIFICATION END */
var emoji = $('.label', $(this)).text(); var emoji = $('.label', $(this)).text();
window.setTimeout(function() { window.setTimeout(function() {
self.onItemSelected(emoji); self.onItemSelected(emoji);
@ -462,14 +503,38 @@
return false; return false;
}); });
this.load(); /* MODIFICATION: Following line was modified by Andre Staltz, in order to select a default category. */
this.selectCategory(0);
}; };
/*! MODIFICATION START
Following code was added by Andre Staltz, to implement category selection.
*/
EmojiMenu.prototype.getTabIndex = function(tab) {
return this.$categoryTabs.find('.emoji-menu-tab').index(tab);
};
EmojiMenu.prototype.selectCategory = function(category) {
var self = this;
this.$categoryTabs.find('.emoji-menu-tab').each(function(index) {
if (index === category) {
this.className += '-selected';
}
else {
this.className = this.className.replace('-selected', '');
}
});
this.currentCategory = category;
this.load(category);
};
/*! MODIFICATION END */
EmojiMenu.prototype.onItemSelected = function(emoji) { EmojiMenu.prototype.onItemSelected = function(emoji) {
this.emojiarea.insert(emoji); this.emojiarea.insert(emoji);
}; };
EmojiMenu.prototype.load = function() { /* MODIFICATION: The following function argument was modified by Andre Staltz, in order to load only icons from a category. */
EmojiMenu.prototype.load = function(category) {
var html = []; var html = [];
var options = $.emojiarea.icons; var options = $.emojiarea.icons;
var path = $.emojiarea.path; var path = $.emojiarea.path;
@ -478,9 +543,9 @@
} }
for (var key in options) { for (var key in options) {
if (options.hasOwnProperty(key)) { /* MODIFICATION: The following 2 lines were modified by Andre Staltz, in order to load only icons from the specified category. */
var filename = options[key]; if (options.hasOwnProperty(key) && options[key][0] === category) {
html.push('<a href="javascript:void(0)" title="' + util.htmlEntities(key) + '">' + EmojiArea.createIcon(key) + '<span class="label">' + util.htmlEntities(key) + '</span></a>'); html.push('<a href="javascript:void(0)" title="' + util.htmlEntities(key) + '">' + EmojiArea.createIcon(options[key]) + '<span class="label">' + util.htmlEntities(key) + '</span></a>');
} }
} }

Loading…
Cancel
Save