// interface_common.js // 2013 Lucas Leal, Miguel Freitas // // Common interface functions to all pages, modal manipulation, button manipulation etc // Profile, mentions and hashtag modal // Post actions: submit, count characters var window_scrollY = 0; function openModal(modal) { window_scrollY = window.pageYOffset; $('body').css('overflow', 'hidden'); if (!modal.classBase) modal.classBase = '.modal-wrapper'; $(modal.classBase + ':not(#templates *)').remove(); modal.self = $('#templates ' + modal.classBase).clone(true) .addClass(modal.classAdd); if (modal.title) modal.self.find('.modal-header h3').html(modal.title); if (modal.content) modal.content = modal.self.find('.modal-content') .append(modal.content); else modal.content = modal.self.find('.modal-content'); modal.self.prependTo('body').fadeIn('fast'); return modal; } function closeModal() { closeModalHandler('.modal-wrapper'); window.location.hash = '#'; window.scroll(window.pageXOffset, window_scrollY); } function closeModalHandler(classBase) { var modalWindows = $(classBase+':not(#templates *)'); modalWindows.fadeOut('fast', function() {modalWindows.remove();}); $('body').css({ 'overflow': 'auto', 'margin-right': '0' }); } function checkNetworkStatusAndAskRedirect(cbFunc, cbArg) { networkUpdate(function(args) { if (!twisterdConnectedAndUptodate) { var redirect = window.confirm(polyglot.t('switch_to_network')); if (redirect) $.MAL.goNetwork(); } else { if (args.cbFunc) args.cbFunc(args.cbArg); } }, {cbFunc:cbFunc,cbArg:cbArg}); } function timeGmtToText(t) { var d = new Date(0); d.setUTCSeconds(t); return d.toString().replace(/GMT.*/g, ''); } function timeSincePost(t) { var d = new Date(0); d.setUTCSeconds(t); var now = new Date(); var t_delta = Math.ceil((now - d) / 1000); var expression; if (t_delta < 60) expression = polyglot.t('seconds', t_delta); else if (t_delta < 3600) // 60 * 60 expression = polyglot.t('minutes', Math.floor(t_delta / 60)); else if (t_delta < 86400) // 24 * 60 * 60 expression = polyglot.t('hours', Math.floor(t_delta / 3600)); // 60 * 60 else expression = polyglot.t('days', Math.floor(t_delta / 86400)); // 24 * 60 * 60 return polyglot.t('time_ago', {time: expression}); } function openProfileModalWithUsernameHandler(username) { var content = $('#profile-modal-template').children().clone(true); updateProfileData(content, username); // FIXME following ctc could be part of updateProfileData() when mobile will be ready for this content.find('.tox-ctc').attr('title', polyglot.t('Copy to clipboard')); content.find('.bitmessage-ctc').attr('title', polyglot.t('Copy to clipboard')); content = openModal({ classAdd: 'profile-modal', content: content, title: polyglot.t('users_profile', {username: username}) }).content; // setup follow button in profile modal window var button = content.find('.profile-card-buttons .follow'); if (button) { if (followingUsers.indexOf(username) !== -1) toggleFollowButton(username, true, function() {setTimeout(loadModalFromHash, 500);}); else button.on('click', userClickFollow); } content.find('.postboard') // potentially dangerous fix because it's supposed for vertical oriented profile modals .height(content.height() - content.find('.profile-card').outerHeight()); } function openHashtagModalFromSearchHandler(hashtag) { var modal = openModal({ classAdd: 'hashtag-modal', content: $('#hashtag-modal-template').children().clone(true), title: '#' + hashtag }); clearHashtagProcessed(); updateHashtagModal(modal.content.find('.postboard-posts'), hashtag, 'hashtag'); } function updateHashtagModal(postboard, hashtag, resource, timeoutArgs) { if (postboard.is('html *')) { requestHashtag(postboard, hashtag, resource, timeoutArgs); if (_hashtagPendingPostsUpdated) { if (resource !== 'mention' && $.Options.showDesktopNotifPostsModal.val === 'enable') { $.MAL.showDesktopNotif (false, polyglot.t('You got')+' '+polyglot.t('new_posts', _hashtagPendingPostsUpdated)+' '+polyglot.t('in search result')+'.', false,'twister_notification_new_posts_modal', $.Options.showDesktopNotifPostsModalTimer.val, function() { $('.postboard-news').hide(); displayHashtagPending($('.hashtag-modal .postboard-posts')); }, false) } _hashtagPendingPostsUpdated = 0; } // use extended timeout parameters on modal refresh (requires twister_core >= 0.9.14). // our first query above should be faster (with default timeoutArgs of twisterd), // then we may possibly collect more posts on our second try by waiting more. setTimeout(updateHashtagModal, 5000, postboard, hashtag, resource, [10000,2000,3]); } } function openMentionsModal(e) { if (e && e.stopPropagation) { e.stopPropagation(); e.preventDefault(); } var userInfo = $(this).closest('[data-screen-name]'); if (userInfo.length) var username = userInfo.attr('data-screen-name'); else if (defaultScreenName) var username = defaultScreenName; else { alert(polyglot.t('No one can mention you because you are not logged in.')); return; } window.location.hash = '#mentions?user=' + username; } function openMentionsModalHandler(username) { var modal = openModal({ classAdd: 'hashtag-modal', content: $('#hashtag-modal-template').children().clone(true), title: polyglot.t('users_mentions', {username: username}) }); clearHashtagProcessed(); updateHashtagModal(modal.content.find('.postboard-posts'), username, 'mention'); if (username === defaultScreenName) { // obtain already cached mention posts from twister_newmsgs.js processHashtag(modal.content.find('.postboard-posts'), defaultScreenName, getMentionsData()); resetMentionsCount(); } } function openFollowingModal(username) { var content = $('#following-modal-template').children().clone(true); content.find('.following-screen-name b').text(username); loadFollowingIntoList(username, content.closest('ol')); openModal({ classAdd: 'following-modal', content: content, title: polyglot.t('followed_by', {username: username}) }); } function fillWhoToFollowModal(list, hlist, start) { var itemTmp = $('#follow-suggestion-template').clone(true) .removeAttr('id'); for (var i = 0; i < followingUsers.length && list.length < start + 20; i++) { if (typeof twisterFollowingO.followingsFollowings[followingUsers[i]] !== 'undefined') { for (var j = 0; j < twisterFollowingO.followingsFollowings[followingUsers[i]].following.length && list.length < start + 25; j++) { var utf = twisterFollowingO.followingsFollowings[followingUsers[i]].following[j]; if (followingUsers.indexOf(utf) < 0 && list.indexOf(utf) < 0) { list.push(utf); var item = itemTmp.clone(true); item.find('.twister-user-info').attr('data-screen-name', utf); item.find('.twister-user-name').attr('href', $.MAL.userUrl(utf)); item.find('.twister-by-user-name').attr('href', $.MAL.userUrl(followingUsers[i])); item.find('.twister-user-tag').text('@' + utf); getAvatar(utf, item.find('.twister-user-photo')); getFullname(utf, item.find('.twister-user-full')); getBio(utf, item.find('.bio')); getFullname(followingUsers[i], item.find('.followed-by').text(followingUsers[i])); item.find('.twister-user-remove').remove(); hlist.append(item); } } } } itemTmp.remove(); if (i >= followingUsers.length - 1) return false; // returns true, if there are more... return true; } function openWhoToFollowModal() { var modal = openModal({ classAdd: 'who-to-follow-modal', title: polyglot.t('Who to Follow') }); var tmplist = []; var hlist = $('
    ') .appendTo(modal.content); modal.content.on('scroll', function() { if (modal.content.scrollTop() >= hlist.height() - modal.content.height() - 20) { if (!fillWhoToFollowModal(tmplist, hlist, tmplist.length)) modal.content.off('scroll'); } }); fillWhoToFollowModal(tmplist, hlist, 0); } function newConversationModal(username, resource) { var content = $('#hashtag-modal-template').children().clone(true); requestPost(content.find('.postboard-posts'), username, resource, function(args) { var postboard = args.content.find('.postboard-posts'); var postLi = postboard.children().first() .css('display', 'none'); getTopPostOfConversation(postLi, null, postboard); }, {content:content} ); content.find('.postboard-news').on('click', function () { $(this).hide(); displayHashtagPending($('.conversation-modal .postboard-posts')); }); return content; } function openConversationClick(e) { e.stopPropagation(); e.preventDefault(); var postData = $(this).parents('.module.post.original.open .module.post.original .post-data'); window.location.hash = '#conversation?post=' + postData.attr('data-screen-name') + ':post' + postData.attr('data-id'); } function openConversationModal(username,resource) { openModal({ classAdd: 'conversation-modal', content: newConversationModal(username, resource), title: polyglot.t('conversation_title', {username: username}) }); } function watchHashChange(e) { if (e != null) { var prevurlsplit = e.oldURL.split('#'); var prevhashstring = prevurlsplit[1]; var notFirstModalView = (prevhashstring !== undefined && prevhashstring.length > 0); var notNavigatedBackToFirstModalView = (window.history.state == null || (window.history.state != null && window.history.state.showCloseButton !== false)); if (notFirstModalView && notNavigatedBackToFirstModalView) { $('.modal-back').css('display', 'inline'); } else { window.history.pushState({showCloseButton: false}, null, null); $('.modal-back').css('display', 'none'); } } loadModalFromHash(); } function loadModalFromHash() { var hashstring = decodeURIComponent(window.location.hash); var hashdata = hashstring.split(':'); if (hashdata[0] !== '#web+twister') hashdata = hashstring.match(/(hashtag|profile|mentions|directmessages|following|conversation)\?(?:user|hashtag|post)=(.+)/); if (hashdata && hashdata[1] !== undefined && hashdata[2] !== undefined) { if (hashdata[1] === 'profile') openProfileModalWithUsernameHandler(hashdata[2]); else if (hashdata[1] === 'hashtag') openHashtagModalFromSearchHandler(hashdata[2]); else if (hashdata[1] === 'mentions') openMentionsModalHandler(hashdata[2]); else if (hashdata[1] === 'directmessages') openDmWithUserModal(hashdata[2]); else if (hashdata[1] === 'following') openFollowingModal(hashdata[2]); else if (hashdata[1] === 'conversation') { splithashdata2 = hashdata[2].split(':'); openConversationModal(splithashdata2[0], splithashdata2[1]); } } else if (hashstring === '#directmessages') directMessagesPopup(); else if (hashstring === '#whotofollow') openWhoToFollowModal(); } function initHashWatching() { // register custom protocol handler if (window.navigator && window.navigator.registerProtocolHandler && !_getResourceFromStorage('twister_protocol_registered')) { window.navigator.registerProtocolHandler( 'web+twister', window.location.protocol + '//' + window.location.host + '/home.html#%s', 'Twister' ); _putResourceIntoStorage('twister_protocol_registered', true); } // register hash spy and launch it once window.addEventListener('hashchange', watchHashChange, false); setTimeout(watchHashChange, 1000); } function reTwistPopup(e) { e.stopPropagation(); if (!defaultScreenName) { alert(polyglot.t('You have to log in to retransmit messages.')); return; } openModal({ classBase: '.prompt-wrapper', classAdd: 'reTwist', title: polyglot.t('retransmit_this') }) .content .append(postToElem($.evalJSON($(this).parents('.post-data').attr('data-userpost')), '')); } // Expande Área do Novo post function replyInitPopup(e, post) { var modal = openModal({ classBase: '.prompt-wrapper', classAdd: 'reply', title: polyglot.t('reply_to', {fullname: ''+post.userpost.n+''}) }); getFullname(post.userpost.n, modal.self.find('h3 .fullname')); modal.content .append($('#reply-modal-template').children().clone(true)) .append(postToElem(post, '')) ; // FIXME passing data through attributes may result in a mess like following var replyArea = modal.content.find('.post-area .post-area-new').addClass('open'); var replyText = replyArea.find('textarea'); var postInlineReplyText = modal.content.find('.post .post-area-new textarea'); $.each(['placeholder', 'data-reply-to'], function(i, attribute) { replyText.attr(attribute, postInlineReplyText.attr(attribute)); }); composeNewPost(e, replyArea); } // abre o menu dropdown de configurações function dropDownMenu() { $('.config-menu').slideToggle('fast'); } // fecha o config menu ao clicar em qualquer lugar da tela function closeThis() { $(this).slideUp('fast'); } function toggleFollowButton(username, toggleUnfollow, bindFunc) { if (!username) return; if (toggleUnfollow) { $('[data-screen-name="'+username+'"]').find('.follow') .removeClass('follow') .addClass('unfollow') .off('click') .on('click', (function(e) { e.stopPropagation(); unfollow(this.username.toString(), (function() { toggleFollowButton(this.username); if (this.bindFunc) this.bindFunc; }).bind({username: this.username, bindFunc: this.bindFunc}) ); }).bind({username: username, bindFunc: bindFunc}) ) .text(polyglot.t('Unfollow')) .trigger('eventToggleUnfollow') ; } else { $('[data-screen-name="'+username+'"]').find('.unfollow') .removeClass('unfollow') .addClass('follow') .off('click') .on('click', (function(e) { userClickFollow(e); if (this.bindFunc) this.bindFunc; }).bind({bindFunc: bindFunc}) ) .text(polyglot.t('Follow')) .trigger('eventToggleFollow'); } } function postExpandFunction(e, postLi) { if (!postLi.hasClass('original')) return; var openClass = 'open'; var originalPost = postLi.find('.post-data.original'); var postInteractionText = originalPost.find('.post-expand'); var postExpandedContent = originalPost.find('.expanded-content'); var postsRelated = postLi.find('.related'); if (!postLi.hasClass(openClass)) { originalPost.detach(); postLi.empty(); postLi.addClass(openClass); postInteractionText.text(polyglot.t('Collapse')); var itemOl = $('
      ', {class:'expanded-post'}).appendTo(postLi); var originalLi = $('
    1. ', {class: 'module post original'}).appendTo(itemOl) .append(originalPost); postExpandedContent.slideDown('fast'); if ($.Options.displayPreview.val === 'enable') { var previewContainer = postExpandedContent.find('.preview-container')[0]; /* was the preview added before... */ if ($(previewContainer).children().length === 0) { var link = originalPost.find('a[rel="nofollow"]'); /*is there any link in the post?*/ for (var i = 0; i < link.length; i++) { if (/^[^?]+\.(?:jpe?g|gif|png)$/i.test(link[i].href)) { var url = proxyURL(link[i].href); $(previewContainer).append($('')); } } } } // insert 'reply_to' before requestRepliedBefore(originalLi); // insert replies to this post after requestRepliesAfter(originalLi); // RTs faces and counter requestRTs(originalLi); } else { postLi.removeClass(openClass); postInteractionText.text( (typeof postLi.find('.post-data.original').attr('data-replied-to-id') === 'undefined') ? polyglot.t('Expand') : polyglot.t('Show conversation') ); if (postsRelated) postsRelated.slideUp('fast'); postExpandedContent.slideUp('fast', function() { originalPost.detach().appendTo(postLi.empty()); }); } e.stopPropagation(); } function postReplyClick(e) { if (!defaultScreenName) { e.stopPropagation(); alert(polyglot.t('You have to log in to post replies.')); return; } var post = $(this).closest('.post'); if (!post.hasClass('original')) replyInitPopup(e, $.evalJSON(post.find('.post-data').attr('data-userpost'))); else { if (!post.parents('.post.open').length) postExpandFunction(e, post); composeNewPost(e, post.find('.post-area-new')); } e.stopPropagation(); } // Expande Área do Novo post function composeNewPost(e, postAreaNew) { e.stopPropagation(); if (!postAreaNew.hasClass('open')) { postAreaNew.addClass('open'); //se o usuário clicar fora é pra fechar postAreaNew.clickoutside(unfocusThis); if ($.Options.splitPosts.val === 'enable') usePostSpliting = true; else if ($.Options.splitPosts.val === 'only-new') { var postOrig = postAreaNew.closest('.post-data'); if (!postOrig.length) postOrig = postAreaNew.closest('.modal-content').find('.post-data'); if (postOrig.length) usePostSpliting = false; else usePostSpliting = true; } else usePostSpliting = false; } var textArea = postAreaNew.find('textarea'); if (textArea.attr('data-reply-to') && !textArea.val().length) { textArea.val(textArea.attr('data-reply-to')); } if (!postAreaNew.find('textarea:focus').length) postAreaNew.find('textarea:last').focus(); } // Reduz Área do Novo post function unfocusThis() { $(this).removeClass('open'); } function checkPostForMentions(post, mentions, max) { return new RegExp('^.{0,' + max.toString() + '}(?:' + mentions.trim().replace(/ /g, '|') + ')').test(post); } var splitedPostsCount = 1; // FIXME it could be property of future textAreaInput and composeNewPost united thing; currently stuff is hell var usePostSpliting = false; function replyTextInput(event) { var textArea = $(event.target); var textAreaForm = textArea.parents('form'); if (textAreaForm.length) { if ($.Options.unicodeConversion.val !== 'disable') textArea.val(convert2Unicodes(textArea.val(), textArea)); if (usePostSpliting && !textArea.parents('.directMessages').length) { var caretPos = textArea.caret(); var reply_to = textArea.attr('data-reply-to'); var tas = textAreaForm.find('textarea'); splitedPostsCount = tas.length; var icurrentta = tas.index(event.target); // current textarea tas index var i = 0; var pml = getPostSplitingPML(); var cci = getPostSpittingCI(icurrentta); for (; i < tas.length; i++) { pml = getPostSplitingPML(); if (tas[i].value.length > pml) { var ci = getPostSpittingCI(i); if (i < splitedPostsCount - 1) { tas[i + 1].value = tas[i].value.substr(ci) + tas[i + 1].value; tas[i].value = tas[i].value.substr(0, ci); if (caretPos > cci) { caretPos -= ci; icurrentta += 1; cci = getPostSpittingCI(icurrentta); var targetta = $(tas[icurrentta]); } else if (i === icurrentta) $(tas[i]).caret(caretPos); } else { var oldta = $(tas[i]); if ($.fn.textcomplete) { oldta.textcomplete('destroy'); event.stopImmediatePropagation(); // something goes wrong in $.fn.textcomplete if we don't stop this immediately } var cp = oldta.val(); var newta = $(oldta).clone(true) .val(cp.substr(ci)) .insertAfter(oldta) ; oldta.val(cp.substr(0, ci)) .addClass('splited-post') .on('focus', function() {this.style.height = '80px';}) .on('focusout', function() {this.style.height = '28px';}) // FIXME move this to CSS ; tas = textAreaForm.find('textarea'); splitedPostsCount = tas.length; pml = getPostSplitingPML(); if (caretPos > cci) { caretPos -= ci; icurrentta += 1; cci = getPostSpittingCI(icurrentta); var targetta = newta; oldta[0].style.height = '28px'; // FIXME move this to CSS } else if (i === icurrentta) { $(tas[i]).caret(caretPos); replyTextUpdateRemaining(tas[i]); if ($.fn.textcomplete) setTextcompleteOnElement(tas[i]); } } } else if (tas.length > 1 && tas[i].value.length === 0) { if (i === tas.length - 1) { tas[i].value = tas[i - 1].value; $(tas[i - 1]).remove(); } else $(tas[i]).remove(); tas = textAreaForm.find('textarea'); i--; splitedPostsCount = tas.length; pml = getPostSplitingPML(); caretPos = -1; if (icurrentta >= i && icurrentta > 0) { icurrentta -= 1; cci = getPostSpittingCI(icurrentta); } var targetta = $(tas[icurrentta]); } } if (typeof targetta !== 'undefined' && targetta[0] !== document.activeElement) { textArea = targetta; textArea.focus(); textArea.caret(caretPos); } } } function getPostSplitingPML() { if (splitedPostsCount > 1) { var pml = 140 - (i+1).toString().length - splitedPostsCount.toString().length - 4; // if mention exists, we shouldn't add it while posting. if (typeof reply_to !== 'undefined' && !checkPostForMentions(tas[i].value, reply_to, pml -reply_to.length)) { pml -= reply_to.length; } } else var pml = 140; return pml; } function getPostSpittingCI(ita) { var ci; var endings = tas[ita].value.match(/[\\\/\.,:;\?\!\*'"\]\)\}\^\|%\u201D\u2026\u2014\u4E00\u3002\uFF0C\uFF1A\uFF1F\uFF01\u3011>\s]/g) // unicode escaped stuff is '”…—一。,:?!】 if (endings) { ci = tas[ita].value.lastIndexOf(endings[endings.length - 1]); for (var j = endings.length - 2; j >= 0 && ci > pml; j--) ci = tas[ita].value.lastIndexOf(endings[j], ci - 1); } if (!(ci > 0)) ci = pml; return (ci > pml) ? pml : ci; } } function replyTextUpdateRemaining(ta) { if (ta.target) ta = ta.target; if (ta === document.activeElement) { var textArea = $(ta); var textAreaForm = textArea.parents('form'); if (textAreaForm.length) { var remainingCount = textAreaForm.find('.post-area-remaining'); var c = replyTextCountRemaining(ta); if (usePostSpliting && !textArea.parents('.directMessages').length && splitedPostsCount > 1) remainingCount.text((textAreaForm.find('textarea').index(ta) + 1).toString() + '/' + splitedPostsCount.toString() + ': ' + c.toString()); else remainingCount.text(c.toString()); var buttonSend = textAreaForm.find('.post-submit'); if (!buttonSend.length) buttonSend = textAreaForm.find('.dm-submit'); var disable = false; textAreaForm.find('textarea').each(function() { if (replyTextCountRemaining(this) < 0) { disable = true; // alternatively we could call replyTextInput() return false; } }); if (!disable && c >= 0 && c < 140 && textArea.val() !== textArea.attr('data-reply-to')) { remainingCount.removeClass('warn'); $.MAL.enableButton(buttonSend); } else { if (disable) remainingCount.addClass('warn'); $.MAL.disableButton(buttonSend); } } } } function replyTextCountRemaining(ta) { var textArea = $(ta); var c; if (usePostSpliting && !textArea.parents('.directMessages').length && splitedPostsCount > 1) { c = 140 - ta.value.length - (textArea.parents('form').find('textarea').index(ta) + 1).toString().length - splitedPostsCount.toString().length - 4; var reply_to = textArea.attr('data-reply-to'); if (typeof reply_to !== 'undefined' && !checkPostForMentions(ta.value, reply_to, 140 -c -reply_to.length)) c -= reply_to.length; } else c = 140 - ta.value.length; return c; } function replyTextKeySend(event) { if (event.keyCode === 13) { if ((!event.metaKey && !event.ctrlKey && $.Options.keysSend.val === 'enter' && $('.dropdown-menu').css('display') === 'none') || ((event.metaKey || event.ctrlKey) && $.Options.keysSend.val === 'ctrlenter')) { var textArea = $(event.target); var textAreaForm = textArea.parents('form'); var buttonSend = textAreaForm.find('.post-submit'); if (!buttonSend.length) buttonSend = textAreaForm.find('.dm-submit'); if (buttonSend.length) { textArea.val(textArea.val().trim()); if (!buttonSend.hasClass('disabled')) buttonSend.click(); } } } } /* * unicode convertion list * k: original string to be replaced * u: unicode * n: index of char to be stored and appended to result */ var unicodeConversionList = { 'punctuation': [ { 'k': /\.\.\./, 'u': '\u2026', 'n': -1 }, { 'k': /\.\../, 'u': '\u2025', 'n': 2 }, { 'k': /\?\?/, 'u': '\u2047', 'n': -1 }, { 'k': /\?!/, 'u': '\u2048', 'n': -1 }, { 'k': /!\?/, 'u': '\u2049', 'n': -1 }, { 'k': /!!/, 'u': '\u203C', 'n': -1 }, { 'k': /--/, 'u': '\u2014', 'n': -1 }, { 'k': /~~/, 'u': '\u2053', 'n': -1 } ], 'emotions': [ { 'k': /:.{0,1}D/, 'u': '\uD83D\uDE03', 'n': -1 }, { 'k': /(0|O):-{0,1}\)/i, 'u': '\uD83D\uDE07', 'n': -1 }, { 'k': /:beer:/, 'u': '\uD83C\uDF7A', 'n': -1 }, { 'k': /3:-{0,1}\)/, 'u': '\uD83D\uDE08', 'n': -1 }, { 'k': /<3/, 'u':'\u2764', 'n': -1 }, // disabled due to urls :/ // { // 'k': /o.O|:\/|:\\/, // 'u': '\uD83D\uDE15', // 'n': -1 // }, { 'k': /:\'\(/, 'u': '\uD83D\uDE22', 'n': -1 }, { 'k': /(:|=)-{0,1}\(/, 'u': '\uD83D\uDE1E', 'n': -1 }, { 'k': /8(\)<|\|)/, 'u': '\uD83D\uDE0E', 'n': -1 }, { 'k': /(:|=)-{0,1}(\)|\])/, 'u': '\uD83D\uDE0A', 'n': -1 }, { 'k': /(\(|\[)-{0,1}(:|=)/, 'u': '\uD83D\uDE0A', 'n': -1 }, { 'k': /:\*/, 'u': '\uD83D\uDE17', 'n': -1 }, { 'k': /\^-{0,1}\^/, 'u': '\uD83D\uDE06', 'n': -1 }, { 'k': /:p/i, 'u': '\uD83D\uDE1B', 'n': -1 }, { 'k': /;-{0,1}\)/, 'u': '\uD83D\uDE09', 'n': -1 }, { 'k': /\(-{0,1};/, 'u': '\uD83D\uDE09', 'n': -1 }, { 'k': /:(O|0)/, 'u': '\uD83D\uDE2E', 'n': -1 }, { 'k': /:@/, 'u': '\uD83D\uDE31', 'n': -1 }, { 'k': /:\|/, 'u': '\uD83D\uDE10', 'n': -1 } ], 'signs': [ { 'k': / tel(|:|=)/i, 'u': ' \u2121', 'n': 4 }, { 'k': /^tel(|:|=)/i, 'u': '\u2121', 'n': 3 }, { 'k': / fax(|:|=)/i, 'u': ' \u213B', 'n': 4 }, { 'k': /^fax(|:|=)/i, 'u': '\u213B', 'n': 3 } ], 'fractions': [ { 'k': /1\/2/, 'u': '\u00BD', 'n': -1 }, { 'k': /1\/3/, 'u': '\u2153', 'n': -1 }, { 'k': /2\/3/, 'u': '\u2154', 'n': -1 }, { 'k': /1\/4/, 'u': '\u00BC', 'n': -1 }, { 'k': /3\/4/, 'u': '\u00BE', 'n': -1 }, { 'k': /1\/5/, 'u': '\u2155', 'n': -1 }, { 'k': /2\/5/, 'u': '\u2156', 'n': -1 }, { 'k': /3\/5/, 'u': '\u2157', 'n': -1 }, { 'k': /4\/5/, 'u': '\u2158', 'n': -1 }, { 'k': /1\/6/, 'u': '\u2159', 'n': -1 }, { 'k': /5\/6/, 'u': '\u215A', 'n': -1 }, { 'k': /1\/7/, 'u': '\u2150', 'n': -1 }, { 'k': /1\/8/, 'u': '\u215B', 'n': -1 }, { 'k': /3\/8/, 'u': '\u215C', 'n': -1 }, { 'k': /5\/8/, 'u': '\u215D', 'n': -1 }, { 'k': /7\/8/, 'u': '\u215E', 'n': -1 }, { 'k': /1\/9/, 'u': '\u2151', 'n': -1 }, { 'k': /1\/10/, 'u': '\u2152', 'n': -1 } ]}; // Marks ranges in a message where unicode replacements will be ignored (inside URLs). function getRangesForUnicodeConversion(msg) { if (!msg) return; var tempMsg = msg; var results = []; var regexHttpStart = /http[s]?:\/\//; var regexHttpEnd = /[ \n\t]/; var start = 0, end, position, rep = true; position = tempMsg.search(regexHttpStart); while (position !== -1) { end = start + position; if (end > start) results.push({start: start, end: end, replace: rep}); rep = !rep; start = end; tempMsg = tempMsg.substring(position, tempMsg.length); if (rep === true) position = tempMsg.search(regexHttpStart); else position = tempMsg.search(regexHttpEnd); } end = msg.length; if (end > start) results.push({start: start, end: end, replace: rep}); return results; } function getUnicodeReplacement(msg, list, ranges, ta) { if (!msg || !list || !ranges) return; if (ranges.length === 0) return ''; var position, substrings = []; for (var j = 0; j < ranges.length; j++) { substrings[j] = msg.substring(ranges[j].start, ranges[j].end); if (ranges[j].replace === true) { for (var i = 0; i < list.length; i++) { position = substrings[j].search(list[i].k); if (position !== -1 && ta.data('disabledUnicodeRules').indexOf(list[i].u) === -1) { var oldSubstring = substrings[j]; substrings[j] = substrings[j].replace(list[i].k, list[i].u); var len = oldSubstring.length - substrings[j].length + list[i].u.length; ta.data('unicodeConversionStack').unshift({ 'k': oldSubstring.substr(position, len), 'u': list[i].u, 'p': ranges[j].start + position }); } } } } var returnString = substrings[0]; for (var j = 1; j < ranges.length; j++) { returnString += substrings[j]; } return returnString; } function convert2Unicodes(s, ta) { if (!ta.data('unicodeConversionStack')) // A stack of undo steps ta.data('unicodeConversionStack', []); if (!ta.data('disabledUnicodeRules')) // A list of conversion rules that are temporarily disabled ta.data('disabledUnicodeRules', []); var ranges = getRangesForUnicodeConversion(s); if ($.Options.unicodeConversion.val === 'enable' || $.Options.convertPunctuationsOpt.val) s = getUnicodeReplacement(s, unicodeConversionList.punctuation, ranges, ta); if ($.Options.unicodeConversion.val === 'enable' || $.Options.convertEmotionsOpt.val) s = getUnicodeReplacement(s, unicodeConversionList.emotions, ranges, ta); if ($.Options.unicodeConversion.val === 'enable' || $.Options.convertSignsOpt.val) s = getUnicodeReplacement(s, unicodeConversionList.signs, ranges, ta); if ($.Options.unicodeConversion.val === 'enable' || $.Options.convertFractionsOpt.val) s = getUnicodeReplacement(s, unicodeConversionList.fractions, ranges, ta); if (ta.data('unicodeConversionStack').length > 0) { var ub = ta.closest('.post-area-new').find('.undo-unicode'); ub.text(polyglot.t('undo') + ': ' + ta.data('unicodeConversionStack')[0].u); $.MAL.enableButton(ub); } else $.MAL.disableButton(ta.closest('.post-area-new').find('.undo-unicode')); return s; } function undoLastUnicode(e) { e.stopPropagation(); e.preventDefault(); var $ta = $(this).closest('.post-area-new').find('textarea'); if ($ta.data('unicodeConversionStack').length === 0) return; var uc = $ta.data('unicodeConversionStack').shift(); var pt = $ta.val(); // If the text was shifted, and character is no longer at the saved position, this function // searches for it to the right. If it is not there, it searches in the oposite direction. // if it's not there either, it means it was deleted, so it is skipped. var substrLeft = pt.substring(0, uc.p); var substrRight = pt.substring(uc.p, pt.length); if (substrRight.search(uc.u) !== -1) { substrRight = substrRight.replace(uc.u, uc.k); $ta.val(substrLeft + substrRight); $ta.data('disabledUnicodeRules').push(uc.u); } else if (substrLeft.search(uc.u) !== -1) { var closestToTheLeft = substrLeft.lastIndexOf(uc.u); var substrCenter = substrLeft.substring(closestToTheLeft, substrLeft.length).replace(uc.u, uc.k); substrLeft = substrLeft.substring(0, closestToTheLeft); $ta.val(substrLeft + substrCenter + substrRight); $ta.data('disabledUnicodeRules').push(uc.u); } if ($ta.data('unicodeConversionStack').length > 0) $(this).text(polyglot.t('undo') + ': ' + $ta.data('unicodeConversionStack')[0].u); else { $(this).text('undo'); $.MAL.disableButton($(this)); } } function postSubmit(e, oldLastPostId) { if (e instanceof $) { var $this = e; //check if previous part was sent... if (oldLastPostId === lastPostId) { setTimeout(postSubmit, 1000, $this, oldLastPostId); return; } } else { e.stopPropagation(); e.preventDefault(); var $this = $(this); } $.MAL.disableButton($this); var $replyText = $this.closest('.post-area-new').find('textarea'); var $postOrig = $this.closest('.post-data'); if (!$postOrig.length) { $postOrig = $this.closest('.modal-content').find('.post-data'); } if (splitedPostsCount > 1) { if ($replyText.length < splitedPostsCount) { //current part will be sent as reply to the previous part... $postOrig = $('
      '); } } if ($replyText.length <= 1) { if (splitedPostsCount > 1) { var postxt = ''; var reply_to = $replyText.attr('data-reply-to'); var val = $replyText.val(); if (typeof reply_to === 'undefined' || checkPostForMentions(val, reply_to, 140)) postxt = val + ' (' + splitedPostsCount.toString() + '/' + splitedPostsCount.toString() + ')'; else postxt = reply_to + val + ' (' + splitedPostsCount.toString() + '/' + splitedPostsCount.toString() + ')'; newPostMsg(postxt, $postOrig); } else newPostMsg($replyText.val(), $postOrig); splitedPostsCount = 1; } else { var postxt = ''; var reply_to = $replyText.attr('data-reply-to'); var val = $replyText[0].value; if (typeof reply_to === 'undefined' || checkPostForMentions(val, reply_to, 140)) postxt = val + ' (' + (splitedPostsCount - $replyText.length + 1).toString() + '/' + splitedPostsCount.toString() + ')'; else postxt = reply_to + val + ' (' + (splitedPostsCount - $replyText.length + 1).toString() + '/' + splitedPostsCount.toString() + ')'; $($replyText[0]).remove(); oldLastPostId = lastPostId; newPostMsg(postxt, $postOrig); setTimeout(postSubmit, 1000, $this, oldLastPostId); return; } $replyText.val('').attr('placeholder', polyglot.t('Your message was sent!')); var tweetForm = $this.parents('form'); var remainingCount = tweetForm.find('.post-area-remaining'); remainingCount.text(140); if ($this.parents('.modal-wrapper').length) closeModal(); else if ($this.parents('.prompt-wrapper').length) closeModalHandler('.prompt-wrapper'); if ($this.closest('.post-area,.post-reply-content')) { $('.post-area-new').removeClass('open').find('textarea').blur(); }; $replyText.data('unicodeConversionStack', []); $replyText.data('disabledUnicodeRules', []); } function retweetSubmit(e) { e.stopPropagation(); e.preventDefault(); newRtMsg($(this).closest('.prompt-wrapper').find('.post-data')); closeModalHandler('.prompt-wrapper'); } function changeStyle() { var style, profile, menu; var theme = $.Options.theme.val; if (theme === 'nin') { style = 'theme_nin/css/style.css'; profile = 'theme_nin/css/profile.css'; $.getScript('theme_nin/js/theme_option.js'); } else if (theme === 'calm') { style = 'theme_calm/css/style.css'; profile = 'theme_calm/css/profile.css'; } else if (theme === 'original') { style = 'css/style.css'; profile = 'css/profile.css'; $.getScript('theme_original/js/theme_option.js'); } $('#stylecss').attr('href', style); $('#profilecss').attr('href', profile); $('').appendTo('head'); setTimeout(function() {$(menu).removeAttr('style');}, 0); } function getMentionsForAutoComplete() { if (defaultScreenName && typeof followingUsers !== 'undefined') { var suggests = followingUsers.slice(); if (suggests.indexOf(defaultScreenName) > -1) suggests.splice(suggests.indexOf(defaultScreenName), 1); if (suggests.length > 0) { suggests.sort(); return [{ mentions: suggests, match: /\B@(\w*)$/, search: function (term, callback) { callback($.map(this.mentions, function (mention) { return mention.indexOf(term) === 0 ? mention : null; })); }, index: 1, replace: function (mention) { return '@'+mention+' '; } }]; } } } function replaceDashboards() { var width = $(window).width(); var wrapper = $('.wrapper'); if (width >= 1200 && !wrapper.hasClass('w1200')) { wrapper.addClass('w1200'); $('.userMenu').addClass('w1200'); $('.module.who-to-follow').detach().appendTo($('.dashboard.right')); $('.module.twistday-reminder').detach().appendTo($('.dashboard.right')); } else if (width < 1200 && wrapper.hasClass('w1200')) { wrapper.removeClass('w1200'); $('.userMenu').removeClass('w1200'); $('.module.who-to-follow').detach().insertAfter($('.module.mini-profile')); $('.module.twistday-reminder').detach().insertAfter($('.module.toptrends')); } } function initInterfaceCommon() { $('.cancel').on('click', function() { if ($('.modal-content').attr('style') != undefined) $('.modal-content').removeAttr('style'); $('.modal-back').css('display', 'none'); $('.mark-all-as-read').css('display', 'none'); closeModal(); }); $('.prompt-close').on('click', function(e) { e.stopPropagation(); closeModalHandler('.prompt-wrapper'); }); /* $('.modal-back').on('click', function() { if ($('.modal-content .direct-messages-list')[0]) return; directMessagesPopup(); $('.modal-content').removeAttr('style'); }); */ $('.post-text').on('click', 'a', function(e) {e.stopPropagation();}); $('.post-reply').on('click', postReplyClick); $('.post-propagate').on('click', reTwistPopup); $('.userMenu-config').clickoutside(closeThis.bind($('.config-menu'))); $('.userMenu-config-dropdown').on('click', dropDownMenu); $('.module.post').on('click', function(e) { if (e.button === 0 && window.getSelection() == 0) postExpandFunction(e,$(this)); }); $('.post-area-new') .on('click', function(e) {composeNewPost(e, $(this));}) .clickoutside(unfocusThis) .children('textarea') .on({ 'input': replyTextInput, // input event fires in modern browsers (IE9+) on any changes in textarea (and copypasting with mouse too) 'input focus': replyTextUpdateRemaining, 'keyup': replyTextKeySend }) ; $('.post-submit').on('click', postSubmit); $('.modal-propagate').on('click', retweetSubmit); $('.expanded-content .show-more').on('click', openConversationClick); if ($.Options.unicodeConversion.val === 'disable') $('.undo-unicode').on('click', undoLastUnicode).css('display', 'none'); else $('.undo-unicode').on('click', undoLastUnicode); $('.open-profile-modal').on('click', function(e) {e.stopPropagation();}); //$('.open-hashtag-modal').on('click', openHashtagModal); //$('.open-following-modal').on('click', openFollowingModal); $('.userMenu-connections a').on('click', openMentionsModal); $('.mentions-from-user').on('click', openMentionsModal); $('#hashtag-modal-template .postboard-news').on('click', function () { $(this).hide(); displayHashtagPending($('.hashtag-modal .postboard-posts')); }); replaceDashboards(); $(window).resize(replaceDashboards); $('.tox-ctc').on('click', promptCopyAttrData); $('.bitmessage-ctc').on('click', promptCopyAttrData); if ($.fn.textcomplete) { $('textarea').on({ 'focus': setTextcompleteOnEventTarget, 'focusout': function () {$(this).textcomplete('destroy');} }); } } function promptCopyAttrData(event) { window.prompt(polyglot.t('copy_to_clipboard'), $(event.target).attr('data')); } function initInterfaceModule(module) { return $('.module.'+module).html($('#'+module+'-template').html()).show(); } function killInterfaceModule(module) { $('.module.'+module).empty().hide(); } function setTextcompleteOnEventTarget(event) { // cursor has not set yet and we need to wait 100ms to skip global click event setTimeout(setTextcompleteOnElement, 100, event.target); } function setTextcompleteOnElement(elem) { elem = $(elem); elem.textcomplete(getMentionsForAutoComplete(), { appendTo: (elem.parents('.dashboard').length) ? elem.parent() : $('body'), listPosition: setTextcompleteDropdownListPos }); } // following workaround function is for calls from $.fn.textcomplete only // we need this because currently implementation of caret position detection is way too imperfect function setTextcompleteDropdownListPos(position) { position = this._applyPlacement(position); if (this.option.appendTo.parents('.dashboard').length > 0) { position.position = 'fixed'; position.top = (parseFloat(position.top) - window.pageYOffset).toString() + 'px'; } else position.position = 'absolute'; this.$el.css(position); return this; }