You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on May 16, 2020. It is now read-only.
hi there, i have used this script prior to today a few years back and it didnt work. I tried it again today as there seemed to be a lot of new updates floating around and i had forgotten it was out there.
it still doesnt work.
i am running the most recent update of firefox, greasemonkey and the most recent updates of this script. Both the 'fast' version and the 'slow' version just time out or, sit there frozen.
Thanks for making this free for everyone and for all the suggested donations you list - i'd be a lot more comfortable with donating again if i knew it worked.
53.0 (32-bit)
this is the script i downloaded - i think the last time i ever saw this was 2012... anyway i hope you manage to at least give some feedback or some updates if you can.
`/**
*
This is a Greasemonkey script and must be run using a Greasemonkey-compatible browser.
@author maymay bitetheappleback@gmail.com /
// ==UserScript==
// @name FetLife ASL Search (Females included)
// @Version 0.3.8
// @namespacehttp://maybemaimed.com/playground/fetlife-aslsearch/
// @description Allows you to search for FetLife profiles based on age, sex, location, and role.
// @includehttps://fetlife.com/
// @excludehttps://fetlife.com/adgear/*
// @excludehttps://fetlife.com/chat/*
// @excludehttps://fetlife.com/im_sessions*
// @excludehttps://fetlife.com/polling/*
// @grant GM_log
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// ==/UserScript==
FL_ASL = {
},
FL_ASL.CONFIG = {
debug: !1, // switch to true to debug.
progress_id: 'fetlife_asl_search_progress',
min_matches: 1
},
FL_ASL.total_result_count = 0, // How many matches have we found, across all pages, on this load?
// Utility debugging function.
FL_ASL.log = function (msg) {
FL_ASL.CONFIG.debug && GM_log('FETLIFE ASL SEARCH: ' + msg)
};
// Initializations.
var uw = unsafeWindow || window; // Help with Chrome compatibility?
GM_addStyle('#fetlife_asl_search_options { display: none; }#fetlife_asl_search_options fieldset { clear: both; margin: 0; padding: 0; }#fetlife_asl_search_options legend { display: none; }#fetlife_asl_search_options label { display: inline-block; white-space: nowrap;}#fetlife_asl_search_options input { width: auto; }#fetlife_asl_search_results { clear: both; }'),
FL_ASL.users = {
},
FL_ASL.init = function () {
FL_ASL.CONFIG.search_form = document.querySelector('form[action="/search"]').parentNode,
FL_ASL.getUserProfile(uw.FetLife.currentUser.id),
FL_ASL.main()
},
window.addEventListener('DOMContentLoaded', FL_ASL.init),
FL_ASL.toggleAslSearch = function () {
var el = document.getElementById('fetlife_asl_search_options');
'block' == el.style.display ? el.style.display = 'none' : el.style.display = 'block'
},
FL_ASL.toggleLocationFilter = function (e) {
var el = document.getElementById('fl_asl_loc_filter_label');
switch (e.currentTarget.value) {
case 'group':
case 'event':
case 'fetish':
case 'search':
case 'user':
'none' == el.style.display && (el.style.display = 'inline');
break;
default:
el.style.display = 'none'
}
},
FL_ASL.aslSubmit = function (e) {
if (!document.getElementById('fetlife_asl_search').checked) return !1;
document.getElementById(FL_ASL.CONFIG.progress_id).innerHTML = 'Searching… ';
// collect the form parameters
var search_params = FL_ASL.getSearchParams();
// search one of the geographic regions "/kinksters" list
return FL_ASL.getKinkstersInSet(search_params.loc),
!1
}, /**
Reads and saves the search parameters from the provided form.
*/
FL_ASL.getSearchParams = function () {
var r = {
age: {
min: null,
max: null
},
sex: [
],
role: [
],
loc: {
},
filter: ''
};
// Collect age parameters, setting wide defaults.
r.age.min = document.getElementById('min_age').value ? parseInt(document.getElementById('min_age').value) : 1,
r.age.max = document.getElementById('max_age').value ? parseInt(document.getElementById('max_age').value) : 99;
for (var x = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="user[sex]"]'), i = 0; i < x.length; i++) x[i].checked && r.sex.push(x[i].value);
for (var y = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="user[role]"]'), iy = 0; iy < y.length; iy++) y[iy].checked && r.role.push(y[iy].value);
for (var search_in = [
], z = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="fl_asl_loc"]'), iz = 0; iz < z.length; iz++) z[iz].checked && search_in.push(z[iz].value);
// Match location parameter with known location ID.
switch (search_in[0]) {
// These cases all use numeric object IDs.
case 'group':
case 'event':
case 'user':
case 'fetish':
r.loc[search_in[0]] = parseInt(FL_ASL.CONFIG.search_form.querySelector('input[data-flasl' + search_in[0] + 'id]').getAttribute('data-flasl' + search_in[0] + 'id'));
break;
// This case uses a string, so no need to parseInt() it.
case 'search':
r.loc[search_in[0]] = FL_ASL.CONFIG.search_form.querySelector('input[data-flasl' + search_in[0] + 'id]').getAttribute('data-flasl' + search_in[0] + 'id');
break;
default:
user_loc = FL_ASL.getLocationForUser(uw.FetLife.currentUser.id);
for (var xk in user_loc) null !== user_loc[xk] && - 1 !== search_in.indexOf(xk) && (r.loc[xk] = user_loc[xk])
} // Collect location filter, if one was entered.
return document.getElementById('fl_asl_loc_filter') && (r.filter = document.getElementById('fl_asl_loc_filter').value),
r
},
FL_ASL.getLocationForUser = function (id) {
var r = {
city_id: null,
area_id: null,
country: null
},
profile_html = FL_ASL.users[id].profile_html,
m = profile_html.match(/href="/countries/([0-9]+)/);
return m && (r.country = m[1]),
m = profile_html.match(/href="/administrative_areas/([0-9]+)/),
m && (r.area_id = m[1]),
m = profile_html.match(/href="/cities/([0-9]+)/),
m && (r.city_id = m[1]),
r
},
FL_ASL.getUserProfile = function (id) {
if (FL_ASL.users[id]) return FL_ASL.users[id].profile_html;
FL_ASL.users[id] = {
},
GM_xmlhttpRequest({
method: 'GET',
url: 'https://fetlife.com/users/' + id.toString(),
onload: function (response) {
FL_ASL.users[id].profile_html = response.responseText
}
})
},
FL_ASL.getKinkstersInSet = function (loc_obj) {
if (loc_obj.group) FL_ASL.getKinkstersInGroup(loc_obj.group);
else if (loc_obj.event) FL_ASL.getKinkstersInEvent(loc_obj.event);
else if (loc_obj.user) FL_ASL.getKinkstersInFriend(loc_obj.user);
else if (loc_obj.fetish) FL_ASL.getKinkstersInFetish(loc_obj.fetish);
else if (loc_obj.search) FL_ASL.getKinkstersInSearch(loc_obj.search);
else if (loc_obj.city_id) FL_ASL.getKinkstersInCity(loc_obj.city_id);
else if (loc_obj.area_id) FL_ASL.getKinkstersInArea(loc_obj.area_id);
else {
if (!loc_obj.country) return !1;
FL_ASL.getKinkstersInCountry(loc_obj.country)
}
},
FL_ASL.getKinkstersInCity = function (city_id, page) {
var url = 'https://fetlife.com/cities/' + city_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInArea = function (area_id, page) {
var url = 'https://fetlife.com/administrative_areas/' + area_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInCountry = function (country, page) {
var url = 'https://fetlife.com/countries/' + country.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInGroup = function (group, page) {
var url = 'https://fetlife.com/groups/' + group.toString() + '/group_memberships';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInEvent = function (event, page) {
var url = 'https://fetlife.com/events/' + event.toString() + '/rsvps';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInFriend = function (user_id, page) {
var url = 'https://fetlife.com/users/' + user_id.toString() + '/friends';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInFetish = function (fetish_id, page) {
var url = 'https://fetlife.com/fetishes/' + fetish_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInSearch = function (search_string, page) {
var url = 'https://fetlife.com/search/kinksters/?q=' + search_string.toString();
url = page ? url + '&page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersFromURL = function (url) {
FL_ASL.log('Getting Kinksters list from URL: ' + url),
// Set minimum matches, if that's been asked for.
document.getElementById('fl_asl_min_matches').value && (FL_ASL.CONFIG.min_matches = document.getElementById('fl_asl_min_matches').value),
prog = document.getElementById(FL_ASL.CONFIG.progress_id),
prog.innerHTML = prog.innerHTML + '.',
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function (response) {
var parser = new DOMParser,
doc = parser.parseFromString(response.responseText, 'text/html'),
els = doc.querySelectorAll('.user_in_list');
result_count = 0;
for (var i = 0; i < els.length; i++) // filter the results based on the form parameters
FL_ASL.matchesSearchParams(els[i]) && ( // display the results in a "results" section in this portion of the page
FL_ASL.displayResult(els[i]), result_count++, // note total results found
FL_ASL.total_result_count += result_count);
// Automatically search on next page if no or too few results were found.
if ( // Set up next request.
my_page = url.match(/\d+$/) ? parseInt(url.match(/\d+$/) [0]) : 1, next_page = my_page + 1, next_page > 2 ? next_url = url.replace(/\d+$/, next_page.toString()) : ( // Already have a query string? If so, append (&) rather than create (?).
next_url = url.match(/?q=/) ? url + '&page=' : url + '?page=', next_url += next_page.toString()), 0 === result_count || FL_ASL.CONFIG.min_matches >= FL_ASL.total_result_count) return FL_ASL.getKinkstersFromURL(next_url),
!1;
// Reset total_result_count for this load.
FL_ASL.total_result_count = 0,
// Reset UI search feedback.
p = prog.parentNode,
p.removeChild(prog),
new_prog = document.createElement('p'),
new_prog.setAttribute('id', FL_ASL.CONFIG.progress_id),
p.appendChild(new_prog),
btn = document.createElement('button'),
btn.setAttribute('id', 'btn_moar'),
btn.setAttribute('onclick', 'var xme = document.getElementById('btn_moar'); xme.parentNode.removeChild(xme); return false;'),
btn.innerHTML = 'Show me MOAR…',
btn.addEventListener('click', function () {
FL_ASL.getKinkstersFromURL(next_url)
}),
document.getElementById('fetlife_asl_search_results').appendChild(btn)
}
})
}, /**
Determines whether a "user_in_list" block matches the searched-for parameters.
@return True if block matches all search parameters, false otherwise. /
FL_ASL.matchesSearchParams = function (el) {
var search_params = FL_ASL.getSearchParams();
// Does block match location string filter?
if ( - 1 === FL_ASL.getLocationString(el).toLowerCase().search(search_params.filter.toLowerCase())) return !1;
// Does block match age range?
var age = FL_ASL.getAge(el);
// Did we supply a minimum age?
// Did we supply a minimum age?
// Did we supply a maximum age?
// Does block match gender/sex selection?
return !(search_params.age.min && search_params.age.min > age) && (!(search_params.age.max && search_params.age.max < age) && ( - 1 !== search_params.sex.indexOf(FL_ASL.getSex(el)) && - 1 !== search_params.role.indexOf(FL_ASL.getRole(el))))
},
FL_ASL.getSex = function (el) {
return el.querySelector('.quiet').innerHTML.match(/^\d\d(\S)/) [1]
},
FL_ASL.getAge = function (el) {
var x = el.querySelector('.quiet').innerHTML,
age = x.match(/^\d\d/);
return parseInt(age)
},
FL_ASL.getRole = function (el) {
return el.querySelector('.quiet').innerHTML.match(/ ?(\S+)?$/) [1]
},
FL_ASL.getLocationString = function (el) {
return el.querySelector('em').innerHTML
},
FL_ASL.displayResult = function (el) {
var id = el.querySelector('a').getAttribute('href').match(/\d+$/),
name = el.querySelector('.large a').childNodes[0].nodeValue,
a = document.createElement('a');
a.href = 'https://fetlife.com/conversations/new?with=' + id,
a.innerHTML = '(send ' + name + ' a message)',
a.style.textDecoration = 'underline',
a.setAttribute('target', '_blank'),
el.appendChild(a),
document.getElementById('fetlife_asl_search_results').appendChild(el)
},
// This is the main() function, executed on page load.
FL_ASL.main = function () {
// Insert ASL search button interface at FetLife "Search" bar.
var label = document.createElement('label');
label.innerHTML = 'A/S/L?';
var input = document.createElement('input');
input.setAttribute('style', '-webkit-appearance: checkbox'),
input.setAttribute('type', 'checkbox'),
input.setAttribute('id', 'fetlife_asl_search'),
input.setAttribute('name', 'fetlife_asl_search'),
input.setAttribute('value', '1'),
input.addEventListener('click', FL_ASL.toggleAslSearch),
label.appendChild(input);
var div = document.createElement('div');
div.setAttribute('id', 'fetlife_asl_search_options'),
div.setAttribute('style', 'display: none;'),
html_string = '
Search for user profiles of the following gender/sex:
',
html_string += 'Show me profiles of people with a gender/sex of…',
html_string += ' Male',
html_string += ' Female',
html_string += 'Crossdresser/Transvestite',
html_string += 'Trans - Male to Female',
html_string += 'Trans - Female to Male',
html_string += 'Transgender',
html_string += 'Gender Fluid',
html_string += 'Genderqueer',
html_string += 'Intersex',
html_string += 'Butch',
html_string += 'Femme',
html_string += '
',
html_string += 'Search for user profiles between the ages of:
',
html_string += '…who are also at least years old and at most years old…',
html_string += '
',
html_string += 'Search for user profiles whose role is:
',
html_string += '…who identify their role as ',
// Note that these values are what show up, not necessarily what's sent to the FetLife backend.
html_string += 'Dominant',
html_string += 'Domme',
html_string += 'Switch',
html_string += 'submissive',
html_string += 'Master',
html_string += 'Mistress',
html_string += 'slave',
html_string += 'pet',
html_string += 'kajira',
html_string += 'kajirus',
html_string += 'Top',
html_string += 'Bottom',
html_string += 'Sadist',
html_string += 'Masochist',
html_string += 'Sadomasochist',
html_string += 'Ageplayer',
html_string += 'Daddy',
html_string += 'babygirl',
html_string += 'brat',
html_string += 'Primal',
html_string += 'Fetishist',
html_string += 'Kinkster',
html_string += 'Hedonist',
html_string += 'Vanilla',
html_string += 'Unsure',
// Note that "Not Applicable" is the equivalent of "it doesn't matter", so we omit this.
//html_string += 'Not Applicable';
html_string += '
',
html_string += 'Search for user profiles located in:
',
html_string += '…from ';
// If we're on a "groups" or "events" or "user" or "fetish" or "search" page,
var which_thing = window.location.toString().match(/(group|event|user|fetish)e?s/(\d+)/) || window.location.toString().match(/(search)/kinksters/??(?:page=\d+&)?q=(\S+)/);
if (null !== which_thing) {
switch (which_thing[1]) {
case 'user':
var label_text = 'user's friends';
break;
case 'group': // fall through
case 'event':
case 'fetish':
case 'search':
default:
var label_text = which_thing[1]
} // offer an additional option to search for users associated with this object rather than geography.
(Don't worry, I'm not looking for where you actually are. Your location was determined from your FetLife profile.)
',
html_string += '
',
faade_dialog.innerHTML = html_string,
document.body.appendChild(faade_dialog),
// Attach event listener to trigger element.
document.querySelector('[data-opens-modal="faade"]').addEventListener('click', function (e) {
document.querySelector('[data-opens-modal="faade"]').dialog('open')
})
},
FAADE.getLocationFromProfileHtml = function (html) {
return (new DOMParser).parseFromString(html, 'text/html').querySelector('h2.bottom + p > em').textContent.split(', ')
},
FAADE.broadcastNewProximalReports = function (doc) {
// Recall timestamp of last record checked.
var last_timestamp_checked = parseInt(FAADE.getValue('last_timestamp_checked', '0')),
rows = doc.querySelectorAll('#tblMain tr'),
latest_timestamp_filed = Date.parse(rows[rows.length - 1].childNodes[1].textContent);
// If never checked, or if there are new records since last timestamp checked
if (last_timestamp_checked < latest_timestamp_filed) {
FAADE.log('Last timestamp checked (' + last_timestamp_checked.toString() + ') is older than latest timestamp filed (' + latest_timestamp_filed.toString() + ').');
for (var num_reports = 0, i = rows.length - 1; i > 0 && Date.parse(rows[i].childNodes[1].textContent) > last_timestamp_checked; i--) num_reports++;
FAADE.log('Total of ' + num_reports + ' new reports since last check.');
var user_loc = FAADE.getLocationFromProfileHtml(FL_ASL.users[uw.FetLife.currentUser.id].profile_html);
FAADE.log('Current user location seems to be ' + user_loc.join(', ') + '.');
for (var reports_to_alert = [
], i = rows.length - num_reports; i <= rows.length - 1; i++) {
// extract the location data from the report
report_loc = rows[i].childNodes[6].textContent;
// compare current user's FetLife profile location against alleged abuse location
// and test each substring of the user profile against the reported location
for (var z = 0; z < user_loc.length; z++) // if a relevant case insensitive substring matches
if ( - 1 !== report_loc.toLowerCase().search(user_loc[z].toLowerCase())) {
FAADE.log('Filed report location ' + report_loc + ' matches user location substring ' + user_loc[z] + '!'),
// store for future pop-up
reports_to_alert.push(rows[i]);
break
}
} // Ask user to view the profiles of the alleged abusers in the user's local vicinity.
if (reports_to_alert.length) {
// Fill in the user-facing message with the appropriate info.
document.getElementById('faade_reports_to_alert').innerHTML = reports_to_alert.length.toString(),
document.getElementById('faade_user_loc').innerHTML = user_loc.join(', ');
// Create the click event we're going to use.
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', !0, !1), // can bubble, can't be cancelled
// "Click" event on hidden code.
document.querySelector('a[data-opens-modal="faade"]').dispatchEvent(evt),
// Attach event listener to "View" button and pass in appropriate URLs.
document.querySelector('.btnsqr[data-closes-modal="faade"]').addEventListener('click', function () {
for (var i = 0; i < reports_to_alert.length; i++) {
GM_openInTab('https://fetlife.com/users/' + reports_to_alert[i].childNodes[2].textContent.match(/\d+/) [0])
}
})
}
} // Make a note of the latest timestamp filed, so we start here next time we're loaded.
FAADE.setValue('last_timestamp_checked', latest_timestamp_filed.toString())
},
// This is the main() function, executed on page load.
FAADE.main = function () {
for (var parser = new DOMParser, doc = parser.parseFromString(FAADE.abuser_database, 'text/html'), els = doc.querySelectorAll('#tblMain td:nth-child(3)'), abuser_ids = [
], i = 1; i < els.length; i++) // we never need the first (0'th) cell because Google provides it blank.
abuser_ids.push(els[i].innerHTML); // give us a few seconds to grab the current user's FetLife profile HTML.
// Are we on a user profile page?
if (FAADE.log('recalled abuser ids ' + abuser_ids), // TODO: Refactor this, it's kludgy.
setTimeout(function () {
FAADE.log('Running time-delayed function.'),
FL_ASL.users[uw.FetLife.currentUser.id].profile_html && (FAADE.log('We have the current user's FetLife profile HTML. Running broadcast checks.'), FAADE.broadcastNewProximalReports(doc))
}, 5000), window.location.href.match(/users/(\d+)/?$/)) {
var profile_nick = document.querySelector('h2.bottom').childNodes[0].textContent.match(/\S+/) [0],
id_in_url = window.location.href.match(/users/(\d+)/?$/) [1];
// If this is a profile page of an alleged abuser,
if ( // If we're not viewing our own profile page, insert a report link.
usr_ops = document.querySelector('#main_content p.quiet'), usr_ops && (usr_ops.appendChild(document.createElement('br')), usr_ops.appendChild(FAADE.createAbuseReportLink(id_in_url, profile_nick))), - 1 !== abuser_ids.indexOf(id_in_url)) {
var report_el = document.createElement('table');
report_el.setAttribute('id', 'faade_abuse_reports'),
report_el.setAttribute('summary', 'Reported consent violations committed by ' + profile_nick + '.');
var caption = document.createElement('caption');
caption.innerHTML = 'There are reports ' + profile_nick + ' violated others' consent in these ways:';
var tfoot = document.createElement('tfoot');
tfoot.innerHTML = '',
tfoot.querySelector('td').appendChild(FAADE.createAbuseReportLink(id_in_url, profile_nick)),
report_el.appendChild(caption),
report_el.appendChild(tfoot);
for (var abuse_reports = [
], ix = 0; ix < els.length; ix++) id_in_url === els[ix].innerHTML && abuse_reports.push(els[ix].parentNode);
// Add this information to the top of this user's profile
for (var iy = 0; iy < abuse_reports.length; iy++) {
var num = iy + 1,
tr = document.createElement('tr');
tr.setAttribute('id', 'faade_abuse_report-' + num.toString());
var details_html = '
' + abuse_reports[iy].childNodes[7].innerHTML + '
';
details_html += '
' + abuse_reports[iy].childNodes[6].innerHTML + '
';
var permalink_html = '' + abuse_reports[iy].childNodes[1].innerHTML + '';
tr.innerHTML += 'Abuse report ' + num.toString() + ' (' + permalink_html + '):' + details_html + '',
tr.innerHTML += '' + abuse_reports[iy].childNodes[5].innerHTML + '',
report_el.appendChild(tr)
}
var before = document.querySelector('#main_content table:last-child');
before.parentNode.insertBefore(report_el, before)
}
} // Collect all user links on this page.
var user_links = [
];
for (i = 0; i < document.links.length; i++) {
var l = document.links[i].href.match(/^(https://fetlife.com)?/users/(\d+)/?$/);
l && l[2] !== uw.FetLife.currentUser.id.toString() && user_links.push(document.links[i])
} // For each user ID found,
var last_id = null;
for (i = 0; i < user_links.length; i++) {
// Collect its user ID number.
var id = user_links[i].href.match(/\d+/?$/);
id && (id = id.toString()); // cast to string for later comparison
// Get nickname.
var n;
// Don't create another link if we just made one for that user.
if ( // This is an avatar link, not a text link.
n = user_links[i].children.length ? user_links[i].childNodes[0].alt : user_links[i].innerHTML, // check the alleged abusers data store for a match.
- 1 !== abuser_ids.indexOf(id) && (FAADE.log('found match on this page for alleged abuser ID number ' + id), // highlight the user's links that matched an alleged abuser using CSS
user_links[i].setAttribute('class', user_links[i].className + ' faade_alleged_abuser')), id !== last_id) {
// Offer a link to add another report for this user.
// See also: https://support.google.com/docs/bin/answer.py?hl=en&answer=160000
// Add link to report this user for a consent violation.
var a = FAADE.createAbuseReportLink(id, n);
user_links[i].parentNode.appendChild(a),
last_id = id
}
}
},
FAADE.createAbuseReportLink = function (id, nick) {
var a = document.createElement('a');
a.setAttribute('class', 'faade_report_link'),
a.setAttribute('target', '_blank');
var href = 'https://docs.google.com/spreadsheet/viewform?formkey=' + FAADE.getReportFormKey();
return href += '&entry_0=' + id,
href += '&entry_1=' + nick,
a.setAttribute('href', href),
a.innerHTML = '(report a consent violation by ' + nick + ')',
a
},
// The following is required for Chrome compatibility, as we need "text/html" parsing.
/*
hi there, i have used this script prior to today a few years back and it didnt work. I tried it again today as there seemed to be a lot of new updates floating around and i had forgotten it was out there.
it still doesnt work.
i am running the most recent update of firefox, greasemonkey and the most recent updates of this script. Both the 'fast' version and the 'slow' version just time out or, sit there frozen.
Thanks for making this free for everyone and for all the suggested donations you list - i'd be a lot more comfortable with donating again if i knew it worked.
53.0 (32-bit)
this is the script i downloaded - i think the last time i ever saw this was 2012... anyway i hope you manage to at least give some feedback or some updates if you can.
`/**
*
/
// ==UserScript==
// @name FetLife ASL Search (Females included)
// @Version 0.3.8
// @namespace http://maybemaimed.com/playground/fetlife-aslsearch/
// @description Allows you to search for FetLife profiles based on age, sex, location, and role.
// @include https://fetlife.com/
// @exclude https://fetlife.com/adgear/*
// @exclude https://fetlife.com/chat/*
// @exclude https://fetlife.com/im_sessions*
// @exclude https://fetlife.com/polling/*
// @grant GM_log
// @grant GM_xmlhttpRequest
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @grant GM_openInTab
// ==/UserScript==
FL_ASL = {
},
FL_ASL.CONFIG = {
debug: !1, // switch to true to debug.
progress_id: 'fetlife_asl_search_progress',
min_matches: 1
},
FL_ASL.total_result_count = 0, // How many matches have we found, across all pages, on this load?
// Utility debugging function.
FL_ASL.log = function (msg) {
FL_ASL.CONFIG.debug && GM_log('FETLIFE ASL SEARCH: ' + msg)
};
// Initializations.
var uw = unsafeWindow || window; // Help with Chrome compatibility?
GM_addStyle('#fetlife_asl_search_options { display: none; }#fetlife_asl_search_options fieldset { clear: both; margin: 0; padding: 0; }#fetlife_asl_search_options legend { display: none; }#fetlife_asl_search_options label { display: inline-block; white-space: nowrap;}#fetlife_asl_search_options input { width: auto; }#fetlife_asl_search_results { clear: both; }'),
FL_ASL.users = {
},
FL_ASL.init = function () {
FL_ASL.CONFIG.search_form = document.querySelector('form[action="/search"]').parentNode,
FL_ASL.getUserProfile(uw.FetLife.currentUser.id),
FL_ASL.main()
},
window.addEventListener('DOMContentLoaded', FL_ASL.init),
FL_ASL.toggleAslSearch = function () {
var el = document.getElementById('fetlife_asl_search_options');
'block' == el.style.display ? el.style.display = 'none' : el.style.display = 'block'
},
FL_ASL.toggleLocationFilter = function (e) {
var el = document.getElementById('fl_asl_loc_filter_label');
switch (e.currentTarget.value) {
case 'group':
case 'event':
case 'fetish':
case 'search':
case 'user':
'none' == el.style.display && (el.style.display = 'inline');
break;
default:
el.style.display = 'none'
}
},
FL_ASL.aslSubmit = function (e) {
if (!document.getElementById('fetlife_asl_search').checked) return !1;
document.getElementById(FL_ASL.CONFIG.progress_id).innerHTML = 'Searching…
';
// collect the form parameters
var search_params = FL_ASL.getSearchParams();
// search one of the geographic regions "/kinksters" list
return FL_ASL.getKinkstersInSet(search_params.loc),
!1
}, /**
*/
FL_ASL.getSearchParams = function () {
var r = {
age: {
min: null,
max: null
},
sex: [
],
role: [
],
loc: {
},
filter: ''
};
// Collect age parameters, setting wide defaults.
r.age.min = document.getElementById('min_age').value ? parseInt(document.getElementById('min_age').value) : 1,
r.age.max = document.getElementById('max_age').value ? parseInt(document.getElementById('max_age').value) : 99;
for (var x = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="user[sex]"]'), i = 0; i < x.length; i++) x[i].checked && r.sex.push(x[i].value);
for (var y = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="user[role]"]'), iy = 0; iy < y.length; iy++) y[iy].checked && r.role.push(y[iy].value);
for (var search_in = [
], z = FL_ASL.CONFIG.search_form.querySelectorAll('input[name="fl_asl_loc"]'), iz = 0; iz < z.length; iz++) z[iz].checked && search_in.push(z[iz].value);
// Match location parameter with known location ID.
switch (search_in[0]) {
// These cases all use numeric object IDs.
case 'group':
case 'event':
case 'user':
case 'fetish':
r.loc[search_in[0]] = parseInt(FL_ASL.CONFIG.search_form.querySelector('input[data-flasl' + search_in[0] + 'id]').getAttribute('data-flasl' + search_in[0] + 'id'));
break;
// This case uses a string, so no need to parseInt() it.
case 'search':
r.loc[search_in[0]] = FL_ASL.CONFIG.search_form.querySelector('input[data-flasl' + search_in[0] + 'id]').getAttribute('data-flasl' + search_in[0] + 'id');
break;
default:
user_loc = FL_ASL.getLocationForUser(uw.FetLife.currentUser.id);
for (var xk in user_loc) null !== user_loc[xk] && - 1 !== search_in.indexOf(xk) && (r.loc[xk] = user_loc[xk])
} // Collect location filter, if one was entered.
return document.getElementById('fl_asl_loc_filter') && (r.filter = document.getElementById('fl_asl_loc_filter').value),
r
},
FL_ASL.getLocationForUser = function (id) {
var r = {
city_id: null,
area_id: null,
country: null
},
profile_html = FL_ASL.users[id].profile_html,
m = profile_html.match(/href="/countries/([0-9]+)/);
return m && (r.country = m[1]),
m = profile_html.match(/href="/administrative_areas/([0-9]+)/),
m && (r.area_id = m[1]),
m = profile_html.match(/href="/cities/([0-9]+)/),
m && (r.city_id = m[1]),
r
},
FL_ASL.getUserProfile = function (id) {
if (FL_ASL.users[id]) return FL_ASL.users[id].profile_html;
FL_ASL.users[id] = {
},
GM_xmlhttpRequest({
method: 'GET',
url: 'https://fetlife.com/users/' + id.toString(),
onload: function (response) {
FL_ASL.users[id].profile_html = response.responseText
}
})
},
FL_ASL.getKinkstersInSet = function (loc_obj) {
if (loc_obj.group) FL_ASL.getKinkstersInGroup(loc_obj.group);
else if (loc_obj.event) FL_ASL.getKinkstersInEvent(loc_obj.event);
else if (loc_obj.user) FL_ASL.getKinkstersInFriend(loc_obj.user);
else if (loc_obj.fetish) FL_ASL.getKinkstersInFetish(loc_obj.fetish);
else if (loc_obj.search) FL_ASL.getKinkstersInSearch(loc_obj.search);
else if (loc_obj.city_id) FL_ASL.getKinkstersInCity(loc_obj.city_id);
else if (loc_obj.area_id) FL_ASL.getKinkstersInArea(loc_obj.area_id);
else {
if (!loc_obj.country) return !1;
FL_ASL.getKinkstersInCountry(loc_obj.country)
}
},
FL_ASL.getKinkstersInCity = function (city_id, page) {
var url = 'https://fetlife.com/cities/' + city_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInArea = function (area_id, page) {
var url = 'https://fetlife.com/administrative_areas/' + area_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInCountry = function (country, page) {
var url = 'https://fetlife.com/countries/' + country.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInGroup = function (group, page) {
var url = 'https://fetlife.com/groups/' + group.toString() + '/group_memberships';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInEvent = function (event, page) {
var url = 'https://fetlife.com/events/' + event.toString() + '/rsvps';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInFriend = function (user_id, page) {
var url = 'https://fetlife.com/users/' + user_id.toString() + '/friends';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInFetish = function (fetish_id, page) {
var url = 'https://fetlife.com/fetishes/' + fetish_id.toString() + '/kinksters';
url = page ? url + '?page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersInSearch = function (search_string, page) {
var url = 'https://fetlife.com/search/kinksters/?q=' + search_string.toString();
url = page ? url + '&page=' + page.toString() : url,
FL_ASL.getKinkstersFromURL(url)
},
FL_ASL.getKinkstersFromURL = function (url) {
FL_ASL.log('Getting Kinksters list from URL: ' + url),
// Set minimum matches, if that's been asked for.
document.getElementById('fl_asl_min_matches').value && (FL_ASL.CONFIG.min_matches = document.getElementById('fl_asl_min_matches').value),
prog = document.getElementById(FL_ASL.CONFIG.progress_id),
prog.innerHTML = prog.innerHTML + '.',
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function (response) {
var parser = new DOMParser,
doc = parser.parseFromString(response.responseText, 'text/html'),
els = doc.querySelectorAll('.user_in_list');
result_count = 0;
for (var i = 0; i < els.length; i++) // filter the results based on the form parameters
FL_ASL.matchesSearchParams(els[i]) && ( // display the results in a "results" section in this portion of the page
FL_ASL.displayResult(els[i]), result_count++, // note total results found
FL_ASL.total_result_count += result_count);
// Automatically search on next page if no or too few results were found.
if ( // Set up next request.
my_page = url.match(/\d+$/) ? parseInt(url.match(/\d+$/) [0]) : 1, next_page = my_page + 1, next_page > 2 ? next_url = url.replace(/\d+$/, next_page.toString()) : ( // Already have a query string? If so, append (&) rather than create (?).
next_url = url.match(/?q=/) ? url + '&page=' : url + '?page=', next_url += next_page.toString()), 0 === result_count || FL_ASL.CONFIG.min_matches >= FL_ASL.total_result_count) return FL_ASL.getKinkstersFromURL(next_url),
!1;
// Reset total_result_count for this load.
FL_ASL.total_result_count = 0,
// Reset UI search feedback.
p = prog.parentNode,
p.removeChild(prog),
new_prog = document.createElement('p'),
new_prog.setAttribute('id', FL_ASL.CONFIG.progress_id),
p.appendChild(new_prog),
btn = document.createElement('button'),
btn.setAttribute('id', 'btn_moar'),
btn.setAttribute('onclick', 'var xme = document.getElementById('btn_moar'); xme.parentNode.removeChild(xme); return false;'),
btn.innerHTML = 'Show me MOAR…',
btn.addEventListener('click', function () {
FL_ASL.getKinkstersFromURL(next_url)
}),
document.getElementById('fetlife_asl_search_results').appendChild(btn)
}
})
}, /**
Determines whether a "user_in_list" block matches the searched-for parameters.
@return True if block matches all search parameters, false otherwise.
Search for user profiles of the following gender/sex:/
FL_ASL.matchesSearchParams = function (el) {
var search_params = FL_ASL.getSearchParams();
// Does block match location string filter?
if ( - 1 === FL_ASL.getLocationString(el).toLowerCase().search(search_params.filter.toLowerCase())) return !1;
// Does block match age range?
var age = FL_ASL.getAge(el);
// Did we supply a minimum age?
// Did we supply a minimum age?
// Did we supply a maximum age?
// Does block match gender/sex selection?
return !(search_params.age.min && search_params.age.min > age) && (!(search_params.age.max && search_params.age.max < age) && ( - 1 !== search_params.sex.indexOf(FL_ASL.getSex(el)) && - 1 !== search_params.role.indexOf(FL_ASL.getRole(el))))
},
FL_ASL.getSex = function (el) {
return el.querySelector('.quiet').innerHTML.match(/^\d\d(\S)/) [1]
},
FL_ASL.getAge = function (el) {
var x = el.querySelector('.quiet').innerHTML,
age = x.match(/^\d\d/);
return parseInt(age)
},
FL_ASL.getRole = function (el) {
return el.querySelector('.quiet').innerHTML.match(/ ?(\S+)?$/) [1]
},
FL_ASL.getLocationString = function (el) {
return el.querySelector('em').innerHTML
},
FL_ASL.displayResult = function (el) {
var id = el.querySelector('a').getAttribute('href').match(/\d+$/),
name = el.querySelector('.large a').childNodes[0].nodeValue,
a = document.createElement('a');
a.href = 'https://fetlife.com/conversations/new?with=' + id,
a.innerHTML = '(send ' + name + ' a message)',
a.style.textDecoration = 'underline',
a.setAttribute('target', '_blank'),
el.appendChild(a),
document.getElementById('fetlife_asl_search_results').appendChild(el)
},
// This is the main() function, executed on page load.
FL_ASL.main = function () {
// Insert ASL search button interface at FetLife "Search" bar.
var label = document.createElement('label');
label.innerHTML = 'A/S/L?';
var input = document.createElement('input');
input.setAttribute('style', '-webkit-appearance: checkbox'),
input.setAttribute('type', 'checkbox'),
input.setAttribute('id', 'fetlife_asl_search'),
input.setAttribute('name', 'fetlife_asl_search'),
input.setAttribute('value', '1'),
input.addEventListener('click', FL_ASL.toggleAslSearch),
label.appendChild(input);
var div = document.createElement('div');
div.setAttribute('id', 'fetlife_asl_search_options'),
div.setAttribute('style', 'display: none;'),
html_string = '
',
',html_string += 'Show me profiles of people with a gender/sex of…',
html_string += ' Male',
html_string += ' Female',
html_string += 'Crossdresser/Transvestite',
html_string += 'Trans - Male to Female',
html_string += 'Trans - Female to Male',
html_string += 'Transgender',
html_string += 'Gender Fluid',
html_string += 'Genderqueer',
html_string += 'Intersex',
html_string += 'Butch',
html_string += 'Femme',
html_string += '
html_string += 'Search for user profiles between the ages of:
',
',html_string += '…who are also at least years old and at most years old…',
html_string += '
html_string += 'Search for user profiles whose role is:
',
',html_string += '…who identify their role as ',
// Note that these values are what show up, not necessarily what's sent to the FetLife backend.
html_string += 'Dominant',
html_string += 'Domme',
html_string += 'Switch',
html_string += 'submissive',
html_string += 'Master',
html_string += 'Mistress',
html_string += 'slave',
html_string += 'pet',
html_string += 'kajira',
html_string += 'kajirus',
html_string += 'Top',
html_string += 'Bottom',
html_string += 'Sadist',
html_string += 'Masochist',
html_string += 'Sadomasochist',
html_string += 'Ageplayer',
html_string += 'Daddy',
html_string += 'babygirl',
html_string += 'brat',
html_string += 'Primal',
html_string += 'Fetishist',
html_string += 'Kinkster',
html_string += 'Hedonist',
html_string += 'Vanilla',
html_string += 'Unsure',
// Note that "Not Applicable" is the equivalent of "it doesn't matter", so we omit this.
//html_string += 'Not Applicable';
html_string += '
html_string += 'Search for user profiles located in:
',
html_string += '…from ';
// If we're on a "groups" or "events" or "user" or "fetish" or "search" page,
var which_thing = window.location.toString().match(/(group|event|user|fetish)e?s/(\d+)/) || window.location.toString().match(/(search)/kinksters/??(?:page=\d+&)?q=(\S+)/);
if (null !== which_thing) {
switch (which_thing[1]) {
case 'user':
var label_text = 'user's friends';
break;
case 'group': // fall through
case 'event':
case 'fetish':
case 'search':
default:
var label_text = which_thing[1]
} // offer an additional option to search for users associated with this object rather than geography.
html_string += '<input type="radio" name="fl_asl_loc" value="' + which_thing[1] + '" data-flasl' + which_thing[1] + 'id="' + which_thing[2] + '"/>this ' + label_text + '',
',html_string += ' located in ',
html_string += ', or '
}
html_string += ' my city',
html_string += 'state/province',
html_string += 'country',
html_string += '.
html_string += 'Result set options:
',
',html_string += 'Return at least matches per search. (Set this lower if no results seem to ever appear.)',
html_string += '
div.innerHTML = html_string,
FL_ASL.CONFIG.search_form.appendChild(label),
FL_ASL.CONFIG.search_form.appendChild(div);
for (var radio_els = document.querySelectorAll('input[name="fl_asl_loc"]'), i = 0; i < radio_els.length; i++) radio_els[i].addEventListener('click', FL_ASL.toggleLocationFilter);
btn_submit = document.createElement('button'),
btn_submit.setAttribute('id', 'btn_fetlife_asl_search_submit'),
btn_submit.setAttribute('onclick', 'var xme = document.getElementById('btn_fetlife_asl_search_submit'); xme.parentNode.removeChild(xme); return false;'),
btn_submit.innerHTML = 'Mine! (I mean, uh, search…)',
btn_submit.addEventListener('click', FL_ASL.aslSubmit),
div.appendChild(btn_submit),
results_container = document.createElement('div'),
results_container.setAttribute('id', 'fetlife_asl_search_results'),
FL_ASL.CONFIG.search_form.appendChild(results_container),
prog = document.createElement('p'),
prog.setAttribute('id', FL_ASL.CONFIG.progress_id),
FL_ASL.CONFIG.search_form.appendChild(prog)
},
FAADE = {
},
FAADE.CONFIG = {
debug: !1, // switch to true to debug.
gdocs_key: '0ArYmNHuRadHbdGNVT1kzSzFnOXhHRjh1RnczZVVmMXc',
gform_key: 'dGNVT1kzSzFnOXhHRjh1RnczZVVmMXc6MQ',
gdocs_development_key: '0ArYmNHuRadHbdGxjMUhyR0FzLWJicHNXUFdxckFEQWc',
gform_development_key: 'dGxjMUhyR0FzLWJicHNXUFdxckFEQWc6MQ'
},
// Utility debugging function.
FAADE.log = function (msg) {
FAADE.CONFIG.debug && GM_log('FETLIFE FAADE: ' + msg)
};
// Initializations.
var uw = unsafeWindow || window; // Help with Chrome compatibility?
GM_addStyle('/* Highlight alleged abusers in bright yellow. /.faade_alleged_abuser { display: inline-block; border: 2px solid yellow;}#faade_abuse_reports caption { background: yellow; color: red;}#faade_abuse_reports tfoot td { padding-top: 1em; text-align: center;}#faade_abuse_reports tr:target > * { border: 1px solid red;}#faade_abuse_reports tr:target th { border-width: 1px 0 1px 1px;}#faade_abuse_reports tr:target td { border-width: 1px 1px 1px 0;}/ FAADE location broadcast dialog styles. /[aria-labelledby="ui-dialog-title-faade"] { border-color: yellow; }#ui-dialog-title-faade { color: red; }/ General prettiness. */#profile #main_content a + a.faade_report_link { padding-left: 5px; }footer .faade_report_link,.blog_entry p.quiet.small .faade_report_link,.byline .faade_report_link { display: block; color: #777;}.mini_feed_title .faade_report_link { float: left; padding-right: 5px;}ul.pictures li a.faade_report_link,#profile ul.friends li { width: auto; }'),
FAADE.init = function () {
FL_ASL.getUserProfile(uw.FetLife.currentUser.id), // run early
FAADE.injectDialog(),
FAADE.abuser_database = FAADE.getValue('abuser_database', !1),
FAADE.abuserDatabaseExpired() && FAADE.fetchAbuserDatabase(),
FAADE.main()
},
window.addEventListener('DOMContentLoaded', FAADE.init),
// Determines whether the abuser database has expired and needs to be re-fetched.
FAADE.abuserDatabaseExpired = function () {
// If we don't have a database, then of course it's "expired."
// If we don't have a database, then of course it's "expired."
// Abuser database was last fetched more than 24 hours (86400 seconds) ago, so refresh.
return FAADE.abuser_database ? (new Date).getTime() > parseInt(FAADE.getValue('last_fetch_time')) + 86400 ? (FAADE.log('Abuser database expired because of time.'), !0) : (FAADE.log('Abuser database still fresh.'), !1) : (FAADE.log('Abuser database expired because of false-equivalent value.'), !0)
},
FAADE.getDatabaseConnectionString = function () {
return FAADE.CONFIG.debug ? FAADE.CONFIG.gdocs_development_key : FAADE.CONFIG.gdocs_key
},
FAADE.getReportFormKey = function () {
return FAADE.CONFIG.debug ? FAADE.CONFIG.gform_development_key : FAADE.CONFIG.gform_key
},
FAADE.setValue = function (x, y) {
return FAADE.CONFIG.debug ? GM_setValue(x += '_development', y) : GM_setValue(x, y)
},
FAADE.getValue = function (x, y) {
return 1 === arguments.length ? FAADE.CONFIG.debug ? GM_getValue(x += '_development') : GM_getValue(x) : FAADE.CONFIG.debug ? GM_getValue(x += '_development', y) : GM_getValue(x, y)
},
FAADE.fetchAbuserDatabase = function () {
var key = FAADE.getDatabaseConnectionString(),
url = 'https://docs.google.com/spreadsheet/pub?key=' + key + '&output=html';
FAADE.log('fetching abusers database from ' + url),
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: function (response) {
if (!response.finalUrl.match(/^https://docs.google.com/spreadsheet/pub/)) return FAADE.log('Failed to fetch abuser database from ' + url),
!1;
FAADE.setValue('last_fetch_time', (new Date).getTime().toString()), // timestamp this fetch
FAADE.setValue('abuser_database', response.responseText),
FAADE.abuser_database = FAADE.getValue('abuser_database')
}
})
},
FAADE.injectDialog = function () {
// Inject hidden dialog box link.
var trigger_el = document.createElement('a');
trigger_el.setAttribute('class', 'opens-modal'),
trigger_el.setAttribute('data-opens-modal', 'faade'),
document.body.appendChild(trigger_el);
// Inject dialog box HTML. FetLife currently uses Rails 3, so mimic that.
// See, for instance, Rails Behaviors: http://josh.github.com/rails-behaviors/
var faade_dialog = document.createElement('div');
faade_dialog.setAttribute('style', 'display: none; position: absolute; overflow: hidden; z-index: 1000; outline: 0px none;'),
faade_dialog.setAttribute('class', 'ui-dialog ui-widget ui-widget-content ui-corner-all'),
faade_dialog.setAttribute('tabindex', '-1'),
faade_dialog.setAttribute('role', 'dialog'),
faade_dialog.setAttribute('aria-labelledby', 'ui-dialog-title-faade');
var html_string = '
html_string += 'FetLife Alleged Abusers Database Engine (FAADE)',
html_string += '',
html_string += 'close',
html_string += '',
html_string += '
html_string += '
html_string += '
There have been X new consent violations filed in FAADE that may have been perpetrated near your location (X, X, X).
',html_string += '
Click "View new nearby FAADE reports" to view the profiles of the people who have been accused of consent violations near your area in new tabs.
',html_string += '
',
',html_string += 'View new nearby FAADE reports',
html_string += ' -or- ',
html_string += 'Cancel',
html_string += '
html_string += '
(Don't worry, I'm not looking for where you actually are. Your location was determined from your FetLife profile.)
',html_string += '
faade_dialog.innerHTML = html_string,
document.body.appendChild(faade_dialog),
// Attach event listener to trigger element.
document.querySelector('[data-opens-modal="faade"]').addEventListener('click', function (e) {
document.querySelector('[data-opens-modal="faade"]').dialog('open')
})
},
FAADE.getLocationFromProfileHtml = function (html) {
return (new DOMParser).parseFromString(html, 'text/html').querySelector('h2.bottom + p > em').textContent.split(', ')
},
FAADE.broadcastNewProximalReports = function (doc) {
// Recall timestamp of last record checked.
var last_timestamp_checked = parseInt(FAADE.getValue('last_timestamp_checked', '0')),
rows = doc.querySelectorAll('#tblMain tr'),
latest_timestamp_filed = Date.parse(rows[rows.length - 1].childNodes[1].textContent);
// If never checked, or if there are new records since last timestamp checked
if (last_timestamp_checked < latest_timestamp_filed) {
FAADE.log('Last timestamp checked (' + last_timestamp_checked.toString() + ') is older than latest timestamp filed (' + latest_timestamp_filed.toString() + ').');
for (var num_reports = 0, i = rows.length - 1; i > 0 && Date.parse(rows[i].childNodes[1].textContent) > last_timestamp_checked; i--) num_reports++;
FAADE.log('Total of ' + num_reports + ' new reports since last check.');
var user_loc = FAADE.getLocationFromProfileHtml(FL_ASL.users[uw.FetLife.currentUser.id].profile_html);
FAADE.log('Current user location seems to be ' + user_loc.join(', ') + '.');
for (var reports_to_alert = [
], i = rows.length - num_reports; i <= rows.length - 1; i++) {
// extract the location data from the report
report_loc = rows[i].childNodes[6].textContent;
// compare current user's FetLife profile location against alleged abuse location
// and test each substring of the user profile against the reported location
for (var z = 0; z < user_loc.length; z++) // if a relevant case insensitive substring matches
if ( - 1 !== report_loc.toLowerCase().search(user_loc[z].toLowerCase())) {
FAADE.log('Filed report location ' + report_loc + ' matches user location substring ' + user_loc[z] + '!'),
// store for future pop-up
reports_to_alert.push(rows[i]);
break
}
} // Ask user to view the profiles of the alleged abusers in the user's local vicinity.
if (reports_to_alert.length) {
// Fill in the user-facing message with the appropriate info.
document.getElementById('faade_reports_to_alert').innerHTML = reports_to_alert.length.toString(),
document.getElementById('faade_user_loc').innerHTML = user_loc.join(', ');
// Create the click event we're going to use.
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', !0, !1), // can bubble, can't be cancelled
// "Click" event on hidden code.
document.querySelector('a[data-opens-modal="faade"]').dispatchEvent(evt),
// Attach event listener to "View" button and pass in appropriate URLs.
document.querySelector('.btnsqr[data-closes-modal="faade"]').addEventListener('click', function () {
for (var i = 0; i < reports_to_alert.length; i++) {
GM_openInTab('https://fetlife.com/users/' + reports_to_alert[i].childNodes[2].textContent.match(/\d+/) [0])
}
})
}
} // Make a note of the latest timestamp filed, so we start here next time we're loaded.
FAADE.setValue('last_timestamp_checked', latest_timestamp_filed.toString())
},
// This is the main() function, executed on page load.
FAADE.main = function () {
for (var parser = new DOMParser, doc = parser.parseFromString(FAADE.abuser_database, 'text/html'), els = doc.querySelectorAll('#tblMain td:nth-child(3)'), abuser_ids = [
], i = 1; i < els.length; i++) // we never need the first (0'th) cell because Google provides it blank.
abuser_ids.push(els[i].innerHTML); // give us a few seconds to grab the current user's FetLife profile HTML.
// Are we on a user profile page?
if (FAADE.log('recalled abuser ids ' + abuser_ids), // TODO: Refactor this, it's kludgy.
setTimeout(function () {
FAADE.log('Running time-delayed function.'),
FL_ASL.users[uw.FetLife.currentUser.id].profile_html && (FAADE.log('We have the current user's FetLife profile HTML. Running broadcast checks.'), FAADE.broadcastNewProximalReports(doc))
}, 5000), window.location.href.match(/users/(\d+)/?$/)) {
var profile_nick = document.querySelector('h2.bottom').childNodes[0].textContent.match(/\S+/) [0],
id_in_url = window.location.href.match(/users/(\d+)/?$/) [1];
// If this is a profile page of an alleged abuser,
if ( // If we're not viewing our own profile page, insert a report link.
usr_ops = document.querySelector('#main_content p.quiet'), usr_ops && (usr_ops.appendChild(document.createElement('br')), usr_ops.appendChild(FAADE.createAbuseReportLink(id_in_url, profile_nick))), - 1 !== abuser_ids.indexOf(id_in_url)) {
var report_el = document.createElement('table');
report_el.setAttribute('id', 'faade_abuse_reports'),
report_el.setAttribute('summary', 'Reported consent violations committed by ' + profile_nick + '.');
var caption = document.createElement('caption');
caption.innerHTML = 'There are reports ' + profile_nick + ' violated others' consent in these ways:';
var tfoot = document.createElement('tfoot');
tfoot.innerHTML = '',
tfoot.querySelector('td').appendChild(FAADE.createAbuseReportLink(id_in_url, profile_nick)),
report_el.appendChild(caption),
report_el.appendChild(tfoot);
for (var abuse_reports = [
], ix = 0; ix < els.length; ix++) id_in_url === els[ix].innerHTML && abuse_reports.push(els[ix].parentNode);
// Add this information to the top of this user's profile
for (var iy = 0; iy < abuse_reports.length; iy++) {
var num = iy + 1,
tr = document.createElement('tr');
tr.setAttribute('id', 'faade_abuse_report-' + num.toString());
var details_html = '
- ' + abuse_reports[iy].childNodes[7].innerHTML + '
';- ' + abuse_reports[iy].childNodes[6].innerHTML + '
';details_html += '
var permalink_html = '' + abuse_reports[iy].childNodes[1].innerHTML + '';
tr.innerHTML += 'Abuse report ' + num.toString() + ' (' + permalink_html + '):' + details_html + '',
tr.innerHTML += '' + abuse_reports[iy].childNodes[5].innerHTML + '',
report_el.appendChild(tr)
}
var before = document.querySelector('#main_content table:last-child');
before.parentNode.insertBefore(report_el, before)
}
} // Collect all user links on this page.
var user_links = [
];
for (i = 0; i < document.links.length; i++) {
var l = document.links[i].href.match(/^(https://fetlife.com)?/users/(\d+)/?$/);
l && l[2] !== uw.FetLife.currentUser.id.toString() && user_links.push(document.links[i])
} // For each user ID found,
var last_id = null;
for (i = 0; i < user_links.length; i++) {
// Collect its user ID number.
var id = user_links[i].href.match(/\d+/?$/);
id && (id = id.toString()); // cast to string for later comparison
// Get nickname.
var n;
// Don't create another link if we just made one for that user.
if ( // This is an avatar link, not a text link.
n = user_links[i].children.length ? user_links[i].childNodes[0].alt : user_links[i].innerHTML, // check the alleged abusers data store for a match.
- 1 !== abuser_ids.indexOf(id) && (FAADE.log('found match on this page for alleged abuser ID number ' + id), // highlight the user's links that matched an alleged abuser using CSS
user_links[i].setAttribute('class', user_links[i].className + ' faade_alleged_abuser')), id !== last_id) {
// Offer a link to add another report for this user.
// See also: https://support.google.com/docs/bin/answer.py?hl=en&answer=160000
// Add link to report this user for a consent violation.
var a = FAADE.createAbuseReportLink(id, n);
user_links[i].parentNode.appendChild(a),
last_id = id
}
}
},
FAADE.createAbuseReportLink = function (id, nick) {
var a = document.createElement('a');
a.setAttribute('class', 'faade_report_link'),
a.setAttribute('target', '_blank');
var href = 'https://docs.google.com/spreadsheet/viewform?formkey=' + FAADE.getReportFormKey();
return href += '&entry_0=' + id,
href += '&entry_1=' + nick,
a.setAttribute('href', href),
a.innerHTML = '(report a consent violation by ' + nick + ')',
a
},
// The following is required for Chrome compatibility, as we need "text/html" parsing.
/*
/
/! @source https://gist.github.com/1129031 /
/global document, DOMParser/
function (DOMParser) {
'use strict';
var DOMParser_proto = DOMParser.prototype,
real_parseFromString = DOMParser_proto.parseFromString;
// Firefox/Opera/IE throw errors on unsupported types
try {
// WebKit returns null on unsupported types
if ((new DOMParser).parseFromString('', 'text/html')) // text/html parsing is natively supported
return
} catch (ex) {
}
DOMParser_proto.parseFromString = function (markup, type) {
if (/^\stext/html\s*(?:;|$)/i.test(type)) {
var doc = document.implementation.createHTMLDocument('');
return doc.body.innerHTML = markup,
doc
}
return real_parseFromString.apply(this, arguments)
}
}(DOMParser);
The text was updated successfully, but these errors were encountered: