How to sort user stories by date (->solution inside)

lgr

Virgin
Joined
Sep 30, 2006
Posts
8
Hi,

I've searched a bit in the forums to see if this has already been posted and I couldn't find anything. If I've missed it, sorry to add to the noise...

But anyway, I've always preferred to read sequential stories in order, and most of the time authors make it easy by adding numbers to the end of their titles (thereby making sure they are sorted correctly, because the member story page sorts by alphabet on the title). However, sometimes stories are sequels to other stories with different names and then the order is shot. As I'm not an American the format for the date makes it extra irritating to try to order the stories in my head.

Finally having had enough of all this, I decided to fix the problem myself. And as I've got it working and I though there might be more people that like their stories ordered like I do, I've decided to post the solution here.

It only works if you have firefox and greasemonkey (an add-on to firefox) installed, as the solution is to 'fix' the page with a greasemonkey script.

The script is included in this post below, all you need to do is save the text of it somewhere as a file named 'literotica.user.js' (or something similar, the end part '.user.js' is necessary for greasemonkey to recognize it as a script it can install) and then install it as a greasemonkey script (and enable it).

The easiest way to install it is to navigate to the saved script file with firefox itself: type in 'file:///' in your address bar and it will start browsing on your computer harddisk, then just navigate to the file and click it. If greasemonkey is enabled it will come up with a pop-up to ask you if you want to install the script. Click the appropriate button and your set...

Now, if you go to the story page of an author it will reorder the stories by date. Voila!

CAUTION: this script works for me, but I have not done intensive testing of it, so it could not work for you, or even mess up your firefox. I take no responsibility for any problems you might have using this script. You can read the code before installing it, so I suggest you do so before trusting it.

But to be honest this script should just work. If not, I'd really like to know. And if it does, I'd also appreciate feedback! ;)

update 1: extended it to add table headers for sorting by title, category or date
update 2: fixed a bug (multiple pages open screwed it up)

Code:
// Literotica - sort user stories by date
// version 0.3
// 2009-0516
//
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @include       http://*.literotica.com*/stories/memberpage.php?uid=*&page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';    
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
  var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  return first_bb_a.parentNode.parentNode.parentNode;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;
  while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + String( i );
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[@class='bb']", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + String( i );
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[@class='intext']", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + String( i );
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 1000) {
      // If we pass 1000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (var i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();

  for (var i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (var i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
Last edited:
Updated version of script

Updated version below. (update: not below, but above in the original post)

Changed it so it adds a table header. Clicking the link in that header (title, category or date) sorts by that column...
 
Last edited:
Hopefully some other Firefox users will test it out and let us know how well it works for them.
Then I'll add it to the FAQ thread.
 
A better way would of course be if the site supported this itself.. but I have no idea where I would go with such a feature request. On the other hand, I find it hard to believe I'm the only one that's ever wanted this feature...
 
You might try PMing Manu with a nice request for the sort by date feature to be added, if/when he gets a chance.
 
Woot, it's been almost 5 years since the script was used. For the most part it works, except that it seems to eat the first story in the list.

I tried it using TheHumpman and MiandEv2007's submission pages.
 
Code:
// Literotica - sort user stories by date
// version 0.4
// 2017-0212
//
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @include       http*://*.literotica.com*/stories/memberpage.php?uid=*&page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';    
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
  var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  return first_bb_a.parentNode.parentNode.parentNode;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;
  while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + String( i );
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + String( i );
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + String( i );
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 1000) {
      // If we pass 1000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (var i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();

  for (var i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (var i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
much easier way

Literotica has already provided an easier way than the OP's method.

On the main Site Contents page, scroll down near the bottom to the link for SEARCH STORIES.

On the search page, choose ADVANCED SEARCH.

Copy and paste the author's name into the appropriate box, select sort by date and ascending order and push the search button.

Voila - the list magically appears in date order.
 
Literotica has already provided an easier way than the OP's method.

On the main Site Contents page, scroll down near the bottom to the link for SEARCH STORIES.

On the search page, choose ADVANCED SEARCH.

Copy and paste the author's name into the appropriate box, select sort by date and ascending order and push the search button.

Voila - the list magically appears in date order.
I guess that depends on your definition of 'easier'. I usually end up on an authors list of stories by clicking through on a story I liked. Your method means I would have to do 7 things before I get a date sorted list of the page I was already on. My script allows me to click once on that page and get it sorted.
 
I guess that depends on your definition of 'easier'. I usually end up on an authors list of stories by clicking through on a story I liked. Your method means I would have to do 7 things before I get a date sorted list of the page I was already on. My script allows me to click once on that page and get it sorted.

But I don't have to modify my browser or load a script to get the sorted list using something that already works.
 
I came here via a google search on "literotica sort stories by date".

I used beingniceblows's version with the Tampermonkey browser extension on Firefox, but it had a couple of syntax errors regarding redefinition of variables. Fixed that, and it works great.

I prefer Firefox because I don't trust Google or Microsoft, but Tampermonkey supposedly also works on Chrome, Edge, and Safari.

Code:
// Literotica - sort user stories by date
// version 0.5
// 2017-0212
//
// --------------------------------------------------------------------
//
// This is a Greasemonkey user script.
//
// To install, you need Greasemonkey: http://greasemonkey.mozdev.org/
// Then restart Firefox and revisit this script.
// Under Tools, there will be a new menu item to "Install User Script".
// Accept the default configuration and install.
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @include       http*://*.literotica.com*/stories/memberpage.php?uid=*&page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
  var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
  return first_bb_a.parentNode.parentNode.parentNode;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;
  while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + String( i );
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + String( i );
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + String( i );
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 1000) {
      // If we pass 1000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();
    var i;

  for (i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
Bugfix. It wouldn't work on pages that did not have multi-chapter stories.

Code:
// Literotica - sort user stories by date
// version 0.6
// 2017-0212
//
// --------------------------------------------------------------------
//
// This is a Tampermonkey user script.
//
// To install, you need Tampermonkey https://www.tampermonkey.net
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @match       https://www.literotica.com/stories/memberpage.php?*page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
    var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    if (first_bb_a == null) {
        first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='t-t84 bb nobck']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    }
    var result = first_bb_a.parentNode.parentNode.parentNode;
  return first_bb_a.parentNode.parentNode.parentNode;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;
  while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + String( i );
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + String( i );
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + String( i );
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 1000) {
      // If we pass 1000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();
    var i;

  for (i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
Another bugfix. There was a limit of 1000 stories. Increased to 5000.

Code:
// Literotica - sort user stories by date
// version 0.7
// 2017-0212
//
// --------------------------------------------------------------------
//
// This is a Tampermonkey user script.
//
// To install, you need Tampermonkey https://www.tampermonkey.net
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @match       https://www.literotica.com/stories/memberpage.php?*page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
    var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    //alert('1 - ' + first_bb_a);
    if (first_bb_a == null) {
        first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='t-t84 bb nobck']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        //alert('2 - ' + first_bb_a);
    }
    var result = first_bb_a.parentNode.parentNode.parentNode;
    //alert(result);
  return result;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;

    while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + String( i );
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + String( i );
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + String( i );
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 5000) {
      // If we pass 5000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();
    var i;

  for (i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
Fix sorting by category to sort by date within category.

Fix sorting by title to work with a mix of standalone and multi-chapter.

Code:
// Literotica - sort user stories by date
// version 0.9
// 2017-0212
//
// --------------------------------------------------------------------
//
// This is a Tampermonkey user script.
//
// To install, you need Tampermonkey https://www.tampermonkey.net
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @match       https://www.literotica.com/stories/memberpage.php?*page=submissions
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
    var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    //alert('1 - ' + first_bb_a);
    if (first_bb_a == null) {
        first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='t-t84 bb nobck']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        //alert('2 - ' + first_bb_a);
    }
    var result = first_bb_a.parentNode.parentNode.parentNode;
    //alert(result);
  return result;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;

    while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      var lineKey = String( i ).padStart(5, '0');
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + lineKey;
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + ' ' + lineKey;
        index = String(title).toLowerCase();
        index = index.replace( /\s/gi, '_' );
        index = index.replace('<span>', '');
        index = index.replace('</span>', '');
        index = index.replace('<!--_//_-->', '');
        //alert('"' + index + '"');

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + ' ' + date + ' ' + lineKey;
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 5000) {
      // If we pass 5000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();
    var i;

  for (i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
Thanks works like a charm.. like the other mentioned, we always navigate to the user submissions and its now easier to see the stories sorted by date...
thanks for sharing
 
Now ignores leading "the ", "a ", "an ", and quotes on titles when sorting.

Code:
// Literotica - sort user stories by date
// version 0.10
//
// --------------------------------------------------------------------
//
// This is a Tampermonkey user script.
//
// To install, you need Tampermonkey https://www.tampermonkey.net
//
// --------------------------------------------------------------------
//
// ==UserScript==
// @name          Literotica - Sort user stories
// @namespace     http://none.com/
// @description   Sorts user stories by date
// @match       https://www.literotica.com/stories/memberpage.php?*page=submissions
// @version 0.10
// ==/UserScript==

var t_body = _get_table_body();

// Reuse the first row as table sorting header
var sorting_row = t_body.removeChild(t_body.firstChild);

if (sorting_row.childNodes.length != 4) {
  alert( "Not a useable row for the sorting table header!" );
}
else {
  sorting_row.id = 'sorting_row';
  for (var i=0;i<sorting_row.childNodes.length;i++) {
    sorting_row.childNodes[i].style.backgroundColor = '#77aacc';
    sorting_row.childNodes[i].style.textAlign = 'center';
  }
  sorting_row.childNodes[0].innerHTML = "<a href='#' class='bb sortheader' id='sort_title'>title</a>";
  sorting_row.childNodes[2].innerHTML = "<a href='#' class='bb sortheader' id='sort_category'>category</a>";
  sorting_row.childNodes[3].innerHTML = "<a href='#' class='bb sortheader' id='sort_date'>date</a>";
}

unsafeWindow.sorted_table = {
 date: {
  rows: {},
  sorted: new Array(),
 },
 title: {
  rows: {},
  sorted: new Array(),
 },
 category: {
  rows: {},
  sorted: new Array(),
 },
};

_initial_sort_table( t_body, sorting_row );



//
// Functions
//

function _get_table_body() {
    var first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='bb']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
    //alert('1 - ' + first_bb_a);
    if (first_bb_a == null) {
        first_bb_a = document.evaluate( "//tbody/tr/td/a[@class='t-t84 bb nobck']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        //alert('2 - ' + first_bb_a);
    }
    var result = first_bb_a.parentNode.parentNode.parentNode;
    //alert(result);
  return result;
}


function _initial_sort_table( table_body, sorting_row ) {

  var i = 0;

    while (table_body.childNodes.length > 0) {
    var row = table_body.removeChild(table_body.firstChild);

    if (row.childNodes.length == 4) {
      var date = _get_date( row );
      var lineKey = String( i ).padStart(5, '0');
      if (date != false) {
        for (var j=0;j<row.childNodes.length;j++) {
          row.childNodes[j].style.padding = '5px 2px';
          row.childNodes[j].style.borderBottom = '1px dotted #77aacc';
        }

        var index = date + lineKey;
        unsafeWindow.sorted_table.date.sorted.push( index );
        unsafeWindow.sorted_table.date.rows[index] = row;

        // Also save title and category sort arrays
        var title_text = document.evaluate("//a[contains(@class,'bb')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var title = String( title_text.innerHTML ) + ' ' + lineKey;

        index = String(title).toLowerCase();

        index = index.replace('<span>', '');
        index = index.replace('</span>', '');

        if (index.startsWith("the ")) {
            index = index.substring(4);
        }
        else if (index.startsWith("a ")) {
            index = index.substring(2);
        }
        else if (index.startsWith("an ")) {
            index = index.substring(3);
        }
        else if (index.startsWith("'")) {
            index = index.substring(1);
        }
        else if (index.startsWith("\"")) {
            index = index.substring(1);
        }

        index = index.replace( /\s/gi, '_' );
        index = index.replace('<!--_//_-->', '');

        //alert('"' + index + '"');

        unsafeWindow.sorted_table.title.sorted.push( index );
        unsafeWindow.sorted_table.title.rows[index] = row;

        var category_text = document.evaluate("//a[contains(@class,'intext')]", row, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        var category = String( category_text.innerHTML ) + ' ' + date + ' ' + lineKey;
        index = String(category).toLowerCase();
        index = index.replace( /\s/gi, '_' );

        unsafeWindow.sorted_table.category.sorted.push( index );
        unsafeWindow.sorted_table.category.rows[index] = row;
      }
    }

    i++;
    if (i > 5000) {
      // If we pass 5000, probably something wrong...
      break;
    }
  }

  // Make sure we actually sort...
  for (var key in unsafeWindow.sorted_table) {
    unsafeWindow.sorted_table[key].sorted.sort();
  }

  // Add the sorting table row
  table_body.appendChild( sorting_row );
  var title_link = document.getElementById('sort_title');
  title_link.addEventListener( 'click', function(event) { sort_table('sort_title'); }, false );
  var category_link = document.getElementById('sort_category');
  category_link.addEventListener( 'click', function(event) { sort_table('sort_category'); }, false );
  var date_link = document.getElementById('sort_date');
  date_link.addEventListener( 'click', function(event) { sort_table('sort_date'); }, false );

  for (i=0;i<unsafeWindow.sorted_table.date.sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table.date.rows[ unsafeWindow.sorted_table.date.sorted[i] ] );
  }
}


function sort_table( sort_by ) {
  var sort_key = sort_by.split('_')[1];
  var table_body = _get_table_body();
    var i;

  for (i=1;i<table_body.childNodes.length;i++) {
    table_body.removeChild[i];
  }
  for (i=0;i<unsafeWindow.sorted_table[sort_key].sorted.length;i++) {
    table_body.appendChild( unsafeWindow.sorted_table[sort_key].rows[ unsafeWindow.sorted_table[sort_key].sorted[i] ] );
  }

  //alert( "resorted table using " + sort_key );
}


function _get_date( row ) {
  if (row.childNodes.length != 4) {
    // Spacer row
    return false;
  }
  var date_string = row.childNodes[3].innerHTML;
  var date = date_string.split('/');

  if (date.length != 3) {
    // Not a date?
    return false;
  }

  var year = date[2].valueOf();

  if (year > 50) {
    year = '19' + year;
  }
  else {
    year = '20' + year;
  }

  return String(year) + date[0] + date[1];
}
 
This script is so wonderful, is it possible to make it so the stories that are in their own series already stay like that instead of being merged back into one long list?
 
This script is so wonderful, is it possible to make it so the stories that are in their own series already stay like that instead of being merged back into one long list?

Sort by title. Click on the "Title" heading at the top.
 
Sort by title. Click on the "Title" heading at the top.
I meant keep the series dividers AND sort by date, also title sort doesnt restore the series sections, it just makes it alphabetical, so a series that doesnt have uniform title formats would be out of order.
 
I meant keep the series dividers AND sort by date, also title sort doesnt restore the series sections, it just makes it alphabetical, so a series that doesnt have uniform title formats would be out of order.
Alas, I don't think there's an easy way to do that.

The way the thing works is to pre-sort the lists and then make the one you want visible when you click the link. In that process, it discards anything that isn't a story link.

I'm not the original author. All I did was tinker with it a bit to get it working and fix a few bugs. To make it keep the headings would probably require a total rewrite.

No promises, but I'll think on it and see if I can come up with something.
 
Alas, I don't think there's an easy way to do that.

The way the thing works is to pre-sort the lists and then make the one you want visible when you click the link. In that process, it discards anything that isn't a story link.

I'm not the original author. All I did was tinker with it a bit to get it working and fix a few bugs. To make it keep the headings would probably require a total rewrite.

No promises, but I'll think on it and see if I can come up with something.
Cool, no worries, figured I would ask and see just in case.
 
Alas, I don't think there's an easy way to do that.

The way the thing works is to pre-sort the lists and then make the one you want visible when you click the link. In that process, it discards anything that isn't a story link.

I'm not the original author. All I did was tinker with it a bit to get it working and fix a few bugs. To make it keep the headings would probably require a total rewrite.

No promises, but I'll think on it and see if I can come up with something.
I just downloaded and installed this. Thank you for this.

I have a request if it's not too much trouble. Can you make it so that links that you've visited are a different color, like normal websites? I can never tell what I've already read because the links don't change color. It'd be a great help.

If it's not doable, that's fine, but I don't know why the site makes it harder to see what you've already clicked on.
 
I just downloaded and installed this. Thank you for this.

I have a request if it's not too much trouble. Can you make it so that links that you've visited are a different color, like normal websites? I can never tell what I've already read because the links don't change color. It'd be a great help.

If it's not doable, that's fine, but I don't know why the site makes it harder to see what you've already clicked on.
It's doable. I, in fact, did it so long ago that I forgot that I did it.

I even posted a suggestion to the Tech Support forum: https://forum.literotica.com/threads/suggestion-stop-overriding-the-visited-link-color.1561670/

The problem is that Literotica overrides the usual behavior.

I did it manually, but there's a browser extension out there called Stylebot that looks like it will do what you want.
 
I just downloaded and installed this. Thank you for this.

I have a request if it's not too much trouble. Can you make it so that links that you've visited are a different color, like normal websites? I can never tell what I've already read because the links don't change color. It'd be a great help.

If it's not doable, that's fine, but I don't know why the site makes it harder to see what you've already clicked on.

https://forum.literotica.com/thread...ve-visited-as-purple-solution-inside.1577812/
 
Back
Top