mirror of
https://github.com/chylex/TweetDuck.git
synced 2025-08-21 03:54:07 +02:00
.github
Application
Browser
Configuration
Controls
Dialogs
Management
Plugins
Properties
Resources
Design
Guide
Images
Plugins
Scripts
imports
markup
scripts
browser.features.js
browser.globals.js
browser.tweaks.js
plugins.base.js
styles
pages
styles
code.js
introduction.js
notification.js
plugins.browser.js
plugins.notification.js
screenshot.js
twitter.js
update.js
Utilities
..code-workspace
PostBuild.fsx
PostCefUpdate.ps1
ScriptLoader.cs
ScriptLoaderDebug.cs
Updates
Utils
bld
lib
subprocess
video
.gitattributes
.gitignore
LICENSE.md
Program.cs
README.md
Reporter.cs
TweetDuck.csproj
TweetDuck.sln
TweetDuck.sln.DotSettings
Version.cs
packages.config
671 lines
22 KiB
JavaScript
671 lines
22 KiB
JavaScript
(function(){
|
|
//
|
|
// Function: Event callback for a new tweet.
|
|
//
|
|
const onNewTweet = (function(){
|
|
const recentMessages = new Set();
|
|
const recentTweets = new Set();
|
|
let recentTweetTimer = null;
|
|
|
|
const resetRecentTweets = () => {
|
|
recentTweetTimer = null;
|
|
recentTweets.clear();
|
|
};
|
|
|
|
const startRecentTweetTimer = () => {
|
|
recentTweetTimer && window.clearTimeout(recentTweetTimer);
|
|
recentTweetTimer = window.setTimeout(resetRecentTweets, 20000);
|
|
};
|
|
|
|
const checkTweetCache = (set, id) => {
|
|
return true if set.has(id);
|
|
|
|
if (set.size > 50){
|
|
set.clear();
|
|
}
|
|
|
|
set.add(id);
|
|
return false;
|
|
};
|
|
|
|
const isSensitive = (tweet) => {
|
|
const main = tweet.getMainTweet && tweet.getMainTweet();
|
|
return true if main && main.possiblySensitive; // TODO these don't show media badges when hiding sensitive media
|
|
|
|
const related = tweet.getRelatedTweet && tweet.getRelatedTweet();
|
|
return true if related && related.possiblySensitive;
|
|
|
|
const quoted = tweet.quotedTweet;
|
|
return true if quoted && quoted.possiblySensitive;
|
|
|
|
return false;
|
|
};
|
|
|
|
const fixMedia = (html, media) => {
|
|
return html.find("a[data-media-entity-id='" + media.mediaId + "'], .media-item").first().removeClass("is-zoomable").css("background-image", 'url("' + media.small() + '")');
|
|
};
|
|
|
|
return function(column, tweet){
|
|
if (tweet instanceof TD.services.TwitterConversation || tweet instanceof TD.services.TwitterConversationMessageEvent){
|
|
return if checkTweetCache(recentMessages, tweet.id);
|
|
}
|
|
else{
|
|
return if checkTweetCache(recentTweets, tweet.id);
|
|
}
|
|
|
|
startRecentTweetTimer();
|
|
|
|
if (column.model.getHasNotification()){
|
|
const sensitive = isSensitive(tweet);
|
|
const previews = $TDX.notificationMediaPreviews && (!sensitive || TD.settings.getDisplaySensitiveMedia());
|
|
// TODO new cards don't have either previews or links
|
|
|
|
const html = $(tweet.render({
|
|
withFooter: false,
|
|
withTweetActions: false,
|
|
withMediaPreview: true,
|
|
isMediaPreviewOff: !previews,
|
|
isMediaPreviewSmall: previews,
|
|
isMediaPreviewLarge: false,
|
|
isMediaPreviewCompact: false,
|
|
isMediaPreviewInQuoted: previews,
|
|
thumbSizeClass: "media-size-medium",
|
|
mediaPreviewSize: "medium"
|
|
}));
|
|
|
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
|
html.find(".js-quote-detail").removeClass("is-actionable margin-b--8"); // prevent quoted tweets from changing the cursor and reduce bottom margin
|
|
|
|
if (previews){
|
|
html.find(".reverse-image-search").remove();
|
|
|
|
const container = html.find(".js-media");
|
|
|
|
for(let media of tweet.getMedia()){
|
|
fixMedia(container, media);
|
|
}
|
|
|
|
if (tweet.quotedTweet){
|
|
for(let media of tweet.quotedTweet.getMedia()){
|
|
fixMedia(container, media).addClass("media-size-medium");
|
|
}
|
|
}
|
|
}
|
|
else if (tweet instanceof TD.services.TwitterActionOnTweet){
|
|
html.find(".js-media").remove();
|
|
}
|
|
|
|
html.find("a[data-full-url]").each(function(){ // bypass t.co on all links and fix tooltips
|
|
this.href = this.getAttribute("data-full-url");
|
|
this.removeAttribute("title");
|
|
});
|
|
|
|
html.find("a[href='#']").each(function(){ // remove <a> tags around links that don't lead anywhere (such as account names the tweet replied to)
|
|
this.outerHTML = this.innerHTML;
|
|
});
|
|
|
|
html.find("p.link-complex-target").filter(function(){
|
|
return $(this).text() === "Show this thread";
|
|
}).first().each(function(){
|
|
this.id = "tduck-show-thread";
|
|
|
|
const moveBefore = html.find(".tweet-body > .js-media, .tweet-body > .js-media-preview-container, .quoted-tweet");
|
|
|
|
if (moveBefore){
|
|
$(this).css("margin-top", "5px").removeClass("margin-b--5").parent("span").detach().insertBefore(moveBefore);
|
|
}
|
|
});
|
|
|
|
if (tweet.quotedTweet){
|
|
html.find("p.txt-mute").filter(function(){
|
|
return $(this).text() === "Show this thread";
|
|
}).first().remove();
|
|
}
|
|
|
|
const type = tweet.getChirpType();
|
|
|
|
if (type === "follow"){
|
|
html.find(".js-user-actions-menu").parent().remove();
|
|
html.find(".account-bio").removeClass("padding-t--5").css("padding-top", "2px");
|
|
}
|
|
else if ((type.startsWith("favorite") || type.startsWith("retweet")) && tweet.isAboutYou()){
|
|
html.children().first().addClass("td-notification-padded");
|
|
}
|
|
else if (type.includes("list_member")){
|
|
html.children().first().addClass("td-notification-padded td-notification-padded-alt");
|
|
html.find(".activity-header").css("margin-top", "2px");
|
|
html.find(".avatar").first().css("margin-bottom", "0");
|
|
}
|
|
|
|
if (sensitive){
|
|
html.find(".media-badge").each(function(){
|
|
$(this)[0].lastChild.textContent += " (possibly sensitive)";
|
|
});
|
|
}
|
|
|
|
const source = tweet.getRelatedTweet();
|
|
const duration = source ? source.text.length + (source.quotedTweet ? source.quotedTweet.text.length : 0) : tweet.text.length;
|
|
|
|
const chirpId = source ? source.id : "";
|
|
const tweetUrl = source ? source.getChirpURL() : "";
|
|
const quoteUrl = source && source.quotedTweet ? source.quotedTweet.getChirpURL() : "";
|
|
|
|
$TD.onTweetPopup(column.model.privateState.apiid, chirpId, window.TDGF_getColumnName(column), html.html(), duration, tweetUrl, quoteUrl);
|
|
}
|
|
|
|
if (column.model.getHasSound()){
|
|
$TD.onTweetSound();
|
|
}
|
|
};
|
|
})();
|
|
|
|
//
|
|
// Block: Enable popup notifications.
|
|
//
|
|
execSafe(function hookDesktopNotifications(){
|
|
ensurePropertyExists(TD, "controller", "notifications");
|
|
|
|
TD.controller.notifications.hasNotifications = function(){
|
|
return true;
|
|
};
|
|
|
|
TD.controller.notifications.isPermissionGranted = function(){
|
|
return true;
|
|
};
|
|
|
|
$.subscribe("/notifications/new", function(obj){
|
|
for(let index = obj.items.length - 1; index >= 0; index--){
|
|
onNewTweet(obj.column, obj.items[index]);
|
|
}
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Fix DM notifications not showing if the conversation is open.
|
|
//
|
|
if (checkPropertyExists(TD, "vo", "Column", "prototype", "mergeMissingChirps")){
|
|
TD.vo.Column.prototype.mergeMissingChirps = prependToFunction(TD.vo.Column.prototype.mergeMissingChirps, function(e){
|
|
const model = this.model;
|
|
|
|
if (model && model.state && model.state.type === "privateMe" && !this.notificationsDisabled && e.poller.feed.managed){
|
|
const unread = [];
|
|
|
|
for(let chirp of e.chirps){
|
|
if (Array.isArray(chirp.messages)){
|
|
Array.prototype.push.apply(unread, chirp.messages.filter(message => message.read === false));
|
|
}
|
|
}
|
|
|
|
if (unread.length > 0){
|
|
if (checkPropertyExists(TD, "util", "chirpReverseColumnSort")){
|
|
unread.sort(TD.util.chirpReverseColumnSort);
|
|
}
|
|
|
|
for(let message of unread){
|
|
onNewTweet(this, message);
|
|
}
|
|
|
|
// TODO sound notifications are borked as well
|
|
// TODO figure out what to do with missed notifications at startup
|
|
}
|
|
}
|
|
});
|
|
}
|
|
})();
|
|
|
|
//
|
|
// Block: Mute sound notifications.
|
|
//
|
|
HTMLAudioElement.prototype.play = prependToFunction(HTMLAudioElement.prototype.play, function(){
|
|
return $TDX.muteNotifications;
|
|
});
|
|
|
|
//
|
|
// Block: Add additional link information to context menu.
|
|
//
|
|
execSafe(function setupLinkContextMenu(){
|
|
$(document.body).delegate("a", "contextmenu", function(){
|
|
const me = $(this)[0];
|
|
|
|
if (me.classList.contains("js-media-image-link")){
|
|
const hovered = getHoveredTweet();
|
|
return if !hovered;
|
|
|
|
const tweet = hovered.obj.hasMedia() ? hovered.obj : hovered.obj.quotedTweet;
|
|
const media = tweet.getMedia().find(media => media.mediaId === me.getAttribute("data-media-entity-id"));
|
|
|
|
if ((media.isVideo && media.service === "twitter") || media.isAnimatedGif){
|
|
$TD.setRightClickedLink("video", media.chooseVideoVariant().url);
|
|
}
|
|
else{
|
|
$TD.setRightClickedLink("image", media.large());
|
|
}
|
|
}
|
|
else if (me.classList.contains("js-gif-play")){
|
|
$TD.setRightClickedLink("video", $(this).closest(".js-media-gif-container").find("video").attr("src"));
|
|
}
|
|
else if (me.hasAttribute("data-full-url")){
|
|
$TD.setRightClickedLink("link", me.getAttribute("data-full-url"));
|
|
}
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Add tweet-related options to context menu.
|
|
//
|
|
execSafe(function setupTweetContextMenu(){
|
|
ensurePropertyExists(TD, "controller", "columnManager", "get");
|
|
ensurePropertyExists(TD, "services", "ChirpBase", "TWEET");
|
|
ensurePropertyExists(TD, "services", "TwitterActionFollow");
|
|
|
|
const processMedia = function(chirp){
|
|
return chirp.getMedia().filter(item => !item.isAnimatedGif).map(item => item.entity.media_url_https + ":small").join(";");
|
|
};
|
|
|
|
app.delegate("section.js-column", "contextmenu", function(){
|
|
const hovered = getHoveredTweet();
|
|
return if !hovered;
|
|
|
|
const tweet = hovered.obj;
|
|
const quote = tweet.quotedTweet;
|
|
|
|
if (tweet.chirpType === TD.services.ChirpBase.TWEET){
|
|
const tweetUrl = tweet.getChirpURL();
|
|
const quoteUrl = quote && quote.getChirpURL();
|
|
|
|
const chirpAuthors = quote ? [ tweet.getMainUser().screenName, quote.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
|
|
const chirpImages = tweet.hasImage() ? processMedia(tweet) : quote && quote.hasImage() ? processMedia(quote) : "";
|
|
|
|
$TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
|
|
}
|
|
else if (tweet instanceof TD.services.TwitterActionFollow){
|
|
$TD.setRightClickedLink("link", tweet.following.getProfileURL());
|
|
}
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Expand shortened links on hover or display tooltip.
|
|
//
|
|
execSafe(function setupLinkExpansionOrTooltip(){
|
|
let prevMouseX = -1, prevMouseY = -1;
|
|
let tooltipTimer, tooltipDisplayed;
|
|
|
|
$(document.body).delegate("a[data-full-url]", {
|
|
mouseenter: function(){
|
|
const me = $(this);
|
|
const text = me.text();
|
|
return if text.charCodeAt(text.length - 1) !== 8230 && text.charCodeAt(0) !== 8230; // horizontal ellipsis
|
|
|
|
if ($TDX.expandLinksOnHover){
|
|
tooltipTimer = window.setTimeout(function(){
|
|
me.attr("td-prev-text", text);
|
|
me.text(me.attr("data-full-url").replace(/^https?:\/\/(www\.)?/, ""));
|
|
}, 200);
|
|
}
|
|
else{
|
|
me.removeAttr("title");
|
|
|
|
tooltipTimer = window.setTimeout(function(){
|
|
$TD.displayTooltip(me.attr("data-full-url"));
|
|
tooltipDisplayed = true;
|
|
}, 400);
|
|
}
|
|
},
|
|
|
|
mouseleave: function(){
|
|
const me = $(this)[0];
|
|
|
|
if (me.hasAttribute("td-prev-text")){
|
|
me.innerText = me.getAttribute("td-prev-text");
|
|
}
|
|
|
|
window.clearTimeout(tooltipTimer);
|
|
|
|
if (tooltipDisplayed){
|
|
tooltipDisplayed = false;
|
|
$TD.displayTooltip(null);
|
|
}
|
|
},
|
|
|
|
mousemove: function(e){
|
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
|
$TD.displayTooltip($(this).attr("data-full-url"));
|
|
prevMouseX = e.clientX;
|
|
prevMouseY = e.clientY;
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Support for extra mouse buttons.
|
|
//
|
|
execSafe(function supportExtraMouseButtons(){
|
|
const tryClickSelector = function(selector, parent){
|
|
return $(selector, parent).click().length;
|
|
};
|
|
|
|
const tryCloseModal1 = function(){
|
|
const modal = $("#open-modal");
|
|
return modal.is(":visible") && tryClickSelector("a.mdl-dismiss", modal);
|
|
};
|
|
|
|
const tryCloseModal2 = function(){
|
|
const modal = $(".js-modals-container");
|
|
return modal.length && tryClickSelector("a.mdl-dismiss", modal);
|
|
};
|
|
|
|
const tryCloseHighlightedColumn = function(){
|
|
const column = getHoveredColumn();
|
|
return false if !column;
|
|
|
|
const ele = $(column.ele);
|
|
return ((ele.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", ele)) || (ele.is(".is-shifted-1") && tryClickSelector(".js-column-back", ele)));
|
|
};
|
|
|
|
window.TDGF_onMouseClickExtra = function(button){
|
|
if (button === 1){ // back button
|
|
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back", ".js-modal-panel") ||
|
|
tryClickSelector(".is-shifted-1 .js-column-back", ".js-modal-panel") ||
|
|
tryCloseModal1() ||
|
|
tryCloseModal2() ||
|
|
tryClickSelector(".js-inline-compose-close") ||
|
|
tryCloseHighlightedColumn() ||
|
|
tryClickSelector(".js-app-content.is-open .js-drawer-close:visible") ||
|
|
tryClickSelector(".is-shifted-2 .js-tweet-social-proof-back, .is-shifted-2 .js-dm-participants-back") ||
|
|
$(".is-shifted-1 .js-column-back").click();
|
|
}
|
|
else if (button === 2){ // forward button
|
|
const hovered = getHoveredTweet();
|
|
|
|
if (hovered){
|
|
$(hovered.ele).children().first().click();
|
|
}
|
|
}
|
|
};
|
|
});
|
|
|
|
//
|
|
// Block: Allow drag & drop behavior for dropping links on columns to open their detail view.
|
|
//
|
|
execSafe(function supportDragDropOverColumns(){
|
|
const regexTweet = /^https?:\/\/twitter\.com\/[A-Za-z0-9_]+\/status\/(\d+)\/?\??/;
|
|
const regexAccount = /^https?:\/\/twitter\.com\/(?!signup$|tos$|privacy$|search$|search-)([^/?]+)\/?$/;
|
|
|
|
let dragType = false;
|
|
|
|
const events = {
|
|
dragover: function(e){
|
|
e.originalEvent.dataTransfer.dropEffect = dragType ? "all" : "none";
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
},
|
|
|
|
drop: function(e){
|
|
const url = e.originalEvent.dataTransfer.getData("URL");
|
|
|
|
if (dragType === "tweet"){
|
|
const match = regexTweet.exec(url);
|
|
|
|
if (match.length === 2){
|
|
const column = TD.controller.columnManager.get($(this).attr("data-column"));
|
|
|
|
if (column){
|
|
TD.controller.clients.getPreferredClient().show(match[1], function(chirp){
|
|
TD.ui.updates.showDetailView(column, chirp, column.findChirp(chirp) || chirp);
|
|
$(document).trigger("uiGridClearSelection");
|
|
}, function(){
|
|
alert("error|Could not retrieve the requested tweet.");
|
|
});
|
|
}
|
|
}
|
|
}
|
|
else if (dragType === "account"){
|
|
const match = regexAccount.exec(url);
|
|
|
|
if (match.length === 2){
|
|
$(document).trigger("uiShowProfile", { id: match[1] });
|
|
}
|
|
}
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
}
|
|
};
|
|
|
|
const selectors = {
|
|
tweet: "section.js-column",
|
|
account: app
|
|
};
|
|
|
|
window.TDGF_onGlobalDragStart = function(type, data){
|
|
if (dragType){
|
|
app.undelegate(selectors[dragType], events);
|
|
dragType = null;
|
|
}
|
|
|
|
if (type === "link"){
|
|
dragType = regexTweet.test(data) ? "tweet" : regexAccount.test(data) ? "account": null;
|
|
app.delegate(selectors[dragType], events);
|
|
}
|
|
};
|
|
});
|
|
|
|
//
|
|
// Block: Make middle click on tweet reply icon open the compose drawer, retweet icon trigger a quote, and favorite icon open a 'Like from accounts...' modal.
|
|
//
|
|
execSafe(function supportMiddleClickTweetActions(){
|
|
app.delegate(".tweet-action,.tweet-detail-action", "auxclick", function(e){
|
|
return if e.which !== 2;
|
|
|
|
const column = TD.controller.columnManager.get($(this).closest("section.js-column").attr("data-column"));
|
|
return if !column;
|
|
|
|
const ele = $(this).closest("article");
|
|
const tweet = column.findChirp(ele.attr("data-tweet-id")) || column.findChirp(ele.attr("data-key"));
|
|
return if !tweet;
|
|
|
|
switch($(this).attr("rel")){
|
|
case "reply":
|
|
const main = tweet.getMainTweet();
|
|
|
|
$(document).trigger("uiDockedComposeTweet", {
|
|
type: "reply",
|
|
from: [ tweet.account.getKey() ],
|
|
inReplyTo: {
|
|
id: tweet.id,
|
|
htmlText: main.htmlText,
|
|
user: {
|
|
screenName: main.user.screenName,
|
|
name: main.user.name,
|
|
profileImageURL: main.user.profileImageURL
|
|
}
|
|
},
|
|
mentions: tweet.getReplyUsers(),
|
|
element: ele
|
|
});
|
|
|
|
break;
|
|
|
|
case "favorite":
|
|
$(document).trigger("uiShowFavoriteFromOptions", { tweet });
|
|
break;
|
|
|
|
case "retweet":
|
|
TD.controller.stats.quoteTweet();
|
|
|
|
$(document).trigger("uiComposeTweet", {
|
|
type: "tweet",
|
|
from: [ tweet.account.getKey() ],
|
|
quotedTweet: tweet.getMainTweet(),
|
|
element: ele // triggers reply-account plugin
|
|
});
|
|
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
e.stopImmediatePropagation();
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Add a pin icon to make tweet compose drawer stay open.
|
|
//
|
|
execSafe(function setupStayOpenPin(){
|
|
$(document).on("tduckOldComposerActive", function(e){
|
|
const ele = $(`#import "markup/pin.html"`).appendTo(".js-docked-compose .js-compose-header");
|
|
|
|
ele.click(function(){
|
|
if (TD.settings.getComposeStayOpen()){
|
|
ele.css("transform", "rotate(0deg)");
|
|
TD.settings.setComposeStayOpen(false);
|
|
}
|
|
else{
|
|
ele.css("transform", "rotate(90deg)");
|
|
TD.settings.setComposeStayOpen(true);
|
|
}
|
|
});
|
|
|
|
if (TD.settings.getComposeStayOpen()){
|
|
ele.css("transform", "rotate(90deg)");
|
|
}
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Make submitting search queries while holding Ctrl or middle-clicking the search icon open the search externally.
|
|
//
|
|
onAppReady.push(function setupSearchTriggerHook(){
|
|
const openSearchExternally = function(event, input){
|
|
$TD.openBrowser("https://twitter.com/search/?q=" + encodeURIComponent(input.val() || ""));
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
input.val("").blur();
|
|
app.click(); // unfocus everything
|
|
};
|
|
|
|
$$(".js-app-search-input").keydown(function(e){
|
|
(e.ctrlKey && e.keyCode === 13) && openSearchExternally(e, $(this)); // enter
|
|
});
|
|
|
|
$$(".js-perform-search").on("click auxclick", function(e){
|
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $(".js-app-search-input:visible"));
|
|
}).each(function(){
|
|
window.TDGF_prioritizeNewestEvent($(this)[0], "click");
|
|
});
|
|
|
|
$$("[data-action='show-search']").on("click auxclick", function(e){
|
|
(e.ctrlKey || e.button === 1) && openSearchExternally(e, $());
|
|
});
|
|
});
|
|
|
|
//
|
|
// Block: Setup video player hooks.
|
|
//
|
|
execSafe(function setupVideoPlayer(){
|
|
const getGifLink = function(ele){
|
|
return ele.attr("src") || ele.children("source[video-src]").first().attr("video-src");
|
|
};
|
|
|
|
const getVideoTweetLink = function(obj){
|
|
let parent = obj.closest(".js-tweet").first();
|
|
let link = (parent.hasClass("tweet-detail") ? parent.find("a[rel='url']") : parent.find("time").first().children("a")).first();
|
|
return link.attr("href");
|
|
};
|
|
|
|
const getUsername = function(tweet){
|
|
return tweet && (tweet.quotedTweet || tweet).getMainUser().screenName;
|
|
};
|
|
|
|
app.delegate(".js-gif-play", {
|
|
click: function(e){
|
|
let src = !e.ctrlKey && getGifLink($(this).closest(".js-media-gif-container").find("video"));
|
|
let tweet = getVideoTweetLink($(this));
|
|
|
|
if (src){
|
|
let hovered = getHoveredTweet();
|
|
window.TDGF_playVideo(src, tweet, getUsername(hovered && hovered.obj));
|
|
}
|
|
else{
|
|
$TD.openBrowser(tweet);
|
|
}
|
|
|
|
e.stopPropagation();
|
|
},
|
|
|
|
mousedown: function(e){
|
|
if (e.button === 1){
|
|
e.preventDefault();
|
|
}
|
|
},
|
|
|
|
mouseup: function(e){
|
|
if (e.button === 1){
|
|
$TD.openBrowser(getVideoTweetLink($(this)));
|
|
e.preventDefault();
|
|
}
|
|
}
|
|
});
|
|
|
|
window.TDGF_injectMustache("status/media_thumb.mustache", "append", "is-gif", " is-paused");
|
|
|
|
TD.mustaches["media/native_video.mustache"] = '<div class="js-media-gif-container media-item nbfc is-video" style="background-image:url({{imageSrc}})"><video class="js-media-gif media-item-gif full-width block {{#isPossiblySensitive}}is-invisible{{/isPossiblySensitive}}" loop src="{{videoUrl}}"></video><a class="js-gif-play pin-all is-actionable">{{> media/video_overlay}}</a></div>';
|
|
|
|
ensurePropertyExists(TD, "components", "MediaGallery", "prototype", "_loadTweet");
|
|
ensurePropertyExists(TD, "components", "BaseModal", "prototype", "setAndShowContainer");
|
|
ensurePropertyExists(TD, "ui", "Column", "prototype", "playGifIfNotManuallyPaused");
|
|
|
|
let cancelModal = false;
|
|
|
|
TD.components.MediaGallery.prototype._loadTweet = appendToFunction(TD.components.MediaGallery.prototype._loadTweet, function(){
|
|
const media = this.chirp.getMedia().find(media => media.mediaId === this.clickedMediaEntityId);
|
|
|
|
if (media && media.isVideo && media.service === "twitter"){
|
|
window.TDGF_playVideo(media.chooseVideoVariant().url, this.chirp.getChirpURL(), getUsername(this.chirp));
|
|
cancelModal = true;
|
|
}
|
|
});
|
|
|
|
TD.components.BaseModal.prototype.setAndShowContainer = prependToFunction(TD.components.BaseModal.prototype.setAndShowContainer, function(){
|
|
if (cancelModal){
|
|
cancelModal = false;
|
|
return true;
|
|
}
|
|
});
|
|
|
|
TD.ui.Column.prototype.playGifIfNotManuallyPaused = function(){};
|
|
});
|
|
|
|
//
|
|
// Block: Detect and notify about connection issues.
|
|
//
|
|
(function(){
|
|
const onConnectionError = function(){
|
|
return if $("#tweetduck-conn-issues").length;
|
|
|
|
const ele = $(`#import "markup/offline.html"`).appendTo(document.body);
|
|
|
|
ele.find("button").click(function(){
|
|
ele.fadeOut(200);
|
|
});
|
|
};
|
|
|
|
const onConnectionFine = function(){
|
|
const ele = $("#tweetduck-conn-issues");
|
|
|
|
ele.fadeOut(200, function(){
|
|
ele.remove();
|
|
});
|
|
};
|
|
|
|
window.addEventListener("offline", onConnectionError);
|
|
window.addEventListener("online", onConnectionFine);
|
|
})();
|