†
Created on .
๐ธ๐ธFor You
A variety of fun/helpful things I've made. :-)
Code
Neocities edit current page button
Sunset Archive More by Same Author
Anniversary Widget on WidgetStar
Graphics
Neocities Edit Current Page Button
An edit page button for your Neocities site. Embed this on any neocities site to create a link to edit the corresponding page in the Neocities text editor. Code:
function url_replace() {
const path = window.location.pathname.replace(/\//g, '') ;
let html = ".html"
const base_url = 'https://neocities.org/site_files/text_editor?filename=' + path + html;
document.querySelector('.editlink').innerHTML =
`<a href="${base_url}">Edit Page</a>`;
}
url_replace();
</script></div>
Sunset Archive Tampermonkey Scripts
Shake Gift
Original code by nonethefewer. Edited for Sunset by me.
// @name Sunset Archive: Shake Gift
// @author nonethefewer original; agnes the alien
// @match *://sunset.femslash.club/users/*/gifts
// @version 1.1.3.0
// @grant none
// @description Shake gifts on Sunset Archive
// @license MIT
// @icon https://sunset.femslash.club/images/ao3_logos/logo_42.png
// @downloadURL for the original https://update.greasyfork.org/scripts/475546/AO3%3A%20Shake%20Gift.user.js
// @updateURL for the original https://update.greasyfork.org/scripts/475546/AO3%3A%20Shake%20Gift.meta.js
// ==/UserScript==
//
// For use in Tampermonkey. Install and select the plus button from the dashboard, or create new script from the dropdown, paste this in.
(function($) {
$(document).ready(function() {
if ( !$('body').hasClass('logged-in') ) { return; }
var $base_url = "https://sunset.femslash.club/collections/$COLLECTION$/works?commit=Sort+and+Filter&work_search%5Bquery%5D=id%3A$ID$";
var $main = $( '#main' );
var $thurl = "";
$('.index.group li.work', $main).each( function() {
var work_id = this.id.replace('work_', '');
var collection = "";
if ( work_id !== '' ) {
var $dove = $('div.mystery', this);
if ($dove.length > 0)
{
collection = $('h5 a', this)[0].getAttribute("href").split("/")[2];
$thurl = $base_url.replace("$COLLECTION$", collection).replace("$ID$", work_id);
$('.actions', this).append('<a style="right: 0; position: absolute; margin-right: 10px;" href="' + $thurl + '">Shake Gift</a>');
}
}
});
});
})(window.jQuery);
More By Same Author
Adds extra links to the tags section at the top of an AO3 work page, redirecting user to any use of same tags by the same author. Original code by rallamajoop.
// @name sunset author+tags quick-search
// @version 1.2
// @include https://sunset.femslash.club/works*
// @include https://sunset.femslash.club/chapters*
// @description Generates quick links from Sunset fics to more by the same author in the same fandom (or character/pairing/any other tag). Original code by rallamajoop
// @namespace rallamajoop
// @downloadURL https://update.greasyfork.org/scripts/393481/AO3%20author%2Btags%20quick-search.user.js
// @updateURL https://update.greasyfork.org/scripts/393481/AO3%20author%2Btags%20quick-search.meta.js
// ==/UserScript==
/* Adds extra links to the tags section at the top of an AO3 work page, redirecting user to any use of same tags by the same author.
* ie. a quick way to find out if the writer of the great fic you just read has written anything else for the same fandom/pairing/trope/etc.
* If printCounts is set to 'true', code will also calculate and display the number of uses of each tag in the fandom/relationship/character sections.
* This will also make the links take longer to load, as code must pre-load each link individually. To limit load, I have not implemented this for freeform
* or other tags.
* Author-based search links are a bit of a hack, but most cases should now select correctly in the tags menu on the right. I've added a subheading identifing the tag to cover all bases.
*/
//Setting variables! Change these if you like
var printCounts = true; // setting this to false will prevent script for printing tag counts, which may decrease loading times and server load
var quickSearchText = "*"; // text for new links - change this if you want something other than an *. You can put <sup>TEXT</sup> to make it superscript
var autoSortResultsBy = ""; // valid options are "Kudos", "Word Count", "Date" or "" (none, usually defaults to date)
var importantTags = ["fandom","relationship","character"]; // these are the tags which the script prints counts for
//Proper code starts here
var loc = location.href;
var href="https://sunset.femslash.club/works?utf8=%E2%9C%93&commit=Sort+and+Filter&work_search"; //[relationship_names]=";
var workPage=["https://sunset.femslash.club/works/", "https:/sunset.femslash.club/chapters/"];
var scriptTag = "&greasemonkey"; //tag to flag that we've clicked an author quicktag link
var sortOpts = {"Kudos": "&work_search[sort_column]=kudos_count", "Word Count": "&work_search[sort_column]=word_count", "Date" : "&work_search[sort_column]=revised_at" };
if (isWorkPage(loc)) {
// Story page, add extra links user-based search to each tag
var h3=document.body.getElementsByClassName("byline heading");
var authors=h3.item(0).getElementsByTagName('a');
authors=parseAuthors(authors);
var allTags=document.body.getElementsByClassName("work meta group").item(0);
var dds = allTags.getElementsByTagName('dd');
var links = dds.item(1).getElementsByTagName('a');
for (var j=0; j<dds.length; j++) {
var tags = dds.item(j);
var tagType = getTagType(tags);
if (!tagType) break;
links = tags.getElementsByTagName('a');
for (var i=links.length-1; i>=0; i--) {
for (var a=0; a<authors.length; a++) {
var author=authors[a];
var tag = getTag(links.item(i).href);
var newHref = buildSearch(tag, author, tagType);
var isCreatedLink = tag.includes(links.item(i).href);
var existing = document.querySelectorAll('[href="' + newHref + '"]');
if (!isCreatedLink && existing.length==0) { //check that we aren't creating a duplicate link - can happen when page partially loads from cache
var text = quickSearchText;
var newlink=document.createElement('span');
if (printCounts && printCountFor(tags) && !ignoreAuthor(author)) {
//If this is an important tag, count search results
var count = getNumFics(newHref);
if (count>1) {
text = "<b><sup>(" + count.toString() + ")<sup></b>";
}
else {
text = "<sup>(1)<sup></b>";
}
}
var toolTip = "'" + unformatTag(tag) + "' fic by " + author;
newlink.innerHTML = " <a href=\"" + newHref + "\" title=\"" + toolTip + "\">" + text + "</a>";
links.item(i).parentNode.appendChild(newlink);
}
}
}
}
}
else if (loc.includes(scriptTag)) {
// Quicktags search results page, add title to show which tag is in use
//var sTag = getBetween("work_search[relationship_names]=", loc, "&user_id=");
let sTag, tagType;
[tagType, sTag] = parseLoc(loc);
var main = document.getElementById("main");
var heading = main.getElementsByClassName("heading").item(0);
sTag = unformatTag(sTag);
var subTitle = "<br> Author QuickSearch: " + sTag;
var span=document.createElement('h3');
span.innerHTML = subTitle;
// const sortBys = sortLinks(loc);
// heading.parentNode.insertBefore(sortBys, heading.nextSibling); //not really necessary as of v1.2
heading.parentNode.insertBefore(span, heading.nextSibling);
checkTagBox(tagType, sTag);
}
//don't bother counting fics by orphan_account - these won't all be by same author anyway
function ignoreAuthor(author) {
if (author == "orphan_account") return true;
return false;
}
function getTagType(element) {
var name = element.className;
if (!name.includes(" tags")) return false;
name = name.replace(" tags", "");
return name;
}
//True if work or chapter
function isWorkPage(href) {
for (var i=0; i<workPage.length; i++) {
if (href.includes(workPage[i])) return true;
}
return false;
}
//Make sure tag is selected on the right menu
function checkTagBox(tagType, tag) {
if (tagType == "warning") tagType = "archive_warning";
debugger
/* let btn = document.getElementById("toggle_include_" + tagType + "_tags");
btn = btn.getElementsByTagName("button");
btn = btn[0];
if (btn.getAttribute("aria-expanded") == "false") {
btn.click();
}*/
const par = document.getElementById("include_" + tagType + "_tags");
const entries = par.getElementsByTagName("label");
for (let i=0; i<entries.length; i++) {
let entry = entries[i];
if (entry.textContent.includes(tag)) {
entry.getElementsByTagName("input")[0].checked=true;
return;
}
}
}
//Generate links to sort by kudos etc
function sortLinks(urlStr) {
var sortBys=document.createElement('div');
let baseUrl = urlStr.replace(scriptTag, "");
let keys = Object.keys(sortOpts);
let inc = [true, true, true];
for (var i =0; i<keys.length; i++) {
let opt = sortOpts[keys[i]];
if (baseUrl.includes(opt)) {
baseUrl = baseUrl.replace(opt, "");
inc[i] = false;
}
}
for (var i =0; i < keys.length; i++) {
if(inc[i]) {
let link=document.createElement('a');
let key=keys[i];
link.href=baseUrl + sortOpts[key] + scriptTag;
link.innerHTML = "Sort by " + key;
sortBys.append("\u2003");
sortBys.appendChild(link);
}
}
return sortBys;
}
//Author+tag search address
function buildSearch(tag, author, tagType="relationship"){
var href="https://sunset.femslash.club/works?utf8=%E2%9C%93&commit=Sort+and+Filter&work_search[" + tagType + "_names]="
href+=tag + "&user_id=" + author;
const sortBy = sortOpts[autoSortResultsBy];
if (sortBy != undefined) href+= sortBy;
href+=scriptTag;
return href;
}
//Limit tag counts to fandom, relationship and character tags
function printCountFor(element) {
var name = getTagType(element);
for (var i=0; i<importantTags.length; i++) {
if (importantTags[i] == name) return true;
}
return false;
}
//Retrieve page as javascript object
function getSourceAsDOM(url)
{
var xmlhttp=new XMLHttpRequest();
xmlhttp.open("GET",url,false);
xmlhttp.send();
var parser=new DOMParser();
return parser.parseFromString(xmlhttp.responseText,"text/html");
}
//Counts results of author+tag search
function getNumFics(url) {
var results = getSourceAsDOM(url);
var works = results.getElementsByClassName("work index group")[0];
if (works.children.length < 20) {
return works.children.length;
}
var h=results.getElementsByTagName("h2");
if (h.length>0) {
h=h[0];
var text = h.textContent;
if (text.includes(" of ")) {
text = getBetween(" of ", text, " Works");
return text; //could convert this into an integer, but not much point
}
}
return 20;
}
//May be multiple authors, may have pseudonyms. Returns as array
function parseAuthors(links) {
var authors = [""];
for (var i=0; i<links.length; i++) {
var text = links.item(i).toString();
authors[i] = getBetween("/users/", text, "/pseuds");
}
return authors;
}
//Retrieve tag and tag type from search string
function parseLoc(href) {
const re=/.+&work_search\[(.+)_names\]=(.+)&user_id=.+/;
const res = re.exec(href);
if (res.length == 3) {
return res.slice(1);
}
return ["",""];
}
//Format tag to pass to search
function getTag(href){
var tag = getBetween("/tags/", href, "/works");
tag = replaceAll(tag, "*s*","%2F");
tag = replaceAll(tag, "*d*",".");
tag = replaceAll(tag, "%20","+");
return tag;
}
//Format tag from href for display in title
function unformatTag(tag) {
tag = decodeURIComponent(tag);
tag = replaceAll(tag, "*s*","/");
tag = replaceAll(tag, "*a*","&");
tag = replaceAll(tag, "*d*",".");
tag = replaceAll(tag, "+", " ");
return tag;
}
function getBetween(tag1, str, tag2) {
var ret = str.split(tag1);
if (ret.length<2) return "";
ret=ret[1];
ret = ret.split(tag2);
if (ret.length<1) return "";
ret = ret[0];
return ret;
}
function replaceAll(str, search, replacement) {
return str.split(search).join(replacement);
}
Anniversary WidgetStar Widget
Requires WidgetStar account.. Stupid simple.
Graphics
Buttons
Fandom Icon Archive
Click here for an archive of every 100x100px fandom/stock icon I've made.
.gif)





