diff --git a/app/css/app.css b/app/css/app.css index 7ac9490c..0b5b43f2 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -79,7 +79,7 @@ input[type="number"] { } .btn-success { color: #ffffff; - background-color: #6AC065; + background-color: #6ec26d; } .btn-success:hover, @@ -94,7 +94,7 @@ input[type="number"] { .btn-success:active, .btn-success.active, .open .dropdown-toggle.btn-success { - background: #5aaf54; + background: #66b864; background-image: none; } @@ -425,7 +425,7 @@ input[type="number"] { .modal-close-button i { display: inline-block; background: url(../img/icons/IconsetW.png) -15px -320px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; width: 12px; height: 12px; margin: 21px; @@ -910,7 +910,7 @@ img.welcome_logo { font-size: 12px; line-height: normal; background: #F2F2F2 url(../img/icons/IconsetW.png) -6px -205px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; border: 1px solid #F2F2F2; border-radius: 3px; padding: 6px 20px 6px 30px; @@ -938,7 +938,7 @@ img.welcome_logo { height: 13px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -15px -192px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.6; } .is_1x .im_dialogs_search_clear { @@ -1070,7 +1070,7 @@ a.im_dialog_selected .im_dialog_message_text { } .im_dialog_badge { - background: #75BB72; + background: #6ec26d; border-radius: 2px; font-size: 10px; padding: 3px 4px; @@ -1144,7 +1144,7 @@ a.im_dialog_selected .im_dialog_date { margin-left: 6px; background: url(../img/icons/IconsetW.png) -17px -444px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; } .is_1x .icon-caret { background-image: url(../img/icons/IconsetW_1x.png); @@ -1302,7 +1302,7 @@ div.im_message_video_thumb { height: 42px; background: url(../img/icons/IconsetW.png) 0 -590px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; z-index: 1; } .is_1x .icon-videoplay { @@ -1329,7 +1329,7 @@ div.im_message_video_thumb { height: 19px; background: url(../img/icons/IconsetW.png) -14px -389px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; } .is_1x .icon-geo-point { background-image: url(../img/icons/IconsetW_1x.png); @@ -1373,39 +1373,44 @@ div.im_message_video_thumb { .im_message_document, .im_message_upload_file { margin-top: 3px; - border-radius: 3px; - display: inline-block; - width: 340px; + /*border-radius: 3px;*/ + /*display: inline-block;*/ + width: 342px; } .im_message_audio { margin-top: 3px; } -.icon-document, -.icon-photo, -.icon-video { +.im_message_file_button { display: block; + background: rgba(218,228,234,0.50); float: left; - width: 38px; - height: 38px; - vertical-align: text-top; - - background: #F2F2F2 url(../img/icons/IconsetW.png) -2px -229px no-repeat; - background-size: 42px 971px; - border-radius: 3px; + width: 42px; + height: 42px; + border-radius: 0; margin-right: 10px; } -.is_1x .icon-document, -.is_1x .icon-photo, -.is_1x .icon-video { +.im_message_file_button_icon { + display: inline-block; + line-height: 0; + /*#dae4ea 50%*/ + background: url(../img/icons/IconsetW.png) -15px -953px no-repeat; + background-size: 42px 1171px; + width: 12px; + height: 20px; + margin: 11px 15px; +} +.is_1x .im_message_file_button_icon { background-image: url(../img/icons/IconsetW_1x.png); } +.im_message_file_button_dl_doc .im_message_file_button_icon { + background-position: -13px -983px; + width: 16px; + height: 18px; + margin: 12px 13px; +} .im_message_selected .icon-document, -.im_message_selected .icon-photo, -.im_message_selected .icon-video, -.im_history_selectable .im_message_outer_wrap:hover .icon-document, -.im_history_selectable .im_message_outer_wrap:hover .icon-photo, -.im_history_selectable .im_message_outer_wrap:hover .icon-video { +.im_history_selectable .im_message_outer_wrap:hover .icon-document { background-color: #dae6f0; background-position: -2px -542px; } @@ -1479,7 +1484,7 @@ img.im_message_document_thumb { width: 14px; height: 17px; background: url(../img/icons/IconsetW.png) -15px -897px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; } .is_1x .audio_player_btn_icon { background-image: url(../img/icons/IconsetW_1x.png); @@ -1759,7 +1764,7 @@ textarea.im_message_field { height: 23px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -12px -68px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.8; } .is_1x .icon-paperclip { @@ -1787,7 +1792,7 @@ textarea.im_message_field { height: 23px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -10px -4px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.8; } .is_1x .icon-emoji { @@ -1836,7 +1841,7 @@ textarea.im_message_field { height: 21px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -9px -132px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.8; } .is_1x .icon-camera { @@ -1852,7 +1857,7 @@ textarea.im_message_field { .icon-online { - background: #6DBF69; + background: #6ec26d; border: 1px solid #FFF; display: block; width: 11px; @@ -1868,13 +1873,41 @@ textarea.im_message_field { .media_modal_wrap .modal-body { padding: 19px 18px 17px; } -a.img_fullsize { +a.img_fullsize, +.img_fullsize_wrap { display: block; text-align: center; } img.img_fullsize { margin: 0 auto; } +.document_modal_image_wrap { + overflow: auto; +} +.document_fullsize_wrap { + display: none; + cursor: zoom-in; + text-align: center; +} +.document_fullsize_zoomed { + cursor: zoom-out; +} +.document_fullsize_img { + /*max-width: 100%;*/ + -webkit-user-select: none; +} +.document_fullsize_zoomed .document_fullsize_img { + /*min-width: 100%;*/ + -webkit-user-select: none; + image-rendering: optimizeSpeed; /* FUCK SMOOTHING, GIVE ME SPEED */ + image-rendering: -moz-crisp-edges; /* Firefox */ + image-rendering: -o-crisp-edges; /* Opera */ + image-rendering: -webkit-optimize-contrast; /* Chrome (and eventually Safari) */ + image-rendering: optimize-contrast; /* CSS3 Proposed */ + -ms-interpolation-mode: nearest-neighbor; /* IE8+ */ + +} + .media_modal_info { color: #999; margin: 20px 0 0; @@ -1886,8 +1919,12 @@ img.img_fullsize { margin-left: 15px; } .media_modal_author { + color: inherit; font-weight: bold; } +.media_modal_author:hover { + color: inherit; +} .non_osx .media_modal_author { font-size: 12px; } @@ -1914,7 +1951,7 @@ img.img_fullsize { overflow: auto; line-height: 17px; - border: 1px solid #d9dbde; + border: 1px solid #d2dbe3; border-radius: 2px; -webkit-box-shadow: none; box-shadow: none; @@ -2179,7 +2216,7 @@ a:hover .icon-twitter { font-size: 12px; line-height: normal; background: url(../img/icons/IconsetW.png) -6px -205px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; border: 1px solid #d9dbde; border-radius: 3px; padding: 6px 15px 6px 30px; @@ -2198,7 +2235,7 @@ a:hover .icon-twitter { height: 13px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -15px -192px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.6; } .is_1x .contacts_modal_search_clear { @@ -2314,7 +2351,7 @@ img.chat_modal_participant_photo { width: 25px; height: 25px; background: url(../img/icons/IconsetW.png) -9px -516px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.5; } .is_1x .icon-contact-tick { @@ -2395,7 +2432,7 @@ img.chat_modal_participant_photo { height: 26px; margin: 13px 0 0 40px; background: url(../img/icons/IconsetW.png) -9px -516px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; } .is_1x .icon-select-tick { background-image: url(../img/icons/IconsetW_1x.png); @@ -2514,7 +2551,7 @@ ce671b orange font-size: 12px; line-height: normal; background: #F2F2F2 url(../img/icons/IconsetW.png) -6px -205px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; border: 1px solid #F2F2F2; border-radius: 3px; padding: 6px 20px 6px 30px; @@ -2537,7 +2574,7 @@ ce671b orange height: 13px; vertical-align: text-top; background: url(../img/icons/IconsetW.png) -15px -192px no-repeat; - background-size: 42px 971px; + background-size: 42px 1171px; opacity: 0.6; } .is_1x .countries_modal_search_clear { diff --git a/app/css/desktop.css b/app/css/desktop.css index ffbcddac..4833179a 100644 --- a/app/css/desktop.css +++ b/app/css/desktop.css @@ -179,7 +179,7 @@ a.footer_lang_link.active:active { .im_history_col .nano > .nano-pane, .contacts_modal_col .nano > .nano-pane, .im_dialogs_modal_col .nano > .nano-pane { - background : rgba(3,36,64,0.08); + background : rgba(216,223,225,0.45); /*45% d8dfe5*/ width : 9px; right: 0; top: 0; @@ -218,7 +218,7 @@ a.footer_lang_link.active:active { .im_history_col .nano > .nano-pane > .nano-slider, .contacts_modal_col .nano > .nano-pane > .nano-slider, .im_dialogs_modal_col .nano > .nano-pane > .nano-slider { - background : rgba(3,46,79,0.22); + background : rgba(137,160,179,0.50); /*50% 89a0b3*/ margin: 0; -moz-border-radius : 2px; -webkit-border-radius : 2px; @@ -324,7 +324,7 @@ a.footer_lang_link.active:active { } .icon-message-status { - background: #43A4DB; + background: #6ba2cb; border: 0; display: block; width: 10px; @@ -381,6 +381,7 @@ a.footer_lang_link.active:active { font-size: 13px; line-height: 17px; min-width: 60px; + border-radius: 2px; } .im_message_selected .im_message_date, @@ -492,7 +493,7 @@ a.footer_lang_link.active:active { .im_panel_own_photo { width: 50px; height: 50px; - border-radius: 3px; + border-radius: 0; overflow: hidden; } div.im_panel_peer_photo { @@ -505,7 +506,7 @@ div.im_panel_own_photo { } .im_panel_peer_online { - background: #6DBF69; + background: #6ec26d; border: 1px solid #FFF; display: block; width: 11px; @@ -516,6 +517,10 @@ div.im_panel_own_photo { margin-top: -7px; margin-left: 43px; } +.emoji-wysiwyg-editor, +.im_message_field { + border-radius: 0; +} /* Peer modals */ .user_modal_window .modal-dialog { diff --git a/app/css/mobile.css b/app/css/mobile.css index f77916b1..c02539ac 100644 --- a/app/css/mobile.css +++ b/app/css/mobile.css @@ -535,9 +535,7 @@ img.im_message_video_thumb, color: #93a2ae; } -.im_message_out .icon-document, -.im_message_out .icon-photo, -.im_message_out .icon-video { +.im_message_out .icon-document { background-color: #dae6f0; background-position: -2px -542px; } diff --git a/app/img/icons/IconsetW.png b/app/img/icons/IconsetW.png index 3b2b0cd4..39e592bf 100644 Binary files a/app/img/icons/IconsetW.png and b/app/img/icons/IconsetW.png differ diff --git a/app/img/icons/IconsetW_1x.png b/app/img/icons/IconsetW_1x.png index f1fa3182..92d5ec04 100644 Binary files a/app/img/icons/IconsetW_1x.png and b/app/img/icons/IconsetW_1x.png differ diff --git a/app/js/controllers.js b/app/js/controllers.js index 47ab1d5e..7420b0c7 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -1765,6 +1765,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) .controller('VideoModalController', function ($scope, $rootScope, $modalInstance, PeersSelectService, AppMessagesManager, AppVideoManager, AppPeersManager, ErrorService) { + $scope.video = AppVideoManager.wrapForFull($scope.videoID); $scope.progress = {enabled: false}; @@ -1799,6 +1800,38 @@ angular.module('myApp.controllers', ['myApp.i18n']) }); }) + .controller('DocumentModalController', function ($scope, $rootScope, $modalInstance, PeersSelectService, AppMessagesManager, AppDocsManager, AppPeersManager, ErrorService) { + + $scope.document = AppDocsManager.wrapForHistory($scope.docID); + + $scope.forward = function () { + var messageID = $scope.messageID; + PeersSelectService.selectPeer({confirm_type: 'FORWARD_PEER'}).then(function (peerString) { + var peerID = AppPeersManager.getPeerID(peerString); + AppMessagesManager.forwardMessages(peerID, [messageID]).then(function () { + $rootScope.$broadcast('history_focus', {peerString: peerString}); + }); + }); + }; + + $scope['delete'] = function () { + var messageID = $scope.messageID; + ErrorService.confirm({type: 'MESSAGE_DELETE'}).then(function () { + AppMessagesManager.deleteMessages([messageID]); + }); + }; + + $scope.download = function () { + AppDocsManager.saveDocFile($scope.docID); + }; + + $scope.$on('history_delete', function (e, historyUpdate) { + if (historyUpdate.msgs[$scope.messageID]) { + $modalInstance.dismiss(); + } + }); + }) + .controller('UserModalController', function ($scope, $location, $rootScope, $modal, AppUsersManager, MtpApiManager, NotificationsManager, AppPhotosManager, AppMessagesManager, AppPeersManager, PeersSelectService, ErrorService) { var peerString = AppUsersManager.getUserString($scope.userID); diff --git a/app/js/directives.js b/app/js/directives.js index 6ded1e2f..09e9235c 100644 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -182,9 +182,25 @@ angular.module('myApp.directives', ['myApp.filters']) templateUrl: templateUrl('message_attach_video') }; }) - .directive('myMessageDocument', function() { + .directive('myMessageDocument', function(AppDocsManager) { return { - templateUrl: templateUrl('message_attach_document') + scope: { + 'document': '=myMessageDocument', + 'messageId': '=messageId' + }, + templateUrl: templateUrl('message_attach_document'), + link: function ($scope, element, attrs) { + AppDocsManager.updateDocDownloaded($scope.document.id); + $scope.docSave = function () { + AppDocsManager.saveDocFile($scope.document.id); + }; + $scope.docOpen = function () { + if (!$scope.document.withPreview) { + return $scope.download(); + } + AppDocsManager.openDoc($scope.document.id, $scope.messageId); + }; + } }; }) .directive('myMessageAudio', function() { @@ -1395,7 +1411,7 @@ angular.module('myApp.directives', ['myApp.filters']) }) - .directive('myLoadGif', function($rootScope, MtpApiFileManager) { + .directive('myLoadGif', function($rootScope, MtpApiFileManager, AppDocsManager) { return { link: link, @@ -1417,11 +1433,9 @@ angular.module('myApp.directives', ['myApp.filters']) $scope.isActive = false; $scope.document.url = MtpApiFileManager.getCachedFile(inputFileLocation); - /*return $scope.document.progress = {enabled: true, percent: 30, total: $scope.document.size};*/ - $scope.toggle = function (e) { if (checkClick(e, true)) { - $rootScope.downloadDoc($scope.document.id); + AppDocsManager.saveDocFile($scope.document.id); return false; } @@ -1464,6 +1478,102 @@ angular.module('myApp.directives', ['myApp.filters']) } }) + .directive('myLoadDocument', function($rootScope, MtpApiFileManager, AppDocsManager) { + + return { + link: link, + templateUrl: templateUrl('full_document'), + scope: { + document: '=myLoadDocument' + } + }; + + function updateModalWidth(element, width) { + while (element && !$(element).hasClass('modal-dialog')) { + element = element.parentNode; + } + if (element) { + $(element).width(width + (Config.Mobile ? 0 : 36)); + } + } + + function link ($scope, element, attrs) { + var loaderWrap = $('.document_fullsize_with_progress_wrap', element); + var fullSizeWrap = $('.document_fullsize_wrap', element); + var fullSizeImage = $('.document_fullsize_img', element); + + var fullWidth = $(window).width() - (Config.Mobile ? 20 : 36); + var fullHeight = $(window).height() - 150; + + $scope.imageWidth = fullWidth; + $scope.imageHeight = fullHeight; + + var thumbPhotoSize = $scope.document.thumb; + + if (thumbPhotoSize && thumbPhotoSize._ != 'photoSizeEmpty') { + var wh = calcImageInBox(thumbPhotoSize.width, thumbPhotoSize.height, fullWidth, fullHeight); + $scope.imageWidth = wh.w; + $scope.imageHeight = wh.h; + + $scope.thumbSrc = MtpApiFileManager.getCachedFile(thumbPhotoSize.location); + } + + $scope.frameWidth = Math.max($scope.imageWidth, Math.min(600, fullWidth)) + $scope.frameHeight = $scope.imageHeight; + + onContentLoaded(function () { + $scope.$emit('ui_height'); + }); + + updateModalWidth(element[0], $scope.frameWidth); + + var checkSizesInt; + var realImageWidth, realImageHeight; + AppDocsManager.downloadDoc($scope.document.id).then(function (url) { + var image = new Image(); + var checkSizes = function (e) { + if (!image.height || !image.width) { + return; + } + realImageWidth = image.width; + realImageHeight = image.height; + clearInterval(checkSizesInt); + + var defaultWh = calcImageInBox(image.width, image.height, fullWidth, fullHeight, true); + var zoomedWh = {w: realImageWidth, h: realImageHeight}; + if (defaultWh.w >= zoomedWh.w && defaultWh.h >= zoomedWh.h) { + zoomedWh.w *= 4; + zoomedWh.h *= 4; + } + + var zoomed = true; + $scope.toggleZoom = function () { + zoomed = !zoomed; + var imageWidth = (zoomed ? zoomedWh : defaultWh).w; + var imageHeight = (zoomed ? zoomedWh : defaultWh).h; + fullSizeImage.css({ + width: imageWidth, + height: imageHeight, + marginTop: $scope.frameHeight > imageHeight ? Math.floor(($scope.frameHeight - imageHeight) / 2) : 0 + }); + fullSizeWrap.toggleClass('document_fullsize_zoomed', zoomed); + }; + + $scope.toggleZoom(false); + + fullSizeImage.attr('src', url); + loaderWrap.hide(); + fullSizeWrap.css({width: $scope.frameWidth, height: $scope.frameHeight}).show(); + + }; + checkSizesInt = setInterval(checkSizes, 20); + image.onload = checkSizes; + image.src = url; + setZeroTimeout(checkSizes); + }); + } + }) + .directive('myMapPoint', function(ExternalResourcesManager) { return { @@ -1730,8 +1840,8 @@ angular.module('myApp.directives', ['myApp.filters']) var updateMargin = function () { var height = element[0].offsetHeight, fullHeight = height - (height && usePadding ? 2 * prevMargin : 0), - contHeight = $($window).height(), ratio = attrs.myVerticalPosition && parseFloat(attrs.myVerticalPosition) || 0.5, + contHeight = attrs.contHeight ? $scope.$eval(attrs.contHeight) : $($window).height(), margin = fullHeight < contHeight ? parseInt((contHeight - fullHeight) * ratio) : '', styles = usePadding ? {paddingTop: margin, paddingBottom: margin} @@ -1747,9 +1857,10 @@ angular.module('myApp.directives', ['myApp.filters']) prevMargin = margin; }; + $($window).on('resize', updateMargin); + onContentLoaded(updateMargin); - $($window).on('resize', updateMargin); $scope.$on('ui_height', function () { onContentLoaded(updateMargin); diff --git a/app/js/i18n.js b/app/js/i18n.js index 4e02f554..197fc73a 100644 --- a/app/js/i18n.js +++ b/app/js/i18n.js @@ -21,16 +21,6 @@ angular.module('myApp.i18n', ['izhukov.utils']) }); } - function encodeEntities(value) { - return value. - replace(/&/g, '&'). - replace(/([^\#-~| |!\n\*])/g, function (value) { // non-alphanumeric - return '&#' + value.charCodeAt(0) + ';'; - }). - replace(//g, '>'); - } - function parseMarkdownString(msgstr, msgid) { msgstr = msgstr.replace(/\*\*(.+?)\*\*/g, "$1") .replace(/\n/g, "
"); diff --git a/app/js/lib/mtproto_wrapper.js b/app/js/lib/mtproto_wrapper.js index bf6ca356..951e8185 100644 --- a/app/js/lib/mtproto_wrapper.js +++ b/app/js/lib/mtproto_wrapper.js @@ -383,6 +383,13 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) }); } + function getDownloadedFile(location, size) { + var fileStorage = getFileStorage(), + fileName = getFileName(location); + + return fileStorage.getFile(fileName, size); + } + function downloadFile (dcID, location, size, options) { if (!FileManager.isAvailable()) { return $q.reject({type: 'BROWSER_BLOB_NOT_SUPPORTED'}); @@ -598,6 +605,7 @@ angular.module('izhukov.mtproto.wrapper', ['izhukov.utils', 'izhukov.mtproto']) return { getCachedFile: getCachedFile, + getDownloadedFile: getDownloadedFile, downloadFile: downloadFile, downloadSmallFile: downloadSmallFile, saveSmallFile: saveSmallFile, diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index 5e7c81a9..61e17733 100644 --- a/app/js/lib/ng_utils.js +++ b/app/js/lib/ng_utils.js @@ -33,7 +33,7 @@ angular.module('izhukov.utils', []) }) -.service('FileManager', function ($window, $q) { +.service('FileManager', function ($window, $q, $timeout) { $window.URL = $window.URL || $window.webkitURL; $window.BlobBuilder = $window.BlobBuilder || $window.WebKitBlobBuilder || $window.MozBlobBuilder; @@ -610,6 +610,186 @@ angular.module('izhukov.utils', []) }; }) +.service('SearchIndexManager', function () { + var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, + trimRe = /^\s+|\s$/g, + accentsReplace = { + a: /[åáâäà]/g, + e: /[éêëè]/g, + i: /[íîïì]/g, + o: /[óôöò]/g, + u: /[úûüù]/g, + c: /ç/g, + ss: /ß/g + } + + return { + createIndex: createIndex, + indexObject: indexObject, + cleanSearchText: cleanSearchText, + search: search + }; + + function createIndex () { + return { + shortIndexes: {}, + fullTexts: {} + } + } + + function cleanSearchText (text) { + text = text.replace(badCharsRe, ' ').replace(trimRe, '').toLowerCase(); + + for (var key in accentsReplace) { + if (accentsReplace.hasOwnProperty(key)) { + text = text.replace(accentsReplace[key], key); + } + } + + return text; + } + + function indexObject (id, searchText, searchIndex) { + if (searchIndex.fullTexts[id] !== undefined) { + return false; + } + + searchText = cleanSearchText(searchText); + + if (!searchText.length) { + return false; + } + + var shortIndexes = searchIndex.shortIndexes; + + searchIndex.fullTexts[id] = searchText; + + angular.forEach(searchText.split(' '), function(searchWord) { + var len = Math.min(searchWord.length, 3), + wordPart, i; + for (i = 1; i <= len; i++) { + wordPart = searchWord.substr(0, i); + if (shortIndexes[wordPart] === undefined) { + shortIndexes[wordPart] = [id]; + } else { + shortIndexes[wordPart].push(id); + } + } + }); + } + + function search (query, searchIndex) { + var shortIndexes = searchIndex.shortIndexes, + fullTexts = searchIndex.fullTexts; + + query = cleanSearchText(query); + + var queryWords = query.split(' '), + foundObjs = false, + newFoundObjs, i, j, searchText, found; + + for (i = 0; i < queryWords.length; i++) { + newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; + if (!newFoundObjs) { + foundObjs = []; + break; + } + if (foundObjs === false || foundObjs.length > newFoundObjs.length) { + foundObjs = newFoundObjs; + } + } + + newFoundObjs = {}; + + for (j = 0; j < foundObjs.length; j++) { + found = true; + searchText = fullTexts[foundObjs[j]]; + for (i = 0; i < queryWords.length; i++) { + if (searchText.indexOf(queryWords[i]) == -1) { + found = false; + break; + } + } + if (found) { + newFoundObjs[foundObjs[j]] = true; + } + } + + return newFoundObjs; + } +}) + +.service('ExternalResourcesManager', function ($q, $http) { + var urlPromises = {}; + + function downloadImage (url) { + if (urlPromises[url] !== undefined) { + return urlPromises[url]; + } + + return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null}) + .then(function (response) { + window.URL = window.URL || window.webkitURL; + return window.URL.createObjectURL(response.data); + }); + } + + return { + downloadImage: downloadImage + } +}) + +.service('IdleManager', function ($rootScope, $window, $timeout) { + + $rootScope.idle = {isIDLE: false}; + + var toPromise, started = false; + + return { + start: start + }; + + function start () { + if (!started) { + started = true; + $($window).on('blur focus keydown mousedown touchstart', onEvent); + + setTimeout(function () { + onEvent({type: 'blur'}); + }, 0); + } + } + + function onEvent (e) { + // console.log('event', e.type); + if (e.type == 'mousemove') { + $($window).off('mousemove', onEvent); + } + var isIDLE = e.type == 'blur' || e.type == 'timeout' ? true : false; + + $timeout.cancel(toPromise); + if (!isIDLE) { + // console.log('update timeout'); + toPromise = $timeout(function () { + onEvent({type: 'timeout'}); + }, 30000); + } + + if ($rootScope.idle.isIDLE == isIDLE) { + return; + } + + // console.log('IDLE changed', isIDLE); + $rootScope.$apply(function () { + $rootScope.idle.isIDLE = isIDLE; + }); + + if (isIDLE && e.type == 'timeout') { + $($window).on('mousemove', onEvent); + } + } +}) + .service('AppRuntimeManager', function ($window) { return { diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index 46cf9624..a86851bc 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -114,3 +114,35 @@ function templateUrl (tplName) { return 'partials/' + (Config.Mobile ? 'mobile' : 'desktop') + '/' + tplName + '.html'; } +function encodeEntities(value) { + return value. + replace(/&/g, '&'). + replace(/([^\#-~| |!])/g, function (value) { // non-alphanumeric + return '&#' + value.charCodeAt(0) + ';'; + }). + replace(//g, '>'); +} + +function calcImageInBox(imageW, imageH, boxW, boxH, noZooom) { + var boxedImageW = boxW; + var boxedImageH = boxH; + + if ((imageW / imageH) > (boxW / boxH)) { + boxedImageH = parseInt(imageH * boxW / imageW); + } + else { + boxedImageW = parseInt(imageW * boxH / imageH); + if (boxedImageW > boxW) { + boxedImageH = parseInt(boxedImageH * boxW / boxedImageW); + boxedImageW = boxW; + } + } + + if (noZooom && boxedImageW >= imageW && boxedImageH >= imageH) { + boxedImageW = imageW; + boxedImageH = imageH; + } + + return {w: boxedImageW, h: boxedImageH}; +} diff --git a/app/js/locales/en-us.json b/app/js/locales/en-us.json index 64c0ca8d..b8dbef04 100644 --- a/app/js/locales/en-us.json +++ b/app/js/locales/en-us.json @@ -325,6 +325,7 @@ "message_attach_document_open": "Open", "message_attach_document_download": "Download", + "message_attach_document_save": "Save File", "message_attach_video_video": "Video", "message_attach_video_download": "Download", diff --git a/app/js/services.js b/app/js/services.js index 8a6b6602..fc3de5fd 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -9,7 +9,7 @@ /* Services */ -angular.module('myApp.services', ['myApp.i18n']) +angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) .service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager, ErrorService, Storage, _) { var users = {}, @@ -606,115 +606,6 @@ angular.module('myApp.services', ['myApp.i18n']) } }) -.service('SearchIndexManager', function () { - var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, - trimRe = /^\s+|\s$/g, - accentsReplace = { - a: /[åáâäà]/g, - e: /[éêëè]/g, - i: /[íîïì]/g, - o: /[óôöò]/g, - u: /[úûüù]/g, - c: /ç/g, - ss: /ß/g - } - - return { - createIndex: createIndex, - indexObject: indexObject, - cleanSearchText: cleanSearchText, - search: search - }; - - function createIndex () { - return { - shortIndexes: {}, - fullTexts: {} - } - } - - function cleanSearchText (text) { - text = text.replace(badCharsRe, ' ').replace(trimRe, '').toLowerCase(); - - for (var key in accentsReplace) { - if (accentsReplace.hasOwnProperty(key)) { - text = text.replace(accentsReplace[key], key); - } - } - - return text; - } - - function indexObject (id, searchText, searchIndex) { - if (searchIndex.fullTexts[id] !== undefined) { - return false; - } - - searchText = cleanSearchText(searchText); - - if (!searchText.length) { - return false; - } - - var shortIndexes = searchIndex.shortIndexes; - - searchIndex.fullTexts[id] = searchText; - - angular.forEach(searchText.split(' '), function(searchWord) { - var len = Math.min(searchWord.length, 3), - wordPart, i; - for (i = 1; i <= len; i++) { - wordPart = searchWord.substr(0, i); - if (shortIndexes[wordPart] === undefined) { - shortIndexes[wordPart] = [id]; - } else { - shortIndexes[wordPart].push(id); - } - } - }); - } - - function search (query, searchIndex) { - var shortIndexes = searchIndex.shortIndexes, - fullTexts = searchIndex.fullTexts; - - query = cleanSearchText(query); - - var queryWords = query.split(' '), - foundObjs = false, - newFoundObjs, i, j, searchText, found; - - for (i = 0; i < queryWords.length; i++) { - newFoundObjs = shortIndexes[queryWords[i].substr(0, 3)]; - if (!newFoundObjs) { - foundObjs = []; - break; - } - if (foundObjs === false || foundObjs.length > newFoundObjs.length) { - foundObjs = newFoundObjs; - } - } - - newFoundObjs = {}; - - for (j = 0; j < foundObjs.length; j++) { - found = true; - searchText = fullTexts[foundObjs[j]]; - for (i = 0; i < queryWords.length; i++) { - if (searchText.indexOf(queryWords[i]) == -1) { - found = false; - break; - } - } - if (found) { - newFoundObjs[foundObjs[j]] = true; - } - } - - return newFoundObjs; - } -}) - .service('AppMessagesManager', function ($q, $rootScope, $location, $filter, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager, PeersSelectService,Storage, _) { var messagesStorage = {}; @@ -2389,21 +2280,9 @@ angular.module('myApp.services', ['myApp.i18n']) full.height = fullHeight; if (fullPhotoSize && fullPhotoSize._ != 'photoSizeEmpty') { - if ((fullPhotoSize.w / fullPhotoSize.h) > (fullWidth / fullHeight)) { - full.height = parseInt(fullPhotoSize.h * fullWidth / fullPhotoSize.w); - } - else { - full.width = parseInt(fullPhotoSize.w * fullHeight / fullPhotoSize.h); - if (full.width > fullWidth) { - full.height = parseInt(full.height * fullWidth / full.width); - full.width = fullWidth; - } - } - - if (!Config.Mobile && full.width >= fullPhotoSize.w && full.height >= fullPhotoSize.h) { - full.width = fullPhotoSize.w; - full.height = fullPhotoSize.h; - } + var wh = calcImageInBox(fullPhotoSize.w, fullPhotoSize.h, fullWidth, fullHeight, Config.Mobile); + full.width = wh.w; + full.height = wh.h; full.modalWidth = Math.max(full.width, Math.min(400, fullWidth)); @@ -2464,7 +2343,7 @@ angular.module('myApp.services', ['myApp.i18n']) mime: mimeType, toFileEntry: writableFileEntry }).then(function (url) { - console.log('file save done'); + // console.log('file save done'); }, function (e) { console.log('photo download failed', e); }); @@ -2563,16 +2442,10 @@ angular.module('myApp.services', ['myApp.i18n']) if (!video.w || !video.h) { full.height = full.width = Math.min(fullWidth, fullHeight); - } - else if (video.w > video.h) { - full.height = parseInt(video.h * fullWidth / video.w); - } - else { - full.width = parseInt(video.w * fullHeight / video.h); - if (full.width > fullWidth) { - full.height = parseInt(full.height * fullWidth / full.width); - full.width = fullWidth; - } + } else { + var wh = calcImageInBox(video.w, video.h, fullWidth, fullHeight); + full.width = wh.w; + full.height = wh.h; } video.full = full; @@ -2669,7 +2542,7 @@ angular.module('myApp.services', ['myApp.i18n']) } }) -.service('AppDocsManager', function ($rootScope, $modal, $window, $timeout, MtpApiFileManager, FileManager) { +.service('AppDocsManager', function ($rootScope, $modal, $window, $timeout, $q, MtpApiFileManager, FileManager) { var docs = {}, docsForHistory = {}, windowW = $(window).width(), @@ -2737,7 +2610,7 @@ angular.module('myApp.services', ['myApp.i18n']) return docsForHistory[docID] = doc; } - function downloadDoc (docID, action) { + function updateDocDownloaded (docID) { var doc = docs[docID], historyDoc = docsForHistory[docID] || doc || {}, inputFileLocation = { @@ -2746,68 +2619,90 @@ angular.module('myApp.services', ['myApp.i18n']) access_hash: doc.access_hash }; - function updateDownloadProgress (progress) { + if (historyDoc.downloaded === undefined) { + MtpApiFileManager.getDownloadedFile(inputFileLocation, doc.size).then(function () { + historyDoc.downloaded = true; + }, function () { + historyDoc.downloaded = false; + }); + } + } + + function downloadDoc (docID, toFileEntry) { + var doc = docs[docID], + historyDoc = docsForHistory[docID] || doc || {}, + inputFileLocation = { + _: 'inputDocumentFileLocation', + id: docID, + access_hash: doc.access_hash + }; + + historyDoc.progress = {enabled: true, percent: 1, total: doc.size}; + + var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, { + mime: doc.mime_type, + toFileEntry: toFileEntry + }); + + downloadPromise.then(function (url) { + delete historyDoc.progress; + historyDoc.url = url; + historyDoc.downloaded = true; + console.log('file save done'); + }, function (e) { + console.log('document download failed', e); + historyDoc.progress.enabled = false; + }, function (progress) { console.log('dl progress', progress); historyDoc.progress.done = progress.done; historyDoc.progress.percent = Math.max(1, Math.floor(100 * progress.done / progress.total)); $rootScope.$broadcast('history_update'); - } + }); + + historyDoc.progress.cancel = downloadPromise.cancel; + + return downloadPromise; + } + + function openDoc (docID, messageID) { + var scope = $rootScope.$new(true); + scope.docID = docID; + scope.messageID = messageID; + + var modalInstance = $modal.open({ + templateUrl: templateUrl('document_modal'), + controller: 'DocumentModalController', + scope: scope, + windowClass: 'document_modal_window' + }); + } + + function saveDocFile (docID) { + var doc = docs[docID], + historyDoc = docsForHistory[docID] || doc || {}; var ext = (doc.file_name.split('.', 2) || [])[1] || ''; FileManager.chooseSave(doc.file_name, ext, doc.mime_type).then(function (writableFileEntry) { if (!writableFileEntry) { return; } - - historyDoc.progress = {enabled: true, percent: 1, total: doc.size}; - - var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, { - mime: doc.mime_type, - toFileEntry: writableFileEntry - }); - - downloadPromise.then(function (url) { - delete historyDoc.progress; + downloadDoc(docID, writableFileEntry).then(function () { console.log('file save done'); - }, function (e) { - console.log('document download failed', e); - historyDoc.progress.enabled = false; - }, updateDownloadProgress); - - historyDoc.progress.cancel = downloadPromise.cancel; + }); }, function () { - historyDoc.progress = {enabled: true, percent: 1, total: doc.size}; - - var downloadPromise = MtpApiFileManager.downloadFile(doc.dc_id, inputFileLocation, doc.size, {mime: doc.mime_type}); - - downloadPromise.then(function (url) { - delete historyDoc.progress; - - historyDoc.url = url; - - switch (action) { - case 1: - window.open(url, '_blank'); - break; - - default: - FileManager.download(url, doc.mime_type, doc.file_name); - } - }, function (e) { - console.log('document download failed', e); - historyDoc.progress.enabled = false; - }, updateDownloadProgress); - - historyDoc.progress.cancel = downloadPromise.cancel; + downloadDoc(docID).then(function (url) { + FileManager.download(url, doc.mime_type, doc.file_name); + }); }); } - $rootScope.downloadDoc = downloadDoc; - return { saveDoc: saveDoc, wrapForHistory: wrapForHistory, - downloadDoc: downloadDoc + updateDocDownloaded: updateDocDownloaded, + downloadDoc: downloadDoc, + openDoc: openDoc, + saveDocFile: saveDocFile } }) @@ -2877,27 +2772,6 @@ angular.module('myApp.services', ['myApp.i18n']) } }) -.service('ExternalResourcesManager', function ($q, $http) { - var urlPromises = {}; - - function downloadImage (url) { - if (urlPromises[url] !== undefined) { - return urlPromises[url]; - } - - return urlPromises[url] = $http.get(url, {responseType: 'blob', transformRequest: null}) - .then(function (response) { - window.URL = window.URL || window.webkitURL; - return window.URL.createObjectURL(response.data); - }); - } - - return { - downloadImage: downloadImage - } -}) - - .service('ApiUpdatesManager', function ($rootScope, MtpNetworkerFactory, AppUsersManager, AppChatsManager, AppPeersManager, MtpApiManager) { var isSynchronizing = true, @@ -3193,16 +3067,6 @@ angular.module('myApp.services', ['myApp.i18n']) wrapPlainText: wrapPlainText }; - function encodeEntities(value) { - return value. - replace(/&/g, '&'). - replace(/([^\#-~| |!])/g, function (value) { // non-alphanumeric - return '&#' + value.charCodeAt(0) + ';'; - }). - replace(//g, '>'); - } - function getEmojiSpritesheetCoords(emojiCode) { var i, row, column, totalColumns; for (var cat = 0; cat < Config.EmojiCategories.length; cat++) { @@ -3377,58 +3241,6 @@ angular.module('myApp.services', ['myApp.i18n']) }) - -.service('IdleManager', function ($rootScope, $window, $timeout) { - - $rootScope.idle = {isIDLE: false}; - - var toPromise, started = false; - - return { - start: start - }; - - function start () { - if (!started) { - started = true; - $($window).on('blur focus keydown mousedown touchstart', onEvent); - - setTimeout(function () { - onEvent({type: 'blur'}); - }, 0); - } - } - - function onEvent (e) { - // console.log('event', e.type); - if (e.type == 'mousemove') { - $($window).off('mousemove', onEvent); - } - var isIDLE = e.type == 'blur' || e.type == 'timeout' ? true : false; - - $timeout.cancel(toPromise); - if (!isIDLE) { - // console.log('update timeout'); - toPromise = $timeout(function () { - onEvent({type: 'timeout'}); - }, 30000); - } - - if ($rootScope.idle.isIDLE == isIDLE) { - return; - } - - // console.log('IDLE changed', isIDLE); - $rootScope.$apply(function () { - $rootScope.idle.isIDLE = isIDLE; - }); - - if (isIDLE && e.type == 'timeout') { - $($window).on('mousemove', onEvent); - } - } -}) - .service('StatusManager', function ($timeout, $rootScope, MtpApiManager, IdleManager) { var toPromise, lastOnlineUpdated = 0, started = false; diff --git a/app/partials/desktop/document_modal.html b/app/partials/desktop/document_modal.html new file mode 100644 index 00000000..0a0e494f --- /dev/null +++ b/app/partials/desktop/document_modal.html @@ -0,0 +1,21 @@ +
+ + + +
diff --git a/app/partials/desktop/full_document.html b/app/partials/desktop/full_document.html new file mode 100644 index 00000000..765e69ce --- /dev/null +++ b/app/partials/desktop/full_document.html @@ -0,0 +1,21 @@ +
+
+
+
+
+
+
+
+
+
+ +
+
+
+ +
+
\ No newline at end of file diff --git a/app/partials/desktop/message.html b/app/partials/desktop/message.html index 059e09ad..c854165c 100644 --- a/app/partials/desktop/message.html +++ b/app/partials/desktop/message.html @@ -52,7 +52,7 @@
-
+
diff --git a/app/partials/desktop/message_attach_document.html b/app/partials/desktop/message_attach_document.html index 888626ca..bf539c6a 100644 --- a/app/partials/desktop/message_attach_document.html +++ b/app/partials/desktop/message_attach_document.html @@ -1,39 +1,42 @@ -
+
-
+
-
+
-
+
- - -
+ + + + +
- - - + + +
-
- - +
+ + +
-
- +
+
-
+
diff --git a/app/partials/desktop/photo_modal.html b/app/partials/desktop/photo_modal.html index 13ce2fd0..d51a35f3 100644 --- a/app/partials/desktop/photo_modal.html +++ b/app/partials/desktop/photo_modal.html @@ -12,7 +12,7 @@

- , + ,

diff --git a/app/partials/desktop/video_modal.html b/app/partials/desktop/video_modal.html index 62b11703..01e75d60 100644 --- a/app/partials/desktop/video_modal.html +++ b/app/partials/desktop/video_modal.html @@ -12,7 +12,7 @@

- , + ,