2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
// =-=-=-=-=-= CONSTANTS =-==-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
2022-12-09 14:27:57 +00:00
const followButtonPaths = [ "div.account__header button.logo-button" , "div.public-account-header a.logo-button" , "div.account-card a.logo-button" , "div.directory-card a.icon-button" , "div.detailed-status a.logo-button" ]
const profileNamePaths = [ "div.account__header__tabs__name small" , "div.public-account-header__tabs__name small" , "div.detailed-status span.display-name__account" , "div.display-name > span" ]
const domainRegex = /^([a-z0-9]+(-[a-z0-9]+)*\.)+[a-z]{2,}$/
const handleExtractUrlRegex = /^(?<domain>https?:\/\/(?:\.?[a-z0-9-]+)+(?:\.[a-z]+){1})?\/?@(?<handle>\w+)(?:@(?<handledomain>(?:[\w-]+\.)+?\w+))?(?:\/(?<tootid>\d+))?\/?$/
const handleExtractUriRegex = /^(?<domain>https?:\/\/(?:\.?[a-z0-9-]+)+(?:\.[a-z]+){1})(?:\/users\/)(?<handle>\w+)(?:(?:\/statuses\/)(?<tootid>\d+))?\/?$/
const enableConsoleLog = true
const logPrepend = "[FediAct]"
const maxElementWaitFactor = 200 // x 100ms for total time
const instanceApi = "/api/v1/instance"
const statusApi = "/api/v1/statuses"
const searchApi = "/api/v2/search"
const accountsApi = "/api/v1/accounts"
2022-12-02 21:45:34 +00:00
const apiDelay = 500
2022-12-08 14:23:58 +00:00
const maxTootCache = 200
2022-11-18 13:20:22 +00:00
// settings keys with defauls
2022-12-02 21:45:34 +00:00
var settings = { }
const settingsDefaults = {
2022-12-06 11:04:25 +00:00
fediact _homeinstance : null ,
fediact _alert : false ,
fediact _mode : "blacklist" ,
fediact _whitelist : null ,
fediact _blacklist : null ,
fediact _target : "_self" ,
fediact _autoaction : true ,
fediact _token : null ,
fediact _showfollows : true ,
fediact _redirects : true ,
fediact _enabledelay : true
2022-11-18 13:20:22 +00:00
}
2022-12-03 01:09:23 +00:00
// fix for cross-browser storage api compatibility and other global vars
2022-12-09 14:32:23 +00:00
var browser , chrome , lasthomerequest , fedireply
2022-12-07 18:49:07 +00:00
// currently, the only reliable way to detect all toots etc. has the drawback that the same element could be processed multiple times
// this will store already processed elements to compare prior to processing and will reset as soon as the site context changes
2022-12-08 14:23:58 +00:00
var processed = [ ]
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-=-
// =-=-=-=-=-= UTILS =-==-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-=-
2022-11-18 14:13:31 +00:00
2022-11-18 13:20:22 +00:00
// wrappers to prepend to log messages
function log ( text ) {
if ( enableConsoleLog ) {
console . log ( logPrepend + ' ' + text )
}
}
2022-12-03 01:09:23 +00:00
// Custom solution for detecting inserted nodes
// Works in combination with nodeinserted.css (fixes Firefox blocking addon-inserted <style> elements for sites with CSP)
2022-12-07 18:49:07 +00:00
// Is more reliable/practicable in certain situations than mutationobserver, who will ignore any node that was inserted with its parent node at once
2022-12-02 21:45:34 +00:00
( function ( $ ) {
$ . fn . DOMNodeAppear = function ( callback , selector ) {
var $this = $ ( this )
selector = selector || ( typeof $this . selector === 'function' && $this . selector )
if ( ! selector ) {
return false
}
$ ( document ) . on ( 'animationstart webkitAnimationStart oanimationstart MSAnimationStart' , function ( e ) {
if ( e . originalEvent . animationName == 'nodeInserted' && $ ( e . target ) . is ( selector ) ) {
if ( typeof callback == 'function' ) {
2022-12-09 14:32:23 +00:00
callback ( e )
2022-12-02 21:45:34 +00:00
}
}
2022-12-09 14:32:23 +00:00
} )
}
jQuery . fn . onAppear = jQuery . fn . DOMNodeAppear
} ) ( jQuery )
2022-11-18 13:20:22 +00:00
2022-12-04 16:56:42 +00:00
// extract given url parameter value
var getUrlParameter = function getUrlParameter ( sParam ) {
var sPageURL = window . location . search . substring ( 1 ) ,
2022-12-09 14:32:23 +00:00
sURLVariables = sPageURL . split ( '&' ) , sParameterName , i
2022-12-04 16:56:42 +00:00
for ( i = 0 ; i < sURLVariables . length ; i ++ ) {
2022-12-09 14:32:23 +00:00
sParameterName = sURLVariables [ i ] . split ( '=' )
2022-12-04 16:56:42 +00:00
if ( sParameterName [ 0 ] === sParam ) {
2022-12-09 14:32:23 +00:00
return sParameterName [ 1 ] === undefined ? true : decodeURIComponent ( sParameterName [ 1 ] )
2022-12-04 16:56:42 +00:00
}
}
2022-12-09 14:32:23 +00:00
return false
}
2022-12-04 16:56:42 +00:00
2022-11-21 22:47:32 +00:00
// promisified xhr for api calls
2022-12-02 21:45:34 +00:00
async function makeRequest ( method , url , extraheaders ) {
// try to prevent error 429 too many request by delaying home instance requests
2022-12-06 11:04:25 +00:00
if ( ~ url . indexOf ( settings . fediact _homeinstance ) && settings . fediact _enabledelay ) {
2022-12-03 12:52:49 +00:00
// get current date
2022-12-02 21:45:34 +00:00
var currenttime = Date . now ( )
var difference = currenttime - lasthomerequest
2022-12-03 16:41:04 +00:00
if ( difference < apiDelay ) {
2022-12-02 21:45:34 +00:00
await new Promise ( resolve => {
setTimeout ( function ( ) {
resolve ( )
} , apiDelay - difference )
} )
}
lasthomerequest = currenttime
}
2022-12-08 14:23:58 +00:00
return new Promise ( function ( resolve ) {
2022-12-09 14:32:23 +00:00
let xhr = new XMLHttpRequest ( )
2022-12-08 14:23:58 +00:00
xhr . open ( method , url )
xhr . timeout = 3000
2022-12-02 21:45:34 +00:00
if ( extraheaders ) {
for ( var key in extraheaders ) {
xhr . setRequestHeader ( key , extraheaders [ key ] )
2022-11-21 22:47:32 +00:00
}
}
xhr . onload = function ( ) {
if ( this . status >= 200 && this . status < 300 ) {
2022-12-08 14:23:58 +00:00
resolve ( xhr . responseText )
2022-11-21 22:47:32 +00:00
} else {
2022-12-08 14:23:58 +00:00
resolve ( false )
2022-11-21 22:47:32 +00:00
}
2022-12-08 14:23:58 +00:00
}
xhr . onerror = function ( ) {
log ( "Request to " + url + " failed." )
2022-12-08 10:08:14 +00:00
resolve ( false )
}
2022-12-08 14:23:58 +00:00
xhr . send ( )
} )
2022-11-21 22:47:32 +00:00
}
2022-12-03 01:09:23 +00:00
// Escape characters used for regex
2022-12-02 21:45:34 +00:00
function escapeRegExp ( string ) {
return string . replace ( /[.*+?^${}()|[\]\\]/g , '\\$&' ) ; // $& means the whole matched string
}
2022-12-03 01:09:23 +00:00
// Replace all occurrences of a substring
2022-12-02 21:45:34 +00:00
function replaceAll ( str , find , replace ) {
2022-12-09 14:32:23 +00:00
return str . replace ( new RegExp ( escapeRegExp ( find ) , 'g' ) , replace )
2022-12-02 21:45:34 +00:00
}
2022-12-03 01:09:23 +00:00
// handles redirects to home instance
2022-11-26 01:40:41 +00:00
function redirectTo ( url ) {
2022-12-06 11:04:25 +00:00
if ( settings . fediact _redirects ) {
if ( settings . fediact _alert ) {
2022-11-26 01:40:41 +00:00
alert ( "Redirecting..." )
}
// open the url in same/new tab
2022-12-09 14:32:23 +00:00
var win = window . open ( url , settings . fediact _target )
2022-11-26 01:40:41 +00:00
log ( "Redirected to " + url )
// focus the new tab if open was successfull
if ( win ) {
2022-12-09 14:32:23 +00:00
win . focus ( )
2022-11-26 01:40:41 +00:00
} else {
// otherwise notify user...
2022-12-09 14:32:23 +00:00
log ( 'Could not open new window. Please allow popups for this website.' )
2022-11-26 01:40:41 +00:00
}
} else {
log ( "Redirects disabled." )
2022-11-22 19:12:23 +00:00
}
2022-11-26 01:40:41 +00:00
}
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
// =-=-=-=-= INTERACTIONS =-=-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
async function followHomeInstance ( id ) {
2022-11-26 01:40:41 +00:00
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-11-26 01:40:41 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/follow"
var responseFollow = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-11-26 01:40:41 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseFollow ) {
2022-12-09 14:32:23 +00:00
responseFollow = JSON . parse ( responseFollow )
2022-11-26 01:40:41 +00:00
if ( ! responseFollow . following && ! responseFollow . requested ) {
2022-12-02 21:45:34 +00:00
log ( "Follow failed." )
2022-12-09 14:32:23 +00:00
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-11-26 01:40:41 +00:00
}
}
} else {
log ( "Auto-action disabled." )
2022-11-21 22:47:32 +00:00
}
2022-11-26 01:40:41 +00:00
}
2022-12-02 21:45:34 +00:00
async function unfollowHomeInstance ( id ) {
2022-11-26 01:40:41 +00:00
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-11-26 01:40:41 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/unfollow"
var responseUnfollow = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-12-02 21:45:34 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseUnfollow ) {
2022-12-09 14:32:23 +00:00
responseUnfollow = JSON . parse ( responseUnfollow )
2022-12-02 21:45:34 +00:00
if ( responseUnfollow . following || responseUnfollow . requested ) {
log ( "Unfollow failed." )
2022-12-09 14:32:23 +00:00
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-12-02 21:45:34 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
async function boostHomeInstance ( id ) {
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-12-02 21:45:34 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/reblog"
var responseBoost = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-11-26 01:40:41 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseBoost ) {
2022-12-09 14:32:23 +00:00
responseBoost = JSON . parse ( responseBoost )
2022-11-26 01:40:41 +00:00
if ( ! responseBoost . reblogged ) {
2022-12-09 14:32:23 +00:00
log ( "Boost failed." )
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-11-26 01:40:41 +00:00
}
}
2022-11-21 22:47:32 +00:00
} else {
2022-11-26 01:40:41 +00:00
log ( "Auto-action disabled." )
2022-11-21 22:47:32 +00:00
}
}
2022-12-02 21:45:34 +00:00
async function unboostHomeInstance ( id ) {
2022-11-26 01:40:41 +00:00
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-11-26 01:40:41 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unreblog"
var responseUnboost = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-12-02 21:45:34 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseUnboost ) {
2022-12-09 14:32:23 +00:00
responseUnboost = JSON . parse ( responseUnboost )
2022-12-02 21:45:34 +00:00
if ( responseUnboost . reblogged ) {
2022-12-09 14:32:23 +00:00
log ( "Unboost failed." )
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-12-02 21:45:34 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
async function favouriteHomeInstance ( id ) {
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-12-02 21:45:34 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/favourite"
var responseFav = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-11-26 01:40:41 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseFav ) {
2022-12-09 14:32:23 +00:00
responseFav = JSON . parse ( responseFav )
2022-11-26 01:40:41 +00:00
if ( ! responseFav . favourited ) {
2022-12-09 14:32:23 +00:00
log ( "Favourite failed." )
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-11-26 01:40:41 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
2022-12-02 21:45:34 +00:00
async function unfavouriteHomeInstance ( id ) {
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-12-02 21:45:34 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unfavourite"
var responseUnFav = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-12-02 21:45:34 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseUnFav ) {
2022-12-09 14:32:23 +00:00
responseUnFav = JSON . parse ( responseUnFav )
2022-12-02 21:45:34 +00:00
if ( responseUnFav . favourited ) {
2022-12-09 14:32:23 +00:00
log ( "Unfavourite failed." )
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-12-02 21:45:34 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
2022-12-04 16:30:47 +00:00
async function bookmarkHomeInstance ( id ) {
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-12-04 16:30:47 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/bookmark"
var responseBookmark = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-12-04 16:30:47 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseBookmark ) {
2022-12-09 14:32:23 +00:00
responseBookmark = JSON . parse ( responseBookmark )
2022-12-04 16:30:47 +00:00
if ( ! responseBookmark . bookmarked ) {
2022-12-09 14:32:23 +00:00
log ( "Bookmark failed." )
return false
2022-12-04 16:30:47 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-12-04 16:30:47 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
async function unbookmarkHomeInstance ( id ) {
// if auto actions are enbaled...
2022-12-06 11:04:25 +00:00
if ( settings . fediact _autoaction ) {
2022-12-04 16:30:47 +00:00
// build follow post request
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unbookmark"
var responseUnbookmark = await makeRequest ( "POST" , requestUrl , settings . tokenheader )
2022-12-04 16:30:47 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseUnbookmark ) {
2022-12-09 14:32:23 +00:00
responseUnbookmark = JSON . parse ( responseUnbookmark )
2022-12-04 16:30:47 +00:00
if ( responseUnbookmark . bookmarked ) {
2022-12-09 14:32:23 +00:00
log ( "Unbookmark failed." )
return false
2022-12-04 16:30:47 +00:00
} else {
2022-12-09 14:32:23 +00:00
return true
2022-12-04 16:30:47 +00:00
}
}
} else {
log ( "Auto-action disabled." )
}
}
2022-12-02 21:45:34 +00:00
async function isFollowingHomeInstance ( ids ) {
2022-12-06 11:04:25 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/relationships?"
2022-12-02 21:45:34 +00:00
for ( const id of ids ) {
// trailing & is no issue
requestUrl += "id[]=" + id . toString ( ) + "&"
}
2022-12-09 14:32:23 +00:00
var responseFollowing = await makeRequest ( "GET" , requestUrl , settings . tokenheader )
const follows = Array ( ids . length ) . fill ( false )
2022-12-02 21:45:34 +00:00
// check if it worked (it is ignored if the user was already followed)
if ( responseFollowing ) {
2022-12-09 14:32:23 +00:00
responseFollowing = JSON . parse ( responseFollowing )
2022-12-02 21:45:34 +00:00
for ( var i = 0 ; i < ids . length ; i ++ ) {
for ( account of responseFollowing ) {
if ( account . id == ids [ i ] ) {
if ( account . following ) {
follows [ i ] = true
}
}
}
}
}
return follows
}
2022-12-05 11:07:07 +00:00
async function executeTootAction ( id , action ) {
var actionExecuted
switch ( action ) {
2022-12-09 14:32:23 +00:00
case 'boost' : actionExecuted = await boostHomeInstance ( id ) ; break
case 'favourite' : actionExecuted = await favouriteHomeInstance ( id ) ; break
case 'bookmark' : actionExecuted = await bookmarkHomeInstance ( id ) ; break
case 'unboost' : actionExecuted = await unboostHomeInstance ( id ) ; break
case 'unfavourite' : actionExecuted = await unfavouriteHomeInstance ( id ) ; break
case 'unbookmark' : actionExecuted = await unbookmarkHomeInstance ( id ) ; break
2022-12-05 11:07:07 +00:00
default :
2022-12-09 14:32:23 +00:00
log ( "No valid action specified." )
2022-12-05 11:07:07 +00:00
}
return actionExecuted
}
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
// =-=-=-=-=-= RESOLVING =-=-==-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
2022-12-03 01:09:23 +00:00
// Return the user id on the home instance
2022-12-02 21:45:34 +00:00
async function resolveHandleToHome ( handle ) {
2022-12-06 11:04:25 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/search?q=" + handle + "&resolve=true&limit=1"
2022-12-02 21:45:34 +00:00
var searchResponse = await makeRequest ( "GET" , requestUrl , settings . tokenheader )
if ( searchResponse ) {
searchResponse = JSON . parse ( searchResponse )
if ( searchResponse [ 0 ] . id ) {
return [ searchResponse [ 0 ] . id , searchResponse [ 0 ] . acct ]
}
}
return false
}
2022-12-05 11:07:07 +00:00
async function resolveTootToHome ( searchstring ) {
2022-12-09 14:32:23 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + searchApi + "/?q=" + searchstring + "&resolve=true&limit=1"
var response = await makeRequest ( "GET" , requestUrl , settings . tokenheader )
2022-12-05 11:07:07 +00:00
if ( response ) {
2022-12-09 14:32:23 +00:00
response = JSON . parse ( response )
2022-12-05 11:07:07 +00:00
if ( ! response . accounts . length && response . statuses . length ) {
2022-12-09 14:32:23 +00:00
var status = response . statuses [ 0 ]
2022-12-05 11:07:07 +00:00
return [ status . account . acct , status . id , status . reblogged , status . favourited , status . bookmarked ]
} else {
return false
}
} else {
return false
}
}
2022-12-07 18:49:07 +00:00
// Get a toot's (external) home instance url by using the 302 redirect feature of mastodon
// we send a message with the toot url to the background script, which will perform the HEAD request
// since XMLHttpRequest/fetch do not allow access to the location header
// TODO: FALLBACK IF 302 IS NOT SUPPORTED
2022-12-02 21:45:34 +00:00
function resolveTootToExternalHome ( tooturl ) {
2022-12-07 18:49:07 +00:00
// TODO: check if a delay is necessary here too
2022-12-02 21:45:34 +00:00
if ( tooturl ) {
2022-12-08 14:23:58 +00:00
return new Promise ( async function ( resolve ) {
2022-12-08 10:08:14 +00:00
try {
2022-12-08 14:23:58 +00:00
await chrome . runtime . sendMessage ( { url : tooturl } , function ( response ) {
2022-12-08 10:08:14 +00:00
if ( response ) {
2022-12-09 14:32:23 +00:00
resolve ( response )
2022-12-08 10:08:14 +00:00
} else {
2022-12-09 14:32:23 +00:00
resolve ( false )
2022-12-08 10:08:14 +00:00
}
2022-12-09 14:32:23 +00:00
} )
2022-12-08 10:08:14 +00:00
} catch ( e ) {
2022-12-08 15:07:37 +00:00
log ( e )
log ( "Reloading page, extension likely got updated." )
location . reload ( )
2022-12-08 10:08:14 +00:00
}
2022-12-09 14:32:23 +00:00
} )
2022-12-02 21:45:34 +00:00
} else {
return false
}
}
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
// =-=-=-=-= SITE PROCESSING =-==-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
2022-12-07 18:49:07 +00:00
// custom implementation for allowing to toggle inline css - .css() etc. are not working for some mastodon instances
2022-12-02 21:45:34 +00:00
function toggleInlineCss ( el , styles , toggleclass ) {
2022-12-09 14:32:23 +00:00
var active = $ ( el ) . toggleClass ( toggleclass ) . hasClass ( toggleclass )
2022-12-02 21:45:34 +00:00
for ( var style of styles ) {
if ( active ) {
$ ( el ) . css ( style [ 0 ] , style [ 2 ] )
} else {
if ( style [ 1 ] == "!remove" ) {
var newinline = replaceAll ( $ ( el ) . attr ( 'style' ) , style [ 0 ] + ": " + style [ 2 ] + ";" , "" )
$ ( el ) . attr ( 'style' , newinline )
} else {
$ ( el ) . css ( style [ 0 ] , style [ 1 ] )
}
}
}
}
// extract handle from elements
function extractHandle ( selectors ) {
// check all of the selectors
for ( const selector of selectors ) {
// if found
if ( $ ( selector ) . length ) {
2022-12-09 14:32:23 +00:00
return $ ( selector ) . text ( ) . trim ( )
2022-12-02 21:45:34 +00:00
}
}
2022-12-09 14:32:23 +00:00
return false
2022-12-02 21:45:34 +00:00
}
2022-12-08 14:23:58 +00:00
function isInProcessedToots ( id ) {
for ( var i = 0 ; i < processed . length ; i ++ ) {
if ( processed [ i ] [ 0 ] == id ) {
return i
}
}
return false
}
function addToProcessedToots ( toot ) {
processed . push ( toot )
var diff = processed . length - maxTootCache
if ( diff > 0 ) {
processed = processed . splice ( 0 , diff )
}
}
2022-12-07 18:49:07 +00:00
// trigger the reply button click - will only run when we are on a home instance url with fedireply parameter
2022-12-04 16:56:42 +00:00
async function processReply ( ) {
$ ( document ) . DOMNodeAppear ( function ( e ) {
2022-12-04 17:22:38 +00:00
$ ( e . target ) . find ( "button:has(i.fa-reply), button:has(i.fa-reply-all)" ) . click ( )
2022-12-04 16:56:42 +00:00
} , "div.detailed-status__action-bar" )
}
2022-11-21 22:47:32 +00:00
// process any toots found on supported sites
async function processToots ( ) {
2022-12-09 14:27:57 +00:00
// determine action when a button is clicked (except reply, which will always redirect)
2022-12-02 21:45:34 +00:00
function getTootAction ( e ) {
var action = false
if ( $ ( e . currentTarget ) . children ( "i.fa-retweet" ) . length ) {
if ( $ ( e . currentTarget ) . children ( "i.fa-retweet" ) . hasClass ( "fediactive" ) ) {
2022-12-09 14:32:23 +00:00
action = "unboost"
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
action = "boost"
2022-12-02 21:45:34 +00:00
}
} else if ( $ ( e . currentTarget ) . children ( "i.fa-star" ) . length ) {
if ( $ ( e . currentTarget ) . hasClass ( "fediactive" ) ) {
2022-12-09 14:32:23 +00:00
action = "unfavourite"
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
action = "favourite"
2022-12-02 21:45:34 +00:00
}
2022-12-04 16:30:47 +00:00
} else if ( $ ( e . currentTarget ) . children ( "i.fa-bookmark" ) . length ) {
if ( $ ( e . currentTarget ) . hasClass ( "fediactive" ) ) {
2022-12-09 14:32:23 +00:00
action = "unbookmark"
2022-12-04 16:30:47 +00:00
} else {
2022-12-09 14:32:23 +00:00
action = "bookmark"
2022-12-04 16:30:47 +00:00
}
2022-12-02 21:45:34 +00:00
} else if ( $ ( e . currentTarget ) . attr ( "href" ) ) {
if ( ~ $ ( e . currentTarget ) . attr ( "href" ) . indexOf ( "type=reblog" ) ) {
if ( $ ( e . currentTarget ) . hasClass ( "fediactive" ) ) {
2022-12-09 14:32:23 +00:00
action = "unboost"
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
action = "boost"
2022-12-02 21:45:34 +00:00
}
} else if ( ~ $ ( e . currentTarget ) . attr ( "href" ) . indexOf ( "type=favourite" ) ) {
if ( $ ( e . currentTarget ) . hasClass ( "fediactive" ) ) {
2022-12-09 14:32:23 +00:00
action = "unfavourite"
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
action = "favourite"
2022-12-02 21:45:34 +00:00
}
}
}
return action
}
2022-12-09 14:27:57 +00:00
// some toots contain an href which can be an already resolved external link or an internal reference
function tootHrefCheck ( temp ) {
if ( temp . startsWith ( "http" ) ) {
var tempUrl = new URL ( temp )
if ( location . hostname == tempUrl . hostname ) {
temp = temp . split ( "/" )
var tempLast = temp . pop ( ) || temp . pop ( )
return [ false , tempLast ]
} else {
// return full URL, since this is already a resolved link to the toot's home instance
return [ true , temp ]
}
} else {
temp = temp . split ( "/" )
var tempLast = temp . pop ( ) || temp . pop ( )
return [ false , tempLast ]
}
}
// get only the toot author handle
function getTootAuthor ( el ) {
2022-12-02 21:45:34 +00:00
if ( $ ( el ) . find ( "span.display-name__account" ) . length ) {
2022-12-09 14:27:57 +00:00
return $ ( el ) . find ( "span.display-name__account" ) . first ( ) . text ( ) . trim ( )
2022-12-02 21:45:34 +00:00
}
2022-12-09 14:27:57 +00:00
}
// check elements that can contain the local toot id and return it if found
function getTootInternalId ( el ) {
2022-12-02 21:45:34 +00:00
if ( $ ( el ) . is ( ".detailed-status__wrapper" ) ) {
2022-12-07 18:49:07 +00:00
// we will use the last part of the URL path - this should be more universal than always selecting the fifth "/" slice
var temp = window . location . href . split ( "?" ) [ 0 ] . split ( "/" )
2022-12-09 14:27:57 +00:00
return ( temp . pop ( ) || temp . pop ( ) )
2022-12-02 21:45:34 +00:00
} else if ( $ ( el ) . attr ( "data-id" ) ) {
2022-12-09 14:27:57 +00:00
// split by "-" to respect some ids startin with "f-"
2022-12-09 14:32:23 +00:00
return $ ( el ) . attr ( "data-id" ) . split ( "-" ) . slice ( - 1 ) [ 0 ]
2022-12-02 21:45:34 +00:00
} else if ( $ ( el ) . closest ( "article[data-id], div[data-id]" ) . length ) {
2022-12-09 14:32:23 +00:00
return $ ( el ) . closest ( "article[data-id], div[data-id]" ) . first ( ) . attr ( "data-id" ) . split ( "-" ) . slice ( - 1 ) [ 0 ]
2022-12-09 14:27:57 +00:00
}
}
// check elements that can contain an href (either resolved external link or internal reference)
function getTootExtIntHref ( el ) {
if ( $ ( el ) . find ( "a.status__relative-time" ) . length ) {
return tootHrefCheck ( $ ( el ) . find ( "a.status__relative-time" ) . first ( ) . attr ( "href" ) . split ( "?" ) [ 0 ] )
} else if ( $ ( el ) . find ( "a.detailed-status__datetime" ) . length ) {
return tootHrefCheck ( $ ( el ) . find ( "a.detailed-status__datetime" ) . first ( ) . attr ( "href" ) . split ( "?" ) [ 0 ] )
2022-12-02 21:45:34 +00:00
} else if ( $ ( el ) . find ( "a.modal-button" ) . length ) {
2022-12-09 14:27:57 +00:00
return tootHrefCheck ( $ ( el ) . find ( "a.modal-button" ) . first ( ) . attr ( "href" ) . split ( "?" ) [ 0 ] )
2022-12-02 21:45:34 +00:00
}
}
2022-12-09 14:27:57 +00:00
// main function to process each detected toot element
2022-12-02 21:45:34 +00:00
async function process ( el ) {
2022-12-09 14:27:57 +00:00
// extra step for detailed status elements to select the correct parent
2022-12-07 18:49:07 +00:00
if ( $ ( el ) . is ( "div.detailed-status" ) ) {
el = $ ( el ) . closest ( "div.focusable" )
}
2022-12-09 14:27:57 +00:00
var tootAuthor = getTootAuthor ( $ ( el ) )
var tootInternalId = getTootInternalId ( $ ( el ) )
var [ tootHrefIsExt , tootHrefOrId ] = getTootExtIntHref ( $ ( el ) )
var internalIdentifier = tootInternalId || tootHrefOrId
if ( internalIdentifier ) {
var homeResolveStrings = [ ]
// check if id is already cached
var cacheIndex = isInProcessedToots ( internalIdentifier )
2022-12-07 18:49:07 +00:00
// get all button elements of this toot
var favButton = $ ( el ) . find ( "button:has(i.fa-star), a.icon-button:has(i.fa-star)" ) . first ( )
var boostButton = $ ( el ) . find ( "button:has(i.fa-retweet), a.icon-button:has(i.fa-retweet)" ) . first ( )
var bookmarkButton = $ ( el ) . find ( "button:has(i.fa-bookmark)" ) . first ( )
var replyButton = $ ( el ) . find ( "button:has(i.fa-reply), button:has(i.fa-reply-all), a.icon-button:has(i.fa-reply), a.icon-button:has(i.fa-reply-all)" ) . first ( )
2022-12-08 14:23:58 +00:00
async function clickAction ( id , e ) {
2022-12-09 14:32:23 +00:00
var action = getTootAction ( e )
2022-12-08 14:23:58 +00:00
if ( action ) {
// resolve url on home instance to get local toot/author identifiers and toot status
var actionExecuted = await executeTootAction ( id , action )
if ( actionExecuted ) {
if ( action == "boost" || action == "unboost" ) {
toggleInlineCss ( $ ( e . currentTarget ) . find ( "i" ) , [ [ "color" , "!remove" , "rgb(140, 141, 255)" ] , [ "transition-duration" , "!remove" , "0.9s" ] , [ "background-position" , "!remove" , "0px 100%" ] ] , "fediactive" )
if ( cacheIndex ) {
processed [ cacheIndex ] [ 3 ] = ! processed [ cacheIndex ] [ 3 ]
}
} else if ( action == "favourite" || action == "unfavourite" ) {
toggleInlineCss ( $ ( e . currentTarget ) , [ [ "color" , "!remove" , "rgb(202, 143, 4)" ] ] , "fediactive" )
if ( cacheIndex ) {
processed [ cacheIndex ] [ 4 ] = ! processed [ cacheIndex ] [ 4 ]
}
} else {
toggleInlineCss ( $ ( e . currentTarget ) , [ [ "color" , "!remove" , "rgb(255, 80, 80)" ] ] , "fediactive" )
if ( cacheIndex ) {
processed [ cacheIndex ] [ 5 ] = ! processed [ cacheIndex ] [ 5 ]
}
}
return true
} else {
log ( "Could not execute action on home instance." )
return false
}
} else {
log ( "Could not determine action." )
return false
}
}
function initStyles ( tootdata ) {
2022-12-07 18:49:07 +00:00
$ ( el ) . find ( ".feditriggered" ) . remove ( )
2022-12-08 15:07:37 +00:00
if ( ! tootdata [ 1 ] ) {
2022-12-09 14:27:57 +00:00
$ ( "<span class='feditriggered' style='color: orange; padding-right: 10px; padding-left: 10px'>Unresolved</span>" ) . insertAfter ( $ ( favButton ) )
2022-12-08 15:07:37 +00:00
} else {
// enable the bookmark button
$ ( bookmarkButton ) . removeClass ( "disabled" ) . removeAttr ( "disabled" )
// set the toot buttons to active, depending on its state
if ( tootdata [ 4 ] ) {
if ( ! $ ( favButton ) . hasClass ( "fediactive" ) ) {
toggleInlineCss ( $ ( favButton ) , [ [ "color" , "!remove" , "rgb(202, 143, 4)" ] ] , "fediactive" )
}
2022-11-22 19:12:23 +00:00
}
2022-12-08 15:07:37 +00:00
if ( tootdata [ 3 ] ) {
if ( ! $ ( boostButton ) . find ( "i.fediactive" ) . length ) {
toggleInlineCss ( $ ( boostButton ) . find ( "i" ) , [ [ "color" , "!remove" , "rgb(140, 141, 255)" ] , [ "transition-duration" , "!remove" , "0.9s" ] , [ "background-position" , "!remove" , "0px 100%" ] ] , "fediactive" )
}
2022-11-21 22:47:32 +00:00
}
2022-12-08 15:07:37 +00:00
if ( tootdata [ 5 ] ) {
if ( ! $ ( bookmarkButton ) . hasClass ( "fediactive" ) ) {
toggleInlineCss ( $ ( bookmarkButton ) , [ [ "color" , "!remove" , "rgb(255, 80, 80)" ] ] , "fediactive" )
}
2022-12-04 16:30:47 +00:00
}
2022-12-07 18:49:07 +00:00
}
}
2022-12-08 14:23:58 +00:00
function clickBinder ( tootdata ) {
2022-12-07 18:49:07 +00:00
$ ( replyButton ) . on ( "click" , function ( e ) {
// reply button is handle specially (always redirects with reply parameter set)
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
2022-12-08 14:23:58 +00:00
redirectTo ( tootdata [ 6 ] + "?fedireply" )
2022-12-07 18:49:07 +00:00
} )
$ ( [ favButton , boostButton , bookmarkButton ] ) . each ( function ( ) {
// all other buttons will behave differently with single / double click
2022-12-09 14:32:23 +00:00
var clicks = 0
var timer
2022-12-07 18:49:07 +00:00
$ ( this ) . on ( "click" , async function ( e ) {
// prevent default and immediate propagation
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
clicks ++
2022-12-07 18:49:07 +00:00
if ( clicks == 1 ) {
timer = setTimeout ( async function ( ) {
// execute action on click and get result (fail/success)
2022-12-09 14:32:23 +00:00
var actionExecuted = await clickAction ( tootdata [ 2 ] , e )
2022-12-05 11:07:07 +00:00
if ( ! actionExecuted ) {
log ( "Action failed." )
2022-12-04 16:30:47 +00:00
}
2022-12-09 14:32:23 +00:00
clicks = 0
} , 350 )
2022-12-07 18:49:07 +00:00
} else {
2022-12-09 14:32:23 +00:00
clearTimeout ( timer )
2022-12-07 18:49:07 +00:00
// same as above, but we redirect if the result is successful
2022-12-09 14:32:23 +00:00
var actionExecuted = await clickAction ( tootdata [ 2 ] , e )
2022-12-07 18:49:07 +00:00
if ( ! actionExecuted ) {
log ( "Action failed." )
} else {
2022-12-08 14:23:58 +00:00
redirectTo ( tootdata [ 6 ] )
2022-12-02 21:45:34 +00:00
}
2022-12-09 14:32:23 +00:00
clicks = 0
2022-12-07 18:49:07 +00:00
}
} ) . on ( "dblclick" , function ( e ) {
// default dblclick event must be prevented
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
} )
} )
2022-12-07 18:49:07 +00:00
}
2022-12-09 14:27:57 +00:00
// if element is not in cache, resolve it
2022-12-08 14:23:58 +00:00
if ( ! cacheIndex ) {
2022-12-09 14:27:57 +00:00
// we can only process internalTootIds if we also have a user handle
if ( tootHrefIsExt ) {
homeResolveStrings . push ( tootHrefOrId )
}
if ( tootAuthor ) {
2022-12-07 18:49:07 +00:00
// get handle/handledomain without @
2022-12-09 14:27:57 +00:00
var matches = tootAuthor . match ( handleExtractUrlRegex )
var [ isExternalHandle , extHomeResolved ] = [ false , false ]
2022-12-07 18:49:07 +00:00
// if we have a handledomain...
if ( matches . groups . handledomain ) {
// check if the current hostname includes that handle domain...
2022-12-09 14:27:57 +00:00
if ( ! ( ~ location . hostname . indexOf ( matches . groups . handledomain ) ) ) {
isExternalHandle = true
}
}
// add ids
var internalTootIds = [ tootInternalId ]
if ( ! tootHrefIsExt ) {
internalTootIds . push ( tootHrefOrId )
}
// filter duplicates and undefined values (shorter than checking with if clauses when adding...)
internalTootIds = internalTootIds . filter ( ( element , index ) => {
return ( element !== undefined && internalTootIds . indexOf ( element ) == index )
} )
// loop through internal ids (will be only 1 normally, but we want to maximize our chances for resolving later on)
for ( var internalTootId of internalTootIds ) {
// if its not an external handle...
if ( ! isExternalHandle ) {
// add resolve strings for both formats on the current external instance
homeResolveStrings . push ( location . protocol + "//" + location . hostname + "/users/" + matches . groups . handle + "/statuses/" + internalTootId )
homeResolveStrings . push ( location . protocol + "//" + location . hostname + "/@" + matches . groups . handle + "/" + internalTootId )
// otherwise, start external resolve process if not done already for one of the internalTootIds
} else if ( ! extHomeResolved ) {
var extResolveString = location . protocol + '//' + location . hostname + "/" + tootAuthor + "/" + internalTootId
var resolveTootHome = await resolveTootToExternalHome ( extResolveString )
2022-12-07 18:49:07 +00:00
if ( resolveTootHome ) {
2022-12-09 14:27:57 +00:00
// update var so next tootid will not be resolved externally, if any more
extHomeResolved = true
// always push the originally returned url
homeResolveStrings . push ( resolveTootHome )
// if it matches the URI format, also add the @ format
if ( handleExtractUriRegex . test ( resolveTootHome ) ) {
var tmpmatches = resolveTootHome . match ( handleExtractUriRegex )
if ( tmpmatches . groups . handle && tmpmatches . groups . tootid && tmpmatches . groups . domain ) {
homeResolveStrings . push ( tmpmatches . groups . domain + "/@" + tmpmatches . groups . handle + "/" + tmpmatches . groups . tootid )
}
// otherwise, if it matches the @ format, also add the URI format
} else if ( handleExtractUrlRegex . test ( resolveTootHome ) ) {
var tmpmatches = resolveTootHome . match ( handleExtractUrlRegex )
if ( tmpmatches . groups . handle && tmpmatches . groups . tootid && tmpmatches . groups . domain ) {
homeResolveStrings . push ( tmpmatches . groups . domain + "/users/" + tmpmatches . groups . handle + "/statuses/" + tmpmatches . groups . tootid )
2022-12-07 18:49:07 +00:00
}
}
} else {
2022-12-09 14:27:57 +00:00
// fallback to current external instance URL (for external handles, there is no /users/... format)
homeResolveStrings . push ( location . protocol + "//" + location . hostname + "/" + tootAuthor + "/" + internalTootId )
2022-12-07 18:49:07 +00:00
}
}
}
}
2022-12-09 14:27:57 +00:00
if ( homeResolveStrings . length ) {
homeResolveStrings = homeResolveStrings . filter ( ( element , index ) => {
return ( homeResolveStrings . indexOf ( element ) == index )
} )
var resolvedToHomeInstance = false
for ( var homeResolveString of homeResolveStrings ) {
if ( ! resolvedToHomeInstance ) {
// resolve toot on actual home instance
var resolvedToot = await resolveTootToHome ( homeResolveString ) // [status.account.acct, status.id, status.reblogged, status.favourited, status.bookmarked]
if ( resolvedToot ) {
resolvedToHomeInstance = true
// set the redirect to home instance URL in @ format
var redirectUrl = 'https://' + settings . fediact _homeinstance + "/@" + resolvedToot [ 0 ] + "/" + resolvedToot [ 1 ]
fullEntry = [ internalIdentifier , ... resolvedToot , redirectUrl , true ]
}
}
}
if ( resolvedToHomeInstance ) {
2022-12-08 14:23:58 +00:00
addToProcessedToots ( fullEntry )
2022-12-07 18:49:07 +00:00
// continue with click handling...
2022-12-08 14:23:58 +00:00
clickBinder ( fullEntry )
initStyles ( fullEntry )
2022-12-07 18:49:07 +00:00
} else {
2022-12-09 14:27:57 +00:00
log ( "Failed to resolve: " + homeResolveStrings )
addToProcessedToots ( [ internalIdentifier , false ] )
initStyles ( [ internalIdentifier , false ] )
2022-12-07 18:49:07 +00:00
}
} else {
log ( "Could not identify a post URI for home resolving." )
2022-12-09 14:27:57 +00:00
addToProcessedToots ( [ internalIdentifier , false ] )
initStyles ( [ internalIdentifier , false ] )
2022-12-02 21:45:34 +00:00
}
2022-11-21 22:47:32 +00:00
} else {
2022-12-08 14:23:58 +00:00
var toot = processed [ cacheIndex ]
initStyles ( toot )
2022-12-08 15:07:37 +00:00
if ( toot [ 1 ] ) {
clickBinder ( toot )
}
2022-11-21 22:47:32 +00:00
}
2022-12-02 21:45:34 +00:00
} else {
log ( "Could not get toot data." )
}
}
2022-12-07 18:49:07 +00:00
// One DOMNodeAppear to rule them all
$ ( document ) . DOMNodeAppear ( async function ( e ) {
process ( $ ( e . target ) )
2022-12-09 14:32:23 +00:00
} , "div.status, div.detailed-status" )
2022-11-21 22:47:32 +00:00
}
2022-11-18 13:20:22 +00:00
2022-11-21 22:47:32 +00:00
// main function to listen for the follow button pressed and open a new tab with the home instance
2022-12-02 21:45:34 +00:00
async function processFollow ( ) {
2022-12-08 15:07:37 +00:00
var fullHandle
var action = "follow"
2022-12-02 21:45:34 +00:00
// for mastodon v3 - v4 does not show follow buttons / account cards on /explore
async function process ( el ) {
2022-12-08 10:08:14 +00:00
// wrapper for follow/unfollow action
2022-12-08 15:07:37 +00:00
async function execFollow ( id ) {
2022-12-08 10:08:14 +00:00
if ( action == "follow" ) {
var followed = await followHomeInstance ( id )
if ( followed ) {
2022-12-09 14:32:23 +00:00
$ ( el ) . text ( "Unfollow" )
2022-12-08 10:08:14 +00:00
action = "unfollow"
return true
}
} else {
var unfollowed = await unfollowHomeInstance ( id )
if ( unfollowed ) {
2022-12-09 14:32:23 +00:00
$ ( el ) . text ( "Follow" )
2022-12-08 10:08:14 +00:00
action = "follow"
return true
}
}
}
2022-12-02 21:45:34 +00:00
// for mastodon v3 explore page
if ( $ ( el ) . closest ( "div.account-card" ) . length ) {
fullHandle = $ ( el ) . closest ( "div.account-card" ) . find ( "div.display-name > span" ) . text ( ) . trim ( )
} else {
// for all other pages, where only one of the selection elements is present
for ( const selector of profileNamePaths ) {
if ( $ ( selector ) . length ) {
fullHandle = $ ( selector ) . text ( ) . trim ( )
break
2022-11-26 01:40:41 +00:00
}
2022-12-02 21:45:34 +00:00
}
}
if ( fullHandle ) {
var resolvedHandle = await resolveHandleToHome ( fullHandle )
if ( resolvedHandle ) {
2022-12-06 11:04:25 +00:00
if ( settings . fediact _showfollows ) {
2022-12-02 21:45:34 +00:00
var isFollowing = await isFollowingHomeInstance ( [ resolvedHandle [ 0 ] ] )
if ( isFollowing [ 0 ] ) {
2022-12-09 14:32:23 +00:00
$ ( el ) . text ( "Unfollow" )
action = "unfollow"
2022-12-02 21:45:34 +00:00
}
2022-11-26 01:40:41 +00:00
}
2022-12-09 14:32:23 +00:00
var clicks = 0
var timer
2022-12-02 21:45:34 +00:00
$ ( el ) . on ( "click" , async function ( e ) {
// prevent default and immediate propagation
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
clicks ++
2022-12-02 21:45:34 +00:00
if ( clicks == 1 ) {
timer = setTimeout ( async function ( ) {
2022-12-08 15:07:37 +00:00
execFollow ( resolvedHandle [ 0 ] )
2022-12-09 14:32:23 +00:00
clicks = 0
} , 350 )
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
clearTimeout ( timer )
2022-12-08 15:07:37 +00:00
var done = await execFollow ( resolvedHandle [ 0 ] )
2022-12-02 21:45:34 +00:00
if ( done ) {
var saveText = $ ( el ) . text ( )
2022-12-06 11:04:25 +00:00
var redirectUrl = 'https://' + settings . fediact _homeinstance + '/@' + resolvedHandle [ 1 ]
2022-12-09 14:32:23 +00:00
$ ( el ) . text ( "Redirecting..." )
2022-11-22 10:00:02 +00:00
setTimeout ( function ( ) {
2022-12-09 14:32:23 +00:00
redirectTo ( redirectUrl )
2022-12-02 21:45:34 +00:00
$ ( el ) . text ( saveText )
2022-12-09 14:32:23 +00:00
} , 1000 )
2022-12-02 21:45:34 +00:00
} else {
log ( "Action failed." )
2022-11-22 10:00:02 +00:00
}
2022-12-09 14:32:23 +00:00
clicks = 0
2022-12-02 21:45:34 +00:00
}
} ) . on ( "dblclick" , function ( e ) {
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
} )
2022-11-21 22:47:32 +00:00
} else {
2022-12-02 21:45:34 +00:00
log ( "Could not resolve user home ID." )
2022-11-21 22:47:32 +00:00
}
2022-12-02 21:45:34 +00:00
}
}
2022-12-08 10:08:14 +00:00
// create css selector from selector array
2022-12-07 21:17:49 +00:00
var allFollowPaths = followButtonPaths . join ( "," )
2022-12-08 10:08:14 +00:00
// one domnodeappear to rule them all
2022-12-07 21:17:49 +00:00
$ ( document ) . DOMNodeAppear ( async function ( e ) {
process ( $ ( e . target ) )
} , allFollowPaths )
2022-11-21 22:47:32 +00:00
}
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
// =-=-=-=-=-= SETUP / RUN =-==-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
// process white/blacklist from ext settings
function processDomainList ( newLineList ) {
// split by new line
2022-12-09 14:32:23 +00:00
var arrayFromList = newLineList . split ( /\r?\n/ )
2022-12-02 21:45:34 +00:00
// array to put checked domains into
2022-12-09 14:32:23 +00:00
var cleanedArray = [ ]
2022-12-02 21:45:34 +00:00
for ( var domain of arrayFromList ) {
// remove whitespace
2022-12-09 14:32:23 +00:00
domain = domain . trim ( )
2022-12-08 14:23:58 +00:00
if ( domain . length ) {
if ( domainRegex . test ( domain ) ) {
cleanedArray . push ( domain )
} else {
log ( "Removed invalid domain " + domain + " from blacklist/whitelist." )
}
2022-12-02 21:45:34 +00:00
}
}
// return newly created set (remvoes duplicates)
2022-12-09 14:32:23 +00:00
return [ ... new Set ( cleanedArray ) ] ;
2022-12-02 21:45:34 +00:00
}
2022-11-21 22:47:32 +00:00
function checkSettings ( ) {
// if the home instance is undefined/null/empty
2022-12-06 11:04:25 +00:00
if ( settings . fediact _homeinstance == null || ! settings . fediact _homeinstance ) {
2022-12-09 14:32:23 +00:00
log ( "Mastodon home instance is not set." )
return false
2022-11-21 22:47:32 +00:00
}
2022-11-26 01:40:41 +00:00
// no token for api available (see background.js)
2022-12-06 11:04:25 +00:00
if ( ! settings . fediact _token ) {
2022-12-09 14:32:23 +00:00
log ( "No API token available. Are you logged in to your home instance? If yes, wait for 1-2 minutes and reload page." )
return false
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 14:32:23 +00:00
settings . tokenheader = { "Authorization" : "Bearer " + settings . fediact _token , }
2022-11-26 01:40:41 +00:00
}
2022-11-21 22:47:32 +00:00
// if the value looks like a domain...
2022-12-06 11:04:25 +00:00
if ( ! ( domainRegex . test ( settings . fediact _homeinstance ) ) ) {
2022-12-09 14:32:23 +00:00
log ( "Instance setting is not a valid domain name." )
return false
2022-11-21 22:47:32 +00:00
}
2022-12-06 11:04:25 +00:00
if ( settings . fediact _mode == "whitelist" ) {
2022-11-21 22:47:32 +00:00
// if in whitelist mode and the cleaned whitelist is empty, return false
2022-12-09 14:32:23 +00:00
settings . fediact _whitelist = processDomainList ( settings . fediact _whitelist )
2022-12-06 11:04:25 +00:00
if ( settings . fediact _whitelist . length < 1 ) {
2022-11-21 22:47:32 +00:00
log ( "Whitelist is empty or invalid." )
2022-12-09 14:32:23 +00:00
return false
2022-11-18 13:20:22 +00:00
}
2022-11-21 22:47:32 +00:00
} else {
// also process the blacklist if in blacklist mode, but an empty blacklist is OK so we do not return false
2022-12-09 14:32:23 +00:00
settings . fediact _blacklist = processDomainList ( settings . fediact _blacklist )
2022-11-21 22:47:32 +00:00
}
2022-12-09 14:32:23 +00:00
return true
2022-11-21 22:47:32 +00:00
}
// test if the current site should be processed or not
// this will also be the function for whitelist/blacklist feature
2022-12-02 21:45:34 +00:00
async function checkSite ( ) {
2022-11-21 22:47:32 +00:00
// is this site on our home instance?
2022-12-06 11:04:25 +00:00
if ( location . hostname == settings . fediact _homeinstance ) {
2022-12-04 16:56:42 +00:00
fedireply = getUrlParameter ( "fedireply" )
if ( ! fedireply ) {
2022-12-09 14:32:23 +00:00
log ( "Current site is your home instance." )
return false
2022-12-04 16:56:42 +00:00
}
2022-11-21 22:47:32 +00:00
}
// are we in whitelist mode?
2022-12-06 11:04:25 +00:00
if ( settings . fediact _mode == "whitelist" ) {
2022-11-21 22:47:32 +00:00
// if so, check if site is NOT in whitelist
2022-12-06 11:04:25 +00:00
if ( $ . inArray ( location . hostname , settings . fediact _whitelist ) < 0 ) {
2022-12-09 14:32:23 +00:00
log ( "Current site is not in whitelist." )
return false
2022-11-21 22:47:32 +00:00
}
} else {
// otherwise we are in blacklist mode, so check if site is on blacklist
2022-12-06 11:04:25 +00:00
if ( $ . inArray ( location . hostname , settings . fediact _blacklist ) > - 1 ) {
2022-12-09 14:32:23 +00:00
log ( "Current site is in blacklist." )
return false
2022-11-21 22:47:32 +00:00
}
}
// last check - and probably the most accurate to determine if it actually is mastadon
2022-12-09 14:32:23 +00:00
var requestUrl = location . protocol + '//' + location . hostname + instanceApi
2022-11-21 22:47:32 +00:00
// call instance api to confirm its mastodon and get normalized handle uri
2022-12-09 14:32:23 +00:00
var response = await makeRequest ( "GET" , requestUrl , null )
2022-11-21 22:47:32 +00:00
if ( response ) {
2022-12-09 14:32:23 +00:00
var uri = JSON . parse ( response ) . uri
2022-11-21 22:47:32 +00:00
if ( uri ) {
// run external mode
2022-12-09 14:32:23 +00:00
return true
2022-11-18 13:20:22 +00:00
}
}
2022-12-09 14:32:23 +00:00
log ( "Does not look like a Mastodon instance." )
return false
2022-11-21 22:47:32 +00:00
}
2022-11-18 13:20:22 +00:00
2022-12-08 14:23:58 +00:00
async function backgroundProcessor ( ) {
2022-12-08 10:08:14 +00:00
// wait for any url change messages from background script
2022-12-07 18:49:07 +00:00
chrome . runtime . onMessage . addListener ( function ( request , sender , sendResponse ) {
2022-12-08 14:23:58 +00:00
if ( request . urlchanged ) {
2022-12-07 18:49:07 +00:00
// reset already processed elements
processed = [ ]
2022-12-08 14:23:58 +00:00
}
if ( request . updatedfedisettings ) {
location . reload ( )
}
} )
2022-12-08 10:08:14 +00:00
// send message to initialize onUpdated listener in background script (this way it gets the tabid and we do not need to bind the listener for ALL sites)
try {
2022-12-08 14:23:58 +00:00
await chrome . runtime . sendMessage ( { running : true } )
2022-12-08 10:08:14 +00:00
return true
} catch ( e ) {
log ( e )
}
return false
2022-12-07 18:49:07 +00:00
}
2022-11-21 22:47:32 +00:00
// run wrapper
async function run ( ) {
2022-12-08 10:08:14 +00:00
// get setting
try {
settings = await ( browser || chrome ) . storage . local . get ( settingsDefaults )
} catch ( e ) {
log ( e )
return false
}
2022-11-21 22:47:32 +00:00
if ( settings ) {
// validate settings
if ( checkSettings ( ) ) {
// check site (if and which scripts should run)
2022-11-26 01:40:41 +00:00
if ( await checkSite ( ) ) {
2022-12-04 16:56:42 +00:00
if ( fedireply ) {
processReply ( )
} else {
2022-12-08 14:23:58 +00:00
if ( backgroundProcessor ( ) ) {
2022-12-08 10:08:14 +00:00
processFollow ( )
processToots ( )
} else {
log ( "Failed to initialize background script." )
}
2022-12-04 16:56:42 +00:00
}
2022-11-21 22:47:32 +00:00
} else {
log ( "Will not process this site." )
}
}
} else {
log ( "Could not load settings." )
}
2022-11-18 13:20:22 +00:00
}
2022-12-08 10:08:14 +00:00
run ( )