Browse Source

Standard.js formatting

Adding linter for JS code
For now it doesn’t pass well, but that’s a start
master
Igor Zhukov 9 years ago
parent
commit
636bc4a83b
  1. 2
      README.md
  2. 30
      app/js/app.js
  3. 4
      app/js/background.js
  4. 4371
      app/js/controllers.js
  5. 3041
      app/js/directives.js
  6. 386
      app/js/directives_mobile.js
  7. 172
      app/js/filters.js
  8. 97
      app/js/init.js
  9. 562
      app/js/lib/bin_utils.js
  10. 194
      app/js/lib/config.js
  11. 30
      app/js/lib/crypto_worker.js
  12. 106
      app/js/lib/i18n.js
  13. 1476
      app/js/lib/mtproto.js
  14. 725
      app/js/lib/mtproto_wrapper.js
  15. 1647
      app/js/lib/ng_utils.js
  16. 125
      app/js/lib/polyfill.js
  17. 634
      app/js/lib/tl_utils.js
  18. 436
      app/js/lib/utils.js
  19. 1620
      app/js/message_composer.js
  20. 2477
      app/js/messages_manager.js
  21. 76
      app/js/offline-manager.js
  22. 3500
      app/js/services.js
  23. 168
      gulpfile.js
  24. 8
      package.json
  25. 255
      server.js

2
README.md

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)
[![Stories in Ready](https://badge.waffle.io/zhukov/webogram.png?label=ready&title=Ready)](https://waffle.io/zhukov/webogram)
## [Webogram](https://web.telegram.org) — Telegram Web App
Telegram offers great [apps for mobile communication](https://www.telegram.org). It is based on the [MTProto protocol](https://core.telegram.org/mtproto) and has an [Open API](https://core.telegram.org/api). I personally like Telegram for its speed and cloud-support (that makes a web app possible, unlike in the case of WA and others).

30
app/js/app.js

@ -5,14 +5,13 @@ @@ -5,14 +5,13 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
'use strict';
'use strict'
var extraModules = [];
var extraModules = []
if (Config.Modes.animations) {
extraModules.push('ngAnimate');
extraModules.push('ngAnimate')
}
// Declare app level module which depends on filters, and services
angular.module('myApp', [
'ngRoute',
@ -31,23 +30,20 @@ angular.module('myApp', [ @@ -31,23 +30,20 @@ angular.module('myApp', [
PRODUCTION_ONLY_END*/
'myApp.directives',
'myApp.controllers'
].concat(extraModules)).
config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) {
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//);
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|tg|mailto|blob|filesystem|chrome-extension|app):|data:/);
].concat(extraModules)).config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function ($locationProvider, $routeProvider, $compileProvider, StorageProvider) {
$compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//)
$compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|tg|mailto|blob|filesystem|chrome-extension|app):|data:/)
/*PRODUCTION_ONLY_BEGIN
$compileProvider.debugInfoEnabled(false);
$compileProvider.debugInfoEnabled(false)
PRODUCTION_ONLY_END*/
if (Config.Modes.test) {
StorageProvider.setPrefix('t_');
StorageProvider.setPrefix('t_')
}
$routeProvider.when('/', {templateUrl: templateUrl('welcome'), controller: 'AppWelcomeController'});
$routeProvider.when('/login', {templateUrl: templateUrl('login'), controller: 'AppLoginController'});
$routeProvider.when('/im', {templateUrl: templateUrl('im'), controller: 'AppIMController', reloadOnSearch: false});
$routeProvider.otherwise({redirectTo: '/'});
}]);
$routeProvider.when('/', {templateUrl: templateUrl('welcome'), controller: 'AppWelcomeController'})
$routeProvider.when('/login', {templateUrl: templateUrl('login'), controller: 'AppLoginController'})
$routeProvider.when('/im', {templateUrl: templateUrl('im'), controller: 'AppIMController', reloadOnSearch: false})
$routeProvider.otherwise({redirectTo: '/'})
}])

4
app/js/background.js

@ -15,5 +15,5 @@ chrome.app.runtime.onLaunched.addListener(function(launchData) { @@ -15,5 +15,5 @@ chrome.app.runtime.onLaunched.addListener(function(launchData) {
minWidth: 320,
minHeight: 400,
frame: 'chrome'
});
});
})
})

4371
app/js/controllers.js

File diff suppressed because it is too large Load Diff

3041
app/js/directives.js

File diff suppressed because it is too large Load Diff

386
app/js/directives_mobile.js

@ -5,359 +5,349 @@ @@ -5,359 +5,349 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
'use strict';
'use strict'
/* Directives */
angular.module('myApp.directives')
.directive('myDialogsListMobile', function ($window, $timeout) {
return {
link: link
};
}
function link ($scope, element, attrs) {
var dialogsColWrap = $('.im_dialogs_col_wrap')[0],
scrollableWrap = element[0],
headWrap = $('.tg_page_head')[0],
panelWrapSelector = attrs.modal
var dialogsColWrap = $('.im_dialogs_col_wrap')[0]
var scrollableWrap = element[0]
var headWrap = $('.tg_page_head')[0]
var panelWrapSelector = attrs.modal
? '.mobile_modal_body .im_dialogs_panel'
: '.im_dialogs_panel',
panelWrap = $(panelWrapSelector)[0],
moreNotified = false;
$scope.$on('ui_dialogs_search', updateSizes);
$scope.$on('ui_dialogs_update', updateSizes);
: '.im_dialogs_panel'
var panelWrap = $(panelWrapSelector)[0]
var moreNotified = false
$scope.$on('ui_dialogs_search', updateSizes)
$scope.$on('ui_dialogs_update', updateSizes)
$scope.$on('ui_dialogs_append', function () {
onContentLoaded(function () {
moreNotified = false;
moreNotified = false
$timeout(function () {
$(scrollableWrap).trigger('scroll');
});
});
});
$(scrollableWrap).trigger('scroll')
})
})
})
$scope.$on('ui_dialogs_change', function () {
onContentLoaded(function () {
moreNotified = false;
moreNotified = false
$timeout(function () {
$(scrollableWrap).trigger('scroll');
});
});
});
$(scrollableWrap).trigger('scroll')
})
})
})
$(scrollableWrap).on('scroll', function (e) {
if (!element.is(':visible')) return;
if (!element.is(':visible')) return
if (!moreNotified && scrollableWrap.scrollTop >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight - 300) {
$scope.$emit('dialogs_need_more');
moreNotified = true;
$scope.$emit('dialogs_need_more')
moreNotified = true
}
});
})
function updateSizes () {
if (!panelWrap || !panelWrap.offsetHeight) {
panelWrap = $(panelWrapSelector)[0];
panelWrap = $(panelWrapSelector)[0]
}
if (attrs.modal) {
$(element).css({
height: $($window).height() -
(panelWrap ? panelWrap.offsetHeight : 58) - 46
});
return;
})
return
}
if (!headWrap || !headWrap.offsetHeight) {
headWrap = $('.tg_page_head')[0];
headWrap = $('.tg_page_head')[0]
}
if (!dialogsColWrap || !dialogsColWrap.offsetHeight) {
dialogsColWrap = $('.im_dialogs_col_wrap')[0];
dialogsColWrap = $('.im_dialogs_col_wrap')[0]
}
$(element).css({
height: $($window).height() -
(headWrap ? headWrap.offsetHeight : 46) -
(panelWrap ? panelWrap.offsetHeight : 58) -
parseInt($(dialogsColWrap).css('paddingBottom') || 0)
});
})
}
$($window).on('resize', updateSizes);
updateSizes();
setTimeout(updateSizes, 1000);
};
$($window).on('resize', updateSizes)
updateSizes()
setTimeout(updateSizes, 1000)
}
})
.directive('myHistoryMobile', function ($window, $timeout, $rootScope, $transition) {
return {
link: link
};
}
function link ($scope, element, attrs) {
var historyWrap = $('.im_history_wrap', element)[0],
historyMessagesEl = $('.im_history_messages', element)[0],
scrollableWrap = $('.im_history_scrollable_wrap', element)[0],
scrollable = $('.im_history_scrollable', element)[0],
bottomPanelWrap = $('.im_bottom_panel_wrap', element)[0],
sendFormWrap = $('.im_send_form_wrap', element)[0],
headWrap = $('.tg_page_head')[0],
sendForm = $('.im_send_form', element)[0],
moreNotified = false,
lessNotified = false;
var historyWrap = $('.im_history_wrap', element)[0]
var historyMessagesEl = $('.im_history_messages', element)[0]
var scrollableWrap = $('.im_history_scrollable_wrap', element)[0]
var scrollable = $('.im_history_scrollable', element)[0]
var bottomPanelWrap = $('.im_bottom_panel_wrap', element)[0]
var sendFormWrap = $('.im_send_form_wrap', element)[0]
var headWrap = $('.tg_page_head')[0]
var sendForm = $('.im_send_form', element)[0]
var moreNotified = false
var lessNotified = false
onContentLoaded(function () {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
});
scrollableWrap.scrollTop = scrollableWrap.scrollHeight
})
$scope.$on('ui_history_append_new', function (e, options) {
if (!atBottom && !options.my) {
return;
return
}
var pr = parseInt($(scrollableWrap).css('paddingRight'))
$(scrollableWrap).addClass('im_history_to_bottom');
$(scrollable).css({bottom: 0, marginLeft: -Math.ceil(pr / 2)});
$(scrollableWrap).addClass('im_history_to_bottom')
$(scrollable).css({bottom: 0, marginLeft: -Math.ceil(pr / 2)})
onContentLoaded(function () {
$(scrollableWrap).removeClass('im_history_to_bottom');
$(scrollable).css({bottom: '', marginLeft: ''});
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
updateBottomizer();
});
});
$(scrollableWrap).removeClass('im_history_to_bottom')
$(scrollable).css({bottom: '', marginLeft: ''})
scrollableWrap.scrollTop = scrollableWrap.scrollHeight
updateBottomizer()
})
})
function changeScroll () {
var unreadSplit, focusMessage;
var unreadSplit
var focusMessage
// console.trace('change scroll');
// console.trace('change scroll')
if (focusMessage = $('.im_message_focus:visible', scrollableWrap)[0]) {
var ch = scrollableWrap.clientHeight,
st = scrollableWrap.scrollTop,
ot = focusMessage.offsetTop,
h = focusMessage.clientHeight;
var ch = scrollableWrap.clientHeight
var st = scrollableWrap.scrollTop
var ot = focusMessage.offsetTop
var h = focusMessage.clientHeight
if (!st || st + ch < ot || st > ot + h) {
scrollableWrap.scrollTop = Math.max(0, ot - Math.floor(ch / 2) + 26);
scrollableWrap.scrollTop = Math.max(0, ot - Math.floor(ch / 2) + 26)
}
atBottom = false;
atBottom = false
} else if (unreadSplit = $('.im_message_unread_split:visible', scrollableWrap)[0]) {
// console.log('change scroll unread', unreadSplit.offsetTop);
scrollableWrap.scrollTop = Math.max(0, unreadSplit.offsetTop - 52);
atBottom = false;
// console.log('change scroll unread', unreadSplit.offsetTop)
scrollableWrap.scrollTop = Math.max(0, unreadSplit.offsetTop - 52)
atBottom = false
} else {
// console.log('change scroll bottom');
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
atBottom = true;
// console.log('change scroll bottom')
scrollableWrap.scrollTop = scrollableWrap.scrollHeight
atBottom = true
}
$timeout(function () {
$(scrollableWrap).trigger('scroll');
scrollTopInitial = scrollableWrap.scrollTop;
});
};
$(scrollableWrap).trigger('scroll')
scrollTopInitial = scrollableWrap.scrollTop
})
}
$scope.$on('ui_history_change', function () {
var pr = parseInt($(scrollableWrap).css('paddingRight'))
$(scrollableWrap).addClass('im_history_to_bottom');
$(scrollable).css({bottom: 0, marginLeft: -Math.ceil(pr / 2)});
$(scrollableWrap).addClass('im_history_to_bottom')
$(scrollable).css({bottom: 0, marginLeft: -Math.ceil(pr / 2)})
onContentLoaded(function () {
$(scrollableWrap).removeClass('im_history_to_bottom');
$(scrollable).css({bottom: '', marginLeft: ''});
updateSizes(true);
moreNotified = false;
lessNotified = false;
changeScroll();
});
});
$(scrollableWrap).removeClass('im_history_to_bottom')
$(scrollable).css({bottom: '', marginLeft: ''})
updateSizes(true)
moreNotified = false
lessNotified = false
changeScroll()
})
})
$scope.$on('ui_history_change_scroll', function () {
onContentLoaded(changeScroll)
});
})
$scope.$on('ui_history_focus', function () {
if (!atBottom) {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
atBottom = true;
scrollableWrap.scrollTop = scrollableWrap.scrollHeight
atBottom = true
}
});
})
$scope.$on('ui_history_prepend', function () {
var sh = scrollableWrap.scrollHeight,
st = scrollableWrap.scrollTop,
pr = parseInt($(scrollableWrap).css('paddingRight')),
ch = scrollableWrap.clientHeight;
$(scrollableWrap).addClass('im_history_to_bottom');
scrollableWrap.scrollHeight; // Some strange Chrome bug workaround
$(scrollable).css({bottom: -(sh - st - ch), marginLeft: -Math.ceil(pr / 2)});
var sh = scrollableWrap.scrollHeight
var st = scrollableWrap.scrollTop
var pr = parseInt($(scrollableWrap).css('paddingRight'))
var ch = scrollableWrap.clientHeight
$(scrollableWrap).addClass('im_history_to_bottom')
scrollableWrap.scrollHeight // Some strange Chrome bug workaround
$(scrollable).css({bottom: -(sh - st - ch), marginLeft: -Math.ceil(pr / 2)})
var upd = function () {
$(scrollableWrap).removeClass('im_history_to_bottom');
$(scrollable).css({bottom: '', marginLeft: ''});
$(scrollableWrap).removeClass('im_history_to_bottom')
$(scrollable).css({bottom: '', marginLeft: ''})
if (scrollTopInitial >= 0) {
changeScroll();
changeScroll()
} else {
scrollableWrap.scrollTop = st + scrollableWrap.scrollHeight - sh;
scrollableWrap.scrollTop = st + scrollableWrap.scrollHeight - sh
}
updateBottomizer();
moreNotified = false;
updateBottomizer()
moreNotified = false
$timeout(function () {
if (scrollableWrap.scrollHeight != sh) {
$(scrollableWrap).trigger('scroll');
$(scrollableWrap).trigger('scroll')
}
});
})
clearTimeout(timer);
unreg();
clearTimeout(timer)
unreg()
},
timer = setTimeout(upd, 0),
unreg = $scope.$on('$viewContentLoaded', upd);
});
unreg = $scope.$on('$viewContentLoaded', upd)
})
$scope.$on('ui_history_append', function () {
var sh = scrollableWrap.scrollHeight;
var sh = scrollableWrap.scrollHeight
onContentLoaded(function () {
atBottom = false;
updateBottomizer();
lessNotified = false;
atBottom = false
updateBottomizer()
lessNotified = false
if (scrollTopInitial >= 0) {
changeScroll();
changeScroll()
}
$timeout(function () {
if (scrollableWrap.scrollHeight != sh) {
$(scrollableWrap).trigger('scroll');
$(scrollableWrap).trigger('scroll')
}
});
});
});
})
})
})
$scope.$on('ui_panel_update', function (e, data) {
onContentLoaded(function () {
updateSizes();
updateSizes()
if (data && data.blur) {
$scope.$broadcast('ui_message_blur');
$scope.$broadcast('ui_message_blur')
} else {
$scope.$broadcast('ui_message_send');
$scope.$broadcast('ui_message_send')
}
$timeout(function () {
$(scrollableWrap).trigger('scroll');
});
});
});
$(scrollableWrap).trigger('scroll')
})
})
})
$scope.$on('ui_selection_clear', function () {
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
window.getSelection().empty();
window.getSelection().empty()
} else if (window.getSelection().removeAllRanges) { // Firefox
window.getSelection().removeAllRanges();
window.getSelection().removeAllRanges()
}
} else if (document.selection) { // IE?
document.selection.empty();
document.selection.empty()
}
});
})
$scope.$on('ui_editor_resize', updateSizes);
$scope.$on('ui_editor_resize', updateSizes)
$scope.$on('ui_height', function () {
onContentLoaded(updateSizes);
});
onContentLoaded(updateSizes)
})
var atBottom = true,
scrollTopInitial = -1;
var atBottom = true
var scrollTopInitial = -1
$(scrollableWrap).on('scroll', function (e) {
if (!element.is(':visible') ||
$(scrollableWrap).hasClass('im_history_to_bottom')) {
return;
return
}
var st = scrollableWrap.scrollTop;
atBottom = st >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight;
var st = scrollableWrap.scrollTop
atBottom = st >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight
if (scrollTopInitial >= 0 && scrollTopInitial != st) {
scrollTopInitial = -1;
scrollTopInitial = -1
}
if (!moreNotified && st <= 300) {
moreNotified = true;
$scope.$emit('history_need_more');
moreNotified = true
$scope.$emit('history_need_more')
}
else if (!lessNotified && st >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight - 300) {
lessNotified = true;
$scope.$emit('history_need_less');
lessNotified = true
$scope.$emit('history_need_less')
}
});
})
function updateSizes (heightOnly) {
if (!element.is(':visible') && !$(element[0].parentNode.parentNode).is(':visible')) {
return;
return
}
if ($(sendFormWrap).is(':visible')) {
$(sendFormWrap).css({
height: $(sendForm).height()
});
})
}
if (!headWrap || !headWrap.offsetHeight) {
headWrap = $('.tg_page_head')[0];
headWrap = $('.tg_page_head')[0]
}
var historyH = $($window).height() - bottomPanelWrap.offsetHeight - (headWrap ? headWrap.offsetHeight : 46);
var historyH = $($window).height() - bottomPanelWrap.offsetHeight - (headWrap ? headWrap.offsetHeight : 46)
$(historyWrap).css({
height: historyH
});
updateBottomizer();
})
updateBottomizer()
if (heightOnly === true) return;
if (heightOnly === true) return
if (atBottom) {
onContentLoaded(function () {
scrollableWrap.scrollTop = scrollableWrap.scrollHeight;
});
scrollableWrap.scrollTop = scrollableWrap.scrollHeight
})
}
}
function updateBottomizer () {
return;
$(historyMessagesEl).css({marginTop: 0});
return
$(historyMessagesEl).css({marginTop: 0})
var marginTop = scrollableWrap.offsetHeight
- historyMessagesEl.offsetHeight
- 20;
- 20
if (historyMessagesEl.offsetHeight > 0 && marginTop > 0) {
$(historyMessagesEl).css({marginTop: marginTop});
$(historyMessagesEl).css({marginTop: marginTop})
}
}
$($window).on('resize', updateSizes);
$($window).on('resize', updateSizes)
updateSizes();
onContentLoaded(updateSizes);
updateSizes()
onContentLoaded(updateSizes)
}
})
.directive('myContactsListMobile', function ($window, $timeout) {
return {
link: link
};
}
function link ($scope, element, attrs) {
var searchWrap = $('.contacts_modal_search')[0],
panelWrap = $('.contacts_modal_panel')[0];
var searchWrap = $('.contacts_modal_search')[0]
var panelWrap = $('.contacts_modal_panel')[0]
function updateSizes () {
$(element).css({
@ -365,27 +355,25 @@ angular.module('myApp.directives') @@ -365,27 +355,25 @@ angular.module('myApp.directives')
(panelWrap && panelWrap.offsetHeight || 0) -
(searchWrap && searchWrap.offsetHeight || 0) -
64
});
})
}
$($window).on('resize', updateSizes);
$($window).on('resize', updateSizes)
$scope.$on('contacts_change', function () {
onContentLoaded(updateSizes)
});
onContentLoaded(updateSizes);
};
})
onContentLoaded(updateSizes)
}
})
.directive('myCountriesListMobile', function ($window, $timeout) {
return {
link: link
};
}
function link ($scope, element, attrs) {
var searchWrap = $('.countries_modal_search')[0],
panelWrap = $('.countries_modal_panel')[0];
var searchWrap = $('.countries_modal_search')[0]
var panelWrap = $('.countries_modal_panel')[0]
function updateSizes () {
$(element).css({
@ -393,41 +381,37 @@ angular.module('myApp.directives') @@ -393,41 +381,37 @@ angular.module('myApp.directives')
- (panelWrap && panelWrap.offsetHeight || 0)
- (searchWrap && searchWrap.offsetHeight || 0)
- (46 + 18)
});
})
}
$($window).on('resize', updateSizes);
onContentLoaded(updateSizes);
};
$($window).on('resize', updateSizes)
onContentLoaded(updateSizes)
}
})
.directive('myInfiniteScrollerMobile', function () {
return {
link: link,
scope: true
};
}
function link ($scope, element, attrs) {
var scrollableWrap = element[0],
moreNotified = false;
var scrollableWrap = element[0]
var moreNotified = false
$(scrollableWrap).on('scroll', function (e) {
if (!element.is(':visible')) return;
if (!element.is(':visible')) return
if (!moreNotified &&
scrollableWrap.scrollTop >= scrollableWrap.scrollHeight - scrollableWrap.clientHeight - 300) {
moreNotified = true;
moreNotified = true
$scope.$apply(function () {
$scope.slice.limit += ($scope.slice.limitDelta || 20);
});
$scope.slice.limit += ($scope.slice.limitDelta || 20)
})
onContentLoaded(function () {
moreNotified = false;
});
moreNotified = false
})
}
})
}
});
};
})

172
app/js/filters.js

@ -5,7 +5,7 @@ @@ -5,7 +5,7 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
'use strict';
'use strict'
/* Filters */
@ -14,63 +14,63 @@ angular.module('myApp.filters', ['myApp.i18n']) @@ -14,63 +14,63 @@ angular.module('myApp.filters', ['myApp.i18n'])
.filter('userName', function (_) {
return function (user) {
if (!user || !user.first_name && !user.last_name) {
return _('user_name_deleted');
return _('user_name_deleted')
}
return user.first_name + (user.last_name ? ' ' + user.last_name : '');
return user.first_name + (user.last_name ? ' ' + user.last_name : '')
}
})
.filter('userFirstName', function (_) {
return function (user) {
if (!user || !user.first_name && !user.last_name) {
return _('user_first_name_deleted');
return _('user_first_name_deleted')
}
return user.first_name || user.last_name;
return user.first_name || user.last_name
}
})
.filter('userStatus', function ($filter, _) {
var relativeTimeFilter = $filter('relativeTime');
var relativeTimeFilter = $filter('relativeTime')
return function (user, botChatPrivacy) {
if (!(user.id % 1000)) {
if (user.id == 777000) {
return _('user_status_service_notifications');
return _('user_status_service_notifications')
}
return _('user_status_support');
return _('user_status_support')
}
var statusType = user && user.status && user.status._;
var statusType = user && user.status && user.status._
if (!statusType) {
statusType = user && user.pFlags && user.pFlags.bot ? 'userStatusBot' : 'userStatusEmpty';
statusType = user && user.pFlags && user.pFlags.bot ? 'userStatusBot' : 'userStatusEmpty'
}
switch (statusType) {
case 'userStatusOnline':
return _('user_status_online');
return _('user_status_online')
case 'userStatusOffline':
return _('user_status_last_seen', relativeTimeFilter(user.status.was_online));
return _('user_status_last_seen', relativeTimeFilter(user.status.was_online))
case 'userStatusRecently':
return _('user_status_recently');
return _('user_status_recently')
case 'userStatusLastWeek':
return _('user_status_last_week');
return _('user_status_last_week')
case 'userStatusLastMonth':
return _('user_status_last_month');
return _('user_status_last_month')
case 'userStatusBot':
if (botChatPrivacy) {
if (user.pFlags.bot_chat_history) {
return _('user_status_bot_noprivacy');
return _('user_status_bot_noprivacy')
} else {
return _('user_status_bot_privacy');
return _('user_status_bot_privacy')
}
}
return _('user_status_bot');
return _('user_status_bot')
case 'userStatusEmpty':
default:
return _('user_status_long_ago');
return _('user_status_long_ago')
}
}
})
@ -78,194 +78,194 @@ angular.module('myApp.filters', ['myApp.i18n']) @@ -78,194 +78,194 @@ angular.module('myApp.filters', ['myApp.i18n'])
.filter('chatTitle', function (_) {
return function (chat) {
if (!chat || !chat.title) {
return _('chat_title_deleted');
return _('chat_title_deleted')
}
return chat.title;
return chat.title
}
})
.filter('dateOrTime', function ($filter) {
var dateFilter = $filter('date');
var dateFilter = $filter('date')
return function (timestamp, extended) {
if (!timestamp) {
return '';
return ''
}
var ticks = timestamp * 1000,
diff = Math.abs(tsNow() - ticks),
format = 'shortTime';
var ticks = timestamp * 1000
var diff = Math.abs(tsNow() - ticks)
var format = 'shortTime'
if (diff > 518400000) { // 6 days
format = extended ? 'mediumDate' : 'shortDate';
format = extended ? 'mediumDate' : 'shortDate'
}
else if (diff > 43200000) { // 12 hours
format = extended ? 'EEEE' : 'EEE';
format = extended ? 'EEEE' : 'EEE'
}
return dateFilter(ticks, format);
return dateFilter(ticks, format)
}
})
.filter('time', function ($filter) {
var cachedDates = {},
dateFilter = $filter('date'),
format = Config.Mobile ? 'shortTime' : 'mediumTime';
var cachedDates = {}
var dateFilter = $filter('date')
var format = Config.Mobile ? 'shortTime' : 'mediumTime'
return function (timestamp) {
if (cachedDates[timestamp]) {
return cachedDates[timestamp];
return cachedDates[timestamp]
}
return cachedDates[timestamp] = dateFilter(timestamp * 1000, format);
return cachedDates[timestamp] = dateFilter(timestamp * 1000, format)
}
})
.filter('myDate', function ($filter) {
var cachedDates = {},
dateFilter = $filter('date');
var cachedDates = {}
var dateFilter = $filter('date')
return function (timestamp) {
if (cachedDates[timestamp]) {
return cachedDates[timestamp];
return cachedDates[timestamp]
}
return cachedDates[timestamp] = dateFilter(timestamp * 1000, 'fullDate');
return cachedDates[timestamp] = dateFilter(timestamp * 1000, 'fullDate')
}
})
.filter('duration', [function () {
return function (duration) {
duration = parseInt(duration);
duration = parseInt(duration)
if (isNaN(duration)) {
duration = 0;
duration = 0
}
var secs = duration % 60,
mins = Math.floor((duration - secs) / 60.0);
var secs = duration % 60
var mins = Math.floor((duration - secs) / 60.0)
if (secs < 10) {
secs = '0' + secs;
secs = '0' + secs
}
return mins + ':' + secs;
return mins + ':' + secs
}
}])
.filter('durationRemains', function ($filter) {
var durationFilter = $filter('duration');
var durationFilter = $filter('duration')
return function (done, total) {
return '-' + durationFilter(total - done);
return '-' + durationFilter(total - done)
}
})
.filter('phoneNumber', [function () {
return function (phoneRaw) {
var nbsp = ' ';
phoneRaw = (phoneRaw || '').replace(/\D/g, '');
var nbsp = ' '
phoneRaw = (phoneRaw || '').replace(/\D/g, '')
if (phoneRaw.charAt(0) == '7' && phoneRaw.length == 11) {
return '+' + phoneRaw.charAt(0) + nbsp + '(' + phoneRaw.substr(1, 3) + ')' + nbsp + phoneRaw.substr(4, 3) + '-' + phoneRaw.substr(7, 2) + '-' + phoneRaw.substr(9, 2);
return '+' + phoneRaw.charAt(0) + nbsp + '(' + phoneRaw.substr(1, 3) + ')' + nbsp + phoneRaw.substr(4, 3) + '-' + phoneRaw.substr(7, 2) + '-' + phoneRaw.substr(9, 2)
}
return '+' + phoneRaw;
return '+' + phoneRaw
}
}])
.filter('formatSize', [function () {
return function (size, progressing) {
if (!size) {
return '0';
return '0'
}
else if (size < 1024) {
return size + ' b';
return size + ' b'
}
else if (size < 1048576) {
return Math.round(size / 1024) + ' KB';
return Math.round(size / 1024) + ' KB'
}
var mbs = size / 1048576;
var mbs = size / 1048576
if (progressing) {
mbs = mbs.toFixed(1);
mbs = mbs.toFixed(1)
} else {
mbs = (Math.round(mbs * 10) / 10);
mbs = (Math.round(mbs * 10) / 10)
}
return mbs + ' MB';
return mbs + ' MB'
}
}])
.filter('formatSizeProgress', function ($filter, _) {
var formatSizeFilter = $filter('formatSize');
var formatSizeFilter = $filter('formatSize')
return function (progress) {
if (!progress.total) {
return '';
return ''
}
var done = formatSizeFilter(progress.done, true),
doneParts = done.split(' '),
total = formatSizeFilter(progress.total),
totalParts = total.split(' ');
var done = formatSizeFilter(progress.done, true)
var doneParts = done.split(' ')
var total = formatSizeFilter(progress.total)
var totalParts = total.split(' ')
if (totalParts[1] === doneParts[1]) {
return _('format_size_progress_mulitple', {done: doneParts[0], total: totalParts[0], parts: (doneParts[1] || '')});
return _('format_size_progress_mulitple', {done: doneParts[0], total: totalParts[0], parts: (doneParts[1] || '')})
}
return _('format_size_progress', {done: done, total: total});
return _('format_size_progress', {done: done, total: total})
}
})
.filter('formatShortNumber', [function () {
return function (num) {
if (!num) {
return '0';
return '0'
}
else if (num < 1000) {
return num.toString();
return num.toString()
}
else if (num < 900000) {
var mult = num > 10000 ? 1 : 10;
return (Math.round(num / 1000 * mult) / mult) + 'K';
var mult = num > 10000 ? 1 : 10
return (Math.round(num / 1000 * mult) / mult) + 'K'
}
var mult = num > 10000000 ? 1 : 10;
return (Math.round(num / 1000000 * mult) / mult) + 'M';
var mult = num > 10000000 ? 1 : 10
return (Math.round(num / 1000000 * mult) / mult) + 'M'
}
}])
.filter('nl2br', [function () {
return function (text) {
return text.replace(/\n/g, '<br/>');
return text.replace(/\n/g, '<br/>')
}
}])
.filter('shortUrl', [function () {
return function (text) {
if (typeof text !== 'string') {
return text;
return text
}
return text.replace(/^https?:\/\//, '').replace(/^www\./, '');
return text.replace(/^https?:\/\//, '').replace(/^www\./, '')
}
}])
.filter('richText', function ($filter) {
var linkyFilter = $filter('linky');
var linkyFilter = $filter('linky')
return function (text) {
return linkyFilter(text, '_blank').replace(/\n|&#10;/g, '<br/>');
return linkyFilter(text, '_blank').replace(/\n|&#10;/g, '<br/>')
}
})
.filter('relativeTime', function ($filter, _) {
var langMinutesPluralize = _.pluralize('relative_time_pluralize_minutes_ago'),
langHoursPluralize = _.pluralize('relative_time_pluralize_hours_ago'),
dateOrTimeFilter = $filter('dateOrTime');
var langMinutesPluralize = _.pluralize('relative_time_pluralize_minutes_ago')
var langHoursPluralize = _.pluralize('relative_time_pluralize_hours_ago')
var dateOrTimeFilter = $filter('dateOrTime')
return function (timestamp) {
var diff = Math.abs(tsNow(true) - timestamp);
var diff = Math.abs(tsNow(true) - timestamp)
if (diff < 60) {
return _('relative_time_just_now');
return _('relative_time_just_now')
}
if (diff < 3600) {
var minutes = Math.floor(diff / 60);
return langMinutesPluralize(minutes);
var minutes = Math.floor(diff / 60)
return langMinutesPluralize(minutes)
}
if (diff < 86400) {
var hours = Math.floor(diff / 3600);
return langHoursPluralize(hours);
var hours = Math.floor(diff / 3600)
return langHoursPluralize(hours)
}
return dateOrTimeFilter(timestamp, true);
return dateOrTimeFilter(timestamp, true)
}
})

97
app/js/init.js

@ -1,98 +1,103 @@ @@ -1,98 +1,103 @@
(function initApplication () {
;(function initApplication () {
var classes = [
Config.Navigator.osX ? 'osx' : 'non_osx',
Config.Navigator.msie ? 'msie' : 'non_msie',
Config.Navigator.retina ? 'is_2x' : 'is_1x'
];
]
if (Config.Modes.ios_standalone) {
classes.push('ios_standalone');
classes.push('ios_standalone')
}
$(document.body).addClass(classes.join(' '));
$(document.body).addClass(classes.join(' '))
ConfigStorage.get('layout_selected', 'i18n_locale', function (params) {
var layout = params[0],
locale = params[1],
defaultLocale = 'en-us',
bootReady = {
var layout = params[0]
var locale = params[1]
var defaultLocale = 'en-us'
var bootReady = {
dom: false,
i18n_ng: false,
i18n_messages: false,
i18n_fallback: false
},
checkReady = function checkReady () {
var i, ready = true;
}
var checkReady = function checkReady () {
var i
var ready = true
for (i in bootReady) {
if (bootReady.hasOwnProperty(i) && bootReady[i] === false) {
ready = false;
break;
ready = false
break
}
}
if (ready) {
bootReady.boot = false;
angular.bootstrap(document, ['myApp']);
bootReady.boot = false
angular.bootstrap(document, ['myApp'])
}
}
};
if (Config.Modes.force_mobile) {
layout = 'mobile';
layout = 'mobile'
}
else if (Config.Modes.force_desktop) {
layout = 'desktop';
layout = 'desktop'
}
switch (layout) {
case 'mobile': Config.Mobile = true; break;
case 'desktop': Config.Mobile = false; break;
case 'mobile':
Config.Mobile = true
break
case 'desktop':
Config.Mobile = false
break
default:
var width = $(window).width();
Config.Mobile = Config.Navigator.mobile || width > 10 && width < 480;
break;
var width = $(window).width()
Config.Mobile = Config.Navigator.mobile || width > 10 && width < 480
break
}
$('head').append(
'<link rel="stylesheet" href="css/' + (Config.Mobile ? 'mobile.css' : 'desktop.css') + '" />'
);
)
if (!locale) {
locale = (navigator.language || '').toLowerCase();
locale = Config.I18n.aliases[locale] || locale;
locale = (navigator.language || '').toLowerCase()
locale = Config.I18n.aliases[locale] || locale
}
for (var i = 0; i < Config.I18n.supported.length; i++) {
if (Config.I18n.supported[i] == locale) {
Config.I18n.locale = locale;
break;
Config.I18n.locale = locale
break
}
}
bootReady.i18n_ng = Config.I18n.locale == defaultLocale; // Already included
bootReady.i18n_ng = Config.I18n.locale == defaultLocale // Already included
$.getJSON('js/locales/' + Config.I18n.locale + '.json').success(function (json) {
Config.I18n.messages = json;
bootReady.i18n_messages = true;
Config.I18n.messages = json
bootReady.i18n_messages = true
if (Config.I18n.locale == defaultLocale) { // No fallback, leave empty object
bootReady.i18n_fallback = true;
bootReady.i18n_fallback = true
}
checkReady();
});
checkReady()
})
if (Config.I18n.locale != defaultLocale) {
$.getJSON('js/locales/' + defaultLocale + '.json').success(function (json) {
Config.I18n.fallback_messages = json;
bootReady.i18n_fallback = true;
checkReady();
});
Config.I18n.fallback_messages = json
bootReady.i18n_fallback = true
checkReady()
})
}
$(document).ready(function () {
bootReady.dom = true;
bootReady.dom = true
if (!bootReady.i18n_ng) { // onDOMready because needs to be after angular
$('<script>').appendTo('body')
.on('load', function () {
bootReady.i18n_ng = true;
checkReady();
bootReady.i18n_ng = true
checkReady()
})
.attr('src', 'vendor/angular/i18n/angular-locale_' + Config.I18n.locale + '.js');
.attr('src', 'vendor/angular/i18n/angular-locale_' + Config.I18n.locale + '.js')
} else {
checkReady();
checkReady()
}
});
});
})();
})
})
})()

562
app/js/lib/bin_utils.js

@ -6,76 +6,77 @@ @@ -6,76 +6,77 @@
*/
function bigint (num) {
return new BigInteger(num.toString(16), 16);
return new BigInteger(num.toString(16), 16)
}
function bigStringInt (strNum) {
return new BigInteger(strNum, 10);
return new BigInteger(strNum, 10)
}
function dHexDump (bytes) {
var arr = [];
var arr = []
for (var i = 0; i < bytes.length; i++) {
if (i && !(i % 2)) {
if (!(i % 16)) {
arr.push("\n");
arr.push('\n')
} else if (!(i % 4)) {
arr.push(' ');
arr.push(' ')
} else {
arr.push(' ');
arr.push(' ')
}
}
arr.push((bytes[i] < 16 ? '0' : '') + bytes[i].toString(16));
arr.push((bytes[i] < 16 ? '0' : '') + bytes[i].toString(16))
}
console.log(arr.join(''));
console.log(arr.join(''))
}
function bytesToHex (bytes) {
bytes = bytes || [];
var arr = [];
bytes = bytes || []
var arr = []
for (var i = 0; i < bytes.length; i++) {
arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16));
arr.push((bytes[i] < 16 ? '0' : '') + (bytes[i] || 0).toString(16))
}
return arr.join('');
return arr.join('')
}
function bytesFromHex (hexString) {
var len = hexString.length,
i,
start = 0,
bytes = [];
i
var start = 0
var bytes = []
if (hexString.length % 2) {
bytes.push(parseInt(hexString.charAt(0), 16));
start++;
bytes.push(parseInt(hexString.charAt(0), 16))
start++
}
for (i = start; i < len; i += 2) {
bytes.push(parseInt(hexString.substr(i, 2), 16));
bytes.push(parseInt(hexString.substr(i, 2), 16))
}
return bytes;
return bytes
}
function bytesToBase64 (bytes) {
var mod3, result = '';
var mod3
var result = ''
for (var nLen = bytes.length, nUint24 = 0, nIdx = 0; nIdx < nLen; nIdx++) {
mod3 = nIdx % 3;
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24);
mod3 = nIdx % 3
nUint24 |= bytes[nIdx] << (16 >>> mod3 & 24)
if (mod3 === 2 || nLen - nIdx === 1) {
result += String.fromCharCode(
uint6ToBase64(nUint24 >>> 18 & 63),
uint6ToBase64(nUint24 >>> 12 & 63),
uint6ToBase64(nUint24 >>> 6 & 63),
uint6ToBase64(nUint24 & 63)
);
nUint24 = 0;
)
nUint24 = 0
}
}
return result.replace(/A(?=A$|$)/g, '=');
return result.replace(/A(?=A$|$)/g, '=')
}
function uint6ToBase64 (nUint6) {
@ -89,580 +90,579 @@ function uint6ToBase64 (nUint6) { @@ -89,580 +90,579 @@ function uint6ToBase64 (nUint6) {
? 43
: nUint6 === 63
? 47
: 65;
: 65
}
function base64ToBlob (base64str, mimeType) {
var sliceSize = 1024;
var byteCharacters = atob(base64str);
var bytesLength = byteCharacters.length;
var slicesCount = Math.ceil(bytesLength / sliceSize);
var byteArrays = new Array(slicesCount);
var sliceSize = 1024
var byteCharacters = atob(base64str)
var bytesLength = byteCharacters.length
var slicesCount = Math.ceil(bytesLength / sliceSize)
var byteArrays = new Array(slicesCount)
for (var sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
var begin = sliceIndex * sliceSize;
var end = Math.min(begin + sliceSize, bytesLength);
var begin = sliceIndex * sliceSize
var end = Math.min(begin + sliceSize, bytesLength)
var bytes = new Array(end - begin);
var bytes = new Array(end - begin)
for (var offset = begin, i = 0; offset < end; ++i, ++offset) {
bytes[i] = byteCharacters[offset].charCodeAt(0);
bytes[i] = byteCharacters[offset].charCodeAt(0)
}
byteArrays[sliceIndex] = new Uint8Array(bytes);
byteArrays[sliceIndex] = new Uint8Array(bytes)
}
return blobConstruct(byteArrays, mimeType);
return blobConstruct(byteArrays, mimeType)
}
function dataUrlToBlob (url) {
// var name = 'b64blob ' + url.length;
// console.time(name);
var urlParts = url.split(',');
var base64str = urlParts[1];
var mimeType = urlParts[0].split(':')[1].split(';')[0];
var blob = base64ToBlob(base64str, mimeType);
// console.timeEnd(name);
return blob;
// var name = 'b64blob ' + url.length
// console.time(name)
var urlParts = url.split(',')
var base64str = urlParts[1]
var mimeType = urlParts[0].split(':')[1].split(';')[0]
var blob = base64ToBlob(base64str, mimeType)
// console.timeEnd(name)
return blob
}
function blobConstruct (blobParts, mimeType) {
var blob;
var blob
try {
blob = new Blob(blobParts, {type: mimeType});
blob = new Blob(blobParts, {type: mimeType})
} catch (e) {
var bb = new BlobBuilder;
var bb = new BlobBuilder
angular.forEach(blobParts, function (blobPart) {
bb.append(blobPart);
});
blob = bb.getBlob(mimeType);
bb.append(blobPart)
})
blob = bb.getBlob(mimeType)
}
return blob;
return blob
}
function bytesCmp (bytes1, bytes2) {
var len = bytes1.length;
var len = bytes1.length
if (len != bytes2.length) {
return false;
return false
}
for (var i = 0; i < len; i++) {
if (bytes1[i] != bytes2[i]) {
return false;
return false
}
}
return true;
return true
}
function bytesXor (bytes1, bytes2) {
var len = bytes1.length,
bytes = [];
var len = bytes1.length
var bytes = []
for (var i = 0; i < len; ++i) {
bytes[i] = bytes1[i] ^ bytes2[i];
bytes[i] = bytes1[i] ^ bytes2[i]
}
return bytes;
return bytes
}
function bytesToWords (bytes) {
if (bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
bytes = new Uint8Array(bytes)
}
var len = bytes.length,
words = [], i;
var len = bytes.length
var words = []
var i
for (i = 0; i < len; i++) {
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8);
words[i >>> 2] |= bytes[i] << (24 - (i % 4) * 8)
}
return new CryptoJS.lib.WordArray.init(words, len);
return new CryptoJS.lib.WordArray.init(words, len)
}
function bytesFromWords (wordArray) {
var words = wordArray.words,
sigBytes = wordArray.sigBytes,
bytes = [];
var words = wordArray.words
var sigBytes = wordArray.sigBytes
var bytes = []
for (var i = 0; i < sigBytes; i++) {
bytes.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff);
bytes.push((words[i >>> 2] >>> (24 - (i % 4) * 8)) & 0xff)
}
return bytes;
return bytes
}
function bytesFromBigInt (bigInt, len) {
var bytes = bigInt.toByteArray();
var bytes = bigInt.toByteArray()
if (len && bytes.length < len) {
var padding = [];
var padding = []
for (var i = 0, needPadding = len - bytes.length; i < needPadding; i++) {
padding[i] = 0;
padding[i] = 0
}
if (bytes instanceof ArrayBuffer) {
bytes = bufferConcat(padding, bytes);
bytes = bufferConcat(padding, bytes)
} else {
bytes = padding.concat(bytes);
}
bytes = padding.concat(bytes)
}
else {
}else {
while (!bytes[0] && (!len || bytes.length > len)) {
bytes = bytes.slice(1);
bytes = bytes.slice(1)
}
}
return bytes;
return bytes
}
function bytesFromLeemonBigInt (bigInt, len) {
var str = bigInt2str(bigInt, 16);
return bytesFromHex(str);
var str = bigInt2str(bigInt, 16)
return bytesFromHex(str)
}
function bytesToArrayBuffer (b) {
return (new Uint8Array(b)).buffer;
return (new Uint8Array(b)).buffer
}
function convertToArrayBuffer (bytes) {
// Be careful with converting subarrays!!
if (bytes instanceof ArrayBuffer) {
return bytes;
return bytes
}
if (bytes.buffer !== undefined &&
bytes.buffer.byteLength == bytes.length * bytes.BYTES_PER_ELEMENT) {
return bytes.buffer;
return bytes.buffer
}
return bytesToArrayBuffer(bytes);
return bytesToArrayBuffer(bytes)
}
function convertToUint8Array (bytes) {
if (bytes.buffer !== undefined) {
return bytes;
return bytes
}
return new Uint8Array(bytes);
return new Uint8Array(bytes)
}
function convertToByteArray (bytes) {
if (Array.isArray(bytes)) {
return bytes;
return bytes
}
bytes = convertToUint8Array(bytes);
var newBytes = [];
bytes = convertToUint8Array(bytes)
var newBytes = []
for (var i = 0, len = bytes.length; i < len; i++) {
newBytes.push(bytes[i]);
newBytes.push(bytes[i])
}
return newBytes;
return newBytes
}
function bytesFromArrayBuffer (buffer) {
var len = buffer.byteLength,
byteView = new Uint8Array(buffer),
bytes = [];
var len = buffer.byteLength
var byteView = new Uint8Array(buffer)
var bytes = []
for (var i = 0; i < len; ++i) {
bytes[i] = byteView[i];
bytes[i] = byteView[i]
}
return bytes;
return bytes
}
function bufferConcat (buffer1, buffer2) {
var l1 = buffer1.byteLength || buffer1.length,
l2 = buffer2.byteLength || buffer2.length;
var tmp = new Uint8Array(l1 + l2);
tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0);
tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1);
var l1 = buffer1.byteLength || buffer1.length
var l2 = buffer2.byteLength || buffer2.length
var tmp = new Uint8Array(l1 + l2)
tmp.set(buffer1 instanceof ArrayBuffer ? new Uint8Array(buffer1) : buffer1, 0)
tmp.set(buffer2 instanceof ArrayBuffer ? new Uint8Array(buffer2) : buffer2, l1)
return tmp.buffer;
return tmp.buffer
}
function longToInts (sLong) {
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000));
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000))
return [divRem[0].intValue(), divRem[1].intValue()];
return [divRem[0].intValue(), divRem[1].intValue()]
}
function longToBytes (sLong) {
return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse();
return bytesFromWords({words: longToInts(sLong), sigBytes: 8}).reverse()
}
function longFromInts (high, low) {
return bigint(high).shiftLeft(32).add(bigint(low)).toString(10);
return bigint(high).shiftLeft(32).add(bigint(low)).toString(10)
}
function intToUint (val) {
val = parseInt(val);
val = parseInt(val)
if (val < 0) {
val = val + 4294967296;
val = val + 4294967296
}
return val;
return val
}
function uintToInt (val) {
if (val > 2147483647) {
val = val - 4294967296;
val = val - 4294967296
}
return val;
return val
}
function sha1HashSync (bytes) {
this.rushaInstance = this.rushaInstance || new Rusha(1024 * 1024);
this.rushaInstance = this.rushaInstance || new Rusha(1024 * 1024)
// console.log(dT(), 'SHA-1 hash start', bytes.byteLength || bytes.length);
var hashBytes = rushaInstance.rawDigest(bytes).buffer;
// console.log(dT(), 'SHA-1 hash finish');
// console.log(dT(), 'SHA-1 hash start', bytes.byteLength || bytes.length)
var hashBytes = rushaInstance.rawDigest(bytes).buffer
// console.log(dT(), 'SHA-1 hash finish')
return hashBytes;
return hashBytes
}
function sha1BytesSync (bytes) {
return bytesFromArrayBuffer(sha1HashSync(bytes));
return bytesFromArrayBuffer(sha1HashSync(bytes))
}
function sha256HashSync (bytes) {
// console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length);
var hashWords = CryptoJS.SHA256(bytesToWords(bytes));
// console.log(dT(), 'SHA-2 hash finish');
// console.log(dT(), 'SHA-2 hash start', bytes.byteLength || bytes.length)
var hashWords = CryptoJS.SHA256(bytesToWords(bytes))
// console.log(dT(), 'SHA-2 hash finish')
var hashBytes = bytesFromWords(hashWords);
var hashBytes = bytesFromWords(hashWords)
return hashBytes;
return hashBytes
}
function rsaEncrypt (publicKey, bytes) {
bytes = addPadding(bytes, 255);
bytes = addPadding(bytes, 255)
// console.log('RSA encrypt start');
var N = new BigInteger(publicKey.modulus, 16),
E = new BigInteger(publicKey.exponent, 16),
X = new BigInteger(bytes),
encryptedBigInt = X.modPowInt(E, N),
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256);
// console.log('RSA encrypt finish');
// console.log('RSA encrypt start')
var N = new BigInteger(publicKey.modulus, 16)
var E = new BigInteger(publicKey.exponent, 16)
var X = new BigInteger(bytes)
var encryptedBigInt = X.modPowInt(E, N),
encryptedBytes = bytesFromBigInt(encryptedBigInt, 256)
// console.log('RSA encrypt finish')
return encryptedBytes;
return encryptedBytes
}
function addPadding (bytes, blockSize, zeroes) {
blockSize = blockSize || 16;
var len = bytes.byteLength || bytes.length;
var needPadding = blockSize - (len % blockSize);
blockSize = blockSize || 16
var len = bytes.byteLength || bytes.length
var needPadding = blockSize - (len % blockSize)
if (needPadding > 0 && needPadding < blockSize) {
var padding = new Array(needPadding);
var padding = new Array(needPadding)
if (zeroes) {
for (var i = 0; i < needPadding; i++) {
padding[i] = 0
}
} else {
(new SecureRandom()).nextBytes(padding);
(new SecureRandom()).nextBytes(padding)
}
if (bytes instanceof ArrayBuffer) {
bytes = bufferConcat(bytes, padding);
bytes = bufferConcat(bytes, padding)
} else {
bytes = bytes.concat(padding);
bytes = bytes.concat(padding)
}
}
return bytes;
return bytes
}
function aesEncryptSync (bytes, keyBytes, ivBytes) {
var len = bytes.byteLength || bytes.length;
var len = bytes.byteLength || bytes.length
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/);
bytes = addPadding(bytes);
// console.log(dT(), 'AES encrypt start', len/*, bytesToHex(keyBytes), bytesToHex(ivBytes)*/)
bytes = addPadding(bytes)
var encryptedWords = CryptoJS.AES.encrypt(bytesToWords(bytes), bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.IGE
}).ciphertext;
}).ciphertext
var encryptedBytes = bytesFromWords(encryptedWords);
// console.log(dT(), 'AES encrypt finish');
var encryptedBytes = bytesFromWords(encryptedWords)
// console.log(dT(), 'AES encrypt finish')
return encryptedBytes;
return encryptedBytes
}
function aesDecryptSync (encryptedBytes, keyBytes, ivBytes) {
// console.log(dT(), 'AES decrypt start', encryptedBytes.length);
// console.log(dT(), 'AES decrypt start', encryptedBytes.length)
var decryptedWords = CryptoJS.AES.decrypt({ciphertext: bytesToWords(encryptedBytes)}, bytesToWords(keyBytes), {
iv: bytesToWords(ivBytes),
padding: CryptoJS.pad.NoPadding,
mode: CryptoJS.mode.IGE
});
})
var bytes = bytesFromWords(decryptedWords);
// console.log(dT(), 'AES decrypt finish');
var bytes = bytesFromWords(decryptedWords)
// console.log(dT(), 'AES decrypt finish')
return bytes;
return bytes
}
function gzipUncompress (bytes) {
// console.log('Gzip uncompress start');
var result = (new Zlib.Gunzip(bytes)).decompress();
// console.log('Gzip uncompress finish');
return result;
// console.log('Gzip uncompress start')
var result = (new Zlib.Gunzip(bytes)).decompress()
// console.log('Gzip uncompress finish')
return result
}
function nextRandomInt (maxValue) {
return Math.floor(Math.random() * maxValue);
};
return Math.floor(Math.random() * maxValue)
}
function pqPrimeFactorization (pqBytes) {
var what = new BigInteger(pqBytes),
result = false;
var what = new BigInteger(pqBytes)
var result = false
// console.log(dT(), 'PQ start', pqBytes, what.toString(16), what.bitLength());
// console.log(dT(), 'PQ start', pqBytes, what.toString(16), what.bitLength())
try {
result = pqPrimeLeemon(str2bigInt(what.toString(16), 16, Math.ceil(64 / bpe) + 1))
} catch (e) {
console.error('Pq leemon Exception', e);
console.error('Pq leemon Exception', e)
}
if (result === false && what.bitLength() <= 64) {
// console.time('PQ long');
// console.time('PQ long')
try {
result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16));
result = pqPrimeLong(goog.math.Long.fromString(what.toString(16), 16))
} catch (e) {
console.error('Pq long Exception', e);
};
// console.timeEnd('PQ long');
console.error('Pq long Exception', e)
}
// console.timeEnd('PQ long')
}
// console.log(result);
// console.log(result)
if (result === false) {
// console.time('pq BigInt');
result = pqPrimeBigInteger(what);
// console.timeEnd('pq BigInt');
// console.time('pq BigInt')
result = pqPrimeBigInteger(what)
// console.timeEnd('pq BigInt')
}
// console.log(dT(), 'PQ finish');
// console.log(dT(), 'PQ finish')
return result;
return result
}
function pqPrimeBigInteger (what) {
var it = 0,
g;
g
for (var i = 0; i < 3; i++) {
var q = (nextRandomInt(128) & 15) + 17,
x = bigint(nextRandomInt(1000000000) + 1),
y = x.clone(),
lim = 1 << (i + 18);
var q = (nextRandomInt(128) & 15) + 17
var x = bigint(nextRandomInt(1000000000) + 1)
var y = x.clone()
var lim = 1 << (i + 18)
for (var j = 1; j < lim; j++) {
++it;
var a = x.clone(),
b = x.clone(),
c = bigint(q);
++it
var a = x.clone()
var b = x.clone()
var c = bigint(q)
while (!b.equals(BigInteger.ZERO)) {
if (!b.and(BigInteger.ONE).equals(BigInteger.ZERO)) {
c = c.add(a);
c = c.add(a)
if (c.compareTo(what) > 0) {
c = c.subtract(what);
c = c.subtract(what)
}
}
a = a.add(a);
a = a.add(a)
if (a.compareTo(what) > 0) {
a = a.subtract(what);
a = a.subtract(what)
}
b = b.shiftRight(1);
b = b.shiftRight(1)
}
x = c.clone();
var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y);
g = z.gcd(what);
x = c.clone()
var z = x.compareTo(y) < 0 ? y.subtract(x) : x.subtract(y)
g = z.gcd(what)
if (!g.equals(BigInteger.ONE)) {
break;
break
}
if ((j & (j - 1)) == 0) {
y = x.clone();
y = x.clone()
}
}
if (g.compareTo(BigInteger.ONE) > 0) {
break;
break
}
}
var f = what.divide(g), P, Q;
var f = what.divide(g), P, Q
if (g.compareTo(f) > 0) {
P = f;
Q = g;
P = f
Q = g
} else {
P = g;
Q = f;
P = g
Q = f
}
return [bytesFromBigInt(P), bytesFromBigInt(Q), it];
return [bytesFromBigInt(P), bytesFromBigInt(Q), it]
}
function gcdLong (a, b) {
while (a.notEquals(goog.math.Long.ZERO) && b.notEquals(goog.math.Long.ZERO)) {
while (b.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
b = b.shiftRight(1);
b = b.shiftRight(1)
}
while (a.and(goog.math.Long.ONE).equals(goog.math.Long.ZERO)) {
a = a.shiftRight(1);
a = a.shiftRight(1)
}
if (a.compare(b) > 0) {
a = a.subtract(b);
a = a.subtract(b)
} else {
b = b.subtract(a);
b = b.subtract(a)
}
}
return b.equals(goog.math.Long.ZERO) ? a : b;
return b.equals(goog.math.Long.ZERO) ? a : b
}
function pqPrimeLong (what) {
var it = 0,
g;
g
for (var i = 0; i < 3; i++) {
var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17),
x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1),
y = x,
lim = 1 << (i + 18);
var q = goog.math.Long.fromInt((nextRandomInt(128) & 15) + 17)
var x = goog.math.Long.fromInt(nextRandomInt(1000000000) + 1)
var y = x
var lim = 1 << (i + 18)
for (var j = 1; j < lim; j++) {
++it;
var a = x,
b = x,
c = q;
++it
var a = x
var b = x
var c = q
while (b.notEquals(goog.math.Long.ZERO)) {
if (b.and(goog.math.Long.ONE).notEquals(goog.math.Long.ZERO)) {
c = c.add(a);
c = c.add(a)
if (c.compare(what) > 0) {
c = c.subtract(what);
c = c.subtract(what)
}
}
a = a.add(a);
a = a.add(a)
if (a.compare(what) > 0) {
a = a.subtract(what);
a = a.subtract(what)
}
b = b.shiftRight(1);
b = b.shiftRight(1)
}
x = c;
var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y);
g = gcdLong(z, what);
x = c
var z = x.compare(y) < 0 ? y.subtract(x) : x.subtract(y)
g = gcdLong(z, what)
if (g.notEquals(goog.math.Long.ONE)) {
break;
break
}
if ((j & (j - 1)) == 0) {
y = x;
y = x
}
}
if (g.compare(goog.math.Long.ONE) > 0) {
break;
break
}
}
var f = what.div(g), P, Q;
var f = what.div(g), P, Q
if (g.compare(f) > 0) {
P = f;
Q = g;
P = f
Q = g
} else {
P = g;
Q = f;
P = g
Q = f
}
return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it];
return [bytesFromHex(P.toString(16)), bytesFromHex(Q.toString(16)), it]
}
function pqPrimeLeemon (what) {
var minBits = 64,
minLen = Math.ceil(minBits / bpe) + 1,
it = 0, i, q, j, lim, g, P, Q,
a = new Array(minLen),
b = new Array(minLen),
c = new Array(minLen),
g = new Array(minLen),
z = new Array(minLen),
x = new Array(minLen),
y = new Array(minLen);
var minBits = 64
var minLen = Math.ceil(minBits / bpe) + 1
var it = 0
var i, q
var j, lim
var g, P
var Q
var a = new Array(minLen)
var b = new Array(minLen)
var c = new Array(minLen)
var g = new Array(minLen)
var z = new Array(minLen)
var x = new Array(minLen)
var y = new Array(minLen)
for (i = 0; i < 3; i++) {
q = (nextRandomInt(128) & 15) + 17;
copyInt_(x, nextRandomInt(1000000000) + 1);
copy_(y, x);
lim = 1 << (i + 18);
q = (nextRandomInt(128) & 15) + 17
copyInt_(x, nextRandomInt(1000000000) + 1)
copy_(y, x)
lim = 1 << (i + 18)
for (j = 1; j < lim; j++) {
++it;
copy_(a, x);
copy_(b, x);
copyInt_(c, q);
++it
copy_(a, x)
copy_(b, x)
copyInt_(c, q)
while (!isZero(b)) {
if (b[0] & 1) {
add_(c, a);
add_(c, a)
if (greater(c, what)) {
sub_(c, what);
sub_(c, what)
}
}
add_(a, a);
add_(a, a)
if (greater(a, what)) {
sub_(a, what);
sub_(a, what)
}
rightShift_(b, 1);
rightShift_(b, 1)
}
copy_(x, c);
copy_(x, c)
if (greater(x, y)) {
copy_(z, x);
sub_(z, y);
copy_(z, x)
sub_(z, y)
} else {
copy_(z, y);
sub_(z, x);
copy_(z, y)
sub_(z, x)
}
eGCD_(z, what, g, a, b);
eGCD_(z, what, g, a, b)
if (!equalsInt(g, 1)) {
break;
break
}
if ((j & (j - 1)) == 0) {
copy_(y, x);
copy_(y, x)
}
}
if (greater(g, one)) {
break;
break
}
}
divide_(what, g, x, y);
divide_(what, g, x, y)
if (greater(g, x)) {
P = x;
Q = g;
P = x
Q = g
} else {
P = g;
Q = x;
P = g
Q = x
}
// console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10));
// console.log(dT(), 'done', bigInt2str(what, 10), bigInt2str(P, 10), bigInt2str(Q, 10))
return [bytesFromLeemonBigInt(P), bytesFromLeemonBigInt(Q), it];
return [bytesFromLeemonBigInt(P), bytesFromLeemonBigInt(Q), it]
}
function bytesModPow (x, y, m) {
try {
var xBigInt = str2bigInt(bytesToHex(x), 16),
yBigInt = str2bigInt(bytesToHex(y), 16),
mBigInt = str2bigInt(bytesToHex(m), 16),
resBigInt = powMod(xBigInt, yBigInt, mBigInt);
var xBigInt = str2bigInt(bytesToHex(x), 16)
var yBigInt = str2bigInt(bytesToHex(y), 16)
var mBigInt = str2bigInt(bytesToHex(m), 16)
var resBigInt = powMod(xBigInt, yBigInt, mBigInt)
return bytesFromHex(bigInt2str(resBigInt, 16));
return bytesFromHex(bigInt2str(resBigInt, 16))
} catch (e) {
console.error('mod pow error', e);
console.error('mod pow error', e)
}
return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256);
return bytesFromBigInt(new BigInteger(x).modPow(new BigInteger(y), new BigInteger(m)), 256)
}

194
app/js/lib/config.js

File diff suppressed because one or more lines are too long

30
app/js/lib/crypto_worker.js

@ -13,38 +13,38 @@ importScripts( @@ -13,38 +13,38 @@ importScripts(
'../../vendor/closure/long.js',
'../../vendor/cryptoJS/crypto.js',
'../../vendor/rusha/rusha.js'
);
)
onmessage = function (e) {
var taskID = e.data.taskID,
result;
result
switch (e.data.task) {
case 'factorize':
result = pqPrimeFactorization(e.data.bytes);
break;
result = pqPrimeFactorization(e.data.bytes)
break
case 'mod-pow':
result = bytesModPow(e.data.x, e.data.y, e.data.m);
break;
result = bytesModPow(e.data.x, e.data.y, e.data.m)
break
case 'sha1-hash':
result = sha1HashSync(e.data.bytes);
break;
result = sha1HashSync(e.data.bytes)
break
case 'aes-encrypt':
result = aesEncryptSync(e.data.bytes, e.data.keyBytes, e.data.ivBytes);
break;
result = aesEncryptSync(e.data.bytes, e.data.keyBytes, e.data.ivBytes)
break
case 'aes-decrypt':
result = aesDecryptSync(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes);
break;
result = aesDecryptSync(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes)
break
default:
throw new Error('Unknown task: ' + e.data.task);
throw new Error('Unknown task: ' + e.data.task)
}
postMessage({taskID: taskID, result: result});
postMessage({taskID: taskID, result: result})
}
postMessage('ready');
postMessage('ready')

106
app/js/lib/i18n.js

@ -1,89 +1,89 @@ @@ -1,89 +1,89 @@
'use strict';
'use strict'
angular.module('myApp.i18n', ['izhukov.utils'])
.factory('_', ['$rootScope', '$locale', function ($rootScope, $locale) {
var locale = Config.I18n.locale;
var messages = Config.I18n.messages;
var fallbackMessages = Config.I18n.fallback_messages;
var paramRegEx = /\{\s*([a-zA-Z\d\-_]+)(?:\s*:\s*(.*?))?\s*\}/g;
var locale = Config.I18n.locale
var messages = Config.I18n.messages
var fallbackMessages = Config.I18n.fallback_messages
var paramRegEx = /\{\s*([a-zA-Z\d\-_]+)(?:\s*:\s*(.*?))?\s*\}/g
function insertParams (msgstr, params) {
return msgstr.replace(paramRegEx, function ($0, paramKey, nestedMsgStr) {
var param = params[paramKey];
var param = params[paramKey]
if (param === undefined) {
console.warn('[i18n] missing param ' + paramKey + ' for message "' + msgstr + '"');
return '';
console.warn('[i18n] missing param ' + paramKey + ' for message "' + msgstr + '"')
return ''
}
if (nestedMsgStr !== undefined) {
param = insertParams(param, nestedMsgStr.split('|'));
param = insertParams(param, nestedMsgStr.split('|'))
}
return param.toString().trim();
});
return param.toString().trim()
})
}
function parseMarkdownString (msgstr, msgid) {
msgstr = msgstr
.replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
.replace(/\n|&#10;/g, "<br/>");
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
.replace(/\n|&#10;/g, '<br/>')
return msgstr;
return msgstr
}
function _ (msgid, params) {
var raw = false;
var msgstr = msgid;
var raw = false
var msgstr = msgid
if (msgid.substr(-4) === '_raw') {
raw = true;
msgid = msgid.substr(0, msgid.length - 4);
raw = true
msgid = msgid.substr(0, msgid.length - 4)
}
if (messages.hasOwnProperty(msgid)) {
msgstr = messages[msgid];
msgstr = messages[msgid]
} else if (fallbackMessages.hasOwnProperty(msgid)) {
msgstr = fallbackMessages[msgid];
console.warn('[i18n] missing locale key ' + locale + ' / ' + msgid);
msgstr = fallbackMessages[msgid]
console.warn('[i18n] missing locale key ' + locale + ' / ' + msgid)
} else {
console.warn('[i18n] missing key ' + msgid);
return msgid;
console.warn('[i18n] missing key ' + msgid)
return msgid
}
if (!raw) {
msgstr = encodeEntities(msgstr);
msgstr = encodeEntities(msgstr)
}
if (msgid.substr(-3) == '_md') {
msgstr = parseMarkdownString(msgstr);
msgstr = parseMarkdownString(msgstr)
}
if (arguments.length > 1) {
if (typeof params == 'string') {
Array.prototype.shift.apply(arguments);
msgstr = insertParams(msgstr, arguments);
Array.prototype.shift.apply(arguments)
msgstr = insertParams(msgstr, arguments)
} else {
msgstr = insertParams(msgstr, params);
msgstr = insertParams(msgstr, params)
}
}
return msgstr;
return msgstr
}
_.locale = function () {
return locale;
};
return locale
}
_.pluralize = function (msgid) {
var categories = $rootScope.$eval(_(msgid + '_raw'));
var categories = $rootScope.$eval(_(msgid + '_raw'))
return function (count) {
return (categories[$locale.pluralCat(count)] || '').replace('{}', count);
return (categories[$locale.pluralCat(count)] || '').replace('{}', count)
}
}
};
return _;
return _
}])
.filter('i18n', ['_', function (_) {
return function (msgid, params) {
return _(msgid + '_raw', params);
return _(msgid + '_raw', params)
}
}])
@ -92,9 +92,9 @@ angular.module('myApp.i18n', ['izhukov.utils']) @@ -92,9 +92,9 @@ angular.module('myApp.i18n', ['izhukov.utils'])
restrict: 'EA',
priority: 1, // execute before built-in ngPluralize
compile: function (element) {
var msgid = element.attr('when');
var msgstr = _(msgid + '_raw');
element.attr('when', msgstr);
var msgid = element.attr('when')
var msgstr = _(msgid + '_raw')
element.attr('when', msgstr)
}
}
}])
@ -104,24 +104,24 @@ angular.module('myApp.i18n', ['izhukov.utils']) @@ -104,24 +104,24 @@ angular.module('myApp.i18n', ['izhukov.utils'])
restrict: 'EA',
compile: function (element) {
var params = element.children('my-i18n-param:not([name]), [my-i18n-param=""]:not([name])').map(function (index, param) {
return param.outerHTML;
}).toArray();
return param.outerHTML
}).toArray()
element.children('my-i18n-param[name], [my-i18n-param]:not([my-i18n-param=""]), [my-i18n-param][name]').each(function (i, param) {
params[angular.element(param).attr('my-i18n-param') || angular.element(param).attr('name')] = param.outerHTML;
});
element.children('my-i18n-param').remove();
var formats = element.attr("my-i18n") || element.attr("msgid") ? element : element.children('my-i18n-format, [my-i18n-format]');
params[angular.element(param).attr('my-i18n-param') || angular.element(param).attr('name')] = param.outerHTML
})
element.children('my-i18n-param').remove()
var formats = element.attr('my-i18n') || element.attr('msgid') ? element : element.children('my-i18n-format, [my-i18n-format]')
formats.each(function (index, element) {
var format = angular.element(element);
var msgid = format.attr("my-i18n") || format.attr("msgid") || format.attr("my-i18n-format") || format.html().replace(/\s+/g, ' ').trim();
var format = angular.element(element)
var msgid = format.attr('my-i18n') || format.attr('msgid') || format.attr('my-i18n-format') || format.html().replace(/\s+/g, ' ').trim()
if (format.hasClass('nocopy')) {
var msgstr = _(msgid + '_raw', params);
format.attr('data-content', msgstr);
var msgstr = _(msgid + '_raw', params)
format.attr('data-content', msgstr)
} else {
var msgstr = _(msgid, params);
format.html(msgstr);
var msgstr = _(msgid, params)
format.html(msgstr)
}
});
})
}
}
}]);
}])

1476
app/js/lib/mtproto.js

File diff suppressed because it is too large Load Diff

725
app/js/lib/mtproto_wrapper.js

File diff suppressed because it is too large Load Diff

1647
app/js/lib/ng_utils.js

File diff suppressed because it is too large Load Diff

125
app/js/lib/polyfill.js

@ -1,20 +1,21 @@ @@ -1,20 +1,21 @@
// Console-polyfill. MIT license.
// https://github.com/paulmillr/console-polyfill
// Make it safe to do console.log() always.
(function(global) {
'use strict';
global.console = global.console || {};
var con = global.console;
var prop, method;
var empty = {};
var dummy = function() {};
var properties = 'memory'.split(',');
;(function (global) {
'use strict'
global.console = global.console || {}
var con = global.console
var prop
var method
var empty = {}
var dummy = function () {}
var properties = 'memory'.split(',')
var methods = ('assert,clear,count,debug,dir,dirxml,error,exception,group,' +
'groupCollapsed,groupEnd,info,log,markTimeline,profile,profiles,profileEnd,' +
'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(',');
while (prop = properties.pop()) if (!con[prop]) con[prop] = empty;
while (method = methods.pop()) if (!con[method]) con[method] = dummy;
})(typeof window === 'undefined' ? this : window);
'show,table,time,timeEnd,timeline,timelineEnd,timeStamp,trace,warn').split(',')
while (prop = properties.pop()) if (!con[prop]) con[prop] = empty
while (method = methods.pop()) if (!con[method]) con[method] = dummy
})(typeof window === 'undefined' ? this : window)
// Using `this` for web workers while maintaining compatibility with browser
// targeted script loaders such as Browserify or Webpack where the only way to
// get to the global object is via `window`.
@ -22,114 +23,114 @@ @@ -22,114 +23,114 @@
/* Array.indexOf polyfill */
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function (searchElement, fromIndex) {
var k;
var k
if (this == null) {
throw new TypeError('"this" is null or not defined');
throw new TypeError('"this" is null or not defined')
}
var O = Object(this);
var len = O.length >>> 0;
var O = Object(this)
var len = O.length >>> 0
if (len === 0) {
return -1;
return -1
}
var n = +fromIndex || 0;
var n = +fromIndex || 0
if (Math.abs(n) === Infinity) {
n = 0;
n = 0
}
if (n >= len) {
return -1;
return -1
}
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
k = Math.max(n >= 0 ? n : len - Math.abs(n), 0)
while (k < len) {
if (k in O && O[k] === searchElement) {
return k;
return k
}
k++
}
k++;
return -1
}
return -1;
};
}
/* Array.isArray polyfill */
if (!Array.isArray) {
Array.isArray = function (arg) {
return Object.prototype.toString.call(arg) === '[object Array]';
};
return Object.prototype.toString.call(arg) === '[object Array]'
}
}
/* Object.create polyfill */
if (typeof Object.create != 'function') {
Object.create = (function () {
var Object = function() {};
var Object = function () {}
return function (prototype) {
if (arguments.length > 1) {
throw Error('Second argument not supported');
throw Error('Second argument not supported')
}
if (typeof prototype != 'object') {
throw TypeError('Argument must be an object');
throw TypeError('Argument must be an object')
}
Object.prototype = prototype
var result = { }
Object.prototype = null
return result
}
Object.prototype = prototype;
var result = new Object();
Object.prototype = null;
return result;
};
})();
})()
}
/* Function.bind polyfill */
if (!Function.prototype.bind) {
Function.prototype.bind = function (oThis) {
if (typeof this !== "function") {
throw new TypeError("Function.prototype.bind - what is trying to be bound is not callable");
if (typeof this !== 'function') {
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function () {},
fBound = function () {
var aArgs = Array.prototype.slice.call(arguments, 1)
var fToBind = this
var fNOP = function () {}
var fBound = function () {
return fToBind.apply(this instanceof fNOP && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
aArgs.concat(Array.prototype.slice.call(arguments)))
}
fNOP.prototype = this.prototype;
fBound.prototype = new fNOP();
fNOP.prototype = this.prototype
fBound.prototype = new fNOP()
return fBound;
};
return fBound
}
}
/* setZeroTimeout polyfill, from http://dbaron.org/log/20100309-faster-timeouts */
(function (global) {
var timeouts = [];
var messageName = 'zero-timeout-message';
var timeouts = []
var messageName = 'zero-timeout-message'
function setZeroTimeout (fn) {
timeouts.push(fn);
global.postMessage(messageName, '*');
timeouts.push(fn)
global.postMessage(messageName, '*')
}
function handleMessage (event) {
if (event.source == global && event.data == messageName) {
event.stopPropagation();
event.stopPropagation()
if (timeouts.length > 0) {
var fn = timeouts.shift();
fn();
var fn = timeouts.shift()
fn()
}
}
}
global.addEventListener('message', handleMessage, true);
global.addEventListener('message', handleMessage, true)
var originalSetTimeout = global.setTimeout;
var originalSetTimeout = global.setTimeout
global.setTimeout = function (callback, delay) {
if (!delay || delay <= 5) {
return setZeroTimeout(callback);
return setZeroTimeout(callback)
}
return originalSetTimeout(callback, delay)
}
return originalSetTimeout(callback, delay);
};
global.setZeroTimeout = setZeroTimeout;
})(this);
global.setZeroTimeout = setZeroTimeout
})(this)

634
app/js/lib/tl_utils.js

@ -6,535 +6,552 @@ @@ -6,535 +6,552 @@
*/
function TLSerialization (options) {
options = options || {};
this.maxLength = options.startMaxLength || 2048; // 2Kb
this.offset = 0; // in bytes
options = options || {}
this.maxLength = options.startMaxLength || 2048 // 2Kb
this.offset = 0 // in bytes
this.createBuffer();
this.createBuffer()
// this.debug = options.debug !== undefined ? options.debug : Config.Modes.debug;
this.mtproto = options.mtproto || false;
return this;
// this.debug = options.debug !== undefined ? options.debug : Config.Modes.debug
this.mtproto = options.mtproto || false
return this
}
TLSerialization.prototype.createBuffer = function () {
this.buffer = new ArrayBuffer(this.maxLength);
this.intView = new Int32Array(this.buffer);
this.byteView = new Uint8Array(this.buffer);
};
this.buffer = new ArrayBuffer(this.maxLength)
this.intView = new Int32Array(this.buffer)
this.byteView = new Uint8Array(this.buffer)
}
TLSerialization.prototype.getArray = function () {
var resultBuffer = new ArrayBuffer(this.offset);
var resultArray = new Int32Array(resultBuffer);
var resultBuffer = new ArrayBuffer(this.offset)
var resultArray = new Int32Array(resultBuffer)
resultArray.set(this.intView.subarray(0, this.offset / 4));
resultArray.set(this.intView.subarray(0, this.offset / 4))
return resultArray;
};
return resultArray
}
TLSerialization.prototype.getBuffer = function () {
return this.getArray().buffer;
};
return this.getArray().buffer
}
TLSerialization.prototype.getBytes = function (typed) {
if (typed) {
var resultBuffer = new ArrayBuffer(this.offset);
var resultArray = new Uint8Array(resultBuffer);
var resultBuffer = new ArrayBuffer(this.offset)
var resultArray = new Uint8Array(resultBuffer)
resultArray.set(this.byteView.subarray(0, this.offset));
resultArray.set(this.byteView.subarray(0, this.offset))
return resultArray;
return resultArray
}
var bytes = [];
var bytes = []
for (var i = 0; i < this.offset; i++) {
bytes.push(this.byteView[i]);
bytes.push(this.byteView[i])
}
return bytes
}
return bytes;
};
TLSerialization.prototype.checkLength = function (needBytes) {
if (this.offset + needBytes < this.maxLength) {
return;
return
}
console.trace('Increase buffer', this.offset, needBytes, this.maxLength);
this.maxLength = Math.ceil(Math.max(this.maxLength * 2, this.offset + needBytes + 16) / 4) * 4;
var previousBuffer = this.buffer,
previousArray = new Int32Array(previousBuffer);
console.trace('Increase buffer', this.offset, needBytes, this.maxLength)
this.maxLength = Math.ceil(Math.max(this.maxLength * 2, this.offset + needBytes + 16) / 4) * 4
var previousBuffer = this.buffer
var previousArray = new Int32Array(previousBuffer)
this.createBuffer();
this.createBuffer()
new Int32Array(this.buffer).set(previousArray);
};
new Int32Array(this.buffer).set(previousArray)
}
TLSerialization.prototype.writeInt = function (i, field) {
this.debug && console.log('>>>', i.toString(16), i, field);
this.debug && console.log('>>>', i.toString(16), i, field)
this.checkLength(4);
this.intView[this.offset / 4] = i;
this.offset += 4;
};
this.checkLength(4)
this.intView[this.offset / 4] = i
this.offset += 4
}
TLSerialization.prototype.storeInt = function (i, field) {
this.writeInt(i, (field || '') + ':int');
};
this.writeInt(i, (field || '') + ':int')
}
TLSerialization.prototype.storeBool = function (i, field) {
if (i) {
this.writeInt(0x997275b5, (field || '') + ':bool');
this.writeInt(0x997275b5, (field || '') + ':bool')
} else {
this.writeInt(0xbc799737, (field || '') + ':bool');
this.writeInt(0xbc799737, (field || '') + ':bool')
}
}
};
TLSerialization.prototype.storeLongP = function (iHigh, iLow, field) {
this.writeInt(iLow, (field || '') + ':long[low]');
this.writeInt(iHigh, (field || '') + ':long[high]');
};
this.writeInt(iLow, (field || '') + ':long[low]')
this.writeInt(iHigh, (field || '') + ':long[high]')
}
TLSerialization.prototype.storeLong = function (sLong, field) {
if (angular.isArray(sLong)) {
if (sLong.length == 2) {
return this.storeLongP(sLong[0], sLong[1], field);
return this.storeLongP(sLong[0], sLong[1], field)
} else {
return this.storeIntBytes(sLong, 64, field);
return this.storeIntBytes(sLong, 64, field)
}
}
if (typeof sLong != 'string') {
sLong = sLong ? sLong.toString() : '0';
sLong = sLong ? sLong.toString() : '0'
}
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000));
var divRem = bigStringInt(sLong).divideAndRemainder(bigint(0x100000000))
this.writeInt(intToUint(divRem[1].intValue()), (field || '') + ':long[low]');
this.writeInt(intToUint(divRem[0].intValue()), (field || '') + ':long[high]');
};
this.writeInt(intToUint(divRem[1].intValue()), (field || '') + ':long[low]')
this.writeInt(intToUint(divRem[0].intValue()), (field || '') + ':long[high]')
}
TLSerialization.prototype.storeDouble = function (f, field) {
var buffer = new ArrayBuffer(8);
var intView = new Int32Array(buffer);
var doubleView = new Float64Array(buffer);
var buffer = new ArrayBuffer(8)
var intView = new Int32Array(buffer)
var doubleView = new Float64Array(buffer)
doubleView[0] = f;
doubleView[0] = f
this.writeInt(intView[0], (field || '') + ':double[low]');
this.writeInt(intView[1], (field || '') + ':double[high]');
};
this.writeInt(intView[0], (field || '') + ':double[low]')
this.writeInt(intView[1], (field || '') + ':double[high]')
}
TLSerialization.prototype.storeString = function (s, field) {
this.debug && console.log('>>>', s, (field || '') + ':string');
this.debug && console.log('>>>', s, (field || '') + ':string')
if (s === undefined) {
s = '';
s = ''
}
var sUTF8 = unescape(encodeURIComponent(s));
this.checkLength(sUTF8.length + 8);
var sUTF8 = unescape(encodeURIComponent(s))
this.checkLength(sUTF8.length + 8)
var len = sUTF8.length;
var len = sUTF8.length
if (len <= 253) {
this.byteView[this.offset++] = len;
this.byteView[this.offset++] = len
} else {
this.byteView[this.offset++] = 254;
this.byteView[this.offset++] = len & 0xFF;
this.byteView[this.offset++] = (len & 0xFF00) >> 8;
this.byteView[this.offset++] = (len & 0xFF0000) >> 16;
this.byteView[this.offset++] = 254
this.byteView[this.offset++] = len & 0xFF
this.byteView[this.offset++] = (len & 0xFF00) >> 8
this.byteView[this.offset++] = (len & 0xFF0000) >> 16
}
for (var i = 0; i < len; i++) {
this.byteView[this.offset++] = sUTF8.charCodeAt(i);
this.byteView[this.offset++] = sUTF8.charCodeAt(i)
}
// Padding
while (this.offset % 4) {
this.byteView[this.offset++] = 0;
this.byteView[this.offset++] = 0
}
}
TLSerialization.prototype.storeBytes = function (bytes, field) {
if (bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
bytes = new Uint8Array(bytes)
}
else if (bytes === undefined) {
bytes = [];
bytes = []
}
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':bytes');
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':bytes')
var len = bytes.byteLength || bytes.length;
this.checkLength(len + 8);
var len = bytes.byteLength || bytes.length
this.checkLength(len + 8)
if (len <= 253) {
this.byteView[this.offset++] = len;
this.byteView[this.offset++] = len
} else {
this.byteView[this.offset++] = 254;
this.byteView[this.offset++] = len & 0xFF;
this.byteView[this.offset++] = (len & 0xFF00) >> 8;
this.byteView[this.offset++] = (len & 0xFF0000) >> 16;
this.byteView[this.offset++] = 254
this.byteView[this.offset++] = len & 0xFF
this.byteView[this.offset++] = (len & 0xFF00) >> 8
this.byteView[this.offset++] = (len & 0xFF0000) >> 16
}
this.byteView.set(bytes, this.offset);
this.offset += len;
this.byteView.set(bytes, this.offset)
this.offset += len
// Padding
while (this.offset % 4) {
this.byteView[this.offset++] = 0;
this.byteView[this.offset++] = 0
}
}
TLSerialization.prototype.storeIntBytes = function (bytes, bits, field) {
if (bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
bytes = new Uint8Array(bytes)
}
var len = bytes.length;
var len = bytes.length
if ((bits % 32) || (len * 8) != bits) {
throw new Error('Invalid bits: ' + bits + ', ' + bytes.length);
throw new Error('Invalid bits: ' + bits + ', ' + bytes.length)
}
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':int' + bits);
this.checkLength(len);
this.debug && console.log('>>>', bytesToHex(bytes), (field || '') + ':int' + bits)
this.checkLength(len)
this.byteView.set(bytes, this.offset);
this.offset += len;
};
this.byteView.set(bytes, this.offset)
this.offset += len
}
TLSerialization.prototype.storeRawBytes = function (bytes, field) {
if (bytes instanceof ArrayBuffer) {
bytes = new Uint8Array(bytes);
bytes = new Uint8Array(bytes)
}
var len = bytes.length;
var len = bytes.length
this.debug && console.log('>>>', bytesToHex(bytes), (field || ''));
this.checkLength(len);
this.byteView.set(bytes, this.offset);
this.offset += len;
};
this.debug && console.log('>>>', bytesToHex(bytes), (field || ''))
this.checkLength(len)
this.byteView.set(bytes, this.offset)
this.offset += len
}
TLSerialization.prototype.storeMethod = function (methodName, params) {
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API,
methodData = false,
i;
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API
var methodData = false,
i
for (i = 0; i < schema.methods.length; i++) {
if (schema.methods[i].method == methodName) {
methodData = schema.methods[i];
methodData = schema.methods[i]
break
}
}
if (!methodData) {
throw new Error('No method ' + methodName + ' found');
throw new Error('No method ' + methodName + ' found')
}
this.storeInt(intToUint(methodData.id), methodName + '[id]');
this.storeInt(intToUint(methodData.id), methodName + '[id]')
var param, type, i, condType, fieldBit;
var len = methodData.params.length;
var param, type
var i, condType
var fieldBit
var len = methodData.params.length
for (i = 0; i < len; i++) {
param = methodData.params[i];
type = param.type;
param = methodData.params[i]
type = param.type
if (type.indexOf('?') !== -1) {
condType = type.split('?');
fieldBit = condType[0].split('.');
condType = type.split('?')
fieldBit = condType[0].split('.')
if (!(params[fieldBit[0]] & (1 << fieldBit[1]))) {
continue;
continue
}
type = condType[1];
type = condType[1]
}
this.storeObject(params[param.name], type, methodName + '[' + param.name + ']');
this.storeObject(params[param.name], type, methodName + '[' + param.name + ']')
}
return methodData.type;
};
return methodData.type
}
TLSerialization.prototype.storeObject = function (obj, type, field) {
switch (type) {
case '#':
case 'int': return this.storeInt(obj, field);
case 'long': return this.storeLong(obj, field);
case 'int128': return this.storeIntBytes(obj, 128, field);
case 'int256': return this.storeIntBytes(obj, 256, field);
case 'int512': return this.storeIntBytes(obj, 512, field);
case 'string': return this.storeString(obj, field);
case 'bytes': return this.storeBytes(obj, field);
case 'double': return this.storeDouble(obj, field);
case 'Bool': return this.storeBool(obj, field);
case 'true': return;
case 'int':
return this.storeInt(obj, field)
case 'long':
return this.storeLong(obj, field)
case 'int128':
return this.storeIntBytes(obj, 128, field)
case 'int256':
return this.storeIntBytes(obj, 256, field)
case 'int512':
return this.storeIntBytes(obj, 512, field)
case 'string':
return this.storeString(obj, field)
case 'bytes':
return this.storeBytes(obj, field)
case 'double':
return this.storeDouble(obj, field)
case 'Bool':
return this.storeBool(obj, field)
case 'true':
return
}
if (angular.isArray(obj)) {
if (type.substr(0, 6) == 'Vector') {
this.writeInt(0x1cb5c415, field + '[id]');
this.writeInt(0x1cb5c415, field + '[id]')
}
else if (type.substr(0, 6) != 'vector') {
throw new Error('Invalid vector type ' + type);
throw new Error('Invalid vector type ' + type)
}
var itemType = type.substr(7, type.length - 8); // for "Vector<itemType>"
this.writeInt(obj.length, field + '[count]');
this.writeInt(obj.length, field + '[count]')
for (var i = 0; i < obj.length; i++) {
this.storeObject(obj[i], itemType, field + '[' + i + ']');
this.storeObject(obj[i], itemType, field + '[' + i + ']')
}
return true;
return true
}
else if (type.substr(0, 6).toLowerCase() == 'vector') {
throw new Error('Invalid vector object');
throw new Error('Invalid vector object')
}
if (!angular.isObject(obj)) {
throw new Error('Invalid object for type ' + type);
throw new Error('Invalid object for type ' + type)
}
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API,
predicate = obj['_'],
isBare = false,
constructorData = false,
i;
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API
var predicate = obj['_']
var isBare = false
var constructorData = false,
i
if (isBare = (type.charAt(0) == '%')) {
type = type.substr(1);
type = type.substr(1)
}
for (i = 0; i < schema.constructors.length; i++) {
if (schema.constructors[i].predicate == predicate) {
constructorData = schema.constructors[i];
constructorData = schema.constructors[i]
break
}
}
if (!constructorData) {
throw new Error('No predicate ' + predicate + ' found');
throw new Error('No predicate ' + predicate + ' found')
}
if (predicate == type) {
isBare = true;
isBare = true
}
if (!isBare) {
this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]');
this.writeInt(intToUint(constructorData.id), field + '[' + predicate + '][id]')
}
var param, type, i, condType, fieldBit;
var len = constructorData.params.length;
var param, type
var i, condType
var fieldBit
var len = constructorData.params.length
for (i = 0; i < len; i++) {
param = constructorData.params[i];
type = param.type;
param = constructorData.params[i]
type = param.type
if (type.indexOf('?') !== -1) {
condType = type.split('?');
fieldBit = condType[0].split('.');
condType = type.split('?')
fieldBit = condType[0].split('.')
if (!(obj[fieldBit[0]] & (1 << fieldBit[1]))) {
continue;
continue
}
type = condType[1];
type = condType[1]
}
this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']');
this.storeObject(obj[param.name], type, field + '[' + predicate + '][' + param.name + ']')
}
return constructorData.type;
};
return constructorData.type
}
function TLDeserialization (buffer, options) {
options = options || {};
options = options || {}
this.offset = 0; // in bytes
this.override = options.override || {};
this.offset = 0 // in bytes
this.override = options.override || {}
this.buffer = buffer;
this.intView = new Uint32Array(this.buffer);
this.byteView = new Uint8Array(this.buffer);
this.buffer = buffer
this.intView = new Uint32Array(this.buffer)
this.byteView = new Uint8Array(this.buffer)
// this.debug = options.debug !== undefined ? options.debug : Config.Modes.debug;
this.mtproto = options.mtproto || false;
return this;
// this.debug = options.debug !== undefined ? options.debug : Config.Modes.debug
this.mtproto = options.mtproto || false
return this
}
TLDeserialization.prototype.readInt = function (field) {
if (this.offset >= this.intView.length * 4) {
throw new Error('Nothing to fetch: ' + field);
throw new Error('Nothing to fetch: ' + field)
}
var i = this.intView[this.offset / 4];
var i = this.intView[this.offset / 4]
this.debug && console.log('<<<', i.toString(16), i, field);
this.debug && console.log('<<<', i.toString(16), i, field)
this.offset += 4;
this.offset += 4
return i;
};
return i
}
TLDeserialization.prototype.fetchInt = function (field) {
return this.readInt((field || '') + ':int');
return this.readInt((field || '') + ':int')
}
TLDeserialization.prototype.fetchDouble = function (field) {
var buffer = new ArrayBuffer(8);
var intView = new Int32Array(buffer);
var doubleView = new Float64Array(buffer);
var buffer = new ArrayBuffer(8)
var intView = new Int32Array(buffer)
var doubleView = new Float64Array(buffer)
intView[0] = this.readInt((field || '') + ':double[low]'),
intView[1] = this.readInt((field || '') + ':double[high]');
intView[1] = this.readInt((field || '') + ':double[high]')
return doubleView[0];
};
return doubleView[0]
}
TLDeserialization.prototype.fetchLong = function (field) {
var iLow = this.readInt((field || '') + ':long[low]'),
iHigh = this.readInt((field || '') + ':long[high]');
var iLow = this.readInt((field || '') + ':long[low]')
var iHigh = this.readInt((field || '') + ':long[high]')
var longDec = bigint(iHigh).shiftLeft(32).add(bigint(iLow)).toString();
var longDec = bigint(iHigh).shiftLeft(32).add(bigint(iLow)).toString()
return longDec;
return longDec
}
TLDeserialization.prototype.fetchBool = function (field) {
var i = this.readInt((field || '') + ':bool');
var i = this.readInt((field || '') + ':bool')
if (i == 0x997275b5) {
return true;
return true
} else if (i == 0xbc799737) {
return false
}
this.offset -= 4;
return this.fetchObject('Object', field);
this.offset -= 4
return this.fetchObject('Object', field)
}
TLDeserialization.prototype.fetchString = function (field) {
var len = this.byteView[this.offset++];
var len = this.byteView[this.offset++]
if (len == 254) {
var len = this.byteView[this.offset++] |
(this.byteView[this.offset++] << 8) |
(this.byteView[this.offset++] << 16);
(this.byteView[this.offset++] << 16)
}
var sUTF8 = '';
var sUTF8 = ''
for (var i = 0; i < len; i++) {
sUTF8 += String.fromCharCode(this.byteView[this.offset++]);
sUTF8 += String.fromCharCode(this.byteView[this.offset++])
}
// Padding
while (this.offset % 4) {
this.offset++;
this.offset++
}
try {
var s = decodeURIComponent(escape(sUTF8));
var s = decodeURIComponent(escape(sUTF8))
} catch (e) {
var s = sUTF8;
var s = sUTF8
}
this.debug && console.log('<<<', s, (field || '') + ':string');
this.debug && console.log('<<<', s, (field || '') + ':string')
return s;
return s
}
TLDeserialization.prototype.fetchBytes = function (field) {
var len = this.byteView[this.offset++];
var len = this.byteView[this.offset++]
if (len == 254) {
var len = this.byteView[this.offset++] |
(this.byteView[this.offset++] << 8) |
(this.byteView[this.offset++] << 16);
(this.byteView[this.offset++] << 16)
}
var bytes = this.byteView.subarray(this.offset, this.offset + len);
this.offset += len;
var bytes = this.byteView.subarray(this.offset, this.offset + len)
this.offset += len
// Padding
while (this.offset % 4) {
this.offset++;
this.offset++
}
this.debug && console.log('<<<', bytesToHex(bytes), (field || '') + ':bytes');
this.debug && console.log('<<<', bytesToHex(bytes), (field || '') + ':bytes')
return bytes;
return bytes
}
TLDeserialization.prototype.fetchIntBytes = function (bits, typed, field) {
if (bits % 32) {
throw new Error('Invalid bits: ' + bits);
throw new Error('Invalid bits: ' + bits)
}
var len = bits / 8;
var len = bits / 8
if (typed) {
var result = this.byteView.subarray(this.offset, this.offset + len);
this.offset += len;
return result;
var result = this.byteView.subarray(this.offset, this.offset + len)
this.offset += len
return result
}
var bytes = [];
var bytes = []
for (var i = 0; i < len; i++) {
bytes.push(this.byteView[this.offset++]);
bytes.push(this.byteView[this.offset++])
}
this.debug && console.log('<<<', bytesToHex(bytes), (field || '') + ':int' + bits);
return bytes;
};
this.debug && console.log('<<<', bytesToHex(bytes), (field || '') + ':int' + bits)
return bytes
}
TLDeserialization.prototype.fetchRawBytes = function (len, typed, field) {
if (len === false) {
len = this.readInt((field || '') + '_length');
len = this.readInt((field || '') + '_length')
}
if (typed) {
var bytes = new Uint8Array(len);
bytes.set(this.byteView.subarray(this.offset, this.offset + len));
this.offset += len;
return bytes;
var bytes = new Uint8Array(len)
bytes.set(this.byteView.subarray(this.offset, this.offset + len))
this.offset += len
return bytes
}
var bytes = [];
var bytes = []
for (var i = 0; i < len; i++) {
bytes.push(this.byteView[this.offset++]);
bytes.push(this.byteView[this.offset++])
}
this.debug && console.log('<<<', bytesToHex(bytes), (field || ''));
this.debug && console.log('<<<', bytesToHex(bytes), (field || ''))
return bytes;
};
return bytes
}
TLDeserialization.prototype.fetchObject = function (type, field) {
switch (type) {
case '#':
case 'int': return this.fetchInt(field);
case 'long': return this.fetchLong(field);
case 'int128': return this.fetchIntBytes(128, false, field);
case 'int256': return this.fetchIntBytes(256, false, field);
case 'int512': return this.fetchIntBytes(512, false, field);
case 'string': return this.fetchString(field);
case 'bytes': return this.fetchBytes(field);
case 'double': return this.fetchDouble(field);
case 'Bool': return this.fetchBool(field);
case 'true': return true;
}
field = field || type || 'Object';
case 'int':
return this.fetchInt(field)
case 'long':
return this.fetchLong(field)
case 'int128':
return this.fetchIntBytes(128, false, field)
case 'int256':
return this.fetchIntBytes(256, false, field)
case 'int512':
return this.fetchIntBytes(512, false, field)
case 'string':
return this.fetchString(field)
case 'bytes':
return this.fetchBytes(field)
case 'double':
return this.fetchDouble(field)
case 'Bool':
return this.fetchBool(field)
case 'true':
return true
}
field = field || type || 'Object'
if (type.substr(0, 6) == 'Vector' || type.substr(0, 6) == 'vector') {
if (type.charAt(0) == 'V') {
var constructor = this.readInt(field + '[id]'),
constructorCmp = uintToInt(constructor);
var constructor = this.readInt(field + '[id]')
var constructorCmp = uintToInt(constructor)
if (constructorCmp == 0x3072cfa1) { // Gzip packed
var compressed = this.fetchBytes(field + '[packed_string]'),
uncompressed = gzipUncompress(compressed),
buffer = bytesToArrayBuffer(uncompressed),
newDeserializer = (new TLDeserialization(buffer));
var compressed = this.fetchBytes(field + '[packed_string]')
var uncompressed = gzipUncompress(compressed)
var buffer = bytesToArrayBuffer(uncompressed)
var newDeserializer = (new TLDeserialization(buffer))
return newDeserializer.fetchObject(type, field);
return newDeserializer.fetchObject(type, field)
}
if (constructorCmp != 0x1cb5c415) {
throw new Error('Invalid vector constructor ' + constructor);
throw new Error('Invalid vector constructor ' + constructor)
}
}
var len = this.readInt(field + '[count]');
var result = [];
var len = this.readInt(field + '[count]')
var result = []
if (len > 0) {
var itemType = type.substr(7, type.length - 8); // for "Vector<itemType>"
for (var i = 0; i < len; i++) {
@ -542,130 +559,131 @@ TLDeserialization.prototype.fetchObject = function (type, field) { @@ -542,130 +559,131 @@ TLDeserialization.prototype.fetchObject = function (type, field) {
}
}
return result;
return result
}
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API,
predicate = false,
constructorData = false;
var schema = this.mtproto ? Config.Schema.MTProto : Config.Schema.API
var predicate = false
var constructorData = false
if (type.charAt(0) == '%') {
var checkType = type.substr(1);
var checkType = type.substr(1)
for (var i = 0; i < schema.constructors.length; i++) {
if (schema.constructors[i].type == checkType) {
constructorData = schema.constructors[i];
constructorData = schema.constructors[i]
break
}
}
if (!constructorData) {
throw new Error('Constructor not found for type: ' + type);
throw new Error('Constructor not found for type: ' + type)
}
}
else if (type.charAt(0) >= 97 && type.charAt(0) <= 122) {
for (var i = 0; i < schema.constructors.length; i++) {
if (schema.constructors[i].predicate == type) {
constructorData = schema.constructors[i];
constructorData = schema.constructors[i]
break
}
}
if (!constructorData) {
throw new Error('Constructor not found for predicate: ' + type);
}
throw new Error('Constructor not found for predicate: ' + type)
}
else {
var constructor = this.readInt(field + '[id]'),
constructorCmp = uintToInt(constructor);
}else {
var constructor = this.readInt(field + '[id]')
var constructorCmp = uintToInt(constructor)
if (constructorCmp == 0x3072cfa1) { // Gzip packed
var compressed = this.fetchBytes(field + '[packed_string]'),
uncompressed = gzipUncompress(compressed),
buffer = bytesToArrayBuffer(uncompressed),
newDeserializer = (new TLDeserialization(buffer));
var compressed = this.fetchBytes(field + '[packed_string]')
var uncompressed = gzipUncompress(compressed)
var buffer = bytesToArrayBuffer(uncompressed)
var newDeserializer = (new TLDeserialization(buffer))
return newDeserializer.fetchObject(type, field);
return newDeserializer.fetchObject(type, field)
}
var index = schema.constructorsIndex;
var index = schema.constructorsIndex
if (!index) {
schema.constructorsIndex = index = {};
schema.constructorsIndex = index = {}
for (var i = 0; i < schema.constructors.length; i++) {
index[schema.constructors[i].id] = i;
index[schema.constructors[i].id] = i
}
}
var i = index[constructorCmp];
var i = index[constructorCmp]
if (i) {
constructorData = schema.constructors[i];
constructorData = schema.constructors[i]
}
var fallback = false;
var fallback = false
if (!constructorData && this.mtproto) {
var schemaFallback = Config.Schema.API;
var schemaFallback = Config.Schema.API
for (i = 0; i < schemaFallback.constructors.length; i++) {
if (schemaFallback.constructors[i].id == constructorCmp) {
constructorData = schemaFallback.constructors[i];
constructorData = schemaFallback.constructors[i]
delete this.mtproto;
fallback = true;
break;
delete this.mtproto
fallback = true
break
}
}
}
if (!constructorData) {
throw new Error('Constructor not found: ' + constructor +' '+ this.fetchInt()+' '+ this.fetchInt());
throw new Error('Constructor not found: ' + constructor + ' ' + this.fetchInt() + ' ' + this.fetchInt())
}
}
predicate = constructorData.predicate;
var result = {'_': predicate},
overrideKey = (this.mtproto ? 'mt_' : '') + predicate,
self = this;
predicate = constructorData.predicate
var result = {'_': predicate}
var overrideKey = (this.mtproto ? 'mt_' : '') + predicate
var self = this
if (this.override[overrideKey]) {
this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']']);
this.override[overrideKey].apply(this, [result, field + '[' + predicate + ']'])
} else {
var i, param, type, isCond, condType, fieldBit, value;
var len = constructorData.params.length;
var i, param
var type, isCond
var condType, fieldBit
var value
var len = constructorData.params.length
for (i = 0; i < len; i++) {
param = constructorData.params[i];
type = param.type;
param = constructorData.params[i]
type = param.type
if (type == '#' && result.pFlags === undefined) {
result.pFlags = {};
result.pFlags = {}
}
if (isCond = (type.indexOf('?') !== -1)) {
condType = type.split('?');
fieldBit = condType[0].split('.');
condType = type.split('?')
fieldBit = condType[0].split('.')
if (!(result[fieldBit[0]] & (1 << fieldBit[1]))) {
continue;
continue
}
type = condType[1];
type = condType[1]
}
value = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']');
value = self.fetchObject(type, field + '[' + predicate + '][' + param.name + ']')
if (isCond && type === 'true') {
result.pFlags[param.name] = value;
result.pFlags[param.name] = value
} else {
result[param.name] = value;
result[param.name] = value
}
}
}
if (fallback) {
this.mtproto = true;
this.mtproto = true
}
return result;
};
return result
}
TLDeserialization.prototype.getOffset = function () {
return this.offset;
};
return this.offset
}
TLDeserialization.prototype.fetchEnd = function () {
if (this.offset != this.byteView.length) {
throw new Error('Fetch end with non-empty buffer');
throw new Error('Fetch end with non-empty buffer')
}
return true
}
return true;
};

436
app/js/lib/utils.js

@ -5,73 +5,73 @@ @@ -5,73 +5,73 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE
*/
var _logTimer = (new Date()).getTime();
var _logTimer = (new Date()).getTime()
function dT () {
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']';
return '[' + (((new Date()).getTime() - _logTimer) / 1000).toFixed(3) + ']'
}
function checkClick (e, noprevent) {
if (e.which == 1 && (e.ctrlKey || e.metaKey) || e.which == 2) {
return true;
return true
}
if (!noprevent) {
e.preventDefault();
e.preventDefault()
}
return false;
return false
}
function isInDOM (element, parentNode) {
if (!element) {
return false;
return false
}
parentNode = parentNode || document.body;
parentNode = parentNode || document.body
if (element == parentNode) {
return true;
return true
}
return isInDOM(element.parentNode, parentNode)
}
function checkDragEvent (e) {
if (!e || e.target && (e.target.tagName == 'IMG' || e.target.tagName == 'A')) return false;
if (!e || e.target && (e.target.tagName == 'IMG' || e.target.tagName == 'A')) return false
if (e.dataTransfer && e.dataTransfer.types) {
for (var i = 0; i < e.dataTransfer.types.length; i++) {
if (e.dataTransfer.types[i] == 'Files') {
return true;
return true
}
}
} else {
return true;
return true
}
return false;
return false
}
function cancelEvent (event) {
event = event || window.event;
event = event || window.event
if (event) {
event = event.originalEvent || event;
event = event.originalEvent || event
if (event.stopPropagation) event.stopPropagation();
if (event.preventDefault) event.preventDefault();
event.returnValue = false;
event.cancelBubble = true;
if (event.stopPropagation) event.stopPropagation()
if (event.preventDefault) event.preventDefault()
event.returnValue = false
event.cancelBubble = true
}
return false;
return false
}
function hasOnlick (element) {
if (element.onclick ||
element.getAttribute('ng-click')) {
return true;
return true
}
var events = $._data(element, 'events');
var events = $._data(element, 'events')
if (events && (events.click || events.mousedown)) {
return true;
return true
}
return false;
return false
}
function getScrollWidth () {
@ -81,203 +81,204 @@ function getScrollWidth() { @@ -81,203 +81,204 @@ function getScrollWidth() {
height: 100,
overflow: 'scroll',
top: -9999
}).appendTo($(document.body));
}).appendTo($(document.body))
var scrollbarWidth = outer[0].offsetWidth - outer[0].clientWidth;
outer.remove();
var scrollbarWidth = outer[0].offsetWidth - outer[0].clientWidth
outer.remove()
return scrollbarWidth;
};
return scrollbarWidth
}
function onCtrlEnter (textarea, cb) {
$(textarea).on('keydown', function (e) {
if (e.keyCode == 13 && (e.ctrlKey || e.metaKey)) {
cb();
return cancelEvent(e);
cb()
return cancelEvent(e)
}
});
})
}
function setFieldSelection (field, from, to) {
field = $(field)[0];
field = $(field)[0]
try {
field.focus();
field.focus()
if (from === undefined || from === false) {
from = field.value.length;
from = field.value.length
}
if (to === undefined || to === false) {
to = from;
to = from
}
if (field.createTextRange) {
var range = field.createTextRange();
range.collapse(true);
range.moveEnd('character', to);
range.moveStart('character', from);
range.select();
var range = field.createTextRange()
range.collapse(true)
range.moveEnd('character', to)
range.moveStart('character', from)
range.select()
}
else if (field.setSelectionRange) {
field.setSelectionRange(from, to);
field.setSelectionRange(from, to)
}
} catch(e) {}
}
function getFieldSelection (field) {
if (field.selectionStart) {
return field.selectionStart;
return field.selectionStart
}
else if (!document.selection) {
return 0;
return 0
}
var c = "\001",
sel = document.selection.createRange(),
txt = sel.text,
dup = sel.duplicate(),
len = 0;
var c = '\x01'
var sel = document.selection.createRange()
var txt = sel.text
var dup = sel.duplicate()
var len = 0
try {
dup.moveToElementText(field);
dup.moveToElementText(field)
} catch(e) {
return 0;
return 0
}
sel.text = txt + c;
len = dup.text.indexOf(c);
sel.moveStart('character', -1);
sel.text = '';
sel.text = txt + c
len = dup.text.indexOf(c)
sel.moveStart('character', -1)
sel.text = ''
// if (browser.msie && len == -1) {
// return field.value.length;
// return field.value.length
// }
return len;
return len
}
function getRichValue (field) {
if (!field) {
return '';
return ''
}
var lines = [];
var line = [];
var lines = []
var line = []
getRichElementValue(field, lines, line);
getRichElementValue(field, lines, line)
if (line.length) {
lines.push(line.join(''));
lines.push(line.join(''))
}
var value = lines.join('\n');
value = value.replace(/\u00A0/g, ' ');
var value = lines.join('\n')
value = value.replace(/\u00A0/g, ' ')
return value;
return value
}
function getRichValueWithCaret (field) {
if (!field) {
return [];
return []
}
var lines = [];
var line = [];
var lines = []
var line = []
var sel = window.getSelection ? window.getSelection() : false;
var selNode, selOffset;
var sel = window.getSelection ? window.getSelection() : false
var selNode
var selOffset
if (sel && sel.rangeCount) {
var range = sel.getRangeAt(0);
var range = sel.getRangeAt(0)
if (range.startContainer &&
range.startContainer == range.endContainer &&
range.startOffset == range.endOffset) {
selNode = range.startContainer;
selOffset = range.startOffset;
selNode = range.startContainer
selOffset = range.startOffset
}
}
getRichElementValue(field, lines, line, selNode, selOffset);
getRichElementValue(field, lines, line, selNode, selOffset)
if (line.length) {
lines.push(line.join(''));
lines.push(line.join(''))
}
var value = lines.join('\n');
var caretPos = value.indexOf('\001');
var value = lines.join('\n')
var caretPos = value.indexOf('\x01')
if (caretPos != -1) {
value = value.substr(0, caretPos) + value.substr(caretPos + 1);
value = value.substr(0, caretPos) + value.substr(caretPos + 1)
}
value = value.replace(/\u00A0/g, ' ');
value = value.replace(/\u00A0/g, ' ')
return [value, caretPos];
return [value, caretPos]
}
function getRichElementValue (node, lines, line, selNode, selOffset) {
if (node.nodeType == 3) { // TEXT
if (selNode === node) {
var value = node.nodeValue;
line.push(value.substr(0, selOffset) + '\001' + value.substr(selOffset));
var value = node.nodeValue
line.push(value.substr(0, selOffset) + '\x01' + value.substr(selOffset))
} else {
line.push(node.nodeValue);
line.push(node.nodeValue)
}
return;
return
}
if (node.nodeType != 1) { // NON-ELEMENT
return;
return
}
var isSelected = (selNode === node);
var isBlock = node.tagName == 'DIV' || node.tagName == 'P';
var curChild;
var isSelected = (selNode === node)
var isBlock = node.tagName == 'DIV' || node.tagName == 'P'
var curChild
if (isBlock && line.length || node.tagName == 'BR') {
lines.push(line.join(''));
line.splice(0, line.length);
lines.push(line.join(''))
line.splice(0, line.length)
}
else if (node.tagName == 'IMG') {
if (node.alt) {
line.push(node.alt);
line.push(node.alt)
}
}
if (isSelected && !selOffset) {
line.push('\001');
line.push('\x01')
}
var curChild = node.firstChild;
var curChild = node.firstChild
while (curChild) {
getRichElementValue(curChild, lines, line, selNode, selOffset);
curChild = curChild.nextSibling;
getRichElementValue(curChild, lines, line, selNode, selOffset)
curChild = curChild.nextSibling
}
if (isSelected && selOffset) {
line.push('\001');
line.push('\x01')
}
if (isBlock && line.length) {
lines.push(line.join(''));
line.splice(0, line.length);
lines.push(line.join(''))
line.splice(0, line.length)
}
}
function setRichFocus (field, selectNode, noCollapse) {
field.focus();
field.focus()
if (selectNode &&
selectNode.parentNode == field &&
!selectNode.nextSibling &&
!noCollapse) {
field.removeChild(selectNode);
selectNode = null;
field.removeChild(selectNode)
selectNode = null
}
if (window.getSelection && document.createRange) {
var range = document.createRange();
var range = document.createRange()
if (selectNode) {
range.selectNode(selectNode);
range.selectNode(selectNode)
} else {
range.selectNodeContents(field);
range.selectNodeContents(field)
}
if (!noCollapse) {
range.collapse(false);
range.collapse(false)
}
var sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
else if (document.body.createTextRange !== undefined) {
var textRange = document.body.createTextRange();
textRange.moveToElementText(selectNode || field);
var textRange = document.body.createTextRange()
textRange.moveToElementText(selectNode || field)
if (!noCollapse) {
textRange.collapse(false);
textRange.collapse(false)
}
textRange.select();
textRange.select()
}
}
@ -286,24 +287,24 @@ function getSelectedText () { @@ -286,24 +287,24 @@ function getSelectedText () {
window.getSelection && window.getSelection() ||
document.getSelection && document.getSelection() ||
document.selection && document.selection.createRange().text || ''
).toString().replace(/^\s+|\s+$/g, '');
).toString().replace(/^\s+|\s+$/g, '')
return sel;
return sel
}
function scrollToNode (scrollable, node, scroller) {
var elTop = node.offsetTop - 15,
elHeight = node.offsetHeight + 30,
scrollTop = scrollable.scrollTop,
viewportHeight = scrollable.clientHeight;
var elTop = node.offsetTop - 15
var elHeight = node.offsetHeight + 30
var scrollTop = scrollable.scrollTop
var viewportHeight = scrollable.clientHeight
if (scrollTop > elTop) { // we are below the node to scroll
scrollable.scrollTop = elTop;
$(scroller).nanoScroller({flash: true});
scrollable.scrollTop = elTop
$(scroller).nanoScroller({flash: true})
}
else if (scrollTop < elTop + elHeight - viewportHeight) { // we are over the node to scroll
scrollable.scrollTop = elTop + elHeight - viewportHeight;
$(scroller).nanoScroller({flash: true});
scrollable.scrollTop = elTop + elHeight - viewportHeight
$(scroller).nanoScroller({flash: true})
}
}
@ -311,66 +312,66 @@ if (Config.Modes.animations && @@ -311,66 +312,66 @@ if (Config.Modes.animations &&
typeof window.requestAnimationFrame == 'function') {
window.onAnimationFrameCallback = function (cb) {
return (function () {
window.requestAnimationFrame(cb);
});
};
window.requestAnimationFrame(cb)
})
}
} else {
window.onAnimationFrameCallback = function (cb) {
return cb;
};
return cb
}
}
function onContentLoaded (cb) {
cb = onAnimationFrameCallback(cb);
setZeroTimeout(cb);
cb = onAnimationFrameCallback(cb)
setZeroTimeout(cb)
}
function tsNow (seconds) {
var t = +new Date() + (window.tsOffset || 0);
return seconds ? Math.floor(t / 1000) : t;
var t = +new Date() + (window.tsOffset || 0)
return seconds ? Math.floor(t / 1000) : t
}
function safeReplaceObject (wasObject, newObject) {
for (var key in wasObject) {
if (!newObject.hasOwnProperty(key) && key.charAt(0) != '$') {
delete wasObject[key];
delete wasObject[key]
}
}
for (var key in newObject) {
if (newObject.hasOwnProperty(key)) {
wasObject[key] = newObject[key];
wasObject[key] = newObject[key]
}
}
}
function listMergeSorted (list1, list2) {
list1 = list1 || [];
list2 = list2 || [];
list1 = list1 || []
list2 = list2 || []
var result = angular.copy(list1);
var result = angular.copy(list1)
var minID = list1.length ? list1[list1.length - 1] : 0xFFFFFFFF;
var minID = list1.length ? list1[list1.length - 1] : 0xFFFFFFFF
for (var i = 0; i < list2.length; i++) {
if (list2[i] < minID) {
result.push(list2[i]);
result.push(list2[i])
}
}
return result;
return result
}
function listUniqSorted (list) {
list = list || [];
var resultList = [],
prev = false;
list = list || []
var resultList = []
var prev = false
for (var i = 0; i < list.length; i++) {
if (list[i] !== prev) {
resultList.push(list[i])
}
prev = list[i];
prev = list[i]
}
return resultList;
return resultList
}
function templateUrl (tplName) {
@ -391,85 +392,77 @@ function templateUrl (tplName) { @@ -391,85 +392,77 @@ function templateUrl (tplName) {
megagroup_edit_modal: 'desktop',
inline_results: 'desktop',
composer_dropdown: 'desktop'
};
var layout = forceLayout[tplName] || (Config.Mobile ? 'mobile' : 'desktop');
return 'partials/' + layout + '/' + tplName + '.html';
}
var layout = forceLayout[tplName] || (Config.Mobile ? 'mobile' : 'desktop')
return 'partials/' + layout + '/' + tplName + '.html'
}
function encodeEntities (value) {
return value.
replace(/&/g, '&amp;').
replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function (value) {
var hi = value.charCodeAt(0);
var low = value.charCodeAt(1);
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';';
}).
replace(/([^\#-~| |!])/g, function (value) { // non-alphanumeric
return '&#' + value.charCodeAt(0) + ';';
}).
replace(/</g, '&lt;').
replace(/>/g, '&gt;');
return value.replace(/&/g, '&amp;').replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]/g, function (value) {
var hi = value.charCodeAt(0)
var low = value.charCodeAt(1)
return '&#' + (((hi - 0xD800) * 0x400) + (low - 0xDC00) + 0x10000) + ';'
}).replace(/([^\#-~| |!])/g, function (value) { // non-alphanumeric
return '&#' + value.charCodeAt(0) + ';'
}).replace(/</g, '&lt;').replace(/>/g, '&gt;')
}
function calcImageInBox (imageW, imageH, boxW, boxH, noZooom) {
var boxedImageW = boxW;
var boxedImageH = boxH;
var boxedImageW = boxW
var boxedImageH = boxH
if ((imageW / imageH) > (boxW / boxH)) {
boxedImageH = parseInt(imageH * boxW / imageW);
}
else {
boxedImageW = parseInt(imageW * boxH / imageH);
boxedImageH = parseInt(imageH * boxW / imageW)
}else {
boxedImageW = parseInt(imageW * boxH / imageH)
if (boxedImageW > boxW) {
boxedImageH = parseInt(boxedImageH * boxW / boxedImageW);
boxedImageW = boxW;
boxedImageH = parseInt(boxedImageH * boxW / boxedImageW)
boxedImageW = boxW
}
}
// if (Config.Navigator.retina) {
// imageW = Math.floor(imageW / 2);
// imageH = Math.floor(imageH / 2);
// imageW = Math.floor(imageW / 2)
// imageH = Math.floor(imageH / 2)
// }
if (noZooom && boxedImageW >= imageW && boxedImageH >= imageH) {
boxedImageW = imageW;
boxedImageH = imageH;
boxedImageW = imageW
boxedImageH = imageH
}
return {w: boxedImageW, h: boxedImageH};
return {w: boxedImageW, h: boxedImageH}
}
function versionCompare (ver1, ver2) {
if (typeof ver1 !== 'string') {
ver1 = '';
ver1 = ''
}
if (typeof ver2 !== 'string') {
ver2 = '';
ver2 = ''
}
ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.');
ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.');
ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.')
ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.')
var a = Math.max(ver1.length, ver2.length), i;
var a = Math.max(ver1.length, ver2.length), i
for (i = 0; i < a; i++) {
if (ver1[i] == ver2[i]) {
continue;
continue
}
if (ver1[i] > ver2[i]) {
return 1;
return 1
} else {
return -1;
return -1
}
}
return 0;
return 0
}
(function (global) {
var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g,
trimRe = /^\s+|\s$/g;
trimRe = /^\s+|\s$/g
function createIndex () {
return {
@ -479,90 +472,92 @@ function versionCompare (ver1, ver2) { @@ -479,90 +472,92 @@ function versionCompare (ver1, ver2) {
}
function cleanSearchText (text) {
var hasTag = text.charAt(0) == '%';
text = text.replace(badCharsRe, ' ').replace(trimRe, '');
var hasTag = text.charAt(0) == '%'
text = text.replace(badCharsRe, ' ').replace(trimRe, '')
text = text.replace(/[^A-Za-z0-9]/g, function (ch) {
return Config.LatinizeMap[ch] || ch;
});
text = text.toLowerCase();
return Config.LatinizeMap[ch] || ch
})
text = text.toLowerCase()
if (hasTag) {
text = '%' + text;
text = '%' + text
}
return text;
return text
}
function cleanUsername (username) {
return username && username.toLowerCase() || '';
return username && username.toLowerCase() || ''
}
function indexObject (id, searchText, searchIndex) {
if (searchIndex.fullTexts[id] !== undefined) {
return false;
return false
}
searchText = cleanSearchText(searchText);
searchText = cleanSearchText(searchText)
if (!searchText.length) {
return false;
return false
}
var shortIndexes = searchIndex.shortIndexes;
var shortIndexes = searchIndex.shortIndexes
searchIndex.fullTexts[id] = searchText;
searchIndex.fullTexts[id] = searchText
angular.forEach(searchText.split(' '), function (searchWord) {
var len = Math.min(searchWord.length, 3),
wordPart, i;
wordPart, i
for (i = 1; i <= len; i++) {
wordPart = searchWord.substr(0, i);
wordPart = searchWord.substr(0, i)
if (shortIndexes[wordPart] === undefined) {
shortIndexes[wordPart] = [id];
shortIndexes[wordPart] = [id]
} else {
shortIndexes[wordPart].push(id);
shortIndexes[wordPart].push(id)
}
}
});
})
}
function search (query, searchIndex) {
var shortIndexes = searchIndex.shortIndexes,
fullTexts = searchIndex.fullTexts;
var shortIndexes = searchIndex.shortIndexes
var fullTexts = searchIndex.fullTexts
query = cleanSearchText(query);
query = cleanSearchText(query)
var queryWords = query.split(' '),
foundObjs = false,
newFoundObjs, i, j, searchText, found;
var queryWords = query.split(' ')
var foundObjs = false,
newFoundObjs, i
var j, searchText
var found
for (i = 0; i < queryWords.length; i++) {
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)];
newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]
if (!newFoundObjs) {
foundObjs = [];
break;
foundObjs = []
break
}
if (foundObjs === false || foundObjs.length > newFoundObjs.length) {
foundObjs = newFoundObjs;
foundObjs = newFoundObjs
}
}
newFoundObjs = {};
newFoundObjs = {}
for (j = 0; j < foundObjs.length; j++) {
found = true;
searchText = fullTexts[foundObjs[j]];
found = true
searchText = fullTexts[foundObjs[j]]
for (i = 0; i < queryWords.length; i++) {
if (searchText.indexOf(queryWords[i]) == -1) {
found = false;
break;
found = false
break
}
}
if (found) {
newFoundObjs[foundObjs[j]] = true;
newFoundObjs[foundObjs[j]] = true
}
}
return newFoundObjs;
return newFoundObjs
}
global.SearchIndexManager = {
@ -571,6 +566,5 @@ function versionCompare (ver1, ver2) { @@ -571,6 +566,5 @@ function versionCompare (ver1, ver2) {
cleanSearchText: cleanSearchText,
cleanUsername: cleanUsername,
search: search
};
})(window);
}
})(window)

1620
app/js/message_composer.js

File diff suppressed because it is too large Load Diff

2477
app/js/messages_manager.js

File diff suppressed because it is too large Load Diff

76
app/js/offline-manager.js

@ -1,49 +1,49 @@ @@ -1,49 +1,49 @@
(function initAutoUpgrade () {
;(function initAutoUpgrade () {
// Prevent click-jacking
try {
if (window == window.top || window.chrome && chrome.app && chrome.app.window) {
document.documentElement.style.display = 'block';
document.documentElement.style.display = 'block'
} else {
top.location = self.location;
top.location = self.location
}
} catch (e) {console.error('CJ protection', e)};
} catch (e) {console.error('CJ protection', e) }
window.safeConfirm = function (params, callback) {
if (typeof params === 'string') {
params = {message: params};
params = {message: params}
}
var result = false
try {
result = confirm(params.message);
result = confirm(params.message)
} catch (e) {
result = true;
result = true
}
setTimeout(function () {callback(result)}, 10)
}
setTimeout(function () {callback(result)}, 10);
};
if ((!navigator.serviceWorker && !window.applicationCache) || Config.Modes.packed || !window.addEventListener) {
return;
return
}
var declined = false;
var declined = false
function updateFound () {
if (!declined) {
safeConfirm({type: 'WEBOGRAM_UPDATED_RELOAD', message: 'A new version of Webogram is downloaded. Launch it?'}, function (result) {
if (result) {
window.location.reload();
window.location.reload()
} else {
declined = true;
declined = true
}
});
})
}
}
if (navigator.serviceWorker) {
// If available, use a Service Worker to handle offlining.
navigator.serviceWorker.register('offline-worker.js').then(function (registration) {
console.log('offline worker registered');
console.log('offline worker registered')
registration.addEventListener('updatefound', function () {
var installingWorker = this.installing;
var installingWorker = this.installing
// Wait for the new service worker to be installed before prompting to update.
installingWorker.addEventListener('statechange', function () {
@ -52,41 +52,41 @@ @@ -52,41 +52,41 @@
// Only show the prompt if there is currently a controller so it is not
// shown on first load.
if (navigator.serviceWorker.controller) {
updateFound();
updateFound()
}
break;
break
case 'redundant':
console.error('The installing service worker became redundant.');
break;
console.error('The installing service worker became redundant.')
break
}
});
});
});
})
})
})
} else {
// Otherwise, use AppCache.
var appCache = window.applicationCache,
updateTimeout = false,
scheduleUpdate = function (delay) {
clearTimeout(updateTimeout);
var appCache = window.applicationCache
var updateTimeout = false
var scheduleUpdate = function (delay) {
clearTimeout(updateTimeout)
updateTimeout = setTimeout(function () {
try {
appCache.update();
appCache.update()
} catch (ex) {
console.log('appCache.update: ' + ex);
console.log('appCache.update: ' + ex)
}
}, delay || 300000)
}
}, delay || 300000);
};
scheduleUpdate(3000);
scheduleUpdate(3000)
window.addEventListener('load', function () {
appCache.addEventListener('updateready', function () {
if (appCache.status == appCache.UPDATEREADY) {
updateFound();
updateFound()
}
}, false);
appCache.addEventListener('noupdate', function () {scheduleUpdate()}, false);
appCache.addEventListener('error', function () {scheduleUpdate()}, false);
});
}, false)
appCache.addEventListener('noupdate', function () {scheduleUpdate()}, false)
appCache.addEventListener('error', function () {scheduleUpdate()}, false)
})
}
})();
})()

3500
app/js/services.js

File diff suppressed because it is too large Load Diff

168
gulpfile.js

@ -1,17 +1,17 @@ @@ -1,17 +1,17 @@
var gulp = require('gulp');
var es = require('event-stream');
var pj = require('./package.json');
var $ = require('gulp-load-plugins')();
var concat = require('gulp-concat');
var path = require('path');
var http = require('http');
var livereload = require('gulp-livereload');
var st = require('st');
var less = require('gulp-less');
var del = require('del');
var runSequence = require('run-sequence');
var oghliner = require('oghliner');
var gulpServiceWorker = require('gulp-serviceworker');
var gulp = require('gulp')
var es = require('event-stream')
var pj = require('./package.json')
var $ = require('gulp-load-plugins')()
var path = require('path')
var http = require('http')
var livereload = require('gulp-livereload')
var st = require('st')
var less = require('gulp-less')
var del = require('del')
var runSequence = require('run-sequence')
var oghliner = require('oghliner')
var gulpServiceWorker = require('gulp-serviceworker')
var standard = require('gulp-standard')
// The generated file is being created at src
// so it can be fetched by usemin.
@ -22,8 +22,8 @@ gulp.task('templates', function() { @@ -22,8 +22,8 @@ gulp.task('templates', function() {
module: 'myApp.templates',
standalone: true
}))
.pipe(gulp.dest('app/js'));
});
.pipe(gulp.dest('app/js'))
})
gulp.task('usemin', function () {
return gulp.src(['app/index.html', 'app/badbrowser.html'])
@ -32,28 +32,36 @@ gulp.task('usemin', function() { @@ -32,28 +32,36 @@ gulp.task('usemin', function() {
js: ['concat', $.ngAnnotate(), $.uglify({outSourceMap: false})],
css: ['concat', $.minifyCss({compatibility: true, keepBreaks: true})]
}))
.pipe(gulp.dest('dist'));
});
.pipe(gulp.dest('dist'))
})
// ulimit -n 10240 on OS X
gulp.task('imagemin', function () {
return gulp.src(['app/img/**/*', '!app/img/screenshot*', '!app/img/*.wav'])
.pipe($.imagemin())
.pipe(gulp.dest('dist/img'));
});
.pipe(gulp.dest('dist/img'))
})
gulp.task('less', function () {
gulp.src('app/less/*.less')
.pipe(less({
paths: [path.join(__dirname, 'less', 'includes')]
}))
.pipe(gulp.dest('app/css'));
});
.pipe(gulp.dest('app/css'))
})
gulp.task('standard', function () {
gulp.src(['app/**/*.js', 'gulpfile.js'])
.pipe(standard())
.pipe(standard.reporter('default', {
breakOnError: true
}))
})
gulp.task('copy-images', function () {
return gulp.src(['app/img/**/*', '!app/img/screenshot*', '!app/img/*.wav'])
.pipe(gulp.dest('dist/img'));
});
.pipe(gulp.dest('dist/img'))
})
gulp.task('copy', function () {
return es.concat(
@ -81,52 +89,52 @@ gulp.task('copy', function() { @@ -81,52 +89,52 @@ gulp.task('copy', function() {
.pipe(gulp.dest('dist/nacl/')),
gulp.src('app/js/background.js')
.pipe(gulp.dest('dist/js'))
);
});
)
})
gulp.task('copy-locales', function () {
var langpackSrc = [],
ngSrc = [];
var langpackSrc = []
var ngSrc = []
pj.locales.forEach(function (locale) {
langpackSrc.push('app/js/locales/' + locale + '.json');
ngSrc.push('app/vendor/angular/i18n/angular-locale_' + locale + '.js');
});
langpackSrc.push('app/js/locales/' + locale + '.json')
ngSrc.push('app/vendor/angular/i18n/angular-locale_' + locale + '.js')
})
return es.concat(
gulp.src(langpackSrc)
.pipe(gulp.dest('dist/js/locales/')),
gulp.src(ngSrc)
.pipe(gulp.dest('dist/vendor/angular/i18n/'))
);
});
)
})
gulp.task('compress-dist', ['build'], function () {
return gulp.src('**/*', {cwd: path.join(process.cwd(), '/dist')})
.pipe($.zip('webogram_v' + pj.version + '.zip'))
.pipe(gulp.dest('releases'));
});
.pipe(gulp.dest('releases'))
})
gulp.task('cleanup-dist', ['compress-dist'], function () {
return del(['releases/**/*', '!releases/*.zip']);
});
return del(['releases/**/*', '!releases/*.zip'])
})
gulp.task('update-version-manifests', function () {
return gulp.src(['app/manifest.webapp', 'app/manifest.json'])
.pipe($.replace(/"version": ".*",/, '"version": "' + pj.version + '",'))
.pipe(gulp.dest('app'));
});
.pipe(gulp.dest('app'))
})
gulp.task('update-version-config', function () {
return gulp.src('app/js/lib/config.js')
.pipe($.replace(/version: '.*?'/, 'version: \'' + pj.version + '\''))
.pipe(gulp.dest('app/js/lib'));
});
.pipe($.replace(/version: '.*?'/, "version: '" + pj.version + "'"))
.pipe(gulp.dest('app/js/lib'))
})
gulp.task('update-version-comments', function () {
return gulp.src('app/**/*.js')
.pipe($.replace(/Webogram v[0-9.]*/, 'Webogram v' + pj.version))
.pipe(gulp.dest('app'));
});
.pipe(gulp.dest('app'))
})
gulp.task('enable-production', function () {
return es.concat(
@ -138,8 +146,8 @@ gulp.task('enable-production', function() { @@ -138,8 +146,8 @@ gulp.task('enable-production', function() {
.pipe($.replace(/PRODUCTION_ONLY_BEGIN(\*\/)?/g, 'PRODUCTION_ONLY_BEGIN*/'))
.pipe($.replace(/(\/\*)?PRODUCTION_ONLY_END/g, '/*PRODUCTION_ONLY_END'))
.pipe(gulp.dest('app'))
);
});
)
})
gulp.task('disable-production', function () {
return es.concat(
@ -151,8 +159,8 @@ gulp.task('disable-production', function() { @@ -151,8 +159,8 @@ gulp.task('disable-production', function() {
.pipe($.replace(/PRODUCTION_ONLY_BEGIN(\*\/)?/g, 'PRODUCTION_ONLY_BEGIN'))
.pipe($.replace(/(\/\*)?PRODUCTION_ONLY_END/g, 'PRODUCTION_ONLY_END'))
.pipe(gulp.dest('app'))
);
});
)
})
var fileGlobs = [
'./dist/**/*',
@ -160,15 +168,15 @@ var fileGlobs = [ @@ -160,15 +168,15 @@ var fileGlobs = [
'!dist/*.html',
'!dist/fonts/*',
'!dist/img/icons/icon*.png',
'!dist/js/background.js',
];
'!dist/js/background.js'
]
gulp.task('generate-service-worker', ['build'], function () {
return gulp.src(fileGlobs)
.pipe(gulpServiceWorker({
rootDir: 'dist/',
}));
});
rootDir: 'dist/'
}))
})
gulp.task('add-appcache-manifest', ['build'], function () {
return gulp.src(fileGlobs)
@ -179,8 +187,8 @@ gulp.task('add-appcache-manifest', ['build'], function() { @@ -179,8 +187,8 @@ gulp.task('add-appcache-manifest', ['build'], function() {
exclude: ['webogram.appcache', 'app.manifest']
})
)
.pipe(gulp.dest('./dist'));
});
.pipe(gulp.dest('./dist'))
})
gulp.task('package-dev', function () {
return es.concat(
@ -213,39 +221,39 @@ gulp.task('package-dev', function() { @@ -213,39 +221,39 @@ gulp.task('package-dev', function() {
.pipe($.replace(/PRODUCTION_ONLY_BEGIN(\*\/)?/g, 'PRODUCTION_ONLY_BEGIN*/'))
.pipe($.replace(/(\/\*)?PRODUCTION_ONLY_END/g, '/*PRODUCTION_ONLY_END'))
.pipe(gulp.dest('dist_package'))
);
});
)
})
gulp.task('watchcss', function () {
gulp.src('app/css/*.css')
.pipe(livereload());
});
.pipe(livereload())
})
gulp.task('watchhtml', function () {
gulp.src('app/partials/**/*.html')
.pipe(livereload());
});
.pipe(livereload())
})
gulp.task('watch', ['server', 'less'], function () {
livereload.listen({ basePath: 'app' });
gulp.watch('app/css/*.css', ['watchcss']);
gulp.watch('app/less/**/*.less', ['less']);
gulp.watch('app/partials/**/*.html', ['watchhtml']);
});
livereload.listen({ basePath: 'app' })
gulp.watch('app/css/*.css', ['watchcss'])
gulp.watch('app/less/**/*.less', ['less'])
gulp.watch('app/partials/**/*.html', ['watchhtml'])
})
gulp.task('server', function (done) {
http.createServer(
st({ path: __dirname, index: 'index.html', cache: false })
).listen(8000, done);
});
).listen(8000, done)
})
gulp.task('clean', function () {
return del(['dist/*', 'app/js/templates.js', 'app/css/*', '!dist/.git']);
});
return del(['dist/*', 'app/js/templates.js', 'app/css/*', '!dist/.git'])
})
gulp.task('bump', ['update-version-manifests', 'update-version-config'], function () {
gulp.start('update-version-comments');
});
gulp.start('update-version-comments')
})
gulp.task('build', ['clean'], function (callback) {
runSequence(
@ -253,17 +261,17 @@ gulp.task('build', ['clean'], function(callback) { @@ -253,17 +261,17 @@ gulp.task('build', ['clean'], function(callback) {
'usemin',
['copy', 'copy-locales', 'copy-images', 'disable-production'],
callback
);
});
)
})
gulp.task('package', ['cleanup-dist']);
gulp.task('package', ['cleanup-dist'])
gulp.task('offline', ['add-appcache-manifest', 'generate-service-worker']);
gulp.task('offline', ['add-appcache-manifest', 'generate-service-worker'])
gulp.task('deploy', ['offline'], function () {
return oghliner.deploy({
rootDir: 'dist/',
});
});
rootDir: 'dist/'
})
})
gulp.task('default', ['build']);
gulp.task('default', ['build'])

8
package.json

@ -58,6 +58,7 @@ @@ -58,6 +58,7 @@
"gulp-replace": "^0.2.0",
"gulp-rev": "^1.1.0",
"gulp-serviceworker": "0.0.3",
"gulp-standard": "^7.0.1",
"gulp-uglify": "^1.0.2",
"gulp-usemin": "^0.3.11",
"gulp-zip": "^0.1.2",
@ -65,5 +66,12 @@ @@ -65,5 +66,12 @@
"oghliner": "^1.0.1",
"run-sequence": "^1.0.2",
"st": "^0.5.2"
},
"standard": {
"globals": [
"angular",
"$",
"chrome"
]
}
}

255
server.js

@ -1,255 +0,0 @@ @@ -1,255 +0,0 @@
#!/usr/bin/env node
var util = require('util'),
http = require('http'),
fs = require('fs'),
url = require('url'),
events = require('events');
var DEFAULT_PORT = 8000;
var DEFAULT_HOST = 'localhost';
function main(argv) {
new HttpServer({
'GET': createServlet(StaticServlet),
'HEAD': createServlet(StaticServlet)
}).start(Number(argv[2]) || DEFAULT_PORT, argv[3] || DEFAULT_HOST);
}
function escapeHtml(value) {
return value.toString().
replace('<', '&lt;').
replace('>', '&gt;').
replace('"', '&quot;');
}
function createServlet(Class) {
var servlet = new Class();
return servlet.handleRequest.bind(servlet);
}
/**
* An Http server implementation that uses a map of methods to decide
* action routing.
*
* @param {Object} Map of method => Handler function
*/
function HttpServer(handlers) {
this.handlers = handlers;
this.server = http.createServer(this.handleRequest_.bind(this));
}
HttpServer.prototype.start = function(port, host) {
this.port = port;
this.host = host;
this.server.listen(port, host);
util.puts('Http Server running at http://' + host + ':' + port + '/');
};
HttpServer.prototype.parseUrl_ = function(urlString) {
var parsed = url.parse(urlString);
parsed.pathname = url.resolve('/', parsed.pathname);
return url.parse(url.format(parsed), true);
};
HttpServer.prototype.handleRequest_ = function(req, res) {
var logEntry = req.method + ' ' + req.url;
if (req.headers['user-agent']) {
logEntry += ' ' + req.headers['user-agent'];
}
util.puts(logEntry);
req.url = this.parseUrl_(req.url);
var handler = this.handlers[req.method];
if (!handler) {
res.writeHead(501);
res.end();
} else {
handler.call(this, req, res);
}
};
/**
* Handles static content.
*/
function StaticServlet() {}
StaticServlet.MimeMap = {
'txt': 'text/plain',
'html': 'text/html',
'css': 'text/css',
'xml': 'application/xml',
'json': 'application/json',
'js': 'application/javascript',
'manifest': 'text/cache-manifest',
'appcache': 'text/cache-manifest',
'jpg': 'image/jpeg',
'jpeg': 'image/jpeg',
'gif': 'image/gif',
'png': 'image/png',
  'svg': 'image/svg+xml',
  'wav': 'audio/wav',
'ico': 'image/vnd.microsoft.icon',
'pexe': 'application/x-pnacl',
'bc': 'application/x-pnacl'
};
StaticServlet.prototype.handleRequest = function(req, res) {
var self = this;
var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){
return String.fromCharCode(parseInt(hex, 16));
});
var parts = path.split('/');
if (parts[parts.length-1].charAt(0) === '.')
return self.sendForbidden_(req, res, path);
fs.stat(path, function(err, stat) {
if (err)
return self.sendMissing_(req, res, path);
if (stat.isDirectory())
return self.sendDirectory_(req, res, path);
return self.sendFile_(req, res, path);
});
}
StaticServlet.prototype.sendError_ = function(req, res, error) {
res.writeHead(500, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>Internal Server Error</title>\n');
res.write('<h1>Internal Server Error</h1>');
res.write('<pre>' + escapeHtml(util.inspect(error)) + '</pre>');
util.puts('500 Internal Server Error');
util.puts(util.inspect(error));
};
StaticServlet.prototype.sendMissing_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(404, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>404 Not Found</title>\n');
res.write('<h1>Not Found</h1>');
res.write(
'<p>The requested URL ' +
escapeHtml(path) +
' was not found on this server.</p>'
);
res.end();
util.puts('404 Not Found: ' + path);
};
StaticServlet.prototype.sendForbidden_ = function(req, res, path) {
path = path.substring(1);
res.writeHead(403, {
'Content-Type': 'text/html'
});
res.write('<!doctype html>\n');
res.write('<title>403 Forbidden</title>\n');
res.write('<h1>Forbidden</h1>');
res.write(
'<p>You do not have permission to access ' +
escapeHtml(path) + ' on this server.</p>'
);
res.end();
util.puts('403 Forbidden: ' + path);
};
StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) {
res.writeHead(301, {
'Content-Type': 'text/html',
'Location': redirectUrl
});
res.write('<!doctype html>\n');
res.write('<title>301 Moved Permanently</title>\n');
res.write('<h1>Moved Permanently</h1>');
res.write(
'<p>The document has moved <a href="' +
redirectUrl +
'">here</a>.</p>'
);
res.end();
util.puts('301 Moved Permanently: ' + redirectUrl);
};
StaticServlet.prototype.sendFile_ = function(req, res, path) {
var self = this;
var file = fs.createReadStream(path);
res.writeHead(200, {
'Content-Type': StaticServlet.
MimeMap[path.split('.').pop()] || 'text/plain'
});
// console.log(path.split('.').pop(), StaticServlet.MimeMap[path.split('.').pop()] || 'text/plain');
if (req.method === 'HEAD') {
res.end();
} else {
file.on('data', res.write.bind(res));
file.on('close', function() {
res.end();
});
file.on('error', function(error) {
self.sendError_(req, res, error);
});
}
};
StaticServlet.prototype.sendDirectory_ = function(req, res, path) {
var self = this;
if (path.match(/[^\/]$/)) {
req.url.pathname += '/';
var redirectUrl = url.format(url.parse(url.format(req.url)));
return self.sendRedirect_(req, res, redirectUrl);
}
fs.readdir(path, function(err, files) {
if (err)
return self.sendError_(req, res, error);
if (!files.length)
return self.writeDirectoryIndex_(req, res, path, []);
var remaining = files.length;
files.forEach(function(fileName, index) {
fs.stat(path + '/' + fileName, function(err, stat) {
if (err)
return self.sendError_(req, res, err);
if (stat.isDirectory()) {
files[index] = fileName + '/';
}
if (!(--remaining))
return self.writeDirectoryIndex_(req, res, path, files);
});
});
});
};
StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) {
path = path.substring(1);
res.writeHead(200, {
'Content-Type': 'text/html'
});
if (req.method === 'HEAD') {
res.end();
return;
}
res.write('<!doctype html>\n');
res.write('<title>' + escapeHtml(path) + '</title>\n');
res.write('<style>\n');
res.write(' ol { list-style-type: none; font-size: 1.2em; }\n');
res.write('</style>\n');
res.write('<h1>Directory: ' + escapeHtml(path) + '</h1>');
res.write('<ol>');
files.forEach(function(fileName) {
if (fileName.charAt(0) !== '.') {
res.write('<li><a href="' +
escapeHtml(fileName) + '">' +
escapeHtml(fileName) + '</a></li>');
}
});
res.write('</ol>');
res.end();
};
// Must be last,
main(process.argv);
Loading…
Cancel
Save