Browse Source

Standard.js formatting

Adding linter for JS code
For now it doesn’t pass well, but that’s a start
TitanNano-voice_recorder
Igor Zhukov 9 years ago
parent
commit
636bc4a83b
  1. 2
      README.md
  2. 30
      app/js/app.js
  3. 6
      app/js/background.js
  4. 4419
      app/js/controllers.js
  5. 3193
      app/js/directives.js
  6. 394
      app/js/directives_mobile.js
  7. 194
      app/js/filters.js
  8. 101
      app/js/init.js
  9. 586
      app/js/lib/bin_utils.js
  10. 206
      app/js/lib/config.js
  11. 30
      app/js/lib/crypto_worker.js
  12. 130
      app/js/lib/i18n.js
  13. 1570
      app/js/lib/mtproto.js
  14. 759
      app/js/lib/mtproto_wrapper.js
  15. 1747
      app/js/lib/ng_utils.js
  16. 139
      app/js/lib/polyfill.js
  17. 636
      app/js/lib/tl_utils.js
  18. 456
      app/js/lib/utils.js
  19. 1640
      app/js/message_composer.js
  20. 2491
      app/js/messages_manager.js
  21. 84
      app/js/offline-manager.js
  22. 3676
      app/js/services.js
  23. 214
      gulpfile.js
  24. 8
      package.json
  25. 255
      server.js

2
README.md

@ -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) [![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 ## [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). 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 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
'use strict'; 'use strict'
var extraModules = []; var extraModules = []
if (Config.Modes.animations) { if (Config.Modes.animations) {
extraModules.push('ngAnimate'); extraModules.push('ngAnimate')
} }
// Declare app level module which depends on filters, and services // Declare app level module which depends on filters, and services
angular.module('myApp', [ angular.module('myApp', [
'ngRoute', 'ngRoute',
@ -31,23 +30,20 @@ angular.module('myApp', [
PRODUCTION_ONLY_END*/ PRODUCTION_ONLY_END*/
'myApp.directives', 'myApp.directives',
'myApp.controllers' 'myApp.controllers'
].concat(extraModules)). ].concat(extraModules)).config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function ($locationProvider, $routeProvider, $compileProvider, StorageProvider) {
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:/)
$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 /*PRODUCTION_ONLY_BEGIN
$compileProvider.debugInfoEnabled(false); $compileProvider.debugInfoEnabled(false)
PRODUCTION_ONLY_END*/ PRODUCTION_ONLY_END*/
if (Config.Modes.test) { if (Config.Modes.test) {
StorageProvider.setPrefix('t_'); StorageProvider.setPrefix('t_')
} }
$routeProvider.when('/', {templateUrl: templateUrl('welcome'), controller: 'AppWelcomeController'}); $routeProvider.when('/', {templateUrl: templateUrl('welcome'), controller: 'AppWelcomeController'})
$routeProvider.when('/login', {templateUrl: templateUrl('login'), controller: 'AppLoginController'}); $routeProvider.when('/login', {templateUrl: templateUrl('login'), controller: 'AppLoginController'})
$routeProvider.when('/im', {templateUrl: templateUrl('im'), controller: 'AppIMController', reloadOnSearch: false}); $routeProvider.when('/im', {templateUrl: templateUrl('im'), controller: 'AppIMController', reloadOnSearch: false})
$routeProvider.otherwise({redirectTo: '/'}); $routeProvider.otherwise({redirectTo: '/'})
}])
}]);

6
app/js/background.js

@ -5,7 +5,7 @@
* https://github.com/zhukov/webogram/blob/master/LICENSE * https://github.com/zhukov/webogram/blob/master/LICENSE
*/ */
chrome.app.runtime.onLaunched.addListener(function(launchData) { chrome.app.runtime.onLaunched.addListener(function (launchData) {
chrome.app.window.create('../index.html', { chrome.app.window.create('../index.html', {
id: 'webogram-chat', id: 'webogram-chat',
innerBounds: { innerBounds: {
@ -15,5 +15,5 @@ chrome.app.runtime.onLaunched.addListener(function(launchData) {
minWidth: 320, minWidth: 320,
minHeight: 400, minHeight: 400,
frame: 'chrome' frame: 'chrome'
}); })
}); })

4419
app/js/controllers.js

File diff suppressed because it is too large Load Diff

3193
app/js/directives.js

File diff suppressed because it is too large Load Diff

394
app/js/directives_mobile.js

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

194
app/js/filters.js

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

101
app/js/init.js

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

586
app/js/lib/bin_utils.js

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

206
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(
'../../vendor/closure/long.js', '../../vendor/closure/long.js',
'../../vendor/cryptoJS/crypto.js', '../../vendor/cryptoJS/crypto.js',
'../../vendor/rusha/rusha.js' '../../vendor/rusha/rusha.js'
); )
onmessage = function (e) { onmessage = function (e) {
var taskID = e.data.taskID, var taskID = e.data.taskID,
result; result
switch (e.data.task) { switch (e.data.task) {
case 'factorize': case 'factorize':
result = pqPrimeFactorization(e.data.bytes); result = pqPrimeFactorization(e.data.bytes)
break; break
case 'mod-pow': case 'mod-pow':
result = bytesModPow(e.data.x, e.data.y, e.data.m); result = bytesModPow(e.data.x, e.data.y, e.data.m)
break; break
case 'sha1-hash': case 'sha1-hash':
result = sha1HashSync(e.data.bytes); result = sha1HashSync(e.data.bytes)
break; break
case 'aes-encrypt': case 'aes-encrypt':
result = aesEncryptSync(e.data.bytes, e.data.keyBytes, e.data.ivBytes); result = aesEncryptSync(e.data.bytes, e.data.keyBytes, e.data.ivBytes)
break; break
case 'aes-decrypt': case 'aes-decrypt':
result = aesDecryptSync(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes); result = aesDecryptSync(e.data.encryptedBytes, e.data.keyBytes, e.data.ivBytes)
break; break
default: 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')

130
app/js/lib/i18n.js

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

1570
app/js/lib/mtproto.js

File diff suppressed because it is too large Load Diff

759
app/js/lib/mtproto_wrapper.js

File diff suppressed because it is too large Load Diff

1747
app/js/lib/ng_utils.js

File diff suppressed because it is too large Load Diff

139
app/js/lib/polyfill.js

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

636
app/js/lib/tl_utils.js

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

456
app/js/lib/utils.js

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

1640
app/js/message_composer.js

File diff suppressed because it is too large Load Diff

2491
app/js/messages_manager.js

File diff suppressed because it is too large Load Diff

84
app/js/offline-manager.js

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

3676
app/js/services.js

File diff suppressed because it is too large Load Diff

214
gulpfile.js

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

8
package.json

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

255
server.js

@ -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