mirror of
https://github.com/chylex/TweetDuck.git
synced 2024-11-25 05:42:47 +01:00
450 lines
14 KiB
JavaScript
450 lines
14 KiB
JavaScript
(function($, $TD, TD){
|
|
//
|
|
// Variable: Current highlighted column jQuery object.
|
|
//
|
|
var highlightedColumnEle;
|
|
|
|
//
|
|
// Variable: Currently highlighted tweet jQuery object.
|
|
//
|
|
var highlightedTweetEle;
|
|
|
|
//
|
|
// Function: Initializes TweetD*ck events. Called after the website app is loaded.
|
|
//
|
|
var initializeTweetDck = function(){
|
|
// Settings button hook
|
|
$("[data-action='settings-menu']").click(function(){
|
|
setTimeout(function(){
|
|
var menu = $(".js-dropdown-content").children("ul").first();
|
|
if (menu.length === 0)return;
|
|
|
|
menu.children(".drp-h-divider").last().after([
|
|
'<li class="is-selectable" data-std><a href="#" data-action="td-settings">'+$TD.brandName+' settings</a></li>',
|
|
'<li class="is-selectable" data-std><a href="#" data-action="td-plugins">'+$TD.brandName+' plugins</a></li>',
|
|
'<li class="drp-h-divider"></li>'
|
|
].join(""));
|
|
|
|
var buttons = menu.children("[data-std]");
|
|
|
|
buttons.on("click", "a", function(){
|
|
var action = $(this).attr("data-action");
|
|
|
|
if (action === "td-settings"){
|
|
$TD.openSettingsMenu();
|
|
}
|
|
else if (action === "td-plugins"){
|
|
$TD.openPluginsMenu();
|
|
}
|
|
});
|
|
|
|
buttons.hover(function(){
|
|
$(this).addClass("is-selected");
|
|
}, function(){
|
|
$(this).removeClass("is-selected");
|
|
});
|
|
}, 0);
|
|
});
|
|
|
|
// Notification handling
|
|
$.subscribe("/notifications/new", function(obj){
|
|
for(let index = obj.items.length-1; index >= 0; index--){
|
|
onNewTweet(obj.column, obj.items[index]);
|
|
}
|
|
});
|
|
|
|
// Setup video element replacement and fix missing target in user links
|
|
new MutationObserver(function(){
|
|
$("video").each(function(){
|
|
$(this).parent().replaceWith("<a href='"+$(this).attr("src")+"' rel='url' target='_blank' style='display:block; border:1px solid #555; padding:3px 6px'>► Open video in browser</a>");
|
|
});
|
|
|
|
$("a[rel='user']").attr("target", "_blank");
|
|
}).observe($(".js-app-columns")[0], {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
|
|
// Finish init and load plugins
|
|
$TD.loadFontSizeClass(TD.settings.getFontSize());
|
|
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
|
|
|
window.TD_APP_READY = true;
|
|
|
|
if (window.TD_PLUGINS){
|
|
window.TD_PLUGINS.onReady();
|
|
}
|
|
};
|
|
|
|
//
|
|
// Function: Prepends code at the beginning of a function. If the prepended function returns true, execution of the original function is cancelled.
|
|
//
|
|
var prependToFunction = function(func, extension){
|
|
return function(){
|
|
return extension.apply(this, arguments) === true ? undefined : func.apply(this, arguments);
|
|
};
|
|
};
|
|
|
|
//
|
|
// Function: Appends code at the end of a function.
|
|
//
|
|
var appendToFunction = function(func, extension){
|
|
return function(){
|
|
var res = func.apply(this, arguments);
|
|
extension.apply(this, arguments);
|
|
return res;
|
|
};
|
|
};
|
|
|
|
//
|
|
// Function: Event callback for a new tweet.
|
|
//
|
|
var onNewTweet = function(column, tweet){
|
|
if (column.model.getHasNotification()){
|
|
var html = $(tweet.render({
|
|
withFooter: false,
|
|
withTweetActions: false,
|
|
withMediaPreview: true,
|
|
isMediaPreviewOff: true,
|
|
isMediaPreviewSmall: false,
|
|
isMediaPreviewLarge: false
|
|
}));
|
|
|
|
html.css("border", "0");
|
|
html.find("footer").last().remove(); // apparently withTweetActions breaks for certain tweets, nice
|
|
|
|
var url = html.find("time").first().children("a").first().attr("href") || "";
|
|
|
|
$TD.onTweetPopup(html.html(), url, tweet.text.length); // TODO column
|
|
}
|
|
else if (column.model.getHasSound()){
|
|
$TD.onTweetSound(); // TODO disable original
|
|
}
|
|
};
|
|
|
|
//
|
|
// Function: Retrieves the tags to be put into <head> for notification HTML code.
|
|
//
|
|
var getNotificationHeadContents = function(){
|
|
var tags = [];
|
|
|
|
$(document.head).children("link[rel='stylesheet'],meta[charset],meta[http-equiv]").each(function(){
|
|
tags.push($(this)[0].outerHTML);
|
|
});
|
|
|
|
return tags.join("");
|
|
};
|
|
|
|
//
|
|
// Block: Observe the app <div> element and initialize TweetD*ck whenever possible.
|
|
//
|
|
var app = $("body").children(".js-app");
|
|
|
|
new MutationObserver(function(){
|
|
if (window.TD_APP_READY && app.hasClass("is-hidden")){
|
|
window.TD_APP_READY = false;
|
|
}
|
|
else if (!window.TD_APP_READY && !app.hasClass("is-hidden")){
|
|
initializeTweetDck();
|
|
}
|
|
}).observe(app[0], {
|
|
attributes: true,
|
|
attributeFilter: [ "class" ]
|
|
});
|
|
|
|
//
|
|
// Block: Hook into settings object to detect when the settings change.
|
|
//
|
|
TD.settings.setFontSize = appendToFunction(TD.settings.setFontSize, function(name){
|
|
$TD.loadFontSizeClass(name);
|
|
});
|
|
|
|
TD.settings.setTheme = appendToFunction(TD.settings.setTheme, function(){
|
|
setTimeout(function(){
|
|
$TD.loadNotificationHeadContents(getNotificationHeadContents());
|
|
}, 0);
|
|
});
|
|
|
|
//
|
|
// Block: Force popup notification settings.
|
|
//
|
|
TD.controller.notifications.hasNotifications = function(){
|
|
return true;
|
|
};
|
|
|
|
TD.controller.notifications.isPermissionGranted = function(){
|
|
return true;
|
|
};
|
|
|
|
//
|
|
// Block: Expand shortened links on hover or display tooltip.
|
|
//
|
|
(function(){
|
|
var cutStart = function(str, search){
|
|
return str.startsWith(search) ? str.substr(search.length) : str;
|
|
};
|
|
|
|
var prevMouseX = -1, prevMouseY = -1;
|
|
var tooltipTimer, tooltipDisplayed;
|
|
|
|
$(document.body).delegate("a[data-full-url]", "mouseenter mouseleave mousemove", function(e){
|
|
var me = $(this);
|
|
|
|
if (e.type === "mouseenter"){
|
|
var text = me.text();
|
|
|
|
if (text.charCodeAt(text.length-1) !== 8230){ // horizontal ellipsis
|
|
return;
|
|
}
|
|
|
|
if ($TD.expandLinksOnHover){
|
|
tooltipTimer = window.setTimeout(function(){
|
|
var expanded = me.attr("data-full-url");
|
|
expanded = cutStart(expanded, "https://");
|
|
expanded = cutStart(expanded, "http://");
|
|
expanded = cutStart(expanded, "www.");
|
|
|
|
me.attr("td-prev-text", text);
|
|
me.text(expanded);
|
|
}, 200);
|
|
}
|
|
else{
|
|
tooltipTimer = window.setTimeout(function(){
|
|
$TD.displayTooltip(me.attr("data-full-url"), false);
|
|
tooltipDisplayed = true;
|
|
}, 400);
|
|
}
|
|
}
|
|
else if (e.type === "mouseleave"){
|
|
if ($TD.expandLinksOnHover){
|
|
var prevText = me.attr("td-prev-text");
|
|
|
|
if (prevText){
|
|
me.text(prevText);
|
|
}
|
|
}
|
|
|
|
window.clearTimeout(tooltipTimer);
|
|
|
|
if (tooltipDisplayed){
|
|
tooltipDisplayed = false;
|
|
$TD.displayTooltip(null, false);
|
|
}
|
|
}
|
|
else if (e.type === "mousemove"){
|
|
if (tooltipDisplayed && (prevMouseX !== e.clientX || prevMouseY !== e.clientY)){
|
|
$TD.displayTooltip(me.attr("data-full-url"), false);
|
|
prevMouseX = e.clientX;
|
|
prevMouseY = e.clientY;
|
|
}
|
|
}
|
|
});
|
|
})();
|
|
|
|
//
|
|
// Block: Allow bypassing of t.co in context menus.
|
|
//
|
|
$(document.body).delegate("a", "contextmenu", function(){
|
|
$TD.setLastRightClickedLink($(this).attr("data-full-url") || "");
|
|
});
|
|
|
|
//
|
|
// Block: Hook into the notification sound effect.
|
|
//
|
|
(function(){
|
|
var soundEle = document.getElementById("update-sound");
|
|
|
|
soundEle.play = prependToFunction(soundEle.play, function(){
|
|
return $TD.muteNotifications;
|
|
});
|
|
})();
|
|
|
|
/* TODO document.getElementById("update-sound").play = function(){
|
|
$TD.onTweetSound();
|
|
};*/
|
|
|
|
//
|
|
// Block: Update highlighted column.
|
|
//
|
|
app.delegate("section", "mouseenter mouseleave", function(e){
|
|
if (e.type === "mouseenter"){
|
|
highlightedColumnEle = $(this);
|
|
}
|
|
else if (e.type === "mouseleave"){
|
|
highlightedColumnEle = null;
|
|
}
|
|
});
|
|
|
|
//
|
|
// Block: Copy tweet address and update highlighted tweet.
|
|
//
|
|
(function(){
|
|
var lastTweet = "";
|
|
|
|
var updateHighlightedTweet = function(link, embeddedLink){
|
|
if (lastTweet !== link){
|
|
$TD.setLastHighlightedTweet(link, embeddedLink);
|
|
lastTweet = link;
|
|
}
|
|
};
|
|
|
|
app.delegate("article.js-stream-item", "mouseenter mouseleave", function(e){
|
|
if (e.type === "mouseenter"){
|
|
highlightedTweetEle = $(this);
|
|
|
|
var link = $(this).find("time").first().children("a").first();
|
|
var embedded = $(this).find(".quoted-tweet[data-tweet-id]").first();
|
|
|
|
updateHighlightedTweet(link.length > 0 ? link.attr("href") : "", embedded.length > 0 ? embedded.find(".account-link").first().attr("href")+"/status/"+embedded.attr("data-tweet-id") : "");
|
|
}
|
|
else if (e.type === "mouseleave"){
|
|
highlightedTweetEle = null;
|
|
updateHighlightedTweet("", "");
|
|
}
|
|
});
|
|
})();
|
|
|
|
//
|
|
// Block: Paste images when tweeting.
|
|
//
|
|
(function(){
|
|
var lastPasteElement;
|
|
var prevScrollTop;
|
|
|
|
var getScroller = function(){
|
|
return $(".js-drawer").find(".js-compose-scroller").first().children().first();
|
|
};
|
|
|
|
var clickUpload = function(){
|
|
var button = $(".js-add-image-button").first();
|
|
|
|
var scroller = getScroller();
|
|
prevScrollTop = scroller.scrollTop();
|
|
|
|
scroller.scrollTop(0);
|
|
scroller.scrollTop(button.offset().top); // scrolls the button into view
|
|
|
|
var buttonPos = button.children().first().offset(); // finds the camera icon offset
|
|
$TD.clickUploadImage(Math.floor(buttonPos.left), Math.floor(buttonPos.top));
|
|
};
|
|
|
|
$(".js-app").delegate(".js-compose-text,.js-reply-tweetbox", "paste", function(){
|
|
lastPasteElement = $(this);
|
|
$TD.tryPasteImage();
|
|
});
|
|
|
|
window.TDGF_tryPasteImage = function(){
|
|
if (lastPasteElement){
|
|
var parent = lastPasteElement.parent();
|
|
|
|
if (parent.siblings(".js-add-image-button").length === 0){
|
|
var pop = parent.closest(".js-inline-reply,.rpl").find(".js-inline-compose-pop,.js-reply-popout");
|
|
|
|
if (pop.length === 0){
|
|
lastPasteElement = null;
|
|
return;
|
|
}
|
|
|
|
pop.click();
|
|
|
|
var drawer = $(".js-drawer");
|
|
var counter = 0;
|
|
|
|
var interval = setInterval(function(){
|
|
if (drawer.offset().left >= 195){
|
|
clickUpload();
|
|
clearInterval(interval);
|
|
}
|
|
else if (++counter >= 10){
|
|
clearInterval(interval);
|
|
}
|
|
}, 51);
|
|
}
|
|
else{
|
|
clickUpload();
|
|
}
|
|
|
|
lastPasteElement = null;
|
|
}
|
|
};
|
|
|
|
window.TDGF_tryPasteImageFinish = function(){
|
|
setTimeout(function(){
|
|
getScroller().scrollTop(prevScrollTop);
|
|
$(".js-drawer").find(".js-compose-text").first()[0].focus();
|
|
}, 10);
|
|
};
|
|
})();
|
|
|
|
//
|
|
// Block: Support for extra mouse buttons.
|
|
//
|
|
(function(){
|
|
var tryClickSelector = function(selector, parent){
|
|
return $(selector, parent).click().length;
|
|
};
|
|
|
|
var tryCloseModal = function(){
|
|
var modal = $("#open-modal");
|
|
return modal.is(":visible") && tryClickSelector("a[rel=dismiss]", modal);
|
|
};
|
|
|
|
var tryCloseHighlightedColumn = function(){
|
|
if (highlightedColumnEle){
|
|
var column = highlightedColumnEle.closest(".js-column");
|
|
return (column.is(".is-shifted-2") && tryClickSelector(".js-tweet-social-proof-back", column)) || (column.is(".is-shifted-1") && tryClickSelector(".js-column-back", column));
|
|
}
|
|
};
|
|
|
|
window.TDGF_onMouseClickExtra = function(button){
|
|
if (button === 1){ // back button
|
|
tryCloseModal() ||
|
|
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") ||
|
|
$(".js-column-back").click();
|
|
}
|
|
else if (button === 2){ // forward button
|
|
if (highlightedTweetEle){
|
|
highlightedTweetEle.children().first().click();
|
|
}
|
|
}
|
|
};
|
|
})();
|
|
|
|
//
|
|
// Block: Fix scheduled tweets not showing up sometimes.
|
|
//
|
|
$(document).on("dataTweetSent", function(e, data){
|
|
if (data.response.state && data.response.state === "scheduled"){
|
|
var column = Object.values(TD.controller.columnManager.getAll()).find(column => column.model.state.type === "scheduled");
|
|
|
|
if (column){
|
|
setTimeout(function(){
|
|
column.reloadTweets();
|
|
}, 1000);
|
|
}
|
|
}
|
|
});
|
|
|
|
//
|
|
// Block: Inject custom CSS and layout into the page.
|
|
//
|
|
(function(){
|
|
var styleOfficial = document.createElement("style");
|
|
document.head.appendChild(styleOfficial);
|
|
|
|
styleOfficial.sheet.insertRule("a[data-full-url] { word-break: break-all; }", 0); // break long urls
|
|
styleOfficial.sheet.insertRule(".column-nav-link .attribution { position: absolute; }", 0); // fix cut off account names
|
|
styleOfficial.sheet.insertRule(".txt-base-smallest .badge-verified:before { height: 13px !important; }", 0); // fix cut off badge icon
|
|
styleOfficial.sheet.insertRule(".keyboard-shortcut-list { vertical-align: top; }", 0); // fix keyboard navigation alignment
|
|
|
|
if ($TD.hasCustomBrowserCSS){
|
|
var styleCustom = document.createElement("style");
|
|
styleCustom.innerHTML = $TD.customBrowserCSS;
|
|
document.head.appendChild(styleCustom);
|
|
}
|
|
})();
|
|
})($, $TD, TD);
|