- // ==UserScript==
- // @name F-List Kinks Calculator
- // @namespace flistkinkscalculator
- // @description Generates an estimated compatibility score when comparing character kinks.
- // @version 1.1.2
- // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAABh0RVh0U29mdHdhcmUAQWRvYmUgRmlyZXdvcmtzT7MfTgAAA4dJREFUWIXFl19oHFUUxn/nbswsbRBDS2jzog9BH2JdhFokVJqA0AcfpI2CoKAvYqlUaqFQUGnBCi2KihYJNCp5FamCCg0V3aBg81LQog/+oVk27UOyNG0C7Y7ZuceHZGbu7E5mdtMUD/twvjsz53733O+cu1dUlf/TugBEBABVPQicCgJbcF+y1obPSSMcfu/6xpjEO43AXvG6u14FZkSkEsYRVUVEUNUh4J2rc4veC0c+fLx5knjetIxJEjlQVbHWsnVLr3/uzOGLwBTwOVCJCAB7gBOjx74YtkEDMYkEbIiJCDZo8PDAdt5+ZaQMjAAk8jRf+Z3bS9c3fHKAxr91bi9dZ6pcTox3tbypirt9J18s4ft+xxN6nsebE7/GAwWT+l4rAWneURgYGOiYQLVaTcQxq8KwNlibgLUBgmBMM4X1mRtntZDQLALhQ+PI+NMLs8Ds+gi45RCWesgkjYC1AUiS+bX5G+uaHJJxNOo1ORkQkk1k7OjeyD/w7mTHODRrQg1kZEBtgIhp6WLxisy6cZiLTA1YGyCS3LtqtRoHFOkYx2Ta0ED4sODU7Hvn/or8QsF0jENrRCLM00BTBj54bSTyX//ox1x8ZF/cM97/6u/IN5EI86oASZaPY83jneDQzWxEcRWsoQEjuThBwMGmnS0I+4B7vn/8zRVnFZKLXUv7n5AjwtYMnDrwROQfG/spF7vmxhHTpgjvlgbCerB5IhS5Mw1MT087eHvkS7tl2Mz87GSyseTh3270OziO3XYj6unp2bDj2DXP8yjc42VnAGC2OsMfl5Jiiv8J68qvaR9FzGqzl5ZKCG3zfX30PbAjuxEBmKUKmybH2Qz0eR43fR8feH5iAmMMjUYjdQKRtSc3xqyegnU23RzMJvDD12d5VsYpeR67tm1jslIBYHR0NDU4rByxzcdsmtXrdS788mc2Ad/3eRA4NDhIwRge6+2lfPnymqsLv6nVarkElpeXW8YSBB7d8RAvHTrOy8Ui1lpUlYXFRRSYm5vLDF4sFiNfNXk5Aeju7qa/v5/h3f+kEpgBFk8fP8wbJ8+gqswvLPDJk89w8Psvsdby1unx3BXm2fDunTy3by+s3I6A+GY0BHwG3A9c/E5kDyBPqZar58/fe2n//keevnXr5ztmsGJTInIiqqy0y+a3YGulkgKMwdB8qZSvsA4tuuimEaiVSkHKN3eFwH8EALqp+4E4kAAAAABJRU5ErkJggg==
- // @require https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js
- // @match *://www.f-list.net/c/*
- // @grant unsafeWindow
- // @grant GM.getValue
- // @grant GM.setValue
- // ==/UserScript==
-
- /* globals $ exportFunction */
-
- var FList = unsafeWindow.FList;
-
- // Descriptions for your scores.
- var ratingNames = [
- {score: -1.00, confidence: 0.00, desc: "Uncertain"},
- {score: -1.00, confidence: 0.20, desc: "Are you fucking kidding me?"},
- {score: -0.30, confidence: 0.15, desc: "Hell No!"},
- {score: -0.15, confidence: 0.15, desc: "Nope"},
- {score: 0.00, confidence: 0.15, desc: "Terrible"},
- {score: 0.20, confidence: 0.20, desc: "Poor"},
- {score: 0.40, confidence: 0.20, desc: "Average"},
- {score: 0.60, confidence: 0.30, desc: "Good"},
- {score: 0.75, confidence: 0.40, desc: "Very Good"},
- {score: 0.85, confidence: 0.50, desc: "Excellent!"}
- ];
-
- // The different scoring matrices available for selection.
- // 1st dimension is your character's kink. 2nd dimension is the other character.
- var scoringProfiles = [
- {
- name: "Unbiased",
- matrix: {
- fave: {fave: 4, yes: 3, maybe: 1, no:-2},
- yes: {fave: 3, yes: 3, maybe: 1, no:-1},
- maybe: {fave: 1, yes: 1, maybe: 1, no: 0},
- no: {fave:-2, yes:-1, maybe: 0, no: 0}
- }
- }, {
- name: "Self-Biased",
- matrix: {
- fave: {fave: 4, yes: 3, maybe: 2, no:-4},
- yes: {fave: 3, yes: 3, maybe: 1, no:-2},
- maybe: {fave: 1, yes: 1, maybe: 1, no: 0},
- no: {fave:-1, yes:-1, maybe: 0, no: 0}
- }
- }
- ];
-
- // Giving/Receiving kink pairs
- var asymmetricalKinks = {
- 16 : 163, 163 : 16, // Rimming
- 157 : 137, 137 : 157, // Anal
- 158 : 141, 141 : 158, // Oral
- 340 : 229, 229 : 340, // Vaginal
- 514 : 512, 512 : 514, // Fellatio
- 515 : 513, 513 : 515, // Cunnilingus
- 422 : 423, 423 : 422 // Vore
- };
-
- // Class names for fave, yes, maybe, no
- var quickCompareClasses = [
- 'Character_QuickCompareNo',
- 'Character_QuickCompareMaybe',
- 'Character_QuickCompareYes',
- 'Character_QuickCompareFave'
- ];
-
-
- var statusText = $('<span id="KinkCalculatorStatus" style="padding-left:0.5em">');
- var scoringProfileSelector = $('<select id="KinkCalculatorProfileSelector">').width('auto');
-
- for (var p in scoringProfiles) {
- scoringProfileSelector.append($('<option>').val(p).append(scoringProfiles[p].name));
- }
-
- // Insert our own controls.
- $('#Character_QuickCompareClear').after(statusText);
- $('#Character_QuickCompareSelect').width('auto').after(scoringProfileSelector).after(" Scoring profile: ");
-
- // Insert our own function into the default Quick Compare function.
- var oldCompare = FList.Character_quickCompare;
-
- var newCompare = function(clear) {
- console.log("Running a comparison...");
- oldCompare(clear);
- kinkCalculator(clear);
- };
-
- exportFunction(newCompare, FList, {defineAs: "Character_quickCompare"});
-
-
- function kinkCalculator(clear) {
- statusText.empty();
-
- $('.KinkCalculatorBreakdown, .KinkCalculatorAsym').remove();
-
- if (clear) return;
-
- statusText.text("Getting character kinks...");
-
- var char1 = {
- id: $('#Character_QuickCompareSelect').val(),
- name: $('#Character_QuickCompareSelect').find(":selected").text()
- };
- var char2 = {
- id: $('#profile-character-id').val(),
- name: $('#profile-character-name').val()
- };
- var profile = scoringProfileSelector.val();
-
- var dataSource = '/json/getFetishes.php';
- $.getJSON(dataSource, {character_id: char1.id, id_only: true}, function (a) {
- $.getJSON(dataSource, {character_id: char2.id, id_only: true}, function (b) {
- char1.kinks = a;
- char2.kinks = b;
- showScore(char1, char2, profile);
- });
- });
- }
-
-
- function calculateKinks(kinks1, kinks2, matrix) {
- var total = 0;
- var gross = 0;
- var positive = 0;
- var negative = 0;
- var neutral = 0;
- var misses = 0;
-
- for (var fid1 in kinks1) {
- var fid2 = asymmetricalKinks[fid1] ? asymmetricalKinks[fid1] : fid1;
-
- if (kinks2[fid2]) {
- var v = matrix[kinks1[fid1]][kinks2[fid2]];
- total += v;
- gross += Math.abs(v);
-
- if (v > 0) positive++;
- else if (v < 0) negative++;
- else neutral++;
-
- var fItem1 = $('#Character_ListedFetish' + fid1);
- var fItem2 = $('#Character_ListedFetish' + fid2);
-
- if (fid1 !== fid2) {
- // Remap the highlighted kink element
- fItem2.addClass(fItem1.attr('class'))
- .attr('title', 'Kinks Calculator re-mapped to this kink using its giving/receiving rules')
- .append(' <b class="KinkCalculatorAsym">⇋</b>');
-
- if (!fItem1.data('KinkCalculatorValue')) {
- fItem1.removeClass(quickCompareClasses.join(' ') + ' Character_QuickCompareActive');
- }
- }
-
- fItem2.data('KinkCalculatorValue', v)
- .append(' <b class="KinkCalculatorBreakdown">' + (v < 0 ? '' : '+') + v + '</b>');
- }
- else {
- misses++;
- }
- }
-
- var score = total / gross;
- var certainty = (3*positive + 2*negative + neutral) / (3*positive + 2*negative + neutral + misses);
-
- var rating = getRating(score, certainty);
-
- return {
- total: total,
- gross: gross,
- positive: positive,
- negative: negative,
- neutral: neutral,
- misses: misses,
- score: score,
- rating: rating,
- certainty: certainty
- };
- }
-
- function getRating(score, confidence) {
- var rating = "?";
-
- for (var r in ratingNames) {
- if (score >= ratingNames[r].score && confidence >= ratingNames[r].confidence) {
- rating = ratingNames[r].desc;
- }
- }
-
- return rating;
- }
-
- function showScore(char1, char2, profile) {
- statusText.text("Scoring...");
-
- var result = calculateKinks(jsonKinksToMap(char1.kinks), jsonKinksToMap(char2.kinks), scoringProfiles[profile].matrix);
-
- var altColorMode = GM.getValue('altColorMode', false);
-
- var breakdownDetails =
- "Compare " + char1.name + " (" + char1.id + ") and " + char2.name+ " (" + char2.id + ")\n\n" +
- "Hits: " + result.positive + " positive, " + result.negative + " negative, " + result.neutral + " neutral\n" +
- "Misses: " + result.misses + "\n" +
- "Points (sum of all hit values): " + result.total + "\n" +
- "Gross (sum of absolute hit values): " + result.gross + "\n" +
- "Score (points / gross): " + toPercent(result.score, 1) + "\n" +
- "Confidence (based on hits / hits + misses): " + toPercent(result.certainty, 1);
-
- statusText.empty().append(
- '<b>Score:</b> ',
- $('<a href="#">' + toPercent(result.score, 0) + '</a>').click(function(){alert(breakdownDetails); return false;}),
- ' ',
- '<b>Confidence:</b> ' + toPercent(result.certainty, 0) + ' ',
- '<b>Rating:</b> ' + result.rating + ' ',
- $('<label for="KinkCalculatorAltColorMode"> Color based on points</label>')
- .prepend($('<input type="checkbox" id="KinkCalculatorAltColorMode"/>')
- .attr('checked', altColorMode ? 'checked' : null)
- .change(toggleQuickCompareColors))
- );
-
- toggleQuickCompareColors();
- }
-
-
- function toggleQuickCompareColors() {
- var altColorMode = $('#KinkCalculatorAltColorMode').is(':checked');
- GM.setValue('altColorMode', altColorMode);
-
- var classesJoined = quickCompareClasses.join(' ');
-
- $('.Character_QuickCompareActive').each(function(idx, el) {
- var e = $(el);
-
- if (altColorMode) {
- e.data('OldClasses', e.attr('class'));
- e.removeClass(classesJoined);
- var v = parseInt(e.data('KinkCalculatorValue'));
- var c = v > 3 ? 3 : v > 0 ? 2 : v < 0 ? 0 : 1;
- e.addClass(quickCompareClasses[c]);
- }
- else {
- e.attr('class', e.data('OldClasses'));
- }
- });
- }
-
-
- function toPercent(x, dp) {
- return isNaN(x) ? "?" : (x * 100).toFixed(dp) + "%";
- }
-
-
- function jsonKinksToMap(json) {
- var map = {};
- $.each(json.fetishes, function (yif, f) {map[f.fid] = f.choice;});
- return map;
- }