diff --git a/app/css/app.css b/app/css/app.css index a65e8e92..b7a22ed3 100644 --- a/app/css/app.css +++ b/app/css/app.css @@ -771,7 +771,7 @@ a.tg_radio_on:hover i.icon-radio { display: inline-block; vertical-align: top; background: url(../img/icons/General.png) no-repeat -5px -10px; - background-size: 40px 678px; + background-size: 40px 778px; margin-right: 18px; } .icon-tg-title { @@ -809,7 +809,7 @@ a.tg_radio_on:hover i.icon-radio { display: inline-block; vertical-align: middle; background: url(../img/icons/General.png) no-repeat -18px -50px; - background-size: 40px 678px; + background-size: 40px 778px; margin-left: 12px; margin-top: -1px; } @@ -1453,7 +1453,7 @@ div.im_message_video_thumb { display: inline-block; line-height: 0; background: url(../img/icons/General.png) no-repeat -14px -509px; - background-size: 40px 678px; + background-size: 40px 778px; width: 12px; height: 18px; margin: 12px 15px; @@ -1476,7 +1476,7 @@ div.im_message_video_thumb { width: 14px; height: 16px; background: url(../img/icons/General.png) no-repeat -13px -611px; - background-size: 40px 678px;; + background-size: 40px 778px;; margin: 13px 16px; } .is_1x .im_message_file_button_dl_audio .im_message_file_button_icon { @@ -1775,42 +1775,6 @@ a.im_message_fwd_photo { line-height: 150%; } -span.emoji { - display: -moz-inline-box; - -moz-box-orient: vertical; - display: inline-block; - vertical-align: baseline; - *vertical-align: auto; - *zoom: 1; - *display: inline; - height: 18px; - width: 18px; - background-repeat: no-repeat; - text-indent: -9999px; -} - -/* widths and heights calculated according to spritesheet dimensions and icon size */ -.emoji-spritesheet-0 { - background-size: 486px 126px; - background-image: url('../img/emojisprite_0.png'); -} -.emoji-spritesheet-1 { - background-size: 522px 72px; - background-image: url('../img/emojisprite_1.png'); -} -.emoji-spritesheet-2 { - background-size: 594px 126px; - background-image: url('../img/emojisprite_2.png'); -} -.emoji-spritesheet-3 { - background-size: 612px 54px; - background-image: url('../img/emojisprite_3.png'); -} -.emoji-spritesheet-4 { - background-size: 612px 126px; - background-image: url('../img/emojisprite_4.png'); -} - .im_history_not_selected, .im_history_empty { visibility: hidden; @@ -1995,116 +1959,189 @@ img.img_fullsize { vertical-align: middle; margin: -3px 0 0 0; } -.emoji-menu { + + + +.composer_emoji_insert_btn { + display: block; + position: absolute; + right: 3px; + top: 2px; + cursor: pointer; + padding: 0; + + width: 22px; + height: 22px; + margin-top: 1px; +} +.icon-emoji { + display: inline-block; + width: 22px; + height: 22px; + vertical-align: top; + background: url(../img/icons/General.png) no-repeat -9px -335px; + background-size: 40px 778px; + opacity: 0.8; +} +.composer_emoji_tooltip { + display: none; position: absolute; z-index: 999; width: 220px; - margin-left: -107px; - margin-top: -252px; - overflow: hidden; + margin-left: -100px; + margin-top: -248px; border: 1px #dfdfdf solid; -webkit-border-radius: 3px; -moz-border-radius: 3px; border-radius: 3px; - overflow: hidden; -webkit-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1); -moz-box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1); box-shadow: 0px 1px 1px rgba(0, 0, 0, 0.1); -} -.emoji-items-wrap1 { background: #FFF; padding: 5px 2px 5px 5px; } -.emoji-items-wrap1 .emoji-menu-tabs { +.icon-tooltip-tail { + background: #FFF; + width: 18px; + height: 18px; + -webkit-transform: rotate(45deg); + -moz-transform: rotate(45deg); + -ms-transform: rotate(45deg); + -o-transform: rotate(45deg); + transform: rotate(45deg); + display: inline-block; + -webkit-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + -moz-box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + box-shadow: 1px 1px 1px rgba(0, 0, 0, 0.1); + border: 1px #dfdfdf solid; + border-width: 0 1px 1px 0; + -webkit-border-radius: 2px; + -moz-border-radius: 2px; + border-radius: 2px; + position: absolute; + bottom: -10px; + left: 50%; + margin-left: -9px; +} + +.composer_emoji_tooltip_tabs { width: 100%; margin-bottom: 8px; margin-top: 3px; } -.emoji-items-wrap1 .emoji-menu-tabs td { - text-align: center; - color: white; +.composer_emoji_tooltip_tab { line-height: 0; -} -.emoji-menu-tabs .emoji-menu-tab { + color: white; display: inline-block; width: 24px; height: 29px; background: url(../img/icons/IconsetSmiles.png) no-repeat; - background-size: 42px 350px; + background-size: 42px 470px; + cursor: pointer; + opacity: 0.7; + margin: 0 3px; } -.is_1x .emoji-menu-tabs .emoji-menu-tab { +.is_1x .composer_emoji_tooltip_tab { background-image: url(../img/icons/IconsetSmiles_1x.png); } +.composer_emoji_tooltip_tab.active { + opacity: 1; +} -.emoji-menu-tabs .icon-recent {background-position: -9px -306px; } -.emoji-menu-tabs .icon-recent-selected {background-position: -9px -277px; } +.composer_emoji_tooltip_tab_recent {background-position: -9px -306px; } +.composer_emoji_tooltip_tab_recent.active {background-position: -9px -277px; } -.emoji-menu-tabs .icon-smile {background-position: -9px -34px; } -.emoji-menu-tabs .icon-smile-selected {background-position: -9px -5px; } +.composer_emoji_tooltip_tab_smile {background-position: -9px -34px; } +.composer_emoji_tooltip_tab_smile.active {background-position: -9px -5px; } -.emoji-menu-tabs .icon-flower {background-position: -9px -145px; } -.emoji-menu-tabs .icon-flower-selected {background-position: -9px -118px; } +.composer_emoji_tooltip_tab_flower {background-position: -9px -145px; } +.composer_emoji_tooltip_tab_flower.active {background-position: -9px -118px; } -.emoji-menu-tabs .icon-bell {background-position: -9px -89px; } -.emoji-menu-tabs .icon-bell-selected {background-position: -9px -61px; } +.composer_emoji_tooltip_tab_bell {background-position: -9px -89px; } +.composer_emoji_tooltip_tab_bell.active {background-position: -9px -61px; } -.emoji-menu-tabs .icon-car {background-position: -9px -196px; } -.emoji-menu-tabs .icon-car-selected {background-position: -9px -170px; } +.composer_emoji_tooltip_tab_car {background-position: -9px -196px; } +.composer_emoji_tooltip_tab_car.active {background-position: -9px -170px; } -.emoji-menu-tabs .icon-grid {background-position: -9px -248px; } -.emoji-menu-tabs .icon-grid-selected {background-position: -9px -222px; } - -.emoji-menu-tabs .icon-recent, -.emoji-menu-tabs .icon-smile, -.emoji-menu-tabs .icon-flower, -.emoji-menu-tabs .icon-bell, -.emoji-menu-tabs .icon-car, -.emoji-menu-tabs .icon-grid { - opacity: 0.7; -} -.emoji-menu-tabs .icon-recent:hover, -.emoji-menu-tabs .icon-smile:hover, -.emoji-menu-tabs .icon-flower:hover, -.emoji-menu-tabs .icon-bell:hover, -.emoji-menu-tabs .icon-car:hover, -.emoji-menu-tabs .icon-grid:hover { - opacity: 1; -} +.composer_emoji_tooltip_tab_grid {background-position: -9px -248px; } +.composer_emoji_tooltip_tab_grid.active {background-position: -9px -222px; } +.composer_emoji_tooltip_tab_stickers {background-position: -9px -361px; } +.composer_emoji_tooltip_tab_stickers.active {background-position: -9px -333px; } -.emoji-menu .emoji-items-wrap { +.composer_emoji_tooltip_content { position: relative; height: 174px; + overflow: hidden; + overflow-y: auto; } -.emoji-menu .emoji-items { - padding-right: 8px; - outline: 0 !important; -} -.emoji-menu img { - width: 20px; - height: 20px; - vertical-align: middle; - border: 0 none; -} -.emoji-menu .emoji-items a { + + +a.composer_emoji_btn { margin: -1px 0 0 -1px; padding: 5px; display: block; float: left; border-radius: 2px; } -.emoji-menu .emoji-items a:hover { +a.composer_emoji_btn:hover { background-color: #edf2f5; } -.emoji-menu:after { - content: ' '; - display: block; - clear: left; + + + +.emoji { + display: -moz-inline-box; + -moz-box-orient: vertical; + display: inline-block; + vertical-align: baseline; + *vertical-align: auto; + *zoom: 1; + *display: inline; + height: 18px; + width: 18px; + background-repeat: no-repeat; + text-indent: -9999px; } -.emoji-menu a .label { - display: none; + +/* widths and heights calculated according to spritesheet dimensions and icon size */ +.emoji-spritesheet-0 { + background-size: 486px 126px; + background-image: url('../img/emojisprite_0.png'); +} +.emoji-spritesheet-1 { + background-size: 522px 72px; + background-image: url('../img/emojisprite_1.png'); +} +.emoji-spritesheet-2 { + background-size: 594px 126px; + background-image: url('../img/emojisprite_2.png'); +} +.emoji-spritesheet-3 { + background-size: 612px 54px; + background-image: url('../img/emojisprite_3.png'); } +.emoji-spritesheet-4 { + background-size: 612px 126px; + background-image: url('../img/emojisprite_4.png'); +} + + +.composer_emoji_btn .emoji { + width: 20px; + height: 20px; + vertical-align: middle; + border: 0 none; + display: inline-block; +} +.composer_emoji_btn .emoji-spritesheet-0 {background-size: 540px 140px;} +.composer_emoji_btn .emoji-spritesheet-1 {background-size: 580px 80px;} +.composer_emoji_btn .emoji-spritesheet-2 {background-size: 660px 140px;} +.composer_emoji_btn .emoji-spritesheet-3 {background-size: 680px 60px;} +.composer_emoji_btn .emoji-spritesheet-4 {background-size: 680px 140px;} + + .error_modal_window .modal-dialog { diff --git a/app/css/desktop.css b/app/css/desktop.css index aa88269e..5bbb607f 100644 --- a/app/css/desktop.css +++ b/app/css/desktop.css @@ -171,7 +171,7 @@ margin-right: 38px; display: inline-block; background: url(../img/icons/General.png) no-repeat -10px -111px; - background-size: 40px 678px; + background-size: 40px 778px; vertical-align: top; margin-top: 3px; } @@ -183,7 +183,7 @@ display: inline-block; vertical-align: top; background: url(../img/icons/General.png) no-repeat -11px -135px; - background-size: 40px 678px; + background-size: 40px 778px; } .icon-settings { width: 20px; @@ -193,7 +193,7 @@ display: inline-block; vertical-align: top; background: url(../img/icons/General.png) no-repeat -10px -163px; - background-size: 40px 678px; + background-size: 40px 778px; } .icon-faq { width: 20px; @@ -203,7 +203,7 @@ display: inline-block; vertical-align: top; background: url(../img/icons/General.png) no-repeat -10px -637px; - background-size: 40px 678px; + background-size: 40px 778px; } .icon-about { width: 21px; @@ -212,7 +212,7 @@ display: inline-block; vertical-align: top; background: url(../img/icons/General.png) no-repeat -10px -193px; - background-size: 40px 678px; + background-size: 40px 778px; } .is_1x .icon-new-group, .is_1x .icon-contacts, @@ -315,7 +315,7 @@ .icon-filter-audio { display: inline-block; background: url(../img/icons/General.png) no-repeat 0 0; - background-size: 40px 678px; + background-size: 40px 778px; margin-right: 12px; vertical-align: top; } @@ -722,26 +722,21 @@ a.footer_link.active:active { background: inherit; } -.im_emoji_quick_select_area { +.composer_emoji_panel { display: block; height: 30px; overflow: hidden; max-width: 210px; } - -.im_emoji_quick_select_area a { +.composer_emoji_panel a { display: inline-block; padding: 5px; outline: 0; border-radius: 2px; } - -.im_emoji_quick_select_area a:hover { +/*.composer_emoji_panel a:hover { background-color: #edf2f5; -} -.im_emoji_quick_select_area a .label { - display: none; -} +}*/ .im_message_selected .im_message_date, .im_message_selected .im_message_document_size, @@ -940,7 +935,8 @@ a.im_panel_peer_photo .peer_initials { margin-left: 36px; } -.im_emoji_btn { +/*.composer_emoji_insert_btn { + display: block; position: absolute; right: 3px; top: 2px; @@ -950,28 +946,43 @@ a.im_panel_peer_photo .peer_initials { width: 22px; height: 22px; margin-top: 1px; -} -.icon-emoji { +}*/ +/*.icon-emoji { display: inline-block; width: 22px; height: 22px; vertical-align: top; background: url(../img/icons/General.png) no-repeat -9px -335px; - background-size: 40px 678px; + background-size: 40px 778px; opacity: 0.8; -} +}*/ .is_1x .icon-emoji { background-image: url(../img/icons/General_1x.png); } -.im_emoji_btn:hover .icon-emoji { +.composer_emoji_insert_btn:hover .icon-emoji { opacity: 1; } -.im_emoji_btn:active .icon-emoji, -.im_emoji_btn.on .icon-emoji { +.composer_emoji_insert_btn:active .icon-emoji, +.composer_emoji_insert_btn.on .icon-emoji { background-position: -9px -367px; opacity: 1; } + + + + + + + + + + + + + + + .im_send_field_wrap { margin-bottom: 15px; } @@ -1014,7 +1025,7 @@ a.im_panel_peer_photo .peer_initials { height: 17px; vertical-align: top; background: url(../img/icons/General.png) no-repeat -11px -455px; - background-size: 40px 678px; + background-size: 40px 778px; opacity: 0.8; margin: 0; } @@ -1050,7 +1061,7 @@ a.im_panel_peer_photo .peer_initials { height: 18px; vertical-align: top; background: url(../img/icons/General.png) no-repeat -10px -399px; - background-size: 40px 678px; + background-size: 40px 778px; opacity: 0.8; } .is_1x .icon-camera { diff --git a/app/img/icons/General.png b/app/img/icons/General.png index b6f3fdf6..aa2e7ec8 100644 Binary files a/app/img/icons/General.png and b/app/img/icons/General.png differ diff --git a/app/img/icons/General_1x.png b/app/img/icons/General_1x.png index 271503ca..045dccad 100644 Binary files a/app/img/icons/General_1x.png and b/app/img/icons/General_1x.png differ diff --git a/app/img/icons/IconsetSmiles.png b/app/img/icons/IconsetSmiles.png index 4fb10645..6618c24b 100644 Binary files a/app/img/icons/IconsetSmiles.png and b/app/img/icons/IconsetSmiles.png differ diff --git a/app/img/icons/IconsetSmiles_1x.png b/app/img/icons/IconsetSmiles_1x.png index 9679265d..b244cb76 100644 Binary files a/app/img/icons/IconsetSmiles_1x.png and b/app/img/icons/IconsetSmiles_1x.png differ diff --git a/app/index.html b/app/index.html index 78eb06a2..e98f1f7c 100644 --- a/app/index.html +++ b/app/index.html @@ -84,6 +84,7 @@ PRODUCTION_ONLY_END--> + diff --git a/app/js/app.js b/app/js/app.js index e8ec4a78..a80ee454 100644 --- a/app/js/app.js +++ b/app/js/app.js @@ -27,25 +27,25 @@ angular.module('myApp', [ ]). config(['$locationProvider', '$routeProvider', '$compileProvider', 'StorageProvider', function($locationProvider, $routeProvider, $compileProvider, StorageProvider) { - var icons = {}, reverseIcons = {}, i, j, hex, name, dataItem, row, column, totalColumns; - - for (j = 0; j < Config.EmojiCategories.length; j++) { - totalColumns = Config.EmojiCategorySpritesheetDimens[j][1]; - for (i = 0; i < Config.EmojiCategories[j].length; i++) { - dataItem = Config.Emoji[Config.EmojiCategories[j][i]]; - name = dataItem[1][0]; - row = Math.floor(i / totalColumns); - column = (i % totalColumns); - icons[':' + name + ':'] = [j, row, column, ':'+name+':']; - reverseIcons[name] = dataItem[0]; - } - } - - $.emojiarea.spritesheetPath = 'img/emojisprite_!.png'; - $.emojiarea.spritesheetDimens = Config.EmojiCategorySpritesheetDimens; - $.emojiarea.iconSize = 20; - $.emojiarea.icons = icons; - $.emojiarea.reverseIcons = reverseIcons; + // var icons = {}, reverseIcons = {}, i, j, hex, name, dataItem, row, column, totalColumns; + + // for (j = 0; j < Config.EmojiCategories.length; j++) { + // totalColumns = Config.EmojiCategorySpritesheetDimens[j][1]; + // for (i = 0; i < Config.EmojiCategories[j].length; i++) { + // dataItem = Config.Emoji[Config.EmojiCategories[j][i]]; + // name = dataItem[1][0]; + // row = Math.floor(i / totalColumns); + // column = (i % totalColumns); + // icons[':' + name + ':'] = [j, row, column, ':'+name+':']; + // reverseIcons[name] = dataItem[0]; + // } + // } + + // $.emojiarea.spritesheetPath = 'img/emojisprite_!.png'; + // $.emojiarea.spritesheetDimens = Config.EmojiCategorySpritesheetDimens; + // $.emojiarea.iconSize = 20; + // $.emojiarea.icons = icons; + // $.emojiarea.reverseIcons = reverseIcons; $compileProvider.imgSrcSanitizationWhitelist(/^\s*(https?|ftp|file|blob|filesystem|chrome-extension|app):|data:image\//); $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|file|mailto|blob|filesystem|chrome-extension|app):|data:image\//); diff --git a/app/js/controllers.js b/app/js/controllers.js index 4c98edbf..cb8aeda4 100644 --- a/app/js/controllers.js +++ b/app/js/controllers.js @@ -2992,7 +2992,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) - .controller('CountrySelectModalController', function ($scope, $modalInstance, $rootScope, SearchIndexManager, _) { + .controller('CountrySelectModalController', function ($scope, $modalInstance, $rootScope, _) { $scope.search = {}; $scope.slice = {limit: 20, limitDelta: 20} @@ -3035,7 +3035,7 @@ angular.module('myApp.controllers', ['myApp.i18n']) }) - .controller('PhonebookModalController', function ($scope, $modalInstance, $rootScope, AppUsersManager, PhonebookContactsService, SearchIndexManager, ErrorService) { + .controller('PhonebookModalController', function ($scope, $modalInstance, $rootScope, AppUsersManager, PhonebookContactsService, ErrorService) { $scope.search = {}; $scope.phonebook = []; diff --git a/app/js/directives.js b/app/js/directives.js index 6d919047..0c6f78d9 100755 --- a/app/js/directives.js +++ b/app/js/directives.js @@ -1071,6 +1071,16 @@ angular.module('myApp.directives', ['myApp.filters']) }; function link ($scope, element, attrs) { + + var emojiButton = $('.composer_emoji_insert_btn', element)[0]; + new EmojiTooltip(emojiButton); + + var emojiPanel = $('.composer_emoji_panel', element)[0]; + new EmojiPanel(emojiPanel); + + return; + + var messageField = $('textarea', element)[0], fileSelects = $('input', element), dropbox = $('.im_send_dropbox_wrap', element)[0], @@ -2587,4 +2597,4 @@ angular.module('myApp.directives', ['myApp.filters']) } }; - }) + }) \ No newline at end of file diff --git a/app/js/lib/ng_utils.js b/app/js/lib/ng_utils.js index c7bb80e5..f26139ac 100644 --- a/app/js/lib/ng_utils.js +++ b/app/js/lib/ng_utils.js @@ -734,115 +734,6 @@ 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 = {}; diff --git a/app/js/lib/utils.js b/app/js/lib/utils.js index f7b2f77d..60cea630 100644 --- a/app/js/lib/utils.js +++ b/app/js/lib/utils.js @@ -58,9 +58,64 @@ function onCtrlEnter (textarea, cb) { }); } +function setFieldSelection(field, from, to) { + field = $(field)[0]; + try { + field.focus(); + if (from === undefined || from === false) { + from = field.value.length; + } + if (to === undefined || to === false) { + to = from; + } + if (field.createTextRange) { + var range = field.createTextRange(); + range.collapse(true); + range.moveEnd('character', to); + range.moveStart('character', from); + range.select(); + } + else if (field.setSelectionRange) { + field.setSelectionRange(from, to); + } + } catch(e) {} +} + +function getFieldSelection (field) { + if (field.selectionStart) { + return field.selectionStart; + } + else if (!document.selection) { + return 0; + } + + var c = "\001", + sel = document.selection.createRange(), + txt = sel.text, + dup = sel.duplicate(), + len = 0; + + try { + dup.moveToElementText(field); + } catch(e) { + return 0; + } + + sel.text = txt + c; + len = dup.text.indexOf(c); + sel.moveStart('character',-1); + sel.text = ''; + + // if (browser.msie && len == -1) { + // return field.value.length; + // } + return len; +} + + function onContentLoaded (cb) { setTimeout(cb, 0); -}; +} function tsNow (seconds) { var t = +new Date() + (window.tsOffset || 0); @@ -153,27 +208,140 @@ function calcImageInBox(imageW, imageH, boxW, boxH, noZooom) { } function versionCompare (ver1, ver2) { - if (typeof ver1 !== 'string') { - ver1 = ''; + if (typeof ver1 !== 'string') { + ver1 = ''; + } + if (typeof ver2 !== 'string') { + ver2 = ''; + } + ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.'); + ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.'); + + var a = Math.max(ver1.length, ver2.length), i; + + for (i = 0; i < a; i++) { + if (ver1[i] == ver2[i]) { + continue; + } + if (ver1[i] > ver2[i]) { + return 1; + } else { + return -1; + } + } + + return 0; +} + + +(function (global) { + + var badCharsRe = /[`~!@#$%^&*()\-_=+\[\]\\|{}'";:\/?.>,<\s]+/g, + trimRe = /^\s+|\s$/g, + accentsReplace = { + a: /[åáâäà]/g, + e: /[éêëè]/g, + i: /[íîïì]/g, + o: /[óôöò]/g, + u: /[úûüù]/g, + c: /ç/g, + ss: /ß/g + }; + + 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; } - if (typeof ver2 !== 'string') { - ver2 = ''; + + 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; + } } - ver1 = ver1.replace(/^\s+|\s+$/g, '').split('.'); - ver2 = ver2.replace(/^\s+|\s+$/g, '').split('.'); - var a = Math.max(ver1.length, ver2.length), i; + newFoundObjs = {}; - for (i = 0; i < a; i++) { - if (ver1[i] == ver2[i]) { - continue; + 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 (ver1[i] > ver2[i]) { - return 1; - } else { - return -1; + if (found) { + newFoundObjs[foundObjs[j]] = true; } } - return 0; + return newFoundObjs; } + + global.SearchIndexManager = { + createIndex: createIndex, + indexObject: indexObject, + cleanSearchText: cleanSearchText, + search: search + }; + +})(window); + diff --git a/app/js/message_composer.js b/app/js/message_composer.js new file mode 100644 index 00000000..1a5d21cd --- /dev/null +++ b/app/js/message_composer.js @@ -0,0 +1,396 @@ +/*! + * Webogram v0.3.9 - messaging web application for MTProto + * https://github.com/zhukov/webogram + * Copyright (C) 2014 Igor Zhukov + * https://github.com/zhukov/webogram/blob/master/LICENSE + */ + +'use strict'; + +/* EmojiHelper */ + +(function (global, emojis, categories, spritesheets) { + + + var emojis = {}; + var shortcuts = {}; + var spritesheetPositions = {}; + var index = false; + + var popular = 'joy,kissing_heart,heart,heart_eyes,blush,grin,+1,relaxed,pensive,smile,sob,kiss,unamused,flushed,stuck_out_tongue_winking_eye,see_no_evil,wink,smiley,cry,stuck_out_tongue_closed_eyes,scream,rage,smirk,disappointed,sweat_smile,kissing_closed_eyes,speak_no_evil,relieved,grinning,yum,laughing,ok_hand,neutral_face,confused'.split(','); + + var i, j, code, shortcut, emoji, row, column, totalColumns; + var len1, len2; + + for (i = 0, len1 = categories.length; i < len1; i++) { + totalColumns = spritesheets[i][1]; + for (j = 0, len2 = categories[i].length; j < len2; j++) { + code = categories[i][j]; + emoji = Config.Emoji[code]; + shortcut = emoji[1][0]; + emojis[code] = [emoji[0], shortcut]; + shortcuts[shortcut] = code; + spritesheetPositions[code] = [i, j, Math.floor(j / totalColumns), j % totalColumns]; + } + } + + function getPopularEmoji (callback) { + ConfigStorage.get('emojis_popular', function (popEmojis) { + var result = []; + if (popEmojis && popEmojis.length) { + for (var i = 0, len = popEmojis.length; i < len; i++) { + result.push({code: popEmojis[i][0], rate: popEmojis[i][1]}); + } + callback(result); + return; + }; + ConfigStorage.get('emojis_recent', function (recentEmojis) { + recentEmojis = recentEmojis || popular || []; + var shortcut, code; + for (var i = 0, len = recentEmojis.length; i < len; i++) { + shortcut = recentEmojis[i]; + if (Array.isArray(shortcut)) { + shortcut = shortcut[0]; + } + if (shortcut.charAt(0) == ':') { + shortcut = shortcut.substr(1, shortcut.length - 2); + } + if (code = shortcuts[shortcut]) { + result.push({code: code, rate: 1}); + } + } + callback(result); + }); + }); + } + + function pushPopularEmoji (code) { + getPopularEmoji(function (popularEmoji) { + var exists = false; + var count = popularEmoji.length; + var result = []; + for (var i = 0; i < count; i++) { + if (popularEmoji[i].code == code) { + exists = true; + popularEmoji[i].rate++; + } + result.push([popularEmoji[i].code, popularEmoji[i].rate]); + } + if (exists) { + result.sort(function (a, b) { + return b[1] - a[1]; + }); + } else { + if (result.length > 41) { + result = result.slice(0, 41); + } + result.push([code, 1]); + } + ConfigStorage.set({emojis_popular: result}); + }); + } + + function indexEmojis () { + if (index === false) { + index = SearchIndexManager.createIndex(); + var shortcut; + for (shortcut in shortcuts) { + if (shortcuts.hasOwnProperty(shortcut)) { + SearchIndexManager.indexObject(shortcuts[shortcut], shortcut, index); + } + } + } + } + + function searchEmojis (q) { + indexEmojis(); + return SearchIndexManager.search(q, index); + } + + global.EmojiHelper = { + emojis: emojis, + shortcuts: shortcuts, + spritesheetPositions: spritesheetPositions, + getPopularEmoji: getPopularEmoji, + pushPopularEmoji: pushPopularEmoji, + indexEmojis: indexEmojis, + searchEmojis: searchEmojis + }; + +})(window, Config.Emoji, Config.EmojiCategories, Config.EmojiCategorySpritesheetDimens); + + +function EmojiTooltip (btnEl, options) { + options = options || {}; + var self = this; + + this.btnEl = $(btnEl); + this.onEmojiSelected = options.onEmojiSelected; + + $(this.btnEl).on('mouseenter mouseleave', function (e) { + self.isOverBtn = e.type == 'mouseenter'; + self.createTooltip(); + + if (self.isOverBtn) { + self.onMouseEnter(true); + } else { + self.onMouseLeave(true); + } + }); +} + +EmojiTooltip.prototype.onMouseEnter = function (triggerShow) { + if (this.hideTimeout) { + clearTimeout(this.hideTimeout); + delete this.hideTimeout; + } + else if (triggerShow && !this.showTimeout) { + this.showTimeout = setTimeout(this.show.bind(this), 500); + } +}; + +EmojiTooltip.prototype.onMouseLeave = function (triggerUnshow) { + if (!this.hideTimeout) { + var self = this; + this.hideTimeout = setTimeout(function () { + self.hide(); + }, 500); + } + else if (triggerUnshow && this.showTimeout) { + clearTimeout(this.showTimeout); + delete this.showTimeout; + } +}; + + + +EmojiTooltip.prototype.createTooltip = function () { + if (this.tooltipEl) { + return false; + } + + var self = this; + this.tooltipEl = $('
').appendTo(document.body); + + this.tabsEl = $('.composer_emoji_tooltip_tabs', this.tooltip); + this.contentEl = $('.composer_emoji_tooltip_content', this.tooltip); + this.footerEl = $('.composer_emoji_tooltip_footer', this.tooltip); + this.settingsEl = $('.composer_emoji_tooltip_settings', this.tooltip); + + angular.forEach(['recent', 'smile', 'flower', 'bell', 'car', 'grid', 'stickers'], function (tabName, tabIndex) { + $('') + .on('mousedown', function (e) { + self.selectTab(tabIndex); + return cancelEvent(e); + }) + .appendTo(self.tabsEl); + }); + + this.contentEl.on('mousedown', function (e) { + e = e.originalEvent || e; + var target = $(e.target), code; + if (target.hasClass('emoji')) { + target = $(target[0].parentNode); + } + if (code = target.attr('data-code')) { + if (self.onEmojiSelected) { + self.onEmojiSelected(code); + } + EmojiHelper.pushPopularEmoji(code); + } + return cancelEvent(e); + }); + + this.tooltipEl.on('mouseenter mouseleave', function (e) { + console.log(dT(), e.type); + if (e.type == 'mouseenter') { + self.onMouseEnter(); + } else { + self.onMouseLeave(); + } + }); + + this.selectTab(0); + + return true; +} + + +EmojiTooltip.prototype.selectTab = function (tab) { + if (this.tab === tab) { + return false; + } + $('.active', this.tabsEl).removeClass('active'); + this.tab = tab; + $(this.tabsEl[0].childNodes[tab]).addClass('active'); + + this.updateTabContents(); +}; + +EmojiTooltip.prototype.updateTabContents = function (tab) { + var html = []; + var self = this; + var iconSize = Config.Mobile ? 26 : 20; + + if (this.tab > 0) { + var categoryIndex = this.tab - 1; + var emoticonCodes = Config.EmojiCategories[categoryIndex]; + var totalColumns = Config.EmojiCategorySpritesheetDimens[categoryIndex][1]; + var count = emoticonCodes.length; + var emoticonCode, emoticonData, i, x, y; + + for (i = 0; i < count; i++) { + emoticonCode = emoticonCodes[i]; + emoticonData = Config.Emoji[emoticonCode]; + x = iconSize * (i % totalColumns); + y = iconSize * Math.floor(i / totalColumns); + html.push(''); + } + this.contentEl.html(html.join('')); + } + else { + EmojiHelper.getPopularEmoji(function (popularEmoji) { + var emoticonCode, emoticonData, spritesheet, pos, categoryIndex; + var count = popularEmoji.length; + var i, x, y; + + for (i = 0; i < count; i++) { + emoticonCode = popularEmoji[i].code; + if (emoticonData = Config.Emoji[emoticonCode]) { + spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]; + categoryIndex = spritesheet[0]; + pos = spritesheet[1]; + x = iconSize * spritesheet[3]; + y = iconSize * spritesheet[2]; + html.push(''); + } + } + self.contentEl.html(html.join('')); + }); + } +}; + +EmojiTooltip.prototype.updatePosition = function () { + var offset = this.btnEl.offset(); + this.tooltipEl.css({top: offset.top, left: offset.left}); +}; + +EmojiTooltip.prototype.show = function () { + this.updatePosition(); + this.tooltipEl.show(); + delete this.showTimeout; +}; + +EmojiTooltip.prototype.hide = function () { + this.tooltipEl.hide(); + delete this.hideTimeout; +}; + + + + +function EmojiPanel (containerEl, options) { + options = options || {}; + // var self = this; + + this.containerEl = $(containerEl); + this.onEmojiSelected = options.onEmojiSelected; + + this.containerEl.on('mousedown', function (e) { + e = e.originalEvent || e; + var target = $(e.target), code; + if (target.hasClass('emoji')) { + target = $(target[0].parentNode); + } + if (code = target.attr('data-code')) { + if (self.onEmojiSelected) { + self.onEmojiSelected(code); + } + EmojiHelper.pushPopularEmoji(code); + } + return cancelEvent(e); + }); + + this.update(); +} + +EmojiPanel.prototype.update = function () { + var html = []; + var self = this; + var iconSize = Config.Mobile ? 26 : 20; + + EmojiHelper.getPopularEmoji(function (popularEmoji) { + var emoticonCode, emoticonData, spritesheet, pos, categoryIndex; + var count = popularEmoji.length; + var i, x, y; + + for (i = 0; i < count; i++) { + emoticonCode = popularEmoji[i].code; + if (emoticonData = Config.Emoji[emoticonCode]) { + spritesheet = EmojiHelper.spritesheetPositions[emoticonCode]; + categoryIndex = spritesheet[0]; + pos = spritesheet[1]; + x = iconSize * spritesheet[3]; + y = iconSize * spritesheet[2]; + html.push(''); + } + } + self.containerEl.html(html.join('')); + }); +} + + + + + +function MessageComposer (textarea, options) { + this.textareaEl = $(textarea); + + this.textareaEl.on('keyup keydown', this.onKeyEvent.bind(this)); + this.textareaEl.on('focus blur', this.onFocusBlur.bind(this)); + + this.isActive = false; +} + +MessageComposer.prototype.onKeyEvent = function (e) { + var self = this; + if (e.type == 'keyup') { + this.checkAutocomplete(); + } +} + +MessageComposer.prototype.checkAutocomplete = function (e) { + var pos = getFieldSelection(e.target); + var value = this.textareaEl[0].value.substr(0, pos); + var matches = value.match(/:([A-Za-z_]*)$/); + if (matches) { + if (matches[1]) { + var found = EmojiHelper.searchEmojis(matches[1]); + self.showEmojiSuggestions(found); + } else { + EmojiHelper.getPopularEmoji(function (found) { + self.showEmojiSuggestions(found); + }); + } + } + else { + self.hideSuggestions(); + } +} + +MessageComposer.prototype.onFocusBlur = function (e) { + this.isActive = e.type == 'focus'; + + if (!this.isActive) { + this.hideSuggestions(); + } +} + + +MessageComposer.prototype.showEmojiSuggestions = function (codes) { + this.autocompleteShown = true; +} + +MessageComposer.prototype.hideSuggestions = function () { + delete this.autocompleteShown; +} diff --git a/app/js/services.js b/app/js/services.js index 9229ec7d..1d7aa89d 100644 --- a/app/js/services.js +++ b/app/js/services.js @@ -11,7 +11,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) -.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiFileManager, MtpApiManager, RichTextProcessor, SearchIndexManager, ErrorService, Storage, _) { +.service('AppUsersManager', function ($rootScope, $modal, $modalStack, $filter, $q, qSync, MtpApiFileManager, MtpApiManager, RichTextProcessor, ErrorService, Storage, _) { var users = {}, usernames = {}, cachedPhotoLocations = {}, @@ -507,7 +507,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) }) -.service('AppChatsManager', function ($rootScope, $modal, _, MtpApiFileManager, MtpApiManager, AppUsersManager, RichTextProcessor, SearchIndexManager) { +.service('AppChatsManager', function ($rootScope, $modal, _, MtpApiFileManager, MtpApiManager, AppUsersManager, RichTextProcessor) { var chats = {}, cachedPhotoLocations = {}; @@ -687,7 +687,7 @@ angular.module('myApp.services', ['myApp.i18n', 'izhukov.utils']) } }) -.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, SearchIndexManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, StatusManager, _) { +.service('AppMessagesManager', function ($q, $rootScope, $location, $filter, ApiUpdatesManager, AppUsersManager, AppChatsManager, AppPeersManager, AppPhotosManager, AppVideoManager, AppDocsManager, AppAudioManager, MtpApiManager, MtpApiFileManager, RichTextProcessor, NotificationsManager, PeersSelectService, Storage, FileManager, TelegramMeWebService, StatusManager, _) { var messagesStorage = {}; var messagesForHistory = {}; diff --git a/app/partials/desktop/emoji_btn_tooltip.html b/app/partials/desktop/emoji_btn_tooltip.html new file mode 100644 index 00000000..51354a1b --- /dev/null +++ b/app/partials/desktop/emoji_btn_tooltip.html @@ -0,0 +1,26 @@ +
+ +
+
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + +
\ No newline at end of file diff --git a/app/partials/desktop/im.html b/app/partials/desktop/im.html index 01e3cc84..c152e5fc 100644 --- a/app/partials/desktop/im.html +++ b/app/partials/desktop/im.html @@ -171,14 +171,13 @@ -
+
-
- -
+ +
- +
@@ -194,7 +193,7 @@
-
+
diff --git a/app/webogram.appcache b/app/webogram.appcache index a43a10bd..9802e602 100644 --- a/app/webogram.appcache +++ b/app/webogram.appcache @@ -1,6 +1,6 @@ CACHE MANIFEST -# 55 +# 57 NETWORK: *