// 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 preventSlide = false; //dispara o modal genérico //o modalClass me permite fazer tratamentos específicos de CSS para cada modal function openModal( modalClass ) { var $oldModal = $("body").children(".modal-blackout"); var $template = $( "#templates" ); var $templateModal = $template.find( ".modal-blackout" ).clone(true); $templateModal.addClass( modalClass ); if( $oldModal.length ) { $templateModal.show(); $oldModal.replaceWith($templateModal); } else { $templateModal.prependTo( "body" ).fadeIn( "fast" ); } //escondo o overflow da tela var $body = $( "body" ); $body.css({ "overflow": "hidden", }) } //fecha o modal removendo o conteúdo por detach function closeModal($this) { var $body = $( "body" ); var $modalWindows = $( "body" ).children( ".modal-blackout" ); $modalWindows.fadeOut( "fast", function() { $modalWindows.detach(); }); $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 < 60 * 60) { expression = polyglot.t("minutes", Math.floor(t_delta/60)); } else if(t_delta < 24 * 60 * 60) { expression = polyglot.t("hours", Math.floor(t_delta/60/60)); } else { expression = polyglot.t("days", Math.floor(t_delta/24/60/60)); } return polyglot.t("time_ago", { time: expression }); } // // Profile, mentions, hashtag, and following modal // ----------------------------------- function newProfileModal(username) { var profileModalContent = $( "#profile-modal-template" ).children().clone(true); updateProfileData(profileModalContent, username); return profileModalContent; } function openProfileModal(e) { e.stopPropagation(); e.preventDefault(); var $this = $( this ); var username = $.MAL.urlToUser( $this.attr("href") ); if(!username) { alert(polyglot.t("You don't have any profile because you are not logged in.")); return; } var profileModalClass = "profile-modal"; openModal( profileModalClass ); var profileModalContent = newProfileModal( username ); profileModalContent.appendTo("." +profileModalClass + " .modal-content"); //título do modal $( "."+profileModalClass + " h3" ).text( polyglot.t("users_profile", { username: username }) ); //hed//add dinamic follow button in profile modal window if(followingUsers.indexOf(username) != -1){ $('.profile-card button.followButton').first().removeClass('follow').addClass('profileUnfollow').text(polyglot.t('Unfollow')).on('click', function(){ unfollow(username); }); }; } function newHashtagModal(hashtag) { var hashtagModalContent = $( "#hashtag-modal-template" ).children().clone(true); hashtagModalContent.find( ".postboard-news").click(function (){ $(this).hide(); displayHashtagPending($(".hashtag-modal .postboard-posts")); }); clearHashtagProcessed(); updateHashtagModal( hashtagModalContent.find(".postboard-posts"), hashtag ); return hashtagModalContent; } function openHashtagModal(e) { e.stopPropagation(); e.preventDefault(); var $this = $( this ); var hashtag = $this.text().substring(1).toLowerCase(); var hashtagModalClass = "hashtag-modal"; openModal( hashtagModalClass ); $( "."+hashtagModalClass ).attr("data-resource","hashtag"); var hashtagModalContent = newHashtagModal( hashtag ); hashtagModalContent.appendTo("." +hashtagModalClass + " .modal-content"); //título do modal $( "."+hashtagModalClass + " h3" ).text( "#" + hashtag ); } function updateHashtagModal(postboard,hashtag) { var $hashtagModalClass = $(".hashtag-modal"); if( !$hashtagModalClass.length || $hashtagModalClass.css("display") == 'none' ) return; var resource = $hashtagModalClass.attr("data-resource"); requestHashtag(postboard,hashtag,resource); setTimeout( function() {updateHashtagModal(postboard,hashtag);}, 5000); } function openMentionsModal(e) { e.stopPropagation(); e.preventDefault(); if(!defaultScreenName) { alert(polyglot.t("No one can mention you because you are not logged in.")); return; } // reuse the same hashtag modal to show mentions var hashtagModalClass = "hashtag-modal"; openModal( hashtagModalClass ); $( "."+hashtagModalClass ).attr("data-resource","mention"); var username; var $userInfo = $(this).closest("[data-screen-name]"); if( $userInfo.length ) username = $userInfo.attr("data-screen-name"); else username = defaultScreenName; var hashtagModalContent = newHashtagModal( username ); hashtagModalContent.appendTo("." +hashtagModalClass + " .modal-content"); //título do modal $( "."+hashtagModalClass + " h3" ).text( polyglot.t("users_mentions", { username: username }) ); // obtain already cached mention posts from twister_newmsgs.js processHashtag(hashtagModalContent.find(".postboard-posts"), defaultScreenName, getMentionsData() ); resetMentionsCount(); } function newFollowingModal(username) { var followingModalContent = $( "#following-modal-template" ).children().clone(true); updateFollowingData(followingModalContent, username); return followingModalContent; } function openFollowingModal(e) { e.stopPropagation(); e.preventDefault(); var $this = $( this ); var username = $.MAL.followingUrlToUser( $this.attr("href") ); var followingModalClass = "following-modal"; openModal( followingModalClass ); var followingModalContent = newFollowingModal( username ); followingModalContent.appendTo("." +followingModalClass + " .modal-content"); //título do modal $( "."+followingModalClass + " h3" ).text( polyglot.t("followed_by", { username: username }) ); } // // Post actions, submit, count characters // -------------------------------------- //dispara o modal de retweet var reTwistPopup = function( e ) { if(!defaultScreenName) { preventSlide=true; alert(polyglot.t("You have to log in to retransmit messages.")); return; } var reTwistClass = "reTwist"; openModal( reTwistClass ); //título do modal $( ".reTwist h3" ).text( polyglot.t("retransmit_this") ); var postdata = $(this).parents(".post-data").attr("data-userpost"); var postElem = postToElem($.evalJSON(postdata),""); postElem.appendTo( ".reTwist .modal-content" ); e.stopPropagation(); } //Expande Área do Novo post var replyInitPopup = function(e, post) { var replyClass = "reply"; openModal( replyClass ); //título do modal var fullname = post.find(".post-info-name").text(); $( ".reply h3" ).text( polyglot.t("reply_to", { fullname: fullname }) ); //para poder exibir a thread selecionada... var replyModalContent = $(".reply .modal-content").hide(); var retweetContent = $( "#reply-modal-template" ).children().clone(true); retweetContent.appendTo(replyModalContent); var postdata = post.find(".post-data").attr("data-userpost"); var postElem = postToElem($.evalJSON(postdata),""); postElem.appendTo(replyModalContent); var replyArea = $(".reply .post-area .post-area-new"); replyArea.addClass("open"); var replyText = replyArea.find("textarea"); var postInlineReplyText = $(".reply .post .post-area-new textarea"); var attrToCopy = ["placeholder", "data-reply-to"]; $.each(attrToCopy, function( i, attribute ) { replyText.attr( attribute, postInlineReplyText.attr(attribute) ); }); composeNewPost(e, replyArea); replyModalContent.fadeIn( "fast" ); } //abre o menu dropdown de configurações var dropDownMenu = function( e ) { var $configMenu = $( ".config-menu" ); $configMenu.slideToggle( "fast" ); e.stopPropagation(); } //fecha o config menu ao clicar em qualquer lugar da tela var closeThis = function() { $( this ).slideUp( "fast" ); }; var postExpandFunction = function( e, postLi ) { if( !postLi.hasClass( "original" ) ) { return; } var originalPost = postLi.find(".post-data.original"); var $postInteractionText = originalPost.find( ".post-expand" ); var $postExpandedContent = originalPost.find( ".expanded-content" ); var $postsRelated = postLi.find(".related"); var openClass = "open"; //This is used in "guest mode", when user gets an alert that he can't reply or retransmit //when not logged in. Otherwise, this click would also be understood as a command to open/close //the post, which would look weird. if(preventSlide) { preventSlide=false; } else 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); originalLi.append(originalPost); $postExpandedContent.slideDown( "fast" ); if ($.Options.getShowPreviewOpt() == '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?*/ if (link.siblings().length > 0){ if (/(\.jpg)|(\.gif)|(\.png)|(\.jpeg)/.test(link.html().toLowerCase())){ $(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( polyglot.t("Expand") ); if( $postsRelated ) $postsRelated.slideUp( "fast" ); $postExpandedContent.slideUp( "fast", function() { originalPost.detach(); postLi.empty(); postLi.append(originalPost); }); } e.stopPropagation(); } var postReplyClick = function( e ) { if(!defaultScreenName) { preventSlide=true; alert(polyglot.t("You have to log in to post replies.")); return; } var post = $(this).closest(".post"); if( !post.hasClass( "original" ) ) { replyInitPopup(e, post); } else { var postLiOpen = post.parents(".post.open"); if( !postLiOpen.length ) { postExpandFunction(e, post); } var postAreaNew = post.find(".post-area-new") composeNewPost(e, postAreaNew); } e.stopPropagation(); } //Expande Área do Novo post var composeNewPost = function( e, postAreaNew ) { e.stopPropagation(); if( !postAreaNew.hasClass("open") ) { postAreaNew.addClass( "open" ); //se o usuário clicar fora é pra fechar postAreaNew.clickoutside( unfocusThis ) } var textArea = postAreaNew.find("textarea"); textArea.focus(); if( textArea.attr("data-reply-to") && !textArea.val().length ) { textArea.val(textArea.attr("data-reply-to")); } } //Reduz Área do Novo post var unfocusThis = function() { var $this = $( this ); $this.removeClass( "open" ); } function replyTextKeypress(e) { e = e || event; var $this = $( this ); var tweetForm = $this.parents("form"); if( tweetForm != undefined ) { if ($.Options.getUnicodeConversionOpt() !== "disable") $this.val(convert2Unicodes($this.val(), $this)); var c = 140 - $this.val().length; var remainingCount = tweetForm.find(".post-area-remaining"); remainingCount.text(c); if( c < 0 ) remainingCount.addClass("warn"); else remainingCount.removeClass("warn"); var tweetAction = tweetForm.find(".post-submit"); if( !tweetAction.length ) tweetAction = tweetForm.find(".dm-submit"); if( c >= 0 && c < 140 && $this.val() != $this.attr("data-reply-to") ) { $.MAL.enableButton(tweetAction); } else { $.MAL.disableButton(tweetAction); } if( $.Options.keyEnterToSend() && $('.dropdown-menu').css('display') == 'none'){ if (e.keyCode === 13 && (!e.metaKey && !e.ctrlKey)) { $this.val($this.val().trim()); if( !tweetAction.hasClass("disabled")) { tweetAction.click(); } } }else if( !$.Options.keyEnterToSend() ){ if (e.keyCode === 13 && (e.metaKey || e.ctrlKey)) { $this.val($this.val().trim()); if( !tweetAction.hasClass("disabled") ) { tweetAction.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 = { "ponctuations": [ { "k": /\.\.\./, "u": "…", "n": -1 }, { "k": /\.\../, "u": "‥", "n": 2 }, { "k": /\?\?/, "u": "⁇", "n": -1 }, { "k": /\?!/, "u": "⁈", "n": -1 }, { "k": /!\?/, "u": "⁉", "n": -1 }, { "k": /!!/, "u": "‼", "n": -1 } ], "emotions": [ { "k": /:.{0,1}D/, "u": "😃", "n": -1 }, { "k": /(0|O):-{0,1}\)/i, "u": "😇", "n": -1 }, { "k": /:beer:/, "u": "🍺", "n": -1 }, { "k": /3:.{0,1}\)/, "u": "😈", "n": -1 }, { "k": /<3/, "u":"❤", "n": -1 }, { "k": /o.O|:\/|:\\/, "u": "😕", "n": -1 }, { "k": /:\'\(/, "u": "😢", "n": -1 }, { "k": /(:|=).{0,1}\(/, "u": "😞", "n": -1 }, { "k": /8(\)<|\|)/, "u": "😎", "n": -1 }, { "k": /(:|=)-{0,1}(\)|\])/, "u": "😊", "n": -1 }, { "k": /(\(|\[)-{0,1}(:|=)/, "u": "😊", "n": -1 }, { "k": /:\*/, "u": "😗", "n": -1 }, { "k": /\^.{0,1}\^/, "u": "😆", "n": -1 }, { "k": /:p/i, "u": "😛", "n": -1 }, { "k": /;-{0,1}\)/, "u": "😉", "n": -1 }, { "k": /\(-{0,1};/, "u": "😉", "n": -1 }, { "k": /:(O|0)/, "u": "😮", "n": -1 }, { "k": /:@/, "u": "😱", "n": -1 } ], "signs": [ { "k": / tel( |:|=)/i, "u": " ℡", "n": 4 }, { "k": /^tel( |:|=)/i, "u": "℡", "n": 3 }, { "k": / fax( |:|=)/i, "u": " ℻", "n": 4 }, { "k": /^fax( |:|=)/i, "u": "℻", "n": 3 } ], "fractions": [ { "k": /1\/2/, "u": "½", "n": -1 }, { "k": /1\/3/, "u": "⅓", "n": -1 }, { "k": /2\/3/, "u": "⅔", "n": -1 }, { "k": /1\/4/, "u": "¼", "n": -1 }, { "k": /3\/4/, "u": "¾", "n": -1 }, { "k": /1\/5/, "u": "⅕", "n": -1 }, { "k": /2\/5/, "u": "⅖", "n": -1 }, { "k": /3\/5/, "u": "⅗", "n": -1 }, { "k": /4\/5/, "u": "⅘", "n": -1 }, { "k": /1\/6/, "u": "⅙", "n": -1 }, { "k": /5\/6/, "u": "⅚", "n": -1 }, { "k": /1\/7/, "u": "⅐", "n": -1 }, { "k": /1\/8/, "u": "⅛", "n": -1 }, { "k": /3\/8/, "u": "⅜", "n": -1 }, { "k": /5\/8/, "u": "⅝", "n": -1 }, { "k": /7\/8/, "u": "⅞", "n": -1 }, { "k": /1\/9/, "u": "⅑", "n": -1 }, { "k": /1\/10/, "u": "⅒", "n": -1 } ] }; /* * a stack for undo... * element for the unicodeConversionStack * { k: original string that's replaced u: unicode p: position in string l: length of k m: new string length after convertion * } */ var unicodeConversionStack = []; //we want to handle new typed text after the last conversion. var lastConvertedIndex = -1; function convert2Unicodes(s, ta) { var tmp = s; //check if there is a deletion... //NOTE: BUGGY... can't handle everytime... if (unicodeConversionStack.length>0 && s.length < unicodeConversionStack[0].m){ //check if a replaced unicode was deleted... for (var i=unicodeConversionStack.length-1; i>=0; i--){ //get position and check the positions are same... var ni = s.indexOf(unicodeConversionStack[i].u); if (ni > -1 && s[ni] !== unicodeConversionStack[i].p){ var op = -1; for (var j=i-1; j>=0; j--){ if (unicodeConversionStack[j].u === unicodeConversionStack[i].u){ if (unicodeConversionStack[j].p === ni){ op = -1; break; } op = unicodeConversionStack[j].p; } } if (op === -1) { //remove deleted unicode... unicodeConversionStack.splice(i, 1); } else { //update the position of the unicode! unicodeConversionStack[i].p = ni; } } } unicodeConversionStack[0].m = s.length; } if (s.length < lastConvertedIndex) lastConvertedIndex = s.length; if ($.Options.getUnicodeConversionOpt() === "enable" || $.Options.getConvertPunctuationsOpt()){ var list = unicodeConversionList.ponctuations; for (var i=0; i 0 && kl.index >= lastConvertedIndex) { var nc = ""; if (list[i].n > -1){ //if it's necessary, get any next char to prevent from any data loss nc = tmp[kl.index + list[i].n]; } tmp = tmp.replace(list[i].k, list[i].u + nc); var len = s.length - tmp.length + list[i].u.length; unicodeConversionStack.unshift({ "k": s.substr(kl.index, len), "u": list[i].u + nc, "p": kl.index, "l": len, "m": tmp.length }); s = tmp; lastConvertedIndex = tmp.length; } } } if ($.Options.getUnicodeConversionOpt() === "enable"|| $.Options.getConvertEmotionsOpt()){ var list = unicodeConversionList.emotions; for (var i=0; i 0 && kl.index >= lastConvertedIndex) { var nc = ""; if (list[i].n > -1){ //if it's necessary, get any next char to prevent from any data loss nc = tmp[kl.index + list[i].n]; } tmp = tmp.replace(list[i].k, list[i].u + nc); var len = s.length - tmp.length + list[i].u.length; unicodeConversionStack.unshift({ "k": s.substr(kl.index, len), "u": list[i].u + nc, "p": kl.index, "l": len, "m": tmp.length }); s = tmp; lastConvertedIndex = tmp.length; } } } if ($.Options.getUnicodeConversionOpt() === "enable"|| $.Options.getConvertSignsOpt()){ var list = unicodeConversionList.signs; for (var i=0; i 0 && kl.index >= lastConvertedIndex) { var nc = ""; if (list[i].n > -1){ //if it's necessary, get any next char to prevent from any data loss nc = tmp[kl.index + list[i].n]; } tmp = tmp.replace(list[i].k, list[i].u + nc); var len = s.length - tmp.length + list[i].u.length; unicodeConversionStack.unshift({ "k": s.substr(kl.index, len), "u": list[i].u + nc, "p": kl.index, "l": len, "m": tmp.length }); s = tmp; lastConvertedIndex = tmp.length; } } } if ($.Options.getUnicodeConversionOpt() === "enable"|| $.Options.getConvertFractionsOpt()){ var list = unicodeConversionList.fractions; for (var i=0; i 0 && kl.index >= lastConvertedIndex) { var nc = ""; if (list[i].n > -1){ //if it's necessary, get any next char to prevent from any data loss nc = tmp[kl.index + list[i].n]; } tmp = tmp.replace(list[i].k, list[i].u + nc); var len = s.length - tmp.length + list[i].u.length; unicodeConversionStack.unshift({ "k": s.substr(kl.index, len), "u": list[i].u + nc, "p": kl.index, "l": len, "m": tmp.length }); s = tmp; lastConvertedIndex = tmp.length; } } } if (unicodeConversionStack.length > 0){ var ub = ta.closest(".post-area-new").find(".undo-unicode"); ub.text("undo: " + unicodeConversionStack[0].u); $.MAL.enableButton(ub); } else { $.MAL.disableButton(ta.closest(".post-area-new").find(".undo-unicode")); } return tmp; } //BUGGY... if user deletes something in the middle, stack could be deformed... function undoLastUnicode(e) { e.stopPropagation(); e.preventDefault(); if (unicodeConversionStack.length === 0) return; var uc = unicodeConversionStack.shift(); $ta = $(this).closest(".post-area-new").find("textarea"); var pt = $ta.val(); if (pt.substr(uc.p, uc.u.length) === uc.u) $ta.val(pt.substr(0,uc.p) + uc.k + pt.substr(uc.p + 2)); else { //if it can't be found at its index, last unicode will be removed var i = pt.lastIndexOf(uc.u); if (i>-1) { $ta.val(pt.substr(0,i) + uc.k + pt.substr(i + 2)); } } if (unicodeConversionStack.length > 0) $(this).text("undo: " + unicodeConversionStack[0].u); else $.MAL.disableButton($(this)); } var postSubmit = function(e) { e.stopPropagation(); e.preventDefault(); var $this = $( 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"); } newPostMsg($replyText.val(), $postOrig); $replyText.val(""); $replyText.attr("placeholder", polyglot.t("Your message was sent!")); var tweetForm = $this.parents("form"); var remainingCount = tweetForm.find(".post-area-remaining"); remainingCount.text(140); $replyText.attr("placeholder", "Your message was sent!"); closeModal($this); if($this.closest('.post-area,.post-reply-content')){ $('.post-area-new').removeClass('open').find('textarea').blur(); }; setTimeout('requestTimelineUpdate("latest",postsPerRefresh,followingUsers,promotedPostsOnly)', 1000); } var retweetSubmit = function(e) { e.stopPropagation(); e.preventDefault(); var $this = $( this ); var $postOrig = $this.closest(".modal-wrapper").find(".post-data"); newRtMsg($postOrig); closeModal($this); } function initInterfaceCommon() { $( "body" ).on( "click", function(event) { if($(event.target).hasClass('cancel')) closeModal($(this)); }); $( ".post-reply" ).bind( "click", postReplyClick ); $( ".post-propagate" ).bind( "click", reTwistPopup ); $( ".userMenu-config-dropdown" ).bind( "click", dropDownMenu ); $( ".config-menu" ).clickoutside( closeThis ); $( ".module.post" ).bind( "click", function(e) { if(window.getSelection() == 0)postExpandFunction(e,$(this)); }); $( ".post-area-new" ).bind( "click", function(e) { composeNewPost(e,$(this));} ); $( ".post-area-new" ).clickoutside( unfocusThis ); $( ".post-submit").click( postSubmit ); $( ".modal-propagate").click( retweetSubmit ); if ($.Options.getUnicodeConversionOpt() === "disable") $( ".undo-unicode" ).click( undoLastUnicode ).css("display", "none"); else $( ".undo-unicode" ).click( undoLastUnicode ); var $replyText = $( ".post-area-new textarea" ); $replyText.on("keyup", replyTextKeypress ); $( ".open-profile-modal").bind( "click", openProfileModal ); $( ".open-hashtag-modal").bind( "click", openHashtagModal ); $( ".open-following-modal").bind( "click", openFollowingModal ); $( ".userMenu-connections a").bind( "click", openMentionsModal ); }