- // ==UserScript==
- // @name Transformania Time Main Page Remake
- // @namespace http://steamcommunity.com/id/siggo/
- // @version 1.0.6
- // @description Reshapes, restyles the Transformania Time main page a bit.
- // @author Prios
- // @match https://www.transformaniatime.com/
- // @match https://test.transformaniatime.com/
- // @copyright 2019, Prios (https://openuserjs.org/users/Prios)
- // @grant none
- // @license MIT
- // jshint multistr: true
- // ==/UserScript==
-
- // PROBLEM: Browser widths between 1200px and 1400px look terrible. All things considered, best to let the components on the left shrink a little.
-
- /*
- function linkifyLogEntries( i ) {
-
- var logActionsMatch = /\b([\wü]*)(?: '?.*')?( .*?)(?: entered from | left toward | cleansed here\.| meditated here\.| searched here\.| cast | dropped | picked up | threw a | shouted |, a | tamed | released a | consumed a )/;
- var logSubject = $( this ).text().match(logActionsMatch); // [1] is first name, [2] is anything beyond the quote-designated nickname (if any) but before the matched action
-
- if ( logSubject !== null ) {
- logSubject = logSubject.slice(1).join(''); // squash the two subgroups together
- // if ( ( logSubject != 'You' )) {
- $(this).wrapInner('<a style="font-weight:normal;color:inherit !important;" href="https://www.transformaniatime.com/PvP/LookAtPlayerItem?vicname=' + logSubject + '"></a>');
- // }
- }
- if ( i >= 300 ) { return false; }
- }
- */
-
- function pseudoShoutClass() { // just simulates a:hover color:black, a color:white
- var $this = $(this);
- var whichEvent = event.type;
- if (whichEvent === 'mouseover') {
- $this.attr('style', $this.attr('style').replace('white', 'black')); // Using .attr because .css doesn't support !important
- }
- else if (whichEvent === 'mouseout') {
- $this.attr('style', $this.attr('style').replace('black', 'white'));
- }
- }
-
- // constructor
- // storage: key used by TfT to store the setting in localStorage; required
- // span: the span tag itself as a jquery object; required
- // friendlyName: string, the public 'end user' name for the setting; required
- // switches: an object with 'true' and 'false' key entries, and corresponding span tag display text as paired values; optional, uses 'ON' and 'OFF' as default
- function DynamicMenuItem(storage, span, friendlyName, switches) {
- this.storageKey = storage;
- this.$spanJQO = span;
- this.myFriendlyName = friendlyName;
- this.switchSettings = switches || {
- 'true': 'ON',
- 'false': 'OFF'
- };
- this.updateSpan = function () {
- var curSetting = localStorage.getItem(this.storageKey) || 'false';
- this.$spanJQO.html(this.switchSettings[curSetting]);
- };
- this.alertStatus = function () {
- var curSetting = localStorage.getItem(this.storageKey) || 'false';
- alert(this.myFriendlyName + ' now ' + this.switchSettings[curSetting] + '!');
- };
- this.flipSwitch = function () {
- var locStoKey = this.storageKey;
- var curSetting = localStorage.getItem(locStoKey) || 'false';
- if (curSetting === 'false') { // this setting is stored as a string in localStorage by TfT
- localStorage.setItem(locStoKey, 'true');
- }
- else {
- localStorage.setItem(locStoKey, 'false');
- }
- this.updateSpan();
- this.alertStatus(); // curSetting will be determined all over again twice but that's okay, this isn't really a super time sensitive operation
- // Plus we'll find out if .setItem did something unexpected, instead of assuming it worked exactly as desired
- };
- this.updateSpan(); // initialize
- }
-
- $(function () {
-
- // var startTime = Date.now();
- 'use strict';
-
- // ACTION BUTTONS - DETACH, AND CANCEL SCRIPT IF NOT PRESENT
- var $playerActions = $('#playerActionBox').detach(); // The original action buttons
- if ($playerActions.length === 0) return; // Stop the script right here if we're not on the right kind of page
-
- // MAJOR COMPONENT EXTRACTION
-
- var $tftSite = $('body').detach(); // Detaching the page to avoid making lots of updates to the DOM itself. Counterintuitively, it's fastest and least intensive to do .finds() from here, when possible.
- var $siteTop = $tftSite.find('#siteTop'); // everything in the body except the footer
- var $siteFooter = $tftSite.find('footer'); // footer tag inside a container-class div
-
- // SUBCOMPONENT VARIABLES
-
- var $settingsWrenchListitem = $tftSite.find('.glyphicon-wrench').parent().parent();
- var $newSettingsMenu = $('<li id="settingsMenuItem" class="dropdown">\
- <a id="settingsMenuAnchor" href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Settings <span class="caret"></span></a>\
- <ul id="settingsMenuList" class="dropdown-menu">\
- <li><a id="settingsTurnAudio" href="#">Turn Alerts are <span id="dynTurnSpan">UNKNOWN (error!)</span></a></li>\
- <li><a id="settingsAttackAudio" href="#">Attack Alerts are <span id="dynAttackSpan">UNKNOWN (error!)</span></a></li>\
- <li><a id="settingsMsgAudio" href="#">Message Alerts are <span id="dynMsgSpan">UNKNOWN (error!)</span></a></li>\
- <li><a id="settingsPopups" href="#">Popup Notifications are <span id="dynPopSpan">UNKNOWN (error!)</span></a></li>\
- <li><a id="settingsBioItem" href="/Settings/SetBio">Update Bio</a></li>\
- <li><a id="settingsBlacklistItem" href="/Settings/MyBlacklistEntries">Manage Blacklist</a></li>\
- <li><a id="settingsReserveNameItem" href="/PvP/ReserveName">Reserve Name</a></li>\
- <li><a id="settingsNicknameItem" href="/Settings/SetNickname">Set your Nickname</a></li>\
- <li><a id="settingsFullPageItem" href="/Settings/Settings">More Settings...</a></li>\
- </ul>\
- </li>'); // ids make finding these elements much faster
-
- var $specialBox = $tftSite.find('.specialBox'); // stats on people playing, boss announcements, chaos round announcement
-
- var $containerInner = $tftSite.find('.containerInner'); // Area with: Avatar info, self portrait, stat bars, action buttons, movement grid, area description
-
- var $innerRows = $containerInner.find('.row'); // Row two starts before the movement grid, row one ends before the action buttons (action buttons are not in a row)
- var $frontOuters = $tftSite.find('.frontOuter'); // the three columns in first row, avatar info/portrait/stat bars
- var $avatarText = $tftSite.find('.avatarText'); // First column's single child, Name/Form/Covenant avatar info box
- var $actionCounts = $frontOuters.find('.avatarCount').slice(0, 2); // first and second, this is number/max of attacks and restorations
-
- var $allIcons = $tftSite.find('.col-sm-12').find('.icon'); // The attack, meditation, and money icons
- var $attackIcon = $tftSite.find('.icon-timesattacking');
- var $recoverIcon = $tftSite.find('.icon-cleansemeditate');
- var $moneyIcon = $tftSite.find('.icon-money');
-
- var $moveGrid = $tftSite.find('.tableLines'); // Movement grid
- var $moveGridCells = $moveGrid.find('td');
-
- var $knownSpells = $tftSite.find('.knownspells'); // spells known in this area
-
- var $covController = $tftSite.find('.covController'); // The sentence stating which covenant has enchanted the area
-
- var $activityLog = $tftSite.find('#RecentActivityLog'); // Original log of room's events, just above footer
- var $activityLogWide; // Widescreen-only clone to place on right side -- cloned later, after some alterations to original
-
- var $lindellaLatest = $activityLog.find('li:contains(Lindella the Soul Vendor )').first();
- var $wuffieLatest = $activityLog.find('li:contains(Wüffie the Soul Pet Vendor )').first();
- var lindellaTrail;
- var wuffieTrail;
-
- // UTILITY VARIABLES
-
- var selfLookURL = $tftSite.find('a:contains(Look at Yourself)').attr('href');
-
- var busStop = $tftSite.find('.bus').length; // 1/true if at a bus stop, 0/false if not
- var covSafeground = $tftSite.find('.covSafeground').length; // 1/true if a safeground, 0/false if not
-
- var barButtonsData = [{
- 'target': 'Cleanse',
- 'text': '',
- 'isBlocked': false
- },
- {
- 'target': 'Meditate',
- 'text': '',
- 'isBlocked': false
- },
- {
- 'target': 'Search',
- 'text': '',
- 'isBlocked': false
- }
- ];
-
- // creating objects associated with the on/off settings menu items, for setting and updating them
- var switchTurnSound = new DynamicMenuItem('play_updateSoundEnabled', $newSettingsMenu.find('#dynTurnSpan'), 'Audible Turn Alerts');
- var switchAttackSound = new DynamicMenuItem('play_AttackSoundEnabled', $newSettingsMenu.find('#dynAttackSpan'), 'Audible Attack Alerts');
- var switchMsgSound = new DynamicMenuItem('play_MessageSoundEnabled', $newSettingsMenu.find('#dynMsgSpan'), 'Audible Message Alerts');
- var switchPopups = new DynamicMenuItem('play_html5PushEnabled', $newSettingsMenu.find('#dynPopSpan'), 'HTML5 Popup Notifications');
-
- // -----------
- // MAIN SCRIPT
-
- // attaching click listeners to dynamic settings menu items
- $newSettingsMenu.find('#settingsTurnAudio').click(function () {
- switchTurnSound.flipSwitch();
- playUpdateSound = localStorage.getItem(switchTurnSound.storageKey) === 'true'; // converting the 'true' or 'false' string into a real boolean
- });
- $newSettingsMenu.find('#settingsAttackAudio').click(function () {
- switchAttackSound.flipSwitch();
- playAttackSound = localStorage.getItem(switchAttackSound.storageKey) === 'true';
- });
- $newSettingsMenu.find('#settingsMsgAudio').click(function () {
- switchMsgSound.flipSwitch();
- playMessageSound = localStorage.getItem(switchMsgSound.storageKey) === 'true';
- });
- $newSettingsMenu.find('#settingsPopups').click(function () {
- switchPopups.flipSwitch();
- notificationsEnabled = localStorage.getItem(switchPopups.storageKey) === 'true';
- });
-
- // Replacing the wrench with the new settings menu
- $settingsWrenchListitem.replaceWith($newSettingsMenu);
-
- // populate barButtonsData[0-2].text with actionButton texts
- $playerActions.find('.actionButton').each(function (i) {
- barButtonsData[i].text = $(this).text();
- });
-
- // Checking whether player has insufficient AP for these actions
- if ((cleanseCost > ap) || (playerMana < 3.0)) {
- barButtonsData[0].isBlocked = true;
- }
- if (meditateCost > ap) {
- barButtonsData[1].isBlocked = true;
- }
- if (searchCost > ap) {
- barButtonsData[2].isBlocked = true;
- }
-
- // General Activity Log alterations
- if ($lindellaLatest.length) { // if Lindella has at least one log entry
- $lindellaLatest.css({
- 'color': 'cyan'
- });
- lindellaTrail = $lindellaLatest.text();
- if (lindellaTrail.includes(" left toward ")) { // check that it's a leaving entry
- lindellaTrail = lindellaTrail.match(/Lindella the Soul Vendor left toward (.*)\n/)[1]; // Now make it just the location she's leaving towards
- }
- else {
- lindellaTrail = null;
- }
- // alert('lindellaTrail is ' + lindellaTrail);
- }
-
- if ($wuffieLatest.length) { // if Wüffie has at least one log entry
- $wuffieLatest.css({
- 'color': 'deeppink'
- });
- wuffieTrail = $wuffieLatest.text();
- if (wuffieTrail.includes(" left toward ")) { // check that it's a leaving entry
- wuffieTrail = wuffieTrail.match(/Wüffie the Soul Pet Vendor left toward (.*)\n/)[1]; // Now make it just the location she's leaving towards
- }
- else {
- wuffieTrail = null;
- }
- // alert('wuffieTrail is ' + wuffieTrail);
- }
-
- // $activityLog.find('li').each( linkifyLogEntries );
-
- $activityLogWide = $activityLog.clone();
-
- // Activity Log alterations (original only, post-cloning)
- $activityLog
- .addClass('hidden-lg');
-
- // Activity Log alterations (sidebar only)
- $activityLogWide
- .attr('id', 'RecentActivityLogSide') // giving the clone a unique ID, unfortunately this removes the id-targeted style rules and forces me to re-add them with the .css method
- .addClass('visible-lg-block') // Bootstrap class thingy that makes this only show up when the page is at least 1200px wide.
- .css({
- 'background': '#C2A9AF',
- 'text-align': 'justify',
- 'overflow-y': 'scroll',
- 'padding': 10,
- 'width': '31%',
- 'position': 'fixed',
- 'resize': 'none',
- 'height': 'initial',
- 'top': 15,
- 'bottom': 15
- }) // the 31% width is a kludge.
- ;
-
- // $specialBox
- // .css({'font-size': 'small'})
- // ;
-
- $siteFooter
- .unwrap() // stripping the footer's container div, footer will be moved into another container later
- ;
-
- $tftSite.find('.offlinePlayersWrapperBG') // this is the inventory row
- .css({
- 'border-bottom': 0,
- 'background': '#ebe8e0'
- });
-
- /*
- $tftSite.find('.formerPlayer') // links to player profiles of ground items
- .wrap(function() {
- var $this = $(this);
- var victimName = $this.text().slice(10, -1);
- return ('<a href="https://www.transformaniatime.com/PvP/LookAtPlayerItem?vicname=' + victimName + '">');
- })
- ;
- */
-
- $actionCounts
- .each(function (i) {
- var $this = $(this);
- if ($this.text() === ' 3 / 3') {
- $this.css({
- 'color': 'red',
- 'font-weight': 'bold'
- }); // warning when tapped out on limited-per-turn actions
- if (i === 1) { // if it's the second one, i.e. recoveries. Kind of confusing and clunky.
- barButtonsData[0].isBlocked = true; // blocking both cleansing and meditation
- barButtonsData[1].isBlocked = true;
- }
- }
- })
- .css({
- 'font-size': 'larger'
- });
-
- $allIcons
- .css({
- 'font-size': 'x-large'
- })
- .removeClass();
-
- $attackIcon
- .html('⚔️');
-
- $recoverIcon
- .html('🔮');
-
- $moneyIcon
- .html('💰');
-
- // preventing style rule freakout
- $containerInner
- .css({
- 'background': '#ebe8e0'
- });
-
- // gross rearrangement of page layout -- this unfortunately causes a marked visual 'jump' once the page is reattached
- $siteTop
- .attr('class', 'container-fluid')
-
- .children()
-
- .wrapAll('<div class="col-lg-8">') // wrap the ENTIRE existing contents in new column
- .parent() // traverse to newly-made column
- .wrap('<div class="row">') // wrap the new column in a new row
- .append($specialBox)
- .append($siteFooter) // move footer to bottom of column
-
- .after('<div class="col-lg-4">') // add another column to the row
- .next() // traverse to newly-made column
- .append($activityLogWide) // add side-log to the new column
-
- ;
-
- $moveGrid // direct alteration before moving to new spot
- .css({
- 'width': 'inherit',
- 'height': 'inherit',
- 'box-shadow': '-5px 5px 5px gray',
- 'table-layout': 'fixed'
- })
-
- .find('td')
- .css({
- 'font-size': 12,
- })
- .end()
-
- .find('tr')
- .css({
- 'height': '33%'
- })
-
- .find('a')
- .css({
- 'display': 'table',
- 'width': '100%',
- 'height': '100%'
- }) // expands the anchor to fill the cell; this requires removing the display:table-cell property, which unfortunately messes up the text's vertical alignment
- .append(function () { // moves the anchor's text to a newly-created nested div element which itself has display:table-cell, fixing the vertical alignment
- var $myText = $(this).text();
- $(this).text('');
- return '<div style="display: table-cell; vertical-align: middle">' + $myText + '</div>';
- })
- .end()
-
- .find('div:contains(' + wuffieTrail + ')') // if wuffieTrail is null, this finds nothing and nothing happens
- .each(function () {
- // alert('text is ' + $(this).text());
- if ($(this).text() === wuffieTrail) { // ensures a precise match, not just containing
- $(this).css({
- 'color': 'deeppink'
- });
- return false; // stops looping
- }
- })
-
- .end()
-
- .find('div:contains(' + lindellaTrail + ')') // if lindellaTrail is null, this finds nothing and nothing happens
- // will overwrite Wuffie's highlighting if they both coincide. A little wasteful
- .each(function () {
- // alert('text is ' + $(this).text());
- if ($(this).text() === lindellaTrail) { // ensures a precise match, not just containing
- $(this).css({
- 'color': 'cyan'
- });
- return false; // stops looping
- }
- })
- .end();
-
- $moveGridCells // this part is a little repetitious, but not too much so, and the individual parts might change in the future
-
- .eq(8) // ninth cell
- .css({
- 'background': '#A16969'
- })
-
- .append('<a href="/pvp/EnchantLocation" style="font-weight: bold; font-size: larger; position: initial; color: white !important">Enchant</a>') // I don't have a better alternative than !important here unfortunately
- .find('a') // selects just-created anchor
- .hover(pseudoShoutClass)
- .end()
-
- .end()
-
- .eq(2) // third cell
- .css({
- 'background': '#A16969'
- })
-
- .append('<a href="/pvp/shout" class="shout" style="font-weight: bold; font-size: larger; position: initial; color: white !important">Shout</a>')
- .find('a') // selects just-created anchor
- .hover(pseudoShoutClass)
- .end()
-
- .end()
-
- .eq(6) // seventh cell
- .css({
- 'background': '#A16969'
- })
-
- .append($knownSpells.html()) // simply sticks the html for $knownSpells into the cell unmodified
- ;
-
- if (busStop) {
- $moveGridCells
-
- .eq(0)
- .css({
- 'background': '#A16969'
- })
-
- .append('<a href="/pvp/Bus" style="font-weight: bold; font-size: larger; position: initial; color: white !important">Take Bus</a>')
- .find('a') // selects just-created anchor
- .hover(pseudoShoutClass);
- }
-
- $avatarText // direct alteration before moving
- .css({
- 'margin-bottom': 10,
- 'display': 'inline-block'
- });
-
- $innerRows.eq(0) // Row with player portrait and stats
- .css({
- 'margin-bottom': 20,
- 'height': 301
- })
-
- .find($frontOuters) // the three columns
- .css({
- 'border': 0,
- 'height': 301
- })
-
- .eq(0) // left column, original location of player name and form name, new location of moveGrid
- .css({
- 'width': 301
- })
- .prepend($moveGrid) // moves it from its original location, already has shadow set
- .end()
-
- .eq(1) // center column, location of player portrait
- .css({
- 'width': 301
- })
-
- .find('.portraitFront')
- .css({
- 'border': 0,
- 'width': 'inherit',
- 'height': 'inherit',
- 'box-shadow': '1px 5px 5px gray'
- })
- .wrap('<a href="' + selfLookURL + '" style="font-weight: normal; width: inherit; height: inherit"></a>')
- .end()
-
- .end()
-
- .eq(2) // right column, location of player stats
-
- .find('.avatarBars')
- .css({
- 'background': '#d9d9d9',
- 'box-shadow': '5px 5px 5px gray'
- })
- .prepend($avatarText) // pulled over here from left column
-
- .find('.barWrapper') // 'backgrounds' of the bars, empty bars
- .css({
- 'height': 35
- })
-
- .find('.barText') // what it says on the tin
- .css({
- 'line-height': '35px'
- })
- .each(function (i) { // this will break if the order of the vital stat bars is changed
- var $this = $(this);
- $this
- .wrap('<a href="/PvP/' + barButtonsData[i].target + '" style="font-weight: normal"></a>') // inherits .barWrapper size
- .hover(
- function () {
- $this // mouseenter
- .data('initialText', $this.text())
- .text(barButtonsData[i].text)
- .css({
- 'color': '#e4e0d4',
- 'font-weight': 'bold'
- });
- if (barButtonsData[i].isBlocked && (secondsToUpdate > 0)) {
- $this
- .css({
- 'text-decoration': 'line-through'
- })
- .parent()
- .attr('href', null);
- }
- },
- function () {
- $this // mouseleave
- .text($this.data('initialText'))
- .css({
- 'color': '',
- 'font-weight': '',
- 'text-decoration': ''
- })
- .parent()
- .attr('href', '/PvP/' + barButtonsData[i].target);
- });
- })
- .end()
-
- .find('.barData') // 'fullness' of bars, widths are percentages of bar wrappers
- .css({
- 'height': 'inherit'
- })
-
- .filter('.barWP') // possibly I should be doing this with an array-like object instead?
- .css({
- 'background': 'linear-gradient(red, darkred)'
- })
- .end()
-
- .filter('.barMP')
- .css({
- 'background': 'linear-gradient(blue, darkblue)'
- })
- .end()
-
- .filter('.barAP')
- .css({
- 'background': 'linear-gradient(orange, darkorange)'
- })
- .end()
-
- .end()
-
- .end()
-
- .find('.avatarXPAmt')
- .css({
- 'background': 'linear-gradient(violet, purple)'
- })
- .wrap('<a href="/PvP/MyPerks"></a>') // inherits size from .avatarXPWrapper
- ;
-
- $innerRows.eq(1) // second row, with now-empty movegrid column and room description
-
- .find('.col-md-4') // former movegrid column
- .remove()
- .end()
-
- .find('.covenDescription') // room description and title
- .removeClass('col-md-8');
-
- if (covSafeground) {
-
- $covController
-
- .css({
- 'color': '#a13d2d'
- })
-
- .contents()
-
- .first() // The text that says 'Enchanted by the '
- .replaceWith('SAFEGROUND for the ');
- }
-
- // Finally update the real site with the changes
- $('head').after($tftSite);
-
- // console.log(Date.now() - startTime);
-
- });