2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
// =-=-=-=-=-= CONSTANTS =-==-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
2022-12-13 13:30:24 +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" , "button.remote-button" , "div.account__header button.button--follow" ]
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" , "a.user-screen-name" , "div.profile-info-panel small" ]
2022-12-09 14:27:57 +00:00
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+))?\/?$/
2022-12-15 11:52:20 +00:00
const enableConsoleLog = false
2022-12-09 14:27:57 +00:00
const logPrepend = "[FediAct]"
const instanceApi = "/api/v1/instance"
const statusApi = "/api/v1/statuses"
const searchApi = "/api/v2/search"
const accountsApi = "/api/v1/accounts"
2022-12-15 11:51:15 +00:00
const domainBlocksApi = "/api/v1/domain_blocks"
2022-12-14 13:13:27 +00:00
const pollsApi = "/api/v1/polls"
2022-12-02 21:45:34 +00:00
const apiDelay = 500
2022-12-08 14:23:58 +00:00
const maxTootCache = 200
2022-12-15 11:51:15 +00:00
const modalHtml = `
< div style = "position: fixed; z-index: 99999; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); margin: 0; padding: 0;" class = "fedimodal" >
< div style = "background-color: #494949; border: 1px solid #888; width: 50%; max-width: 300px; left: 50%; top: 50%; transform: translate(-50%, -50%); position: absolute; margin: 0; padding: 0;" class = "fedimodal-content" >
< ul style = "width: 100%; margin: 0; padding: 0;" >
< / u l >
< / d i v >
< / d i v > `
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 ,
2022-12-10 15:01:38 +00:00
fediact _enabledelay : true ,
fediact _hidemuted : false ,
2022-12-15 00:23:31 +00:00
fediact _runifloggedin : false ,
fediact _showtoot : true ,
2022-12-10 15:37:06 +00:00
fediact _mutesblocks : [ ] ,
fediact _domainblocks : [ ]
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-11 14:55:09 +00:00
var [ processed , processedFollow , isProcessing ] = [ [ ] , [ ] , [ ] ]
2022-12-02 21:45:34 +00:00
2022-12-10 13:11:05 +00:00
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 ) {
if ( ! selector ) {
return false
}
2022-12-10 13:09:08 +00:00
// catch all animationstart events
2022-12-02 21:45:34 +00:00
$ ( document ) . on ( 'animationstart webkitAnimationStart oanimationstart MSAnimationStart' , function ( e ) {
2022-12-10 13:09:08 +00:00
// check if the animatonname equals our animation and if the element is one of our selectors
2022-12-02 21:45:34 +00:00
if ( e . originalEvent . animationName == 'nodeInserted' && $ ( e . target ) . is ( selector ) ) {
if ( typeof callback == 'function' ) {
2022-12-10 13:09:08 +00:00
// return the complete object in the callback
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-14 10:44:01 +00:00
// for checking if logged in on an external instance
function isLoggedIn ( ) {
return new Promise ( function ( resolve ) {
if ( $ ( document ) . find ( "script#initial-state" ) . length ) {
var initialState = $ ( document ) . find ( "script#initial-state" ) . first ( )
var stateJson = JSON . parse ( $ ( initialState ) . text ( ) )
if ( stateJson . meta . access _token ) {
resolve ( true )
}
} else {
$ ( document ) . DOMNodeAppear ( function ( e ) {
var initialState = $ ( e . target )
var stateJson = JSON . parse ( $ ( initialState ) . text ( ) )
if ( stateJson . meta . access _token ) {
resolve ( true )
}
} , "script#initial-state" )
}
resolve ( false )
} )
}
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-14 13:13:27 +00:00
async function makeRequest ( method , url , extraheaders , jsonbody ) {
2022-12-02 21:45:34 +00:00
// 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-10 13:09:08 +00:00
// get current time
2022-12-02 21:45:34 +00:00
var currenttime = Date . now ( )
2022-12-10 13:09:08 +00:00
// get difference of current time and time of last request
2022-12-02 21:45:34 +00:00
var difference = currenttime - lasthomerequest
2022-12-10 13:09:08 +00:00
// if difference is smaller than our set api delay value...
2022-12-03 16:41:04 +00:00
if ( difference < apiDelay ) {
2022-12-10 13:09:08 +00:00
// ... then wait the time required to reach the api delay value...
2022-12-02 21:45:34 +00:00
await new Promise ( resolve => {
setTimeout ( function ( ) {
resolve ( )
} , apiDelay - difference )
} )
}
2022-12-10 13:09:08 +00:00
// TODO: move this to the top? or get new Date.now() here?
2022-12-02 21:45:34 +00:00
lasthomerequest = currenttime
2022-12-10 13:09:08 +00:00
}
// return a new promise...
2022-12-08 14:23:58 +00:00
return new Promise ( function ( resolve ) {
2022-12-10 13:09:08 +00:00
// create xhr
2022-12-09 14:32:23 +00:00
let xhr = new XMLHttpRequest ( )
2022-12-10 13:09:08 +00:00
// open it with the method and url specified
2022-12-08 14:23:58 +00:00
xhr . open ( method , url )
2022-12-10 13:09:08 +00:00
// set timeout
2022-12-08 14:23:58 +00:00
xhr . timeout = 3000
2022-12-10 13:09:08 +00:00
// set extra headers if any were given
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
}
}
2022-12-10 13:09:08 +00:00
// on load, check if status is OK...
2022-11-21 22:47:32 +00:00
xhr . onload = function ( ) {
if ( this . status >= 200 && this . status < 300 ) {
2022-12-10 13:09:08 +00:00
// is ok, resolve promise with response
2022-12-08 14:23:58 +00:00
resolve ( xhr . responseText )
2022-11-21 22:47:32 +00:00
} else {
2022-12-10 13:09:08 +00:00
// nope, resolve false
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
}
2022-12-10 13:09:08 +00:00
// on any error, resolve false
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-10 13:09:08 +00:00
// send the request
2022-12-14 13:13:27 +00:00
if ( jsonbody ) {
xhr . setRequestHeader ( "Content-Type" , "application/json" )
xhr . send ( JSON . stringify ( jsonbody ) )
} else {
xhr . send ( )
}
2022-12-08 14:23:58 +00:00
} )
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-10 13:09:08 +00:00
// check if redirects are enabled at all
2022-12-06 11:04:25 +00:00
if ( settings . fediact _redirects ) {
2022-12-14 22:55:27 +00:00
// check if alert before redirect is enabled and show the prompt if so
2022-12-06 11:04:25 +00:00
if ( settings . fediact _alert ) {
2022-12-14 22:55:27 +00:00
if ( ! confirm ( "Redirecting to " + url ) ) {
return
}
2022-11-26 01:40:41 +00:00
}
// 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 =-=-=-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
2022-12-14 13:13:27 +00:00
async function executeAction ( id , action , polldata ) {
var requestUrl , condition , jsonbody
2022-12-15 11:51:15 +00:00
var method = "POST"
2022-12-10 15:01:38 +00:00
switch ( action ) {
2022-12-15 11:51:15 +00:00
case 'domainblock' :
requestUrl = 'https://' + settings . fediact _homeinstance + domainBlocksApi + "?domain=" + id
condition = function ( response ) { if ( response ) { return true } }
break
case 'domainunblock' :
requestUrl = 'https://' + settings . fediact _homeinstance + domainBlocksApi + "?domain=" + id
condition = function ( response ) { if ( response ) { return true } }
method = "DELETE"
break
case 'mute' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/mute"
condition = function ( response ) { return response . muting }
break
case 'unmute' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/unmute"
condition = function ( response ) { return ! response . muting }
break
case 'block' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/block"
condition = function ( response ) { return response . blocking }
break
case 'unblock' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/unblock"
condition = function ( response ) { return ! response . blocking }
break
2022-12-14 13:13:27 +00:00
case 'vote' :
requestUrl = 'https://' + settings . fediact _homeinstance + pollsApi + "/" + id + "/votes"
condition = function ( response ) { return response . voted }
jsonbody = polldata
break
2022-12-10 15:01:38 +00:00
case 'follow' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/follow"
condition = function ( response ) { return response . following || response . requested }
break
case 'boost' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/reblog"
condition = function ( response ) { return response . reblogged }
break
case 'favourite' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/favourite"
condition = function ( response ) { return response . favourited }
break
case 'bookmark' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/bookmark"
condition = function ( response ) { return response . bookmarked }
break
case 'unfollow' :
requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/" + id + "/unfollow"
condition = function ( response ) { return ! response . following && ! response . requested }
break
case 'unboost' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unreblog"
condition = function ( response ) { return ! response . reblogged }
break
case 'unfavourite' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unfavourite"
condition = function ( response ) { return ! response . favourited }
break
case 'unbookmark' :
requestUrl = 'https://' + settings . fediact _homeinstance + statusApi + "/" + id + "/unbookmark"
condition = function ( response ) { return ! response . bookmarked }
break
default :
log ( "No valid action specified." ) ; break
}
if ( requestUrl ) {
2022-12-15 11:51:15 +00:00
var response = await makeRequest ( method , requestUrl , settings . tokenheader , jsonbody )
2022-12-10 15:01:38 +00:00
if ( response ) {
// convert to json object
response = JSON . parse ( response )
if ( condition ( response ) ) {
return true
2022-12-04 16:30:47 +00:00
}
}
}
2022-12-10 15:01:38 +00:00
log ( action + " action failed." )
2022-12-04 16:30:47 +00:00
}
2022-12-10 13:09:08 +00:00
// allows to check if user is following one or multiple user IDs on the home instance
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-10 13:09:08 +00:00
// build the request url with one or multiple IDs
2022-12-02 21:45:34 +00:00
for ( const id of ids ) {
// trailing & is no issue
requestUrl += "id[]=" + id . toString ( ) + "&"
}
2022-12-10 13:09:08 +00:00
// make the request
2022-12-14 13:13:27 +00:00
var responseFollowing = await makeRequest ( "GET" , requestUrl , settings . tokenheader , null )
2022-12-10 13:09:08 +00:00
// fill response array according to id amount with false
2022-12-09 14:32:23 +00:00
const follows = Array ( ids . length ) . fill ( false )
2022-12-10 13:09:08 +00:00
// parse the response
2022-12-02 21:45:34 +00:00
if ( responseFollowing ) {
2022-12-09 14:32:23 +00:00
responseFollowing = JSON . parse ( responseFollowing )
2022-12-10 13:09:08 +00:00
// iterate over ids and accounts in response
2022-12-02 21:45:34 +00:00
for ( var i = 0 ; i < ids . length ; i ++ ) {
for ( account of responseFollowing ) {
2022-12-10 13:09:08 +00:00
// check if the current account matches the id at the current index
2022-12-02 21:45:34 +00:00
if ( account . id == ids [ i ] ) {
2022-12-11 11:44:01 +00:00
if ( account . following || account . requested ) {
2022-12-10 13:09:08 +00:00
// update the response array at the given index with true if the account is already followed
2022-12-02 21:45:34 +00:00
follows [ i ] = true
}
}
}
}
}
return follows
}
2022-12-10 15:01:38 +00:00
// check if handle is muted
function isMuted ( handle ) {
// remove leading @
2022-12-11 20:05:30 +00:00
if ( handle . startsWith ( "@" ) ) {
handle = handle . slice ( 1 )
}
2022-12-10 15:01:38 +00:00
// add local uri if handle has no domain already
if ( handle . split ( "@" ) . length - 1 == 0 ) {
handle = handle + "@" + settings . fediact _exturi
}
2022-12-10 15:37:06 +00:00
if ( settings . fediact _mutesblocks . includes ( handle ) || settings . fediact _domainblocks . includes ( handle . split ( "@" ) [ 1 ] ) ) {
2022-12-10 15:01:38 +00:00
return true
}
}
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
// =-=-=-=-=-= RESOLVING =-=-==-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=
2022-12-10 13:09:08 +00:00
// Return the user id on the users home instance
2022-12-02 21:45:34 +00:00
async function resolveHandleToHome ( handle ) {
2022-12-15 09:39:43 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + accountsApi + "/search?q=" + handle + "&resolve=true&limit=1&exclude_unreviewed=false"
2022-12-14 13:13:27 +00:00
var searchResponse = await makeRequest ( "GET" , requestUrl , settings . tokenheader , null )
2022-12-02 21:45:34 +00:00
if ( searchResponse ) {
searchResponse = JSON . parse ( searchResponse )
if ( searchResponse [ 0 ] . id ) {
2022-12-10 13:09:08 +00:00
// return the first account result
2022-12-02 21:45:34 +00:00
return [ searchResponse [ 0 ] . id , searchResponse [ 0 ] . acct ]
}
}
return false
}
2022-12-10 13:09:08 +00:00
// resolve a toot to the users home instance
2022-12-05 11:07:07 +00:00
async function resolveTootToHome ( searchstring ) {
2022-12-15 09:39:43 +00:00
var requestUrl = 'https://' + settings . fediact _homeinstance + searchApi + "/?q=" + searchstring + "&resolve=true&limit=1&exclude_unreviewed=false"
2022-12-14 13:13:27 +00:00
var response = await makeRequest ( "GET" , requestUrl , settings . tokenheader , null )
2022-12-05 11:07:07 +00:00
if ( response ) {
2022-12-09 14:32:23 +00:00
response = JSON . parse ( response )
2022-12-10 13:09:08 +00:00
// do we have a status as result?
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-14 13:13:27 +00:00
if ( status . poll ) {
2022-12-15 11:51:15 +00:00
return [ [ status . account . acct , status . id , status . reblogged , status . favourited , status . bookmarked , status . account . id ] , [ status . poll . id , status . poll . voted ] ]
2022-12-14 13:13:27 +00:00
} else {
// return the required status data
2022-12-15 11:51:15 +00:00
return [ [ status . account . acct , status . id , status . reblogged , status . favourited , status . bookmarked , status . account . id ] , [ false , false ] ]
2022-12-14 13:13:27 +00:00
}
2022-12-05 11:07:07 +00:00
} else {
2022-12-14 13:13:27 +00:00
return [ false , false ]
2022-12-05 11:07:07 +00:00
}
} else {
2022-12-14 13:13:27 +00:00
return [ false , false ]
2022-12-05 11:07:07 +00:00
}
}
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-10 13:09:08 +00:00
// if we encounter an error here, it is likely since the extension context got invalidated, so reload the page
2022-12-08 15:07:37 +00:00
log ( e )
2022-12-10 13:09:08 +00:00
log ( "Reloading page, extension likely got updated or reloaded." )
2022-12-08 15:07:37 +00:00
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
}
}
2022-12-10 13:11:05 +00:00
2022-12-02 21:45:34 +00:00
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
// =-=-=-=-= SITE PROCESSING =-==-=-=
// =-=-=-=-==-=-=-=-==-=-=-=-==-=-=-=
2022-12-10 13:09:08 +00:00
// custom implementation for allowing to toggle inline css
2022-12-02 21:45:34 +00:00
function toggleInlineCss ( el , styles , toggleclass ) {
2022-12-10 13:09:08 +00:00
// toggle the active class (specified) on the element (specified), then check the current state
2022-12-09 14:32:23 +00:00
var active = $ ( el ) . toggleClass ( toggleclass ) . hasClass ( toggleclass )
2022-12-10 13:09:08 +00:00
// we can have multiple styles as input, so loop through them
2022-12-02 21:45:34 +00:00
for ( var style of styles ) {
2022-12-10 13:09:08 +00:00
// if the element now has the active class...
2022-12-02 21:45:34 +00:00
if ( active ) {
2022-12-10 13:09:08 +00:00
// set the third value as style (first value / 0 is style itself)
2022-12-02 21:45:34 +00:00
$ ( el ) . css ( style [ 0 ] , style [ 2 ] )
} else {
2022-12-10 13:09:08 +00:00
// otherwise, if the second value is "!remove"...
2022-12-02 21:45:34 +00:00
if ( style [ 1 ] == "!remove" ) {
2022-12-10 13:09:08 +00:00
// remove the inline css by regex replacing
2022-12-02 21:45:34 +00:00
var newinline = replaceAll ( $ ( el ) . attr ( 'style' ) , style [ 0 ] + ": " + style [ 2 ] + ";" , "" )
$ ( el ) . attr ( 'style' , newinline )
} else {
2022-12-10 13:09:08 +00:00
// otherwise set the second value as style
2022-12-02 21:45:34 +00:00
$ ( el ) . css ( style [ 0 ] , style [ 1 ] )
}
}
}
}
2022-12-10 13:09:08 +00:00
// check if an toot identifier is already in the "processed" array
2022-12-08 14:23:58 +00:00
function isInProcessedToots ( id ) {
2022-12-10 13:09:08 +00:00
// iterate array
2022-12-08 14:23:58 +00:00
for ( var i = 0 ; i < processed . length ; i ++ ) {
2022-12-10 13:09:08 +00:00
// if the the first value of the nested array at the current index matches the id we look for...
2022-12-08 14:23:58 +00:00
if ( processed [ i ] [ 0 ] == id ) {
2022-12-10 13:09:08 +00:00
// return the index
2022-12-08 14:23:58 +00:00
return i
}
}
2022-12-10 13:09:08 +00:00
// if none was found...
2022-12-08 14:23:58 +00:00
return false
}
2022-12-10 13:09:08 +00:00
// add a toot to the "processed" array
2022-12-08 14:23:58 +00:00
function addToProcessedToots ( toot ) {
2022-12-10 13:09:08 +00:00
// push the array first
2022-12-08 14:23:58 +00:00
processed . push ( toot )
2022-12-10 13:09:08 +00:00
// check the difference of the max elements to cache and the current length of the processed array
2022-12-08 14:23:58 +00:00
var diff = processed . length - maxTootCache
2022-12-10 13:09:08 +00:00
// if diff is greater than 0...
2022-12-08 14:23:58 +00:00
if ( diff > 0 ) {
2022-12-10 13:09:08 +00:00
// remove the first diff items from it
2022-12-08 14:23:58 +00:00
processed = processed . splice ( 0 , diff )
}
2022-12-14 01:14:26 +00:00
return processed . length - 1
2022-12-08 14:23:58 +00:00
}
2022-12-15 11:51:15 +00:00
function showModal ( settings ) {
// [ [action,tootdata],[action,tootdata] ]
var baseEl = $ ( modalHtml )
var appendTo = $ ( baseEl ) . find ( "ul" )
for ( const entry of settings ) {
var nameUppercase = entry [ 0 ] . charAt ( 0 ) . toUpperCase ( ) + entry [ 0 ] . slice ( 1 ) ;
var append = "<li style='cursor: pointer; width: 100%; padding: 5px 10px; box-sizing: border-box;'><a style='font-size: 16px; cursor: pointer; text-decoration: none; color: white;' fediaction='" + entry [ 0 ] + "' fediid='" + entry [ 1 ] + "'>" + nameUppercase + "</a></li>"
$ ( appendTo ) . append ( $ ( append ) )
}
$ ( "body" ) . append ( $ ( baseEl ) )
$ ( "body" ) . on ( "click" , function ( e ) {
if ( $ ( e . target ) . is ( ".fedimodal li, .fedimodal li a" ) ) {
if ( $ ( e . target ) . is ( ".fedimodal li" ) ) {
e . target = $ ( e . target ) . find ( "a" )
}
var action = $ ( e . target ) . attr ( "fediaction" )
var id = $ ( e . target ) . attr ( "fediid" )
var done = executeAction ( id , action , null )
if ( done ) {
$ ( baseEl ) . remove ( )
$ ( "body" ) . off ( )
} else {
alert ( "Failed to " + action )
}
} else {
$ ( baseEl ) . remove ( )
$ ( "body" ) . off ( )
}
} )
}
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 ( ) {
2022-12-10 13:09:08 +00:00
// wait for the detailed status action bar to appear
2022-12-04 16:56:42 +00:00
$ ( document ) . DOMNodeAppear ( function ( e ) {
2022-12-10 13:09:08 +00:00
// find the reply button and click it
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 ) {
2022-12-09 17:07:13 +00:00
// set to false initially
2022-12-02 21:45:34 +00:00
var action = false
2022-12-09 17:07:13 +00:00
// check if the clicked element has a retweet icon
2022-12-02 21:45:34 +00:00
if ( $ ( e . currentTarget ) . children ( "i.fa-retweet" ) . length ) {
2022-12-09 17:07:13 +00:00
// if so, check if the retweet icon has the fediactive class (for all other buttons it will be the button element itself)
2022-12-02 21:45:34 +00:00
if ( $ ( e . currentTarget ) . children ( "i.fa-retweet" ) . hasClass ( "fediactive" ) ) {
2022-12-09 17:07:13 +00:00
// yep, has the class - so this action should be unboost
2022-12-09 14:32:23 +00:00
action = "unboost"
2022-12-02 21:45:34 +00:00
} else {
2022-12-09 17:07:13 +00:00
// nope, so it will be a boost
2022-12-09 14:32:23 +00:00
action = "boost"
2022-12-02 21:45:34 +00:00
}
2022-12-09 17:07:13 +00:00
// repeat for favourite and bookmark
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-10 13:09:08 +00:00
// should rarely reach this point, but some v3 instances include the action in the href only, so we have this as a fallback
// (v3 public view does NOT have a bookmark button)
2022-12-02 21:45:34 +00:00
} else if ( $ ( e . currentTarget ) . attr ( "href" ) ) {
2022-12-10 13:09:08 +00:00
// does the href include "type=reblog"?
2022-12-02 21:45:34 +00:00
if ( ~ $ ( e . currentTarget ) . attr ( "href" ) . indexOf ( "type=reblog" ) ) {
2022-12-10 13:09:08 +00:00
// if so, do as above...
2022-12-02 21:45:34 +00:00
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
}
2022-12-10 13:09:08 +00:00
// repeat for favourite
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 ) {
2022-12-09 17:07:13 +00:00
// is it a full url?
2022-12-09 14:27:57 +00:00
if ( temp . startsWith ( "http" ) ) {
2022-12-09 17:07:13 +00:00
// yes, create a new URL() to access its parts
2022-12-09 14:27:57 +00:00
var tempUrl = new URL ( temp )
2022-12-09 17:07:13 +00:00
// is the hostname of the URL the same as the current instance hostname?
2022-12-09 14:27:57 +00:00
if ( location . hostname == tempUrl . hostname ) {
2022-12-09 17:07:13 +00:00
// yes, so its a local toot id
2022-12-09 14:27:57 +00:00
temp = temp . split ( "/" )
2022-12-09 17:07:13 +00:00
// handle trailing / case
2022-12-09 14:27:57 +00:00
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 {
2022-12-09 17:07:13 +00:00
// no, so it must be a local toot id as well
2022-12-09 14:27:57 +00:00
temp = temp . split ( "/" )
var tempLast = temp . pop ( ) || temp . pop ( )
return [ false , tempLast ]
}
}
// get only the toot author handle
function getTootAuthor ( el ) {
2022-12-09 17:07:13 +00:00
// find the element containing the display name and return text if found
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-09 17:07:13 +00:00
// detailed status wrapper - get id from current document url
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-09 17:07:13 +00:00
// otherwise check if current element has data-id attribute
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-09 17:07:13 +00:00
// otherwise do the same for any closest article or div with the data-id attribute
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-14 10:44:01 +00:00
} else if ( $ ( el ) . find ( "a.icon-button:has(i.fa-star), a.detailed-status__link:has(i.fa-star)" ) . length ) {
var hrefEl = $ ( el ) . find ( "a.icon-button:has(i.fa-star), a.detailed-status__link:has(i.fa-star)" ) . first ( )
if ( $ ( hrefEl ) . attr ( "href" ) ) {
var hrefAttr = $ ( hrefEl ) . attr ( "href" )
if ( ~ hrefAttr . indexOf ( "interact/" ) ) {
var splitted = hrefAttr . split ( "?" ) [ 0 ] . split ( "/" )
var lastpart = splitted . pop ( ) || splitted . pop ( )
return lastpart
}
}
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 ) {
2022-12-09 17:07:13 +00:00
// for each element possibly containing an href, check if its and external fully resolved href or an internal reference and return the first found
2022-12-09 14:27:57 +00:00
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-14 10:44:01 +00:00
return [ false , undefined ]
2022-12-02 21:45:34 +00:00
}
2022-12-10 15:01:38 +00:00
// check toot author, mentions and toot prepend mentions for applying mutes
function processMutes ( el , tootAuthor ) {
if ( settings . fediact _hidemuted ) {
var hrefs = [ ]
if ( $ ( el ) . siblings ( ".status__prepend" ) . length ) {
var prepended = $ ( el ) . siblings ( ".status__prepend" ) . first ( )
2022-12-11 11:19:46 +00:00
if ( $ ( prepended ) . find ( "a" ) . attr ( "href" ) ) {
hrefs . push ( $ ( prepended ) . find ( "a" ) . attr ( "href" ) . split ( "?" ) [ 0 ] )
}
2022-12-10 15:01:38 +00:00
}
// build array of mentions in @user@domain.com format
// NOTE: this will fail if ax external user handle uses another subdomain than hostname, but FediAct was not designed for that - this is best effort
$ ( el ) . find ( "span.h-card" ) . each ( function ( ) {
hrefs . push ( $ ( this ) . find ( "a" ) . attr ( "href" ) . split ( "?" ) [ 0 ] )
} )
var processedHrefs = [ ]
2022-12-14 10:44:01 +00:00
for ( var href of hrefs ) {
2022-12-10 15:01:38 +00:00
var splitted = href . split ( "/" )
var lastpart = splitted . pop ( ) || splitted . pop ( )
lastpart = lastpart . slice ( 1 )
if ( href . startsWith ( "http" ) ) {
var url = new URL ( href )
if ( url . hostname != settings . fediact _exturi && url . hostname != location . hostname ) {
// external handle
processedHrefs . push ( lastpart + "@" + url . hostname )
} else {
processedHrefs . push ( lastpart + "@" + settings . fediact _exturi )
}
} else {
processedHrefs . push ( lastpart + "@" + settings . fediact _exturi )
}
}
2022-12-10 15:37:06 +00:00
if ( processedHrefs . some ( r => settings . fediact _mutesblocks . includes ( r ) ) || isMuted ( tootAuthor ) || processedHrefs . some ( r => settings . fediact _domainblocks . includes ( r . split ( "@" ) [ 1 ] ) ) ) {
2022-12-10 15:01:38 +00:00
$ ( el ) . hide ( )
if ( prepended ) {
$ ( prepended ) . hide ( )
}
return true
}
}
}
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-14 10:44:01 +00:00
if ( $ ( el ) . is ( "div.detailed-status" ) && $ ( el ) . closest ( "div.focusable" ) . length ) {
2022-12-07 18:49:07 +00:00
el = $ ( el ) . closest ( "div.focusable" )
}
2022-12-09 17:07:13 +00:00
// get toot data
2022-12-09 14:27:57 +00:00
var tootAuthor = getTootAuthor ( $ ( el ) )
2022-12-10 15:01:38 +00:00
// check if mutes apply and return if so
if ( processMutes ( el , tootAuthor ) ) {
return
}
2022-12-09 14:27:57 +00:00
var tootInternalId = getTootInternalId ( $ ( el ) )
var [ tootHrefIsExt , tootHrefOrId ] = getTootExtIntHref ( $ ( el ) )
2022-12-09 17:07:13 +00:00
// we will always need an internal reference to the toot, be it an actual internal toot id or the href of a toot already resolved to its home
// tootInternalId will be preferred if both are set
2022-12-09 14:27:57 +00:00
var internalIdentifier = tootInternalId || tootHrefOrId
2022-12-09 17:07:13 +00:00
// do we have one of those?
2022-12-09 14:27:57 +00:00
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
2022-12-14 19:06:44 +00:00
var favButton = $ ( el ) . find ( "button:has(i.fa-star)" ) . first ( )
if ( ! $ ( favButton ) . length ) {
favButton = $ ( el ) . find ( "a.icon-button:has(i.fa-star), a.detailed-status__link:has(i.fa-star)" )
}
var boostButton = $ ( el ) . find ( "button:has(i.fa-retweet)" ) . first ( )
if ( ! $ ( boostButton ) . length ) {
boostButton = $ ( el ) . find ( "a.icon-button:has(i.fa-retweet), a.detailed-status__link:has(i.fa-retweet)" )
}
2022-12-07 18:49:07 +00:00
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-14 13:13:27 +00:00
var voteButton = $ ( el ) . find ( "div.poll button" ) . first ( )
2022-12-15 11:51:15 +00:00
var moreButton = $ ( el ) . find ( "button:has(i.fa-ellipsis-h)" ) . first ( )
2022-12-15 00:23:31 +00:00
// handles process when a vote button is clicked
2022-12-14 13:13:27 +00:00
async function pollAction ( id , redirect , e ) {
if ( settings . fediact _autoaction ) {
var pollData = {
choices : [ ]
}
var pollDiv = $ ( e . currentTarget ) . closest ( "div.poll" )
var listEls = $ ( pollDiv ) . find ( "li" )
for ( var i = 0 ; i < listEls . length ; i ++ ) {
if ( $ ( listEls [ i ] ) . find ( "span.active" ) . length ) {
pollData . choices . push ( String ( i ) )
}
}
var result = await executeAction ( id , "vote" , pollData )
if ( result ) {
$ ( e . currentTarget ) . hide ( )
$ ( pollDiv ) . find ( "ul" ) . replaceWith ( "<p style='font-style: italic'><a style='font-weight:bold; color:orange' href='" + redirect + "' target='" + settings . fediact _target + "'>View the results</a> on your home instance.<p>" )
if ( cacheIndex ) {
2022-12-15 11:51:15 +00:00
processed [ cacheIndex ] [ 10 ] = true
processed [ cacheIndex ] [ 11 ] = true
2022-12-14 13:13:27 +00:00
}
}
return result
}
}
// handles process when a toot button is clicked
async function tootAction ( id , e ) {
2022-12-10 15:01:38 +00:00
if ( settings . fediact _autoaction ) {
// determine the action to perform
var action = getTootAction ( e )
if ( action ) {
// resolve url on home instance to get local toot/author identifiers and toot status
2022-12-14 13:13:27 +00:00
var actionExecuted = await executeAction ( id , action , null )
2022-12-10 15:01:38 +00:00
if ( actionExecuted ) {
2022-12-15 00:23:31 +00:00
if ( cacheIndex ) {
// set interacted to true
2022-12-15 11:51:15 +00:00
processed [ cacheIndex ] [ 11 ] = true
2022-12-15 00:23:31 +00:00
}
2022-12-10 15:01:38 +00:00
// if the action was successfully executed, update the element styles
if ( action == "boost" || action == "unboost" ) {
// toggle inline css styles
2022-12-12 09:25:42 +00:00
toggleInlineCss ( $ ( e . currentTarget ) , [ [ "color" , "!remove" , "rgb(140, 141, 255)" ] ] , "fediactive" )
toggleInlineCss ( $ ( e . currentTarget ) . find ( "i" ) , [ [ "transition-duration" , "!remove" , "0.9s" ] , [ "background-position" , "!remove" , "0px 100%" ] ] , "fediactive" )
2022-12-10 15:01:38 +00:00
// update element in cache if exists
if ( cacheIndex ) {
processed [ cacheIndex ] [ 3 ] = ! processed [ cacheIndex ] [ 3 ]
}
// same for favourite, bookmarked....
} else if ( action == "favourite" || action == "unfavourite" ) {
toggleInlineCss ( $ ( e . currentTarget ) , [ [ "color" , "!remove" , "rgb(202, 143, 4)" ] ] , "fediactive" )
2022-12-12 09:25:42 +00:00
toggleInlineCss ( $ ( e . currentTarget ) . find ( "i" ) , [ [ "animation" , "spring-rotate-out 1s linear" , "spring-rotate-in 1s linear" ] ] , "fediactive" )
2022-12-10 15:01:38 +00:00
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 ]
}
2022-12-14 01:14:26 +00:00
}
2022-12-10 15:01:38 +00:00
return true
2022-12-08 14:23:58 +00:00
} else {
2022-12-10 15:01:38 +00:00
log ( "Could not execute action on home instance." )
}
2022-12-08 14:23:58 +00:00
} else {
2022-12-10 15:01:38 +00:00
log ( "Could not determine action." )
2022-12-08 14:23:58 +00:00
}
} else {
2022-12-10 15:01:38 +00:00
log ( "Auto-action disabled." )
return true
2022-12-08 14:23:58 +00:00
}
}
2022-12-09 17:07:13 +00:00
// handles initialization of element styles
2022-12-08 14:23:58 +00:00
function initStyles ( tootdata ) {
2022-12-09 17:07:13 +00:00
// always remove any existing "Unresolved" indicator from the element first
2022-12-07 18:49:07 +00:00
$ ( el ) . find ( ".feditriggered" ) . remove ( )
2022-12-09 17:07:13 +00:00
// is the toot unresolved?
2022-12-08 15:07:37 +00:00
if ( ! tootdata [ 1 ] ) {
2022-12-09 17:07:13 +00:00
// yes, then add the Unresolved indicator
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 {
2022-12-15 00:23:31 +00:00
// otherwise start processing button styles (if enabled OR if the toot was already interacted with, to restore the state while still on the same page)
2022-12-09 17:07:13 +00:00
// first enable the bookmark button (is disabled on external instances)
2022-12-08 15:07:37 +00:00
$ ( bookmarkButton ) . removeClass ( "disabled" ) . removeAttr ( "disabled" )
2022-12-15 11:51:15 +00:00
$ ( moreButton ) . removeClass ( "disabled" ) . removeAttr ( "disabled" )
2022-12-14 13:13:27 +00:00
$ ( voteButton ) . removeAttr ( "disabled" )
2022-12-15 11:51:15 +00:00
if ( settings . fediact _showtoot || tootdata [ 11 ] ) {
2022-12-15 00:23:31 +00:00
// set the toot buttons to active, depending on the state of the resolved toot and if the element already has the active class
if ( tootdata [ 4 ] ) {
if ( ! $ ( favButton ) . hasClass ( "fediactive" ) ) {
toggleInlineCss ( $ ( favButton ) , [ [ "color" , "!remove" , "rgb(202, 143, 4)" ] ] , "fediactive" )
toggleInlineCss ( $ ( favButton ) . find ( "i" ) , [ [ "animation" , "spring-rotate-out 1s linear" , "spring-rotate-in 1s linear" ] ] , "fediactive" )
}
2022-12-08 15:07:37 +00:00
}
2022-12-15 00:23:31 +00:00
// repeat for other buttons
if ( tootdata [ 3 ] ) {
if ( ! $ ( boostButton ) . find ( "i.fediactive" ) . length ) {
toggleInlineCss ( $ ( boostButton ) , [ [ "color" , "!remove" , "rgb(140, 141, 255)" ] ] , "fediactive" )
toggleInlineCss ( $ ( boostButton ) . find ( "i" ) , [ [ "transition-duration" , "!remove" , "0.9s" ] , [ "background-position" , "!remove" , "0px 100%" ] ] , "fediactive" )
}
2022-12-08 15:07:37 +00:00
}
2022-12-15 00:23:31 +00:00
if ( tootdata [ 5 ] ) {
if ( ! $ ( bookmarkButton ) . hasClass ( "fediactive" ) ) {
toggleInlineCss ( $ ( bookmarkButton ) , [ [ "color" , "!remove" , "rgb(255, 80, 80)" ] ] , "fediactive" )
}
}
2022-12-15 11:51:15 +00:00
if ( tootdata [ 10 ] ) {
2022-12-15 00:23:31 +00:00
$ ( voteButton ) . hide ( )
2022-12-15 11:51:15 +00:00
$ ( voteButton ) . closest ( "div.poll" ) . find ( "ul" ) . replaceWith ( "<p style='font-style: italic'><a style='font-weight:bold; color:orange' href='" + tootdata [ 7 ] + "' target='" + settings . fediact _target + "'>View the results</a> on your home instance.<p>" )
2022-12-08 15:07:37 +00:00
}
2022-12-14 13:13:27 +00:00
}
2022-12-07 18:49:07 +00:00
}
}
2022-12-09 17:07:13 +00:00
// handles binding of clicks events for all buttons of a toot
2022-12-08 14:23:58 +00:00
function clickBinder ( tootdata ) {
2022-12-09 17:07:13 +00:00
// reply button is simple, it will always redirect to the homeinstance with the fedireply parameter set
2022-12-07 18:49:07 +00:00
$ ( replyButton ) . on ( "click" , function ( e ) {
2022-12-09 17:07:13 +00:00
// prevent default and immediate propagation
2022-12-09 14:32:23 +00:00
e . preventDefault ( )
e . stopImmediatePropagation ( )
2022-12-09 17:07:13 +00:00
// redirect to the resolved URL + fedireply parameter (so the extension can handle it after redirect)
2022-12-15 11:51:15 +00:00
redirectTo ( tootdata [ 7 ] + "?fedireply" )
} )
$ ( moreButton ) . on ( "click" , function ( e ) {
// prevent default and immediate propagation
e . preventDefault ( )
e . stopImmediatePropagation ( )
// redirect to the resolved URL + fedireply parameter (so the extension can handle it after redirect)
var domainsplit = tootdata [ 1 ] . split ( "@" )
var domain = domainsplit . pop ( ) || domainsplit . pop ( )
showModal ( [ [ "mute" , tootdata [ 6 ] ] , [ "unmute" , tootdata [ 6 ] ] , [ "block" , tootdata [ 6 ] ] , [ "unblock" , tootdata [ 6 ] ] , [ "domainblock" , domain ] , [ "domainunblock" , domain ] ] )
2022-12-07 18:49:07 +00:00
} )
2022-12-09 17:07:13 +00:00
// for all other buttons...
2022-12-14 13:13:27 +00:00
$ ( [ favButton , boostButton , bookmarkButton , voteButton ] ) . each ( function ( ) {
if ( $ ( voteButton ) . length ) {
if ( $ ( voteButton ) . get ( 0 ) . isEqualNode ( $ ( this ) . get ( 0 ) ) ) {
var isVote = true
}
}
2022-12-09 17:07:13 +00:00
// these behave differently with single / double click
// we use a custom solution for handling dblclick since the default event does not work here
// init function global vars required for single/double click handling
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 ( )
2022-12-09 17:07:13 +00:00
// increase click counter
2022-12-09 14:32:23 +00:00
clicks ++
2022-12-09 17:07:13 +00:00
// this will always run, but see below for double click handling
2022-12-07 18:49:07 +00:00
if ( clicks == 1 ) {
timer = setTimeout ( async function ( ) {
2022-12-15 11:51:15 +00:00
if ( isVote && ! tootdata [ 10 ] ) {
var actionExecuted = pollAction ( tootdata [ 9 ] , tootdata [ 7 ] , e )
2022-12-14 13:13:27 +00:00
} else {
// execute action on click and get result (fail/success)
var actionExecuted = await tootAction ( 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 17:07:13 +00:00
// reset clicks
2022-12-09 14:32:23 +00:00
clicks = 0
} , 350 )
2022-12-07 18:49:07 +00:00
} else {
2022-12-09 17:07:13 +00:00
// if we get here, the element was clicked twice before the above timeout was over, so this is a double click
// reset the above timeout so it wont execute
2022-12-09 14:32:23 +00:00
clearTimeout ( timer )
2022-12-14 13:13:27 +00:00
if ( isVote ) {
2022-12-15 11:51:15 +00:00
var actionExecuted = pollAction ( tootdata [ 9 ] , tootdata [ 7 ] , e )
2022-12-14 13:13:27 +00:00
} else {
// execute action on click and get result (fail/success)
var actionExecuted = await tootAction ( tootdata [ 2 ] , e )
}
2022-12-07 18:49:07 +00:00
if ( ! actionExecuted ) {
log ( "Action failed." )
} else {
2022-12-09 17:07:13 +00:00
// redirect to home instance with the resolved toot url
2022-12-15 11:51:15 +00:00
redirectTo ( tootdata [ 7 ] )
2022-12-02 21:45:34 +00:00
}
2022-12-09 17:07:13 +00:00
// reset clicks
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 17:07:13 +00:00
// always add the already resolved external toot href first, if it was set
2022-12-09 14:27:57 +00:00
if ( tootHrefIsExt ) {
homeResolveStrings . push ( tootHrefOrId )
}
2022-12-09 17:07:13 +00:00
// we can only process internalTootIds if we also have a user handle
2022-12-09 14:27:57 +00:00
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
}
}
}
2022-12-09 15:34:38 +00:00
// always add 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 17:14:48 +00:00
// if we have any resolve strings to resolve on our home instance...
2022-12-09 14:27:57 +00:00
if ( homeResolveStrings . length ) {
2022-12-09 17:14:48 +00:00
// filter duplicates
2022-12-09 14:27:57 +00:00
homeResolveStrings = homeResolveStrings . filter ( ( element , index ) => {
return ( homeResolveStrings . indexOf ( element ) == index )
} )
2022-12-09 17:14:48 +00:00
// initialize with false
2022-12-09 14:27:57 +00:00
var resolvedToHomeInstance = false
2022-12-09 17:14:48 +00:00
// for each resolve string...
2022-12-09 14:27:57 +00:00
for ( var homeResolveString of homeResolveStrings ) {
2022-12-09 17:14:48 +00:00
// run only if not already resolved
2022-12-09 14:27:57 +00:00
if ( ! resolvedToHomeInstance ) {
// resolve toot on actual home instance
2022-12-15 11:51:15 +00:00
var [ resolvedToot , poll ] = await resolveTootToHome ( homeResolveString ) // [status.account.acct, status.id, status.reblogged, status.favourited, status.bookmarked, status.account.id], [pollid/false, voted]
2022-12-09 14:27:57 +00:00
if ( resolvedToot ) {
2022-12-09 17:14:48 +00:00
// if successful, set condition to true (so it will not be resolved twice)
2022-12-09 14:27:57 +00:00
resolvedToHomeInstance = true
// set the redirect to home instance URL in @ format
var redirectUrl = 'https://' + settings . fediact _homeinstance + "/@" + resolvedToot [ 0 ] + "/" + resolvedToot [ 1 ]
2022-12-09 17:14:48 +00:00
// prepare the cache entry / toot data entry
2022-12-15 00:23:31 +00:00
var fullEntry = [ internalIdentifier , ... resolvedToot , redirectUrl , true , ... poll , false ]
2022-12-15 11:51:15 +00:00
// 0: internal identifier; 1: toot home acct / false 2: toot home id 3: toot reblogged 4: toot favourited 5: toot bookmarked 6: home account id 7: redirect url 8: ??? crap! 9: poll id / false 10: poll voted 11: interacted
2022-12-09 14:27:57 +00:00
}
}
}
2022-12-09 17:14:48 +00:00
// was any resolve successful?
2022-12-09 14:27:57 +00:00
if ( resolvedToHomeInstance ) {
2022-12-09 17:14:48 +00:00
// yes, so add to processed toots with the full toot data entry
2022-12-14 01:14:26 +00:00
cacheIndex = addToProcessedToots ( fullEntry )
2022-12-09 17:14:48 +00:00
// ... and init styles
2022-12-08 14:23:58 +00:00
initStyles ( fullEntry )
2022-12-14 01:14:26 +00:00
// continue with click handling...
clickBinder ( fullEntry )
2022-12-07 18:49:07 +00:00
} else {
2022-12-09 17:14:48 +00:00
// no, but we will still add the toot to cache as unresolved
2022-12-09 14:27:57 +00:00
log ( "Failed to resolve: " + homeResolveStrings )
2022-12-14 01:14:26 +00:00
cacheIndex = addToProcessedToots ( [ internalIdentifier , false ] )
2022-12-09 14:27:57 +00:00
initStyles ( [ internalIdentifier , false ] )
2022-12-07 18:49:07 +00:00
}
} else {
2022-12-09 17:14:48 +00:00
// no resolve possible without any resolve strings, but we will still add the toot to cache as unresolved
2022-12-07 18:49:07 +00:00
log ( "Could not identify a post URI for home resolving." )
2022-12-14 01:14:26 +00:00
cacheIndex = addToProcessedToots ( [ internalIdentifier , false ] )
2022-12-09 14:27:57 +00:00
initStyles ( [ internalIdentifier , false ] )
2022-12-02 21:45:34 +00:00
}
2022-11-21 22:47:32 +00:00
} else {
2022-12-09 17:14:48 +00:00
// the toot is already in cache, so grab it
2022-12-08 14:23:58 +00:00
var toot = processed [ cacheIndex ]
2022-12-09 17:14:48 +00:00
// init stylings
2022-12-08 14:23:58 +00:00
initStyles ( toot )
2022-12-09 17:14:48 +00:00
// if it is NOT unresolved, bind click handlers again
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 ) {
2022-12-11 14:55:09 +00:00
if ( ! isProcessing . includes ( $ ( e . target ) . get ( 0 ) ) ) {
isProcessing . push ( $ ( e . target ) . get ( 0 ) )
process ( $ ( e . target ) )
}
2022-12-09 14:32:23 +00:00
} , "div.status, div.detailed-status" )
2022-12-11 14:55:09 +00:00
// try to find all existing elements (fixes some elements not being detected by DOMNodeAppear in rare cases, esp. v3)
$ ( document ) . find ( "div.status, div.detailed-status" ) . each ( function ( ) {
if ( ! isProcessing . includes ( $ ( this ) . get ( 0 ) ) ) {
isProcessing . push ( $ ( this ) . get ( 0 ) )
process ( $ ( this ) )
}
} )
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 ( ) {
// for mastodon v3 - v4 does not show follow buttons / account cards on /explore
async function process ( el ) {
2022-12-11 11:44:01 +00:00
var fullHandle
var action = "follow"
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-10 15:01:38 +00:00
if ( settings . fediact _autoaction ) {
// execute action and save result
2022-12-14 13:13:27 +00:00
var response = await executeAction ( id , action , null )
2022-12-10 15:01:38 +00:00
// if action was successful, update button text and action value according to performed action
if ( action == "follow" && response ) {
$ ( el ) . text ( "Unfollow" )
action = "unfollow"
return true
// repeat for unfollow action
} else if ( action == "unfollow" && response ) {
$ ( el ) . text ( "Follow" )
action = "follow"
return true
}
} else {
log ( "Auto-action disabled." )
2022-12-10 13:09:08 +00:00
return true
2022-12-08 10:08:14 +00:00
}
}
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 ( )
2022-12-13 13:30:24 +00:00
if ( fullHandle . split ( "@" ) . length - 1 == 1 ) {
fullHandle = fullHandle + "@" + settings . fediact _exturi
}
2022-12-02 21:45:34 +00:00
break
2022-11-26 01:40:41 +00:00
}
2022-12-02 21:45:34 +00:00
}
}
2022-12-09 17:14:48 +00:00
// do we have a full handle?
2022-12-02 21:45:34 +00:00
if ( fullHandle ) {
2022-12-11 14:55:09 +00:00
if ( ! processedFollow . includes ( fullHandle ) ) {
// yes, so resolve it to a user id on our homeinstance
var resolvedHandle = await resolveHandleToHome ( fullHandle )
if ( resolvedHandle ) {
processedFollow . push ( fullHandle )
// successfully resolved
// if showfollows is enabled...
if ( settings . fediact _showfollows ) {
// ... then check if user is already following
var isFollowing = await isFollowingHomeInstance ( [ resolvedHandle [ 0 ] ] )
// update button text and action if already following
if ( isFollowing [ 0 ] ) {
$ ( el ) . text ( "Unfollow" )
action = "unfollow"
}
2022-12-02 21:45:34 +00:00
}
2022-12-11 14:55:09 +00:00
// single and double click handling (see toot processing for explanation, is the same basically)
var clicks = 0
var timer
$ ( el ) . on ( "click" , async function ( e ) {
// prevent default and immediate propagation
e . preventDefault ( )
e . stopImmediatePropagation ( )
clicks ++
if ( clicks == 1 ) {
timer = setTimeout ( async function ( ) {
execFollow ( resolvedHandle [ 0 ] )
clicks = 0
} , 350 )
2022-12-02 21:45:34 +00:00
} else {
2022-12-11 14:55:09 +00:00
clearTimeout ( timer )
var done = await execFollow ( resolvedHandle [ 0 ] )
if ( done ) {
var saveText = $ ( el ) . text ( )
var redirectUrl = 'https://' + settings . fediact _homeinstance + '/@' + resolvedHandle [ 1 ]
$ ( el ) . text ( "Redirecting..." )
setTimeout ( function ( ) {
redirectTo ( redirectUrl )
$ ( el ) . text ( saveText )
} , 1000 )
} else {
log ( "Action failed." )
}
clicks = 0
2022-11-22 10:00:02 +00:00
}
2022-12-11 14:55:09 +00:00
} ) . on ( "dblclick" , function ( e ) {
e . preventDefault ( )
e . stopImmediatePropagation ( )
} )
} else {
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 ) {
2022-12-11 14:55:09 +00:00
if ( ! isProcessing . includes ( $ ( e . target ) . get ( 0 ) ) ) {
isProcessing . push ( $ ( e . target ) . get ( 0 ) )
process ( $ ( e . target ) )
}
2022-12-07 21:17:49 +00:00
} , allFollowPaths )
2022-12-11 14:55:09 +00:00
// try to find all existing elements (fixes some elements not being detected by DOMNodeAppear in rare cases, esp. v3)
$ ( document ) . find ( allFollowPaths ) . each ( function ( ) {
if ( ! isProcessing . includes ( $ ( this ) . get ( 0 ) ) ) {
isProcessing . push ( $ ( this ) . get ( 0 ) )
process ( $ ( this ) )
}
} )
2022-11-21 22:47:32 +00:00
}
2022-12-10 13:11:05 +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-12 09:40:41 +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-14 13:13:27 +00:00
var response = await makeRequest ( "GET" , requestUrl , null , 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 ) {
2022-12-13 13:30:24 +00:00
if ( uri . startsWith ( "http" ) ) {
uri = new URL ( uri )
settings . fediact _exturi = uri . hostname
} else {
settings . fediact _exturi = uri
}
2022-12-15 00:30:40 +00:00
// at this point, we know that it's mastodon and the background processor should start running
if ( ! backgroundProcessor ( ) ) {
log ( "Could not start background process" )
return false
}
// if option is enabled, check if logged in on that instance and stop
2022-12-15 00:23:31 +00:00
if ( ! settings . fediact _runifloggedin ) {
if ( await isLoggedIn ( ) ) {
log ( "Already logged in to this external instance." )
return false
}
2022-12-14 10:44:01 +00:00
}
2022-12-15 00:23:31 +00:00
} else {
return false
2022-11-18 13:20:22 +00:00
}
2022-12-15 00:23:31 +00:00
} else {
return false
2022-11-18 13:20:22 +00:00
}
2022-12-15 00:23:31 +00:00
return true
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-11 11:19:46 +00:00
chrome . runtime . onMessage . addListener ( async 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-11 14:55:09 +00:00
processedFollow = [ ]
isProcessing = [ ]
2022-12-11 11:19:46 +00:00
// rerun getSettings to keep mutes/blocks up to date while not reloading the page
if ( ! await getSettings ( ) ) {
// but reload if settings are invalid
location . reload ( )
}
2022-12-08 14:23:58 +00:00
}
2022-12-11 11:19:46 +00:00
// if the settings were updated, we do a page reload
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-12-11 11:19:46 +00:00
function getSettings ( ) {
// get setting
return new Promise ( async function ( resolve ) {
try {
settings = await ( browser || chrome ) . storage . local . get ( settingsDefaults )
} catch ( e ) {
log ( e )
resolve ( false )
}
if ( settings ) {
// validate settings
if ( checkSettings ( ) ) {
resolve ( true )
} else {
resolve ( false )
}
} else {
resolve ( false )
}
} )
}
2022-11-21 22:47:32 +00:00
// run wrapper
async function run ( ) {
2022-12-11 11:19:46 +00:00
// validate settings
if ( await getSettings ( ) ) {
// check site (if and which scripts should run)
if ( await checkSite ( ) ) {
if ( fedireply ) {
processReply ( )
} else {
2022-12-15 00:30:40 +00:00
processFollow ( )
processToots ( )
2022-11-21 22:47:32 +00:00
}
2022-12-11 11:19:46 +00:00
} else {
log ( "Will not process this site." )
2022-11-21 22:47:32 +00:00
}
} else {
log ( "Could not load settings." )
}
2022-11-18 13:20:22 +00:00
}
2022-12-08 10:08:14 +00:00
run ( )