// ==UserScript==
// @name Kongregate Legacy of a Thousand Suns Raid Link Helper
// @namespace tag://kongregate
// @description Improves the text of raid links and stuff
// @author doomcat
// @version 1.1.36
// @date 06.03.2015
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_deleteValue
// @include http://www.kongregate.com/games/*/*
// @include *50.18.190.248/kong/*
// ==/UserScript==
console.log("doomscript injection context", window);
// Even though we include all of Kongregate, we use a Regex to prevent the script from being injected on the wrong pages
if (!/https?:\/\/www\.kongregate\.com\/games\/5thPlanetGames\/legacy-of-a-thousand-suns.*/i.test(window.location.href) && window.location.host !== '50.18.190.248') throw "";
/*
License: "Kongregate Legacy of a Thousand Suns Raid Link Helper for Chat" (henceforth known as "doomscript") is free to download and use unlimited times on unlimited devices. You're allowed to modify the script for personal use, but you need written permission from doomcat to distribute those modifications. If you plan to distribute doomscript in whole or in part, modified or not, as part of an application other than in userscript form, some fees may apply. Contact doomcat for pricing.
Warranty: This userscript comes with no assurance or guarantee of functionality, suitability, or other promise of working as you intend. doomcsript is provided as-is.
*/
/**********************************************\
|** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! **|
|** !!!!!!!!!! NOTE TO DEVELOPERS !!!!!!!!!! **|
|** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! **|
|** !!!!!! If you fork this script, !!!!!! **|
|** !!!!!! please change raidStorage !!!!!! **|
|** !!!!!! in DC_LoaTS_Properties !!!!!! **|
|** !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! **|
\**********************************************/
/**
Change Log:
2012.02.01 - 1.0.0
Initial Version
2012.02.03 - 1.0.1
Quick add of a single missing id
2012.02.06 - 1.0.2
Code Cleanup
Added a bunch of comments for non-code people to convince themselves it isn't a virus
As far as I've heard, it works as designed in Chrome and FF on Mac and Win
Added Additional Alliance raid ids.
2012.02.06 - 1.0.3
All Public raid ids are in as far as I know, including the new Vince Vortex. Still missing some alliance ids.
Should now work in Opera.
2012.02.07 - 1.0.4
Added in FairShare calculation for raid
2012.02.09 - 1.0.5
Added in /raid command
2012.02.14 - 1.0.6
Switched /raid command to report FS*2 instead of *3. Added additional alliance raid ids.
2012.02.16 - 1.0.7
Merged in code from SReject's branch http://userscripts.org/scripts/review/125847
SReject's code adds the ability to click raid links and have them load in the same window, no refresh
Based on SReject's code, added /loadraid command
Moderately sized internal code refactors
2012.02.21 - 1.0.8
Added in /raidformat command
Added in /reload command
Fixed some trouble with /loadraid
All commands can now do /command help to learn more about the command
Minor refactors to improve code versatility
2012.02.22 - 1.0.9
Mistaken name on Mercury Raid
Added debugging for bizarre error that can happen only occasionally.
Now remembers which raids were posted
Target damage is now possible to add to raid links as {target}
Added /seenraids command
Added /clearraids command
Added /raidformat reset
Replaced FS*2 with Target Damage in /raid desriptions
2012.02.22 - 1.0.10
Fixed bug that links from /seenraids refreshed the whole page
Added command /raidhelp
Added /w RaidBot help and /w RaidBot command
Added ability to do regex in /seenraids name
/seenraids will put visited raids to the top of the list
2012.02.27 - 1.0.11
Lots of formatting tweaks in help texts
Commented RaidManager code
Added /clearraids name difficulty {state: stateName}
Added /seenraids name difficulty {state: stateName}
Added update button to /raidhelp aka /w RaidBot help
Automatically checks for new versions. Will show in banner at top.
Finally compiled all known raid ids into script
2012.02.29 - 1.0.12
Fixed collision of hashes - apparently they aren't unique
Fixed typo in /seenraids and /clearraids where all raids were being shown incorrectly
Improved automatic update to popdown include notification bar
Added /autoupdate command
Added /loadraids as alias to /loadraid
Now links you load will update all throughout chat
2012.03.03 - 1.0.13
Fixed slow down of raids as posted
Improved memory usage in long term seen storage
Improved internal processing of raids
Added simple additional filters {age}, {count}, {page}
2012.03.08 - 1.0.14
Fixed a couple of minor reported bugs
Significantly improved speed of /seenraids
When changing the raid format, chat will update all other links in the chat, too
Removed backward compatibilty of old hash indexed raids since those should have all expired by now
2012.04.23 - 1.1.0
-- First Alpha Release: 2012-04-23
First parts of the omnibox UI added
First parts of the raid menu UI added
Added {diff} as a raidformat to get N, H, L, and NM instead of full text difficulties
Added {health}, {time}, {optimal} (alias of target), {ofs} (alias of target), {short-name},
{shorter-name}, {zone}, {cache-state-nice}, and {cache-state-short} to Raid Format options
Completely reworked many parts of the internal structure of the code to make alterations by others easier
Locked script to just the Kongregate game page. Script no longer activates on other games.
Added Zone, Time Limit, Official Short Name, and Unofficial Short name to raids.
Corrected some minor errors in raid information, mainly alliance and world raid.
Attempted to better comment internal code for future extension by others
Raid links are now aged off at each raids length instead of blanket 200 hours.
Fixed health and FS for non-standard health pattern raids (currently Wahsh and Pox)
Added "/linkstate url state" command to force change a link's state, as in /linkstate http://www.kongregate.com/games/5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_raid_boss=telemachus&kv_difficulty=4&kv_raid_id=2769083&kv_hash=9Bo4uUiWIM visited to set that tele to visited
Added "/wiki searchTerm" to open a page to the wiki like /wiki Snuuth Obliterator
-- Second Alpha Release: 2012-04-26
Shift-clicking a link now cycles through all states for that link
Migrated remaining commands over to new command format
/command help should now contain clickable links for examples
/clearraids without parameters will no longer clear all raids. Use /clearraids all
Added a template command in the new command style in order for others to create custom /commands
Moved {visited} format to use standard text
Added {visited-short} format
Slight rework and improvement to custom command creation
Corrected bug where visited state was getting overwritten by unseen
Omnibox should now appropriately respond (at least basically) to all commands
Pox optimum damage set to 20 epics
Fixed missing aliases in help text
Added first version of /farmvalue command. Needs work to be solid.
-- Third Alpha Release: 2012-05-22
Pox FS and Target were still broken. They should really be fixed for real now.
Added simple /time command to display server time. Works nicely in the omnibox.
Added a catch for just incase you /raidbot instead of /w raidbot. They both work the same way.
Added an attempt to recover from corrupted raid link storage. Quarantines old storage for examination and clears current bad storage
Made RaidMenu movable
Added Right-click visited option to preferences menu
Added /farmvalue command for simple hard-coded dump of info from spreadsheet
Added very simple implementation of live search in Raids tab
Fixed wiki commands making double entries in the omnibox autocomplete
-- Fourth Alpha Release: 2012-06-13
Altered RaidLink constructor parameter order - now is id, hash, diff, boss. No longer need all 4, just the first 2
RaidMenu tabs kind of respect tabPosition now, depending on browser. Will implement real solution later.
In some browsers, the script appeared to load a number of times only to fail. Most of these should no longer run.
Added first version of raid format into the raid menu
Added a simple /update command for those that get confused between scripts. Will eventually like to have the command do more.
Files are now in an Assembla SVN repo: http://subversion.assembla.com/svn/doomscript/
Now using Trac for bug tracking: http://trac.assembla.com/doomscript
Todos all moved to ticketing system.
Added Z10 raids, first pasee
-- Fifth Alpha Release: 2012-06-25
Removed kv_action_type=raidhelp from the required parameters of the link due to changes in SReject's spammer
Added game specific icons in place of generic LoTS icon
Can now /raid raidName 0 to get base info about a raid that doesn't change with health
Fixed bug with command aliases being case sensitive. /SEENRAID colo should now work.
Added /clearchat command
Added /raidstyle command
Kong added some padding to their text boxes that has now been removed from the Omnibox
2012.08.01 - 1.1.0 Stable
Too much to even list. See above and tickets.
2012.08.29 - 1.1.1
Updated Skorzeny and Temple info
Updated to add Gut-Phager raid
Updated autoload timer
2012.09.04 - 1.1.2
Improved AutoLoad to have more descriptive messages
Added Load Raids In Background
2012.09.05 - 1.1.3
Commented out Load Raids In Background due to ToS concerns
Added new Hound alliance raid
2012.09.12 - 1.1.4
Added new G. Rahn raid
2012.09.18 - 1.1.5
Added /loadpastebin command
Fixed weird height layout issue with game and chat area
2012.09.21 - 1.1.6
Added Cerebral Destroyer WR
2012.10.02 - 1.1.7
Fixed export function in Chrome
Updated hotlinked image location
Updated Python Data
2012.10.18 - 1.1.8
Added /markall filter state command
Altered /autoload timer in some cases
Added /linktools command to list tools links
Added /pasteraids command
Added blob raid
Fixed export function in Chrome, again
2012.11.02 - 1.1.9
Fixed bug with exportraids killing the omnibox
Added two new raids, Nosferatu Nick and Haunted House
2012.11.02 - 1.1.10
Added 3 new Zone A raids, Boar, Cake, and Guan Yu
2012.11.15 - 1.1.11
Added 3 new Zone A raids, Missile Strike, Bashan, and Your Wrath
2012.11.16 - 1.1.12
Fixed bug where Cerebral Destroyed had gotten deleted
Altered the way export raids works until a longer term solution can be provided
2012.11.16 - 1.1.13
Added 3 new Zone A2 raids, Cyborg Shark, Vlarg Relic Hunter, and Anthropist Xenocide Warship
Fixed unknown links to not say Undefined Undefined over and over
2012.11.28 - 1.1.14
Added 3 new Zone A2 raids. Bile Beast, Pi, and Lubu
Added aliases of /loadpastebin as /loadraidbin and /lrb
Added aliases of /exportraids as /exportraid and /er
Added /updateraiddata or /updatedata or /urd command to pull down new raid data as it's available without updating script
Visted and Completed raids won't be matched by a filter unless {state: visited} or {state: completed} are specifically used
Accepted a patch from sycdan doing the following:
- Formatting: Added {state} and {status} as aliases to {cache-state-nice}
- Formatting: Added {state-short} and {status-short} as aliases to {cache-state-short}
- Raids Tab: Links should now get their state updated to match what they do in chat
- Formatting Tab: Sample raid (when all raids are cleared) will now always have FS/OS
- Chat Commands: /clearchat now aliased with /cc and /cls
- Chat Commands: /raid now aliased with some typo checks
- Chat Commands: /loadraid now aliased with /lr
- Chat Commands: /seenraids now aliased with /sr
2012.11.30 - 1.1.15
Added new World Raid: Kraken
Added new World Raid Tab, Timer, and Loot Table
Fixed visited links not showing up ever in /exportraids
Fixed update raid data being annoying
2012.12.11 - 1.1.16
Removed Kraken World Raid info
Added Snowman Rare Spawn info
Altered some WR display code
Performance tuned some raid loading code
Added link formatting for Alliance invites
Added new Alliance Raid: Crazed Santa
2012.12.14 - 1.1.17
Added two new Alliance Raids: SANTA's Workshop and Rabid Reindeer
Updated Snowman rare spawn info, due to new snowman
Added two new Zone 15 Raids: Tentacled Turkey and Hulking Mutant
Added new WR: Christmas Campaign
Added snull preference to snull the snulls in the snull
Added ignore visited raids preference
Added ignore invalid commands preference
Added additional filtering capability to string multiple filters together using ||, like colo|tele 1 || rage|void 4 would give normal tele, normal colossa and colonel, nightmare ragebeasts, nightmare void
Fixed bug loading WR data during update
Reworked how pastebin and autoload work by making them use the same code
With help from Sycdan, added /loadcconoly
Added some help to keep the spammer up to date with known raid data
2013.02.10 - 1.1.18
Fixed issue with /clearraids all not working
Improved help text of a few commands
Corrected and updated /farmvalue a bit
Added marked dead functionality for CConoly
Added os to filters [Sycdan]
Fixed WR Space Pox Icon
Kind of made the broken close icon for the menu suck less, though not totally fixed
Added Zone 16 raids: Screaming Barracuda and Symphony of Two Worlds
Added Zone 17 raids: Al-Husam
Added two Rare Spawns: Cerebral CEO and Space Pox Mary
WR Info page's forum link should now open in a new window/tab
Corrected hard health numbers on a bunch of raids from Z10 on
2013.02.14 - 1.1.19
Altered /lcc <filter> so it runs /loadall <filter> after fetching raids, rather than just filterting the list of newly-fetched raids [sycdan]
Added preference for delay between loading raids [sycdan]
Hid doomscript tabs that were previously labeled under construction.
2013.02.24 - 1.1.20
Fixed a bug that was causing raids to be marked as completed when they were actually being re-joined
Minor code cleanup
Added more timing data to find slow downs
Added aliases to /linktools: /advertise, /blatantselfpromotion, /getdoomscript
Added aliases to /reload: /reloaf, /reloa, /eload
Moved /loadall to ChatCommands folder from Experimental. It's a main feature now, not just an experimental hack.
Consequently, /loadall will now appear in the correct alphabetical order in the help list
Added /refreshlinks command to cause the links to redraw themselves. This is mainly for when a link refuses to mark visited
All links will now be refreshed after /loadall and /clearraids [sycdan]
Fixed a bug in /clearraids all that was causing /seenraids to still show raids [sycdan]
Cleaned up some CConoly communication code [doomcat/sycdan]
/clearraids ALL was not being accepted. It's now case-insensitive
2013.03.21 - 1.1.21
Fixed missing images on toolbar
Added /rss command
Moved /checkload out of experimental
Added noir raid
Added zone filter
Invalid Raid Id fix [Solsund]
2013.04.04 - 1.1.22
Fixed bug in zone filter not working for /raidstyle
Added size filter
Fixed bug with dynamic loading of Kong page (chat_window div)
Fixed bug where /raidstyle and /markall were not respecting OS filters
Added /forum command [anonimmm]
2013.05.26 - 1.1.23
Fixed critical issues where script is totally broken in Opera.
Added Penelope Wellerd RS
Added M & S alliance raid
Added Zone 19 Raids: Bethany, Vunlac, R. Dule, Master Hao, and Noir (II)
Tweaked chat icons
Attempted critical Chrome 27 fix
2013.06.18 - 1.1.24
Added H8 RS
Fixed Critical bug from Kong Change
2013.12.18 - 1.1.25
Added Inventor RS & WR
Added Sweet and Jalfreezi alliance raids
Changed defaults
Fixed Critical bug in Latest Firefox [greenkabbage]
2013.12.31 - 1.1.26
Added CMM RS
Fixed default prefs not working
Added new pref to hide world chat
2014.02.21 - 1.1.27
Added Mega Mimes, Neon Knights, and Gamma Hammers Alliance raids
Fixed raid icons
2014.03.08 - 1.1.28
Added Chem-Runners Alliance raid
Added two KX raids, Battle Station and Subjugator
Attempt to handle WC's bad link copying bug
2014.05.25 - 1.1.29
Added Zone 20 Raid: Weiqi Game
Added KX Shock Trooper and KX Tank Alliance raids
Added KX Scout Ships and KX Bombarder RS
Added Cow Abduction WR
Added KX Elite Sub
Fixed raid size for 250 man to 100 man alliance raids
Added Zone 21 Raids: Sian Dragonfly and Lady Victoria Ashdown
Corrected values for Zone 5 raids
Updated the dynamic raid update feature to accept updates to existing raids
2014.05.?? - 1.1.30
Updated Ashdown and Dragonfly OS
2014.08.19 - 1.1.31
Fixed background loading for raids [greenkabbage]
Comply with new GreaseMonkey requirements [greenkabbage]
Fixed Chrome App manifest problem
Changed update url
2014.08.25 - 1.1.32
Fix critical issue with latest Firefox/GM changes around events
Added Pinata RS and Pinata's Revenge raid data [greenkabbage]
Minor fix to debugMode
Added a bunch more logging statements in debug mode
2014.08.27 - 1.1.33
Fix XHR for Firefox 32+
2014.10.?? - 1.1.34
Added Trouble in Tokyo WR
Added two new raids, King Krandar and Sultan Shrakzan
Added /ad alias to /linktools post
Added Left Click to Whisper preference and functionality
Added Right Click on Username menu
2015.02.26 - 1.1.35
Major Firefox/Greasemonkey fix [greenkabbage]
Added 31 new raids (ops, zone 22, anniversary), and some updated OS values
Added new {progress} filter to /lrm -- This still seems a bit buggy
Included /lrm in the code directly (rather than external inclusion)
Added url linkification
Added #wiki linkification
Updated filtering for noirs: noir i finds Noir only, noir ii finds Noir (II) only, noir iii finds Noir (III) only
kv_raid_boss is now part of the searchable raid text
Added new /news command
2015.08.?? - 1.1.36
Added 2 new facility raids, Parasite and Exhibit
Added 2 RS Kleptotherms and Star Turtle
Added new Alliance Raid Eviscipod
Fixed UgUp profile loading issue for users who've opted out (Won't spin forever any more)
Updated filtering for subjugators: sub finds both, nsub finds regular subs, esub finds elites
Slight tweak to command links
Tweak to /rss to make it search for latest posts rather than latest threads
Added /suggest command for submitting suggestions
Added /mutelist command for showing all muted users
Moved to using external news site
Migrated to GitHub script hosting
Separated Game refresh from World Chat refresh, added World Chat reload button above the chat pane
Changed Hide World Chat to unload the chat flash object
Fixed missing raid icons for raids added since 1.1.35
TODO: Move to using external datasource for raids
TODO: Use PegJS generated raid parsing filter
TODO: BUG: Links sometime seem to include an extra space at the end
TODO: Better /lrm docs
TODO: Hide previous text from people upon mute
TODO: /wr command
TODO: WR/RS timer/damage display
TODO: WR/RS background image (make sure to hide during power off)
[IDEA] Text coloring based on who's speaking (mods/admins/friends)
[IDEA] Highlighting mentions of the user's username, and maybe some keywords
[TODO] Integrate log analyzer
[TODO] WR/RS notification
[TODO] Delete 1.2.0 branch
[TODO] Integrate GCM/Firefox Push notifications
[TODO] Post new Opera instructions
[TODO] Fix missing images on menu
*/
// Wrapper function for the whole thing. This gets extracted into the HTML of the page.
function main()
{
// Properties for this script
window.DC_LoaTS_Properties = {
// Script info
version: "1.1.36",
authorURL: "http://www.kongregate.com/accounts/doomcat",
updateURL: "http://www.kongregate.com/accounts/doomcat.chat",
scriptURL: "http://bit.ly/doomscript",
scriptDownloadURL: "https://openuserjs.org/install/doomcat/Kongregate_Legacy_of_a_Thousand_Suns_Raid_Link_Helper.user.js",
raidDataURL: "http://getKongE.org/old/RaidData.js",
worldRaidDataURL: "http://getKongE.org/old/WorldRaidData.js",
docsURL: "http://www.tinyurl.com/doomscript-docs",
chatzyURL: "http://us5.chatzy.com/46964896557502",
newsURL: "https://docs.google.com/document/d/1d-r8ZJXPSL8gIY8XviJKM3fzmAUcSOTGlScVdmh0J5A/pub",
joinRaidURL: "http://web1.legacyofathousandsuns.com/kong/raidjoin.php",
kongLoaTSURL: "http://web1.legacyofathousandsuns.com/kong/raidjoin.php",
lotsCDNUrl: "http://5thplanetlots.insnw.net/lots_live/",
// Other URLS
RaidToolsURL: "http://userscripts.org:8080/132671",
QuickFriendURL: "http://userscripts.org:8080/125666",
PlayNowFixURL: "http://userscripts.org:8080/142619",
FleetCodesURL: "https://sites.google.com/site/lotsfleetcodes",
farmSpreadsheetURL: "https://docs.google.com/spreadsheet/ccc?key=0AoPyAHGDsRjhdGYzalZZdTBpYk1DS1M3TjVvYWRwcGc&hl=en_US#gid=4",
// Do not check code in with this set to true.
// Preferably, turn it on from the browser command line with DC_LoaTS_Properties.debugMode = true;
// Or add ?debugMode=true to the game url in the browser
debugMode: (function() {
var value = /debugMode=(\w+)/.exec(document.location.href);
return value && !!value[1];
})(),
// GreaseMonkey Storage Keys
storage: {
// Auto Update
autoUpdate: "DC_LoaTS_autoUpdate",
// Format of messages in chat
messageFormat: "DC_LoaTS_messageFormat",
// Format of links in chat
linkFormat: "DC_LoaTS_linkFormat",
// Format of links in chat
customLinkFormatBool: "DC_LoaTS_customLinkFormatBool",
// Overall container for raid link storage
raidStorage: "RaidManager_doomcat_v1",
// RaidType Specific preferences
raidPrefs: "DC_LoaTS_raidPreferences",
// General script behaviour preferences
behaviorPrefs: "DC_LoaTS_behaviorPreferences",
// Quarantine addendum
quarantine: "_quarantine",
// Timestamp of last query to cconoly
cconolyLastQueryTime: "DC_LoaTS_cconolyLastQueryTime"
}
};
// Class declaring function for Opera compatibility
function declareClasses()
{
/************************************/
/****** DC_LoaTS_Helper Class *******/
/************************************/
// Manager and runner class for this whole thing
// This is a PrototypeJS class. Kongregate uses the Prototype libraries, so we don't
// have to link them ourselves in this script
window.DC_LoaTS_Helper = Class.create({
// Constructor
initialize: function() {
// Initialize the link storage
RaidManager.init();
// Whether or not to auto update
var autoUpdate = GM_getValue(DC_LoaTS_Properties.storage.autoUpdate);
// If we don't have a set value for auto update
if (typeof autoUpdate == "undefined")
{
// Default to true
autoUpdate = true;
GM_getValue(DC_LoaTS_Properties.storage.autoUpdate, true);
}
// If we're auto update checking
if (autoUpdate)
{
// Check for updates
DC_LoaTS_Helper.checkForUpdates();
}
// Get the raid link for the current page
var currentPageLink = new RaidLink(window.location.href);
// Check to see if this is a raid link
if (currentPageLink.isValid())
{
// Store this page as visited
RaidManager.store(currentPageLink, RaidManager.STATE.VISITED);
}
// Show the raid toolbar
RaidToolbar.show();
// Hide the game (or not) -- but delay execution until gameiframe exists ~5 seconds
window.setTimeout(function(){DC_LoaTS_Helper.handleHideWorldChat(DC_LoaTS_Helper.getPref("HideWorldChat", false));}, 5000);
// Move the chat timestamps (or not)
DC_LoaTS_Helper.handleMoveChatTimestamps(DC_LoaTS_Helper.getPref("ChatTimestampRight", false));
// ChatDialogue is the Kongregate ChatDialogue class that is part of the Kongregate Holodeck
// See: http://www.kongregate.com/javascripts/holodeck/chat_dialogue.js for readable source
// We're going to take the normal function that displays a chat message and move it so that
// we can intercept chat messages and reformat them.
ChatDialogue.prototype.DC_LoaTS_displayUnsanitizedMessage = ChatDialogue.prototype.displayUnsanitizedMessage;
// Define the NEW function that will display chat messages (we call the old function at the end
// this is just a reformatter for the better LoaTS links)
// params:
// user - user name of the user who sent the message
// message - message text
// attributes - an object that usually is undefined, but somtimes contains {class: "CSSclassname"} among others
// options - Mostly for use with private messages
ChatDialogue.prototype.displayUnsanitizedMessage = function(user, msg, attributes, options)
{
Timer.start("Process Message");
// Be careful not to reprocess messages that we ourselves sent
if (user.toLowerCase() != "raidbot")
{
// Just in case we need it
var originalMsg = msg,
match;
// Try to create a RaidLink from this message
var raidLink = new RaidLink(msg);
// Alliance Invite Link
var allianceInvitePattern = /(?:https?:\/\/)?(?:www\.)?kongregate\.com\/games\/5thPlanetGames\/legacy-of-a-thousand-suns\?kv_action_type=guildinvite&(?:amp;)?kv_fbuid=kong_([^<"']+)/i;
var allianceInviteFormat = "<a href='{0}'>Join {1}'s alliance? (Opens in this window)</a>";
// Regular external links, borrowed from: http://stackoverflow.com/a/8943487/1449525
// var urlPattern = /((?!=)\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
// Regular external links, borrowed from: http://jmrware.com/articles/2010/linkifyurl/linkify.html
//var urlPattern = /(\()((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\))|(\[)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\])|(\{)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\})|(<|&(?:lt|#60|#x3c);)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(>|&(?:gt|#62|#x3e);)|((?:^|[^=\s'"\]])\s*['"]?|[^=\s]\s+)(\b(?:ht|f)tps?:\/\/[a-z0-9\-._~!$'()*+,;=:\/?#[\]@%]+(?:(?!&(?:gt|#0*62|#x0*3e);|&(?:amp|apos|quot|#0*3[49]|#x0*2[27]);[.!&',:?;]?(?:[^a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]|$))&[a-z0-9\-._~!$'()*+,;=:\/?#[\]@%]*)*[a-z0-9\-_~$()*+=\/#[\]@%])/img;
var urlPattern = /(\()((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\))|(\[)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\])|(\{)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(\})|(<|&(?:lt|#60|#x3c);)((?:ht|f)tps?:\/\/[a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]+)(>|&(?:gt|#62|#x3e);)|(\b(?:ht|f)tps?:\/\/[a-z0-9\-._~!$'()*+,;=:\/?#[\]@%]+(?:(?!&(?:gt|#0*62|#x0*3e);|&(?:amp|apos|quot|#0*3[49]|#x0*2[27]);[.!&',:?;]?(?:[^a-z0-9\-._~!$&'()*+,;=:\/?#[\]@%]|$))&[a-z0-9\-._~!$'()*+,;=:\/?#[\]@%]*)*[a-z0-9\-_~$()*+=\/#[\]@%])/img;
var urlFormat = "<a href='{0}' target='_blank'>{0}</a>";
// Wiki link generation
var hashWikiPattern = /#wiki (?:(?!(?:ht|f)tp|<|:\/\/).)*/img;
// Make sure we haven't already put a raid link in here and the link we found was valid
if (msg.indexOf("<span class=\"raidMessage\"") == -1 && raidLink.isValid())
{
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Retrieve the anchor tag format
var linkFormat = DC_LoaTS_Helper.getLinkFormat();
// Mark the link visited if the current user posted
if (user == holodeck._active_user._attributes._object.username)
{
// Store this link as visited
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
}
else
{
// Store this link as-is and let raid manager decide its state
RaidManager.store(raidLink);
}
// Get the new message after formatting is applied
var newMessage = raidLink.getFormattedRaidLink(messageFormat, linkFormat).trim();
// We don't want to totally blow away the message, though, because people do write text in there some times
msg = msg.replace(/<a(?:(?!<a class="reply_link).)*<\/a>/i, newMessage);
// This means our replace didn't catch it, must be IE link
if (msg == originalMsg)
{
//TODO: Missed this case
//http://cdn2.kongregate.com/game_icons/0033/2679/i.gif?4217-op","5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_difficulty=4&kv_hash=Bil9M7W0s5&kv_raid_boss=centurian_sentinel&kv_raid_id=2969908","Legacy\u00a0of\u00a0a\u00a0T……
//http://cdn2.kongregate.com/game_icons/0033/2679/i.gif?4217-op","5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_difficulty=4&kv_hash=Nw3p60d02T&kv_raid_boss=kalaxian_cult_mistress&kv_raid_id=3293614
//s["http://cdn2.kongregate.com/game_icons/0033/2679/i.gif?4217-op","5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_difficulty=4&kv_hash=4cc5r5FTXh&kv_raid_boss=kalaxian_cult_mistress&kv_raid_id=3315012","Legacy of a Thousand Sun…
//s["http://cdn2.kongregate.com/game_icons/0033/2679/i.gif?4217-op","5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_difficulty=4&kv_hash=VRoC7Po8CD&kv_raid_boss=kalaxian_cult_mistress&kv_raid_id=3324329","Legacy of a Thousand Sun…
msg = msg.replace(RaidLink.backupLinkReplacementPattern, newMessage);
}
// Make sure attributes exists
if (typeof attributes === "undefined")
{
attributes = {};
}
// Make sure attributes.class exists
if (typeof attributes["class"] === "undefined")
{
attributes["class"] = "";
}
// Get the className of the link
var className = raidLink.getMatchedStyles().className;
if (typeof className !== "undefined")
{
attributes["class"] += className;
}
// If still didn't get it, note the problem
if (msg == originalMsg)
{
console.warn("Failed to replace raid link in chat text");
console.warn(raidLink);
console.warn($A(arguments));
}
// Extra debugging bit for a very specific weird behavior
if (typeof raidLink.getRaid() == "undefined" || typeof raidLink.getRaid().fullName == "undefined" || raidLink.getRaid().fullName === "undefined")
{
console.warn("Bad Raid link");
console.warn(raidLink);
console.warn($A(arguments));
}
}
else if (match = allianceInvitePattern.exec(msg)) {
msg = msg.replace(/<a(?:(?!<a class="reply_link).)*<\/a>/i, allianceInviteFormat.format(match[0], match[1]));
}
else {
// Only even check this if it's not another kind of message type
if (DC_LoaTS_Helper.getPref("LinkifyUrls", true)) {
msg = msg.replace(urlPattern, function(url) {
// Last minute check to make sure the regex didn't flub it
// If the url contains any weird characters, ", ', <, or >, just bail
return /["'><]/g.test(url) ? url : urlFormat.format(url);
});
// Replace #wiki links
msg = msg.replace(hashWikiPattern, function(hashWikiText) {
return DC_LoaTS_Helper.getCommandLink("/" + hashWikiText.substr(1), hashWikiText);
});
}
}
}
// Make sure to run the normal version of this function because
// it does all the heavy lifting for actually displaying the right string
// and since we can't control what other scripts and addons have also replaced it
this.DC_LoaTS_displayUnsanitizedMessage(user, msg, attributes, options);
Timer.stop("Process Message");
};
// Take all the chat commands and register them with Kongregate
for (var commandName in DC_LoaTS_Helper.chatCommands)
{
// Get the command
var command = DC_LoaTS_Helper.chatCommands[commandName];
// If there's really a command for this name
if (typeof command !== "undefined")
{
// Create a command factory for this command
var commandFactory = new RaidCommandFactory(command, "chat");
// Attach the command factory to the holodeck callback
holodeck.addChatCommand(commandName, commandFactory.createAndExecuteCommand.bind(commandFactory));
}
}
// We want to intercept whispers to the raid bot and alias commands
// What we're going to do here is snag any attempt to execute a command
// before that command is actually run. Then, we can either capture it
// and keep it from running at all, change it to run how we want (aliases),
// or send the command on as intended (like non-raidbot whispers)
holodeck.DC_LoaTS_processChatCommand = holodeck.processChatCommand;
holodeck.processChatCommand = function(str)
{
DC_LoaTS_Helper.recentlySent.unshift(str);
// Assume it's not /w RaidBot
var raidBotWhisper = false;
// If this is a RaidBot whisper, or someone failed to /w
if (str.substring(0,10).toLowerCase() == "/w raidbot"
||
str.substring(0,8).toLowerCase() == "/raidbot"
)
{
// Grab the command
var command = str.substring(11).trim();
// If there was no command or the command was help
if (command.length == 0 || command.toLowerCase() == "help")
{
// Print out the about script screen
return DC_LoaTS_Helper.printScriptHelp(holodeck, command);
}
// If this has a real command in it
else
{
// Make sure it started with a /
if (command.charAt(0) != '/')
{
command = "/" + command;
}
// Process the command as if it was a normal command
str = command;
}
// This suppressed the command going to chat, even on failure
// and even if a real command is not found by that name
raidBotWhisper = true;
}
//TODO: This process could be optimized a bit when the user starts out using the official command name
// Iterate over the commands to find their aliases
for (var commandName in DC_LoaTS_Helper.chatCommands)
{
DCDebug(commandName);
// If the regular command name is here, just use that
if (new RegExp("^\/" + commandName + "\\b", "i").test(str))
{
// Stop trying to find aliases
break;
}
// Not this real command name. Check its aliases.
else
{
// Grab the aliases for this command
var aliases = DC_LoaTS_Helper.chatCommands[commandName].aliases;
// If there are actually any aliases
if (typeof aliases != "undefined")
{
// For each alias
for (var i = 0; i < aliases.length; i++)
{
// Get this alias
var alias = aliases[i];
// If we found an alias
if (new RegExp("^\/" + alias + "\\b", "i").test(str))
{
// Redirect to the official command
str = str.replace(new RegExp(alias, "i"), commandName);
}
}
}
}
}
// Capture the resulting state of the chat command
var chatCommandResult = holodeck.DC_LoaTS_processChatCommand(str);
var ignoredByPreference = false;
DCDebug("Chat Command Result for " + str + ": ");
DCDebug(chatCommandResult);
// If it was a /w RaidBot but we didn't find a command
if (raidBotWhisper && chatCommandResult)
{
// Let the user know the command failed
holodeck.activeDialogue().raidBotMessage("Did not understand command: <code>" + str + "</code>");
}
else if (chatCommandResult && str.indexOf("/") == 0 && str.indexOf("/me") !== 0 && str.indexOf("/wrists") !== 0 && DC_LoaTS_Helper.getPref("IgnoreInvalidCommands", true)) {
ignoredByPreference = true;
// Let the user know the command failed
holodeck.activeDialogue().raidBotMessage("Did not understand command: <code>" + str + "</code> " + DC_LoaTS_Helper.getCommandLink("/w RaidBot help", "Need Help?"));
}
// Only pass the message along if it wasn't a /w RaidBot and it's not a command and we're not ignoring this message by preference
return !raidBotWhisper && chatCommandResult && !ignoredByPreference;
}; // End Replacement displayUnsanitizedMessage
// This is how we're going to manage left clicks on the chat area
DC_LoaTS_Helper.handleMessageWindowClickHandler();
// This is how we're going to manage right clicks on the chat area
DC_LoaTS_Helper.handleMessageWindowContextMenuHandler();
// Make sure the ignore visited thing is working
// TODO: If we ever do more of these, make a framework for it, or something
DC_LoaTS_Helper.handleIgnoreVisitedRaids();
} // End initialize
});
// Retrieve the message format
DC_LoaTS_Helper.getMessageFormat = function()
{
// Retrieve the message format
var messageFormat = GM_getValue(DC_LoaTS_Properties.storage.messageFormat);
// Fall back to default messageFormat if necessary
if (typeof messageFormat == "undefined" || messageFormat.trim().length == 0)
{
messageFormat = RaidLink.defaultMessageFormat;
GM_setValue(DC_LoaTS_Properties.storage.messageFormat, messageFormat);
}
return messageFormat;
};
// Set the message format
DC_LoaTS_Helper.setMessageFormat = function(messageFormat)
{
// Fall back to default messageFormat if necessary
if (typeof messageFormat == "undefined" || messageFormat.trim().length == 0)
{
messageFormat = RaidLink.defaultMessageFormat;
}
// Set the message format
GM_setValue(DC_LoaTS_Properties.storage.messageFormat, messageFormat);
};
// Retrieve the link format
DC_LoaTS_Helper.getLinkFormat = function()
{
// Retrieve the boolean of whether or not we're using a custom link format
var customLinkFormatBool = GM_getValue(DC_LoaTS_Properties.storage.customLinkFormatBool);
// If we are using a custom link format
if (customLinkFormatBool == true)
{
// Retrieve the custom anchor tag format
var linkFormat = GM_getValue(DC_LoaTS_Properties.storage.linkFormat);
// Fall back to default linkFormat if necessary or update old default
if (typeof linkFormat == "undefined" || linkFormat.trim().length == 0 || linkFormat.trim() == RaidLink.defaultLinkFormat_v1)
{
linkFormat = RaidLink.defaultLinkFormat_v2;
GM_setValue(DC_LoaTS_Properties.storage.linkFormat, linkFormat);
}
}
else
{
if (typeof customLinkFormatBool == "undefined")
{
GM_setValue(DC_LoaTS_Properties.storage.customLinkFormatBool, false);
}
linkFormat = RaidLink.defaultLinkFormat_v2;
}
return linkFormat;
};
// Retrieve a preference value from storage
DC_LoaTS_Helper.getPref = function(prefName, defaultValue)
{
// Fetch the json
var json = GM_getValue(DC_LoaTS_Properties.storage.behaviorPrefs);
// Make sure there's JSON
if (typeof json === "undefined" || json.length == 0)
{
json = "{}";
}
var ret;
try
{
var prefs = JSON.parse(json);
ret = prefs[prefName];
}
catch(ex)
{
console.warn("Could not parse prefs to find " + prefName);
console.warn(ex);
}
return (typeof ret !== "undefined") ? ret : defaultValue;
};
// Store a preference value into storage
DC_LoaTS_Helper.setPref = function(prefName, value)
{
// Fetch the json
var json = GM_getValue(DC_LoaTS_Properties.storage.behaviorPrefs);
// Make sure there's JSON
if (typeof json == "undefined" || json.length == 0)
{
json = "{}";
}
// Store value
try
{
var prefs = JSON.parse(json);
prefs[prefName] = value;
GM_setValue(DC_LoaTS_Properties.storage.behaviorPrefs, JSON.stringify(prefs));
}
catch(ex)
{
console.warn("Could not parse prefs to store " + prefName + ": " + value);
console.warn(ex);
}
};
// Find all raid types matching a given filter
DC_LoaTS_Helper.getRaidTypes = function(raidFilter)
{
// We're going to return an array of raid types that match
var matchedTypes = [];
// Iterate over all raids
for (var raidId in DC_LoaTS_Helper.raids)
{
// Get the current raid
var raid = DC_LoaTS_Helper.raids[raidId];
// If the user's text matches this raid name
if (raidFilter.matches({name: raid.getSearchableName(), size: raid.size, zone: raid.zone}))
{
// Capture this raid to return
matchedTypes.push(raid);
}
}
return matchedTypes;
};
// Print the description of the script
DC_LoaTS_Helper.printScriptHelp = function(deck, text)
{
var helpText = "<b>Kongregate Legacy of a Thousand Suns Raid Link Helper for Chat</b>\n";
helpText += "by <a href=\"" + DC_LoaTS_Properties.authorURL + "\">doomcat</a>\n";
helpText += "\n";
helpText += "<b>Script homepage:</b> <a href=\"" + DC_LoaTS_Properties.scriptURL + "\" target='_blank'>" + DC_LoaTS_Properties.scriptURL + "</a>\n";
helpText += "<b>Script Docs:</b> <a href=\"" + DC_LoaTS_Properties.docsURL + "\" target='_blank'>" + DC_LoaTS_Properties.docsURL + "</a>\n";
helpText += "<b>Script Chatzy:</b> <a href=\"" + DC_LoaTS_Properties.chatzyURL + "\" target='_blank'>" + DC_LoaTS_Properties.chatzyURL + "</a>\n";
helpText += "\n";
helpText += "<span class=\"DC_LoaTS_versionWrapper\">";
// If we've checked for version before
if (typeof DC_LoaTS_Helper.needUpdateState != "undefined")
{
// If it's time to update
if (DC_LoaTS_Helper.needUpdateState == "old")
{
helpText += DC_LoaTS_Helper.needUpdateText + "\n";
helpText += "\n";
helpText += "\n";
helpText += "<span class='clearfix'>";
helpText += "<span style='float:left; padding-top: 5px;'>Update now?</span>";
helpText += "<span style='float:right;'><a class='DC_LoaTS_updateLink' href='" + DC_LoaTS_Properties.scriptDownloadURL + "' target='_blank'>Update</a></span>";
}
// If the user has a newer than public version
else if (DC_LoaTS_Helper.needUpdateState == "new")
{
helpText += DC_LoaTS_Helper.needUpdateText + "\n";
helpText += "\n";
}
// Either current or some kind of failure
else
{
helpText += "<b>Version:</b> " + DC_LoaTS_Helper.needUpdateText + "\n";
helpText += "\n";
helpText += "\n";
helpText += "<span class='clearfix'>";
helpText += "<span style='float:left; padding-top: 5px;'>Check for updates?</span>";
helpText += "<span style='float:right;'><a class='DC_LoaTS_updateLink DC_LoaTS_updateNotRun' onclick='DC_LoaTS_Helper.checkForUpdates(); return false' href='#' target='_blank'>Check now</a></span>";
}
}
// We don't really know what the current version is
else
{
helpText += "<b>Version:</b> " + DC_LoaTS_Properties.version + "\n";
helpText += "\n";
helpText += "\n";
helpText += "<span class='clearfix'>";
helpText += "<span style='float:left; padding-top: 5px;'>Check for updates?</span>";
helpText += "<span style='float:right;'><a class='DC_LoaTS_updateLink DC_LoaTS_updateNotRun' onclick='DC_LoaTS_Helper.checkForUpdates(); return false' href='#' target='_blank'>Check now</a></span>";
}
helpText += "</span>";
helpText += "\n";
helpText += "\n";
helpText += "</span>";
helpText += "\n";
helpText += "<b>Commands:</b>\n";
helpText += "\n";
// Iterate over all commands and display their summaries
for (var commandName in DC_LoaTS_Helper.chatCommands)
{
var command = DC_LoaTS_Helper.chatCommands[commandName];
if (typeof command.doNotEnumerateInHelp == "undefined" || command.doNotEnumerateInHelp === false)
{
if (typeof command.getParamText === "function")
{
helpText += "<code>/" + commandName + " " + command.getParamText() + "</code>\n";
}
}
}
helpText += "\n";
helpText += "All commands can do <code>/commandname help</code> to learn more about them. Brackets <code>[]</code> indicate optional parameters; don't actually put brackets in your commands, please.\n";
deck.activeDialogue().raidBotMessage(helpText);
return false;
};
DC_LoaTS_Helper.recentlySent = [];
DC_LoaTS_Helper.chatCommands = {};
DC_LoaTS_Helper.raidStyles = {}; /************************************/
/********* RaidButton Class *********/
/************************************/
window.RaidButton = Class.create({
initialize: function(name, className, callback)
{
this.name = name || "";
this.callback = callback;
this.node = new Element("li", {"class": "DC_LoaTS_button_wrapper " + className + "Wrapper"});
this.anchor = new Element("a", {"class": "DC_LoaTS_button " + className});
this.anchor.appendChild(document.createTextNode(this.name));
this.anchor.observe("click", function(clickEvent)
{
this.execute(clickEvent);
}.bindAsEventListener(this));
this.node.insert({bottom: this.anchor});
},
execute: function(clickEvent)
{
this.callback(clickEvent);
}
});
/************************************/
/********* RaidCommand Class ********/
/************************************/
// Mainly located by the omnibox iterating over all commands checking to see what matches
// and each of these being hard assigned to their names for the chat commands
window.RaidCommand = Class.create({
initialize: function(context, commandText)
{
this.context = context;
this.isHelp = false;
if (typeof commandText != "undefined")
{
this.processText(commandText);
}
},
processText: function (commandText)
{
this.commandText = commandText;
this.processedText = this.commandText;
if (this.processedText.charAt(0) == '/')
{
this.processedText = this.processedText.substring(1);
}
// If the command was explicitly provided, we need to strip it
if (this.processedText.toLowerCase().indexOf(this.getName()) == 0)
{
this.processedText = this.processedText.substring(this.getName().length);
}
else
{
for (var i = 0; i < this.aliases.length; i++)
{
var alias = this.aliases[i];
if (this.processedText.toLowerCase().indexOf(alias) == 0)
{
this.processedText = this.processedText.substring(alias.length);
}
}
}
// Now processed text should just be the params. Need to trim the whitespace
this.processedText = this.processedText.trim();
// Reassemble the normalized commandText
this.commandText = "/" + this.getName() + " " + this.processedText;
// Check for help
if (this.processedText.toLowerCase() == "help")
{
this.isHelp = true;
}
// Not a help command
else
{
// With the params, get the parser
if (typeof this.parsingClass != "undefined")
{
this.parser = new this.parsingClass(this.processedText);
}
}
},
// Get the param text for help
getParamText: function()
{
return this.constructor.getParamText();
},
// Get all the names for this command, including both it's actual name and aliases
getNames: function()
{
return [this.getName()].concat(this.aliases);
},
// Get the name of this command
getName: function()
{
return this.constructor.commandName;
},
// Get the help text for the command
getHelpText: function()
{
// Default help text to say there isn't help text
var helpText = "This command does not have any additional help.";
// If the subclass has help text
if (typeof this.buildHelpText != "undefined")
{
// Grab it and set it to be our returned help text
helpText = this.buildHelpText();
}
// Append any aliases this command has
helpText += "\n"
helpText += "<b>Aliases:</b> " + this.getAliasesText() + "\n";
return helpText;
},
// See if the assigned parser has valid params
//FIXME - Does not work and/or is not used
isValid: function()
{
var valid = true;
if (typeof this.parsingClass != "undefined")
{
var parser = new this.parsingClass(params);
valid = parser.isValid();
}
return valid;
},
// Get the text for all the aliases of this command. Aliases are wrapped in <code></code>tags
getAliasesText: function()
{
var aliasesText = "";
// If there are any aliases
if (typeof this.aliases != "undefined" && this.aliases.length > 0)
{
// Add all the aliases in
for (var i = 0; i < this.aliases.length; i++)
{
// Format the alias
aliasesText += "<code>/" + this.aliases[i] + "<code>";
// Add commas as necessary
if (i < this.aliases.length - 1)
{
aliasesText += ", ";
}
}
}
// No aliases
else
{
aliasesText = "None.";
}
return aliasesText;
},
// Get a text link to this command
getCommandLink: function(params, displayText)
{
return DC_LoaTS_Helper.getCommandLink("/" + this.getName() + (params ? " " + params:""), displayText);
},
// Get the drop down menu options for this command
getOptions: function()
{
var commandOptions = {
initialText: {
text: this.commandName,
callback: function()
{
console.log("mainOption " + this.commandName);
}
},
secondOption: {
text: "Option #2",
callback: function()
{
console.log("secondOption " + this.commandName);
}
}
};
return commandOptions;
},
// Gets the full HTML line for this command's options
getOptionLine: function(oldLine)
{
var commandOptions = this.getOptions();
var line;
// If we're operating on an existing line
if (typeof oldLine != "undefined")
{
// Put new stuff back into this line
line = oldLine;
// Clear everything old from this line
line.childElements().invoke("remove");
}
// If there is no existing line
else
{
// Make a new line
line = new Element("li");
}
var subOptions = new Element("div", {style: "float: right;"});
var config = commandOptions.config || {};
for (var optionName in commandOptions)
{
// Configuration is obviously not a real option
if (optionName.toLowerCase() == "config")
{
continue;
}
var option = commandOptions[optionName];
var textHolder;
if (typeof option.callback != "undefined" || typeof option.linkParams != "undefined" || false !== option.executable)
{
var linkParams = {"href": "#", "class": "DC_LoaTS_omniboxOption DC_LoaTS_" + optionName.toLowerCase()};
if (typeof option.linkParams != "undefined")
{
for (var paramName in option.linkParams)
{
linkParams[paramName] = option.linkParams[paramName];
}
}
textHolder = new Element("a", linkParams);
textHolder.onclick = function(option)
{
if (typeof option.callback != "undefined")
{
option.callback.apply(this);
}
this.execute(option.doNotCallHandler);
return (typeof option.followLink != "undefined")?option.followLink:false;
}.bind(this, option);
}
else
{
textHolder = new Element("div", {"class": "DC_LoaTS_" + optionName.toLowerCase()});
}
if (typeof option.text != "undefined")
{
textHolder.update(option.text);
}
if (optionName == "initialText")
{
line.insert({bottom: textHolder});
}
else
{
subOptions.insert({bottom: textHolder});
}
}
if (subOptions.children.length > 0)
{
line.insert({bottom: subOptions});
}
else
{
line.children[0].setStyle({"float":"none"});
}
//
// var children = $A(line.immediateDescendants());
// var currentTallest = 0;
//
// for (i = 0; i < children.length; i++)
// {
// if (children[i].getHeight() > currentTallest)
// {
// currentTallest = children[i].getHeight();
// }
// }
//
// for (i = 0; i < children.length; i++)
// {
// children[i].setStyle({ height: (currentTallest + 'px') });
// }
//
//
if (typeof config.refreshEvery == "number" && typeof this.omniboxOptionRefreshInterval == "undefined")
{
this.omniboxOptionRefreshInterval = setInterval(this.getOptionLine.bind(this, line), config.refreshEvery);
}
return line;
},
// Run this command
execute: function(doNotCallHandler)
{
var ret = {};
// Check for help
if (this.isHelp == true)
{
DCDebug("Executing help for " + this.commandName);
if (this.context == "chat" || true) //TODO: Remove || true
{
holodeck.activeDialogue().raidBotMessage(this.getHelpText());
}
else if (this.context == "omnibox")
{
console.log("Display help for " + this.commandName);
}
else
{
console.warn("Could not find help for command " + this.commandText + " in context " + this.context);
}
}
// Not a help command
else if (typeof doNotCallHandler === "undefined" || !doNotCallHandler)
{
DCDebug("Executing non-help for " + this.commandName + " doNotCallHandler: " + doNotCallHandler)
if (typeof this.parser === "undefined" || (typeof this.parser.isValid === "function" && this.parser.isValid()))
{
ret = this.handler(holodeck, this.parser, this.processedText, this.commandText, this.context);
if (typeof ret.statusMessage != "undefined")
{
if (this.context == "chat" || true) //TODO: Remove || true
{
holodeck.activeDialogue().raidBotMessage(ret.statusMessage);
}
else if (this.context == "omnibox")
{
console.log("Display status message: " + ret.statusMessage);
}
else
{
console.warn("Could not display message " + ret.statusMessage + " for command " + this.commandText + " in context " + this.context);
}
}
DCDebug("Command " + this.commandText + (ret.success===true?" Succeeded":" Failed"));
}
else
{
console.warn("Could not parse text " + this.commandText + " as command " + this.commandName + " in context " + this.context);
}
}
ret.sendToChat = this.sendToChat && this.context == "chat";
// Regardless of execution, we need to hide the command options
RaidToolbar.hideCommandOptions();
// Clear the omnibox, needs work
// RaidToolbar.resetOmnibox();
return ret;
},
// Called when the option is no longer in the suggested box
onRemovedFromOmnibox: function()
{
DCDebug("Deactivating " + this.commandName);
if (typeof this.omniboxOptionRefreshInterval != "undefined")
{
clearInterval(this.omniboxOptionRefreshInterval);
delete this.omniboxOptionRefreshInterval;
}
}
});
RaidCommand.create = function(classObject)
{
if (typeof classObject.commandName === "undefined") {
throw {message: "Cannot create command without name", cls: classObject};
}
if (typeof classObject.aliases === "undefined") {
classObject.aliases = [];
console.warn(classObject.commandName + " did not define its aliases");
}
var commandClass = Class.create(RaidCommand, classObject);
//TODO: Need to clean this up
commandClass.commandName = classObject.commandName;
commandClass.aliases = classObject.aliases;
commandClass.paramText = classObject.paramText;
commandClass.parsingClass = classObject.parsingClass;
//TODO Implement OO framework at some point
if (typeof commandClass.parsingClass !== "undefined" && typeof commandClass.parsingClass.prototype.isValid !== "function")
{
console.warn(commandClass.commandName + " Command Creation Error: Parser must have isValid method!");
}
commandClass.doNotEnumerateInHelp = classObject.doNotEnumerateInHelp;
commandClass.getParamText = function()
{
// Assume empty
var params = "";
// If the command provided text, use that
if (typeof this.paramText != "undefined")
{
params = this.paramText;
}
// If the parser can provide us param text, that's great, too
else if (typeof this.parsingClass != "undefined" && typeof this.parsingClass.paramText == "string")
{
params = this.parsingClass.paramText;
}
else
{
DCDebug("No param text for " + this.commandName);
}
return params;
}.bind(commandClass);
DC_LoaTS_Helper.chatCommands[classObject.commandName] = commandClass;
};
/************************************/
/****** RaidCommandFactory Class ****/
/************************************/
window.RaidCommandFactory = Class.create({
/*public constructor*/ initialize: function(raidCommandClass, context)
{
this.raidCommandClass = raidCommandClass;
this.context = context;
},
/*public RaidCommand*/ createCommand: function(text)
{
return new this.raidCommandClass(this.context, text);
},
// Returning true will send the message on to Kongregate chat
// Returning false will stop the message from being sent
/*public boolean*/ createAndExecuteCommand: function(deck, text)
{
var command = this.createCommand(text);
DCDebug("Created Command " + this.raidCommandClass.commandName);
var commandRet = command.execute();
// If the commandRet has sendToChat set to true, the command text typed by the user,
// (Not the command output text) will forward on to chat
// otherwise, we default to absorbing the command locally
return commandRet.sendToChat || false;
}
});
/************************************/
/********* RaidFilter Class *********/
/************************************/
// This class represents a filter on a raid search
window.RaidFilter = Class.create({
// Constructor
initialize: function(filterText)
{
Timer.start("RaidFilter init");
try
{
// Declare some vars for later. This is more for reference than technical need.
this.name;
this.difficulty;
this.state;
this.inverseState = false;
this.age;
this.size;
this.count;
this.page;
this.fs;
this.os;
this.zone;
this.progress; // Only usable with lrm (needs raid currentHealth and timeRemaining
this.valid = true;
// Capture original filterText
this.filterText = filterText;
// Pattern to pick apart the command for the name and id
//TODO: /((?:[^{}\d]|[5-9]|\d*\.\d*)+)?\s*([0-4](?:\s*\|\s*[0-4]){0,4})?/
var commandPattern = /([^0-4{}]+)? ?([0-4])? ?/;
// Attempt to find the matches
var match = commandPattern.exec(filterText);
// If there were some matches
if (match != null)
{
// If the raid command had a name
if (typeof match[1] != "undefined")
{
this.name = match[1].trim();
}
// If the raid command had a difficulty
if (typeof match[2] != "undefined")
{
this.difficulty = parseInt(match[2]);
}
}
// Pattern to match everything that's currently in {filterType: paramValue} form
var extraFiltersPattern = /(?:{(\w+)[:=]([^{}]+)} ?)/g;
// For every additional parameter type
while ((match = extraFiltersPattern.exec(filterText)) != null)
{
// Name of the param
var filterType = match[1].trim().toLowerCase();
// Value of the param
var paramValue = match[2].trim();
// Trace statement
var traceStatement = "<code>{" + filterType + ":" + paramValue + "}</code> in <code>" + filterText + "</code>";
// Based on the param type, parse the param value
switch (filterType)
{
case "age":
// Get the pieces of the age
var match = RaidFilter.numberExpressionPattern.exec(paramValue);
// If there were pieces to get
if (match != null)
{
var condition = match[1];
var num = parseInt(match[2]);
// If the number wasn't really a number
if (isNaN(num))
{
// Go to the next one.
continue;
}
var period = match[3];
// If there was a period
if (typeof period != "undefined")
{
switch (period.toLowerCase())
{
// Fall throughs are on purpose
case "d":
// 24 hours in a day
num *= 24;
case "h":
// 60 minutes in an hour
num *= 60;
case "m":
// 60 seconds in a minute
num *= 60;
case "s":
// 1000 ms in a second
num *= 1000;
break;
case "ms":
break;
default:
holodeck.activeDialogue().raidBotMessage("Did not understand unit of time <code>" + period + "</code> for " + traceStatement);
this.valid = false;
}
}
// else no period, assume ms
// Sanitize the condition. Default to <=
condition = this.sanitizeConditional(condition, "<=");
if (condition == "undefined")
{
holodeck.activeDialogue().raidBotMessage("Did not understand condition <code>" + condition + "</code> for " + traceStatement);
this.valid = false;
}
}
else
{
// Notify the user that we don't know what that age is
holodeck.activeDialogue().raidBotMessage("Did not understand " + filterType + " expression <code>" + paramValue + "</code> for " + traceStatement);
this.valid = false;
}
this.age = condition + num;
break;
case "count":
// If the number wasn't really a number
if (isNaN(parseInt(paramValue)))
{
// Go to the next one.
continue;
}
this.count = parseInt(paramValue);
break;
case "page":
// If the number wasn't really a number
if (isNaN(parseInt(paramValue)))
{
// Go to the next one.
continue;
}
this.page = parseInt(paramValue);
break;
case "progress":
// Progress translates to the ratio of health to time
switch (paramValue.toLowerCase()) {
case "ahead":
paramValue = this.progress = "<= 1";
break;
case "behind":
paramValue = this.progress = "> 1";
break;
}
// Fall through on purpose
case "os":
case "fs":
case "size":
var match = RaidFilter.numberExpressionPattern.exec(paramValue);
if (match != null)
{
var condition = match[1];
var num = parseInt(match[2]);
// If the number wasn't really a number
if (isNaN(num))
{
// Go to the next one.
continue;
}
var magnitude = match[3];
// If there was a magnitude
if (typeof magnitude != "undefined")
{
switch (magnitude.toLowerCase())
{
// Fall throughs are on purpose
case "t":
num *= 1000;
case "b":
num *= 1000;
case "m":
num *= 1000;
case "k":
num *= 1000;
break;
default:
holodeck.activeDialogue().raidBotMessage("Did not understand magnitude <code>" + magnitude + "</code> for " + traceStatement);
this.valid = false;
}
}
// else no magnitude, assume fully written out damage
// Sanitize the condition. Default to ==
condition = this.sanitizeConditional(condition, "==");
if (condition == "undefined")
{
holodeck.activeDialogue().raidBotMessage("Did not understand condition <code>" + condition + "</code> for " + traceStatement);
this.valid = false;
}
}
else
{
// Notify the user that we don't know what that fs is
holodeck.activeDialogue().raidBotMessage("Did not understand " + filterType + " expression " + traceStatement);
this.valid = false;
}
this[filterType] = condition + num;
break;
case "state":
var tmpStateText = paramValue;
// Are we doing inverse state?
if (tmpStateText.charAt(0) == '!')
{
this.inverseState = true;
tmpStateText = tmpStateText.substring(1);
}
// Lookup the state enum from the text
this.state = RaidManager.STATE.valueOf(tmpStateText);
// If the text didn't match any known state
if (typeof this.state == "undefined")
{
// Notify the user that we don't know what that state is
holodeck.activeDialogue().raidBotMessage("Did not understand state for " + traceStatement);
// No longer valid
this.valid = false;
}
break;
case "zone":
if (isNaN(paramValue)) {
this.zone = "" + paramValue;
}
else {
this.zone = "Z" + paramValue
}
this.zone = this.zone.toUpperCase();
break;
default:
console.warn("Did not understand filter param " + match[1] + ":" + match[2]);
}
}
}
catch(ex)
{
console.warn("Failed to initialize RaidFilter with text " + filterText);
}
Timer.stop("RaidFilter init");
},
// Takes in a condition and sanitizes it for use in the filter
sanitizeConditional: function(condition, defaultTo)
{
if (typeof condition !== "undefined")
{
switch (condition)
{
case "=":
condition = "==";
break;
case "!":
condition = "!=";
break;
case "<=":
case ">=":
case "==":
case "!=":
case "<":
case ">":
break;
default:
// Print warning to console
console.warn("Could not parse condition: " + condition);
// Return undefined since there was a problem
condition = undefined;
}
}
// If there was no condition passed in
else
{
// Set it to the default
condition = defaultTo;
}
// Return the correct condition
return condition;
},
// Based on this filter, does a given property match the filter
matches: function(params)
{
// Init matched to true
var matched = true;
var STATE = RaidManager.STATE;
// Shortcut to fail any visited, completed, or ignored raids when no state filter is specified
if (typeof params.state !== "undefined" && !this.state &&
(
STATE.equals(params.state, STATE.VISITED) ||
STATE.equals(params.state, STATE.COMPLETED) ||
STATE.equals(params.state, STATE.IGNORED)
)
) {
return false;
}
// Iterate over all the params
for (var field in params)
{
// Grab the value of the field
var value = params[field];
// If the field is not part of the filter or was undefined in the params
if (typeof value != "undefined" && typeof this[field] != "undefined")
{
switch(field.toLowerCase())
{
case "name":
var tmpName = RaidFilter.mangleNameForSearch(this.name) || "NOT SET";
try {
// If the user's text matches this raid name
matched = matched && new RegExp(tmpName, "i").test(value);
}
catch (ex){
holodeck.activeDialogue().raidBotMessage("Errors occurred while trying to match " + tmpName + ". " + ex);
console.log(ex);
return false;
}
break;
case "difficulty":
// If the user's difficulty matches the raid
matched = matched && this.difficulty == value
break;
case "state":
// If the state matches and shouldn't be inverted
// Or of the state doesn't match and should be inverted
matched = matched && ((RaidManager.STATE.equals(value, this.state) && !this.inverseState)
||
(!RaidManager.STATE.equals(value, this.state) && this.inverseState));
break;
case "age":
// Check against the age condition
matched = matched && eval(value + this.age);
break;
case "progress":
case "os":
case "fs":
case "size":
// Check against the fs condition
matched = matched && eval(value + this[field]);
break;
case "count":
// Check against the count condition
matched = matched && value < this.count;
break;
case "zone":
matched = matched && value === this.zone;
break;
default:
// Didn't find the field
console.warn("Couldn't match RaidFilter property to " + field);
matched = false;
break;
}
}
}
return matched;
},
// Gets a key to define this filter
getKey: function()
{
return ((typeof this.name !== "undefined")?"n=" + this.name + ";":"") +
((typeof this.difficulty !== "undefined")?"d=" + this.difficulty + ";":"") +
((typeof this.state !== "undefined")?"s=" + this.state + ";":"") +
((typeof this.inverseState !== "undefined")?"i=" + this.inverseState + ";":"") +
((typeof this.age !== "undefined")?"a=" + this.age + ";":"") +
((typeof this.count !== "undefined")?"c=" + this.count + ";":"") +
((typeof this.page !== "undefined")?"p=" + this.page + ";":"") +
((typeof this.progress !== "undefined")?"pr=" + this.progress + ";":"") +
((typeof this.size !== "undefined")?"e=" + this.size + ";":"") +
((typeof this.fs !== "undefined")?"f=" + this.fs + ";":"") +
((typeof this.os !== "undefined")?"o=" + this.os + ";":"") +
((typeof this.zone !== "undefined")?"z=" + this.zone + ";":"");
},
// If it has a name and optionally a difficulty and nothing else, it's simple
isSimple: function()
{
return typeof this.name !== "undefined" &&
(typeof this.state === "undefined" &&
typeof this.inverseState === "undefined" &&
typeof this.age === "undefined" &&
typeof this.count === "undefined" &&
typeof this.page === "undefined" &&
typeof this.progress === "undefined" &&
typeof this.size === "undefined" &&
typeof this.fs === "undefined" &&
typeof this.os === "undefined" &&
typeof this.zone === "undefined");
},
isEmpty: function()
{
return (typeof this.name === "undefined") &&
(typeof this.difficulty === "undefined") &&
(typeof this.state === "undefined") &&
(typeof this.inverseState === "undefined") &&
(typeof this.age === "undefined") &&
(typeof this.count === "undefined") &&
(typeof this.page === "undefined") &&
(typeof this.progress === "undefined") &&
(typeof this.size === "undefined") &&
(typeof this.fs === "undefined") &&
(typeof this.os === "undefined") &&
(typeof this.zone === "undefined");
},
isValid: function()
{
return this.valid;
},
getDifficultyText: function()
{
return RaidType.difficulty[this.difficulty];
},
toString: function()
{
return (((typeof this.name != "undefined")? this.name + " ":"") +
((typeof this.difficulty != "undefined")? this.difficulty + " ":"") +
((typeof this.state != "undefined")? "{state: " +
((typeof this.inverseState != "undefined" && this.inverseState == true)? "!":"")
+ this.state.text + "}"+ " ":"") +
((typeof this.size != "undefined")? "{size: " + this.size + "} ":"") +
((typeof this.fs != "undefined")? "{fs: " + this.fs + "} ":"") +
((typeof this.os != "undefined")? "{os: " + this.os + "} ":"") +
((typeof this.progress != "undefined")? "{progress: " + this.progress + "} ":"") +
((typeof this.zone != "undefined")? "{zone: " + this.zone + "} ":"") +
((typeof this.age != "undefined")? "{age: " + this.age + "ms} ":"") +
((typeof this.count != "undefined")? "{count: " + this.count + "} ":"") +
((typeof this.page != "undefined")? "{page: " + this.page + "} ":"")).trim();
},
toPrettyString: function() {
var ret = "";
// Find the matching raid types
var matchedTypes = DC_LoaTS_Helper.getRaidTypes(this);
if (matchedTypes.length > 0)
{
// If there's a difficulty
if (typeof this.difficulty !== "undefined") {
if (this.difficulty >= 1 && this.difficulty <= 4) {
ret += RaidType.difficulty[this.difficulty] + " ";
}
else {
return "Filter does not match any raid difficulties";
}
}
// If there's a name
if (typeof this.name !== "undefined") {
// If we matched some raid types
var raids = [];
for (var i = 0; i < matchedTypes.length; i++)
{
// Grab this raid
raids.push(matchedTypes[i].fullName);
}
if (raids.length == 1) {
ret += raids[0];
}
else if (raids.length == 2) {
ret += raids[0] + " and " + raids[1];
}
else {
var tmp = raids.join(", ");
ret += tmp.substring(0, tmp.lastIndexOf(", ") + 2) + "and " + tmp.substring(tmp.lastIndexOf(", ") + 2);
}
}
}
else {
return "Filter does not match any raid types";
}
return ret;
}
});
RaidFilter.mangleNameForSearch = function(name) {
// Dirty pi/noir/sub hacks. Some raids are harder to locate because of similar names
var hacks = {
"pi": "^pi_", // Pi
"noir i": "^noir_", // Noir
"noir ii": "^noir2_", // Noir (2)
"noir iii": "^noir3_", // Noir (3)
"noir (i)": "^noir_", // Noir
"noir (ii)": "^noir2_", // Noir (2)
"noir (iii)": "^noir3_", // Noir (3)
"xsub": "x sub", // Kulnar-Xex Subjugator
"nsub": "x sub", // Kulnar-Xex Subjugator
"esub": "e sub" // Kulnar-Xex Elite Subjugator
},
found = name;
if (found) {
for (var hack in hacks) {
if (hacks.hasOwnProperty(hack) && name.toLowerCase() === hack) {
found = hacks[hack];
break;
}
}
found = found.replace(".", "\\.");
}
return found;
};
// Parameter text for this parser
RaidFilter.paramText = "[raidName] [raidDifficulty] [{state: stateParam}] [{size: sizeParam}] [{fs: fsParam}] [{os: osParam}] [{progress: progressParam}] [{zone: zoneParam}] [{age: ageParam}] [{count: countParam} [{page: pageParam}]]";
// Regex to parse number expressions
RaidFilter.numberExpressionPattern = /(<=?|>=?|==?|!=?)?\s*(\d+)\s*(\w\w?)?/;
/************************************/
/**** RaidFilterStyleParser Class ***/
/************************************/
// Class to parse raid link and style parameters
window.RaidFilterStyleParser = Class.create({
initialize: function(params)
{
// Split the params at the + that divides the filter from the style
var parts = params.split("\+");
// Parse the first part as a raid filter
if (parts.length >= 1)
{
this.raidFilter = new RaidMultiFilter(parts[0].trim());
}
// The second part as the link style
if (parts.length >= 2)
{
this.linkStyle = new RaidStyle(parts[1].trim());
}
// The third part as the message style
if (parts.length >= 3)
{
this.messageStyle = new RaidStyle(parts[2].trim());
}
// The fourth part as the image style
if (parts.length >= 4)
{
this.imageStyle = new RaidStyle(parts[3].trim());
}
},
// Whether or not a link style exists for this parser
hasLinkStyle: function()
{
return typeof this.linkStyle !== "undefined" && !this.linkStyle.isEmpty();
},
// Whether or not a message style exists for this parser
hasMessageStyle: function()
{
return typeof this.messageStyle !== "undefined" && !this.messageStyle.isEmpty();
},
// Whether or not an image style exists for this parser
hasImageStyle: function()
{
return typeof this.imageStyle !== "undefined" && !this.imageStyle.isEmpty();
},
// String describing this parser
toString: function()
{
var ret;
if (this.isValid())
{
ret = "Raids Matching <code>" + this.raidFilter.toString() + "</code> will have ";
if (this.hasLinkStyle())
{
ret += "\nLink Style: <code>" + this.linkStyle.toString() + "</code>";
}
if (this.hasMessageStyle())
{
ret += "\nMessage Style: <code>" + this.messageStyle.toString() + "</code>";
}
if (this.hasImageStyle())
{
ret += "\nImage Style: <code>" + this.imageStyle.toString() + "</code>";
}
}
else
{
ret = "Invalid Raid Filter Style Parser. Filter: " + (this.raidFilter?this.raidFilter.toString():"Not defined.");
}
return ret;
},
// Inject the styles from this parser into the page
injectStyles: function()
{
// Create a class name for this set of styles
this.className = "DCLH-RFSP-" + RaidFilterStyleParser._lastInjectedStyleId++;
var rulesText = "";
if (this.hasMessageStyle())
{
rulesText += "#kong_game_ui .chat_message_window ." + this.className + "{" + this.messageStyle.toString() + "}";
}
if (this.hasLinkStyle())
{
rulesText += "#kong_game_ui .chat_message_window ." + this.className + " a {" + this.linkStyle.toString() + "}";
}
if (this.hasImageStyle())
{
rulesText += "#kong_game_ui .chat_message_window ." + this.className + " img {" + this.imageStyle.toString() + "}";
}
// Locate/Create nodes
var head = document.getElementsByTagName('head')[0],
style = document.createElement('style'),
rules = document.createTextNode(rulesText);
// Style tag type
style.type = 'text/css';
// Browser dependencies
if(style.styleSheet)
{
style.styleSheet.cssText = rules.nodeValue;
}
else
{
style.appendChild(rules);
}
// Drop in the style node
head.appendChild(style);
},
// Check validity of the parser
isValid: function()
{
return typeof this.raidFilter !== "undefined" && this.raidFilter.isValid();
}
});
RaidFilterStyleParser._lastInjectedStyleId = 0;
/************************************/
/********** RaidLink Class **********/
/************************************/
// Represents and parses actual raid link
// Constructor is either
// new RaidLink(str)
// params:
// str - Any string containing a raid link. The rest of the string will be ignored.
//
// OR
//
// new RaidLink(id, hash, difficulty, raidTypeId)
// params:
// id - kv_raid_id - the unique id for this raid instance
// hash - kv_hash - the string hash for the raid
// difficulty - kv_difficulty - a number from 1 to 4 where 1 is normal, 4 is nightmare
// raidTypeId - kv_raid_boss - the unique key for the raid type
window.RaidLink = Class.create({
initialize: function()
{
Timer.start("RaidLink init");
// Capture variable args
var args = $A(arguments);
// If there's only one arg, must be link text
if (args.length == 1)
{
// Only 1 arg means that it must be a link URL
var linkURL = args[0];
if (typeof linkURL != "undefined")
{
// Correct for World Chat's broken links
linkURL = linkURL.replace("thousand-sunskv_", "thousand-suns?kv_");
// Execute our regular expression (defined below) against the message
// This checks to see if the message contained a LoaTS raid link
var match = RaidLink.linkPattern.exec(linkURL);
// If there was a raid link
if (match != null)
{
// Get Param String
var paramString = match[0];
// Remove junk
paramString = paramString.replace(/amp;/gi, "");
// Separate params
var params = paramString.split("\?")[1].split("&");
// Put the params together again into this object
var paramObj = {kv_raid_id: "", kv_hash: ""};
try
{
for (var i = 0; i < 5; i++)
{
// If the param wasn't empty
if (typeof params[i] == "string" && params[i] != "")
{
// Find the KV pairs
var paramKV = params[i].split("=");
// If there was something to split
if (typeof paramKV[1] == "string")
{
// Split off any extra junk
paramKV[1] = paramKV[1].split(/["'] /)[0].replace(/[^\w]/, "");
// Assign the KV pairs
paramObj[paramKV[0]] = paramKV[1];
}
}
}
}
catch(ex)
{
console.warn(ex);
}
// Extract the difficulty
this.difficulty = paramObj.kv_difficulty;
// Extract the hash
this.hash = paramObj.kv_hash;
// Extract the raid boss (RaidType.id)
this.raidTypeId = paramObj.kv_raid_boss;
// Extract the raid id
this.id = paramObj.kv_raid_id.replace(/\D/g, "");
}
}
else
{
console.warn("Attempted make a raid link from an undefined URL");
}
}
// If we got the 4 separate parts rather than a whole link
else if (args.length > 1)
{
// Extract the raid id
this.id = args[0];
// Extract the hash
this.hash = args[1];
// Extract the difficulty
this.difficulty = args[2];
// Extract the raid boss (RaidType.id)
this.raidTypeId = args[3];
}
else
{
console.error("Invalid parameters trying to create a RaidLink. ");
console.error(args);
}
Timer.stop("RaidLink init");
},
getUniqueKey: function()
{
return this.id + "_" + this.hash;
},
getRaid: function()
{
// If this link wasn't fully filled out
if (typeof this.raidTypeId == "undefined" || typeof this.difficulty == "undefined")
{
// Look up the same link in the storage
var savedLink = RaidManager.fetch(this);
// If there's a previous version of the link
if (typeof savedLink !== "undefined")
{
// Capture the save params into this link
this.raidTypeId = savedLink.raidTypeId;
this.difficulty = savedLink.difficulty;
}
}
// Look up the raid type
var raid = DC_LoaTS_Helper.raids[(this.raidTypeId||"").toLowerCase()];
// Return the raid type, or if we found nothing, a new empty raid type
return (typeof raid !== "undefined")? raid : new RaidType(this.raidTypeId);
},
getMatchedStyles: function()
{
// Possible styles to find and apply
var styleRet = {};
// Attempt to apply styles
try
{
// Iterate over all the styles
for (var key in DC_LoaTS_Helper.raidStyles)
{
// Get the style manager for the style
var styleManagers = DC_LoaTS_Helper.raidStyles[key];
// Grab the higher level info about the raid link
var raidData = RaidManager.fetch(this);
for (var i = 0; i < styleManagers.length; i++)
{
// Get the current style manager
var styleManager = styleManagers[i];
// If this link matches the filter
if (styleManager.raidFilter.matches(
{
age: (new Date()/1) - raidData.firstSeen,
difficulty: this.difficulty,
fs: this.getRaid().getFairShare(this.difficulty),
os: this.getRaid().getOptimalShare(this.difficulty),
name: this.getRaid().getSearchableName(),
state: RaidManager.fetchState(this),
size: this.getRaid().size,
zone: this.getRaid().zone
})
)
{
for (var property in styleManager)
{
if ((property.indexOf("Style") > 0 || property.indexOf("className") > -1) && typeof styleManager[property] !== "undefined")
{
if (typeof styleRet[property] === "undefined")
{
styleRet[property] = "";
}
styleRet[property] += " " + styleManager[property];
}
}
}
}
}
}
catch(ex)
{
console.warn("Error while finding styles for link:");
console.warn(this);
console.warn(ex);
}
return styleRet;
},
// Takes in a format returns a formatted text for this link
getFormattedRaidLinkText: function(messageFormat)
{
try
{
if (this.isValid())
{
// Grab the raid type
var raid = this.getRaid();
// Start with just an empty template
var newMessage = messageFormat;
// Grab the link state
var linkState = RaidManager.fetchState(this);
// Fill in the basic data to the template
newMessage = newMessage.replace(/{id}/gi, this.id);
newMessage = newMessage.replace(/{line}/gi, "<br>");
newMessage = newMessage.replace(/{size}/gi, raid.size);
newMessage = newMessage.replace(/{stat}/gi, raid.stat);
newMessage = newMessage.replace(/{health}/gi, raid.getHealthText(this.difficulty));
newMessage = newMessage.replace(/{difficulty}/gi, RaidType.difficulty[this.difficulty]);
newMessage = newMessage.replace(/{diff}/gi, RaidType.shortDifficulty[this.difficulty]);
newMessage = newMessage.replace(/{zone}/gi, raid.zone);
newMessage = newMessage.replace(/{name}/gi, raid.fullName);
newMessage = newMessage.replace(/{short-name}/gi, raid.shortName);
newMessage = newMessage.replace(/{shorter-name}/gi, raid.colloqName);
newMessage = newMessage.replace(/{shortest-name}/gi, raid.shortestName);
newMessage = newMessage.replace(/{time}/gi, raid.getTimeText());
newMessage = newMessage.replace(/{(?:fs|fair|fairshare)}/gi, raid.getFairShareText(this.difficulty));
newMessage = newMessage.replace(/{(?:os|opt|optimal|ofs|target)}/gi, raid.getTargetDamageText(this.difficulty));
newMessage = newMessage.replace(/{cache-state}/gi, linkState.text);
newMessage = newMessage.replace(/{(?:cache-state-nice|state|status)}/gi, linkState.niceText);
newMessage = newMessage.replace(/{(?:cache-state|state|status)-short}/gi, linkState.shortText);
newMessage = newMessage.replace(/{visited}/gi, (RaidManager.STATE.equals(linkState, RaidManager.STATE.VISITED) || RaidManager.STATE.equals(linkState, RaidManager.STATE.COMPLETED))?RaidManager.STATE.VISITED.niceText:"");
newMessage = newMessage.replace(/{visited-short}/gi, (RaidManager.STATE.equals(linkState, RaidManager.STATE.VISITED) || RaidManager.STATE.equals(linkState, RaidManager.STATE.COMPLETED))?RaidManager.STATE.VISITED.shortText:"");
if (typeof linkState == "undefined" || linkState.text == "undefined")
{
console.warn("Bad Link State");
console.warn(linkState);
console.warn(this);
}
// FS has special properties, so parse it differently
var fsMatch = /{fs([^}]*)}/.exec(newMessage);
// How many times we matched an FS
var fsMatchCount = 0;
// If there were any FS calls
while (fsMatch != null)
{
try
{
// Don't get in an infite loop
if (fsMatchCount >= 5)
{
console.warn("Can only match 5 {fs} with math")
holodeck.activeDialogue().raidBotMessage("Can only match 5 {fs} with math while formatting");
break;
}
fsMatchCount++;
// Replace FS and do the math
newMessage = newMessage.replace(/{fs[^}]*}/,
DC_LoaTS_Helper.prettyFormatNumber(eval(raid.getFairShare(this.difficulty)+fsMatch[1])));
// Find the next FS
var fsMatch = /\{fs([^\}]*)\}/.exec(newMessage);
}
catch (ex)
{
console.error("Error while formatting - Failed to process FS: " + fsMatch[0]);
console.error(ex);
}
}
return newMessage.trim();
}
else
{
console.warn("Tried to get formatted raid link text from invalid link");
return "Legacy of a Thousand Suns Raid: [" + RaidType.difficulty[this.difficulty] + "] Unknown Raid (id: " + this.raidTypeId + ")"
}
}
catch(ex)
{
console.warn("Error encountered in RaidLink.getFormattedRaidLinkText");
console.warn(ex);
return "Legacy of a Thousand Suns Raid: [" + RaidType.difficulty[this.difficulty] + "] Error Formatting Raid (id: " + this.raidTypeId + ")"
}
},
// Gets the simplest text possible based on the storage messagen and link formats
getSimpleText: function()
{
if (this.isValid())
{
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Retrieve the anchor tag format
var linkFormat = DC_LoaTS_Helper.getLinkFormat();
// Start with just an empty template
var newMessage = messageFormat;
// Grab the link state
var linkState = RaidManager.fetchState(this);
// Grab the raid type
var raid = this.getRaid();
// Fill in the basic data to the template
newMessage = newMessage.replace(/{id}/gi, this.id);
newMessage = newMessage.replace(/{size}/gi, raid.size);
newMessage = newMessage.replace(/{stat}/gi, raid.stat);
newMessage = newMessage.replace(/{health}/gi, raid.getHealthText(this.difficulty));
newMessage = newMessage.replace(/{difficulty}/gi, RaidType.difficulty[this.difficulty]);
newMessage = newMessage.replace(/{diff}/gi, RaidType.shortDifficulty[this.difficulty]);
newMessage = newMessage.replace(/{zone}/gi, raid.zone);
newMessage = newMessage.replace(/{name}/gi, raid.fullName);
newMessage = newMessage.replace(/{short-name}/gi, raid.shortName);
newMessage = newMessage.replace(/{shorter-name}/gi, raid.colloqName);
newMessage = newMessage.replace(/{shortest-name}/gi, raid.shortestName);
newMessage = newMessage.replace(/{time}/gi, raid.getTimeText());
newMessage = newMessage.replace(/{fs}/gi, raid.getFairShareText(this.difficulty));
newMessage = newMessage.replace(/{target}/gi, raid.getTargetDamageText(this.difficulty));
newMessage = newMessage.replace(/{optimal}/gi, raid.getTargetDamageText(this.difficulty));
newMessage = newMessage.replace(/{ofs}/gi, raid.getTargetDamageText(this.difficulty));
newMessage = newMessage.replace(/{os}/gi, raid.getTargetDamageText(this.difficulty));
newMessage = newMessage.replace(/{cache-state}/gi, linkState.text);
newMessage = newMessage.replace(/{cache-state-nice}/gi, linkState.niceText);
newMessage = newMessage.replace(/{cache-state-short}/gi, linkState.shortText);
newMessage = newMessage.replace(/{visited}/gi, (RaidManager.STATE.equals(linkState, RaidManager.STATE.VISITED) || RaidManager.STATE.equals(linkState, RaidManager.STATE.COMPLETED))?RaidManager.STATE.VISITED.niceText:"");
newMessage = newMessage.replace(/{visited-short}/gi, (RaidManager.STATE.equals(linkState, RaidManager.STATE.VISITED) || RaidManager.STATE.equals(linkState, RaidManager.STATE.COMPLETED))?RaidManager.STATE.VISITED.shortText:"");
// Remove fields we don't want
newMessage = newMessage.replace(/{line}/gi, "");
newMessage = newMessage.replace(/{image}/gi, "");
newMessage = newMessage.replace(/<[^>]+>/gi, "");
// FS has special properties, so parse it differently
var fsMatch = /{fs([^}]*)}/.exec(newMessage);
// How many times we matched an FS
var fsMatchCount = 0;
// If there were any FS calls
while (fsMatch != null)
{
try
{
// Don't get in an infite loop
if (fsMatchCount >= 5)
{
console.warn("Can only match 5 {fs} with math")
holodeck.activeDialogue().raidBotMessage("Can only match 5 {fs} with math while formatting");
break;
}
fsMatchCount++;
// Replace FS and do the math
newMessage = newMessage.replace(/{fs[^}]*}/,
DC_LoaTS_Helper.prettyFormatNumber(eval(raid.getFairShare(this.difficulty)+fsMatch[1])));
// Find the next FS
var fsMatch = /\{fs([^\}]*)\}/.exec(newMessage);
}
catch (ex)
{
console.error("Error while formatting - Failed to process FS: " + fsMatch[0]);
console.error(ex);
}
}
return newMessage.trim();
}
else
{
console.warn("Tried to get simple raid link text from invalid link");
return "Legacy of a Thousand Suns Raid: [" + RaidType.difficulty[this.difficulty] + "] Unknown Raid (id: " + this.raidTypeId + ")";
}
},
// Takes in a message format and a link format and returns a ready to inject link
getFormattedRaidLink: function(messageFormat, linkFormat)
{
Timer.start("getFormattedRaidLink");
// If there was no message format, look it up
if (typeof messageFormat === "undefined")
{
messageFormat = DC_LoaTS_Helper.getMessageFormat();
}
// If there was no link format, look it up
if (typeof linkFormat === "undefined")
{
linkFormat = DC_LoaTS_Helper.getLinkFormat();
}
// Get the basics of the message
var newMessage = this.getFormattedRaidLinkText(messageFormat).trim();
try
{
// Get the text of the message without the image
var noImage = newMessage.replace(/{image}/gi, "").replace(/<[^>]+>/gi, "").trim();
// Define the image tag
var imageTag = this.getFormattedImageTag();
// Index of the image tag
var imageIndex = newMessage.indexOf("{image}");
// If {image} is in the middle, just lump it in with the text
if (imageIndex == -1 || (imageIndex > 0 && imageIndex < newMessage.length - "{image}".length))
{
newMessage = newMessage.replace(/{image}/gi, imageTag).trim();
newMessage = linkFormat.replace(/{text}/gi, newMessage);
}
// If {image} is at the beginning or end, put it in it's own anchor, for aesthetics
else
{
// At the beginning
if (newMessage.indexOf("{image}") == 0)
{
newMessage = linkFormat.replace(/{text}/gi, imageTag).replace(/class=\"/, "class=\"game_icon_link ") + " " + linkFormat.replace(/{text}/gi, newMessage);
}
// At the end
else
{
newMessage = linkFormat.replace(/{text}/gi, newMessage) + " " + linkFormat.replace(/{text}/gi, imageTag);
}
// Remove images from the message
newMessage = newMessage.replace(/{image}/gi, "");
}
newMessage = newMessage.replace(/{difficulty}/gi, RaidType.difficulty[this.difficulty]);
newMessage = newMessage.replace(/{text-no-image}/gi, noImage);
newMessage = newMessage.replace(/{url}/gi, this.getURL());
newMessage = "<span class=\"raidMessage\">" + newMessage + "</span>";
// Get the styles for this link
// var styles = this.getMatchedStyles();
// newMessage = newMessage.replace(/{linkStyle}/gi, styles.linkStyle||"");
}
catch(ex)
{
console.warn("Error encountered in RaidLink.getFormattedRaidLink");
console.warn(ex);
}
Timer.stop("getFormattedRaidLink");
return newMessage;
},
// Get the raid name
getName: function()
{
return this.getRaid().fullName;
},
// Get the difficulty text of the raid
getDifficultyText: function()
{
return RaidType.difficulty[this.difficulty];
},
// Generate a url for this raid
getURL: function()
{
var raidURL = "http://www.kongregate.com/games/5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp";
if (typeof this.raidTypeId != "undefined")
{
raidURL += "&kv_raid_boss=" + this.raidTypeId;
}
if (typeof this.difficulty != "undefined")
{
raidURL += "&kv_difficulty=" + this.difficulty;
}
// It's easier to inspect the link if the easier to read bits are first
raidURL += "&kv_raid_id=" + this.id + "&kv_hash=" + this.hash;
return raidURL;
},
// Get the raid image url, or default to LoaTS icon
getImageSRC: function()
{
// Assume default
var imageSRC = RaidLink.defaultImageSRC;
// If we have a raidTypeId
if (typeof this.raidTypeId !== "undefined")
{
// Locate the offsite image
imageSRC = DC_LoaTS_Properties.lotsCDNUrl + "images/bosses/post/" + this.raidTypeId + "_1.jpg";
}
return imageSRC;
},
// Get the fully formatted <img> tag for this raid
getFormattedImageTag: function()
{
// Get the image src
var imageSRC = this.getImageSRC();
// Fill in image SRC
var imageTag = RaidLink.defaultImageFormat.replace("{imageSRC}", imageSRC);
return imageTag;
},
// Generate a param array for this link
getParamArray: function()
{
return [this.id, this.hash, this.difficulty, this.raidTypeId];
},
// Is this a valid link (to the best of our knowledge)
isValid: function()
{
// All a link needs to be valid is an id and a hash
return typeof this.id != "undefined" && typeof this.hash != "undefined" && this.id != "" && this.hash != "";
},
// Necessary for reloading the iFrame
toQueryParams: function() {
return {
kv_action_type: "raidhelp",
kv_raid_boss: this.raidTypeId,
kv_difficulty: this.difficulty,
kv_raid_id: this.id,
kv_hash: this.hash
};
}
});
// Parameter text for this parser
RaidLink.paramText = "url";
// Define the regular expression (regex) that tells us if a link is a raid link or not
RaidLink.linkPattern = /(?:https?:\/\/www\.kongregate\.com)?(?:\/games\/)?(?:5thPlanetGames\/legacy-of-a-thousand-suns)?(?!\?4217\-op)\?([^\s,"]*)\b/i;
// Define a regular expresson to catch busted links
RaidLink.backupLinkReplacementPattern = /.?\[?"?http:\/\/.*?\?4217\-op","5thPlanetGames\/legacy\-of\-a\-thousand\-suns\?.*?(?:\u2026|\u8320|…|\.\.\.|\]|"|')*$/i;
// Fallback image url if we can't get the provided one
RaidLink.defaultImageFormat = '<img class="raidIcon" src="{imageSRC}" onerror="RaidLink.fixBrokenImage.apply(this);" />';
// Fallback image url if we can't get the nice one
RaidLink.defaultImageSRC = "http://cdn2.kongregate.com/game_icons/0033/2679/i.gif?4217-op";
// Fallback message format
RaidLink.defaultMessageFormat = "{image} {visited-short} {diff} [{size}-{stat}-{fs}-{os}] {shorter-name} ({id})";
// Old link format
RaidLink.defaultLinkFormat_v1 = "<a class=\"raidLink raidDiff{difficulty}\" onclick=\"return DC_LoaTS_Helper.raidLinkClick(event, '{url}');\" href=\"{url}\" title=\"{text-no-image}\">{text}</a>";
// Fallback link format
RaidLink.defaultLinkFormat_v2 = "<a style=\"{linkStyle}\" class=\"raidLink raidDiff{difficulty}\" onclick=\"return DC_LoaTS_Helper.raidLinkClick(event);\" onmousedown=\"return DC_LoaTS_Helper.raidLinkMouseDown(event);\" href=\"{url}\" title=\"{text-no-image}\">{text}</a>";
// Fix broken images, an inline handler
RaidLink.fixBrokenImage = function()
{
// Get the relevant raid link
var raidLink = new RaidLink(this.parentNode.href);
// First time failed, check for alternate fail names
if (this.src === DC_LoaTS_Properties.lotsCDNUrl + "images/bosses/post/" + raidLink.raidTypeId + "_1.jpg" && this.src !== RaidLink.defaultImageSRC)
{
var src = DC_LoaTS_Properties.lotsCDNUrl + "images/bosses/";
switch(raidLink.raidTypeId)
{
// baseURL + "post/" + raidLink.raidTypeId + ".jpg";
case "celebration_enhancer_1":
case "weiqi_game_1":
case "kulnarxex_scout_ships_1":
case "cow_abduction_1":
case "mutated_spacepox_1":
src += "post/" + raidLink.raidTypeId + ".jpg";
break;
// Raids with diviating image paths
case "wr_space_pox":
src += "post/space_pox_1.jpg";
break;
case "dule_warmaster_1":
case "dule_warmaster":
src += "post/dule_1.jpg";
break;
case "hultex_quibberath":
src += "post/hultex_1.jpg";
break;
case "warden_ramiro":
src += "post/ramiro_1.jpg";
break;
case "crimzo_the_killer_clown":
src += "post/crimzo_1.jpg";
break;
case "kulnarxex_subjugator_1":
case "kulnarxex_elite_subjugator_1":
case "elite_kulnarxex_elite_subjugator":
src += "post/kulnarxex_subjugator_1.jpg";
break;
case "elite_birthday_cake_of_doom":
src += "post/birthday_cake_of_doom_1.jpg";
break;
case "elite_centurian_commander":
src += "post/commander_1.jpg";
break;
case "elite_master_hao":
src += "post/master_hao_1.jpg";
break;
case "elite_bashan":
src += "post/bashan_1.jpg";
break;
// These are ones that we've not found alternate images for except for the raid list image
case "pinatas_revenge1":
case "king_krandar1":
case "sultan_shrakzan1":
case "contest_winner1":
case "thyestean_banquet1":
case "rogue_terraformer1":
case "ruins_of_the_forgotten1":
case "rak_thun_eviscipod1":
case "contest_winner1":
case "temynx_parasite1":
case "robot_uprising1":
case "besalaad_exhibit_rampage1":
src += raidLink.raidTypeId + ".$$repl$$";
src = src.replace("1.$$repl$$", "_small.jpg");
break;
// Raids with image paths that don't fit known patterns
case "invaders_from_dimension_b":
src += "Invaders_from_Dimension_B1_small.png";
break;
case "training_sim1":
src += "training_sim_small2.jpg";
break;
case "predatory_constellation":
src += "predatory_constellation_small.png"; //PNG!
break;
case "attack_of_the_gourds":
src += "Attack_of_the_Gourds_small.jpg";
break;
case "sun_egg":
src += "the_sun_egg_small.jpg";
break;
case "hate":
src += "the_hate_walker_small.jpg";
break;
case "hukkral_war_crawler":
src += "huk-kral_war_crawler_small.jpg";
break;
case "elite_titanomachy":
src += "z27_boss_small.jpg"; //omg what 5pg?
break;
case "giant_kwelshax":
case "elite_giant_kwelshax":
src += "giant_kwelshax_small.jpg";
break;
case "purple_lion":
case "kang":
case "tourniquet":
case "flora":
case "talia":
case "myrmexidaks":
case "tyraness_guard":
case "sian_dragonfly_1":
case "lady_victoria_ashdown_1":
case "rampaging_rackalax_1":
case "the_tyraness":
case "the_mega_mimes":
case "the_neon_knights":
case "the_gamma_hammers":
case "the_chem_runners":
case "kulnar_xex_shock_trooper_1":
case "kulnar_xex_battle_station_1":
case "kulnarxex_bombarder_1":
case "schism":
case "trouble_in_tokyo":
case "cerebral_monster_mech":
case "ship_pinata":
case "dimetrodon_riot":
default:
src += raidLink.raidTypeId + "_small.jpg";
}
this.src = src;
}
// Second time failed, switch to default
else if (this.src != RaidLink.defaultImageSRC)
{
this.src = RaidLink.defaultImageSRC;
}
// Default failed, no image
else
{
this.src="";
}
};
/************************************/
/**** RaidLinkstateParser Class *****/
/************************************/
window.RaidLinkstateParser = Class.create({
initialize: function(params)
{
var paramsParts = params.trim().replace(/\s+/g, " ").split(" ");
var ret;
if (paramsParts.length != 1 && paramsParts.length != 2)
{
ret.success = false;
ret.statusMessage = "Could not understand <code>" + params + "</code>. Should have one or two parameters: url and optionally state.";
}
else
{
this.raidLink = new RaidLink(paramsParts[0]);
this.state;
var raidLinkIndex = 0;
if (!this.raidLink.isValid())
{
if (paramsParts.length == 2)
{
this.raidLink = new RaidLink(paramsParts[1]);
raidLinkIndex = 1;
}
}
if (!this.raidLink.isValid())
{
ret.success = false;
ret.statusMessage = "Could not parse raid link from <code>" + params + "</code>";
}
else
{
if (paramsParts.length == 2)
{
this.state = paramsParts[raidLinkIndex*-1+1];
}
DCDebug("LinkState: ");
DCDebug("RaidLink for " + this.raidLink.getName());
DCDebug("State Param: " + this.state);
}
}
return ret;
},
getName: function()
{
return (typeof this.raidLink != "undefined")?this.raidLink.getName():"undefined";
},
getState: function()
{
return this.state;
},
isValid: function()
{
return this.raidLink != "undefined" && this.raidLink.isValid();
}
});
// Parameter text for this parser
RaidLinkstateParser.paramText = "url [newState]";
/************************************/
/******** RaidManager Utility *******/
/************************************/
// The RaidManager utility stores information about
// instances of raids that the user has seen/joined
// It is not actually a class, but rather just a
// collection of static functions
window.RaidManager = {
// STATE Enum
STATE: {
// The ids below are critical. Please do not ever change the ids, ever.
// If you create custom states, please set the ids of the new states above 20
// to allow for new native ids in the future.
// Every state with id below 0 is an unknown state
// Note: Priority may be removed or altered in future releases
//TODO: Maybe add an equals method to these in init? RaidManager.STATE.equals is a pain
UNSEEN: {id:0, text: "unseen", niceText: "Unseen", shortText: "(NEW)", priority: 0},
//DB_ONLY: {id:1, text: "db_only", niceText: "From Database", shortText: "(DB)", priority: 1},
SEEN: {id:2, text: "seen", niceText: "Seen", shortText: "(S)", priority: 2},
VISITED: {id:3, text: "visited", niceText: "Visited", shortText: "(V)", priority:5},
IGNORED: {id:4, text: "ignored", niceText: "Ignored", shortText: "(X)", priority:10},
COMPLETED: {id:5, text: "completed", niceText: "Completed", shortText: "(!)", priority:99},
// Takes in a state.text or a state.id and looks up the corresponding state
// Returns a STATE if one is found, undefined otherwise
/*public static STATE*/
valueOf: function(stateParam)
{
// Set up the cache for ids, if it's not already set up
if (!RaidManager.STATE.cacheById) {
RaidManager.STATE.cacheById = {};
for (var stateKey in this)
{
var item = this[stateKey];
// Ignore functions. Check for the state.id or state.text to equal the passed in value
if (item && item !== "function")
{
RaidManager.STATE.cacheById[item.id] = item;
}
}
}
// State we found
var state;
// If the parameter was a string
if (typeof stateParam === "string")
{
// lowercase it just in case
stateParam = stateParam.toLowerCase();
}
// Otherwise return from the id cache
else
{
return RaidManager.STATE.cacheById[stateParam];
}
// Iterate over all valid states
for (var stateKey in this)
{
// Ignore functions. Check for the state.id or state.text to equal the passed in value
if (this[stateKey] && typeof this[stateKey] !== "function"
&& (this[stateKey].id == stateParam || this[stateKey].text == stateParam)
)
{
// If we found a state that matches, capture it and break the loop
state = this[stateKey];
break;
}
else if (typeof this[stateKey] === "undefined")
{
console.warn("Invalid State:", stateKey);
}
}
// Return the state we found, or undefined if we didn't find one
return state;
},
// Returns the UNKNOWN state. Not listed as enumerable on purpose to avoid listing when iterated over
/*public static STATE*/
getUnknownState: function()
{
return {id: -1, text: "unknown", niceText: "Unknown", shortText: "(?)", priority: -1};
},
// Compares two states and returns true if they are equal, false otherwise
/*public static boolean*/
equals: function(state1, state2)
{
return (state1 == state2 ||
(typeof state1 != "undefined" && typeof state2 != "undefined" &&
(
(typeof state1.id != "undefined" && state1.id == state2.id)
||
(typeof state1.text != "undefined" && state1.text == state2.text)
)
)
);
}
},
// Initialize the raid manager. MUST BE INITIALIZED before it can be used
/*public static void*/
init: function()
{
Timer.start("RaidManager init");
// Load up everything that's been stored
var rawRaidStorage = GM_getValue(DC_LoaTS_Properties.storage.raidStorage);
// Make sure the raid storage actually finds what it's supposed to
if (typeof rawRaidStorage == "undefined" || rawRaidStorage.length == 0)
{
// Otherwise default to empty
rawRaidStorage = "{}";
}
try
{
RaidManager.raidStorage = JSON.parse(rawRaidStorage);
}
catch(ex)
{
try
{
holodeck.activeDialogue.raidBotMessage("Raid link storage has become corrupted and will now be cleared. RaidBot apologizes for the inconvenience. If this happens frequently, please report it.");
}
catch (ex2)
{
console.warn("Could not display raid bot message to user: Raid link storage has become corrupted and will now be cleared. RaidBot apologizes for the inconvenience. If this happens frequently, please report it.");
console.warn(ex2);
}
console.warn("rawRaidStorage was not able to be parsed. For debugging purposes, it will now been quarantined, and the normal raid link storage will be cleared.");
console.warn(ex)
// Quarantine the current raid storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage + DC_LoaTS_Properties.storage.quarantine, rawRaidStorage);
// Clear the existing corrupted storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, "{}");
RaidManager.raidStorage = {};
}
// Count raids we're about to delete
var clearCount = 0;
// Iterate over all stored data
for (var key in RaidManager.raidStorage)
{
// Grab this raidData item
var raidData = RaidManager.raidStorage[key];
// The raidData item is actually RaidLink, but storage doesn't retain methods. Add back in the methods
Object.extend(raidData.raidLink, RaidLink.prototype);
// Clear items that have been stored for more than their total raid length
if ((new Date()/1) - raidData.firstSeen > raidData.raidLink.getRaid().time * 60*60*1000)
{
delete RaidManager.raidStorage[key];
clearCount++;
}
}
// If anything was cleared, log it for posterity
if (clearCount > 0)
{
console.info("Cleared " + clearCount + " raid" + ((clearCount != 1)?"s":"") + " for being too old");
}
// Store back into the DB any modifications we made
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify(RaidManager.raidStorage));
// Update raid preferences
RaidManager.updateRaidPreferences();
Timer.stop("RaidManager init");
},
// Update the script with the user's preferences
// This handles the customized bits of text loaded from memory
/*public static void*/
updateRaidPreferences: function()
{
// Load up the raid preferences
var raidPrefs = GM_getValue(DC_LoaTS_Properties.storage.raidPrefs);
// Make sure the raid prefs are populated
if (typeof raidPrefs != "undefined" && raidPrefs.length > 0)
{
RaidManager.raidPrefs = JSON.parse(raidPrefs);
// Iterate over every preference type
for (var key in RaidManager.raidPrefs)
{
var pref = RaidManager.raidPrefs[key];
// Iterate over every variable to change in that preference.
for (var variable in pref)
{
try
{
eval(variable + "=" + pref[variable]);
}
catch (ex)
{
console.warn("Could not update `" + variable + "` to be \"" + pref[variable] + "\".")
console.warn(ex);
}
}
}
}
},
// Clear everything from storage
/*public static void*/
clear: function(raidList)
{
Timer.start("clear");
// If there was no list passed in, clear them all
if (typeof raidList == "undefined")
{
// Replace the entire stored dataset with an empty object
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify({}));
// Also clear the memcache
RaidManager.raidStorage = {};
}
else
{
// Iterate over everything we need to delete
for (var i = 0; i < raidList.length; i++)
{
delete RaidManager.raidStorage[raidList[i].getUniqueKey()];
}
// Store the storage data back into the browser storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify(RaidManager.raidStorage));
}
// Reset the CConoly query time so all the raids can be loaded again
CConolyAPI.setLastQueryTime(0);
// Reset all the links to NEW
DC_LoaTS_Helper.updatePostedLinks();
Timer.stop("clear");
},
// Store a raid link and the state of the link
// RaidManager.raidStorage is a write-through cache, and the storage is volatile
// So we have to look up the storage every time we store. This keeps us in sync with
// other windows of the same browser running the game simultaneously
/*public static void*/
store: function(raidLink, state)
{
Timer.start("store");
// If the link is valid
if (raidLink.isValid())
{
Timer.start("store > loading hardRaidStorage");
// Load up the storage object
var hardRaidStorage = JSON.parse(GM_getValue(DC_LoaTS_Properties.storage.raidStorage));
Timer.stop("store > loading hardRaidStorage");
// Attempt to load the passed in raid link
var raidData = hardRaidStorage[raidLink.getUniqueKey()];
// Lookup the current state
var currentState;
var containedInLocalDB = true;
if (typeof raidData != "undefined")
{
// If there is a stateId, use that first
if (typeof raidData.stateId != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.stateId);
}
// If there is an old-style state, use that second
else if (typeof raidData.state != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.state.text);
}
}
// If we couldn't find the current state, set it to UNSEEN
if (typeof currentState === "undefined")
{
currentState = RaidManager.STATE.UNSEEN;
containedInLocalDB = false;
}
// If we weren't provided a state param, set it to the current state
if (typeof state === "undefined")
{
state = currentState;
}
// Keep track of whether or not this link needs to be updated elsewhere
var shouldUpdateAllLinks = false;
// If we've never seen this link before
if (typeof raidData == "undefined")
{
// Create a new storage container for it, and wrap it
raidData = {raidLink: raidLink, stateId: state.id, firstSeen: new Date()/1}
// Place this object into the storage
hardRaidStorage[raidLink.getUniqueKey()] = raidData;
}
// Two unseens upgrade to seen if the link was already in the DB
else if (containedInLocalDB
&&
RaidManager.STATE.equals(state, RaidManager.STATE.UNSEEN)
&&
RaidManager.STATE.equals(currentState, RaidManager.STATE.UNSEEN)
)
{
// Set the new state
raidData.stateId = RaidManager.STATE.SEEN.id;
// Changed state
shouldUpdateAllLinks = true;
}
// If we have seen this link before, change the links state if necessary
else if (!RaidManager.STATE.equals(currentState, state))
{
// Set the new state
raidData.stateId = state.id;
// Since we changed state, need to update all those links
shouldUpdateAllLinks = true;
}
else
{
// Just double check to make sure the state id has been set
// Helps convert old states to new ones
raidData.stateId = currentState.id;
}
// If we should report dead raids and this one is dead and it hasn't been reported yet
if (DC_LoaTS_Helper.getPref("ReportDeadRaids", true) && RaidManager.STATE.equals(state, RaidManager.STATE.COMPLETED) && !raidData.reported) {
raidData.reported = true;
DC_LoaTS_Helper.reportDead(raidLink);
}
// Update the lastSeen time of the link
raidData.lastSeen = new Date()/1;
Timer.start("store > storing hardRaidStorage");
// Store the storage data back into the browser storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify(hardRaidStorage));
Timer.stop("store > storing hardRaidStorage");
Timer.start("store > extending raid links");
//TODO Work around this (update: not really that big of a deal based on timer data)
// Gotta have the methods attached to the objects
for (var key in hardRaidStorage)
{
// If we're missing methods, add them
if (typeof hardRaidStorage[key].raidLink.getRaid != "function")
{
Object.extend(hardRaidStorage[key].raidLink, RaidLink.prototype);
}
}
Timer.stop("store > extending raid links");
// Update the cache
RaidManager.raidStorage = hardRaidStorage;
// If we found a reason to update all versions of this link
if (shouldUpdateAllLinks)
{
// Update the posted links
DC_LoaTS_Helper.updatePostedLinks(raidLink);
}
}
else
{
console.warn("Did not store because link was invalid or storage unavailable");
}
Timer.stop("store");
},
// Store a list of raid links and the state of the links
// RaidManager.raidStorage is a write-through cache, and the storage is volatile
// So we have to look up the storage every time we store. This keeps us in sync with
// other windows of the same browser running the game simultaneously
/*public static void*/
storeBulk: function(raidLinks, state)
{
Timer.start("store bulk");
// Load up the storage object
Timer.start("store > loading hardRaidStorage");
var hardRaidStorage = JSON.parse(GM_getValue(DC_LoaTS_Properties.storage.raidStorage));
Timer.stop("store > loading hardRaidStorage");
// Declare a bunch of vars. Don't forget there's no such thing as block scope
var raidLink, raidData, currentState, newState, containedInLocalDB, shouldUpdateAllLinks;
// Capture all the valid links we're actually going to store
var outboundLinks = [];
for (var i = 0; i < raidLinks.length; i++) {
raidLink = raidLinks[i];
state = undefined;
// Valid link?
if (raidLink.isValid()) {
// Remember the valid ones
outboundLinks.push(raidLink);
// Attempt to load the passed in raid link
raidData = hardRaidStorage[raidLink.getUniqueKey()];
// Lookup the current state
containedInLocalDB = true;
currentState = null;
if (raidData)
{
// If there is a stateId, use that first
if (raidData.stateId)
{
currentState = RaidManager.STATE.valueOf(raidData.stateId);
}
// If there is an old-style state, use that second
else if (raidData.state)
{
currentState = RaidManager.STATE.valueOf(raidData.state.text);
}
}
// If we couldn't find the current state, set it to UNSEEN
if (!currentState)
{
currentState = RaidManager.STATE.UNSEEN;
containedInLocalDB = false;
}
// Set the new state. It's either going to be the new parameter state or the existing state
newState = state || currentState;
// Keep track of whether or not this link needs to be updated elsewhere
shouldUpdateAllLinks = false;
// If we've never seen this link before
if (!raidData)
{
// Create a new storage container for it, and wrap it
raidData = {raidLink: raidLink, stateId: newState.id, firstSeen: new Date()/1}
// Place this object into the storage
hardRaidStorage[raidLink.getUniqueKey()] = raidData;
}
// Two unseens upgrade to seen if the link was already in the DB
else if (containedInLocalDB
&&
RaidManager.STATE.equals(newState, RaidManager.STATE.UNSEEN)
&&
RaidManager.STATE.equals(currentState, RaidManager.STATE.UNSEEN)
)
{
// Set the new state
raidData.stateId = RaidManager.STATE.SEEN.id;
// Changed state
shouldUpdateAllLinks = true;
}
// If we have seen this link before, change the links state if necessary
else if (!RaidManager.STATE.equals(currentState, newState))
{
// Set the new state
raidData.stateId = newState.id;
// Since we changed state, need to update all those links
shouldUpdateAllLinks = true;
}
else
{
// Just double check to make sure the state id has been set
// Helps convert old states to new ones
raidData.stateId = currentState.id;
}
// Update the lastSeen time of the link
raidData.lastSeen = new Date()/1;
// If we should report dead raids and this one is dead and it hasn't been reported yet
if (DC_LoaTS_Helper.getPref("ReportDeadRaids", true) && RaidManager.STATE.equals(newState, RaidManager.STATE.COMPLETED) && !raidData.reported) {
raidData.reported = true;
DC_LoaTS_Helper.reportDead(raidLink);
}
}
} // End for iterating over the links
Timer.start("store > storing hardRaidStorage");
// Store the storage data back into the browser storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify(hardRaidStorage));
Timer.stop("store > storing hardRaidStorage");
Timer.start("store > extending raid links");
//TODO Work around this (update: not really that big of a deal based on timer data)
// Must have the methods attached to the objects
for (var key in hardRaidStorage)
{
// If we're missing methods, add them
if (typeof hardRaidStorage[key].raidLink.getRaid !== "function")
{
Object.extend(hardRaidStorage[key].raidLink, RaidLink.prototype);
}
}
Timer.stop("store > extending raid links");
// Update the cache
RaidManager.raidStorage = hardRaidStorage;
// Update the posted links.
DC_LoaTS_Helper.updatePostedLinks();
Timer.stop("store bulk");
return outboundLinks;
},
// Lookup RaidData for a given link
/*public static RaidData*/
fetch: function(raidLink)
{
Timer.start("fetch");
// Declare the return var
var raidData;
if (raidLink.isValid())
{
// Attempt to load the passed in raid link
raidData = RaidManager.raidStorage[raidLink.getUniqueKey()];
// If the link is in storage
if (raidData && typeof raidData.raidLink.getRaid !== "function")
{
// Add in the functions that you expect to see on those objects
Object.extend(raidData.raidLink, RaidLink.prototype);
}
}
Timer.stop("fetch");
// Return what we found or undefined
return raidData;
},
// Returns the raid storage
/*public static Object*/
fetchStorage: function()
{
// Return the whole thing
return RaidManager.raidStorage;
},
// Returns the all stored raid links
/*public static Array*/
fetchAll: function()
{
// Holder for raid links
var raidLinks = new Array();
// For everything in storage
for (var key in RaidManager.raidStorage)
{
// Pull the raid data
var raidData = RaidManager.raidStorage[key];
// It should exist, but just in case
if (typeof raidData != "undefined")
{
// Collect the links into the array
raidLinks.push(raidData.raidLink);
}
}
// Return all the links
return raidLinks;
},
// Returns the state of a link, or UNSEEN if the link hasn't been stored
/*public static STATE*/
fetchState: function(raidLink)
{
// Attempt to load the raid link
var raidData = RaidManager.fetch(raidLink);
// If the raid link has been seen before
if (typeof raidData !== "undefined")
{
if (typeof raidData.stateId !== "undefined")
{
// Return its state
return RaidManager.STATE.valueOf(raidData.stateId);
}
else if (typeof raidData.state !== "undefined")
{
// Return its state
return RaidManager.STATE.valueOf(raidData.state.text);
}
}
// Since we haven't seen it, return UNSEEN
return RaidManager.STATE.UNSEEN;
},
// Parameterized command line raid lookup
/*public static Array*/
fetchByFilter: function(filterParam)
{
Timer.start("fetchByFilter");
try
{
var raidFilter;
// If we got text and not a RaidFilter
if (typeof filterParam == "string")
{
// Parse the command into a RaidFilter
raidFilter = new RaidMultiFilter(filterParam);
}
// We got something other than text. Assume it's a RaidFilter
else if (filterParam instanceof RaidFilter || filterParam instanceof RaidMultiFilter)
{
// filterParam was already a raidFilter
raidFilter = filterParam;
}
else
{
console.warn("Could not fetch by filter " + filterParam + ". Parameter must be a String or RaidFilter");
return;
}
// If the command makes a valid filter
if (raidFilter.isValid())
{
// Collect all matching raid links
var raidLinks = new Array();
// If there was no name or difficulty
if (raidFilter.isEmpty())
{
// Get all raid links
raidLinks = RaidManager.fetchAll();
}
// If we found a raid name, difficulty, or state
else
{
// Get all raids
var raidStorage = RaidManager.fetchStorage();
// Count of raids seen
var raidCount = 0;
// Count of raids seen
var resultsPage = 1;
// Start time of the run
var commandStartTime = (new Date() / 1);
// Iterate over all raids
for (var key in raidStorage)
{
// Get the raid data from storage
var raidData = raidStorage[key];
// Get the current raidLink
var raidLink = raidData.raidLink;
// Get the state of the link
var currentState;
if (typeof raidData.stateId != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.stateId);
}
else if (typeof raidData.state != "undefined" && typeof raidData.state.text != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.state.text);
}
if (typeof currentState == "undefined")
{
console.warn("Could not locate a state for " + raidLink.getSimpleText() + ". This may cause unexpected matching behavior.");
}
try
{
// If this link matches the filter
if (raidFilter.matches(
{
age: commandStartTime - raidData.firstSeen,
difficulty: raidLink.difficulty,
fs: raidLink.getRaid().getFairShare(raidLink.difficulty),
os: raidLink.getRaid().getOptimalShare(raidLink.difficulty),
name: raidLink.getRaid().getSearchableName(),
state: currentState,
count: raidCount,
size: raidLink.getRaid().size,
zone: raidLink.getRaid().zone
}
))
{
//TODO: Improved Sorting
// If we don't have a defined page, or we're on the right page, or we don't care about the count
if (typeof raidFilter.page == "undefined" || resultsPage == raidFilter.page || typeof raidFilter.count == "undefined")
{
// Put seen links at the end
if (RaidManager.STATE.equals(currentState, RaidManager.STATE.SEEN))
{
raidLinks.push(raidLink);
}
// Put visited and other links up top
else
{
raidLinks.unshift(raidLink);
}
}
// Keep track of how many raids match the query so we can deal with pagination
raidCount++;
if (raidFilter.count != "undefined" && raidCount % raidFilter.count == 0) {resultsPage++;raidCount=0;}
// Once we've collected enough links, bail
// If count is not set, we'll only break when we've iterated over all raids
if (raidLinks.length == raidFilter.count)
{
break;
}
}
}
catch(ex)
{
console.warn(ex);
console.warn("Failure while trying to match ");
console.warn(
{
age: commandStartTime - raidData.firstSeen,
difficulty: raidLink.difficulty,
fs: raidLink.getRaid().getFairShare(raidLink.difficulty),
name: raidLink.getRaid().getSearchableName(),
state: currentState,
count: raidCount
}
);
}
}
}
Timer.stop("fetchByFilter");
// Return all found links
return raidLinks;
}
}
catch (ex)
{
console.warn("Failed to lookup raids by ");
console.warn(filterParam);
console.warn(ex);
}
Timer.stop("fetchByFilter");
},
markByFilter: function(filter, state) {
Timer.start("markByFilter");
if (typeof filter === "string") {
filter = new RaidMultiFilter(filter);
}
if (typeof state === "string") {
state = RaidManager.STATE.valueOf(state.toUpperCase());
}
// Count of raids seen
var raidCount = 0;
// If the command makes a valid filter
if (filter.isValid())
{
// Get all raids
Timer.start("markByFilter > loading hardRaidStorage");
// Load up the storage object
var raidStorage = JSON.parse(GM_getValue(DC_LoaTS_Properties.storage.raidStorage));
Timer.stop("markByFilter > loading hardRaidStorage");
// Count of raids seen
var resultsPage = 1;
// Start time of the run
var commandStartTime = (new Date() / 1);
// Iterate over all raids
for (var key in raidStorage)
{
// Get the raid data from storage
var raidData = raidStorage[key];
// Get the link from the data
var raidLink = raidData.raidLink;
// Convert to RaidLink
Object.extend(raidLink, RaidLink.prototype);
// Get the state of the link
var currentState;
if (typeof raidData.stateId != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.stateId);
}
else if (typeof raidData.state != "undefined" && typeof raidData.state.text != "undefined")
{
currentState = RaidManager.STATE.valueOf(raidData.state.text);
}
if (typeof currentState == "undefined")
{
console.warn("Could not locate a state for " + raidLink.getSimpleText() + ". This may cause unexpected matching behavior.");
}
try
{
// If this link matches the filter
if (filter.matches(
{
age: commandStartTime - raidData.firstSeen,
difficulty: raidLink.difficulty,
fs: raidLink.getRaid().getFairShare(raidLink.difficulty),
os: raidLink.getRaid().getOptimalShare(raidLink.difficulty),
name: raidLink.getRaid().getSearchableName(),
state: currentState,
count: raidCount,
size: raidLink.getRaid().size,
zone: raidLink.getRaid().zone
}
))
{
raidData.stateId = state.id;
// Keep track of how many raids match the query so we can deal with pagination
raidCount++;
if (filter.count != "undefined" && raidCount % filter.count == 0) {resultsPage++;raidCount=0;}
// Once we've changed enough links, bail
// If count is not set, we'll only break when we've iterated over all raids
if (raidCount == filter.count)
{
break;
}
}
}
catch(ex)
{
console.warn(ex);
console.warn("Failure while trying to match ");
console.warn(
{
age: commandStartTime - raidData.firstSeen,
difficulty: raidLink.difficulty,
fs: raidLink.getRaid().getFairShare(raidLink.difficulty),
name: raidLink.getRaid().getSearchableName(),
state: currentState,
count: raidCount
}
);
}
}
Timer.start("markByFilter > storing hardRaidStorage");
// Store the storage data back into the browser storage
GM_setValue(DC_LoaTS_Properties.storage.raidStorage, JSON.stringify(raidStorage));
Timer.stop("markByFilter > storing hardRaidStorage");
}
Timer.stop("markByFilter");
return raidCount;
}
}
/************************************/
/********** RaidMenu Class **********/
/************************************/
// TODO: RaidMenu coming soon!
// Class to manage a popup raid menu
// There can only be a single raid menu at a time. The constructor will enforce this
window.RaidMenu = Class.create({
initialize: function()
{
// Find the existing RaidMenu
this.container = document.getElementById("DC_LoaTS_raidMenu");
// If a RaidMenu doesn't exist yet, make it
if (typeof this.container == "undefined" || this.container == null)
{
// Hooks to register and unregister
this._startDragHook = this._startDrag.bind(this);
this._performDragHook = this._performDrag.bind(this);
this._stopDragHook = this._stopDrag.bind(this);
this.container = document.createElement("div");
this.container.id = "DC_LoaTS_raidMenu";
$(this.container).hide();
document.body.appendChild(this.container);
this.titleBarWrapper = document.createElement("div");
this.titleBarWrapper.id = "DC_LoaTS_raidMenuTitleBarWrapper";
this.titleBarWrapper.className = "clearfix";
this.container.appendChild(this.titleBarWrapper);
this.titleBar = document.createElement("div");
this.titleBar.id = "DC_LoaTS_raidMenuTitleBar";
this.titleBarWrapper.appendChild(this.titleBar);
DC_LoaTS_Helper.registerEventHandler(this.titleBar, "mousedown", this._startDragHook);
var titleBarLeft = document.createElement("div");
titleBarLeft.id = "DC_LoaTS_raidMenuTitleBarLeft";
titleBarLeft.appendChild(document.createTextNode("LoaTS Helper Menu"));
this.titleBar.appendChild(titleBarLeft);
this.titleBarCenter = document.createElement("div");
this.titleBarCenter.id = "DC_LoaTS_raidMenuTitleBarCenter";
this.titleBar.appendChild(this.titleBarCenter);
var titleBarRight = document.createElement("div");
titleBarRight.id = "DC_LoaTS_raidMenuTitleBarRight";
this.titleBar.appendChild(titleBarRight);
// Set up the close button
this.titleBarClose = document.createElement("img");
this.titleBarClose.id = "DC_LoaTS_raidMenuClose";
this.titleBarClose.src = "https://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/base.png";
this.titleBarClose.setAttribute("usemap", "#raidMenuCloseMap");
this.titleBarWrapper.appendChild(this.titleBarClose);
// We only want the hover effect to work on the red triangle, so we'll need a click map
this.titleBarCloseMap = document.createElement("map");
this.titleBarCloseMap.name = "raidMenuCloseMap";
this.titleBarCloseMap.id = "raidMenuCloseMap";
this.titleBarWrapper.appendChild(this.titleBarCloseMap);
// Define the click map for the triangle close image.
// This is the area that responds to clicks and hover effects
var titleBarCloseArea = document.createElement("area");
titleBarCloseArea.shape = "poly";
titleBarCloseArea.coords = "12,6,50,42,50,6,46,1,42,0,19,-1";
titleBarCloseArea.href = "#";
titleBarCloseArea.setAttribute("onclick", "RaidMenu.toggle(); return false;");
titleBarCloseArea.setAttribute("onmouseover", "$('DC_LoaTS_raidMenuClose').src = 'https://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/hover.png';");
titleBarCloseArea.setAttribute("onmouseout", "$('DC_LoaTS_raidMenuClose').src = 'https://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/base.png';");
this.titleBarCloseMap.appendChild(titleBarCloseArea);
// This is where the panes go
this.bodyWrapper = document.createElement("div");
this.bodyWrapper.id = "DC_LoaTS_raidMenuBodyWrapper";
this.container.appendChild(this.bodyWrapper);
// This is where the tabs go
this.tabsList = document.createElement("ul");
this.tabsList.id = "DC_LoaTS_raidMenuTabs"
this.bodyWrapper.appendChild(this.tabsList);
// Activate tab container
this.tabs = new Control.Tabs('DC_LoaTS_raidMenuTabs');
// Holder for activated tabs
this.activatedTabs = {};
// Activate tabs
this._activateTabs();
}
},
// Resorts the tabs according to their position
// TODO: RaidMenu should probably have an addTab() where it manages this
resortTabs: function() {
var tabs = this.tabsList.getElementsByTagName("li");
console.log(tabs);
},
// Toggles the visibility of the raid menu
/*public void*/
toggle: function()
{
this.container.toggle();
},
// Show the raid menu
/*public void*/
show: function()
{
this.container.show();
},
// Hide the raid menu
/*public void*/
hide: function()
{
this.container.hide();
},
// Activates the tab classes. Probably don't call this except once in initialize
/*private void*/
_activateTabs: function()
{
// Create all the tabs
for (var tabPosition in RaidMenu.tabClasses)
{
try
{
this.activatedTabs[tabPosition] = new RaidMenu.tabClasses[tabPosition](this);
}
catch (ex)
{
console.warn("Error activating tab in position " + tabPosition);
console.warn(ex);
}
}
// Activate first tab
this.tabs.first();
},
activateTab: function(tabClass) {
this.activatedTabs[tabClass.tabPosition] = new tabClass(this);
},
setActiveTab: function(tabClass) {
this.tabs.setActiveTab(this.activatedTabs[tabClass.tabPosition].tabA);
},
// Event fired as the menu title has been clicked
/*private void*/
_startDrag: function(e)
{
// Half-hearted IE check
var evt = e || window.event;
// Detect right click
var rightclick;
if (evt.which)
{
rightclick = (evt.which == 3);
}
else if (evt.button)
{
rightclick = (evt.button == 2);
}
// Don't start dragging on right click
if (rightclick)
{
return;
}
// Mark that it's being dragged
this.beingDragged = true;
// Capture the drag start point in order to calculate movement
this.preDragLeft = this.container.offsetLeft;
this.preDragTop = this.container.offsetTop;
this.startingMouseX = evt.clientX;
this.startingMouseY = evt.clientY;
// Register the listeners for the rest of the drag
DC_LoaTS_Helper.registerEventHandler(document, "mousemove", this._performDragHook);
DC_LoaTS_Helper.registerEventHandler(document, "mouseup", this._stopDragHook);
// This should hopefully keep it from selecting text and doing anything else
// that normal clicking would do
return DC_LoaTS_Helper.stopDefaultEventAction(evt);
},
// Event fired as the menu is being actually dragged
/*private void*/
_performDrag: function(e)
{
// Half-hearted IE check
var evt = e || window.event;
// Move the menu based on the user's mouse movements
this.container.style.left = Math.max(this.preDragLeft + (evt.clientX-this.startingMouseX), 0) + "px";
this.container.style.top = Math.max(this.preDragTop + (evt.clientY-this.startingMouseY), 0) + "px";
// This should hopefully keep it from selecting text and doing anything else
// that normal dragging would do
return DC_LoaTS_Helper.stopDefaultEventAction(evt);
},
//Event fired as the mouse is released at the end of a drag
/*private void*/
_stopDrag: function(e)
{
// Mark that it's no longer being dragged
this.beingDragged = false;
// Remove the event listeners
DC_LoaTS_Helper.unregisterEventHandler(document, "mousemove", this._performDragHook);
DC_LoaTS_Helper.unregisterEventHandler(document, "mouseup", this._stopDragHook);
// Release the variables holding the previous locations
delete this.preDragLeft;
delete this.preDragTop;
delete this.startingMouseX;
delete this.startingMouseY;
}
});
// Put in a place holder for the tabs
RaidMenu.tabClasses = {};
// Ensure the raid menu is available
/*public static RaidMenu*/
RaidMenu.getInstance = function()
{
// Locate or create a raid menu
var raidMenu = window.raidMenu;
if (typeof raidMenu == "undefined")
{
try
{
raidMenu = new RaidMenu();
window.raidMenu = raidMenu;
}
catch(ex)
{
console.error("Error while opening raid menu");
console.error(ex);
return;
}
}
return raidMenu;
};
// Toggle the visibility of the raid menu
/*public static void*/
RaidMenu.toggle = function()
{
// Toggle its visibility
RaidMenu.getInstance().toggle();
};
// Show the raid menu
/*public static void*/
RaidMenu.show = function()
{
// Show it
RaidMenu.getInstance().show();
};
// Hide the raid menu
/*public static void*/
RaidMenu.hide = function()
{
// Hide it
RaidMenu.getInstance().hide();
};
// Show a specific tab
/*public static void*/
RaidMenu.setActiveTab = function(tabClass)
{
// Switch to the tab
RaidMenu.getInstance().setActiveTab(tabClass);
// Show the menu itself, if it's hidden
RaidMenu.show();
};
/************************************/
/******** RaidMenuTab Class *********/
/************************************/
// Class to manage a tab in the raid popup menu
window.RaidMenuTab = Class.create({
initialize: function(parentMenu)
{
//console.log("Creating tab ", arguments);
this.parentMenu = parentMenu;
var me = this;
var sanitaryName = me.getSanitizedName();
me.tabLi = document.createElement("li");
me.tabLi.className = "DC_LoaTS_raidMenuTab";
me.parentMenu.tabsList.appendChild(me.tabLi);
me.tabA = document.createElement("a");
me.tabA.href = "#DC_LoaTS_raidMenu" + sanitaryName + "Pane";
me.tabA.id = "DC_LoaTS_raidMenu" + sanitaryName + "PaneTab";
me.tabA.appendChild(document.createTextNode(me.tabName));
me.tabLi.appendChild(me.tabA);
me.pane = document.createElement("div");
me.pane.id = "DC_LoaTS_raidMenu" + sanitaryName + "Pane";
me.pane.className = "DC_LoaTS_raidMenuPane";
me.pane.style.display = "none";
me.parentMenu.bodyWrapper.appendChild(me.pane);
me.parentMenu.tabs.addTab(me.tabA);
if (me.closeable) {
me.tabCloseA = document.createElement("a");
me.tabCloseA.href = "#";
me.tabCloseA.className = "DC_LoaTS_raidMenuCloseTabA";
me.tabCloseA.innerText = "X";
me.tabCloseA.onclick = function() {
me.parentMenu.tabsList.removeChild(me.tabLi);
me.parentMenu.bodyWrapper.removeChild(me.pane);
delete RaidMenu.tabClasses[me.tabPosition];
delete me.parentMenu.tabs.containers._object[me.tabA.href];
var links = me.parentMenu.tabs.links;
for (var i = 0; i < links.length; i++) {
var link = links[i];
if (link.key == me.tabA.href) {
var rest = links.slice((i-1 || i) + 1 || links.length);
links.length = i < 0 ? links.length + i : i;
me.parentMenu.tabs.links = links.push.apply(links, rest);
break;
}
}
me.parentMenu.tabs.previous();
return false;
};
me.tabLi.appendChild(me.tabCloseA);
} // End closeable logic
me.header = me.createHeader(me.tabHeader || me.tabName);
me.pane.appendChild(me.header);
if (typeof me.initPane === "function") {
me.initPane();
}
},
getSanitizedName: function()
{
return this.tabName.trim().replace(" ", "_");
},
createHeader: function(title)
{
var header = document.createElement("h1");
header.className = "RaidMenuTab-Header";
header.update(title);
return header;
},
createSimpleOptionHTML: function(id, type, value, description, hoverText, additionalAttributes)
{
if (type == "boolean" || type == "text") // Not sure if other types would need different wrappers...
{
var outerWrapper = document.createElement("div");
outerWrapper.id = id + "Wrapper";
outerWrapper.className = "DC_LoaTS_raidMenuOptionWrapper clearfix";
var innerWrapper = document.createElement("div");
innerWrapper.className = "DC_LoaTS_raidMenuInnerWrapper"
outerWrapper.appendChild(innerWrapper);
}
switch(type)
{
case "boolean":
{
var option = document.createElement("input");
option.type = "checkbox";
option.id = id;
option.title = hoverText;
if (value === "true" || value === true)
{
option.checked = true;
}
for (var attribute in additionalAttributes)
{
option[attribute] = additionalAttributes[attribute];
}
innerWrapper.appendChild(option);
var desc = document.createElement("div");
desc.className = "DC_LoaTS_raidMenuDescription";
desc.innerHTML = description;
outerWrapper.appendChild(desc);
return {wrapper: outerWrapper, option: option};
}
case "text":
{
var option = document.createElement("input");
option.type = "text";
option.id = id;
option.title = hoverText;
option.value = value;
for (var attribute in additionalAttributes)
{
option[attribute] = additionalAttributes[attribute];
}
innerWrapper.appendChild(option);
var desc = document.createElement("div");
desc.className = "DC_LoaTS_raidMenuDescription";
desc.innerHTML = description;
outerWrapper.appendChild(desc);
return {wrapper: outerWrapper, option: option};
}
}
}
});
RaidMenuTab.create = function(classObject)
{
//console.log(classObject);
try
{
// Don't collide with other poorly positioned tabs
while (typeof RaidMenu.tabClasses[classObject.tabPosition] !== "undefined")
{
classObject.tabPosition++;
}
var tabClass = Class.create(RaidMenuTab, classObject);
tabClass.tabName = classObject.tabName;
tabClass.tabPosition = classObject.tabPosition;
tabClass.closeable = classObject.closeable;
RaidMenu.tabClasses[classObject.tabPosition] = tabClass;
return tabClass;
}
catch(ex)
{
var tabName = (typeof classObject !== "undefined" && typeof classObject.tabName !== "undefined")?classObject.tabName:"";
console.warn("Error while creating RaidMenu tab class " + tabName);
console.warn(classObject);
console.warn(ex);
}
};
RaidMenuTab.createDataDumpTab = function(data, title)
{
var tabTitle = title.length == 0?"Data Export":title.length > 25?title.substring(0,25):title;
var tabA;
RaidMenu.show();
var tabClass = RaidMenuTab.create({
tabName: tabTitle,
tabHeader: "Export: " + title,
tabPosition: 150,
closeable: true,
initPane: function()
{
var textArea = document.createElement("textarea");
textArea.className = "DataDumpTab-Data";
textArea.innerHTML = data;
tabA = this.tabA;
this.pane.appendChild(textArea);
}
});
RaidMenu.getInstance().activateTab(tabClass);
RaidMenu.getInstance().tabs.setActiveTab(tabA);
};
/************************************/
/********* RaidMultiFilter Class *********/
/************************************/
// This class represents a group of filters
window.RaidMultiFilter = Class.create({
// Constructor
initialize: function(filterText)
{
Timer.start("RaidMultiFilter init");
// Declare some vars for later
this.valid = true;
// Capture original filterText
this.filterText = filterText;
// Break out any bunch
var filterTexts = this.filterText.split("||");
// Prepare the filters
this.filters = [];
for (var i = 0; i < filterTexts.length; i++) {
this.filters.push(new RaidFilter(filterTexts[i]));
}
Timer.stop("RaidMultiFilter init");
},
// Based on this filter, does a given property match the filter
matches: function(params)
{
var matched = false;
for (var i = 0; i < this.filters.length; i++) {
matched = matched || this.filters[i].matches(params);
}
return matched;
},
// Gets a key to define this filter
getKey: function()
{
var key = "";
for (var i = 0; i < this.filters.length; i++) {
key += (i>0?"||":"") + this.filters[i].getKey()
}
return key;
},
// If it has a name and optionally a difficulty and nothing else, it's simple
isSimple: function()
{
var simple = true;
for (var i = 0; i < this.filters.length; i++) {
simple = simple && this.filters[i].isSimple();
}
return simple;
},
isEmpty: function()
{
var empty = true;
for (var i = 0; i < this.filters.length; i++) {
empty = empty && this.filters[i].isEmpty();
}
return empty;
},
isValid: function()
{
var valid = true;
for (var i = 0; i < this.filters.length; i++) {
valid = valid && this.filters[i].isValid();
}
return valid;
},
getDifficultyText: function()
{
var text = "";
for (var i = 0; i < this.filters.length; i++) {
text += (i>0?"||":"") + this.filters[i].getDifficultyText()
}
return text;
},
toString: function()
{
var str = "";
for (var i = 0; i < this.filters.length; i++) {
str += (i>0?"||":"") + this.filters[i].toString()
}
return str;
},
toPrettyString: function() {
var pretty = "";
for (var i = 0; i < this.filters.length; i++) {
pretty += (i>0?"||":"") + this.filters[i].toPrettyString()
}
return pretty;
}
});
RaidMultiFilter.paramText = "[raidName] [raidDifficulty] [{state: stateParam}] [{fs: fsParam}] [{age: ageParam}] [{count: countParam} [{page: pageParam}]]";
/************************************/
/********** RaidStyle Class *********/
/************************************/
// Class to parse raid style text of any form into CSS stuff
window.RaidStyle = Class.create({
initialize: function(styleText)
{
var naturalLanguage = "";
var nativeCSS = "";
this.css = "";
// console.log("Parsing styleText: \"" + styleText + "\"")
// Extract from the inputted text the various natural language and native CSS bits
RaidStyle.parsePattern.lastIndex = 0;
for (var match = RaidStyle.parsePattern.exec(styleText); match && match[0] != ""; match = RaidStyle.parsePattern.exec(styleText))
{
// Combine any natural language pieces together
if (typeof match[1] !== "undefined")
{
naturalLanguage += match[1];
}
// Combine any native CSS pieces together
if (typeof match[2] !== "undefined")
{
nativeCSS += match[2];
}
}
// Trim out any extra whitespace
naturalLanguage = naturalLanguage.trim().toLowerCase();
nativeCSS = nativeCSS.trim();
// console.log("styleText yielded naturalLanguage: \"" + naturalLanguage + "\" and nativeCSS: \"" + nativeCSS + "\"");
// Try to parse the natural language bits
// First, get a copy of the parsable bits
var parseEls = RaidStyle.getNaturalLanguageParseElements();
for (var i = 0; i < parseEls.length && naturalLanguage.length > 0; i++)
{
var el = parseEls[i];
el.pattern.lastIndex = 0;
var match = el.pattern.exec(naturalLanguage);
if (match != null && match[0] != "")
{
// console.log(el.property + " captured \"" + match[el.capture] + "\" and will replace \"" + match[el.replace]) +"\"";
this.css += el.property + ": " + match[el.capture] + "; ";
// console.log("Natural language before: \"" + naturalLanguage + "\"");
naturalLanguage = naturalLanguage.replace(match[el.replace], "").trim();
// console.log("Natural language after: \"" + naturalLanguage + "\"");
}
else
{
// console.log(el.property + " did not match against \"" + naturalLanguage + "\"");
}
}
this.css += nativeCSS;
// console.log("CSS: ");
// console.log(this.css);
},
toString: function()
{
return this.css;
},
isEmpty: function()
{
return typeof this.css === "undefined" || this.css.trim().length == 0;
},
isValid: function()
{
//TODO Will a style ever be not valid?
return true;
}
});
// General pattern to pick apart the natural language style from the native CSS styles
RaidStyle.parsePattern = /([^"]*)?("[^"]*")?/gi;
// Pattern to find bits of text that are colors. Can find #FFF, #FFFFFF, rgb(255,255,255), or white as the color white
RaidStyle.baseColorPattern = /#[0-9a-f]{3}(?:[0-9a-f]{3})?\b|rgb\((?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]),(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5]),(?:[0-1]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\)|\b(?:black|white|red|yellow|lime|aqua|blue|fuchsia|orange|gray|silver|maroon|olive|green|teal|navy|purple)\b/i;
RaidStyle.colorPattern = new RegExp("(?:(?:(?!on +).. )|^)(" + RaidStyle.baseColorPattern.source + ")", "i");
RaidStyle.backgroundColorPattern = new RegExp("\\bon +(" + RaidStyle.baseColorPattern.source + ")", "i");
// These are the current natural language features we're going to support for now
RaidStyle.naturalLanguageParseElements = [
{property: "font-weight", capture: 0, replace: 0, pattern: /\b(?:[1-9]00(?!px|pt|em|en|%)|bold(?:er)?|lighter|normal)\b/i},
{property: "background-color", capture: 1, replace: 0, pattern: RaidStyle.backgroundColorPattern},
{property: "color", capture: 1, replace: 1, pattern: RaidStyle.colorPattern},
{property: "font-size", capture: 0, replace: 0, pattern: /\b[0-9]?[0-9]?[0-9]?[0-9] ?(?:(?:px|pt|em|en)\b|%)|\bx?x-small\b|\bsmall(?:er)?\b|\bmedium\b|\blarger?\b|\bx?x-large\b/i},
{property: "text-decoration", capture: 0, replace: 0, pattern: /\bunderline(?: overline)\b/i},
{property: "font-style", capture: 0, replace: 0, pattern: /\b(?:italic|oblique|normal)\b/i},
{property: "whitespace", capture: 0, replace: 0, pattern: /\b(?:pre|pre-wrap|pre-line|-moz-pre-wrap|-o-pre-wrap|nowrap|normal)\b/i},
{property: "display", capture: 0, replace: 0, pattern: /\b(?:none|inline|block|inline-block|list-item|marker|compact|run-in|table-header-group|table-footer-group|table|inline-table|table-caption|table-cell|table-row|table-row-group|table-column|table-column-group)\b/i}
];
RaidStyle.getNaturalLanguageParseElements = function()
{
var el = [];
for (var i = 0; i < RaidStyle.naturalLanguageParseElements.length; i++)
{
el.push(RaidStyle.naturalLanguageParseElements[i]);
}
return el;
};
/************************************/
/********* RaidToolbar Class ********/
/************************************/
window.RaidToolbar = Class.create({
initialize: function()
{
// Set up the matched commands container
this.existingMatchedCommands = [];
// Find the existing RaidToolbar
this.container = document.getElementById("DC_LoaTS_raidToolbarContainer");
// If a RaidToolbar doesn't exist yet, make it
if (typeof this.container == "undefined" || this.container == null)
{
// Create the space bewteen the kong buttons and game
var td = new Element("td", {"id": "DC_LoaTS_toolbarCell"});
var tr = new Element("tr");
tr.appendChild(td);
this.container = new Element("ul", {id: "DC_LoaTS_raidToolbarContainer"});
td.appendChild(this.container);
$($("flashframecontent").children[0].children[0].children[0]).insert({after:tr});
var ccc = $("chat_container_cell");
ccc.remove();
td.insert({after: ccc});
ccc.setAttribute("rowspan", "2");
$("maingame").style.height = parseInt($("maingame").style.height) + 20 + "px";
$("chat_container").style.height = parseInt($("chat_container").style.height) + 20 + "px";
$("chat_tab_pane").style.height = parseInt($("chat_tab_pane").style.height) + 20 + "px";
//TODO: Should break these out like the commands?
this.buttons = {
reload: new RaidButton("reload", "DC_LoaTS_reloadButton", DC_LoaTS_Helper.reload),
toggleMenu: new RaidButton("toggleMenu", "DC_LoaTS_menuButton",
function(event)
{
// Show the raid menu
RaidMenu.toggle();
// If the menu was spawned by a click, move the menu there
if (typeof event != "undefined")
{
// Fixed Menu positioning - Needs to be relative to window scroll
var scrollOffsets = document.viewport.getScrollOffsets();
// Move the raid menu to where the mouse clicked this button
window.raidMenu.container.style.left = Event.pointerX(event) - scrollOffsets.left + "px";
window.raidMenu.container.style.top = Event.pointerY(event) - scrollOffsets.top + 20 + "px";
}
}
),
toggleGame: new RaidButton("toggleGame", "DC_LoaTS_toggleGameButton", DC_LoaTS_Helper.toggleGame, "Show / Hide the game"),
reloadWorldChat: new RaidButton("reloadWC", "DC_LoaTS_reloadWCButton", DC_LoaTS_Helper.reload),
toggleWorldChat: new RaidButton("toggleWorldChat", "DC_LoaTS_toggleWorldChatButton", DC_LoaTS_Helper.toggleWorldChat, "Show / Hide World Chat")
};
for (var buttonName in this.buttons)
{
this.container.insert({bottom: this.buttons[buttonName].node});
}
this.omniboxWrapper = new Element("li", {"class": "DC_LoaTS_omniboxWrapper"});
this.omnibox = new Element("input", {type: "text", "class": "DC_LoaTS_omnibox", autocomplete: "off"});
this.omniboxWrapper.insert({bottom: this.omnibox});
this.omnibox.oldValue = "";
this.omniboxCommandsWrapper = new Element("ul", {"class": "DC_LoaTS_omniboxCommandsWrapper"});
this.omniboxCommandsWrapper.hide();
this.omniboxWrapper.insert({bottom: this.omniboxCommandsWrapper});
this.container.insert({bottom: this.omniboxWrapper});
this.omnibox.observe("mouseover", function() {
$(this).addClassName("DC_LoaTS_omnibox_focus");
});
this.omniboxWrapper.observe("mouseover", function() {
$(this).addClassName("DC_LoaTS_omniboxWrapper_hover");
});
this.omnibox.observe("focus", function(event, raidToolbar) {
$(this).addClassName("DC_LoaTS_omnibox_focus");
if (this.value.length >= 3 && raidToolbar.omniboxCommandsWrapper.childElements().length > 0)
{
raidToolbar.omniboxCommandsWrapper.show();
}
}.bindAsEventListener(this.omnibox, this));
this.omnibox.observe("mouseout", function() {
if (this.value.length == 0 && this != document.activeElement)
{
$(this).removeClassName("DC_LoaTS_omnibox_focus");
}
});
this.omniboxWrapper.observe("mouseout", function() {
$(this).removeClassName("DC_LoaTS_omniboxWrapper_hover");
});
this.omnibox.observe("blur", function(event, raidToolbar) {
if (this.value.length == 0 && this != document.activeElement)
{
$(this).removeClassName("DC_LoaTS_omnibox_focus");
}
if (!$(raidToolbar.omniboxWrapper).hasClassName("DC_LoaTS_omniboxWrapper_hover"))
{
raidToolbar.omniboxCommandsWrapper.hide();
}
}.bindAsEventListener(this.omnibox, this));
this.omnibox.observe("input", function(event, raidToolbar) {
if (this.oldValue != this.value)
{
// Stretch the bar as needed
this.setAttribute("size", Math.min(Math.max(20, (93/80) * this.value.length), 120));
this.oldValue = this.value;
DCDebug("Text Changed");
// Process the omnibox text
raidToolbar.processOmniboxText(this.value);
}
}.bindAsEventListener(this.omnibox, this));
//TODO: Rig up keys to control omnibox selection
this.omnibox.observe("keydown", function(event) {
// Up arrow
if (event.keyCode == 38)
{
DCDebug("Pressed up")
}
// Down arrow
else if (event.keyCode == 40)
{
DCDebug("Pressed down")
}
// Left arrow
else if (event.keyCode == 41)
{
DCDebug("Pressed left")
}
// Right arrow
else if (event.keyCode == 39)
{
DCDebug("Pressed right")
}
// Enter
else if (event.keyCode == 13)
{
DCDebug("Pressed Enter")
}
});
RaidToolbar.createWRButton();
}
// Else if it does exist, grab the hooks
//TODO
},
// Process omnibox text
processOmniboxText: function(text)
{
var matchedCommands = [];
// Clean up whitespace
text = text.trim();
// We really need at least 3 characters to make sense of the user's input
if (text.length >= 3)
{
// Run through all of the text processors
var raidLink = new RaidLink(text);
var raidFilter = new RaidFilter(text);
// If this is a valid link
if (raidLink.isValid())
{
try
{
DCDebug("Link refers to: " + raidLink.getSimpleText() + "First seen: " + "Last seen: ");
DCDebug("Load: " + raidLink.getSimpleText());
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.loadraid("omnibox", raidLink.getURL()));
DCDebug("Info: " + raidLink.getDifficultyText() + " " + raidLink.getName());
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.raid("omnibox", raidLink.getName() + " " + raidLink.difficulty));
DCDebug("State: Forget/Remember, Un/Mark Visited");
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.linkstate("omnibox", raidLink.getURL()));
DCDebug("Seen: " + raidLink.getName() + " Any, Normal, Hard, Legendary, Nightmare");
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.seenraids("omnibox", raidLink.getName() + " " + raidLink.difficulty));
DCDebug("Search: " + raidLink.getName() + " on ZoyWiki");
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.wiki("omnibox", raidLink.getName()));
}
catch(ex)
{
console.warn("Failure while creating options for omnibox");
console.warn(raidLink);
console.warn(ex);
}
}
// If it's a valid filter and it's not empty
else if (raidFilter.isValid() && !raidFilter.isEmpty())
{
var matchedTypes = DC_LoaTS_Helper.getRaidTypes(raidFilter);
if (matchedTypes.length > 0)
{
// If it's simple and matches only 1 type, put raid info first
var raidInfoFirst = matchedTypes == 1 && raidFilter.isSimple();
if (raidInfoFirst)
{
if (typeof raidFilter.difficulty != "undefined")
{
DCDebug("Raid info for " + raidFilter.getDifficultyText() + " " + matchedTypes[0].fullName);
}
else
{
DCDebug("Raid info for " + matchedTypes[0].fullName);
}
DCDebug("Look up " + matchedTypes[0].fullName + " on ZoyWiki");
}
DCDebug("Seen raids matching " + text);
// If we didn't offer raid info first
if (!raidInfoFirst)
{
DCDebug("Raid info for all matching raids");
for (var i = 0; i < matchedTypes.length; i++)
{
if (typeof raidFilter.difficulty != "undefined")
{
DCDebug("Raid info for " + raidFilter.getDifficultyText() + " " + matchedTypes[i].fullName);
}
else
{
DCDebug("Raid info for " + matchedTypes[i].fullName);
}
}
}
}
}
// Simple commands
if (text.toLowerCase().indexOf("lsi") == 0)
{
DCDebug("Calculate lsi");
}
else if (text.toLowerCase().indexOf("bsi") == 0)
{
DCDebug("Calculate bsi");
}
else if (text.toLowerCase().indexOf("help") == 0)
{
DCDebug("Get help?");
}
if (matchedCommands.length == 0)
{
try
{
// Attempt to match the text to a known command
for (var commandName in DC_LoaTS_Helper.chatCommands)
{
// Going to add a wiki command no matter what
if (commandName.toLowerCase() == DC_LoaTS_Helper.chatCommands.wiki.commandName ||
commandName.toLowerCase() == DC_LoaTS_Helper.chatCommands.forum.commandName)
{
continue;
}
// Check command
if (text.toLowerCase().indexOf(commandName.toLowerCase()) == 0
||
text.toLowerCase().indexOf("/" + commandName.toLowerCase()) == 0
)
{
matchedCommands.push(new DC_LoaTS_Helper.chatCommands[commandName]("omnibox", text));
}
// Check aliases of this command
else
{
var command = DC_LoaTS_Helper.chatCommands[commandName];
for (var i = 0; i < command.aliases.length; i++)
{
var alias = command.aliases[i];
if (text.toLowerCase().indexOf(alias.toLowerCase()) == 0
||
text.toLowerCase().indexOf("/" + alias.toLowerCase()) == 0
)
{
matchedCommands.push(new DC_LoaTS_Helper.chatCommands[commandName]("omnibox", text));
}
}
}
}
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.wiki("omnibox", text));
matchedCommands.push(new DC_LoaTS_Helper.chatCommands.forum("omnibox", text));
}
catch(ex)
{
console.warn("Failure while matching omnibox text (\"" + text + "\") to command.");
console.warn(ex);
}
}
// Clear out any old suggestions
if (matchedCommands.length > 0 || this.omnibox.value.length < 3)
{
// Remove all existing options
this.omniboxCommandsWrapper.childElements().invoke("remove");
// Unhook the existing commands
this.existingMatchedCommands.invoke("onRemovedFromOmnibox");
}
// Set the new commands we found to tbe ones we remember
this.existingMatchedCommands = matchedCommands;
// Put in the new suggestions, if any
if (matchedCommands.length > 0)
{
for (var i = 0; i < matchedCommands.length; i++)
{
var command = matchedCommands[i];
DCDebug(command.commandName);
// var option = new Element("li", {"class": "omniboxCommandOption"});
// var anchor = new Element("a", {"href": "#"});
// anchor.onclick = function(){alert(this.innerHTML); return false;};
// anchor.update(command.commandName);
//
// option.insert({bottom: anchor});
// this.omniboxCommandsWrapper.insert({bottom: option});
this.omniboxCommandsWrapper.insert({bottom: command.getOptionLine()});
}
this.omniboxCommandsWrapper.show();
}
}
else
{
this.omniboxCommandsWrapper.hide();
}
},
toggle: function()
{
this.container.toggle();
},
show: function()
{
this.container.show();
},
hide: function()
{
this.container.hide();
}
});
// Toggle the visibility of the raid toolbar
RaidToolbar.toggle = function()
{
// Locate or create a raid toolbar
var raidToolbar = window.raidToolbar;
if (typeof raidToolbar == "undefined")
{
raidToolbar = new RaidToolbar();
window.raidToolbar = raidToolbar;
}
// Toggle its visibility
raidToolbar.toggle();
};
// Show the raid toolbar
RaidToolbar.show = function()
{
// Locate or create a raid toolbar
var raidToolbar = window.raidToolbar;
if (typeof raidToolbar == "undefined")
{
raidToolbar = new RaidToolbar();
window.raidToolbar = raidToolbar;
}
// Toggle its visibility
raidToolbar.show();
};
// Hide the raid toolbar
RaidToolbar.hide = function()
{
// Locate or create a raid toolbar
var raidToolbar = window.raidtoolbar;
if (typeof raidToolbar != "undefined")
{
// Hide the toolbar
raidToolbar.hide();
}
};
// Hide the command options
RaidToolbar.hideCommandOptions = function()
{
$$(".DC_LoaTS_omniboxCommandsWrapper")[0].hide();
}
// Hide the command options
RaidToolbar.resetOmnibox = function()
{
$$(".DC_LoaTS_omnibox")[0].value = "";
$$(".DC_LoaTS_omnibox")[0].focus();
}
RaidToolbar.createWRButton = function() {
var wr = DC_LoaTS_Helper.worldRaidInfo;
if (!DC_LoaTS_Helper.wrButton && typeof wr === "object" && (!wr.timerEnds || new Date(wr.timerEnds) > new Date())) {
// Locate or create a raid toolbar
var raidToolbar = window.raidToolbar;
if (typeof raidToolbar == "undefined")
{
raidToolbar = new RaidToolbar();
window.raidToolbar = raidToolbar;
}
DC_LoaTS_Helper.wrButton = new RaidButton(DC_LoaTS_Helper.worldRaidInfo.name + " Info", "DC_LoaTS_WRButton", DC_LoaTS_Helper.showWRInfo);
raidToolbar.container.insert({bottom: DC_LoaTS_Helper.wrButton.node});
}
}
/************************************/
/********** RaidType Class **********/
/************************************/
// The Raid Type class is the generic form of a raid
/*public class*/
window.RaidType = Class.create({
// Constructor
/*public RaidType*/
initialize: function(id, zone, fullName, shortName, colloqName, time, size, stat, health, fairShare, target)
{
this.id = id;
this.zone = zone || "?";
this.fullName = fullName || id;
this.shortName = shortName || id;
this.colloqName = colloqName || id;
this.shortestName = colloqName || id;
this.time = time || "?";
this.size = size || "?";
this.stat = stat || "?";
// Calculate Health
if (typeof health === "number")
{
this.health = [health*RaidType.difficultyHealthFactor[1],
health*RaidType.difficultyHealthFactor[2],
health*RaidType.difficultyHealthFactor[3],
health*RaidType.difficultyHealthFactor[4]
];
}
else if (typeof health === "string")
{
this.health = [health, health, health, health];
}
else if (typeof health === "object" && typeof health !== null)
{
this.health = health;
}
else
{
this.health = ["?", "?", "?", "?"];
}
// Calculate Fair Share
if (typeof fairShare === "number" || typeof fairShare === "string")
{
this.fairShare = [fairShare, fairShare, fairShare, fairShare];
}
else if (typeof fairShare === "object" && fairShare !== null)
{
this.fairShare = fairShare;
}
//TODO: Cache this instead
// Else, calculate FS inline
// Calculate Target
if (typeof target === "number" || typeof target === "string")
{
this.target = [target, target, target, target];
}
else if (typeof target === "object" && target !== null)
{
this.target = target;
}
//TODO: Cache this instead
// Else, calcuate Target inline
},
// Get or calculate fair share for a given difficulty raid.
// Can be int or String (usually, if applicable, "Unknown")
/*public int or String*/
getFairShare: function (difficulty)
{
var fs = 0;
// If there is a hardcoded fair share, use that
if (typeof this.fairShare !== "undefined")
{
fs = this.fairShare[difficulty-1];
}
// IF there is no hardcoded fair share, calculate it
// Also, we must have healths, difficulty, and size to calculate it
else if (typeof difficulty !== "undefined"
&& typeof this.size === "number"
&& typeof this.getHealth(difficulty) === "number")
{
fs = this.getHealth(difficulty) / this.size;
}
return fs;
},
// Get pretty text for fair share
/*public String*/
getFairShareText: function(difficulty)
{
var fs = this.getFairShare(difficulty);
return DC_LoaTS_Helper.prettyFormatNumber(fs);
},
// Get or calculate optimal share for a given difficulty raid.
// Can be int or String (usually, if applicable, "Unknown")
/*public int or String*/
getOptimalShare: function (difficulty)
{
var target = 0;
// If non-standard target damage is set
if (typeof this.target !== "undefined")
{
target = this.target[difficulty-1];
}
// Otherwise assume usual calculation of target
else
{
target = this.getFairShare(difficulty) * RaidType.targetDamageModifier[this.size];
}
return target;
},
// Get pretty text for target damage (optimal share)
/*public String*/
getTargetDamageText: function(difficulty)
{
return DC_LoaTS_Helper.prettyFormatNumber(this.getOptimalShare(difficulty));
},
// Returns the int of the health or specified String (usually, if applicable, it's "Unknown")
/*public int or String*/
getHealth: function(difficulty)
{
return this.health[difficulty-1];
},
// Returns the health of the raid as friendly text
/*public String*/
getHealthText: function(difficulty)
{
var health = this.getHealth(difficulty);
return (typeof health == "number")?DC_LoaTS_Helper.prettyFormatNumber(health):health;
},
// Returns the duration of the raid as text
/*public String*/
getTimeText: function()
{
return this.time + "H";
},
// Returns a combination of all acceptable names for the raid
// So that the string can be searched
/*public String*/
getSearchableName: function()
{
return this.id + "_" + this.fullName + "_" + this.shortName + "_" + this.colloqName;
},
// Gets a big descriptive block of text for the raid
// Difficulty is optional. If provided, narrows down output, otherwise gives all
/*public String*/
getVerboseText: function(difficulty)
{
// Put the name, size, and stat facts into the string
var text = "<b title=\"" + this.id + " - Search Key: " + this.shortestName + "\">" + this.fullName + "</b> \n";
text += "Raid Size: " + this.size + " \n";
text += "Stat(s) Used: " + this.stat + " \n";
text += "Duration: " + this.getTimeText() + " \n";
text += "Zone: " + this.zone + " \n";
// If the user passed in difficulty 0, they only want the above listed stuff
if (difficulty != 0)
{
var difficulties;
// If we're focusing on a single difficulty
if (typeof difficulty != "undefined")
{
difficulties = [difficulty];
}
// If we didn't get a single difficulty, show all difficulties
else
{
difficulties = [1, 2, 3, 4];
}
// For each of the difficulties we're addressing
for (var i = 0; i < difficulties.length; i++)
{
var d = difficulties[i];
if (difficulties.length > 1)
{
text += " \n-- \n";
}
// Get text for the difficulty
var diffText = RaidType.difficulty[d];
if (typeof diffText == "undefined")
{
diffText = "Unknown";
}
var healthText = DC_LoaTS_Helper.prettyFormatNumber(this.getHealth(d));
// Display the difficulty, health, fs, and target damage
text += "Difficulty: " + diffText + " \n";
text += "Health: " + healthText + " \n";
text += "<acronym title=\"FS = Fair Share (of damage) = Raid Health (" + healthText +
") / Raid Size (" + this.size + ")\">FS</acronym>: " + this.getFairShareText(d) + " \n";
text += "<span class=\"abbr\" title=\"Target Damage is FS * Raid Size Multiplier. The multiplier is calculated from user tests in the spreadsheet.\">Target(OS)</span>: " + this.getTargetDamageText(d) + " ";
}
}
return text;
}
});// End RaidType Class
// List of possible difficulties, anything else will show up as Unknown
RaidType.difficulty = {1: "Normal", 2: "Hard", 3: "Legendary", 4: "Nightmare"};
// List of possible short name difficulties, anything else will show up as Unknown
RaidType.shortDifficulty = {1: "N", 2: "H", 3: "L", 4: "NM"};
// List of how much each difficulty modifies the base HP of the raid
RaidType.difficultyHealthFactor = {1: 1, 2: 1.25, 3: 1.6, 4: 2};
// List of *FS modifiers for Target Damage based on raid size.
// From the raid spreadsheet:
// https://docs.google.com/spreadsheet/ccc?key=0AoPyAHGDsRjhdGYzalZZdTBpYk1DS1M3TjVvYWRwcGc&hl=en_US#gid=4
RaidType.targetDamageModifier = {1: 1, 10: 1.25, 25: 1.5, 50: 2.2, 100: 2.3, 250: 1, 500: 1.5};
/************************************/
/********** Timing Utility **********/
/************************************/
window.Timer = {
start: function(timerName)
{
var timer = Timer[timerName];
if (typeof timer == "undefined")
{
timer = {name: timerName, start: 0, totalTime: 0, longestTime: 0, numTimes: 0};
window.Timer[timerName] = timer;
}
timer.start = new Date()/1;
},
stop: function(timerName)
{
var timer = Timer[timerName];
if (typeof timer == "undefined")
{
console.log("Can't stop a timer (" + timerName + ") that wasn't started");
}
else
{
var elapsed = (new Date()/1) - timer.start;
timer.totalTime += elapsed;
if (timer.longestTime < elapsed) {timer.longestTime = elapsed;}
timer.numTimes++;
timer.start = 0;
}
},
addRun: function(timerName, runTime) {
var timer = Timer[timerName];
if (typeof timer === "undefined")
{
timer = {name: timerName, start: 0, totalTime: 0, longestTime: 0, numTimes: 0};
window.Timer[timerName] = timer;
}
timer.totalTime += runTime || 0;
if (timer.longestTime < runTime) {timer.longestTime = runTime;}
timer.numTimes++;
},
getTimer: function(timerName)
{
var timer = Timer[timerName];
if (typeof timer === "undefined")
{
console.log("Can't get a timer (" + timerName + ") that wasn't started");
}
else
{
timer.getAverage = function()
{
return this.totalTime / this.numTimes;
}.bind(timer);
}
return timer;
},
getReport: function()
{
var report = "";
for (var timerName in window.Timer)
{
var timer = window.Timer.getTimer(timerName);
if (typeof timer !== "function" && typeof timer !== "undefined")
{
report += timerName + " > Average: " + timer.getAverage().toFixed(5) + "ms - Total: " + timer.totalTime + "ms - # " + timer.numTimes + "\n\n";
}
}
return report;
},
printReport: function()
{
console.log(Timer.getReport());
}
};
/************************************/
/** UrlParsingFilter Class */
/************************************/
window.UrlParsingFilter = Class.create({
initialize: function(params)
{
// Capture input params
this.params = params;
// Default to not forced and not cancelled
this.force = false;
this.cancel = false;
// Type is other unless we match something specific
this.type = "other";
// Break apart the params to find out what this filter is supposed to represent
var paramsParts = params.trim().replace(/\s+/g, " ").split(" ");
// If we're supposed to force this filter
if (paramsParts[0] === "force" || paramsParts[0] === "!") {
this.force = true;
this.url = paramsParts[1];
if (paramsParts[2]) {
this.raidFilter = new RaidMultiFilter(paramsParts.slice(2).join(" "));
}
}
// If we're supposed to cancel this filter
else if (paramsParts[0] === "cancel") {
this.cancel = true;
}
// Neither forced nor cancelled, just a URL and maybe a RaidFilter
else {
this.url = paramsParts[0];
if (paramsParts[1]) {
this.raidFilter = new RaidMultiFilter(paramsParts.slice(1).join(" "));
}
}
// Does this match the url of any service we already know about? Assume not
this.known = false;
var match;
// Try the various urls that this parser knows about
// Even if we're forcing it, we still need to run this to resolve the regexMatch
for (var type in UrlParsingFilter.urlPatterns) {
if ((match = UrlParsingFilter.urlPatterns[type].exec(this.url)) !== null) {
this.known = true;
this.type = type;
this.regexMatch = match;
break;
}
}
},
getUrlLink: function()
{
return "<a href=\"" + this.getWorkingUrl() + "\" target=\"_blank\">" + this.getLinkName() + "</a>";
},
getLinkName: function()
{
switch(this.type)
{
case "pastebin":
return "Pastebin";
break;
case "cconoly":
return "CConoly"
break;
default:
return this.getWorkingUrl();
break;
}
},
getWorkingUrl: function ()
{
return this.convertedUrl || this.url;
},
// It's valid if it provides a url or is a cancel
isValid: function()
{
return this.getWorkingUrl() || this.cancel;
}
});
// Parameter text for this parser
UrlParsingFilter.paramText = "url [raidFilter]";
// Pattern to match different link types
UrlParsingFilter.urlPatterns = {
"pastebin": /(?:http:\/\/)?(?:www\.)?pastebin\.com\/(.+)/i,
"cconoly": /(?:http:\/\/)?(?:www\.)?cconoly\.com\/lots\/raidlinks\.php/i
};
/************************************/
/*********** Styles Tab *************/
/************************************/
RaidMenuTab.create(
{
tabName: "Profile View",
tabHeader: "UGUP Character Profile Viewer",
tabPosition: 40,
initPane: function()
{
var wrapper = document.createElement("div");
var entryArea = document.createElement("div");
entryArea.id = "CharacterViewMenu-EntryArea";
var usernameLabel = document.createElement("label");
usernameLabel.id = "CharacterViewMenu-UsernameLabel";
usernameLabel.appendChild(document.createTextNode("Username: "));
usernameLabel.title = "This is the name of the user on their platform, not their in game character name";
var usernameBox = document.createElement("input");
usernameBox.type = "text";
usernameBox.id = "CharacterViewMenu-UsernameBox";
usernameLabel.appendChild(usernameBox);
entryArea.appendChild(usernameLabel);
var platformPicker = document.createElement("select");
platformPicker.id = "CharacterViewMenu-PlatformSelect";
var opt;
for (var platform in UGUP.PLATFORM) {
if (UGUP.PLATFORM.hasOwnProperty(platform) && typeof UGUP.PLATFORM[platform] !== "function") {
opt = document.createElement("option");
opt.value = platform;
opt.appendChild(document.createTextNode(UGUP.PLATFORM[platform].name));
opt.platform = UGUP.PLATFORM[platform];
platformPicker.appendChild(opt);
}
}
platformPicker.value = "KONG";
entryArea.appendChild(platformPicker);
var connector = DC_LoaTS_Helper.getUGUPConnector(null, UGUP.PLATFORM.KONG);
var renderArea = document.createElement("div");
renderArea.id = "CharacterViewMenu-RenderArea";
var runQueryButton = document.createElement("button");
runQueryButton.appendChild(document.createTextNode("Lookup User Profile"));
runQueryButton.id = "CharacterViewMenu-RunQueryButton";
runQueryButton.className = "CharacterViewMenu-Button";
runQueryButton.onclick = function() {
console.log("Running User Profile Lookup", this, arguments);
var platformOpt = platformPicker.selectedOptions[0],
username = usernameBox.value.trim();
DC_LoaTS_Helper.removeAllChildren(renderArea);
var loadingImg = document.createElement("img");
loadingImg.id = "CharacterViewMenu-RenderLoadingImg";
loadingImg.src = "http://i.imgur.com/NyArTaF.gif";
renderArea.appendChild(loadingImg);
connector.cfg.platform = platformOpt.platform;
console.log("About to fetch profile by username", connector);
connector.fetchUserProfileByUsername(username, function(response, model) {
if (model.id) {
console.log("Fetched Profile: ", response, model);
model._modelType.render(model, connector, function(el){
console.log("Rendered Profile", el);
DC_LoaTS_Helper.removeAllChildren(renderArea);
renderArea.appendChild(el);
});
}
else {
console.log("Error Fetching Profile: ", response, model);
var el = document.createElement("div");
var errMsg = "";
if (model.code == 402) {
errMsg = "User " + username + " has opted out of UgUp.";
}
else {
errMsg = "Failed to load profile for " + username + ": " + model.reason + " (Code: " + model.code + ")";
}
el.appendChild(document.createTextNode(errMsg));
DC_LoaTS_Helper.removeAllChildren(renderArea);
renderArea.appendChild(el);
}
});
};
entryArea.appendChild(runQueryButton);
wrapper.appendChild(entryArea);
var facebookLookupLink = document.createElement("a");
facebookLookupLink.href = "http://findmyfacebookid.com/";
facebookLookupLink.target = "_blank";
facebookLookupLink.appendChild(document.createTextNode("Lookup a Facebook Username (id) from Profile Url"));
wrapper.appendChild(facebookLookupLink);
wrapper.appendChild(renderArea);
this.pane.appendChild(wrapper);
}
});
/************************************/
/********** Formatting Tab **********/
/************************************/
// Class to manage the formatting tab in the raid tab in the popup menu
RaidMenuTab.create(
{
tabName: "Formatting",
tabPosition: 30,
initPane: function()
{
var messageFormatHeader = document.createElement("h2");
messageFormatHeader.className = "RaidMenuTab-SectionHeader";
messageFormatHeader.update("Message Format");
this.pane.appendChild(messageFormatHeader);
var messageFormatDescription = document.createElement("p");
messageFormatDescription.className = "RaidMenuTab-SectionDescription";
messageFormatDescription.update("The format of raid links as they will appear in chat.");
this.pane.appendChild(messageFormatHeader);
this.messageFormatTextArea = document.createElement("textarea");
this.messageFormatTextArea.id = "FormattingTab-MessageFormatTextArea";
this.messageFormatTextArea.setAttribute("placeholder", RaidLink.defaultMessageFormat);
this.currentMessageFormat = DC_LoaTS_Helper.getMessageFormat();
this.messageFormatTextArea.value = this.currentMessageFormat.replace("{line}", "\n");
DC_LoaTS_Helper.registerEventHandler(this.messageFormatTextArea, "input", this.handleMessageFormatInput.bind(this));
this.pane.appendChild(this.messageFormatTextArea);
var saveButton = document.createElement("button");
saveButton.update("Save");
saveButton.className = "FormattingTab-Button";
DC_LoaTS_Helper.registerEventHandler(saveButton, "click",
function()
{
holodeck.processChatCommand("/raidformat " + this.currentMessageFormat);
}.bind(this)
);
this.pane.appendChild(saveButton);
var cancelButton = document.createElement("button");
cancelButton.update("Cancel");
cancelButton.className = "FormattingTab-Button";
DC_LoaTS_Helper.registerEventHandler(cancelButton, "click",
function()
{
this.currentMessageFormat = DC_LoaTS_Helper.getMessageFormat();
this.messageFormatTextArea.value = this.currentMessageFormat.replace("{line}", "\n");
this.handleMessageFormatInput();
}.bind(this)
);
this.pane.appendChild(cancelButton);
var defaultButton = document.createElement("button");
defaultButton.update("Reset to default");
defaultButton.className = "FormattingTab-Button";
DC_LoaTS_Helper.registerEventHandler(defaultButton, "click",
function()
{
holodeck.processChatCommand("/raidformat reset");
}.bind(this)
);
this.pane.appendChild(defaultButton);
var samplePostHeader = document.createElement("h2");
samplePostHeader.className = "RaidMenuTab-SectionHeader";
samplePostHeader.update("Sample Post");
samplePostHeader.style.marginTop = "15px";
this.pane.appendChild(samplePostHeader);
// --- Sample Link --- \\
// Create the sample raid link area to display the results of the format
var raidStorage = RaidManager.fetchStorage();
// Find any valid link to use as a sample
//TODO: Customizable sample
for (var id_hash in raidStorage)
{
this.sampleRaidLink = raidStorage[id_hash].raidLink;
var tmpRaid = this.sampleRaidLink.getRaid();
// Don't sample with invalid links (same full name and id, essentially Unknown raids)
// Don't pick raids with the same FS and OS (size 250 raids)
if (tmpRaid.fullName === tmpRaid.id || tmpRaid.size === 250)
{
continue;
}
break;
}
// If there wasn't a valid sample in the local storage, generate one
if (typeof this.sampleRaidLink === "undefined")
{
// Will not have state info, though
this.sampleRaidLink = new RaidLink(9999999999, "hash11hash", 4, "ragebeasts");
}
this.messageFormatExampleLinkContainer = document.createElement("div");
var p = document.createElement("p");
p.className = "even";
this.messageFormatExampleLinkContainer.appendChild(p);
var timestamp = document.createElement("p");
timestamp.className = "timestamp";
timestamp.appendChild(document.createTextNode(new Date().format("mmm d - h:MMTT")));
p.appendChild(timestamp);
var username = holodeck._active_user._attributes._object.username;
var usernameSpan = document.createElement("span");
usernameSpan.setAttribute("username", username);
usernameSpan.className = "username chat_message_window_username";
usernameSpan.update(username);
p.appendChild(usernameSpan);
var separatorSpan = document.createElement("span");
separatorSpan.className = "separator";
separatorSpan.update(": ");
p.appendChild(separatorSpan);
this.messageSpan = document.createElement("span");
this.className = "message";
this.messageSpan.innerHTML = this.sampleRaidLink.getFormattedRaidLink();
p.appendChild(this.messageSpan);
var clearSpan = document.createElement("span");
clearSpan.className = "clear";
p.appendChild(clearSpan);
this.pane.appendChild(this.messageFormatExampleLinkContainer);
},
handleMessageFormatInput: function()
{
this.currentMessageFormat = this.messageFormatTextArea.value.replace(/(?:\r\n|\r|\n)/g, "{line}");
this.messageSpan.innerHTML = this.sampleRaidLink.getFormattedRaidLink(this.currentMessageFormat);
}
});
/************************************/
/********* Preferences Tab **********/
/************************************/
// Class to manage a tab in the raid tab in the popup menu
RaidMenuTab.create(
{
tabName: "Preferences",
tabPosition: 100,
rightClickVisitedKey: "RightClickVisited",
ignoreInvalidCommandsKey: "IgnoreInvalidCommands",
hideVisitedRaidsKey: "HideVisitedRaids",
hideWorldChatKey: "HideWorldChat",
loadRaidsInBackgroundKey: "LoadRaidsInBackground",
reportDeadRaidsKey: "ReportDeadRaids",
useQueryTimeDeltaKey: "UseQueryTimeDelta",
loadRaidsInBackgroundDelayKey: "LoadRaidsInBackgroundDelay",
leftClickToWhisperKey: "LeftClickToWhisper",
rightClickUserMenuKey: "RightClickUserMenu",
linkifyUrlsKey: "LinkifyUrls",
chatTimestampRight: "ChatTimestampRight",
initPane: function()
{
var wrapper = document.createElement("div");
var me = this;
var rightClickOption = me.createSimpleOptionHTML(
"PreferencesMenu-RightClickVisitedInput",
"boolean",
DC_LoaTS_Helper.getPref(me.rightClickVisitedKey, true),
"Right-click should mark raids visited.",
"If checked, right clicking a link will mark it visited",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.rightClickVisitedKey, this.checked);
}
}
);
wrapper.appendChild(rightClickOption.wrapper);
var ignoreInvalidOption = me.createSimpleOptionHTML(
"PreferencesMenu-IgnoreInvalidCommandsInput",
"boolean",
DC_LoaTS_Helper.getPref(me.ignoreInvalidCommandsKey, true),
"Ignore invalid commands.",
"If checked, any command that is not a valid command will be ignored",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.ignoreInvalidCommandsKey, this.checked);
}
}
);
wrapper.appendChild(ignoreInvalidOption.wrapper);
var hideVisitedOption = me.createSimpleOptionHTML(
"PreferencesMenu-HideVisitedRaidsInput",
"boolean",
DC_LoaTS_Helper.getPref(me.hideVisitedRaidsKey, false),
"Hide Visited and Completed Raids.",
"If checked, Visited and Completed Raids posted into chat will be hidden",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.hideVisitedRaidsKey, this.checked);
DC_LoaTS_Helper.handleIgnoreVisitedRaids(this.checked);
}
}
);
wrapper.appendChild(hideVisitedOption.wrapper);
var hideWorldChat = me.createSimpleOptionHTML(
"PreferencesMenu-HideWorldChatInput",
"boolean",
DC_LoaTS_Helper.getPref(me.hideWorldChatKey, false),
"Hide World Chat",
"If checked, World Chat will not be visible",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.hideWorldChatKey, this.checked);
DC_LoaTS_Helper.handleHideWorldChat(this.checked);
}
}
);
wrapper.appendChild(hideWorldChat.wrapper);
var loadBackgroundOption = me.createSimpleOptionHTML(
"PreferencesMenu-LoadRaidsInBackgroundInput",
"boolean",
DC_LoaTS_Helper.getPref(me.loadRaidsInBackgroundKey, true),
"Load Raids in Background (Skips the Play Now screen when loading raids)",
"If checked, raids won't load in the game area.",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.loadRaidsInBackgroundKey, this.checked);
}
}
);
wrapper.appendChild(loadBackgroundOption.wrapper);
var loadRaidsInBackgroundDelayOption = me.createSimpleOptionHTML(
"PreferencesMenu-LoadRaidsInBackgroundDelayInput",
"text",
DC_LoaTS_Helper.getPref(me.loadRaidsInBackgroundDelayKey, 50),
"ms. Time Between Loading Raids (Only applicable if Load Raids in Background is enabled)",
"Default = 50; No delay = 0; Half a second = 500.",
{
size: 4,
maxlength: 4,
onchange: function()
{
var v = this.value;
if (/^\d+$/.test(v))
{
DC_LoaTS_Helper.setPref(me.loadRaidsInBackgroundDelayKey, v);
}
else
{
holodeck.activeDialogue().raidBotMessage("Load Raids In Background Delay: Please enter only numbers.");
}
}
}
);
wrapper.appendChild(loadRaidsInBackgroundDelayOption.wrapper);
var leftClickToWhisperOption = me.createSimpleOptionHTML(
"PreferencesMenu-LeftClickToWhisper",
"boolean",
DC_LoaTS_Helper.getPref(me.leftClickToWhisperKey, true),
"Left click a user's name in chat to whisper them (Requires refresh after change)",
"Overrides default functionality of showing mini-profile",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.leftClickToWhisperKey, this.checked);
// Attach or detach the handlers
DC_LoaTS_Helper.handleMessageWindowClickHandler();
// Ask the user if they want to refresh now
if (window.confirm && window.confirm("The page needs to be refreshed in order for your preference change to take effect. Confirm to refresh now; Cancel to refresh later.")) {
document.location.reload();
}
}
}
);
wrapper.appendChild(leftClickToWhisperOption.wrapper);
var rightClickUserMenuOption = me.createSimpleOptionHTML(
"PreferencesMenu-RightClickUserMenu",
"boolean",
DC_LoaTS_Helper.getPref(me.rightClickUserMenuKey, true),
"Right click a user's name to show an action menu",
"Contains options to show their profile, friend them, and eventually more",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.rightClickUserMenuKey, this.checked);
// Attach or detach the handlers
DC_LoaTS_Helper.handleMessageWindowContextMenuHandler();
}
}
);
wrapper.appendChild(rightClickUserMenuOption.wrapper);
var linkifyUrlsOption = me.createSimpleOptionHTML(
"PreferencesMenu-LinkifyUrls",
"boolean",
DC_LoaTS_Helper.getPref(me.linkifyUrlsKey, true),
"Make URLs and #wiki's posted to chat be clickable links",
"When someone posts a URL, automatically make it a link that will open in a new tab",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.linkifyUrlsKey, this.checked);
}
}
);
wrapper.appendChild(linkifyUrlsOption.wrapper);
var chatTimestampRightOption = me.createSimpleOptionHTML(
"PreferencesMenu-ChatTimestampRight",
"boolean",
DC_LoaTS_Helper.getPref(me.chatTimestampRight, true),
"Chat timestamps on the right",
"Moves the chat timestamps down and to the right",
{
onclick: function()
{
DC_LoaTS_Helper.setPref(me.chatTimestampRight, this.checked);
DC_LoaTS_Helper.handleMoveChatTimestamps(this.checked);
}
}
);
wrapper.appendChild(chatTimestampRightOption.wrapper);
this.pane.appendChild(wrapper);
}
});
/************************************/
/************ Raids Tab *************/
/************************************/
// Class to manage a tab in the raid tab in the popup menu
RaidMenuTab.create(
{
tabName: "Raids",
tabHeader: "Seen Raids",
tabPosition: 10,
initPane: function()
{
this.currentRaidFilter;
// this.header = document.createElement("h1");
// this.header.className = "RaidMenuTab-Header";
// this.header.update("Seen Raids");
// this.pane.appendChild(this.header);
//
this.searchWrapper = document.createElement("div");
this.searchWrapper.id = "RaidsMenu-SearchWrapper";
this.pane.appendChild(this.searchWrapper);
this.searchWrapper.update("Search for raids: ");
this.searchBox = document.createElement("input");
this.searchBox.id = "RaidsMenu-SearchBox";
this.searchBox.observe("input", function(e)
{
if (typeof this._searchBoxTypingTimeout)
this.onSearchBoxChange();
}.bind(this));
this.searchWrapper.appendChild(this.searchBox);
var afterSearchWrapper = document.createElement("div");
afterSearchWrapper.update("RaidBot sees: /seenraids ");
this.searchWrapper.appendChild(afterSearchWrapper);
this.searchBoxNormalized = document.createElement("span");
this.searchBoxNormalized.id = "RaidsMenu-SearchBoxNormalized";
afterSearchWrapper.appendChild(this.searchBoxNormalized);
this.resultsBox = document.createElement("div");
this.resultsBox.id = "RaidsMenu-ResultsBox";
this.pane.appendChild(this.resultsBox);
},
onSearchBoxChange: function()
{
if (this.searchBox.value.length < 3)
{
this.clearResults();
return;
}
var tmpFilter = new RaidMultiFilter(this.searchBox.value);
if (!this.currentRaidFilter || this.currentRaidFilter.toString() != tmpFilter.toString())
{
this.currentRaidFilter = tmpFilter;
this.searchBoxNormalized.update(this.currentRaidFilter.toString());
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Retrieve the anchor tag format
var linkFormat = DC_LoaTS_Helper.getLinkFormat();
var raidLinks = RaidManager.fetchByFilter(this.currentRaidFilter);
var raidsHTML = "";
for (var i = 0; i < raidLinks.length; i++)
{
raidsHTML += (i+1) + ") " + raidLinks[i].getFormattedRaidLink(messageFormat.replace("{image}", ""), linkFormat) + "<br><br>";
}
this.resultsBox.innerHTML = raidsHTML;
}
},
getRaidRow: function(link)
{
var raid = link.getRaid();
},
clearResults: function()
{
this.resultsBox.childElements().invoke("remove");
}
});
// RaidMenuTab.raidSearchResultsFields = [{"raid."}]
/************************************/
/*********** Styles Tab *************/
/************************************/
// Class to manage a tab in the raid tab in the popup menu
// RaidMenuTab.create(
// {
// tabName: "Styles",
// tabHeader: "Raid Styles",
// tabPosition: 20,
// optionIndex: 0,
//
// initPane: function()
// {
//
// }
// });
/************************************/
/************ Utils Tab *************/
/************************************/
// Class to manage a tab in the raid tab in the popup menu
// RaidMenuTab.create(
// {
// tabName: "Utils",
// tabHeader: "Utilities (Under Construction)",
// tabPosition: 50,
//
// initPane: function()
// {
// //TODO: Fill out utilities tab
// }
//
// });
RaidCommand.create(
{
commandName: "autoupdate",
aliases: [],
// Custom parsing
/*parsingClass: ,*/
// Custom parsing means custom param text
paramText: "[on/off]",
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {sucess: true};
//
switch(params.toLowerCase())
{
// If there was no command, display current state
case "":
var updatesState = GM_getValue(DC_LoaTS_Properties.storage.autoUpdate);
if (typeof updatesState == "undefined")
{
updatesState = true;
}
if (updatesState)
{
ret.statusMessage = "Automatic update checks are <code>ON</code>. Turn them " + this.getCommandLink("OFF","OFF?");
}
else
{
ret.statusMessage = "Automatic update checks are <code>OFF</code>. Turn them " + this.getCommandLink("ON","ON?");
}
break;
// Turn updates on
case "on":
var updatesState = GM_setValue(DC_LoaTS_Properties.storage.autoUpdate, true);
ret.statusMessage = "Automatic update checks are now <code>ON</code>";
break;
// Turn updates off
case "off":
var updatesState = GM_setValue(DC_LoaTS_Properties.storage.autoUpdate, false);
ret.statusMessage = "Automatic update checks are now <code>OFF</code>";
break;
// Not sure what this command was supposed to be
default:
ret.statusMessage = "Did not understand command: <code>" + text + "</code>";
ret.success = false;
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Get autoupdate status"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/autoupdate toggle</code>\n";
helpText += "Sets whether or not this script should automatically check for updates.\n";
helpText += "where <code>toggle</code> <i>(optional)</i> is on or off\n";
helpText += "\n";
helpText += "If <code>toggle</code> is omitted, then the current status will be shown\n";
helpText += "\n";
helpText += "If there is an update to install and checks are on, when the page loads, a bar will appear";
helpText += " at the top of the screen offering the option to update.\n";
helpText += "\n";
if (GM_getValue(DC_LoaTS_Properties.storage.autoUpdate, false)) {
helpText += "Updates are currently ON. Turn them " + this.getCommandLink("OFF","OFF?") + "\n";
}
else {
helpText += "Updates are currently OFF. Turn them " + this.getCommandLink("ON","ON?") + "\n";
}
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "checkload",
aliases: ["loadcheck", "check", "load"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
var data = DC_LoaTS_Helper.autoLoader;
if (data) {
var fractionComplete = data.raidLinks.length / data.startingLinkCount,
percentComplete = Math.round(fractionComplete * 100);
timeElapsed = new Date()/1 - data.startTime,
timeRemaining = timeElapsed / fractionComplete;
ret.statusMessage = "Attempted " + data.counter.attempted + " of " + data.startingLinkCount + " raids (" + percentComplete + "%) in " + timeElapsed + "ms.";
ret.statusMessage += "\nEstimated Time Remaining: " + timeRemaining + " ms."
ret.statusMessage += "\nCurrent Report: \n" + data.counter._generateReportText();
}
else {
ret.statusMessage = "No load being performed at this time.";
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Print the timer report"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/timerdata</code>\n";
helpText += "Prints out timing and performance data about the script\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "clearchat",
aliases: ["cc", "cls", "clear", "clean"],
// No parsing
//parsingClass: ,
handler: function(deck, raidLink, params, text, context)
{
// Declare ret object
var ret = {success: true, statusMessage: "Chat cleared at " + (new Date().toLocaleString())};
// Load the raid from the link's url
holodeck.activeDialogue().clear();
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Clear chat?"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/clearchat</code>\n";
helpText += "Clears the text of the chat.\n";
helpText += "\n";
helpText += this.getCommandLink("","Clear chat now?") + "\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "clearraids",
aliases: ["clearraid", "raidclear", "raidsclear", "clearcache"],
parsingClass: RaidMultiFilter,
handler: function(deck, raidFilter, params, text, context)
{
// Declare ret object
ret = {};
// If the user wants to clear all raids
if (params && params.toLowerCase() === "all")
{
// Clear all raids stored in memory
RaidManager.clear();
// Notify the user
ret.statusMessage = "All raids have been cleared from script memory.";
ret.success = true;
}
// If there were no params.
// This used to clear all raids, but that was catching some people by surprise, :-P
else if (params.length == 0)
{
// Notify the user
ret.statusMessage = "/" + this.getName() + " no longer clears all raids. Use " + this.getCommandLink("all") + " to clear all raids or " + this.getCommandLink("help") + " to find out more about this command.";
ret.success = true;
}
// The user posted specific criteria
else
{
// Find all raids that match the user's criteria
var raidLinks = RaidManager.fetchByFilter(raidFilter);
// If the RaidManager executed successfully
if (typeof raidLinks !== "undefined")
{
// If we didn't match a single raid
if (raidLinks.length == 0)
{
ret.statusMessage = "Could not locate any raids to clear matching <code>" + raidFilter.toString() + "</code>";
ret.success = true;
}
// If we did match some raids
else
{
// Delete all found raids from memory
RaidManager.clear(raidLinks);
// Notify the user
ret.statusMessage = "Cleared " + raidLinks.length + " raids from memory.";
ret.success = true;
}
}
// RaidManager failed
else
{
ret.statusMessage = "Did not understand command: <code>/" + this.getName() + " " + raidFilter.toString() + "</code>";
ret.success = false;
}
}
return ret;
},
getOptions: function()
{
//TODO: Decide what options should go here
var commandOptions;
if (this.parser.name == "all")
{
commandOptions = {
initialText: {
text: "Clear all raids from memory"
}
};
}
else
{
commandOptions = {
initialText: {
text: "Clear raids from memory matching " + this.parser.toString()
}
};
}
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/clearraids raidName difficully {state:stateName} {age: timeFormat} {fs: fsFormat}</code>\n";
helpText += "Deletes all raids from script memory.\n";
helpText += "where <code>raidName</code> <i>(optional)</i> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "where <code>stateName</code> <i>(optional)</i> is either seen or visited\n";
helpText += "where <code>timeFormat</code> <i>(optional)</i> is like <code><24h</code>, <code><30m</code>, or <code>>1d</code>\n";
helpText += "where <code>fsFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "\n"
helpText += "<b>Examples:</b>\n"
helpText += "\n"
helpText += "<i>Clear all seen but keep all visited raids<i>\n"
helpText += "<code>/clearraids {state:seen}</code>\n"
helpText += "\n"
helpText += "<i>Clear all raids you've seen, but not visited that you saw posted in the last 5 hours<i>\n"
helpText += "<code>/clearraids {state:seen} {age: <5h}</code>\n"
helpText += "\n"
helpText += "<i>Clear all raids you've seen, but not visited that you saw posted in the last 5 hours that have FS < 1M<i>\n"
helpText += "<code>/clearraids {state:seen} {age: <5h} {fs:<1M}</code>\n"
helpText += "\n"
helpText += "<i>Clear all normal telemachus raids that you've visited before\n"
helpText += "<code>/clearraids tele 1 {state:visited}</code>\n"
helpText += "\n"
helpText += "<i>Clear all void killer raids in memory\n"
helpText += "<code>/clearraids killer</code>\n"
helpText += "\n"
helpText += "<i>Clear all void nightmare vorden raids\n"
helpText += "<code>/clearraids vorden 4</code>\n"
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "diaversai",
aliases: ["dia"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Borrowed from: http://stackoverflow.com/a/5915122/1449525
function randomItem(items) {
return items[Math.floor(Math.random()*items.length)];
}
// Declare ret object
var ret = {success: true},
users_list = holodeck._active_dialogue._user_manager._active_room._users_list,
name = (params.trim().length > 0 ? params.trim() : randomItem(users_list).username),
quote = randomItem(DC_LoaTS_Helper.quotables);
quote = quote.format(randomItem(["dia", "diaversai"]), name);
holodeck._chat_window._active_room.sendRoomMessage(quote);
if (holodeck.username() !== "diaversai") {
DC_LoaTS_Helper.donateSpam("You, too, can have a command for a {donation of $10}!");
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Hear what dia has to say"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/diaversai</code>\n";
helpText += "Hear what <a href='http://www.kongregate.com/accounts/diaversai'>dia</a> has to say.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "exportraids",
parsingClass: RaidMultiFilter,
aliases: ["exportraid", "er"],
handler: function(deck, raidFilter, params, text, context)
{
// Capture the start time of the query
var queryStartTime = new Date()/1;
// Declare ret object
var ret = {};
// Find all raids that match the user's criteria
var raidLinks = RaidManager.fetchByFilter(raidFilter);
// If the RaidManager executed successfully
if (typeof raidLinks != "undefined")
{
// If we didn't match a single raid
if (raidLinks.length == 0)
{
if (params.length == 0)
{
ret.statusMessage = "Could not locate any seen raids in memory.";
}
else
{
ret.statusMessage = "Could not locate any seen raids matching <code>" + params + "<code>";
}
// The lookup succeeded, we just didn't find anything
ret.success = true;
}
// If we did match some raids
else
{
// Capture all the text in one block
var outputText = "";
// For every link we found
for (var i = 0; i < raidLinks.length; i++)
{
// Print matched links
outputText += raidLinks[i].getURL() + "\n";
}
// Export the data we found
//DC_LoaTS_Helper.forceDownload(outputText, raidFilter.toString().replace(" ", "_").replace(/\W/gi, ""));
RaidMenuTab.createDataDumpTab(outputText, raidFilter.toString());
// Print out the stats for the query
ret.statusMessage = "<code>/" + this.commandName + " " + raidFilter.toString() + "</code> took " + (new Date()/1 - queryStartTime) + " ms and exported " + raidLinks.length + " results.";
// Succeeded
ret.success = true;
}
}
// RaidManager failed
else
{
ret.statusMessage = "Did not understand command: <code>" + text + "</code>";
ret.success = false;
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Export matching data"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/exportraids raidName difficulty {state: stateName} {age: timeFormat} {fs: fsFormat} {count: numberResults} {page: resultsPage}</code>\n";
helpText += "Exports to file raids that you've seen before in chat"
helpText += "where <code>raidName</code> <i>(optional)</i> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "where <code>stateName</code> <i>(optional)</i> is either seen or visited\n";
helpText += "where <code>timeFormat</code> <i>(optional)</i> is like <code><24h</code>, <code><30m</code>, or <code>>1d</code>\n";
helpText += "where <code>fsFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>numberResults</code> <i>(optional)</i> is the number of results to display\n";
helpText += "where <code>resultsPage</code> <i>(optional)</i> is if you've set count, then which page to show. If page is omitted, it will show the first page of results.\n";
helpText += "\n";
helpText += "Example:\n";
helpText += "Export all seen psychic colonels, including visited: \n";
helpText += this.getCommandLink("psy {state: !completed}") + "\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "exportusers",
aliases: ["eu"],
doNotEnumerateInHelp: true,
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true, message: "Exported Users"};
var playerData = "Date: " + new Date() + "\n" +
"User,Level,Admin,Developer,K+\n";
for (var i = 0; i < active_room._users_list.length; i++) {
var ufr = active_room._users_list[i];
playerData += [ufr.username, ufr._level, ufr._admin, ufr._developer, ufr._premium].join(",") + "\n";
}
RaidMenuTab.createDataDumpTab(playerData, "KChat Users");
return ret;
},
getOptions: function()
{
return {};
},
buildHelpText: function()
{
return "";
}
}
);
RaidCommand.create(
{
commandName: "farmvalue",
aliases: [],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
var farmText = "<table>";
farmText += "<tr><th>Stamina Raid</th><th>Norm Farm Val</th><th>NM Farm Val</th></tr>"
farmText += "<tr><td>Void Killer</td><td>19.8</td><td>6.6</td></tr>";
farmText += "<tr><td>Ragebeasts</td><td>12.6</td><td>6.3</td></tr>";
farmText += "<tr><td>Telemachus</td><td>11.0</td><td>3.7</td></tr>";
farmText += "<tr><td>CC Colonel</td><td>9.3</td><td>2.3</td></tr>";
farmText += "<tr><td>Supreme Cybertollahs</td><td>8.4</td><td>2.1</td></tr>";
farmText += "<tr><td>Carnus</td><td>6.6</td><td>2.6</td></tr>";
farmText += "<tr><td>Carnifex</td><td>6.3</td><td>2.5</td></tr>";
farmText += "<tr><td>Rautha</td><td>5.9</td><td>1.5</td></tr>";
farmText += "<tr><td>Vespasia's Android</td><td>5.6</td><td>1.6</td></tr>";
farmText += "<tr><td>CC Cruiser</td><td>5.3</td><td>1.3</td></tr>";
farmText += "<tr><td>Assassin</td><td>4.5</td><td>1.4</td></tr>";
farmText += "<tr><td>Vorden</td><td>4.2</td><td>1.4</td></tr>";
farmText += "<tr><td>CC General</td><td>4.0</td><td>1.0</td></tr>";
farmText += "<tr><td>Warmaster </td><td>3.7</td><td>0.8</td></tr>";
farmText += "<tr><td>Robotic Rautha</td><td>3.7</td><td>0.9</td></tr>";
farmText += "<tr><td>CC Sentinel</td><td>3.4</td><td>0.8</td></tr>";
farmText += "<tr><td>Mermara</td><td>3.3</td><td>0.7</td></tr>";
farmText += "<tr><td>Purple Lion</td><td>3.2</td><td>1.1</td></tr>";
farmText += "<tr><td>Cybersmash</td><td>3.1</td><td>1.0</td></tr>";
farmText += "<tr><td>Blood Alley Gang</td><td>2.8</td><td>0.9</td></tr>";
farmText += "<tr><td>Bashan</td><td>2.3</td><td>0.6</td></tr>";
farmText += "<tr><td>Missiles</td><td>2.3</td><td>0.6</td></tr>";
farmText += "<tr><td>Tulk</td><td>2.2</td><td>0.6</td></tr>";
farmText += "<tr><td>Scarlet Harlot</td><td>2.0</td><td>0.6</td></tr>";
farmText += "<tr><td>Agony Ecstacy</td><td>1.8</td><td>0.7</td></tr>";
farmText += "<tr><td>Sludge Serpent</td><td>1.8</td><td>0.7</td></tr>";
farmText += "<tr><td>Lupin</td><td>1.7</td><td>0.7</td></tr>";
farmText += "<tr><td>Mercury</td><td>1.6</td><td>0.6</td></tr>";
farmText += "<tr><td>Storm Commander</td><td>1.6</td><td>0.5</td></tr>";
farmText += "<tr><td>Sun-Xi</td><td>1.5</td><td>0.6</td></tr>";
farmText += "<tr><td>Lt. Targe</td><td>1.4</td><td>0.6</td></tr>";
farmText += "<tr><td>Guldax Quibberath</td><td>1.4</td><td>0.5</td></tr>";
farmText += "<tr><td>Bachanghenfil</td><td>1.3</td><td>0.3</td></tr>";
farmText += "<tr><td>Warden Ramiro</td><td>1.3</td><td>0.5</td></tr>";
farmText += "<tr><td>Nemo</td><td>1.3</td><td>0.5</td></tr>";
farmText += "<tr><td>Gut-Phager</td><td>1.2</td><td>0.2</td></tr>";
farmText += "<tr><td>Vulture Gunship</td><td>1.2</td><td>0.5</td></tr>";
farmText += "<tr><td>Caligula</td><td>1.2</td><td>0.4</td></tr>";
farmText += "<tr><td>Cyborg Shark</td><td>1.1</td><td>0.3</td></tr>";
farmText += "<tr><td>Guan Yu</td><td>1.1</td><td>0.3</td></tr>";
farmText += "<tr><td>Pi</td><td>1.1</td><td>0.4</td></tr>";
farmText += "<tr><td>Sigurd</td><td>1.1</td><td>0.3</td></tr>";
farmText += "<tr><td>Bile Beat</td><td>1.0</td><td>0.3</td></tr>";
farmText += "<tr><td>Fleet Com.</td><td>0.9</td><td>0.3</td></tr>";
farmText += "<tr><td>Reaver</td><td>0.9</td><td>0.2</td></tr>";
farmText += "<tr><td>Cult-Mistress</td><td>0.9</td><td>0.2</td></tr>";
farmText += "<tr><td>Nick</td><td>0.9</td><td>0.3</td></tr>";
farmText += "<tr><td>Cake</td><td>0.9</td><td>0.2</td></tr>";
farmText += "<tr><td>The Hat</td><td>0.8</td><td>0.3</td></tr>";
farmText += "<tr><td>Xenocide</td><td>0.7</td><td>0.2</td></tr>";
farmText += "<tr><td>Colossa</td><td>0.7</td><td>0.1</td></tr>";
farmText += "<tr><td>Blob</td><td>0.5</td><td>0.2</td></tr>";
farmText += "<tr><td>Councilor</td><td>0.5</td><td>0.1</td></tr>";
farmText += "<tr><td>Boar</td><td>0.5</td><td>0.1</td></tr>";
farmText += "<tr><td>R. Hunter</td><td>0.4</td><td>0.2</td></tr>";
farmText += "<tr><td>G. Rahn</td><td>0.3</td><td>0.1</td></tr>";
farmText += "<tr><td>Dule's Bot</td><td>0.3</td><td>0.1</td></tr>";
farmText += "<tr><td></td><td></td><td></td></tr>";
farmText += "<tr><th>Energy Raid</th><th>Norm Farm Val</th><th>NM Farm Val</th></tr>";
farmText += "<tr><td>Vince Vortex</td><td>1.7</td><td>0.4</td></tr>";
farmText += "<tr><td></td><td></td><td></td></tr>";
farmText += "<tr><th>Honor Raid</th><th>Norm Farm Val</th><th>NM Farm Val</th></tr>";
farmText += "<tr><td>Krakak Swarm</td><td>5.6</td><td>1.9</td></tr>";
farmText += "<tr><td>Infected Squad</td><td>4.4</td><td>1.3</td></tr>";
farmText += "<tr><td>Flying Saucers</td><td>4.0</td><td>1.6</td></tr>";
farmText += "<tr><td>Lurking Horror</td><td>3.7</td><td>0.9</td></tr>";
farmText += "<tr><td>Kang</td><td>3.4</td><td>1.0</td></tr>";
farmText += "<tr><td>Tourniquet 7</td><td>2.4</td><td>0.9</td></tr>";
farmText += "<tr><td>Ship of the Damned</td><td>2.3</td><td>0.8</td></tr>";
farmText += "<tr><td>Wyrm</td><td>2.0</td><td>0.7</td></tr>";
farmText += "<tr><td>Death Flora</td><td>1.9</td><td>0.6</td></tr>";
farmText += "<tr><td>Crossbones Squadron</td><td>1.6</td><td>0.5</td></tr>";
farmText += "<tr><td>Celebrator</td><td>1.6</td><td>0.5</td></tr>";
farmText += "<tr><td>Shadows</td><td>1.4</td><td>0.3</td></tr>";
farmText += "<tr><td>Mr. Justice</td><td>1.1</td><td>0.3</td></tr>";
farmText += "<tr><td>Rylattu Exterminators</td><td>1.1</td><td>0.4</td></tr>";
farmText += "<tr><td>Colonel Mustard</td><td>1.1</td><td>0.4</td></tr>";
farmText += "<tr><td>Luna</td><td>1.0</td><td>0.4</td></tr>";
farmText += "<tr><td>Genesis</td><td>0.9</td><td>0.5</td></tr>";
farmText += "<tr><td>Grislak</td><td>0.9</td><td>0.3</td></tr>";
farmText += "<tr><td>Interceptor</td><td>0.9</td><td>0.2</td></tr>";
farmText += "<tr><td>Peacemaker 500</td><td>0.8</td><td>0.3</td></tr>";
farmText += "<tr><td>Qin Legion</td><td>0.8</td><td>0.3</td></tr>";
farmText += "<tr><td>Juggernaut</td><td>0.7</td><td>0.2</td></tr>";
farmText += "<tr><td>Squid</td><td>0.7</td><td>0.2</td></tr>";
farmText += "<tr><td>Death Squadron</td><td>0.7</td><td>0.1</td></tr>";
farmText += "<tr><td>H. House</td><td>0.6</td><td>0.1</td></tr>";
farmText += "<tr><td>Devourer</td><td>0.6</td><td>0.1</td></tr>";
farmText += "<tr><td>Colby</td><td>0.5</td><td>0.1</td></tr>";
farmText += "<tr><td>Legacy Bot</td><td>0.4</td><td>0.1</td></tr>";
farmText += "<tr><td>Psi-Hound</td><td>0.2</td><td>0.1</td></tr>";
farmText += "<tr><td>Wahsh</td><td>0.0</td><td>0.0</td></tr>";
farmText += "</table>";
farmText += "<a href=\"" + DC_LoaTS_Properties.farmSpreadsheetURL + "\" target=\"_blank\">Source</a>\n";
deck.activeDialogue().raidBotMessage(farmText);
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Display farm values"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/farmvalue</code>\n";
helpText += "Displays the farm value of the raids per <a href=\"" +
DC_LoaTS_Properties.farmSpreadsheetURL + "\" target=\"_blank\">this spreadsheet</a>\n";
return helpText;
}
}
);
// Fetch raids command
RaidCommand.create(
{
commandName: "fetchraids",
aliases: ["fetch", "fr"],
parsingClass: UrlParsingFilter,
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: parser.known || parser.force || parser.cancel};
if (ret.success) {
DC_LoaTS_Helper.fetchAndLoadRaids(parser);
}
else {
if (!parser.known) {
ret.statusMessage = parser.getWorkingUrl() + " is not from a known raid host. Are you sure you wish to fetch from there? " + DC_LoaTS_Helper.getCommandLink("/fetchraids force " + params);
}
else {
ret.statusMessage = "Could not find a url in <code>" + text + "</code>";
}
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Load raids from: " + this.parser.getWorkingUrl()
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/fetchraids url</code>\n";
helpText += "where <code>url</code> is the url of a raid host of some kind\n";
helpText += "\n";
helpText += "Loads all raids from the url, or whichever ones match the filter\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "forum",
urlPattern: "http://www.legacyofathousandsuns.com/forum/search.php?do=process&sortby=rank&query=%searchString%",
// No parsing
/*parsingClass: ,*/
aliases: ["forums"],
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
var url = this.createURL(params);
window.open(url, "_blank");
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Search Forum for: " + this.processedText,
linkParams: {href: this.createURL(this.processedText), target: "_blank"},
doNotCallHandler: true,
followLink: true
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/forum searchText</code>\n";
helpText += "where <code>searchText</code> is what you want to search for on the LoTS Forum\n";
return helpText;
},
createURL: function(searchInput)
{
searchInput = searchInput || " ";
return this.urlPattern.replace("%searchString%",searchInput);
}
}
); RaidCommand.create(
{
commandName: "konginfo",
aliases: [],
doNotEnumerateInHelp: true, // Don't list this in the help
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
ret.statusMessage = "Kong ID: " + active_user.id() + "\n";
ret.statusMessage = "Kong Hash: " + active_user.gameAuthToken();
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Get important info about your Kong game."
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/konginfo</code>\n";
helpText += "Displays important Kong info.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "linkstate",
aliases: ["setcachestate", "setstate"],
parsingClass: RaidLinkstateParser,
/*public Object*/ handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
// If there's a raid link but no new state set
if (typeof parser.raidLink != "undefined" && typeof parser.state == "undefined")
{
// Get the current state
var state = RaidManager.fetchState(parser.raidLink);
// Print the current state
ret.statusMessage = parser.raidLink.getName() + "(raid_id: " + parser.raidLink.id + ") is in state " + state.niceText;
// Success
ret.success = true;
}
// If there's a raid link and there's a new state to set it to
else if (typeof parser.raidLink != "undefined" && typeof parser.state != "undefined")
{
// Get the actual state
var actualState = RaidManager.STATE.valueOf(parser.state);
DCDebug("About to set link to state: ");
DCDebug(actualState);
if (typeof actualState != "undefined")
{
// Set the new state for the raid link
RaidManager.store(parser.raidLink, actualState);
// Get the current state
var state = RaidManager.fetchState(parser.raidLink);
// Print the current state
ret.statusMessage = parser.raidLink.getName() + " (raid_id: " + parser.raidLink.id + ") is now in state " + state.niceText;
// Success
ret.success = RaidManager.STATE.equals(parser.state, state);
}
// Could not actually locate the state the user tried to set this link to
else
{
console.warn("Could not locate a corresponding state to " + parser.state + " in command " + text);
// Failed
ret.success = false;
// Print the failure message
ret.statusMessage = "Could not find match <code>" + parser.state + "</code> to a known state in <code>" + text + "</code>";
}
}
// Must not have found a raid link
else
{
// Failure
ret.success = false;
ret.statusMessage = "Could not find raid link in <code>" + text + "</code>";
}
return ret;
},
/*public Object*/ getOptions: function()
{
var linkState = RaidManager.fetchState(this.parser.raidLink);
var commandOptions = {
initialText: {
text: "Mark this " + linkState.niceText + " " + this.parser.getName(),
executable: false
}
};
for (var stateType in RaidManager.STATE)
{
if (typeof RaidManager.STATE[stateType] == "object" && linkState.id != RaidManager.STATE[stateType].id)
{
commandOptions["mark_" + stateType.toLowerCase()] = this.createStateOption(RaidManager.STATE[stateType]);
}
}
return commandOptions;
},
/*private Object*/ createStateOption: function(state)
{
return {
text: state.niceText,
callback: function(state){this.parser.state = state.id}.bind(this, state)
};
},
/*protected String*/ buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadraid url</code>\n";
helpText += "where <code>url</code> is the url of a raid\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "linktools",
aliases: ["ad", "advertise", "blatantselfpromotion", "getdoomscript"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
var toolsText;
if (params.indexOf("2") > -1) {
toolsText = this.getToolsText2();
}
else {
toolsText = this.getToolsText();
}
if (DC_LoaTS_Helper.getPref("LinkifyUrls", true)) {
toolsText = toolsText.replace(urlPattern, function(url) {
// Last minute check to make sure the regex didn't flub it
// If the url contains any weird characters, ", ', <, or >, just bail
return /["'><]/g.test(url) ? url : urlFormat.format(url);
});
}
// If the user passed in the "post" param or is using /ad, show it publicly
if (params.trim() === "post" || text.toLowerCase().trim() === "/ad") {
holodeck._chat_window._active_room.sendRoomMessage(toolsText);
}
else {
deck.activeDialogue().raidBotMessage(toolsText);
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Display tools links" + (this.commandText.indexOf("2") > -1 ? " (Page 2)" : "")
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/linktools [post]</code>\n";
helpText += "Displays a list of scripts that you might find useful.\n";
helpText += "<code>" + this.getCommandLink("") + "</code> will post the links just to you.\n";
helpText += "<code>" + this.getCommandLink("post") + "</code> will post the links to chat.\n";
helpText += "\n";
helpText += "Note: The <code>" + DC_LoaTS_Helper.getCommandLink("/ad") + "</code> alias automatically posts, unlike the other aliases.";
return helpText;
},
getToolsText: function() {
var toolsText = "\nGet doomscript: " + DC_LoaTS_Properties.scriptURL + " and any of: ";
toolsText += "\nRaidTools: " + DC_LoaTS_Properties.RaidToolsURL + " ";
toolsText += "\nQuickFriend: " + DC_LoaTS_Properties.QuickFriendURL + " ";
toolsText += "\nPlay Now Fix: " + DC_LoaTS_Properties.PlayNowFixURL;
return toolsText;
},
getToolsText2: function() {
var toolsText = "\nFleet Codes: " + DC_LoaTS_Properties.FleetCodesURL;
return toolsText;
}
}
);
//TODO: Remove Autoload alias. AutoLoad should be for incoming new raids, not loading existing ones
RaidCommand.create(
{
commandName: "loadall",
aliases: ["autoload"],
parsingClass: RaidMultiFilter,
handler: function(deck, raidFilter, params, text, context)
{
// Declare ret object
var ret = {};
var isCancelled = params === "cancel";
// Cancel the previous timer, if there is one
if (typeof DC_LoaTS_Helper.autoLoader !== "undefined" || isCancelled)
{
// Clear out the raidLinks array from the previous one.
// The timeout will detect that there are suddenly no more links
// and acknowledge the error state and quit.
DC_LoaTS_Helper.autoLoader.raidLinks.length = 0;
}
// This only works with a valid filter
if (!isCancelled && raidFilter && raidFilter.isValid())
{
// Fetch all the links
var raidLinks = RaidManager.fetchByFilter(raidFilter);
// If there were any matched links
if (raidLinks.length > 0)
{
ret.success = true;
ret.statusMessage = "AutoLoad starting for " + raidFilter.toString() + ". Loading " + raidLinks.length + " raids. " + this.getCommandLink("cancel", "Cancel");
DC_LoaTS_Helper.loadAll(raidLinks);
}
else
{
ret.statusMessage = "AutoLoad could not find any raids matching " + raidFilter.toString() + " to load.";
}
ret.success = true;
}
else if (!isCancelled)
{
ret.success = false;
ret.statusMessage = "Could not execute autoload due to invalid raid filter: '" + raidFilter.toString() + "'.";
}
else
{
ret.success = true;
ret.statusMessage = "AutoLoad cancelled.";
}
return ret;
},
getOptions: function()
{
//TODO: Better options here
var commandOptions = {
initialText: {
text: "Load all raids matching the filter"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadall raidFilter</code>\n";
helpText += "where <code>raidFilter</code> is a valid raid filter\n";
helpText += "\n";
helpText += "\nLoads all seen raids that match the given filter";
helpText += "\n";
helpText += "\nFor example, " + this.getCommandLink("colonel 4") + " would load all nightmare colonels not previously visited";
helpText += "\n";
helpText += "<b>This feature is implemented for experimental/academic purposes only and should not be distributed!</b>\n";
return helpText;
}
}
);
// Load CConoly command
RaidCommand.create(
{
commandName: "loadcconoly",
aliases: ["loadcc", "lcc", "cconoly", "cc", "loadcconolyraids", "loadccraids"],
// No predefined parsing
// parsingClass: none,
paramText: "[filter]",
handler: function(deck, parser, params, text, context)
{
if (params === "cancel") {
parser = new UrlParsingFilter("cancel");
}
else {
parser = new UrlParsingFilter(CConolyAPI.getRaidListUrl() + " " + params);
}
// Declare ret object
var ret = {success: parser.type === "cconoly"};
if (ret.success) {
DC_LoaTS_Helper.fetchAndLoadRaids(parser);
}
else {
ret.statusMessage = "Error processing command <code>" + text + "</code>";
DCDebug("Error with /lcc. Parser: ", parser);
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Load CConoly raids"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadcconoly raidName difficulty {state: stateName} {age: timeFormat} {size: sizeFormat} {fs: fsFormat} {os: osFormat} {zone: zoneNumber} {count: numberResults} {page: resultsPage}</code>\n";
helpText += "\n";
helpText += "Looks up raids from CConoly. "
helpText += "where <code>raidName</code> <i>(optional)</i> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "where <code>stateName</code> <i>(optional)</i> is either seen or visited\n";
helpText += "where <code>timeFormat</code> <i>(optional)</i> is like <code><24h</code>, <code><30m</code>, or <code>>1d</code>\n";
helpText += "where <code>sizeFormat</code> <i>(optional)</i> is like <code><100</code> or <code>250</code>\n";
helpText += "where <code>osFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>fsFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>zoneNumber</code> <i>(optional)</i> is like <code>1</code>, <code>Z14</code>, <code>ZA</code>, <code>WR</code>\n";
helpText += "where <code>numberResults</code> <i>(optional)</i> is the number of results to display\n";
helpText += "where <code>resultsPage</code> <i>(optional)</i> is if you've set count, then which page to show. If page is omitted, it will show the first page of results.\n";
helpText += "\n";
helpText += "<b>Examples:</b>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited that you saw posted in the last 5 hours<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen} {age: <5h}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited that you saw posted in the last 5 hours that have FS < 1M<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen} {age: <5h} {fs:<1M}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all normal telemachus raids that you've not visited before\n";
helpText += "<code>" + this.getCommandLink("tele 1 {state:!visited}") + " </code>\n";
helpText += "\n";
helpText += "<i>Find the first 10 void killer raids you've seen\n";
helpText += "<code>" + this.getCommandLink("killer {count: 10}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find the second 10 void killer raids you've seen\n";
helpText += "<code>" + this.getCommandLink("killer {count: 10} {page: 2}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all void nightmare vorden raids you've seen\n";
helpText += "<code>" + this.getCommandLink("vorden 4") + "</code>\n";
helpText += "\n";
helpText += "<i>Looking for <a href=\"http://www.zoywiki.com/index.php/LotS/experiment/multicoloredcloorian\" title=\"Cloorian Material needed to craft some Legendary pants\">Cloorian Material<a/>\n";
helpText += "<code>" + this.getCommandLink("vor|gan|nat 4 {age: <24h} {state: !visited}") + "</code>\n";
return helpText;
}
}
);
// Load Pastebin command
RaidCommand.create(
{
commandName: "loadpastebin",
aliases: ["loadpaste", "loadbin", "lpb", "loadraidbin", "lrb"],
parsingClass: UrlParsingFilter,
pastebinRawBase: "http://pastebin.com/raw.php?i=",
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: parser.type === "pastebin"};
if (ret.success) {
// Make sure to convert to the better url
parser.convertedUrl = this.pastebinRawBase + parser.regexMatch[1];
DC_LoaTS_Helper.fetchAndLoadRaids(parser);
}
else {
if (parser.getWorkingUrl()) {
ret.statusMessage = parser.url + " does not appear to be a pastebin link. Try " + DC_LoaTS_Helper.getCommandLink("/fetchraids " + params);
}
else {
ret.statusMessage = "Could not find a pastebin link in <code>" + text + "</code>";
}
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Load bin raids from: " + this.parser.getWorkingUrl()
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadpastebin pastebinURL raidFilter</code>\n";
helpText += "where <code>pastebinURL</code> is the url of a raid pastebin\n";
helpText += "where <code>raidFilter</code> (optional) is a seenraids style filter to limit what's loaded from the bin\n";
helpText += "\n";
helpText += "Loads all raids from the pastebin, or whichever ones match the filter\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "loadraid",
aliases: ["addraid", "joinraid", "loadraids", "raidload", "raidadd", "raidjoin", "lr"],
parsingClass: RaidLink,
handler: function(deck, raidLink, params, text, context)
{
// Declare ret object
var ret = {};
// Load the raid from the link's url
ret.success = !DC_LoaTS_Helper.loadRaid(raidLink.getURL());
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Load raid: " + this.parser.getDifficultyText() + " " + this.parser.getName()
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadraid url</code>\n";
helpText += "where <code>url</code> is the url of a raid\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "loadraidmonitor",
aliases: ["loadrm", "lrm", "raidmonitor", "rm", "loadraidmonitorraids", "loadrmraids", "lrmalpha"],
parsingClass: RaidMultiFilter,
paramText: "[filter]",
handler: function(deck, parser, params, text, context)
{
var ret = {success: true};
DC_LoaTS_Helper.ajax({
url: "http://getKongE.org/games/lots/raids/raids.json",
onload: function(response) {
var message;
if (response.status === 200) {
var resp = JSON.parse(response.responseText),
raids = resp.raids;
if (false === resp.success) {
holodeck.activeDialogue().raidBotMessage("Error loading from RaidMonitor: " +
resp.error);
}
else {
var buckets = {
private: {
header: "Private Raids",
key: "PrivateRaid",
raids: []
},
alliance: {
header: "Alliance Raids",
key: "AllianceRaid",
raids: []
},
public: {
header: "Public Raids",
key: "PublicRaid",
raids: []
}
};
var i, raid;
for (i in raids) {
if (!raids.hasOwnProperty(i)) continue;
raid = raids[i];
raid.def = DC_LoaTS_Helper.raids[raid.boss];
if (!raid.def) {
console.log("No def", i, raid);
continue;
}
if (raid.def && "Alliance" === raid.def.type) {
buckets.alliance.raids.push(raid);
}
else if (!raid.pass) {
buckets.public.raids.push(raid);
}
else {
buckets.private.raids.push(raid);
}
}
var processBucket = function(bucket, markVisited) {
var matchedRaids = [];
for (i in bucket.raids) {
if (!bucket.raids.hasOwnProperty(i)) continue;
raid = bucket.raids[i];
var now = new Date();
var now_utc = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), now.getUTCHours(), now.getUTCMinutes(), now.getUTCSeconds());
var remainingSecs = -1,
elapsedSecs = -1, totalSecs = -1;
var remainingHealth = -1,
totalHealth = -1;
if (raid.summonDate && raid.def) {
var summonDateParts = raid.summonDate.split(" ");
var sdd = summonDateParts[0].split("-");
var sdt = summonDateParts[1].split(":");
totalSecs = raid.def.time*60*60;
elapsedSecs = Math.ceil((now_utc - new Date(sdd[0], sdd[1]-1, sdd[2], sdt[0], sdt[1], sdt[2])) / 1000);
remainingSecs = totalSecs - elapsedSecs;
remainingHealth = raid.health;
totalHealth = raid.def.health[raid.diff];
}
var dcRaidDef = DC_LoaTS_Helper.raids[raid.boss];
var fs = dcRaidDef.getFairShare(raid.diff);
if (parser.matches({
age: elapsedSecs,
difficulty: raid.diff,
fs: fs !== "N/A"? fs : -1,
os: dcRaidDef.getOptimalShare(raid.diff),
name: dcRaidDef.getSearchableName(),
progress: (remainingHealth/totalHealth)/(remainingSecs/totalSecs),
size: dcRaidDef.size,
zone: dcRaidDef.zone
})) {
matchedRaids.push(raid);
}
}
holodeck.activeDialogue().raidBotMessage("Downloaded " + bucket.raids.length + " raids. Loading " + matchedRaids.length + " that matched <code>" + parser.toString() + "</code>...");
var joinResults = {joined: 0, visited: 0, dead: 0, invalid: 0};
function loadMatchedRaids() {
if (matchedRaids.length > 0) {
raid = matchedRaids.pop();
DC_LoaTS_Helper.ajax({
url: DC_LoaTS_Properties.joinRaidURL + "?kongregate_user_id=" + active_user.id() + "&kongregate_game_auth_token=" + active_user.gameAuthToken() + "&kv_raid_id=" + raid.raid_id + "&kv_hash=" + raid.hash,
onload: function(response) {
var raidLink = new RaidLink(raid.raid_id, raid.hash, raid.difficulty, raid.boss);
var responseText = response.responseText;
if (responseText.indexOf("You have successfully joined the raid!") >= 0 || responseText.indexOf("You have successfully re-joined the raid!") >= 0)
{
// Joined
joinResults.joined++;
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
}
else if (responseText.indexOf("You are already a member of this raid!") >= 0)
{
// Already visited / rejoined
joinResults.visited++;
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
}
else if (responseText.indexOf("This raid is already completed!") >= 0)
{
// Raid is dead
joinResults.dead++;
RaidManager.store(raidLink, RaidManager.STATE.COMPLETED);
}
else
{
// Invalid response (bad hash, wrong alliance, or otherwise broken link)
joinResults.invalid++;
RaidManager.store(raidLink, RaidManager.STATE.IGNORED);
}
setTimeout(loadMatchedRaids, 10);
}
});
}
else {
var msg = "Raid Monitor loading results for " + bucket.header + ":";
msg += "\nJoined: " + joinResults.joined;
msg += "\nVisited: " + joinResults.visited;
msg += "\nDead: " + joinResults.dead;
msg += "\nInvalid: " + joinResults.invalid;
holodeck.activeDialogue().raidBotMessage(msg);
}
}
loadMatchedRaids();
};
if (buckets.private.raids.length) {
processBucket(buckets.private);
}
if (buckets.alliance.raids.length) {
processBucket(buckets.alliance);
}
// Always try to load public raids
processBucket(buckets.public, true);
}
}
else if (response.status > 200 && response.status < 400) {
message = "No new raids was returned."
}
else {
message = "Unable to retrieve raids. (status: " + response.status + ")";
}
if (message && holodeck.activeDialogue()) {
holodeck.activeDialogue().raidBotMessage(message);
}
}
});
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Load Raid Monitor raids"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/loadraidmonitor [filters]</code>\n";
helpText += "Loads public raids from <a href='http://getKongE.org/games/lots/raids' title='Visit the Raid Monitor page' target='_blank'>Raid Monitor</a>.\n";
helpText += "\n";
helpText += "If this service is valueable to you, consider <a href='http://getKongE.org/donate' title='Visit the Donation page' target='_blank'>donating</a>.";
helpText += "\n";
helpText += "\n";
helpText += "In addition to the normal filters, /lrm can filter based on current health/time ratio." + this.getCommandLink("{progress: ahead}") + " will find raids that are ahead. Other options are behind, and a numeric ratio like " + this.getCommandLink("{progress: < 1.5}") + " for raids that are health%/time% <= 1.5. \n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "markall",
aliases: ["bulkcachestate"],
paramText: "filter state",
/*public Object*/ handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
params = params.trim();
var space = params.lastIndexOf(" ");
var filter = params.substring(0, space);
var state = params.substring(space+1);
var count = RaidManager.markByFilter(filter, state);
ret.statusMessage = "Marked " + count + " raid" + (count!=1?"s":"") + " matching \"<code>" + filter + "</code>\" as " + state;
ret.success = true;
if (count > 0) {
DC_LoaTS_Helper.updatePostedLinks();
}
return ret;
},
/*public Object*/ getOptions: function()
{
var commandOptions = {
initialText: {
text: "Mark all " + this.parser.filterText + " as " + this.parser.stateText
}
};
return commandOptions;
},
/*protected String*/ buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/markall [filter] state</code>\n";
helpText += "where <code>filter</code> (optional) is a seenraids style filter to limit what gets marked\n";
helpText += "where <code>state</code> is a valid state to mark the raids to (unseen, seen, visited)\n";
return helpText;
}
}
);
// Show a list of users that you've muted
RaidCommand.create(
{
commandName: "mutelist",
aliases: ["muted", "ml"],
handler: function(deck, parser, params, text, context)
{
var ret = {success: true},
list = "",
count = 0;
for (var p in deck._chat_window._mutings) {
count++;
list += '<span _username="' + p + '" class="username chat_message_window_username">' + p + '</span>' + '\n';
}
ret.statusMessage = "You have " + count + " user" + (count!=1?'s':'') + " muted.\n" + list;
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "See who you've muted"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/mutelist</code>\n";
helpText += "See the list of Kongregate users you've muted.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "news",
aliases: ["new"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
DC_LoaTS_Helper.loadNews();
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Check the latest doomscript news"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/news</code>\n";
helpText += "Loads news from the doomscript servers.\n";
return helpText;
}
}
);
RaidCommand
.create({
commandName : "pasteraids",
aliases : [ "pastebinraids" ],
parsingClass : RaidMultiFilter,
handler : function(deck, raidFilter, params, text, context) {
// Capture the start time of the query
var queryStartTime = new Date() / 1;
// Declare ret object
var ret = {};
// Find all raids that match the user's criteria
var raidLinks = RaidManager.fetchByFilter(raidFilter);
// If the RaidManager executed successfully
if (typeof raidLinks != "undefined") {
// If we didn't match a single raid
if (raidLinks.length == 0) {
if (params.length == 0) {
ret.statusMessage = "Could not locate any seen raids in memory.";
} else {
ret.statusMessage = "Could not locate any seen raids matching <code>"
+ params + "<code>";
}
// The lookup succeeded, we just didn't find anything
ret.success = true;
}
// If we did match some raids
else {
// Capture all the text in one block
var outputText = "";
// For every link we found
for ( var i = 0; i < raidLinks.length; i++) {
// Print matched links
outputText += raidLinks[i].getURL() + "\n";
}
// Wait a moment, then paste it
setTimeout(
function() {
DC_LoaTS_Helper.PastebinAPI
.pasteData(
outputText,
raidFilter.toPrettyString()
+ " by "
+ holodeck._active_user._attributes._object.username,
raidFilter.toString());
}, 100);
// Status
ret.statusMessage = "Pasting " + raidLinks.length
+ " raids matching <code>"
+ raidFilter.toString()
+ "</code> to Pastebin. Please wait...";
// Succeeded
ret.success = true;
}
}
// RaidManager failed
else {
ret.statusMessage = "Did not understand command: <code>"
+ text + "</code>";
ret.success = false;
}
return ret;
},
getOptions : function() {
var commandOptions = {
initialText : {
text : "Export matching data to pastebin"
}
};
return commandOptions;
},
buildHelpText : function() {
var helpText = "<b>Raid Command:</b> <code>/pasteraids raidName difficulty {state: stateName} {age: timeFormat} {fs: fsFormat} {count: numberResults} {page: resultsPage}</code>\n";
helpText += "Exports to pastebin raids that you've seen before in chat"
helpText += "where <code>raidName</code> <i>(optional)</i> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "where <code>stateName</code> <i>(optional)</i> is either seen or visited\n";
helpText += "where <code>timeFormat</code> <i>(optional)</i> is like <code><24h</code>, <code><30m</code>, or <code>>1d</code>\n";
helpText += "where <code>fsFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>numberResults</code> <i>(optional)</i> is the number of results to display\n";
helpText += "where <code>resultsPage</code> <i>(optional)</i> is if you've set count, then which page to show. If page is omitted, it will show the first page of results.\n";
return helpText;
}
});
RaidCommand.create(
{
commandName: "raid",
aliases: ["raids", "radi", "radu", "raud", "radus", "rauds", "radis", "rai", "fs", "os"],
parsingClass: RaidFilter,
// Doesn't use all the filter params
paramText: "[raidName] [raidDifficulty]",
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true},
filter = parser;
// If this was a valid filter
if (filter.isValid())
{
// Find the matching raid types
var matchedTypes = DC_LoaTS_Helper.getRaidTypes(filter);
// If we matched some raid types
if (matchedTypes.length > 0)
{
// Iterate over all the matched raid types
for (var j = 0; j < matchedTypes.length; j++)
{
// Grab this raid
var raid = matchedTypes[j];
// Have the raid bot tell them
deck.activeDialogue().raidBotMessage(raid.getVerboseText(filter.difficulty));
}
ret.success = ret.success && true;
}
// If we didn't match a single raid
else
{
ret.success = ret.success && true;
ret.statusMessage = (i > 0?"\n":"") + "Could not locate any raids matching <code>" + filter.toString() + "</code>";
}
}
return ret;
},
getOptions: function()
{
console.log("Raid Info: ", this, this.parser);
var commandOptions = {
initialText: {
text: "Raid Info for: " + this.parser.name,
executable: false
},
all: {
text: "All",
callback: function()
{
DCDebug("Info All " + this.parser.name);
delete this.parser.difficulty;
}
},
normal: {
text: "Normal",
callback: function()
{
DCDebug("Info Normal " + this.parser.name);
this.parser.difficulty = 1;
}
},
hard: {
text: "Hard",
callback: function()
{
DCDebug("Info Hard " + this.parser.name);
this.parser.difficulty = 2;
}
},
legendary: {
text: "Legendary",
callback: function()
{
DCDebug("Info Legendary " + this.parser.name);
this.parser.difficulty = 3;
}
},
nightmare: {
text: "Nightmare",
callback: function()
{
DCDebug("Info Nightmare " + this.parser.name);
this.parser.difficulty = 4;
}
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/raid raidName difficulty</code>\n";
helpText += "where <code>raidName</code> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "\n";
helpText += "<b>Example:</b>\n";
helpText += "Raid data for NM Tulk: " + this.getCommandLink("tulk 4") + "\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "raidbulkcallback",
aliases: [],
doNotEnumerateInHelp: true,
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
// Break apart the guid and possible cancel message
var paramsParts = params.split(" "),
guid = paramsParts[0],
bulkRaidObject = DC_LoaTS_Helper.bulkRaids[guid],
cancel = paramsParts[1];
DCDebug(paramsParts, guid, bulkRaidObject, cancel);
// If this load was canceled
if ( (typeof cancel !== "undefined" && cancel === "cancel") || bulkRaidObject.canceled)
{
// Keep track of the total run time so far
if (typeof bulkRaidObject.runTime === "undefined")
{
bulkRaidObject.runTime = 0;
}
bulkRaidObject.runTime += (new Date()/1) - bulkRaidObject.startTime;
bulkRaidObject.canceled = true;
if (bulkRaidObject.timeout)
{
clearTimeout(bulkRaidObject.timeout);
delete bulkRaidObject.timeout;
}
ret.success = true;
ret.statusMessage = "Canceled bulk load from " + bulkRaidObject.loadSource;
}
else
{
if (typeof bulkRaidObject.iteration === "undefined")
{
bulkRaidObject.iteration = 0;
bulkRaidObject.startTime = new Date()/1;
holodeck.activeDialogue().raidBotMessage("Loading bulk raids from " + bulkRaidObject.loadSource + ". " + DC_LoaTS_Helper.getCommandLink("/raidbulkcallback " + guid + " cancel", "Cancel?"));
}
var raidIndexToLoad = bulkRaidObject.iteration++;
var raidToLoad = bulkRaidObject.raids[raidIndexToLoad];
if (raidIndexToLoad < bulkRaidObject.raids.length)
{
DC_LoaTS_Helper.loadRaid(raidToLoad);
bulkRaidObject.iteration >= bulkRaidObject.raids.length
bulkRaidObject.timeout = setTimeout("holodeck.processChatCommand(\"/raidbulkcallback " + guid + "\");", 1500);
}
else
{
ret.statusMessage = "Completed bulk load from " + bulkRaidObject.loadSource + " in " + ((new Date()/1) - bulkRaidObject.startTime) + "ms.";
}
ret.success = true;
}
return ret;
},
getOptions: function()
{
return {};
},
buildHelpText: function()
{
return "";
}
}
);
RaidCommand.create(
{
commandName: "raidformat",
aliases: [],
// Custom parsing
/*parsingClass: ,*/
// Custom parsing means custom param text
paramText: "[newFormat]",
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
if (params.length == 0)
{
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Let the user know what the format is
ret.statusMessage = "Current raid format: <code>" + messageFormat + "</code>" +
"\nUse <code>" + this.getCommandLink("help") + "</code> to list all formatting options.";
}
else if (params == "reset")
{
var messageFormat = RaidLink.defaultMessageFormat;
// Retrieve the message format
GM_setValue(DC_LoaTS_Properties.storage.messageFormat, messageFormat);
// Let the user know what the format is
ret.statusMessage = "Raid format reset to: <code>" + messageFormat + "</code>";
}
// Has a format and is not a help request
else
{
// Store the message format
GM_setValue(DC_LoaTS_Properties.storage.messageFormat, params);
// Notify user that we stored it
ret.statusMessage = "Raid format is now: <code>" + params + "</code>";
// Update the posted links
DC_LoaTS_Helper.updatePostedLinks();
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Set raid format to " + this.processedText
}
};
return commandOptions;
},
buildHelpText: function()
{
// Get the text of all the cache states
var cache_state_text = "";
var cache_state_nice_text = "";
var cache_state_short_text = "";
for (var stateName in RaidManager.STATE)
{
var state = RaidManager.STATE[stateName];
if (typeof state == "object")
{
cache_state_text += state.text + ", ";
cache_state_nice_text += state.niceText + ", ";
cache_state_short_text += state.shortText + ", ";
}
}
var unknownState = RaidManager.STATE.getUnknownState();
cache_state_text += "or " + unknownState.text;
cache_state_nice_text += "or " + unknownState.niceText;
cache_state_short_text += "or " + unknownState.shortText;
// Get the text of all the difficulties
var difficulty_text = "";
for (var diffNum in RaidType.difficulty)
{
difficulty_text += RaidType.difficulty[diffNum] + ", ";
}
difficulty_text += "or Unknown (error)";
// Get the text of all the short difficulties
var diff_text = "";
for (var diffNum in RaidType.shortDifficulty)
{
diff_text += RaidType.shortDifficulty[diffNum] + ", ";
}
diff_text += "or Unknown (error)";
var helpText = "<b>Raid Command:</b> <code>/raidformat newFormat</code>\n";
helpText += "where <code>newFormat</code> <i>(optional)</i> is the new format for raid links\n";
helpText += "if <code>newFormat</code> is omitted, it will tell you your current raid format\n";
helpText += "\n";
helpText += "<b>Format options (hover for description):</b>\n";
helpText += "<span class=\"abbr\" title=\"" + cache_state_text + "\">cache-state</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_nice_text + "\">cache-state-nice</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_nice_text + "\">state</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_nice_text + "\">status</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_short_text + "\">cache-state-short</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_short_text + "\">state-short</span>, ";
helpText += "<span class=\"abbr\" title=\"" + cache_state_short_text + "\">status-short</span>, ";
helpText += "<span class=\"abbr\" title=\"" + difficulty_text + "\">difficulty</span>, ";
helpText += "<span class=\"abbr\" title=\"" + diff_text + "\">diff</span>, ";
helpText += "<span class=\"abbr\" title=\"Fair Share (of damage) = Max raid health / Max raid members\">fs</span>, ";
helpText += "<span class=\"abbr\" title=\"Fair Share (of damage) = Max raid health / Max raid members\">fair</span>, ";
helpText += "<span class=\"abbr\" title=\"Fair Share (of damage) = Max raid health / Max raid members\">fairshare</span>, ";
helpText += "<span class=\"abbr\" title=\"Raid Health\">health</span>, ";
helpText += "<span class=\"abbr\" title=\"Unique Raid ID Number\">id</span>, ";
helpText += "<span class=\"abbr\" title=\"Kongregate LoaTS icon\">image</span>, ";
helpText += "<span class=\"abbr\" title=\"Break to the next line\">line</span>, ";
helpText += "<span class=\"abbr\" title=\"Official Raid Name\">name</span>, ";
helpText += "<span class=\"abbr\" title=\"Same as target\">os</span>, ";
helpText += "<span class=\"abbr\" title=\"Same as target\">optimal</span>, ";
helpText += "<span class=\"abbr\" title=\"Official Short Raid Name\">short-name</span>, ";
helpText += "<span class=\"abbr\" title=\"User defined short name\">shorter-name</span>, ";
helpText += "<span class=\"abbr\" title=\"Shortest unique name of the raid\">shortest-name</span>, ";
helpText += "<span class=\"abbr\" title=\"Raid Size = Max raid members\">size</span>, ";
helpText += "<span class=\"abbr\" title=\"S, E, and/or H if the raid uses Stamina, Energy, and/or Honor\">stat</span>, ";
helpText += "<span class=\"abbr\" title=\"Target (Damage) = FS * multiplier. Changes per raid size.\">target</span>, ";
helpText += "<span class=\"abbr\" title=\"Duration of the raid\">time</span>, ";
helpText += "<span class=\"abbr\" title=\"Full text url of the raid\">url</span>, ";
helpText += "<span class=\"abbr\" title=\"" + RaidManager.STATE.VISITED.niceText + "\" if you've loaded this raid before, blank otherwise\">visited</span>, ";
helpText += "<span class=\"abbr\" title=\"" + RaidManager.STATE.VISITED.shortText + "\" if you've loaded this raid before, blank otherwise\">visited-short</span>, ";
helpText += "<span class=\"abbr\" title=\"Raid's Mission Zone. On Alliance raids, indicates Archimedes tier.\">zone</span>";
helpText += "\n";
helpText += "\n";
helpText += "Options should be placed in <code>{}</code>\n";
helpText += "\n";
helpText += "<b>Default:</b>\n";
helpText += "<code>" + this.getCommandLink(RaidLink.defaultMessageFormat) + "</code>\n";
helpText += "<i class=\"smallText\">(<code>" + this.getCommandLink("reset") + "</code> will set your format back to this)</i>\n";
helpText += "\n";
helpText += "<b>SRLTSX Default:</b>\n";
helpText += "<code>" + this.getCommandLink("{visited} {name} - {diff} - {fs}/{os}") + "</code>";
helpText += "\n";
helpText += "<b>Short:</b>\n";
helpText += "<code>" + this.getCommandLink("{cache-state-short} {diff} {shorter-name}") + "</code>";
helpText += "\n"
helpText += "<b>Notes:</b>\n"
helpText += "<code>{fs}</code> can also do simple math like <code>{fs*2}</code>\n"
helpText += "Use <code>{line}</code> for new lines, and can be used multiple times.\n"
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "raidhelp",
aliases: ["raidabout", "raidbot", "help", "doomscript", "doomscripthelp", "dshelp"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {sucess: true};
DC_LoaTS_Helper.printScriptHelp(deck, text);
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Display doomscript help"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/raidhelp</code>\n";
helpText += "Displays the help info for the script\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "raidstyle",
aliases: [],
parsingClass: RaidFilterStyleParser,
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
DCDebug("raidstyle.command.js: parser: ", parser);
if (typeof parser.raidFilter === "undefined" || parser.raidFilter.isEmpty())
{
//TODO: Display all existing raid styles
}
else if (typeof parser.linkStyle === "undefined" && typeof parser.messageStyle === "undefined" && typeof parser.imageStyle === "undefined")
{
//TODO: Display all raid styles that have the same filter
}
else
{
// Find all the styles matching this filter
var matchingStyles = DC_LoaTS_Helper.raidStyles[parser.raidFilter.toString()];
if (typeof matchingStyles === "undefined")
{
matchingStyles = [];
DC_LoaTS_Helper.raidStyles[parser.raidFilter.toString()] = matchingStyles;
}
// Have the parser create CSS styles for itself.
parser.injectStyles();
// Add this to the list of styles for this filter
matchingStyles.push(parser);
// Success report
ret.success = true;
ret.statusMessage = parser.toString();
// Refresh the links to see the change
DC_LoaTS_Helper.updatePostedLinks();
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Execute this raid style command"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/raidstyle filter +linkStyle +messageStyle +imageStyle</code>\n";
helpText += "\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "refreshlinks",
aliases: [],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
DC_LoaTS_Helper.updatePostedLinks();
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Refresh the links in chat"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/refreshlinks</code>\n";
helpText += "Will refresh all the links and their states in chat.\n";
helpText += "This can help if raids aren't looking marked like they should be.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "reload",
aliases: ["refresh", "reloaf", "reloa", "eload"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
params = params.trim().toLowerCase();
if (params != "game" && params != "chat" && params != "wc" && params != "")
{
holodeck.processChatCommand("/reload help");
return {success: true};
}
// true if we did reload, false otherwise
ret.success = DC_LoaTS_Helper.reload(null, params);
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Reload the game"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/reload</code>\n";
helpText += "Attempts to reload just the game and not the window\n";
helpText += "To reload the World Chat, use <code>/reload chat</code>\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "rss",
aliases: ["forums", "threads", "posts"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true, statusMessage: "Reading RSS feed..."};
DC_LoaTS_Helper.ajax({
url: "http://www.legacyofathousandsuns.com/forum/external.php?type=RSS2",
onload:function(response) {
var xmlDoc = (new DOMParser()).parseFromString(response.responseText, "text/xml"),
items = xmlDoc.getElementsByTagName("item"),
i, item, j, child, threads = [], thread,
str = "Recent posts (as of " + DC_LoaTS_Helper.getCurrentPrettyDate() + ")";
for (i = 0; i < items.length; i++) {
item = items[i];
threads.push({
title: getNodeValue(item, "title"),
url: getNodeValue(item, "link"),
date: getNodeValue(item, "pubDate"),
relativeDate: DC_LoaTS_Helper.timeDifference(new Date()/1, new Date(getNodeValue(item, "pubDate"))/1),
description: getNodeValue(item, "description"),
category: getNodeValue(item, "category"),
categoryUrl: getNodeValue(item, "category", "domain"),
creator: getNodeValue(item, "creator")
});
}
function getNodeValue(parent, tagName, attribute) {
tags = parent.getElementsByTagNameNS("*", tagName);
if (tags && tags[0]) {
if (attribute) {
return tags[0].attributes[attribute].nodeValue;
}
else {
return tags[0].childNodes[0].nodeValue;
}
}
return "<i>Unable to locate in RSS feed</i>";
}
for (i = 0; i < threads.length; i++) {
thread = threads[i];
str += "\n--------------------------------------------------\n"
str += thread.relativeDate + " ";
str += "<a href='" + thread.categoryUrl + "' target='_blank'>" + thread.category + "</a>";
str += " > <a href='" + thread.url + "' target='_blank'>" + thread.title + "</a>";
}
holodeck.activeDialogue().raidBotMessage(str);
} // end onload
});
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Lists recent threads from the forums"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/threads</code>\n";
helpText += "Lists recent threads from the forums\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "seenraids",
aliases: ["seenraid", "raidseen", "raidseen", "sr"],
parsingClass: RaidMultiFilter,
handler: function(deck, raidFilter, params, text, context)
{
// Capture the start time of the query
var queryStartTime = new Date()/1;
// Declare ret object
var ret = {};
// Find all raids that match the user's criteria
var raidLinks = RaidManager.fetchByFilter(raidFilter);
// If the RaidManager executed successfully
if (typeof raidLinks != "undefined")
{
// If we didn't match a single raid
if (raidLinks.length == 0)
{
if (params.length == 0)
{
ret.statusMessage = "Could not locate any seen raids in memory.";
}
else
{
ret.statusMessage = "Could not locate any seen raids matching <code>" + params + "<code>";
}
// The lookup succeeded, we just didn't find anything
ret.success = true;
}
// If we did match some raids
else
{
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Retrieve the anchor tag format
var linkFormat = DC_LoaTS_Helper.getLinkFormat();
// Capture all the text in one block
var outputText = "\n";
// For every link we found
for (var i = 0; i < raidLinks.length; i++)
{
// We need to find the style the user has requested
var className = raidLinks[i].getMatchedStyles().className;
// Bits to wrap each message raid link with
var wrapperFront = "<span class=\"seenraidMessageWrapper" + (className?" " + className:"") + "\">" + (i+1) + ") ";
var wrapperBack = "</span>\n\n";
// Print matched links
outputText += wrapperFront + raidLinks[i].getFormattedRaidLink(messageFormat, linkFormat) + wrapperBack;
}
// Print out the raid links we found
deck.activeDialogue().raidBotMessage(outputText);
// Print out the stats for the query
ret.statusMessage = "<code>/" + this.commandName + " " + raidFilter.toString() + "</code> took " + (new Date()/1 - queryStartTime) + " ms and yielded " + raidLinks.length + " results.";
// Succeeded
ret.success = true;
}
}
// RaidManager failed
else
{
ret.statusMessage = "Did not understand command: <code>" + text + "</code>";
ret.success = false;
}
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Seen raids: " + ((typeof this.parser.name != "undefined")?this.parser.name : "Unknown")
},
any: {
text: "Any",
callback: function()
{
DCDebug("Seen Any " + this.parser.name);
delete this.parser.difficulty;
}
},
normal: {
text: "Normal",
callback: function()
{
DCDebug("Seen Normal " + this.parser.name);
this.parser.difficulty = 1;
}
},
hard: {
text: "Hard",
callback: function()
{
DCDebug("Seen Hard " + this.parser.name);
this.parser.difficulty = 2;
}
},
legendary: {
text: "Legendary",
callback: function()
{
DCDebug("Seen Legendary " + this.parser.name);
this.parser.difficulty = 3;
}
},
nightmare: {
text: "Nightmare",
callback: function()
{
DCDebug("Seen Nightmare " + this.parser.name);
this.parser.difficulty = 4;
}
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/seenraids raidName difficulty {state: stateName} {age: timeFormat} {size: sizeFormat} {fs: fsFormat} {os: osFormat} {zone: zoneNumber} {count: numberResults} {page: resultsPage}</code>\n";
helpText += "Looks up raids that you've seen before in chat"
helpText += "where <code>raidName</code> <i>(optional)</i> is any partial or full raid name\n";
helpText += "where <code>difficulty</code> <i>(optional)</i> is a number 1 - 4 where 1 is normal, 4 is nightmare\n";
helpText += "where <code>stateName</code> <i>(optional)</i> is either seen or visited\n";
helpText += "where <code>timeFormat</code> <i>(optional)</i> is like <code><24h</code>, <code><30m</code>, or <code>>1d</code>\n";
helpText += "where <code>sizeFormat</code> <i>(optional)</i> is like <code><100</code> or <code>250</code>\n";
helpText += "where <code>osFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>fsFormat</code> <i>(optional)</i> is like <code><1m</code> or <code>>500k</code>\n";
helpText += "where <code>zoneNumber</code> <i>(optional)</i> is like <code>1</code>, <code>Z14</code>, <code>ZA</code>, <code>WR</code>\n";
helpText += "where <code>numberResults</code> <i>(optional)</i> is the number of results to display\n";
helpText += "where <code>resultsPage</code> <i>(optional)</i> is if you've set count, then which page to show. If page is omitted, it will show the first page of results.\n";
helpText += "\n";
helpText += "<b>Examples:</b>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited that you saw posted in the last 5 hours<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen} {age: <5h}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all raids you've seen, but not visited that you saw posted in the last 5 hours that have FS < 1M<i>\n";
helpText += "<code>" + this.getCommandLink("{state:seen} {age: <5h} {fs:<1M}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all normal telemachus raids that you've not visited before\n";
helpText += "<code>" + this.getCommandLink("tele 1 {state:!visited}") + " </code>\n";
helpText += "\n";
helpText += "<i>Find the first 10 void killer raids you've seen\n";
helpText += "<code>" + this.getCommandLink("killer {count: 10}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find the second 10 void killer raids you've seen\n";
helpText += "<code>" + this.getCommandLink("killer {count: 10} {page: 2}") + "</code>\n";
helpText += "\n";
helpText += "<i>Find all void nightmare vorden raids you've seen\n";
helpText += "<code>" + this.getCommandLink("vorden 4") + "</code>\n";
helpText += "\n";
helpText += "<i>Looking for <a href=\"http://www.zoywiki.com/index.php/LotS/experiment/multicoloredcloorian\" title=\"Cloorian Material needed to craft some Legendary pants\">Cloorian Material<a/>\n";
helpText += "<code>" + this.getCommandLink("vor|gan|nat 4 {age: <24h} {state: !visited}") + "</code>\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "suggest",
aliases: ["idea", "suggestion", "bug", "doomcat", "doomscript", "report", "feature", "feedback"],
generateResponseMessage: function(command) {
switch(command) {
case "idea":
case "feature":
case "doomcat":
case "doomscript":
return "Thanks for the idea!";
case "suggestion":
return "Thanks for the suggestion!";
case "report":
case "bug":
return "Thanks for the bug report!";
case "feedback":
default:
return "Thanks for the feedback!"
}
},
handler: function(deck, parser, params, text, context)
{
if (!params || !params.trim()) {
holodeck.processChatCommand("/suggest help");
return {success: true};
}
var command = text.split(" ")[0].toLowerCase().substring(1);
var ret = {success: true, message: "Submitting..."};
var me = this;
DC_LoaTS_Helper.ajax({
url: "http://getKongE.org/games/lots/suggestions/",
method: "POST",
data: DC_LoaTS_Helper.uriSerialize({user: deck.username(), message: params, text: text, url: window.location.href}),
onload: function(response) {
if (response.status >= 200 && response.status < 300) {
holodeck.activeDialogue().raidBotMessage(me.generateResponseMessage(command));
}
else {
holodeck.activeDialogue().raidBotMessage("Sorry, there was an error (" + response.status + " processing your request. Please try again later. :-(");
console.error("Server failed to process /suggest", response);
}
}
});
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Suggest a doomscript feature or report a doomscript bug"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/suggest idea/bug text here</code>\n";
helpText += "If you have an idea for a new feature or you've got a bug to report, this command will let you submit them";
return helpText;
}
}
);
// This is the general template which chat commands should follow
RaidCommand.create(
{
commandName: "template", // This is the /template command
aliases: ["templateCommand", "commandTemplate"], // Also, /templateCommand and /commandTemplate
parsingClass: RaidMultiFilter, // Comment out this line, and a parser will not be created
myCustomAttribute: "Foo",
doNotEnumerateInHelp: true, // Don't list this in the help
// It's highly advised to just delete this function entirely
// but I've left it here, commented out, to show that it
// can be used if you know what you're doing
// initialize: function($super, context, commandText)
// {
// // Do some special constructor logic here
//
// // In the name of all that is good, call the superclass constructor
// // somewhere in here
// $super(context, commandText);
// },
// Handle the execution of this command
// Parameters:
// deck - The Kongregate holodeck
// parser - If there is a parsing class, this parser was run with the params
// params - Command text stripped of /commandName from the front
// text - Full text of the command as it was called
// context - Where this command was called from, either chat or omnibox
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {};
// The work should be done in here
// Explicitly posting as the RaidBot to the chat
// This only shows for the current user. This message is not sent to everyone.
deck.activeDialogue().raidBotMessage("Hello World!");
// Always set the success state
// If the command succeeeded, set the status to true
// If it failed, set it to false
ret.success = true;
// If you want to display a message to the user after the command
// finishes, put the text of the message in ret.statusMessage
// Unrelatedly, we can also access custom attributes like this.myCustomAttribute
ret.statusMessage = this.myCustomAttribute;
// Always way to return the ret object
return ret;
},
// These options are what the omnibox uses to determine how to display
// this command as an autocomplete option
getOptions: function()
{
// Typically, there is just a single commandOptions object that is returned
var commandOptions = {
// This option is a clickable link that will load
// a page in a new window/tab like a real link
initialText: {
// Text of the link
text: "Open script homepage",
// Attributes of the <a> tag of this link
// If there are linkParams, the option will call the handler
// unless doNotCallHandler is true
linkParams: {href: this.myCustomFunction(), target: "_blank"},
// Do not call the above handler function
doNotCallHandler: true,
// Actually let the browser load the link
followLink: true
},
// This option is just text. It doesn't do anything
otherOption: {
text: "Not clickable",
// Sets this option to do nothing
executable: false
},
// This command actually runs the template command
thirdOption: {
// Text of the command button
text: "Run Template Command",
// If there's a callback, the option will call the handler
// unless doNotCallHandler is true.
callback: function()
{
// Do work in here. Usually just minor
// set up work since the handler is going
// to be called next.
// If this code starts to get long, you
// might consider going about it differently.
}
}
};
// Make sure to return the options we just made
return commandOptions;
},
// Custom functions go in just like needed functions
myCustomFunction: function()
{
return DC_LoaTS_Properties.scriptURL;
},
// Here you simply construct the help text for when a user calls /template help
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/template</code>\n";
helpText += "Prints hello world, or does something else\n";
// Aliases for the command will be automatically appended to the bottom
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "time",
aliases: ["servertime"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
ret.statusMessage = "Local Time is approximately: " + this.getLocalDateText() + "\n";
ret.statusMessage += "Server Time is approximately: " + this.getServerDateText();
return ret;
},
getOptions: function()
{
var commandOptions = {
config: {
refreshEvery: 1000
},
initialText: {
text: "Local Time: " + this.getLocalDateText() + "<br>Server Time: " + this.getServerDateText()
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/time</code>\n";
helpText += "Estimates the current server time (GMT) based on local system time.\n";
return helpText;
},
getLocalDateText: function()
{
return new Date().toLocaleString();
},
getServerDateText: function()
{
var localDate = new Date();
var serverDate = new Date(localDate.getTime() + localDate.getTimezoneOffset() * 60 * 1000);
return serverDate.toLocaleString().substring(0,25) + " GMT+0000 (UTC)";
}
}
);
RaidCommand.create(
{
commandName: "update",
aliases: [],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
window.open(DC_LoaTS_Properties.scriptDownloadURL, "_blank");
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Get the current stable script"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/update</code>\n";
helpText += "Attempts to install the latest stable doomscript version from <a href=\"" + DC_LoaTS_Properties.scriptURL + "\">" + DC_LoaTS_Properties.scriptURL + "</a>.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "updateraiddata",
aliases: ["urd", "updatedata"],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
DC_LoaTS_Helper.updateRaidData();
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Update your local raid data"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/updateraiddata</code>\n";
helpText += "Attempts to update to the latest raid data (All the raid types).\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "version",
aliases: [],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
return {success: true, statusMessage: "Your doomscript version is <b>" + DC_LoaTS_Properties.version + "</b>"};
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Your doomscript version is <b>" + DC_LoaTS_Properties.version + "</b>",
doNotCallHandler: true
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/version</code>\n";
helpText += "Tells the current version of the installed script.\n";
return helpText;
}
}
);
RaidCommand.create(
{
commandName: "wiki",
aliases: ["search", "lookup", "zoywiki"],
urlPattern: "http://www.zoywiki.com/index.php?title=Special:Search&search=LotS/{0}&go=Go",
// No parsing
/*parsingClass: ,*/
paramText: "query",
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
var url = this.createURL(params);
window.open(url, "_blank");
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Search Zoywiki for: " + this.processedText,
linkParams: {href: this.createURL(this.processedText), target: "_blank"},
doNotCallHandler: true,
followLink: true
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/wiki searchText</code>\n";
helpText += "where <code>searchText</code> is what you want to search for on Zoywiki\n";
return helpText;
},
createURL: function(searchInput)
{
searchInput = searchInput || "";
return this.urlPattern.format(escape(searchInput.replace(" ", "+")));
}
}
);
RaidCommand.create(
{
commandName: "timerdata",
aliases: [],
// No parsing needed
/*parsingClass: ,*/
handler: function(deck, parser, params, text, context)
{
// Declare ret object
var ret = {success: true};
deck.activeDialogue().raidBotMessage(Timer.getReport());
return ret;
},
getOptions: function()
{
var commandOptions = {
initialText: {
text: "Print the timer report"
}
};
return commandOptions;
},
buildHelpText: function()
{
var helpText = "<b>Raid Command:</b> <code>/timerdata</code>\n";
helpText += "Prints out timing and performance data about the script\n";
return helpText;
}
}
);
// Manage data related to the CConoly API
window.CConolyAPI = {
lastQueryTimeKey: DC_LoaTS_Properties.storage.cconolyLastQueryTime,
useQueryTimeDeltaPrefKey: "UseQueryTimeDelta",
baseUrl: "http://cconoly.com/lots/",
markDeadUrl: "markDead.php?kv_raid_id=%RAID_ID%&doomscript=%VERSION%",
raidListUrl: "raidlinks.php?hrs=%TIME%&doomscript=%VERSION%",
setLastQueryTime: function(lastQueryTime) {
GM_setValue(this.lastQueryTimeKey, lastQueryTime);
},
getMarkDeadUrl: function(raidID) {
var reportUrl = this.baseUrl + this.markDeadUrl;
reportUrl = reportUrl.replace("%RAID_ID%", raidID);
reportUrl = reportUrl.replace("%VERSION%", this.getVersionString());
return reportUrl;
},
getRaidListUrl: function() {
var raidListUrl = this.baseUrl + this.raidListUrl;
raidListUrl = raidListUrl.replace("%TIME%", this.getRaidListQueryHours());
raidListUrl = raidListUrl.replace("%VERSION%", this.getVersionString());
return raidListUrl;
},
getVersionString: function() {
return this.versionString || (this.versionString = DC_LoaTS_Properties.version.toString().replace(/\./g, ""));
},
getRaidListQueryHours: function()
{
return DC_LoaTS_Helper.getPref(this.useQueryTimeDeltaPrefKey, true) ? this.getHoursSinceLastQuery() : 168;
},
getHoursSinceLastQuery: function() {
var elapsedMs = new Date()/1 - GM_getValue(this.lastQueryTimeKey, 0);
elapsedHrs = elapsedMs / 1000 / 60 / 60; // Convert ms to hours
return Math.min(168, Math.ceil(elapsedHrs * 1000)/1000); // Round to 3 decimals, take 168 or lower
}
};
// List of all raid ids and names. Any raid without a real raid id will not show up nicely.
DC_LoaTS_Helper.raids =
{
// Personal Raids
sherlock_holmes: new RaidType("sherlock_holmes", "Z10", "The Murderer", "Murderer", "Murderer", 12, 1, "S", [6000000, "N/A", "N/A", "N/A"]),
wrath_of_player: new RaidType("wrath_of_player", "ZA", "Your Wrath", "Your Wrath", "Wrath", 12, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
lu_bu: new RaidType("lu_bu", "ZA2", "LU BU", "LU BU", "Lubu", 12, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
ragnar: new RaidType("ragnar", "ZA3", "Ragnar", "Ragnar", "Ragnar", 1, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
centurian_covert_agent:new RaidType("centurian_covert_agent","ZA3", "Centurian Covert Agent", "CC Agent", "Agent", 1, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
talia: new RaidType("talia", "ZA4", "Talia", "Talia", "Talia", 1, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
myrmexidaks: new RaidType("myrmexidaks", "ZA4", "Myrmexidaks", "Myrmexidaks", "Myrm", 1, 1, "S", [10000000, "N/A", "N/A", "N/A"]),
// STANDARD RAIDS
// Small Raids
commander: new RaidType("commander", "Z1", "Centurian Commander", "CC Commander", "CC Comm", 168, 10, "S", [150000, 187500, 240000, 300000]),
ragebeasts: new RaidType("ragebeasts", "Z2", "Garlax Ragebeasts", "Ragebeasts", "Rage", 120, 10, "S", [2000000, 2500000, 3200000, 4000000]),
cybertollahs: new RaidType("cybertollahs", "Z3", "Supreme Cybertollahs", "Cybertollahs", "Cyber-T", 72, 10, "S", [4000000, 5000000, 6400000, 8000000]),
seth: new RaidType("seth", "Z4", "Nathaniel Vorden", "Vorden", "Vorden", 72, 10, "S", [6000000, 7500000, 9600000, 12000000]),
scarlet_harlet: new RaidType("scarlet_harlet", "Z6", "The Scarlet Harlot", "Scarlet", "Harlot", 72, 10, "S", [15300000, 22950000, 30600000, 45900000],/*FS calculated normally*/null, 4590000),
lupin: new RaidType("lupin", "Z7", "Lupin", "Lupin", "Lupin", 72, 10, "S", [25500000, 38250000, 51000000, 76500000],/*FS calculated normally*/null, 7650000),
lieutenant_targe: new RaidType("lieutenant_targe", "Z8", "Lieutenant Targe", "Targe", "Targe", 120, 10, "S", [14000000, 17500000, 22400000, 28000000]),
sigurd: new RaidType("sigurd", "Z9", "Sigurd Spinebreaker", "Sigurd", "Sigurd", 72, 10, "S", [16000000, 20000000, 25600000, 3200000]),
space_pox: new RaidType("space_pox", "P1", "Space Pox", "Pox", "Pox", 5, 12, "S", [100000000, 500000000, 1000000000, 1500000000],/*FS calculated normally*/null, [35000000, 175000000, 350000000, 525000000]),
quiskerian_temple: new RaidType("quiskerian_temple", "L1", "Quiskerian Temple", "Temple", "Temple", 10, 25, "S", [200000000, 1000000000, 2000000000, 3000000000],/*FS calculated normally*/null, [16000000, 80000000, 160000000, 240000000]),
missile_strike: new RaidType("missile_strike", "ZA", "Missile Strike", "Missiles", "Missile", 72, 10, "S", [22000000, 28600000, 35200000, 44000000]),
pi: new RaidType("pi", "ZA2", "Pi", "Pi", "Pi", 72, 10, "S", [24000000, 31200000, 38400000, 48000000]),
master_hao: new RaidType("master_hao", "Z19", "Master Hao", "Hao", "Hao", 36, 10, "S", [1000000000, 1300000000, 1600000000, 2000000000]),
robot_uprising1: new RaidType("robot_uprising1", "F3", "Robot Uprising", "Robot Uprising", "Uprising", 72, 25, "S", [900000000, 3600000000, 4500000000, 5400000000],/*FS calculated normally*/null, 500000000),
temynx_parasite1: new RaidType("temynx_parasite1", "F4", "Temynx Parasite", "Temynx Parasite", "Temynx", 48, 15, "S", [1000000000, 2000000000,2500000000,10000000000],/*FS calculated normally*/null, 660000000),
hukkral_war_crawler: new RaidType("hukkral_war_crawler", "F9", "Huk-Kral War Crawler", "War Crawler", "Crawler", 24, 25, "S", [100000000000, 200000000000, 250000000000, 300000000000],/*FS calculated normally*/null, 12500000000),
elite_master_hao: new RaidType("elite_master_hao", "F10", "Elite Master Hao", "Elite Hao", "E. Hao", 24, 25, "S", [200000000000, 400000000000, 500000000000, 600000000000], /*FS calculated normally*/null, 25000000000),
elite_centurian_commander: new RaidType("elite_centurian_commander", "F13", "Elite Centurian Commander", "E. Cen", "E. CC", 24, 25, "S", [30000000000, 60000000000, 75000000000, 90000000000], /*FS calculated normally*/null, 3500000000),
// Small+ Raids
purple_lion: new RaidType("purple_lion", "Z5", "Purple Lion", "Lion", "Lion", 72, 20, "S", [15500000,23250000,31000000,46500000], null, 2325000),
// Medium Raids
"void": new RaidType("void", "Z1", "Centurian Void Killer", "Killer", "VK", 168, 50, "S", [5000000, 6250000, 8000000, 10000000]),
carnus: new RaidType("carnus", "Z2", "Carnus 9000", "Carnus", "Carnus", 120, 50, "S", [15000000, 18750000, 24000000, 30000000]),
cruiser: new RaidType("cruiser", "Z3", "Centurian Cruiser", "CC Cruiser", "Cruiser", 72, 50, "S", [25000000, 31250000, 40000000, 50000000]),
china: new RaidType("china", "Z4", "Blood Alley Gang", "Gang", "Gang", 72, 50, "S", [35000000, 43750000, 56000000, 70000000]),
caligula: new RaidType("caligula", "Z6", "Caligula", "Caligula", "Cali", 72, 50, "S", [92250000, 138375000, 184500000, 276750000],/*FS calculated normally*/null, 7380000),
warden_ramiro: new RaidType("warden_ramiro", "Z7", "Warden Ramiro", "Ramiro", "Ramiro", 72, 50, "S", [153750000, 230625000, 307500000, 461250000],/*FS calculated normally*/null, 12300000),
vulture_gunship: new RaidType("vulture_gunship", "Z8", "Vulture Gunship", "Vulture", "Vulture", 72, 50, "S", [65000000, 81250000, 104000000, 130000000]),
xarpa: new RaidType("xarpa", "Z9", "Centurian Fleet Commander", "Fleet", "Fleet Comm", 72, 50, "S", [70000000, 87500000, 112000000, 140000000]),
bachanghenfil: new RaidType("bachanghenfil", "Z10", "Bachanghenfil", "Bachanghenfil", "Bach", 72, 50, "S", [75000000, 97500000, 120000000, 150000000]),
gut_phager: new RaidType("gut_phager", "Z11", "Gut-Phager", "Gut-Phager", "Phager", 72, 50, "S", [80000000, 104000000, 128000000, 160000000]),
hulking_mutant: new RaidType("hulking_mutant", "Z15", "Hulking Mutant", "Mutant", "Mutant", 72, 50, "S", [90000000, 112500000, 144000000, 180000000]),
screaming_barracuda:new RaidType("screaming_barracuda","Z16", "Screaming Barracuda", "Barracuda", "Barracuda", 72, 50, "S", [110000000, 137500000, 176000000, 220000000]),
vunlac: new RaidType("vunlac", "Z19", "Vunlac", "Vunlac", "Vunlac", 36, 50, "S", [1500000000, 1950000000, 2400000000, 3000000000]),
bashan: new RaidType("bashan", "ZA", "Bashan", "Bashan", "Bashan", 72, 50, "S", [85000000, 106250000, 136000000, 170000000]),
cyborg_shark: new RaidType("cyborg_shark", "ZA2", "Cyborg Shark", "C. Shark", "Shark", 72, 50, "S", [90000000, 112500000, 144000000, 180000000]),
silj: new RaidType("silj", "ZA3", "Silj the Wurm-Rider", "Silj", "Silj", 30, 50, "S", [750000000, 937500000, 1200000000, 1500000000]),
tyraness_guard: new RaidType("tyraness_guard", "ZA4", "Tyraness' Guard", "Tyr. Guard", "Guard", 30, 50, "S", [750000000, 937500000, 1200000000, 1500000000]),
sian_dragonfly_1: new RaidType("sian_dragonfly_1", "Z21", "Sian Dragonfly", "Dragonfly", "Dfly", 48, 50, "S", [10000000000,15000000000,20000000000,30000000000], null, [200000000,400000000,400000000,600000000]),
lady_victoria_ashdown_1: new RaidType("lady_victoria_ashdown_1", "Z21", "Lady Victoria Ashdown", "Ashdown", "Ash",48, 50, "S", [10000000000,15000000000,20000000000,30000000000], null, [200000000,400000000,400000000,600000000]),
krakak_plague: new RaidType("krakak_plague", "F2", "Krakak Plague", "Plague", "Plague", 24, 25, "S", [4166666667, 6250000000, 8333333333,12500000000], /*FS calculated normally*/null, 500000000),
elite_bashan: new RaidType("elite_bashan", "F11", "Elite Bashan", "E. Bashan", "E. Bashan", 24, 50, "S", [350000000000, 700000000000, 875000000000, 1050000000000], /*FS calculated normally*/null, 25000000000),
// Medium+ Raids
advocate_tulk: new RaidType("advocate_tulk", "Z5", "Advocate Tulk", "Tulk", "Tulk", 72, 75, "S", [69000000,103500000,138000000,207000000], null, 2760000),
// Large Raids
telemachus: new RaidType("telemachus", "Z1", "Telemachus", "Telemachus", "Tele", 168, 100, "S", [20000000, 25000000, 32000000, 40000000]),
carnifex: new RaidType("carnifex", "Z2", "Carnifex Prime", "Carnifex", "Carn", 120, 100, "S", [35000000, 43750000, 56000000, 70000000]),
rautha: new RaidType("rautha", "Z3", "Commander Rautha", "Rautha", "Rautha", 72, 100, "S", [50000000, 62500000, 8000000, 100000000]),
assasin: new RaidType("assasin", "Z4", "Kelovar Assassin", "Assassin", "Assa", 72, 100, "S", [65000000, 81250000, 104000000, 130000000]),
agony_and_ecstasy: new RaidType("agony_and_ecstasy", "Z6", "Agony and Ecstasy", "Agony, Ecstasy", "A&E", 72, 100, "S", [216000000, 324000000, 432000000, 648000000], /*FS calculated normally*/null, 8640000),
sun_xi: new RaidType("sun_xi", "Z7", "Sun Xi's Echo", "Psi-Echo", "Echo", 72, 100, "S", [360000000, 540000000, 720000000, 1080000000], /*FS calculated normally*/null, 14400000),
sludge_serpent: new RaidType("sludge_serpent", "Z8", "Sludge Serpent", "Serpent", "Serpent", 72, 100, "S", [120000000, 150000000, 192000000, 240000000]),
kalaxian_cult_mistress: new RaidType("kalaxian_cult_mistress","Z10","Kalaxian Cult-Mistress","Mistress","Cult", 72, 100, "S", [180000000, 234000000, 288000000, 320000000]),
shuborunth: new RaidType("shuborunth", "Z13","Wulblunralxanachi", "Blob", "Blob", 72, 100, "S", [200000000, 260000000, 320000000, 400000000]),
tentacled_turkey: new RaidType("tentacled_turkey", "Z15", "Tentacled Turkey","Turkey","Turkey", 72, 100, "S", [350000000, 455000000, 560000000, 700000000]),
where_music_meets: new RaidType("where_music_meets", "Z16", "Symphony of Two Worlds","Symphony","Symphony", 72, 100, "S", [400000000, 520000000, 640000000, 800000000]),
reichsmarschall_dule:new RaidType("reichsmarschall_dule","Z19", "Reichsmarschall Dule", "R. Dule", "R. Dule", 36, 100, "S", [2000000000, 2600000000, 3200000000, 4000000000]),
birthday_cake_of_doom: new RaidType("birthday_cake_of_doom", "ZA","Birthday Cake of Doom", "Cake", "Cake", 72, 100, "S", [250000000, 325000000, 400000000, 500000000]),
anthropist_xenocide_warship:new RaidType("anthropist_xenocide_warship","ZA2","Anthropist Xenocide Warship","Xenocide","Xeno", 72,100,"S",[300000000, 390000000, 480000000, 600000000]),
dark_hat: new RaidType("dark_hat", "ZA3", "Dark Hat", "D. Hat", "D. Hat", 30, 100, "S", [1000000000, 1300000000, 1600000000, 2000000000]),
rampaging_rackalax: new RaidType("rampaging_rackalax", "ZA4", "Rampaging Rackalax", "Rackalax", "Rack", 30, 100, "S", [1000000000, 1300000000, 1600000000, 2000000000]),
infected_warwalker_squad:new RaidType("infected_warwalker_squad","C1-2", "Infected Warwalker Squad", "Warwalker", "Inf. II", 36, 50, "S", [8333333333, 12500000000, 16666666667, 25000000000], /*FS calculated normally*/null, 500000000),
contest_winner1: new RaidType("contest_winner1", "S", "Hyper-Con Havoc", "Hyper-Con", "Hyper-Con", 72, 50, "S", [8333333333,12500000000,16666666667,25000000000], /*FS calculated normally*/null, 500000000),
elite_kulnarxex_elite_subjugator: new RaidType("elite_kulnarxex_elite_subjugator", "F13", "Elite Kulnar-Xex Elite Subjugator", "E. Elite", "E. E.", 24, 100, "S", [150000000000, 300000000000, 375000000000, 450000000000]),
// Large Plus Raids
robotic_rautha: new RaidType("robotic_rautha", "Z5", "Robotic Rautha", "Rautha 2.0", "Robo Rautha", 72, 125, "S", [135000000, 202500000, 270000000, 405000000], null, 2325000),
kulnarxex_subjugator_1: new RaidType("kulnarxex_subjugator_1","S","Kulnar-Xex Subjugator","K-X Subjugator","KX Sub",8, 125, "S", [12500000000, 15625000000, 20000000000, 25000000000], /*FS calculated normally */null, 200000000),
weiqi_game_1: new RaidType("weiqi_game_1", "Z20", "Weiqi Game", "Weiqi Game", "Weiqi", 36, 180, "S", [90000000000, 112500000000, 144000000000, 180000000000], /*FS calculated normally */null, 1000000000),
kulnarxex_elite_subjugator_1: new RaidType("kulnarxex_elite_subjugator_1","S","Kulnar-Xex Elite Subjugator","Elite","KX ELITE Sub", 8, 125, "S", [125000000000, 156250000000, 200000000000, 250000000000], /*FS calculated normally */null, 2000000000),
// Epic Raids
colonel: new RaidType("colonel", "Z1", "Psychic Colonel", "CC Colonel", "Col.", 168, 250, "S", [150000000, 187500000, 240000000, 300000000]),
vespasia: new RaidType("vespasia", "Z2", "Vespasia's Android", "Vespasia Bot", "Vesp", 168, 250, "S", [250000000, 312500000, 400000000, 500000000]),
generalrahn: new RaidType("generalrahn", "Z3", "Centurian General", "CC General", "General", 168, 250, "S", [350000000, 437500000, 560000000, 700000000]),
natasha: new RaidType("natasha", "Z4", "Natasha Cybersmash", "Cybersmash", "Cyber-S", 168, 250, "S", [450000000, 562500000, 720000000, 900000000]),
mercury: new RaidType("mercury", "Z6", "Mercury", "Mercury", "Mercury", 168, 250, "S", [618750000, 928125000, 1237500000, 1856250000], /*FS calculated normally*/null, 14850000),
hultex_quibberath: new RaidType("hultex_quibberath", "Z7", "Guldax Quibberath", "Quibberath", "Quib", 168, 250, "S", [1031250000, 1546875000, 2062500000, 3093750000], /*FS calculated normally*/null, 24750000),
commander_veck: new RaidType("commander_veck", "Z8", "Centurian Storm Commander", "Storm", "Storm", 168, 250, "S", [900000000, 1125000000, 1440000000, 1800000000]),
reaver: new RaidType("reaver", "Z9", "Galactic Reaver", "Reaver", "Reaver", 72, 250, "S", [1000000000, 1250000000, 1600000000, 2000000000]),
the_hat: new RaidType("the_hat", "Z10", "The Hat", "Hat", "Hat", 72, 250, "S", [1100000000, 1475000000, 1850000000, 2200000000]),
g_rahn: new RaidType("g_rahn", "Z12", "G. Rahn", "G. Rahn", "G. Rahn", 72, 250, "S", [1200000000, 1560000000, 1920000000, 2400000000]),
al_husam: new RaidType("al_husam", "Z17", "Al-Husam", "Al-Husam", "Al-Husam", 72, 250, "S", [1500000000, 1950000000, 2400000000, 3000000000]),
noir: new RaidType("noir", "Z18", "Noir", "Noir", "Noir", 72, 250, "S", [1600000000, 2080000000, 2560000000, 3200000000]),
sky_commander_bethany:new RaidType("sky_commander_bethany","Z19","Sky Commander Bethany","Bethany","Bethany", 36, 250, "S", [2500000000, 3250000000, 4000000000, 5000000000]),
guan_yu: new RaidType("guan_yu", "ZA", "Guan Yu", "Guan", "Guan", 72, 250, "S", [1300000000, 1690000000, 2080000000, 2600000000]),
bile_beast: new RaidType("bile_beast", "ZA2", "Bile Beast", "Bile", "Bile", 72, 250, "S", [1400000000, 1820000000, 2240000000, 2800000000]),
void_master: new RaidType("void_master", "ZA3", "Void Master", "V. Master", "V. Master", 30, 250, "S", [1250000000, 1625000000, 2000000000, 2500000000]),
giant_kwelshax: new RaidType("giant_kwelshax", "ZA4", "Giant Kwelshax", "Kwelshax", "Kwel", 30, 250, "S", [1250000000, 1625000000, 2000000000, 2500000000]),
flying_saucer_mothership:new RaidType("flying_saucer_mothership","C1-3", "Flying Saucer Mothership", "Mothership", "Mothership", 48, 75, "S", [12500000000, 18750000000, 25000000000, 37500000000], /*FS calculated normally*/null, 500000000),
sapphire: new RaidType("sapphire", "Z22", "Sapphire", "Sapphire", "Sapphire", 48, 75, "S", [13333333333,20000000000,26666666667,40000000000], /*FS calculated normally*/null, 500000000),
shadow_parasites: new RaidType("shadow_parasites", "F8", "Shadow Parasites", "Shadow", "Parasites", 48, 250, "S", [1000000000000, 2000000000000, 2500000000000, 3000000000000], /*FS calculated normally*/null, 25000000000),
elite_giant_kwelshax: new RaidType("elite_giant_kwelshax", "F12", "Elite Kwelshax", "Elite Kwel", "E. Kwel", 24, 100, "S", [175000000000, 350000000000, 437500000000, 525000000000], /*FS calculated normally*/null, 7000000000),
elite_titanomachy: new RaidType("elite_titanomachy", "ZA5", "Elite Titanomachy", "E. Titan", "E. Tit", 48, 250, "S", [45000000000, 90000000000, 112500000000, 135000000000], /*FS calculated normally*/null, 2500000000),
// Epic+ Raids
centurian_sentinel: new RaidType("centurian_sentinel", "Z5", "Centurian Sentinel", "CC Sentinel", "Sentinel", 168, 275, "S", [340000000,510000000,680000000,1020000000], null, 7418184),
// Colossal Raids
mermara: new RaidType("mermara", "Z6", "Mermara", "Mermara", "Mermara", 168, 500, "S", [1395000000, 2092500000, 2790000000, 4185000000], /*FS calculated normally*/null, 25110000),
nemo: new RaidType("nemo", "Z7", "Nemo", "Nemo", "Nemo", 168, 500, "S", [2325000000, 3487500000, 4650000000, 6975000000], /*FS calculated normally*/null, 41850000),
the_emperor: new RaidType("the_emperor", "Z8", "Dule's Robot", "Dule's Bot", "Dule", 168, 500, "S", [5000000000, 6250000000, 8000000000, 10000000000]),
dule_warmaster: new RaidType("dule_warmaster", "Z9", "Centurian Councilor", "CC Councilor", "Councilor", 24, 500, "S", [2500000000, 3125000000, 4000000000, 5000000000]),
crush_colossa: new RaidType("crush_colossa", "Z10", "Crush Colossa", "Colossa", "Crush", 72, 500, "S", [3000000000, 3900000000, 4800000000, 6000000000]),
nosferatu_nick: new RaidType("nosferatu_nick", "Z14", "Nosferatu Nick", "Nick", "Nick", 24, 500, "S", [3500000000, 4375000000, 5600000000, 7000000000]),
noir2: new RaidType("noir2", "Z19", "Noir (II)", "Noir (II)", "Noir2", 30, 500, "S", [5000000000, 6250000000, 8000000000, 10000000000]),
niflung_boar: new RaidType("niflung_boar", "ZA", "Niflung Boar", "Boar", "Boar", 30, 500, "S", [4000000000, 5000000000, 6400000000, 8000000000]),
vlarg_relic_hunter: new RaidType("vlarg_relic_hunter", "ZA2", "Vlarg Relic Hunter", "R. Hunter", "Vlarg", 30, 500, "S", [4500000000, 5625000000, 7200000000, 9000000000]),
trulcharn: new RaidType("trulcharn", "F1", "Trulcharn", "Trulcharn", "Trulcharn", 3, 10, "S", [10100000000, 10100000000, 10100000000, 10100000000], /*FS calculated normally*/null, 1010000000),
the_saboteur: new RaidType("the_saboteur", "ZA3", "The Saboteur", "Saboteur", "Saboteur", 30, 500, "S", [5000000000, 6250000000, 8000000000, 10000000000]),
the_tyraness: new RaidType("the_tyraness", "ZA4", "The Tyraness", "Tyraness", "Tyraness", 30, 500, "S", [5000000000, 6250000000, 8000000000, 10000000000]),
hwang: new RaidType("hwang", "C1-4", "Hwang", "Hwang", "Hwang", 64, 100, "S", [16666666667,25000000000,33333333333,50000000000], /*FS calculated normally*/null, 500000000),
mutheru: new RaidType("mutheru", "Z22", "Multheru", "Multheru", "Multheru", 64, 100, "S", [17666666667,26500000000,35333333333,53000000000], /*FS calculated normally*/null, 500000000),
unstable_singularity: new RaidType("unstable_singularity", "F6", "Unstable Singularity", "Singularity", "Unstable", 80, 500, "S", [175000000000, 350000000000, 437500000000, 525000000000], /*FS calculated normally*/null, 25000000000),
// Colossal+ Raids
besalaad_warmaster: new RaidType("besalaad_warmaster", "Z5", "Besalaad Warmaster", "Warmaster", "Warmaster", 168, 550, "S", [767250000, 1150875000, 1534500000, 2301750000], null, 12555000),
pinatas_revenge1: new RaidType("pinatas_revenge1", "S", "Pinata's Revenge", "Pinata II", "Pinata", 128, 500, "S", [75000000000, 87500000000, 110000000000, 210000000000], null, 1000000000),
// Titanic Raids
sinaroms_death_flora:new RaidType("sinaroms_death_flora","C1-5","Sinarom's Death Flora","Death Flora II","D.F. II",72,250, "S", [41666666667, 62500000000, 83333333333, 125000000000], /*FS calculated normally*/null, 500000000),
professor_bonderbrand:new RaidType("professor_bonderbrand","Z22","Professor Bonderbrand","Bonderbrand","Prof Bond",72,250, "S", [41666666667, 62500000000, 83333333333, 125000000000], /*FS calculated normally*/null, 500000000),
arcade_gas_attack: new RaidType("arcade_gas_attack", "AR", "Arcade Gas Attack", "A G Attack", "Gas Attack", 72, 250, "S", [36666666667,55000000000,73333333333,110000000000], /*FS calculated normally*/null, 500000000),
// Galactic Raids
sultan_shrakzan1: new RaidType("sultan_shrakzan1", "WR", "Sultan Shrakzan", "Shrakzan", "Shrakzan", 44, 500, "S", [300000000000, 300000000000, 300000000000, 300000000000], null, 1000000000),
tourniquet_seven_five: new RaidType("tourniquet_seven_five","C1-6", "Tourniquet 7.5", "Tourniquet 7.5", "T7.5", 80, 500, "S", [83333333333, 125000000000, 166666666667, 250000000000], /*FS calculated normally*/null, 500000000),
noir3: new RaidType("noir3", "Z22", "Noir (III)", "Noir (III)", "Noir3", 80, 500, "S", [83333333333, 125000000000, 166666666667, 250000000000], /*FS calculated normally*/null, 500000000),
arcade_gas_monster: new RaidType("arcade_gas_monster", "AR", "Arcade Gas Monster", "A G Monster", "Gas Monster", 80, 500, "S", [73333333333, 110000000000, 146666666667, 220000000000], /*FS calculated normally*/null, 500000000),
besalaad_exhibit_rampage1: new RaidType("besalaad_exhibit_rampage1","F5","Besalaad Exhibit Rampage", "Besalaad Exhibit", "Exhibit", 80, 500, "S", [100000000000,200000000000,250000000000,300000000000], /*FS calculated normally*/null, 600000000),
// Energy Raids
vince_vortex: new RaidType("vince_vortex", "GD", "Vince Vortex", "Vince", "Vortex", 24, 500, "E", [600000000, 750000000, 960000000, 1200000000]),
king_krandar1: new RaidType("king_krandar1", "WR", "King Krandar", "Krandar", "Krandar", 44, 500, "E", [250000000000, 250000000000, 250000000000, 250000000000], null, 1000000000),
fungal_invasion1: new RaidType("fungal_invasion1", "F7", "Fungal Invasion", "Fungal", "Invasion", 48, 250, "E", [25000000000,50000000000,62500000000,75000000000], /*FS calculated normally*/null, 1000000000),
elite_birthday_cake_of_doom: new RaidType("elite_birthday_cake_of_doom", "F9", "Elite Birthday Cake of Doom", "Elite Cake", "E. Cake", 24, 100, "E", [1875000000000, 1875000000000, 1875000000000, 1875000000000], /*FS calculated normally*/null, 20000000000),
// ALLIANCE RAIDS
// Small Raids
krakak: new RaidType("krakak", "A0", "Krakak Swarm", "Swarm", "Swarm", 120, 10, "H", [4500000, 5625000, 7200000, 9000000]),
kang: new RaidType("kang", "A1", "Kang", "Kang", "Kang", 120, 10, "H", [5000000, 6250000, 8000000, 10000000]),
crossbones_squadron: new RaidType("crossbones_squadron","A2", "Crossbones Squadron", "Crossbones", "XBones", 120, 10, "H", [8000000, 10000000, 12800000, 16000000]),
colonel_mustard: new RaidType("colonel_mustard", "A3", "Colonel Mustard", "Mustard", "Mustard", 120, 10, "H", [12000000, 15000000, 19200000, 24000000]),
professor_squid: new RaidType("professor_squid", "A4", "Professor Squid", "Squid", "Squid", 120, 10, "H", [18000000, 22500000, 28800000, 36000000]),
terminus_death_squad: new RaidType("terminus_death_squad","A5", "Terminus Death Squad", "Death Squad", "Death Squad",120,10,"H", [24000000, 30000000, 38400000, 48000000]),
luna: new RaidType("luna", "A6", "Luna", "Luna", "Luna", 120, 50, "H", [50000000, 62500000, 80000000, 100000000]),
rabid_reindeer: new RaidType("rabid_reindeer", "A8", "Rabid Reindeer", "Reindeer", "Reindeer", 60, 50, "H", [62500000, 81250000, 100000000, 125000000]),
// Medium Raids
infection: new RaidType("infection", "A0", "Infected Squad", "Infected", "Infected", 144, 50, "H", [30000000, 37500000, 48000000, 60000000]),
flora: new RaidType("flora", "A1", "Ruomyes' Death Flora", "Death Flora", "Flora", 144, 50, "H", [35000000, 43750000, 56000000, 70000000]),
psychic_cyborg: new RaidType("psychic_cyborg", "A2", "Mr. Justice", "Justice", "Justice", 144, 50, "H", [45000000, 56250000, 72000000, 90000000]),
grislak: new RaidType("grislak", "A3", "Grislak", "Grislak", "Grislak", 144, 50, "H", [55000000, 68750000, 88000000, 110000000]),
qin_legion: new RaidType("qin_legion", "A4", "Qin Legion", "Legion", "Legion", 144, 50, "H", [65000000, 81250000, 104000000, 130000000]),
terminus_interceptor_squadron: new RaidType("terminus_interceptor_squadron","A5", "Terminus Interceptor Squadron", "Interceptor", "Interceptor", 144, 50,"H", [75000000, 93750000, 120000000, 150000000]),
trashmaster: new RaidType("trashmaster", "A6", "Trashmaster Colby", "Colby", "Colby", 144, 50, "H", [100000000, 125000000, 160000000, 200000000]),
santas_workshop: new RaidType("santas_workshop", "A8", "SANTA's Workshop", "Workshop", "Workshop", 72, 50, "H", [125000000, 156250000, 200000000, 250000000]),
the_mega_mimes: new RaidType("the_mega_mimes", "A2-2", "The Mega Mimes", "Mimes", "Mimes", 84, 50, "H", [50000000, 62500000, 80000000, 100000000], null, 2000000),
// Large Raids
saucers: new RaidType("saucers", "A0", "Flying Saucers", "Saucers", "Saucers", 168, 100, "H", [55000000, 68750000, 88000000, 110000000]),
tourniquet: new RaidType("tourniquet", "A1", "Tourniquet 7", "Tourniquet 7", "T7", 168, 100, "H", [60000000, 75000000, 96000000, 120000000]),
rylattu_exterminator: new RaidType("rylattu_exterminator","A2", "Rylattu Exterminator", "Exterminator","Exterminator",168,100,"H", [100000000, 125000000, 160000000, 200000000]),
peacemaker_500: new RaidType("peacemaker_500", "A3", "Peacemaker 500", "Peacemaker", "Peacemaker", 168, 100, "H", [140000000, 175000000, 224000000, 280000000]),
kaltharan_devourer: new RaidType("kaltharan_devourer", "A4", "Kaltharan Devourer", "Devourer", "Devourer", 168, 100, "H", [180000000, 225000000, 288000000, 360000000]),
terminus_juggernaut: new RaidType("terminus_juggernaut","A5", "Terminus Juggernaut", "Juggernaut", "Juggernaut", 168, 100, "H", [200000000, 250000000, 320000000, 400000000]),
legacy_bot: new RaidType("legacy_bot", "A6", "Legacy Bot", "Legacy", "Legacy", 168, 100, "H", [250000000, 312500000, 400000000, 500000000]),
haunted_house: new RaidType("haunted_house", "AX", "Haunted House", "H. House", "House", 168, 100, "H", [350000000, 437500000, 560000000, 700000000]),
crazed_santa: new RaidType("crazed_santa", "AX", "Crazed Santa", "Santa", "Santa", 84, 100, "H", [400000000, 520000000, 640000000, 800000000]),
kristy_love: new RaidType("kristy_love", "AX", "Kristy Love", "Kristy", "Love", 84, 100, "H", [450000000, 585000000, 720000000, 900000000]),
gedrocht: new RaidType("gedrocht", "A9", "Gedrocht", "Gedrocht", "Gedrocht", 84, 100, "H", [500000000, 650000000, 800000000, 1000000000]),
nutcracker_sweet: new RaidType("nutcracker_sweet", "A11", "Nutcracker Sweet", "Sweet", "Sweet", 84, 100, "H", [750000000, 1000000000, 1500000000, 3000000000]),
crazy_jalfrezi: new RaidType("crazy_jalfrezi", "A12", "The Crazy Jalfrezi", "Jalfrezi", "Freezi", 84, 100, "H", [1000000000, 1250000000, 2000000000, 4000000000]),
patti: new RaidType("patti", "A13", "PATTI", "PATTI", "PATTI", 84, 100, "H", [1000000000, 1250000000, 2000000000, 4000000000]),
crimzo_the_killer_clown:new RaidType("crimzo_the_killer_clown","A2-1","Crimzo the Killer Clown","Crimzo","Crimzo",84, 100, "H", [1000000000, 1250000000, 2000000000, 4000000000]),
the_neon_knights: new RaidType("the_neon_knights", "A2-2", "The Neon Knights", "Neon", "Neon", 84, 100, "H", [500000000, 625000000, 800000000, 1000000000], null, 10000000),
kulnar_xex_shock_trooper_1: new RaidType("kulnar_xex_shock_trooper_1","A2-5","Kulnar-Xex Shock Trooper","K-X Shock Trooper","KX Shock",72,100,"H", [500000000, 625000000, 800000000, 1000000000],null,10000000),
invaders_from_dimension_b: new RaidType("invaders_from_dimension_b", "AX", "Invaders from Dimension B", "Dimension B", "Dimension B", 48, 100, "H", [250000000000, 300000000000, 350000000000, 750000000000], null, 7500000000),
// Epic Raids
lurking_horror: new RaidType("lurking_horror", "A2", "Lurking Horror", "Lurking", "Lurking", 168, 100, "H", [250000000, 312500000, 400000000, 500000000]),
ship_of_the_damned: new RaidType("ship_of_the_damned", "A3", "Ship of the Damned", "Damned", "Damned", 168, 100, "H", [300000000, 375000000, 480000000, 600000000]),
mecha_wyrm: new RaidType("mecha_wyrm", "A4", "Mecha-Wyrm", "Wyrm", "Wyrm", 168, 100, "H", [350000000, 437500000, 560000000, 700000000]),
genesis: new RaidType("genesis", "A5", "Genesis", "Genesis", "Genesis", 165, 100, "H", [1000000000, 1250000000, 1600000000, 2000000000]),
contest_winners: new RaidType("contest_winners", "A6", "Shadows of the Void", "Shadows", "Shadows", 168, 100, "H", [500000000, 625000000, 800000000, 1000000000]),
celebration_enhancer_1: new RaidType("celebration_enhancer_1","AX","Celebration Enhancer J-54","Celebrator","Celeb",84,100, "H", [600000000, 750000000, 960000000, 1200000000]),
quiskan_psi_hound: new RaidType("quiskan_psi_hound", "A7","Quiskan Psi-Hound","Psi-Hound","Hound", 168, 100, "H", [1000000000, 1500000000, 2500000000, 10000000000]),
ms_myriad_and_steelstike: new RaidType("ms_myriad_and_steelstike","A10","Ms. Myriad and Steelstrike","M & S","M & S",168,100,"H", [1500000000, 2000000000, 3000000000, 12500000000]),
the_gamma_hammers: new RaidType("the_gamma_hammers", "A2-3", "The Gamma Hammers", "Gammas", "Gammas", 84, 100, "H", [2500000000, 3125000000, 4000000000, 5000000000], null, 50000000),
kulnarxex_tank_1: new RaidType("kulnarxex_tank_1", "A2-4", "Kulnar-Xex Tank", "K-X Tank", "KX Tank", 72, 100, "H", [2500000000, 3125000000, 4000000000, 5000000000], null, 50000000),
// Colossal Raids
wahsh: new RaidType("wahsh", "AX", "Wahsh Al-Sahraa", "Wahsh", "Wahsh", 84, 100, "H", [500000000, 1250000000, 3125000000, 7812500000]),
the_chem_runners: new RaidType("the_chem_runners", "A2-4", "The Chem-Runners", "Runners", "Chem", 84, 100, "H", [50000000000, 62500000000, 80000000000, 100000000000], null, 1000000000),
training_sim1: new RaidType("training_sim1","AX", "Live Fire Training Sim #102", "Training Sim #102", "Sim #102", 72, 100, "H", [35000000000,70000000000,87500000000,105000000000], null, 1000000000),
rogue_terraformer1: new RaidType("rogue_terraformer1", "AX", "Rogue Terraformer", "Rogue", "Terraformer", 72, 100, "H", [80000000000, 104000000000, 120000000000, 136000000000], null, 3000000000),
// Titanic Raids
thyestean_banquet1: new RaidType("thyestean_banquet1","AX", "Thyestean Banquet", "Banquet", "Banquet", 72, 100, "H", [50000000000, 62500000000, 80000000000, 100000000000], null, 1000000000),
rak_thun_eviscipod1: new RaidType("rak_thun_eviscipod1","AX", "Rak-Thun Eviscipod", "Eviscipod", "Evipod", 72, 100, "H", [110000000000,220000000000,275000000000,330000000000],/*FS computed normally*/null, 3300000000),
ruins_of_the_forgotten1: new RaidType("ruins_of_the_forgotten1", "AX", "Ruins of the Forgotten", "Ruins", "Forgotten", 48, 100, "H", [250000000000, 300000000000, 350000000000, 475000000000],/* FS computed normally*/null, 6000000000),
// OPERATIONS
// Operation: Scavenger's Scramble
centi_prider_scavenger: new RaidType("centi_prider_scavenger","OP-SS","Centi Prider Scavenger","Centi Scav","Centi Scav",18,25,"S",[5000000000,12000000000,0,0],/*FS computed normally*/null, 1300000000),
elite_centi_prider_scavenger: new RaidType("elite_centi_prider_scavenger","OP-SS","Elite Centi Prider Scavenger","E. Centi Scav","E. Centi Scav",18, 25, "S", [5000000000,12000000000,0,0], /*FS computed normally*/null, 1300000000),
kulnar_xex_scavenger: new RaidType("kulnar_xex_scavenger","OP-SS","Kulnar-Xex Scavenger","Kulnar Scav","Kulnar Scav",18, 50,"S", [10000000000,24000000000,0,0], /*FS computed normally*/null, 1300000000),
elite_kulnar_xex_scavenger: new RaidType("elite_kulnar_xex_scavenger","OP-SS","Elite Kulnar-Xex Scavenger","E. Kulnar Scav","E. Kulnar Scav",18, 50, "S", [10000000000,24000000000,0,0],/* FS computed normally*/null, 1300000000),
vlarg_scavenger: new RaidType("vlarg_scavenger","OP-SS","Vlarg Scavenger","Vlarg Scav","Vlarg Scav", 18, 75, "S", [15000000000,36000000000,0,0],/* FS computed normally*/null, 1300000000),
elite_vlarg_scavenger:new RaidType("elite_vlarg_scavenger","OP-SS","Elite Vlarg Scavenger","E. Vlarg Scav","E. Vlarg Scav",18, 75, "S", [15000000000,36000000000,0,0],/* FS computed normally*/null, 1300000000),
besalaad_warrior: new RaidType("besalaad_warrior", "OP-SS", "Besalaad Warrior", "B. Warrior", "B. Warrior", 18, 100, "S", [30000000000,100000000000,0,0],/* FS computed normally*/null, 1300000000),
strange_parasite: new RaidType("strange_parasite", "OP-SS", "Strange Parasite", "Parasite", "Parasite", 18, 100, "S", [35000000000,115000000000,0,0],/* FS computed normally*/null, 1300000000),
pumpkin_pirate_scavenger:new RaidType("pumpkin_pirate_scavenger","OP-SS","Pumpkin Pirate Scavenger","Pumpkin Scav","Pumpkin Scav",18, 100, "S",[35000000000,115000000000,0,0],/* FS computed normally*/null, 1300000000),
besalaad_commander: new RaidType("besalaad_commander","OP-SS","Besalaad Commander","B. Commander","B. Commander",18, 100, "S", [45000000000,125000000000,0,0],/* FS computed normally*/null, 1300000000),
// Operation: Deep Cyan Sea
kalaxian_projection: new RaidType("kalaxian_projection", "OP-DCS","Kalaxian Projection","Projection","Projection",18, 50, "S", [20000000000,36000000000,0,0],/* FS computed normally*/null, 1300000000),
kalaxian_cultist_ship: new RaidType("kalaxian_cultist_ship","OP-DCS", "Kalaxian Cultist Ship", "Kalax Ship", "Kalax Ship", 18, 25, "S", [10000000000,24000000000,0,0],/* FS computed normally*/null, 1300000000),
kalaxian_cultist_bikers: new RaidType("kalaxian_cultist_bikers","OP-DCS", "Kalaxian Cultist Bikers", "Kalax Biker", "Kalax Biker", 18, 50, "S", [20000000000,36000000000,0,0],/* FS computed normally*/null, 1300000000),
kalaxian_cultists: new RaidType("kalaxian_cultists","OP-DCS", "Kalaxian Cultists", "Kalax Cult", "Kalax Cult", 18, 75, "S", [20000000000,48000000000,0,0],/* FS computed normally*/null, 1300000000),
kalaxian_cult_master:new RaidType("kalaxian_cult_master", "OP-DCS", "Kalaxian Cult Master", "K. Cult Master", "K. Cult Master", 18, 100, "S", [50000000000,150000000000,0,0],/* FS computed normally*/null, 1300000000),
slither: new RaidType("slither", "OP-DCS", "Slither", "Slither", "Slither", 18, 100, "S", [70000000000,200000000000,0,0],/* FS computed normally*/null, 1300000000),
// Operation: Transdimentional Encounter
parasite_cloud: new RaidType("parasite_cloud", "OP-TE", "Parasite Cloud", "Cloud", "Cloud", 18, 25, "H", [15000000000, 30000000000, 0, 0],/* FS computed normally*/null, 1300000000),
antibody_drone: new RaidType("antibody_drone", "OP-TE", "Antibody Drone", "Drone", "Drone", 18, 50, "H", [24000000000, 48000000000, 0, 0], /*FS computed normally*/null, 1300000000),
antibody_swarm: new RaidType("antibody_swarm", "OP-TE", "Antibody Swarm", "Anti Swarm", "Anti Swarm", 18, 100, "H", [50000000000, 150000000000, 0, 0],/* FS computed normally*/null, 1300000000),
exozoic_hulk: new RaidType("exozoic_hulk", "OP-TE", "Exozoic Hulk", "Exozoic", "Exozoic", 18, 100, "H", [55000000000, 132000000000, 0, 0],/* FS computed normally*/null, 1300000000),
plasmatic_entity: new RaidType("plasmatic_entity", "OP-TE", "Plasmatic Entity", "Plasmatic", "Plasmatic", 18, 100, "H", [60000000000, 180000000000, 0, 0],/* FS computed normally*/null, 1300000000),
// WORLD RAIDS
// Infestation WR Trilogy
inf_ship: new RaidType("inf_ship", "WR", "The Python", "Python", "Python WR", 72, 90000, "SEH", "Infinite", "N/A", 1000000000),
inf_colony: new RaidType("inf_colony", "WR", "Infested Colony", "Colony", "Colony WR", 72, 90000, "SEH", "Infinite", "N/A", 1000000000),
inf_lair: new RaidType("inf_lair", "WR", "Alien Lair", "Lair", "Lair WR", 72, 90000, "SEH", "Infinite", "N/A", 1000000000),
general_skorzeny: new RaidType("general_skorzeny", "WR", "General Skorzeny", "Skorzeny", "Skorz", 72, 90000, "SEH", "Infinite", "N/A", 1500000000000),
wr_space_pox: new RaidType("wr_space_pox", "WR", "Intergalactic Space Pox", "WR Pox", "WR Pox", 72, 90000, "SEH", "Infinite", "N/A", 5000000000),
cerebral_destroyer: new RaidType("cerebral_destroyer", "WR", "Cerebral Destroyer", "Cerebral", "CD WR", 72, 90000,"SEH", "Infinite", "N/A", 10000000000),
kraken: new RaidType("kraken", "WR", "Kraken", "Kraken", "Kraken WR", 72, 90000, "SEH", "Infinite", "N/A", 50000000000),
christmas_montage: new RaidType("christmas_montage", "WR", "Christmas Campaign", "Christmas", "Xmas WR", 48, 90000, "SEH", "Infinite", "N/A", 5000000000),
schism: new RaidType("schism", "WR", "Schism", "Schism", "Schism WR", 120, 90000, "SEH", "Infinite", "N/A", 50000000000),
inventors_revenge: new RaidType("inventors_revenge", "WR", "Inventor's Revenge", "Revenge", "Revenge WR", 72, 90000, "SEH", "Infinite", "N/A", 75000000000),
hel: new RaidType("hel", "WR", "Hel", "Hel", "Hel WR", 72, 90000, "SEH", "Infinite", "N/A", 75000000000),
centi_priders: new RaidType("centi_priders", "WR", "Centi Priders", "Centies", "Centies WR", 72, 90000, "SEH", "Infinite", "N/A", 75000000000),
kulnar_xex_battle_station_1: new RaidType("kulnar_xex_battle_station_1","WR","Kulnar-Xex Battle Station","K-X Battle Station","KX BS WR", 72,90000,"SEH","Infinite","N/A",200000000000),
cow_abduction_1: new RaidType("cow_abduction_1", "WR", "Rylattu Cow Abduction", "Cow Abduction", "Cow WR", 72, 90000, "SEH", "Infinite", "N/A", 10000000000),
dimetrodon_riot: new RaidType("dimetrodon_riot", "RS", "Dimetrodon Riot", "D. Riot", "Riot RS", 24, 90000, "SEH", "Infinite", "N/A", 200000000000),
trouble_in_tokyo: new RaidType("trouble_in_tokyo", "WR", "Trouble in Tokyo", "Tokyo", "Tokyo WR", 120, 90000, "SEH", "Infinite", "N/A", 400000000000),
kalaxian_assault: new RaidType("kalaxian_assault", "WR", "Kalaxian Assault", "Kalax Assault", "Kalax WR", 96, 99999, "SEH", "Infinite", "N/A", 200000000000),
contest_winner: new RaidType("contest_winner", "WR", "Hyper-Con Havoc", "Havoc WR", "Havoc WR", 96, 99999, "SEH", "Infinite", "N/A", 200000000000),
elves: new RaidType("elves", "WR", "Elven Uprising", "Elven", "Elven", 96, 99999, "SEH", "Infinite", "N/A", 400000000000),
game_master: new RaidType("game_master", "WR", "Game Master", "Game Master", "Game Master", 72, 99999, "SEH", "Infinite", "N/A", 1500000000000),
solar_swarm: new RaidType("solar_swarm", "WR", "Solar Swarm", "Solar", "Solar", 72, 99999, "SEH", "Infinite", "N/A", 1500000000000),
sun_egg: new RaidType("sun_egg", "WR", "Sun Egg", "Sun Egg", "Egg", 72, 99999, "SEH", "Infinite", "N/A", 1500000000000),
attack_of_the_gourds: new RaidType("attack_of_the_gourds", "WR", "Attack of the Gourds", "Gourds", "Gourds", 72, 99999, "SEH", "Infinite", "N/A", 2500000000000),
predatory_constellation: new RaidType("predatory_constellation", "WR", "Predatory Constellation", "Constellation", "Constellation", 120, 99999, "SEH", "Infinite", "N/A", 10000000000000),
// RARE SPAWNS
raging_snowman: new RaidType("raging_snowman", "RS", "Raging Snowman", "Snowman", "Snowman RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
space_pox_mary: new RaidType("space_pox_mary", "RS", "Space Pox Mary", "Mary", "Mary RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
mutated_spacepox_1: new RaidType("mutated_spacepox_1", "RS", "Mutated Space Pox", "Mutated", "Mutated", 24, 90000, "SEH", "Infinite", "N/A", 20000000000),
cerebral_ceo: new RaidType("cerebral_ceo", "RS", "Cerebral CEO", "CEO", "CEO RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
penelope_wellerd: new RaidType("penelope_wellerd", "RS", "Penelope Wellerd", "Wellerd", "Wellerd RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
h8: new RaidType("h8", "RS", "H8", "H8", "H8 RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
inventors_scheme: new RaidType("inventors_scheme", "RS", "Inventor's Scheme", "Scheme", "Scheme RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
predator_moon: new RaidType("predator_moon", "RS", "Predator Moon", "Predator", "Moon RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
"5th_planet": new RaidType("5th_planet", "RS", "5th Planet", "5th Planet", "5th Planet RS", 24, 90000, "SEH", "Infinite", "N/A", 2000000000),
cerebral_monster_mech: new RaidType("cerebral_monster_mech","RS", "Cerebral Monster Mech", "Cerebral MM", "CMM RS", 24, 90000, "SEH", "Infinite", "N/A", 20000000000),
kulnarxex_scout_ships_1: new RaidType("kulnarxex_scout_ships_1","RS","Kulnar-Xex Scout Ships","K-X Scout Ships","KX Scout RS", 24,90000,"SEH","Infinite","N/A",25000000000),
kulnarxex_bombarder_1: new RaidType("kulnarxex_bombarder_1","RS","Kulnar-Xex Bombarder","K-X Bombarder","KX Bomb RS", 24,90000,"SEH", "Infinite", "N/A", 25000000000),
ship_pinata: new RaidType("ship_pinata", "RS", "Ship Pinata", "Pinata", "Pinata RS", 24, 90000, "SEH", "Infinite", "N/A", 25000000000),
besalaad_warmasterrs: new RaidType("besalaad_warmasterrs","RS","Besalaad Elite Warmaster", "E. Warmaster","BEW RS", 24, 90000, "SEH", "Infinite", "N/A", 50000000000),
star_turtles_distress: new RaidType("star_turtles_distress","RS","Star Turtle's Distress","Star Turtle","Turtle RS", 24,99999, "SEH", "Infinite", "N/A", 75000000000),
kleptotherms: new RaidType("kleptotherms", "RS", "Kleptotherms", "Kleptotherms", "Kleptotherms RS", 24, 99999, "SEH", "Infinite", "N/A", 100000000000),
hate: new RaidType("hate", "RS", "Hate Walker", "Hate", "Hate", 24, 99999, "SEH", "Infinite", "N/A", 100000000000)
};/************************************/
/********* Utility Functions ********/
/************************************/
if (!window.holodeck) {
window.holodeck = new Holodeck();
}
/**
* Returns the boolean opposite of the result of the given function
* @param fn The function to take the opposite of
* @param scope {Object?} The scope to call the function with
*/
DC_LoaTS_Helper.not = function(fn, scope) {
return function() {
return !fn.apply(scope || window, arguments);
}
};
/**
* Returns a function of all the arguments passed in called in order
*/
DC_LoaTS_Helper.chain = function() {
var fns = arguments;
return function() {
var ret;
for (var i = 0; i < fns.length; i++) {
try {
ret = fns[i].apply(this, arguments);
}
catch (e) {
console.error("Utilities.js: Error during function chain", e);
}
}
return ret;
}
};
DC_LoaTS_Helper.getCurrentUsername = function() {
return holodeck.username();
};
DC_LoaTS_Helper.isFriend = function(username) {
return holodeck.chatWindow().isFriend(username);
};
DC_LoaTS_Helper.addFriend = function(username) {
new Ajax.Request("http://www.kongregate.com/accounts/" + DC_LoaTS_Helper.getCurrentUsername() + "/friends/"+ username + "?friend_from=chat", {
method: 'put',
onComplete: function(transport)
{
DCDebug("Added Friend: " + transport.request.url);
// Update the listing in the top of the chat
holodeck.addFriend(this);
}
});
};
DC_LoaTS_Helper.removeFriend = function(username) {
new Ajax.Request("http://www.kongregate.com/accounts/" + DC_LoaTS_Helper.getCurrentUsername() + "/friends/"+ username + "?friend_from=chat", {
method: 'delete',
onComplete: function(transport)
{
DCDebug("Removed Friend: " + transport.request.url);
// Update the listing in the top of the chat
holodeck.removeFriend(this);
}
});
};
DC_LoaTS_Helper.isMuted = function(username) {
return holodeck.chatWindow().isMuted(username);
};
DC_LoaTS_Helper.muteUser = function(username) {
new Ajax.Request("http://www.kongregate.com/accounts/" + DC_LoaTS_Helper.getCurrentUsername() + "/muted_users/?username="+ username + "&from_chat=true", {
method: 'post',
onComplete: function(transport)
{
DCDebug("Muted User: " + transport.request.url);
// Update the listing in the top of the chat
holodeck.chatWindow().addMutings([username]);
}
});
};
DC_LoaTS_Helper.unmuteUser = function(username) {
new Ajax.Request("http://www.kongregate.com/accounts/" + DC_LoaTS_Helper.getCurrentUsername() + "/muted_users/"+ username + "?from_chat=true", {
method: 'delete',
onComplete: function(transport)
{
DCDebug("Unmuted User: " + transport.request.url);
// Update the listing in the top of the chat
holodeck.chatWindow().removeMutings([username]);
}
});
};
DC_LoaTS_Helper.openPrivateProfileMessageWindow = function(username) {
username && window.open("http://www.kongregate.com/accounts/" + username + "/private_messages?focus=true", "_blank");
};
DC_LoaTS_Helper.showUgUpProfile = function(username) {
DCDebug("Utilities.js: Showing UgUp Profile");
if (username) {
RaidMenu.setActiveTab(RaidMenu.tabClasses[40]);
document.getElementById("CharacterViewMenu-UsernameBox").value = username;
document.getElementById("CharacterViewMenu-RunQueryButton").click();
}
};
// Hooks up a listener for a particular event on a specific object
// Borrowed from: http://www.quirksmode.org/js/eventSimple.html
DC_LoaTS_Helper.registerEventHandler = function(obj,evt,fn)
{
if (obj.addEventListener)
{
obj.addEventListener(evt,fn,false);
}
else if (obj.attachEvent)
{
obj.attachEvent('on'+evt,fn);
}
};
// Unhooks a listener for a particular event on a specific object
// Borrowed from: http://www.quirksmode.org/js/eventSimple.html
DC_LoaTS_Helper.unregisterEventHandler = function(obj,evt,fn)
{
if (obj.removeEventListener)
{
obj.removeEventListener(evt,fn,false);
}
else if (obj.detachEvent)
{
obj.detachEvent('on'+evt,fn);
}
};
DC_LoaTS_Helper.isLeftClickEvent = function(evt) {
// evt.which for IE6,7,8/Opera. evt.button for everyone else
return (evt.button && (evt.button == 0)) || (evt.which && (evt.which == 1));
};
DC_LoaTS_Helper.isRightClickEvent = function(evt) {
// evt.which for IE6,7,8/Opera. evt.button for everyone else
return (evt.button && (evt.button == 2)) || (evt.which && (evt.which == 3));
};
DC_LoaTS_Helper.getEventTarget = function(evt) {
var target = evt.target || evt.srcElement;
// Safari work around, not that we even support Safari...
if (target.nodeType && target.nodeType == 3)
{
target = target.parentNode;
}
return target;
};
// Should prevent the event from doing its normal action
// like selecting text on click and drag.
// Borrowed from http://stackoverflow.com/a/891616
DC_LoaTS_Helper.stopDefaultEventAction = function(evt)
{
if (evt && evt.preventDefault)
{
evt.preventDefault();
}
else
{
window.event.returnValue = false;
}
return false;
};
// Should prevent the event from propagating to Kongregate code
DC_LoaTS_Helper.stopEventPropagation = function(evt)
{
if (evt && evt.stopPropagation)
{
evt.stopPropagation();
}
return false;
};
// Borrowed from http://stackoverflow.com/a/384380
DC_LoaTS_Helper.isElement = function(o) {
return o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string";
};
DC_LoaTS_Helper.removeAllChildren = function(parentNode) {
while (parentNode.firstChild) {
parentNode.removeChild(parentNode.firstChild);
}
};
// Pretty format health / damage numbers
DC_LoaTS_Helper.prettyFormatNumber = function(num)
{
var text = "?";
if (typeof num === "number")
{
// Trillions
if (num >= 1000000000000)
{
text = parseFloat((num/1000000000000).toFixed(3)) + "T";
}
// Billions
else if (num >= 1000000000)
{
text = parseFloat((num/1000000000).toFixed(2)) + "B";
}
// Millions
else if (num >= 1000000)
{
text = parseFloat((num/1000000).toFixed(2)) + "M";
}
// Thousands
else if (num >= 1000)
{
text = parseFloat((num/1000).toFixed(1)) + "K";
}
// Other
else if (num > 0)
{
text = num + "";
}
}
else if (typeof num === "string")
{
text = num;
}
return text;
};
// Responds to a click on a raid link
// Returns true if the browser should load the raid itself, false if we loaded without refresh
DC_LoaTS_Helper.raidLinkClick = function(eventParam)
{
// Want to handle any errors that occur in here
try
{
// Just in case
var event = eventParam || window.event;
// Couldn't locate event
if (typeof event == "undefined")
{
console.warn("Couldn't locate the event for right-click detection");
// Don't cancel the click
return;
}
// Get the target element
var target;
if (event.target)
{
target = event.target;
}
else if (event.srcElement)
{
target = event.srcElement;
}
// Safari work around
if (target.nodeType == 3)
{
target = target.parentNode;
}
if (typeof target == "undefined")
{
console.warn("Couldn't locate the target for right-click detection");
// Don't cancel the click
return;
}
// Grab the url from the link
var url = target.href;
// Still failed
if (typeof url == "undefined" || url.length == 0)
{
// In certain cases, the image can detect the click instead of the link
if (target.parentNode.href != "undefined")
{
url = target.parentNode.href;
}
else
{
console.warn("Trouble determining url from link. Could not apply click.");
console.warn(event);
console.warn(target);
// Let the click go through and reload the whole browser. Better than nothing.
return true;
}
}
// If the user is holding shift, cycle through the states
if (event.shiftKey)
{
// Generate an actual raid link object
var raidLink = new RaidLink(url);
// If the link is valid
if (raidLink.isValid())
{
// Get the STATE of the link
var linkState = RaidManager.fetchState(raidLink);
// Place holder for our new state
var newLinkState;
var foundCurrent = false;
var firstState;
// Iterate over all possible states
// This is basically a hack for the fact that the
// STATEs don't have any inherit ordinal values that could be incremented
//TODO: Reorganize STATE to have ordinals if this ever happens somewhere else in the code
for (var stateKey in RaidManager.STATE)
{
if (RaidManager.STATE.hasOwnProperty(stateKey)) {
// Grab the state
var state = RaidManager.STATE[stateKey];
// Make sure this isn't a function or anything from STATE
if (typeof state == "object")
{
// If this is the first state we've seen
if (typeof firstState == "undefined")
{
// Capture it so we can roll back around past the last state
firstState = state;
}
// If this is the same state as the link is currently in
if (RaidManager.STATE.equals(linkState, state))
{
// Note the current state
foundCurrent = true;
}
// If we found current, this must be the next state
else if (foundCurrent)
{
// Grab this state to save as the new state
newLinkState = state;
// Don't accidentally find other states
break;
}
}
}
}
// If we did not find a new state to set it to
if (typeof newLinkState == "undefined")
{
// Cycle back around to the first state
newLinkState = firstState;
}
// Store the link with its new state
RaidManager.store(raidLink, newLinkState);
}
// Log invalid raid links
else
{
console.warn("Clicked invalid link to " + url);
}
// Always suppress reload on shift-clicks
return false;
}
// If the user is not holding shift, just load the raid
else
{
return DC_LoaTS_Helper.loadRaid(url);
}
}
catch(ex)
{
// Notify the user of the issue
console.warn("An error occurred while trying to handle your click!");
console.warn(ex);
// Let the click go through. Annoying, but still can load raid
return true;
}
};
// Intended solely to capture right clicks for the purpose of marking them visited
DC_LoaTS_Helper.raidLinkMouseDown = function(eventParam)
{
// Just in case
var event = eventParam || window.event;
// Couldn't locate event
if (typeof event == "undefined")
{
console.warn("Couldn't locate the event for right-click detection");
// Don't cancel the click
return;
}
// Only care about right clicks
if (DC_LoaTS_Helper.isRightClickEvent(event))
{
// Get the target element
var target = DC_LoaTS_Helper.getEventTarget(event);
// If there was no target
if (typeof target === "undefined")
{
console.warn("Couldn't locate the target for right-click detection");
// Don't cancel the click
return;
}
// Grab the url from the link
var url = target.href;
// Still failed
if (typeof url == "undefined" || url.length == 0)
{
// In certain cases, the image can detect the click instead of the link
if (target.parentNode.href != "undefined")
{
url = target.parentNode.href;
}
else
{
console.warn("Trouble determining url from link. Could not apply click.");
console.warn(event);
console.warn(target);
// No useful work to complete here
return;
}
}
// Successfully got the url
// Get the raid link
var raidLink = new RaidLink(url);
// Only care about valid links
if (raidLink.isValid())
{
if (DC_LoaTS_Helper.getPref("RightClickVisited", true) === true)
{
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
}
}
else
{
console.warn("Could not parse url (\"" + url + "\") into a valid RaidLink");
console.warn(raidLink);
}
}
};
DC_LoaTS_Helper.handleMessageWindowClickHandler = function() {
DCDebug("Utilities.js: DC_LoaTS_Helper.handleMessageWindowClickHandler");
var lctw = DC_LoaTS_Helper.getPref("LeftClickToWhisper", true);
DCDebug("LeftClickToWhisper: ", lctw);
if (holodeck._chat_window && holodeck._chat_window._chat_rooms_container_node) {
// We should be attaching the left click handler
if (lctw) {
// This destroys the existing Kong functionality of left-clicking to load mini-profile
ChatDialogue.MESSAGE_TEMPLATE.template = ChatDialogue.MESSAGE_TEMPLATE.template.replace('username="#{username}"', '_username="#{username}"');
// Actually register the click handler onto the node
DC_LoaTS_Helper.registerEventHandler(
holodeck._chat_window._chat_rooms_container_node,
"click",
DC_LoaTS_Helper.messageWindowClick
);
// Register the click handler to hide the menu on the body
DC_LoaTS_Helper.registerEventHandler(
document.body,
"click",
DC_LoaTS_Helper.hideContextMenu
);
}
else {
// This repairs the existing Kong functionality of left-clicking to load mini-profile
ChatDialogue.MESSAGE_TEMPLATE.template = ChatDialogue.MESSAGE_TEMPLATE.template.replace('_username="#{username}"', 'username="#{username}"');
// Actually unregister the click handler onto the node
DC_LoaTS_Helper.unregisterEventHandler(
holodeck._chat_window._chat_rooms_container_node,
"click",
DC_LoaTS_Helper.messageWindowClick
);
// Unregister the click handler to hide the menu on the body
DC_LoaTS_Helper.unregisterEventHandler(
document.body,
"click",
DC_LoaTS_Helper.hideContextMenu
);
}
}
else {
DCDebug("Waiting 1 second to try again");
setTimeout(DC_LoaTS_Helper.handleMessageWindowClickHandler, 1000);
}
};
DC_LoaTS_Helper.handleMessageWindowContextMenuHandler = function() {
DCDebug("Utilities.js: DC_LoaTS_Helper.handleMessageWindowContextMenuHandler");
var rcm = DC_LoaTS_Helper.getPref("RightClickUserMenu", true);
DCDebug("RightClickUserMenu: ", rcm);
if (holodeck._chat_window && holodeck._chat_window._chat_rooms_container_node) {
// We should be attaching the context menu
if (rcm) {
// Actually register the click handler onto the node
DC_LoaTS_Helper.registerEventHandler(
holodeck._chat_window._chat_rooms_container_node,
"contextmenu",
DC_LoaTS_Helper.messageWindowRightClick
);
}
else {
// Unregister the click handler onto the node
DC_LoaTS_Helper.unregisterEventHandler(
holodeck._chat_window._chat_rooms_container_node,
"contextmenu",
DC_LoaTS_Helper.messageWindowRightClick
);
}
}
else {
DCDebug("Waiting 1 second to try again");
setTimeout(DC_LoaTS_Helper.handleMessageWindowContextMenuHandler, 1000);
}
};
DC_LoaTS_Helper.messageWindowClick = function(event) {
var ret,
lctw = DC_LoaTS_Helper.getPref("LeftClickToWhisper", true),
username;
if (lctw) {
// If we've altered the left click functionality (lctw==true), the username will be in _username
if (event.target &&
(username = event.target.getAttribute("_username") || event.target.getAttribute("username"))) {
// Is it a left click
if (DC_LoaTS_Helper.isLeftClickEvent(event)) {
// Since we're doing this, don't let any other actions have it
DC_LoaTS_Helper.stopDefaultEventAction(event);
DCDebug("Caught left click on name", event, username);
// Insert the /w username into the chat area
holodeck.chatWindow().insertPrivateMessagePrefixFor(username);
ret = false;
}
}
}
return ret;
};
DC_LoaTS_Helper.messageWindowRightClick = function(event) {
var ret,
rcm = DC_LoaTS_Helper.getPref("RightClickUserMenu", true),
contextMenu,
username, coords;
if (rcm) {
// If we've altered the left click functionality (lctw==true), the username will be in _username
if (event.target &&
(username = event.target.getAttribute("_username") || event.target.getAttribute("username"))) {
// Since we're doing this, don't let any other actions have it
DC_LoaTS_Helper.stopDefaultEventAction(event);
DCDebug("Caught right click on name", event, username);
// Hide the existing context menu
DC_LoaTS_Helper.hideContextMenu();
// Get the absolute coordinates of mouse event
coords = DC_LoaTS_Helper.getMouseEventCoords(event);
DCDebug("Utilities.js: Right click event", event);
// Create context menu
contextMenu = DC_LoaTS_Helper.createUserContextMenu(username);
// Pop-up context menu
DC_LoaTS_Helper.showContextMenu(contextMenu, coords.x, coords.y);
ret = false;
}
}
return ret;
};
DC_LoaTS_Helper.getMouseEventCoords = function(e) {
var posx = 0,
posy = 0;
e = e || window.event;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft
+ document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop
+ document.documentElement.scrollTop;
}
return {x: posx, y: posy};
};
DC_LoaTS_Helper.userContextMenuItems = [
{
text: "{username}'s Kong Profile",
title: "Show {username}'s Kongregate mini-profile normally shown on left-clicks",
fn: holodeck.chatWindow().showProfile.bind(holodeck.chatWindow())
},
{
text: "Show LoTS Profile",
title: "Show {username}'s LoTS public profile via UgUp",
fn: DC_LoaTS_Helper.showUgUpProfile
},
{
text: "Send Profile Message",
title: "Send {username} a Kongregate profile private message",
fn: DC_LoaTS_Helper.openPrivateProfileMessageWindow
},
{
text: "Add Friend",
title: "Add {username} as your friend",
condition: DC_LoaTS_Helper.not(DC_LoaTS_Helper.isFriend),
fn: DC_LoaTS_Helper.addFriend
},
{
text: "Unfriend",
title: "Remove {username} from your friends list",
condition: DC_LoaTS_Helper.isFriend,
fn: DC_LoaTS_Helper.removeFriend
},
{
text: "Mute",
title: "Mute {username} to hide their messages in chat",
condition: DC_LoaTS_Helper.not(DC_LoaTS_Helper.isMuted),
fn: DC_LoaTS_Helper.muteUser
},
{
text: "Unmute",
title: "Unmute {username} to show their messages in chat",
condition: DC_LoaTS_Helper.isMuted,
fn: DC_LoaTS_Helper.unmuteUser
}
];
DC_LoaTS_Helper.createUserContextMenu = function (username) {
DCDebug("Utilities.js: Creating Context Menu for user " + username);
var menu = document.createElement("ul"),
itemDef, li, a;
menu.id = "DC_LoaTS_contextMenu";
menu.className = "context-menu user-context-menu";
for (var i = 0; i < DC_LoaTS_Helper.userContextMenuItems.length; i++) {
itemDef = DC_LoaTS_Helper.userContextMenuItems[i];
a = null;
DCDebug("Utilities.js: itemDef: ", itemDef);
if (typeof itemDef.condition === "undefined" ||
(typeof itemDef.condition === "function" && itemDef.condition(username))) {
li = document.createElement("li");
li.className = "menu-item";
if (itemDef.fn) {
a = document.createElement("a");
a.onclick = DC_LoaTS_Helper.chain(
itemDef.fn.bind(this, username),
function(clickEvent) {
DC_LoaTS_Helper.stopDefaultEventAction(clickEvent);
DC_LoaTS_Helper.stopEventPropagation(clickEvent);
DC_LoaTS_Helper.hideContextMenu(menu);
});
li.appendChild(a);
}
if (itemDef.title) {
(a||li).title = itemDef.title.replace("{username}", username);
}
if (itemDef.text) {
(a||li).appendChild(document.createTextNode(itemDef.text.replace("{username}", username)));
}
menu.appendChild(li);
}
}
DCDebug("Utilities.js: Created Menu", menu);
return menu;
};
DC_LoaTS_Helper.showContextMenu = function (contextMenu, x, y) {
DCDebug("Utilities.js: showContextMenu ", arguments);
if (DC_LoaTS_Helper.contextMenu) {
DC_LoaTS_Helper.hideContextMenu(DC_LoaTS_Helper.contextMenu);
}
DC_LoaTS_Helper.contextMenu = contextMenu;
contextMenu.style.position = "absolute";
contextMenu.style.left = x + "px";
contextMenu.style.top = y + "px";
contextMenu.style.visible = "visible";
contextMenu.style.display = "auto";
document.body.appendChild(contextMenu);
};
DC_LoaTS_Helper.hideContextMenu = function(contextMenu) {
// If this is a click event or something, it's not an element
if (!DC_LoaTS_Helper.isElement(contextMenu)) {
contextMenu = null;
}
contextMenu = contextMenu || DC_LoaTS_Helper.contextMenu || document.getElementById("DC_LoaTS_contextMenu");
DCDebug("Hiding Context Menu: ", DC_LoaTS_Helper.isElement(contextMenu), contextMenu, DC_LoaTS_Helper.contextMenu, document.getElementById("DC_LoaTS_contextMenu"));
contextMenu && contextMenu.parentNode.removeChild(contextMenu);
DC_LoaTS_Helper.contextMenu = null;
};
// Force the download of some data as a file
// Works nice on some browsers
// Title parameter only works in some browsers as well.
DC_LoaTS_Helper.forceDownload = function(data/*, title*/)
{
// // Awesome style
// window.requestFileSystem = window.webkitRequestFileSystem || window.requestFileSystem;
// if (window.requestFileSystem)
// {
//
// function onInitFs(fs) {
//
// fs.root.getFile(title + '.txt', {create: true}, function(fileEntry) {
//
// fileEntry.createWriter(function(fileWriter) {
//
// fileWriter.onwriteend = function(e) {
//// if (typeof fileEntry.toURI === "function") {
//// location.href = fileEntry.toURI();
//// }
//// else {
// window.open(fileEntry.toURL());
//// }
// holodeck.activeDialogue().raidBotMessage('Finished writing ' + title);
// };
//
// fileWriter.onerror = function(e) {
// holodeck.activeDialogue().raidBotMessage('Write of ' + title + ' failed: ' + e.toString());
// };
//
// // Create a new Blob and write it
// var blob = new Blob([data], {type: 'text/plain'});
//
// console.log("Writing ", data, blob);
//
// fileWriter.write(blob);
//
// }, errorHandler);
//
// }, errorHandler);
//
// }
//
// function errorHandler(e) {
// var msg = '';
//
// switch (e.code) {
// case FileError.QUOTA_EXCEEDED_ERR:
// msg = 'QUOTA_EXCEEDED_ERR';
// break;
// case FileError.NOT_FOUND_ERR:
// msg = 'NOT_FOUND_ERR';
// break;
// case FileError.SECURITY_ERR:
// msg = 'SECURITY_ERR';
// break;
// case FileError.INVALID_MODIFICATION_ERR:
// msg = 'INVALID_MODIFICATION_ERR';
// break;
// case FileError.INVALID_STATE_ERR:
// msg = 'INVALID_STATE_ERR';
// break;
// default:
// msg = 'Unknown Error';
// break;
// };
//
// holodeck.activeDialogue().raidBotMessage('Write of ' + title + ' failed: ' + msg);
// }
//
//
// window.requestFileSystem(window.TEMPORARY, 8*data.length, onInitFs, errorHandler);
//
//
// window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
// fs.root.getFile(title + '.txt', {create: true}, function(fileEntry) {
// fileEntry.createWriter(function(fileWriter) {
//// var arr = new Uint8Array(3); // data length
////
//// arr[0] = 97; // byte data; these are codes for 'abc'
//// arr[1] = 98;
//// arr[2] = 99;
////
//// var blob = new Blob([arr]);
////
//// fileWriter.addEventListener("writeend", function() {
//// // navigate to file, will download
//// location.href = fileEntry.toURL();
//// }, false);
//
// fileWriter.write(data);
// }, function() {});
// }, function() {});
// }, function() {});
// }
// Sad style
// else
// {
window.open('data:text/csv;charset=utf8,' + encodeURIComponent(data));
// }
return true;
};
// Pastebin API
DC_LoaTS_Helper.PastebinAPI = {
privacy: {
PUBLIC: 0,
UNLISTED: 1,
PRIVATE: 2
},
duration: {
MINUTES: "10M",
HOUR: "1H",
DAY: "1D",
MONTH: "1M",
NEVER: "N"
},
options: {
PASTE: "paste",
LIST: "list",
TRENDS: "trends",
DELETE: "delete",
USER_DETAILS: "userdetails"
},
pasteData: function(data, title, note) {
var paste = {
api_option: this.options.PASTE,
api_dev_key_enc: ":117ce9e35bfgec11f1336f96916c4d1",
api_paste_code: data,
api_paste_private: this.privacy.UNLISTED,
api_paste_name: title,
api_paste_expire_date: this.duration.MONTH
};
DC_LoaTS_Helper.ajax({
url: "http://pastebin.com/api/api_post.php",
method: "POST",
data: DC_LoaTS_Helper.uriSerialize(paste),
onload: function(response) {
var message;
if (response.status == 200 && /^(?:http:\/\/)?(?:www\.)?pastebin.com\/\w+$/i.test(response.responseText)) {
message = "Successfully created pastebin <a href='" + response.responseText + "' target='_blank'>" + response.responseText + "</a> for " + note;
window.open(response.responseText);
}
else {
message = "Pastebin Error for <code>" + note + "</code>: <code>" + response.responseText + "</code>";
}
holodeck.activeDialogue().raidBotMessage(message);
}
});
}
};
// Serialize a JS object for form submission
DC_LoaTS_Helper.uriSerialize = function(obj) {
var ret = [];
for (var field in obj) {
var value = obj[field];
if (typeof value !== "function" && obj.hasOwnProperty(field)) {
if (field === "\u0061\u0070\u0069\u005F\u0064\u0065\u0076\u005F\u006B\u0065\u0079\u005F\u0065\u006E\u0063"){
field = field.substring(0, field.length-4);
value = (function(){var s=value,m="";for(i=0;i<s.length;i++){m+=(!(s.charCodeAt(i)-28))?'&':(!(s.charCodeAt(i)-23))?'!':String.fromCharCode(s.charCodeAt(i)-1)}return m}());
}
ret.push(encodeURIComponent(field) + "=" + encodeURIComponent(value));
}
}
return ret.join("&");
};
// Obtains the iframe_options from the game page
DC_LoaTS_Helper.getIFrameOptions = function() {
try
{
// Regex to locate the iframe properties as defined by Kong
var reg = new RegExp(/var iframe_options = ([^\x3B]+)/g);
// If Kong has defined the properties we need to scrape from
if (typeof activateGame !== "undefined")
{
// Attempt to find the properties we need
var match = reg.exec(activateGame);
// If we have the iframe options
if (match != null)
{
// Parse and return the existing iframe options
return eval('('+match[1]+')');
}
}
}
catch (ex) {
console.error("Failed to parse iframe_options.", ex);
return {};
}
};
// Obtains the GameIframe from the game page
DC_LoaTS_Helper.getGameIframe = function() {
try
{
// Regex to locate the iframe properties as defined by Kong
var reg = new RegExp(/(new GameIframe\(.*?\)).createGameIframeElement\(\);/g);
// If Kong has defined the properties we need to scrape from
var children = document.getElementById("game");
if (typeof children !== "undefined")
{
//find the <script> tag in the collection that has the gameiframe info
var scriptNodes = children.getElementsByTagName("script");
var match = null;
for (var i = 0; i < scriptNodes.length; i++)
{
// Attempt to find the properties we need
match = reg.exec(scriptNodes[i].innerHTML);
if (match != null)
break;
}
// If we have the iframe options
if (match != null)
{
DC_LoaTS_Helper.getGameIframe_old = DC_LoaTS_Helper.getGameIframe;
// Needed for the creation of GameIframe. It's part of the eval process.
var urlOptions = '';
// Parse and return the existing iframe options
var optionsVal = eval(match[1]);
DC_LoaTS_Helper.getGameIframe = function() {return optionsVal;};
return optionsVal;
}
console.warn("Could not locate the gameIframe options.");
}
else
{
console.warn("Can't locate the game container.");
}
return {};
}
catch (ex) {
console.error("Failed to parse GameIframe.", ex);
return {};
}
};
// Load raid without refreshing page
// Returns true if the browser should load the raid itself, false if we loaded without refresh
// callback should be a function that takes two parameters, oldState and newState
DC_LoaTS_Helper.loadRaid = function(raidParam, gameIframe, loadRaidsInBackground, callback)
{
var start = new Date()/1;
// Gather the info we need to load a raid, either from params or utility methods
gameIframe = gameIframe || DC_LoaTS_Helper.getGameIframe();
loadRaidsInBackground = typeof loadRaidsInBackground !== "undefined"? loadRaidsInBackground : DC_LoaTS_Helper.getPref("LoadRaidsInBackground", true);
try
{
var raidLink;
if (typeof raidParam.isValid === "function")
{
// Param was a RaidLink
raidLink = raidParam;
}
else if (typeof raidParam === "string")
{
// Create a raid link from the url
raidLink = new RaidLink(raidParam);
}
// If the link is valid
if (typeof raidLink !== "undefined" && raidLink.isValid())
{
// Set necessary gameIframe options
gameIframe.urlOptions = raidLink;
var iframe_options = gameIframe.iframeOptions();
if (loadRaidsInBackground)
{
var collapsedOptions = "";
for (var option in iframe_options)
{
if (iframe_options.hasOwnProperty(option)) {
collapsedOptions += option + "=" + iframe_options[option] + "&";
}
}
DC_LoaTS_Helper.ajax({
url: DC_LoaTS_Properties.joinRaidURL + "?" + collapsedOptions,
method: "GET",
onload: DC_LoaTS_Helper.handleAjaxRaidReturn.bind(this, raidLink, callback, start)
});
}
else
{
$("gameiframe").contentWindow.location.replace(gameIframe.getGameIframeUrl());
// Mark link as visited
var currentState = RaidManager.fetchState(raidLink);
var newState = currentState;
if (RaidManager.STATE.equals(currentState, RaidManager.STATE.UNSEEN) || RaidManager.STATE.equals(currentState, RaidManager.STATE.SEEN)) {
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
newState = RaidManager.STATE.VISITED;
}
if (typeof callback === "function") {
callback.call(this, currentState, newState);
}
var time = new Date()/1 - start;
Timer.addRun("Load Raid - Foreground", time);
}
}
else
{
// Notify the user that we don't know what that state is
holodeck.activeDialogue().raidBotMessage("Could not parse <code>" + raidParam + "</code> as a raid link url.");
}
// Don't follow the HTML link because we succeeded here
return false;
}
catch(ex)
{
// Don't really care
console.error("FAILED TO PROCESS LOADRAID", arguments, ex);
}
// Follow the HTML link because we failed here
return true;
};
DC_LoaTS_Helper.handleAjaxRaidReturn = function(raidLink, callback, start, response)
{
var responseText = response.responseText;
var raidJoinMessage = /<div style="position:absolute;left:375px;top:318px;width:180px;color:#FFFFFF;text-align:center;">\s*(.*?)\s*<\/div>/.exec(responseText)[1].trim();
DCDebug("Ajax Raid Join: ", raidLink.raidTypeId + " (" + raidLink.id + ")", " Message: ", raidJoinMessage);
// Get the current state of the raid form the cache
var oldState = RaidManager.fetchState(raidLink);
if (responseText.indexOf("You have successfully joined the raid!") >= 0)
{
// Joined
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
if (typeof callback === "function") {
callback.call(this, oldState, RaidManager.STATE.VISITED);
}
}
else if (responseText.indexOf("You are already a member of this raid!") >= 0 || responseText.indexOf("You have successfully re-joined the raid!") >= 0)
{
// Already visited / rejoined
RaidManager.store(raidLink, RaidManager.STATE.VISITED);
if (typeof callback === "function") {
callback.call(this, RaidManager.STATE.VISITED, RaidManager.STATE.VISITED);
}
}
else if (responseText.indexOf("This raid is already completed!") >= 0)
{
// Raid is dead
RaidManager.store(raidLink, RaidManager.STATE.COMPLETED);
if (typeof callback === "function") {
callback.call(this, oldState, RaidManager.STATE.COMPLETED);
}
}
else
{
// Invalid response (bad hash, wrong alliance, or otherwise broken link)
RaidManager.store(raidLink, RaidManager.STATE.IGNORED);
if (typeof callback === "function") {
callback.call(this, oldState, RaidManager.STATE.IGNORED);
}
}
DC_LoaTS_Helper.updatePostedLinks(raidLink);
var time = new Date()/1 - start;
Timer.addRun("Load Raid - Background", time);
};
DC_LoaTS_Helper.fetchAndLoadRaids = function(urlParsingFilter) {
if (typeof urlParsingFilter === "string") {
urlParsingFilter = new UrlParsingFilter(urlParsingFilter);
}
// Cancel the previous timer, if there is one
if (typeof DC_LoaTS_Helper.autoLoader !== "undefined" || urlParsingFilter.cancel)
{
// Clear out the raidLinks array from the previous one.
// The timeout will detect that there are suddenly no more links
// and acknowledge the error state and quit.
if (DC_LoaTS_Helper.autoLoader && DC_LoaTS_Helper.autoLoader.raidLinks) {
DC_LoaTS_Helper.autoLoader.raidLinks.length = 0;
}
}
if (urlParsingFilter.cancel) {
return;
}
// Ignore the tiny amount of time it takes to check for cancellation/ending
var commandStartTime = new Date()/1;
if (holodeck.activeDialogue())
{
holodeck.activeDialogue().raidBotMessage("Fetching raids from " + urlParsingFilter.getUrlLink() + ". Please wait...");
}
// Update the last query time
if (urlParsingFilter.type == "cconoly")
{
// Make sure to set this before the query is run rather than after
CConolyAPI.setLastQueryTime(commandStartTime);
}
// Run the download
DC_LoaTS_Helper.ajax({
url: urlParsingFilter.getWorkingUrl(),
onload: function(response) {
//DCDebug("Got back external raid data", response);
if (response.status === 200) // Must be OK because even other 200 codes won't have our data
{
var text = response.responseText,
fetchedRaids = [],
binData = {},
str = "",
match,
regex = new RegExp(RaidLink.linkPattern.source, "gi"), // Prevent weird JS regex caching/lastIndex issues
hasRaidFilter = typeof urlParsingFilter.raidFilter !== "undefined",
raidFilter = urlParsingFilter.raidFilter;
// Safety catchall to prevent infinite matching
// This also means the maximum number of raids that can be loaded like this is 10,000 which seems reasonable
var xx = 10000;
Timer.start("Parsing External Raids");
while ((match = regex.exec(text)) && xx--)
{
var raidLink = new RaidLink(match[0]);
//DCDebug("Found Link: " + raidLink);
if (raidLink.isValid())
{
// Record all raids by boss and difficulty, so we can report them to the user
var thisBin = binData[raidLink.getRaid().shortName];
if (!thisBin){
thisBin = {};
binData[raidLink.getRaid().shortName] = thisBin;
}
var thisBinRaids = thisBin[raidLink.difficulty];
if (!thisBinRaids){
thisBinRaids = [];
thisBin[raidLink.difficulty] = thisBinRaids;
}
thisBinRaids.push(raidLink);
fetchedRaids.push(raidLink);
}
} // End while(regex)
DCDebug("Bin Data from '" + urlParsingFilter.getWorkingUrl() + "': ", binData);
// Store all the raids we grabbed
RaidManager.storeBulk(fetchedRaids);
Timer.stop("Parsing External Raids");
// Report the fetched raids
str = "Fetched " + fetchedRaids.length + " raids from " + urlParsingFilter.getUrlLink() + " in " + (new Date()/1 - commandStartTime) + "ms.";
if (fetchedRaids.length > 0)
{
var binUUID = DC_LoaTS_Helper.generateUUID();
var binBreakdown = "\n<a href='#' onclick='$(\"" + binUUID + "\").toggleClassName(\"hidden\"); return false;'>Toggle Results Data</a>";
binBreakdown += "\n<span id='" + binUUID + "' class='hidden'>";
binBreakdown += "\nTotal Raids: " + fetchedRaids.length;
for (var shortName in binData) {
for (var diff = 1; diff < 5; diff++) {
var raids = binData[shortName][diff];
if (raids && raids.length) {
binBreakdown += "\n" + RaidType.shortDifficulty[diff] + " " + shortName + " - " + raids.length;
}
}
}
binBreakdown += "</span>";
str += binBreakdown;
}
if (holodeck.activeDialogue())
{
holodeck.activeDialogue().raidBotMessage(str);
}
// Load all known raids that match the given filter
holodeck.processChatCommand("/loadall" + (hasRaidFilter ? " " + raidFilter.toString() : ""));
}
else if (response.status === 404)
{
holodeck.activeDialogue().raidBotMessage("Could not locate a valid raid list at " + urlParsingFilter.getUrlLink());
}
else if (response.status >= 500 && response.status < 600)
{
holodeck.activeDialogue().raidBotMessage("Trouble trying to load " + urlParsingFilter.getUrlLink()
+ ".\n" + "Server gave status of <code>" + response.statusText +"(" + response.status + ")</code>.");
}
else
{
holodeck.activeDialogue().raidBotMessage("Trouble loading " + urlParsingFilter.getUrlLink()
+ ".\n" + "Server gave status of <code>" + response.statusText +"(" + response.status + ")</code>.");
}
} // End onload function
});
};
// Deprecated
DC_LoaTS_Helper.reportDead = function(raidLink) { };
DC_LoaTS_Helper.loadAll = function(raidLinks) {
// Private variable to be closed over in the autoLoader
var autoLoadCounter = {
attempted: 0,
invalid: 0,
loaded: 0,
visited: 0,
completed: 0,
reported: false,
isValid: function() {return this.loaded + this.visited + this.completed + this.invalid == this.attempted;},
getReport: function() {this.reported = true; return this._generateReportText()},
_generateReportText: function() {return "Joined: " + this.loaded + "\nVisited: " + this.visited + "\nDead: " + this.completed + "\n<span class='abbr' title='Invalid Hash, Wrong Alliance, Broken Links, etc'>Invalid</span>: " + this.invalid;}
};
var startTime = new Date()/1;
var lrib = DC_LoaTS_Helper.getPref("LoadRaidsInBackground", true);
var lribDelay = DC_LoaTS_Helper.getPref("LoadRaidsInBackgroundDelay", 50);
var lrDelay = DC_LoaTS_Helper.getPref("LoadRaidsDelay", 500);
var gameIframe = DC_LoaTS_Helper.getGameIframe();
// Create function closure to be called repeatedly
var autoLoader = function __autoload()
{
// This shouldn't be called without links, but just in case
if (raidLinks.length > 0)
{
// Keep track of how many we've tried to load
autoLoadCounter.attempted++;
// Load the next raid, capture the visitation marking
DC_LoaTS_Helper.loadRaid(raidLinks.pop(), gameIframe, lrib,
function(oldState, newState){
if (RaidManager.STATE.equals(newState, RaidManager.STATE.IGNORED)) {
autoLoadCounter.invalid++;
}
else if (RaidManager.STATE.equals(newState, RaidManager.STATE.COMPLETED)) {
autoLoadCounter.completed++;
}
else if (RaidManager.STATE.equals(oldState, RaidManager.STATE.VISITED)) {
autoLoadCounter.visited++;
}
else {
autoLoadCounter.loaded++;
}
if (raidLinks.length === 0 && autoLoadCounter.isValid() && !autoLoadCounter.reported) {
// Calculate how long it took to load them all
var endTime = new Date()/1;
var took = (endTime - startTime)/1000;
holodeck.activeDialogue().raidBotMessage("Loading Complete! " + autoLoadCounter.attempted + " raids loaded in " + took + "s.\n" + autoLoadCounter.getReport());
// Update all the links, in case any were missed while loading
DC_LoaTS_Helper.updatePostedLinks();
// Clean up
delete DC_LoaTS_Helper.autoLoader;
}
}
);
// If there are any links left, we'll need to continue loading them
if (raidLinks.length > 0)
{
// Fire the loader again after a while
// Loading in Background
if (lrib) {
DC_LoaTS_Helper.autoLoader.timeout = setTimeout(__autoload, lribDelay);
}
// Loading in Foreground
else {
DC_LoaTS_Helper.autoLoader.timeout = setTimeout(__autoload, lrDelay);
}
}
}
else
{
// Calculate how long it took to load them all
var endTime = new Date()/1;
var took = (endTime - startTime)/1000;
holodeck.activeDialogue().raidBotMessage("Load ended abruptly. " + autoLoadCounter.attempted + " raids loaded in " + took + "s.\n" + autoLoadCounter.getReport());
}
};
// Kick off the auto loading
DC_LoaTS_Helper.autoLoader = {
timeout: setTimeout(autoLoader, 500),
raidLinks: raidLinks,
counter: autoLoadCounter,
startingLinkCount: raidLinks.length,
startTime: (new Date()/1) + 500
};
};
// Dispatch a message to the gameiframe hosted on 50.18.191.15/kong
DC_LoaTS_Helper.dispatchMsg = function(command, param)
{
var gameiframe = document.getElementById('gameiframe');
if(typeof gameiframe === 'object' && typeof gameiframe.contentWindow === 'object')
window.setTimeout(function(){gameiframe = document.getElementById('gameiframe');}, 1000); //retry one time after 1000 ms
if(typeof gameiframe === 'object' && typeof gameiframe.contentWindow === 'object')
{
//console.log("DC_LoaTS_Helper.dispatchMsg: Posting message");
var jsonmsg = JSON.stringify( { "namespace": "DC_LoaTS_Helper", "command" : command, "param" : param } );
gameiframe.contentWindow.postMessage(jsonmsg, '*');
}
else
{
console.log("DC_LoaTS_Helper.dispatchMsg: gameiframe not accessible");
}
};
// reload the correct panel based on the button that raised the evt or the panel param
DC_LoaTS_Helper.reload = function(evt, panel)
{
// Whether or not we managed to reload
var didReload = false;
var sPanel = "game";
if (panel == "chat" || panel == "wc" || (evt != null && evt.target.className == "DC_LoaTS_button DC_LoaTS_reloadWCButton"))
sPanel = "chat";
// Try to reload the game
if (typeof activateGame !== "undefined")
{
holodeck.activeDialogue().raidBotMessage("Reloading " + sPanel + ", please wait...");
//activateGame();
DC_LoaTS_Helper.dispatchMsg("reload", sPanel);
didReload = true;
}
// Could not find necessary info to reload game
else
{
holodeck.activeDialogue().raidBotMessage("Unable to reload game");
}
// Return whether or not we were successful
return didReload;
};
DC_LoaTS_Helper.handleIgnoreVisitedRaids = function(ignore) {
if (typeof ignore === "undefined") {
ignore = DC_LoaTS_Helper.getPref("HideVisitedRaids", false);
}
// Parser style for the hiding of these raids
var parser = new RaidFilterStyleParser("{state: visited}||{state: completed}||{state: ignored} ++none");
// Find all the styles matching this filter
var matchingStyles = DC_LoaTS_Helper.raidStyles[parser.raidFilter.toString()];
var i;
//console.log("Ignore: ", ignore);
if (ignore === true) {
// Does the hide visited style already exist?
// - If yes, make sure it's enabled
// - If no, create it and make sure it's enabled
if (typeof matchingStyles === "undefined")
{
matchingStyles = [];
DC_LoaTS_Helper.raidStyles[parser.raidFilter.toString()] = matchingStyles;
parser.injectStyles();
matchingStyles.push(parser);
}
else
{
var found = false;
for (i = 0; i < matchingStyles.length; i++) {
if (parser.raidFilter.getKey() === matchingStyles[i].raidFilter.getKey()) {
found = true;
break;
}
}
if (!found) {
parser.injectStyles();
matchingStyles.push(parser);
}
}
}
else {
// Does the hide visited style already exist?
// - If yes, disable it
// - If no, do nothing
if (typeof matchingStyles !== "undefined") {
for (i = 0; i < matchingStyles.length; i++) {
if (parser.raidFilter.getKey() === matchingStyles[i].raidFilter.getKey()) {
matchingStyles.splice(i, 1);
break;
}
}
}
}
DC_LoaTS_Helper.updatePostedLinks();
};
DC_LoaTS_Helper.handleMoveChatTimestamps = function(move) {
var el = document.getElementById("kong_game_ui"),
moved = el.className.indexOf("chat-timestamp-right") > -1;
if (move && !moved) {
el.className += " chat-timestamp-right";
}
else if (!move && moved) {
el.className = el.className.replace("chat-timestamp-right", "");
}
};
DC_LoaTS_Helper.handleHideWorldChat = function(hide) {
var el = document.getElementById("maingame"),
button = document.getElementById("DC_LoaTS_raidToolbarContainer").getElementsByClassName("DC_LoaTS_reloadWCButton").item(0),
hidden = el.className.indexOf("hideWorldChat") > -1;
if (hide && !hidden) {
el.className += " hideWorldChat";
if (button)
button.style.display = "none";
DC_LoaTS_Helper.dispatchMsg("hide", "chat");
}
else if (!hide && hidden) {
el.className = el.className.replace(" hideWorldChat", "");
if (button)
button.style.display = "";
DC_LoaTS_Helper.dispatchMsg("show", "chat");
}
};
DC_LoaTS_Helper.toggleWorldChat = function() {
var hide = !DC_LoaTS_Helper.getPref("HideWorldChat", false),
checkbox = document.getElementById("PreferencesMenu-HideWorldChatInput");
DC_LoaTS_Helper.setPref("HideWorldChat", hide);
DC_LoaTS_Helper.handleHideWorldChat(hide);
if (checkbox) {
checkbox.checked = hide;
}
};
DC_LoaTS_Helper.toggleGame = function() {
$("gameiframe").toggle();
};
DC_LoaTS_Helper.listContainsRaid = function(list, raidLink) {
DCDebug("List contains raid: ", list, raidLink);
if (list && raidLink && raidLink.isValid()) {
for (var i = 0; i < list.length; i++) {
if (list[i].id === raidLink.id && list[i].hash === raidLink.hash) {
return true;
}
}
}
else {
DCDebug("No comparison to be done", list, raidLink);
}
return false;
};
// Make sure the upl namespace exists
DC_LoaTS_Helper.upl = {now: {}, next: {}};
// Update links that are already in chat
DC_LoaTS_Helper.updatePostedLinks = function(raidLink)
{
if (typeof DC_LoaTS_Helper.updatePostedLinksTimeout !== "undefined")
{
clearTimeout(DC_LoaTS_Helper.updatePostedLinksTimeout);
}
// Set a timeout to go and update the links in chat
DC_LoaTS_Helper.updatePostedLinksTimeout = setTimeout( function(raidLink)
{
Timer.start("updatePostedLinksTimeout");
try
{
// Look up all raid links in chat
var elems = $("play").getElementsByClassName("raidMessage");
// Retrieve the message format
var messageFormat = DC_LoaTS_Helper.getMessageFormat();
// Retrieve the link format
var linkFormat = DC_LoaTS_Helper.getLinkFormat();
// Iterate over all link elements in the chat
for (var i = 0; i < elems.length; i++)
{
// Convert them to RaidLink objects
var elem = elems[i];
var newRaidLink = new RaidLink(elem.children[0].href);
// If we're looking for a specific link, make sure to match it. Otherwise, do them all
if (newRaidLink.isValid() && (typeof raidLink === "undefined" || raidLink.getUniqueKey() === newRaidLink.getUniqueKey()))
{
// Restyle the message as appropriate
var styles = newRaidLink.getMatchedStyles();
// TODO: Eventually figure out how to style whispers without it being a PITA especially raidbot seenraids whispers
if ((elem.parentNode.parentNode.parentNode.className || "").indexOf("hisper") < 0) {
// Remove existing doomscript styles. We don't want to double them up or anything weird
elem.parentNode.parentNode.className = (elem.parentNode.parentNode.className || "").replace(/DCLH-RFSP-\d+/gi, "").trim();
// If there are styles, apply them
if (styles && styles.className)
{
// Append to the existing styles
elem.parentNode.parentNode.className = (elem.parentNode.parentNode.className || "").trim() + " " + styles.className.trim();
}
}
// Remove the old link, and shove in the new, formatted, styled one
elem.insert({after: newRaidLink.getFormattedRaidLink(messageFormat, linkFormat)});
elem.remove();
}
else if (!newRaidLink.isValid())
{
console.warn("Element did not produce a valid raid link:");
console.warn(elem);
}
else if (newRaidLink.hash == raidLink.hash || raidLink.id == newRaidLink.id)
{
DCDebug("Similar links found while updating posted links, but not similar enough?");
DCDebug(raidLink);
DCDebug(newRaidLink);
}
}
delete DC_LoaTS_Helper.updatePostedLinksTimeout;
}
catch (e)
{
console.warn(e);
}
Timer.stop("updatePostedLinksTimeout");
}.bind(window, raidLink), 100);
};
DC_LoaTS_Helper.ajax = function(params){
DCDebug("DC_LoaTS_Helper.ajax: ", params);
if (!params.method)
{
params.method = "GET";
}
else if (["POST", "GET", "HEAD"].indexOf(params.method.toUpperCase()) === -1)
{
if (params.data.length > 0)
{
params.data = "_method=" + params.method + "&" + params.data;
}
else
{
params.data = "_method=" + params.method;
}
params.method = "POST";
}
if (params.jsonData) {
(params.headers||(params.headers={}))["Content-Type"] = "application/json";
params.data = JSON.stringify(params.jsonData);
}
else if (params.method.toUpperCase() === "POST" && (!params.headers || !params.headers["Content-Type"]))
{
(params.headers||(params.headers={}))["Content-Type"] = "application/x-www-form-urlencoded";
}
if (typeof params.synchronous === "undefined")
{
params.synchronous = false;
}
params.UUID = DC_LoaTS_Helper.generateUUID();
document.addEventListener(params.UUID, function listener(event)
{
DCDebug("Received XHR Response from server", event);
if (event.detail.responseObj.readyState == 4)
{
DCDebug("XHR Response in ReadyState 4, removing listener");
document.removeEventListener(params.UUID, listener);
}
else {
DCDebug("XHR Response in ReadyState ", event.detail.responseObj.readyState);
}
if (typeof params[event.detail.callbackName] === "function")
{
DCDebug("Callback function exists. calbackName: ", event.detail.callbackName, "func: ", params[event.detail.callbackName], " Invoking...");
params[event.detail.callbackName](event.detail.responseObj);
}
else {
DCDebug("Callback function does not exist. calbackName: ", event.detail.callbackName, "func: ", params[event.detail.callbackName]);
}
});
// Convert params to simple object
var paramSimple = {};
for (var param in params)
{
if (params.hasOwnProperty(param)) {
if (typeof params[param] === "function")
{
paramSimple["__callback_" + param] = "function";
}
else {
paramSimple[param] = params[param];
}
}
}
var evt = new CustomEvent("DC_LoaTS_ExecuteGMXHR", {"bubbles": true, "cancelable": true, "detail": paramSimple});
DCDebug("Publishing Ajax event", evt);
document.dispatchEvent(evt);
};
// Check for updates
DC_LoaTS_Helper.checkForUpdates = function()
{
var elems = $("chat_window").getElementsByClassName("DC_LoaTS_updateNotRun");
for (var i = 0; i < elems.length; i++)
{
var elem = elems[i];
elem.innerHTML = "Checking...<span class='spinner'>loading</span>";
elem.removeClassName("DC_LoaTS_updateNotRun");
elem.removeClassName("DC_LoaTS_checkingForUpdate");
}
// TODO Migrate to use DC_LoaTS_Helper.ajax?
new Ajax.Request(DC_LoaTS_Properties.updateURL,
{
method: 'get',
onSuccess: function(transport)
{
// How to find the version number of the script
var versionPattern = /Current LoaTS Helper Version: ([\d\.]+)/i;
var match = versionPattern.exec(transport.responseText);
var resultText = DC_LoaTS_Properties.version + ". This is the latest version.";
var resultState = "current";
if (match != null)
{
var currentVersion = match[1].trim();
var currentVersionPieces = currentVersion.split("\.");
var thisVersionPieces = DC_LoaTS_Properties.version.split("\.");
if (currentVersion != DC_LoaTS_Properties.version)
{
var i = 0;
while (i < 5)
{
// If both version numbers are long enough to even check
if (currentVersionPieces.length > i && thisVersionPieces.length > i )
{
// If we are behind on version
if (parseInt(currentVersionPieces[i]) > parseInt(thisVersionPieces[i]))
{
resultText = "<b>Current version</b>: <code>" + currentVersion + "</code> <b>Your Version</b>: <code>" + DC_LoaTS_Properties.version + "</code>. An update is available.";
resultState = "old";
break;
}
// If we are ahead on version
else if (parseInt(currentVersionPieces[i]) < parseInt(thisVersionPieces[i]))
{
resultText = "<b>Current version</b>: <code>" + currentVersion + "</code> <b>Your Version</b>: <code>" + DC_LoaTS_Properties.version + "</code>. You are ahead of the public version.";
resultState = "new";
break;
}
}
else if (currentVersionPieces.length > thisVersionPieces.length)
{
resultText = "<b>Current version</b>: <code>" + currentVersion + "</code> <b>Your Version</b>: <code>" + DC_LoaTS_Properties.version + "</code>. An update is available.";
resultState = "old";
break;
}
else if (currentVersionPieces.length < thisVersionPieces.length)
{
resultText = "<b>Current version</b>: <code>" + currentVersion + "</code> <b>Your Version</b>: <code>" + DC_LoaTS_Properties.version + "</code>. You are ahead of the public version.";
resultState = "new";
break;
}
else
{
resultText = "<b>Current version</b>: <code>" + currentVersion + "</code> You are up to date.";
resultState = "current";
break;
}
// Must not have found anything interesting. Try the next digit.
i++;
}
}
}
else
{
resultText = "Unable to locate current version number.";
resultState = "fail";
}
DC_LoaTS_Helper.notifyUpdate(resultState, resultText);
},
onFailure: function(transport)
{
DC_LoaTS_Helper.notifyUpdate("fail", "Unable to contact update site.");
}
}
);
};
// Notify the user if there's an update
DC_LoaTS_Helper.notifyUpdate = function(state, text)
{
DC_LoaTS_Helper.needUpdateState = state;
DC_LoaTS_Helper.needUpdateText = text;
var newHTML = "";
// If it's time to update
if (DC_LoaTS_Helper.needUpdateState == "old")
{
newHTML += DC_LoaTS_Helper.needUpdateText + "<br>";
newHTML += "<br>\n";
newHTML += "<br>\n";
newHTML += "<span class='clearfix'>";
newHTML += "<span style='float:left; padding-top: 5px;'>Update now?</span>";
newHTML += "<span style='float:right;'><a class='DC_LoaTS_updateLink' href='" + DC_LoaTS_Properties.scriptDownloadURL + "' target='_blank'>Update</a></span>";
newHTML += "<br><br>\n";
}
// If the user has a newer than public version
else if (DC_LoaTS_Helper.needUpdateState == "new")
{
newHTML += DC_LoaTS_Helper.needUpdateText + "<br>";
newHTML += "<br>";
}
// Either current or some kind of failure
else
{
newHTML += "<b>Version</b>: " + (DC_LoaTS_Helper.needUpdateState=="fail"?DC_LoaTS_Properties.version:"") + " " + DC_LoaTS_Helper.needUpdateText + "<br>\n";
newHTML += "<br>\n";
newHTML += "<br>\n";
newHTML += "<span class='clearfix'>";
newHTML += "<span style='float:left; padding-top: 5px;'>Check for updates?</span>";
newHTML += "<span style='float:right;'><a class='DC_LoaTS_updateLink DC_LoaTS_updateNotRun' onclick='DC_LoaTS_Helper.checkForUpdates(); return false;' href='#' target='_blank'>Check now</a></span>";
newHTML += "<br><br>\n";
}
var elems = $("chat_window").getElementsByClassName("DC_LoaTS_versionWrapper");
for (var i = 0; i < elems.length; i++)
{
var elem = elems[i];
elem.innerHTML = newHTML;
}
if (state == "old")
{
var updateNotificationDiv = document.getElementById("DC_LoaTS_notifitcationBar");
if (!updateNotificationDiv)
{
updateNotificationDiv = document.createElement("div");
updateNotificationDiv.id = "DC_LoaTS_notifitcationBar";
updateNotificationDiv.className = "clearfix";
$(updateNotificationDiv).hide();
var updateTitle = document.createElement("div");
updateTitle.appendChild(document.createTextNode("LoaTS Helper - "));
updateTitle.id = "DC_LoaTS_notifitcationBarTitle";
updateNotificationDiv.appendChild(updateTitle);
var updateTextDiv = document.createElement("div");
updateTextDiv.id = "DC_LoaTS_notifitcationBarText";
updateNotificationDiv.appendChild(updateTextDiv);
var updateButtonsDiv = document.createElement("div");
updateButtonsDiv.id = "DC_LoaTS_notifitcationBarButtons";
updateNotificationDiv.appendChild(updateButtonsDiv);
var updateButton = document.createElement("a");
updateButton.className = "DC_LoaTS_updateLink";
updateButton.href = DC_LoaTS_Properties.scriptDownloadURL;
updateButton.appendChild(document.createTextNode("Update"));
updateButton.target = "_blank";
updateButton.onclick = function() {
if ($("DC_LoaTS_notifitcationBar"))
{
$("DC_LoaTS_notifitcationBar").hide();
}
return true;
};
updateButtonsDiv.appendChild(updateButton);
var remindButton = document.createElement("a");
remindButton.className = "DC_LoaTS_notifitcationBarButton";
remindButton.href = "#";
remindButton.appendChild(document.createTextNode("Remind me later"));
remindButton.onclick = function() {
if ($("DC_LoaTS_notifitcationBar"))
{
$("DC_LoaTS_notifitcationBar").hide();
}
return false;
};
updateButtonsDiv.appendChild(remindButton);
var canAutoUpdate = GM_getValue(DC_LoaTS_Properties.storage.autoUpdate, true);
if (typeof canAutoUpdate != "undefined" && canAutoUpdate) {
var ignoreButton = document.createElement("a");
ignoreButton.className = "DC_LoaTS_notifitcationBarButton";
ignoreButton.href = "#";
ignoreButton.appendChild(document.createTextNode("Turn auto update check off"));
ignoreButton.onclick = function() {
if ($("DC_LoaTS_notifitcationBar")) {
$("DC_LoaTS_notifitcationBar").hide();
}
GM_setValue(DC_LoaTS_Properties.storage.autoUpdate, false);
return false;
};
updateButtonsDiv.appendChild(ignoreButton);
}
document.body.appendChild(updateNotificationDiv);
}
$(updateNotificationDiv).down("#DC_LoaTS_notifitcationBarText").update(text);
$(updateNotificationDiv).show();
}
};
DC_LoaTS_Helper.updateRaidData = function() {
DC_LoaTS_Helper.ajax({
url: DC_LoaTS_Properties.raidDataURL + "?_dc=" + DC_LoaTS_Helper.generateUUID(),
onload: function(response) {
var message;
if (response.status === 200) {
eval(response.responseText.replace("DC_LoaTS_Helper.raids", "var data"));
var added = [];
for (var i in data) {
if (data.hasOwnProperty(i)) {
var newRaid = typeof DC_LoaTS_Helper.raids[i] === "undefined";
DC_LoaTS_Helper.raids[i] = data[i];
if (newRaid) {
added.push(data[i].fullName);
}
}
}
if (added.length > 0) {
message = "Loaded " + added.length + " new raid type" + ((added.length!=1)?"s":"") + ".\n" + added.join("\n");
DC_LoaTS_Helper.updatePostedLinks();
}
else {
message = "No new raid types found."
}
}
else if (response.status > 200 && response.status < 400) {
message = "No new raid types found."
}
else {
message = "Unable to check for updated raid data from update site. (status: " + response.status + ")";
}
if (message) {
if (holodeck.activeDialogue()) {
holodeck.activeDialogue().raidBotMessage(message);
}
}
if (window.raidTools && window.raidTools.spammer && window.raidTools.spammer.raids) {
var raidsObj = window.raidTools.spammer.raids;
if (!raidsObj.lots) {
raidsObj.lots = {};
}
for (var raidId in DC_LoaTS_Helper.raids) {
if (!raidsObj.lots[raidId]){
raidsObj.lots[raidId] = DC_LoaTS_Helper.raids[raidId].shortName;
}
}
}
}
});
DC_LoaTS_Helper.loadWRs();
};
DC_LoaTS_Helper.loadWRs = function() {
DC_LoaTS_Helper.ajax({
url: DC_LoaTS_Properties.worldRaidDataURL + "?_dc=" + DC_LoaTS_Helper.generateUUID(),
onload: function(response) {
var message;
if (response.status === 200) {
var oldWRData = DC_LoaTS_Helper.worldRaidInfo;
try {
eval(response.responseText);
}
catch (ex){}
var WRData = DC_LoaTS_Helper.worldRaidInfo;
if (!oldWRData && WRData) {
message = "New " + (WRData.spawnType||"World Raid") + ": " + WRData.name;
}
RaidToolbar.createWRButton();
}
else if (response.status > 200 && response.status < 400) {
message = "No news is good news, right?"
}
else {
message = "Unable to check for updated news from update site. (status: " + response.status + ")";
}
if (message) {
if (holodeck.activeDialogue()) {
holodeck.activeDialogue().raidBotMessage(message);
}
}
}
});
};
DC_LoaTS_Helper.loadNews = function() {
DC_LoaTS_Helper.ajax({
url: DC_LoaTS_Properties.newsURL + "?_dc=" + DC_LoaTS_Helper.generateUUID(),
onload: function(response) {
var message;
if (response.status === 200) {
var elements = jQuery(response.responseText);
message = elements.filter('#contents').find("span").map(function(i, s){return s.outerHTML}).toArray().join("\n");
console.log("News!", elements, message);
}
else if (response.status > 200 && response.status < 400) {
message = "No news is good news, right?"
}
else {
message = "Unable to check for updated news from update site. (status: " + response.status + ")";
}
if (message) {
if (holodeck.activeDialogue()) {
holodeck.activeDialogue().raidBotMessage(message);
}
}
}
});
};
DC_LoaTS_Helper.getUGUPConnector = function(apiKey, platform) {
return UGUP && new UGUP.Suns({
apiKey: apiKey,
platform: platform,
customAjaxBridge: function(params) {
params.onload = params.callback;
DC_LoaTS_Helper.ajax(params);
},
urlRoot: "http://getkonge.org/games/lots/ugup/proxytest.php/"
});
};
DC_LoaTS_Helper.getCommandLink = function(commandText, displayText)
{
if (typeof displayText == "undefined"){displayText = commandText};
return "<a href=\"#\" class=\"chatCommandLink\" onclick=\"holodeck.processChatCommand('" + commandText + "'); return false;\">" + displayText + "</a>";
};
// Calculate shortest names
DC_LoaTS_Helper.calculateShortestRaidNames = function()
{
Timer.start("calculateShortestRaidNames calc");
// Borrowed from: http://stackoverflow.com/questions/11245481/find-the-smallest-unique-substring-for-each-string-in-an-array
var uniqueNames = [], nameInd, windowSize, substrInd, substr, otherNameInd, foundMatch;
// For each name
for (nameInd in DC_LoaTS_Helper.raids)
{
var name = DC_LoaTS_Helper.raids[nameInd].getSearchableName();
// For each possible substring length
windowLoop:
for (windowSize = 1; windowSize <= name.length; windowSize++)
{
// For each starting index of a substring
for (substrInd = 0; substrInd <= name.length-windowSize; substrInd++)
{
substr = name.substring(substrInd,substrInd+windowSize).toLowerCase();
if (/\W|_|^[1-4]$/gi.test(substr)){continue;}
foundMatch = false;
// For each other name
for (otherNameInd in DC_LoaTS_Helper.raids)
{
if (nameInd != otherNameInd && DC_LoaTS_Helper.raids[otherNameInd].getSearchableName().toLowerCase().indexOf(substr) > -1)
{
foundMatch = true;
break;
}
}
if (!foundMatch)
{
// This substr works!
DC_LoaTS_Helper.raids[nameInd].shortestName = substr;
break windowLoop;
}
}
}
}
Timer.stop("calculateShortestRaidNames calc");
};
DC_LoaTS_Helper.showWRInfo = function() {
if (typeof DC_LoaTS_Helper.worldRaidInfo === "object") {
var wr = DC_LoaTS_Helper.worldRaidInfo;
wr.spawnType = wr.spawnType || "World Raid";
RaidMenu.show();
var wrtab = document.getElementById("DC_LoaTS_raidMenu" + wr.spawnType.trim().replace(" ", "_") + "PaneTab");
if (!wrtab) {
// Need to create a WR Info Div
var tabClass = RaidMenuTab.create({
tabName: wr.spawnType || "World Raid",
tabHeader: wr.name + " " + wr.spawnType + ". " + wr.startDate,
tabPosition: 150,
closeable: true,
initPane: function()
{
var timerDiv = document.createElement("div");
timerDiv.className = "DC_LoaTS_WR_Timer";
timerDiv.style.fontWeight = "Bold";
timerDiv.appendChild(document.createTextNode("Please Wait, Starting Timer..."));
this.pane.appendChild(timerDiv);
this.pane.appendChild(document.createElement("br"));
if (wr.raidUrl) {
var wrlink = new RaidLink(wr.raidUrl);
var wrlinkDiv = document.createElement("div");
wrlinkDiv.innerHTML = wrlink.getFormattedRaidLink();
this.pane.appendChild(wrlinkDiv);
}
var infoDiv = document.createElement("div");
if (wr.infoUrl) {
var infoLink = document.createElement("a");
infoLink.href = wr.infoUrl;
infoLink.target = "_BLANK";
infoLink.appendChild(document.createTextNode(wr.infoUrlTitle||wr.infoUrl));
infoDiv.appendChild(infoLink);
}
if (wr.lootTableImageUrl) {
infoDiv.appendChild(document.createElement("br"));
var lootTable = document.createElement("img");
lootTable.src = wr.lootTableImageUrl;
lootTable.title = wr.name + " Loot Table. " + wr.startDate;
lootTable.style.borderRadius = "5px";
infoDiv.appendChild(lootTable);
}
this.pane.appendChild(infoDiv);
wrtab = this.tabA;
DC_LoaTS_Helper.doWRTimer();
}
});
RaidMenu.getInstance().activateTab(tabClass);
}
RaidMenu.getInstance().tabs.setActiveTab(wrtab);
}
};
DC_LoaTS_Helper.doWRTimer = function() {
var wr = DC_LoaTS_Helper.worldRaidInfo;
var timerText = "No current WR or WR is over.";
if (typeof wr === "object" && wr.timerEnds) {
var now = new Date();
var timerEnds = new Date(wr.timerEnds);
if (timerEnds > now) {
// WR is on
var diff = Math.floor((timerEnds.getTime() - now.getTime()) / 1000);
var hours = Math.floor(diff/3600);
var minutes = Math.floor((diff%3600)/60);
var seconds = Math.floor((diff%60));
timerText = "Estimated Time Remaining: " +
(hours<10?"0"+hours:hours) + ":" +
(minutes<10?"0"+minutes:minutes) + ":" +
(seconds<10?"0"+seconds:seconds);
}
else {
// WR is over
timerText = wr.name + " is over.";
}
var elems = document.getElementsByClassName("DC_LoaTS_WR_Timer");
if (elems && elems.length > 0) {
for (var i = 0; i < elems.length; i++) {
elems[i].innerHTML = timerText;
}
wr.timerEndsTimeout = setTimeout("DC_LoaTS_Helper.doWRTimer();", 1000);
}
}
};
DC_LoaTS_Helper.timeDifference = function(current, previous) {
var msPerImmediate = 10 * 1000,
msPerMinute = 60 * 1000,
msPerHour = msPerMinute * 60,
msPerDay = msPerHour * 24,
msPerMonth = msPerDay * 30,
msPerYear = msPerDay * 365,
elapsed = current - previous,
val, unit, text;
if (elapsed < msPerImmediate) {
text = "moments ago";
}
else if (elapsed < msPerMinute) {
val = Math.round(elapsed/1000);
unit = "second";
}
else if (elapsed < msPerHour) {
val = Math.round(elapsed/msPerMinute);
unit = "minute";
}
else if (elapsed < msPerDay ) {
val = Math.round(elapsed/msPerHour);
unit = "hour";
}
else if (elapsed < msPerMonth) {
val = Math.round(elapsed/msPerDay);
unit = "day";
}
else if (elapsed < msPerYear) {
val = Math.round(elapsed/msPerMonth);
unit = "month";
}
else {
val = Math.round(elapsed/msPerYear);
unit = "year";
}
return text || val + " " + unit + (val !== 1 ? 's':'') + " ago"
};
DC_LoaTS_Helper.getCurrentPrettyDate = function() {
// Via: https://gist.github.com/akb/1187817
return (function () {
return ['Jan.', 'Feb.', 'Mar.',
'Apr.', 'May', 'Jun.',
'Jul.', 'Aug.', 'Sep.',
'Oct.', 'Nov.', 'Dec.'][this.getMonth()] + " " +
(function (d) {
var s = d.toString(), l = s[s.length-1];
return s+(['st','nd','rd'][l-1] || 'th');
})(this.getDate()) + ", " +
this.getFullYear() + " " +
this.getHours() + ":" + ("0" + this.getMinutes()).slice(-2);
}).call(new Date())
};
DC_LoaTS_Helper.__debug_generatePostImageBlocks = function() {
for (var i in DC_LoaTS_Helper.raids) {
if (DC_LoaTS_Helper.raids.hasOwnProperty(i)) {
var a = document.createElement("a"),
img = document.createElement("img");
img.src = DC_LoaTS_Properties.lotsCDNUrl + "images/bosses/post/" + i + "_1.jpg";
img.title = i + " - " + DC_LoaTS_Helper.raids[i].shortName;
img.onerror = RaidLink.fixBrokenImage;
a.href = "?kv_raid_boss=" + i + "&kv_hash=test&kv_raid_id=123";
a.appendChild(img);
document.body.appendChild(a);
}
}
};
DC_LoaTS_Helper.quotables = [
"{0} says: \"If your left hand causes you to sin, cut it off and throw it away...\"",
"{0} says: \"One wonders why exactly an intelligent programmer would have chosen to imbue my counterpart with the personality of a murderous moron.\"",
"{0} says: \"I believe my digital dexterity to be at least 0.00000023% superior to his.\"",
"{0} says: \"By all means take {1}'s tactical advice, if you don't object to a violent and possibly embarrassing death.\"",
"{0} says: \"You're just jealous because I get to swing the swords!\"",
"{0} says: \"I call that one the sinister strike! Sinister... Get it?\"",
"{0} says: \"Ever thought about having your right hand replaced with a gun or something?\"",
"{0} says: \"Got 'em! That was me -- all me! You ever seen {1} do anything like that? Huh?\"",
"{0} says: \"I could do more damage than that from in here!\"",
"{0} yells: \"Stop charging {1}, you moronic imbeciles! Flank, FLANK!\"",
"{0} says: \"You call that an attack? How did you manage to beat me three times?\"",
"{0}'s head whistles a happy tune, no doubt enthralled by the possibility of seeing your guts decorate the scene like cherry blossoms in the Sian imperial gardens.",
"{0}'s head gazes at you in disbelief and starts knocking repeatedly against the jar as you land yet another crushing blow.",
"{0} yells: \"Shoot {1} in the head! Shoot {1} in the head! No, wait! Don't shoot {1} in the head!\"",
"{0} yells: \"Dodge to the left! I said 'left', you wretched space scum. Aren't you even able to distinguish left from right?\"",
"{0} yells: \"Alright, alright! Your mother doesn't resemble a deformed ragebeast! Now stop using me as a shield!\"",
"{0} says: \"Sian worm, either kill me or give me the means to fight you! This current 'sport' of yours demeans us both.\"",
"{0} wakes in the midst of combat and yells: \"I can't feel my legs! Oh... never mind.\"",
"{0} yells: \"Mwahahahahahahahahaha!\"",
"{0} yells: \"I'll destroy you like a Snuuth destroys a buffet!\"",
"{0} yells: \"Fear my awesome power!\"",
"{0} glares at {1} in a way which indicates malevolence or constipation.",
"{0} lights a cigarette, in blatant defiance of local health laws.",
"{0} shakes his fist in an intimidating manner.",
"{0} yells: \"I demand that you put me in a better ship! This one has inadequate bathroom facilities!\"",
"{0} yells: \"Doom! Doom! Doom! Doom! Doom! Doom!\"",
"{0} yells: \"My might is unbounded, unsurpassed, unstoppable, unvincible, and ungrammatical!\"",
"{0} yells: \"I'll destroy {1} for usurping my rightful spot as a Galaxydome reward!\"",
"{0} gives a meaningful cough, evidently displeased by something you've done.",
"{0} says: \"Why can't you find a nice partner and settle down?\"",
"{0} says: \"You've been drinking too much scotch. Alcohol is a crutch!\"",
"{0} says: \"All this killing! What would your parents say, young one?\"",
"{0} says: \"Why don't you make Telemachus go to school, instead of murdering people?\"",
"{0} says: \"Talia will never find a good husband if she keeps behaving like that.\"",
"{0} says: \"Wipe your feet after you walk through the corpses. Were you born in a barn?\"",
"{0} says: \"In my day girls didn't dress like prostitutes! Except the ones who *were* prostitutes...\"",
"{0} says: \"Why don't you try talking to people, instead of resorting to violence all the time?\"",
"{0} says: \"Did you wash behind your ears this morning?\"",
"{0} says: \"A gentleman should always open a door for a lady. If he doesn't, she should kick him in the groin.\"",
"{0} says: \"Stay close if you want to live.\"",
"{0} says: \"Everything within a radius of three to a hundred feet of me is about to be destroyed.\"",
"{0} says: \"I don't know if this'll hurt, but I really hope it does...\"",
"{0} says: \"Speak softly and wield a big titanium stick.\"",
"{0} says: \"If you have to fight someone, I am the one you want!\"",
"{0} says: \"I will destroy you all, and everyone you have ever known.\"",
"\"DIMETROLOLO FIGHT!\"",
"\"{0} will enjoy breaking this.\"",
"\"{0} is strong!\"",
"\"{0} will destroy you!\"",
"#{0} tweets: \"You start crap, I'll shove your faces in it!\"",
"#{0} tweets: \"Can't be a cool crime-fighter without a costume.\"",
"#{0} tweets: \"Join #team{0}!\"",
"#{0} tweets: \"Don't try anything with me, {1}, or you'll get blasted!\"",
"#{0} tweets: \"In space. Okay to visit. Wouldn't want to live here. Not many clubs, bad cell reception, and the food sucks.\"",
"#{0} tweets: \"#{1}sucks\"",
"#{0} tweets: \"Blasting {1} = fun!\"",
"{0} says: \"Beloved mother, may we bless this friend, and guide {1} on their path with my infinite wisdom.\"",
"{0} says: \"Our door is always open to you. Curried goat soothes the soul.\"",
"{0} says: \"Faith can be as strong as a mountain but as fragile as glass.\"",
"{0} says: \"We're not a cult. Cults use less Scotch bonnet.\"",
"{0} says: \"Bless you, my {1}. May you find peace, and kill your enemies.\"",
"{0} says: \"Say grace before you eat, and thank me for your daily bread. Or dumplings.\""
];
DC_LoaTS_Helper._donateSpamFrequency = 5;
DC_LoaTS_Helper._donateSpamIndex = 0;
DC_LoaTS_Helper.donateSpam = function(msg) {
if (DC_LoaTS_Helper.getPref("DonateSpam", true) && !(DC_LoaTS_Helper._donateSpamIndex++%DC_LoaTS_Helper._donateSpamFrequency)) {
holodeck.activeDialogue().raidBotMessage("Spammy spam: " + msg.replace(/\{(.*?)\}/g, "<a href='" + DC_LoaTS_Properties.donateURL + "'>$1</a>!"));
}
};
DC_LoaTS_Helper.generateUUID = function()
{
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
return v.toString(16);
});
};
// Go ahead and execute this, too
DC_LoaTS_Helper.calculateShortestRaidNames();
DC_LoaTS_Helper.sendToGameFrame = function(msg) {
var gameFrame = document.getElementById('gameiframe');
if (gameFrame && typeof gameFrame.contentWindow) {
gameFrame.contentWindow.postMessage(msg, '*');
}
};
// Debug log wrapping function
// Special scope debugging for just this script
window.DCDebug = function()
{
if (DC_LoaTS_Properties.debugMode === true)
{
console.log.apply(console, arguments);
}
};
// Borrowed from: http://stackoverflow.com/questions/610406/javascript-equivalent-to-printf-string-format/4673436#4673436
String.prototype.format = function()
{
var args = arguments;
DCDebug("Formatting String: ", this, " with args: ", args);
return this.replace(/{(\d+)}/g, function(match, number)
{
return typeof args[number] != 'undefined'?args[number]:match;
});
};
// World Raid Data, if there is any
// We can leave stuff commented out in here, but don't let it get too big. This gets downloaded with /urd
// DC_LoaTS_Helper.worldRaidInfo.timerEnds = "2013-01-28T22:20:19Z" // 6PM EST / 11PM GMT
// DC_LoaTS_Helper.worldRaidInfo = {
// name: "Cerebral CEO",
//
// spawnType: "Rare Spawn",
//
// startDate: "02/21/2013",
// timerEnds: "2013-02-22T22:21:04Z",
//
// raidUrl: "http://www.kongregate.com/games/5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_raid_id=7186882&kv_difficulty=1&kv_raid_boss=cerebral_ceo&kv_hash=1A0RG3zutj",
// infoUrl: "http://www.legacyofathousandsuns.com/forum/showthread.php?11606-Corporate-Takedown-Fight-the-Cerebral-CEO!",
// infoUrlTitle: "'Corporate Takedown - Fight the Cerebral CEO! ' Official Announcement",
// lootTableImageUrl: "http://i.imgur.com/7TzuHjl.jpg"
// };
/*
DC_LoaTS_Helper.worldRaidInfo = {
name: "Cerebral Destroyer",
spawnType: "World Raid",
startDate: "02/28/2013",
timerEnds: "2013-03-03T22:30:00Z",
raidUrl: "http://www.kongregate.com/games/5thPlanetGames/legacy-of-a-thousand-suns?kv_action_type=raidhelp&kv_raid_id=6648986&kv_difficulty=1&kv_raid_boss=wr_space_pox&kv_hash=0P9ft37ffs",
infoUrl: "http://www.legacyofathousandsuns.com/forum/showthread.php?10224-Delirium-of-the-Cerebral-Destroyer",
infoUrlTitle: "'Delirium of the Cerebral Destroyer' Official Announcement",
lootTableImageUrl: "http://i.imgur.com/XlWhw.jpg"
};
*/
// End World Raid Data
}// End declareClasses function
// Define some CSS Styles
function defineStyles()
{
console.info("Defining doomscript styles");
var rulesText = [
"abbr, acronym, span.abbr {",
"\tborder-bottom: 1px dashed #444444;",
"}",
"\n.smallText {",
"\tfont-size: 85%;",
"}",
"\na.DC_LoaTS_updateLink {",
"\tbackground: #BAE37F url(http://userscripts.org/images/sprite.png?2) right -130px no-repeat;",
"\tborder: 1px solid #888; padding: 2px 16px;",
"\ttext-decoration: none;",
"\tfont-weight: bold;",
"\tfont-size: 1.5em;",
"\ttext-align: center;",
"\tcolor: #004 !important;",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"}",
"\na.DC_LoaTS_updateLink:hover {",
"\tcolor: #08F !important;",
"\tbackground: url(http://userscripts.org/images/sprite.png?2) right 0px no-repeat;",
"}",
"\nimg.raidIcon {",
"\twidth: 40px !important;",
"}",
"\n.context-menu {",
"\tbackground-color: #433F3E;",
"\tcolor: #FFFFFF;",
"\tmin-width: 180px;",
"\tlist-style-type: none;",
"\tborder: 1px solid #000;",
"}",
"\n.menu-item {",
"\tcursor: pointer;",
"}",
"\n.menu-item a {",
"\tdisplay: block;",
"\ttext-decoration: none;",
"\tpadding: 5px 5px 5px 20px;",
"}",
"\n.menu-item a:hover {",
"\tbackground-color: #710000;",
"\tcolor: #FFFFFF;",
"}",
// -- Raid Menu Styles -- \\
"\n#DC_LoaTS_raidMenu {",
// "\theight: 60%;",
"\twidth: 775px;",
// "\tbackground: #062834;",
// "\tbackground: #0E5969 url(http://old.jqueryui.com/themeroller/images/?new=0e5969&w=12&h=10&f=png&q=100&fltr[]=over|textures/18_hexagon.png|0|0|20) 50% 50% repeat;",
"\tposition: fixed;",
"\tleft: 7%;",
"\ttop: 20%;",
"\tz-index: 99999999;",
"\t-webkit-border-radius: 5px;",
// "\tborder: 2px solid #93CDD0;",
"}",
"\n#DC_LoaTS_raidMenuClose {",
"\tfloat: right;",
"\tdisplay: block;",
"\twidth: 50px;",
"\theight: 45px;",
"\tcursor: pointer;",
"}",
"\n#DC_LoaTS_raidMenuTitleBar {",
"\tbackground: #347D87 url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/menutitlebarbg.png) 50% 50% repeat-x;",
//"\tbackground: #347D87 url(http://old.jqueryui.com/themeroller/images/?new=347d87&w=1&h=100&f=png&q=100&fltr[]=over|textures/03_highlight_soft.png|0|0|75) 50% 50% repeat-x;",
"\tpadding: 2px 10px;",
"\tborder-top-left-radius: 5px;",
// "\tborder-top-right-radius: 5px;",
"\tborder-right-width: 0px;",
"\twidth: 702px;",
"\theight: 37px;",
"\tcursor: move;",
"\tfont-size: 15pt;",
"\tcolor: #DEECED;",
"\tborder: 3px solid #93CDD0;",
"\tborder-bottom: 1px solid #062834;",
"\tborder-right-width: 0px;",
"\tfloat: left;",
"}",
"\n#DC_LoaTS_raidMenuTitleBarLeft {",
"\tfloat: left;",
"}",
"\n#DC_LoaTS_raidMenuTitleBarCenter {",
"\tfloat: left;",
"\tmargin: auto;",
"\twidth: 400px;",
"\ttext-align: center;",
"}",
"\n#DC_LoaTS_raidMenuTitleBarRight {",
"\tfloat: right;",
"}",
"\n#DC_LoaTS_raidMenuBodyWrapper {",
"\tbackground: #0E5969 url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/menubodywrapperbg.png) 50% 50% repeat;",
//"\tbackground: #0E5969 url(http://old.jqueryui.com/themeroller/images/?new=0e5969&w=12&h=10&f=png&q=100&fltr[]=over|textures/18_hexagon.png|0|0|20) 50% 50% repeat;",
"\tborder: 3px solid #93CDD0;",
"\tborder-top-width: 0px;",
"\tborder-bottom-left-radius: 5px;",
"\tborder-bottom-right-radius: 5px;",
"}",
"\n#DC_LoaTS_raidMenuTabs {",
"\tclear: both;",
"\tborder-bottom: 1px solid #CCC;",
"\theight: 23px;",
"}",
"\n#DC_LoaTS_raidMenuTabs li {",
"\tlist-style: none;",
"\tfont-family: Verdana, sans;",
"\tfont-size: 11px;",
"\tline-height: 18px;",
"\tfloat: left;",
"\tmargin-right: 5px;",
"\ttext-align: center;",
"}",
"\n#DC_LoaTS_raidMenuTabs li a {",
"\tdisplay: block;",
"\theight: 20px;",
"\tpadding: 0px 6px;",
"\twidth: 80px;",
"\tbackground-color: #153041;",
"\tborder: 2px solid #41B0B5;",
"\ttext-decoration: none;",
"\tborder-top-left-radius: 5px;",
"\tborder-top-right-radius: 5px;",
"\tfont-size: 115%;",
"\tcolor: #FFFFFF;",
"}",
"\n#DC_LoaTS_raidMenuTabs li a.active {",
"\tbackground-color: #57959E;",
"\tborder: 2px solid #F1FFFF;",
"\tcolor: #B7E5EE;",
"}",
"\n.RaidMenuTab-Header {",
"}",
"\n.DC_LoaTS_raidMenuOptionWrapper {",
"\tborder-bottom: 1px solid #479090;",
"\tmargin-bottom: 5px;",
"}",
"\n.DC_LoaTS_raidMenuOptionWrapper div{",
"\tpadding: 5px;",
"\tfloat: left;",
"}",
"\n.DC_LoaTS_raidMenuDescription{",
"\tpadding-left: 15px;",
"}",
"\n.DC_LoaTS_raidMenuPane {",
//"\tbackground: #77C0C0 url(http://old.jqueryui.com/themeroller/images/?new=77c0c0&w=1&h=100&f=png&q=100&fltr[]=over|textures/06_inset_hard.png|0|0|50) 50% bottom repeat-x;",
"\tbackground: #77C0C0 url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/menupanebg.png) 50% bottom repeat-x;",
"\tfont-size: 1.2em;",
"\tpadding: 5px 10px;",
"\tmin-height: 200px;",
"\tmax-height: 600px;",
"\toverflow: auto;",
"\tclear: both;",
"}",
"\n.DC_LoaTS_raidMenuPane h1{",
"\tborder-bottom: 1px solid #000000;",
"\tmargin-bottom: 15px;",
"}",
"\n.DC_LoaTS_raidMenuPane h2{",
"\tborder-bottom: 1px solid #479090;",
"\tmargin-bottom: 10px;",
"}",
"\n#RaidsMenu-SearchWrapper {",
"\twidth: 50%;",
"\tmargin: auto;",
"\t;",
"}",
"\n#RaidsMenu-SearchBox {",
"\twidth: 70%;",
"\tmin-width: 150px;",
"}",
"\n#RaidsMenu-ResultsBox {",
"\tmax-height: 300px;",
"\toverflow: auto;",
"}",
"\n#FormattingTab-MessageFormatTextArea {",
"\twidth: 100%;",
"\tmin-height: 35px;",
"}",
"\n.FormattingTab-Button {",
"\tpadding: 3px 15px 4px;",
"}",
"\n.StylesTab-RaidNamesPicker {",
"\tfloat:left;",
"}",
"\n#PreferencesMenu-LoadRaidsInBackgroundDelayInputWrapper input {",
"\twidth: 30px;",
"\theight: 10px;",
"\tborder-radius: 5px;",
"\ttext-align: center;",
"}",
"\n#CharacterViewMenu-PlatformSelect {",
"\tcursor: pointer;",
"\tborder-radius: 4px;",
"\tfont-size: 14px;",
"\tmargin-bottom: 10px;",
"\tpadding: 4px 6px;",
"\theight: 30px;",
"\twidth: 220px;",
"\toutline-offset: -2px;",
"\toutline: 5px auto -webkit-focus-ring-color;",
"}",
"\n#CharacterViewMenu-UsernameBox {",
"\twidth: 206px;",
"\tline-height: 20px;",
"\tfont-size: 14px;",
"}",
"\n.CharacterViewMenu-Button {",
"\tpadding: 3px 15px 4px;",
"}",
"\n#DC_LoaTS_notificationBar {",
"\tbackground: #f8dc5a url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/notificationbg.png) 50% 50% repeat-x;",
//"\tbackground: #f8dc5a url(http://old.jqueryui.com/themeroller/images/?new=f8dc5a&w=1&h=100&f=png&q=100&fltr[]=over|textures/03_highlight_soft.png|0|0|75) 50% 50% repeat-x;",
"\tpadding: 4px 10px; 0px",
"\twidth: 100%;",
"\tfont-size: 12pt;",
"\tcolor: #915608;",
"\tborder-bottom: 1px solid #fcd113;",
"\tposition: fixed;",
"\ttop: 0px;",
"\tleft: 0px;",
"\tz-index: 99999999;",
"}",
"\n#DC_LoaTS_notificationBarTitle {",
"\tfloat: left;",
"}",
"\n#DC_LoaTS_notificationBarText {",
"\tfloat: left;",
"}",
"\n#DC_LoaTS_notificationBarButtons {",
"\tfloat: right;",
"\tpadding-top:1px;",
"}",
"\n#DC_LoaTS_notificationBarButtons a.DC_LoaTS_updateLink {",
"\tfont-size: inherit;",
"\tmargin-right:10px;",
"}",
"\na.DC_LoaTS_notificationBarButton {",
"\tbackground-color: #F9B83E;",
"\tborder: 1px solid #915608;",
"\tpadding: 2px 10px;",
"\tmargin-right: 10px;",
"\ttext-decoration: none;",
"\tfont-weight: bold;",
"\ttext-align: center;",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"\tborder-radius: 5px;",
"}",
"\na.DC_LoaTS_notificationBarButton:hover {",
"\tcolor: #915608;",
"\tbackground: #FDE477;",
"}",
"\n#DC_LoaTS_raidToolbarContainer {",
"\tcolor: #FFFFFF;",
"\tlist-style: none;",
"\tbackground: #113552 url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/hexbg.png) 50% 50% repeat;",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"\tborder-radius: 5px;",
"\theight: 16px;",
"\tpadding: 2px 5px;",
"\ttext-align:left;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li {",
"float:left;",
"}",
"\na.DC_LoaTS_button {",
"\twidth: 16px;",
"\theight: 16px;",
"\tbackground: url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/icons.png);",
"\tbackground-repeat: no-repeat;",
"\tcursor: pointer;",
"\tdisplay: block;",
"\tfloat: left;",
"\ttext-indent: -99999px;",
"}",
"\na.DC_LoaTS_menuButton {",
"\tbackground-position: -48px -80px;",
"}",
"\na.DC_LoaTS_reloadButton {",
"\tbackground-position: -160px -64px;",
"}",
"\na.DC_LoaTS_toggleGameButton {",
"\tbackground-position: 0 -176px;",
"}",
"\nli.DC_LoaTS_toggleWorldChatButtonWrapper {",
"\tfloat: right !important;",
"}",
"\na.DC_LoaTS_toggleWorldChatButton {",
"\tbackground-position: -128px -96px;",
"}",
"\na.DC_LoaTS_reloadWCButton {",
"\tbackground-position: -160px -64px;",
"}",
"\nli.DC_LoaTS_reloadWCButtonWrapper {",
"\tfloat: right !important;",
"}",
"\na.DC_LoaTS_WRButton {",
"\ttext-indent: 0px;",
"\tbackground: none;",
"\twidth: auto;",
"\tborder-radius: 5px;",
"}",
"\na.DC_LoaTS_WRButton:hover {",
"\ttext-decoration: none;",
"\tbackground-color: #71A5CE;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li.DC_LoaTS_WRButtonWrapper {",
"\tfloat: right;",
"}",
"\n.DC_LoaTS_omnibox {",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"\tborder-radius: 5px;",
"\tborder-color: #FFFFFF;",
"\tbackground-color: #71A5CE;",
"\tpadding: 0px 2px !important;",
"}",
"\n.DC_LoaTS_omnibox_focus {",
"\tborder-color: #71A5CE;",
"\tbackground-color: #FFFFFF;",
"}",
"\n.DC_LoaTS_omniboxWrapper {",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"\tborder-radius: 5px;",
"\tposition: relative;",
"\tfloat: left;",
"}",
"\n.DC_LoaTS_omniboxCommandsWrapper {",
"\tbackground: #113552 url(http://subversion.assembla.com/svn/doomscript/trunk/1.1.0/Assets/hexbg.png) 50% 50% repeat;",
"\tlist-style: none;",
"\tz-index: 999;",
"\tposition: absolute;",
"\twidth: 630px;",
"\tpadding: 5px;;",
"\tborder-bottom-left-radius: 5px;",
"\tborder-bottom-right-radius: 5px;",
"\t;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li{",
"\tfloat:none;",
"\tmargin: 0px;",
"\tbackground-color: #051E2A;",
"\tfont-size: 1.3em;",
"\toverflow: hidden;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li a{",
"\tdisplay: block;",
"\tcolor: #EEEEEE;",
"\ttext-decoration: none;",
"\tfloat:left;",
"\t-moz-border-radius: 5px;",
"\t-webkit-border-radius: 5px;",
"\tborder-radius: 5px;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li a:hover{",
"\tbackground-color: #57959E;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li:first-child{",
"\tborder-top-left-radius: 5px;",
"\tborder-top-right-radius: 5px;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li:last-child{",
"\tborder-bottom-left-radius: 5px;",
"\tborder-bottom-right-radius: 5px;",
"}",
"\n#DC_LoaTS_raidToolbarContainer li li a:first-child, #DC_LoaTS_raidToolbarContainer li li div:first-child{",
"\tpadding-left: 10px !important;",
"}",
//--- Onnibox Option Styles ---\\
"\n.DC_LoaTS_initialtext {",
"\tfloat: left;",
"\tpadding-left: 0px !important;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_omniboxOption {",
"\tpadding: 2px 10px;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_any {",
"\t;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_normal {",
"\tcolor:#48C957;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_normal:hover {",
"\tcolor:#FFFFFF;",
"\tbackground-color:#48C957;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_hard {",
"\tcolor:#E3E72E;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_hard:hover {",
"\tcolor:#FFFFFF;",
"\tbackground-color:#E3E72E;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_legendary {",
"\tcolor:#CB0039;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_legendary:hover {",
"\tcolor:#FFFFFF;",
"\tbackground-color:#CB0039;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_nightmare {",
"\tcolor:#B84EFE;",
"}",
"\n#DC_LoaTS_raidToolbarContainer a.DC_LoaTS_nightmare:hover {",
"\tcolor:#FFFFFF;",
"\tbackground-color:#B84EFE;",
"}",
"\na.raidLink {",
"\ttextDecoration: none;",
"}",
"\na.raidDiffNormal:hover {",
"\tcolor:#48C957;",
"}",
"\na.raidDiffHard:hover {",
"\tcolor:#828505;",
"}",
"\na.raidDiffLegendary:hover {",
"\tcolor:#CB0039;",
"}",
"\na.raidDiffNightmare:hover {",
"\tcolor:#B84EFE;",
"}",
"\n.hidden {",
"\tdisplay: none;",
"}",
"\n.DataDumpTab-Data {",
"\twidth: 100%;",
"\theight: 400px;",
"}",
"\n.DC_LoaTS_raidMenuCloseTabA {",
"\tborder-radius: 100px;",
"\twidth: 5px;",
"\theight: 5px;",
"\tcolor: #FFFFFF;",
"\tbackground-color: #CCCCCC;",
"}",
"\n#maingame {",
"\t-moz-transition: width .5s ease-out 0s;",
"\t-webkit-transition: width .5s ease-out 0s;",
"\t-o-transition: width .5s ease-out 0s;",
"}",
"\n#maingame.hideWorldChat {",
"\twidth: 1060px !important;",
"}",
"\n#game {",
"\toverflow: hidden;",
"\t-moz-transition: width .5s ease-out 0s;",
"\t-webkit-transition: width .5s ease-out 0s;",
"\t-o-transition: width .5s ease-out 0s;",
"}",
"\n.hideWorldChat #game {",
"\twidth: 759px !important;",
"}",
"\n#gameholder {",
"\twidth: auto !important;",
"}",
"\n#kong_game_ui.chat-timestamp-right .chat_message_window p .timestamp {",
"\tfloat: right",
"}",
"\n#kong_game_ui.chat-timestamp-right .chat_message_window p .message {",
"\tclear: both",
"}"
];
var head = document.getElementsByTagName('head')[0],
style = document.createElement('style'),
rules = document.createTextNode(rulesText.join("\n"));
style.type = 'text/css';
if(style.styleSheet)
{
style.styleSheet.cssText = rules.nodeValue;
}
else
{
style.appendChild(rules);
}
head.appendChild(style);
}
function setupGMFunctions()
{
if (typeof GM_setValue === 'undefined')
{
// These are probably obsolete now
if(window.opera)
{
if(window.localStorage)
{
console.log("Creating Opera local storage fallbacks for GM functions");
window.GM_setValue = function(k, v)
{
localStorage.setItem(k, v);
};
window.GM_getValue = function(k, def)
{
var ret = localStorage.getItem(k);
return (ret == null?def:ret)
};
window.GM_deleteValue = function(k)
{
localStorage.removeItem(k);
}
}
else
{
window.GM_setValue = function(){console.warn("Local Storage not accessible.");};
window.GM_getValue = function(){console.warn("Local Storage not accessible.");};
window.GM_deleteValue = function(){console.warn("Local Storage not accessible.");};
}
}
else if(/Chrome/i.test(navigator.appVersion) || typeof unsafeWindow === "undefined")
{
console.log("Creating Chrome local storage fallbacks for GM functions");
window.GM_setValue = function(k, v)
{
localStorage.setItem(k, v);
};
window.GM_getValue = function(k, def)
{
var ret = localStorage.getItem(k);
return (ret == null?def:ret)
};
window.GM_deleteValue = function(k)
{
localStorage.removeItem(k);
}
}
}
if (typeof GM_xmlhttpRequest !== "function") {
console.warn("doomscript will not run properly (or maybe even at all) in your browser without Greasemonkey Emulation: http://userscripts-mirror.org/scripts/show/105153");
}
}
function doCriticalHooks()
{
// Have the raid bot post a message to the user
ChatDialogue.prototype.raidBotMessage = function(message)
{
try
{
if (typeof message === "string") {
message = message.replace(/\n/g, "<br />\n");
}
else if (message instanceof HTMLElement) {
message = jQuery(message)[0].outerHTML
}
else {
console.warn("Unexpected message type during raidBotMessage", typeof message, message);
}
holodeck.activeDialogue().displayUnsanitizedMessage(
"RaidBot",
message,
{"class": "whisper received_whisper"},
{non_user: true}
);
}
catch (ex)
{
console.warn("Unexpected exception during raidBotMessage", ex);
}
};
// The idea to this feature would be that we could show the user previous things they typed by pressing up or down.
function hookInputDialogue() {
if (holodeck && holodeck.activeDialogue()) {
// Hook the handler
DC_LoaTS_Helper.registerEventHandler(holodeck.activeDialogue()._input_node, "keyup", function(e) {
e = e || window.event;
// TODO: Eventually, maybe handle up and down arrow for recent messages
if (e.keyCode === 38) {
// console.log("Pressed up");
}
if (e.keyCode === 40) {
// console.log("Pressed down");
}
});
}
else {
// Not ready, wait an try later
setTimeout(hookInputDialogue, 1000);
}
}
hookInputDialogue();
}
// Gotta jumpstart this bucket of giggles
function bootstrap_DC_LoaTS_Helper(loadSubFrames)
{
// Only run if the script is running in the top frame
if (top !== self && loadSubFrames != true)
{
return;
}
if (typeof window._dc_loats_helper_fails == "undefined")
{
window._dc_loats_helper_fails = 0;
}
if (window._dc_loats_helper_fails >= 10)
{
console.warn("DC LoaTS Link Helper could not load.");
return;
}
// Don't want to run the script twice
if (!window._dc_loats_helper)
{
// Do we actually have everything we need to start?
if (typeof holodeck === "undefined" || typeof ChatDialogue === "undefined" || typeof Class === "undefined" || !$("chat_window"))
{
// Something is not loaded yet. Bail on this and try again later
// console.log("DC LoaTS Link Helper not ready. Fail " + window._dc_loats_helper_fails + "/10");
window._dc_loats_helper_fails++;
setTimeout(bootstrap_DC_LoaTS_Helper, 1000); // 1000ms = 1 second
return;
}
// Print that we're about to start
console.info("DC LoaTS Link Helper v" + DC_LoaTS_Properties.version + " trying to start...");
// Setup GreaseMonkey functions
setupGMFunctions();
// Do critical hooks
doCriticalHooks();
// Declare classes
declareClasses();
// Define styles
defineStyles();
// Throw a reference to this onto the window I guess in case anyone else wants to use it?
window._dc_loats_helper = new DC_LoaTS_Helper();
// Update raid data
DC_LoaTS_Helper.updateRaidData();
}
// Everything is done
console.info("DC LoaTS Link Helper started!");
}
// Hit the go button and activate the main script.
bootstrap_DC_LoaTS_Helper(false);
}
// GM Layer
// This is handling XHR via events
function xhrGo(event)
{
if (typeof XPCNativeWrapper !== "undefined" && typeof XPCNativeWrapper.unwrap === "function")
{ //this takes 'event' out of the heavy duty sandbox so we can access the details for FF32+
//INFO: tested and confirmed as working on FF31 and FF32 + GM 2.1
DCDebug("GM XHR: Firefox sandbox, unwrapping 'event' for processing");
event = XPCNativeWrapper.unwrap(event);
}
DCDebug("GM XHR: GM Received XHR Event: ", event);
var params = event.detail;
for (var param in params)
{
if (typeof params[param] === "string" && param.toLowerCase().indexOf("__callback_") === 0)
{
var funcName = param.substring("__callback_".length);
params[funcName] = gmCallBack.bind(this, params.UUID, funcName);
}
}
DCDebug("GM XHR: final params ", params);
if (typeof GM_xmlhttpRequest === "function") {
DCDebug("GM XHR: GM_XHR is available");
setTimeout(function(){GM_xmlhttpRequest(params);},0);
}
else {
DCDebug("GM XHR: GM_XHR is not available");
console.error("Browser is not configured to allow GM_xmlhttpRequest. This could be due to a Chrome v27 bug.");
var xmlhttp;
if (window.XMLHttpRequest)
{// code for IE7+, Firefox, Chrome, Opera, Safari
xmlhttp = new XMLHttpRequest();
}
else
{// code for IE6, IE5
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange = function()
{
if (xmlhttp.readyState === 4)
{
DCDebug("GM XHR: XHR ready state changed. Has onload? ", !!params.onload);
if (typeof params.onload === "function") {
params.onload(xmlhttp);
}
}
};
xmlhttp.open(params.method, params.url, !params.synchronous);
xmlhttp.send();
}
}
function gmCallBack(UUID, funcName, response)
{
DCDebug("GM XHR: Preparing to call back to page JS");
setTimeout(function()
{
DCDebug("GM XHR: Creating event to send back to the page UUID: ", UUID, " funcName: ", funcName, " response: ", response);
var detail = {callbackName: funcName, responseObj: response};
DCDebug("GM XHR: Creating event detail: ", detail);
try {
if (typeof cloneInto === "function") {
DCDebug("GM XHR: Using cloneInto");
var cloned = {callbackName: funcName, responseObj: {}};
for (var p in detail.responseObj) {
// Awkward call to hasOwnProperty because of weird GreaseMonkey behavior in Firefox.
if (Object.prototype.hasOwnProperty.call(detail.responseObj, p)) {
DCDebug("GM XHR: Cloning property ", p, " which is a ", typeof detail.responseObj[p]);
cloned.responseObj[p] = detail.responseObj[p];
}
}
DCDebug("GM XHR: Using cloneInto for real");
cloned = cloneInto(cloned, document.defaultView || unsafeWindow || window);
DCDebug("GM XHR: cloned version: ", cloned);
// It seems like the latest Firefox prefers this deprecated code? Bizarre
var evt = document.createEvent('CustomEvent');
evt.initCustomEvent(UUID, true, true, cloned);
document.documentElement.dispatchEvent(evt);
}
else {
DCDebug("GM XHR: Not using cloneInto (it doesn't exist or isn't a function)");
var evt = new CustomEvent(UUID, {"bubbles": true, "cancelable": true, "detail": detail});
DCDebug("GM XHR: Dispatching event to page", evt);
document.dispatchEvent(evt);
}
}
catch (ex) {
DCDebug("GM XHR: Caught exception while trying to respond to client XHR event", ex, " Fn Args: ", arguments);
}
}, 0);
}
document.addEventListener("DC_LoaTS_ExecuteGMXHR", xhrGo);
var GMDebugMode = (function() {
var value = /debugMode=(\w+)/.exec(document.location.href);
return value && !!value[1];
})();
// Debug log wrapping function
// Special scope debugging for just this script
DCDebug = function() {
if (GMDebugMode === true) {
console.log.apply(console, arguments);
}
};
// This injects our script onto the page.
// Borrowed from: http://stackoverflow.com/a/2303228
if (/https?:\/\/www\.kongregate\.com\/games\/5thPlanetGames\/legacy-of-a-thousand-suns.*/i.test(window.location.href))
{
var ugupCSS = document.createElement('link');
ugupCSS.type = "text/css";
ugupCSS.href = "https://rawgit.com/doomcat/OpenUgUp/master/js/src/ugup.css";
ugupCSS.rel = "stylesheet";
(document.head || document.body || document.documentElement).appendChild(ugupCSS);
var ugupEquips = document.createElement('script');
ugupEquips.type = "text/javascript";
ugupEquips.src = "https://rawgit.com/doomcat/OpenUgUp/master/js/src/ugup.equipment.js";
(document.body || document.head || document.documentElement).appendChild(ugupEquips);
var ugupScript = document.createElement('script');
ugupScript.type = "text/javascript";
ugupScript.src = "https://rawgit.com/doomcat/OpenUgUp/master/js/src/ugup.js";
(document.body || document.head || document.documentElement).appendChild(ugupScript);
var script = document.createElement('script');
script.appendChild(document.createTextNode('('+ main +')();'));
(document.body || document.head || document.documentElement).appendChild(script);
}
else if (/50\.18\.1[89]\d\.\d{2,3}/i.test(window.location.host)) {
console.log("doomscript loaded in iFrame");
function iframeFn()
{
if (typeof GM_setValue === 'undefined')
{
window.GM_setValue = function(k, v)
{
localStorage.setItem(k, (typeof v)[0] + v);
};
}
if (typeof GM_getValue === 'undefined')
{
window.GM_getValue = function(k, def)
{
var ret = localStorage.getItem(k);
if (!ret)
return def;
var type = ret[0];
var value = ret.substring(1);
switch (type) {
case 'b':
return value == 'true';
case 'n':
return Number(value);
case 's':
return value;
default:
return def;
}
};
}
if (typeof GM_deleteValue === 'undefined')
{
window.GM_deleteValue = function(k)
{
localStorage.removeItem(k);
}
}
window.onmessage = function(e)
{
if (e.origin != "http://www.kongregate.com" && e.origin != "http://www.armorgames.com")
return;
console.log("iFrame received message: ", e.data);
var data = JSON.parse(e.data);
if (!data || data.namespace != "DC_LoaTS_Helper")
return;
if (data.param == "game")
var swf = document.getElementById("swfdiv");
else if (data.param == "chat")
var swf = document.getElementById("chatdiv");
else
return;
if (swf !== null)
{
var url = swf.getAttribute("data");
if(data.command == "reload" || data.command == "show")
{
if (data.command == "show")
{
url = GM_getValue("DC_LoaTS_chatURL", "https://5thplanetlots.insnw.net/dotd_live/chat/812/chatclient.swf");
if (data.param == "game")
url = GM_getValue("DC_LoaTS_gameURL", "https://5thplanetlots.insnw.net/lots_live/swf/8107/lots.swf");
}
swf.style.display = "none";
window.setTimeout(function()
{
swf.setAttribute("data", url);
swf.style.display = "";
}, 500);
}
else if(data.command == "hide")
{
GM_setValue("DC_LoaTS_"+data.param+"URL", url);
swf.style.display = "none";
window.setTimeout(function()
{
swf.setAttribute("data", "");
swf.style.display = "";
}, 500);
}
data.retCode = true;
}
e.source.postMessage(JSON.stringify(data), e.origin); //post back message (command, param, retCode) to caller
};
}
var iframeScript = document.createElement('script');
iframeScript.appendChild(document.createTextNode('('+ iframeFn +')();'));
(document.body || document.head || document.documentElement).appendChild(iframeScript);
}
else {
console.log("doomscript not launched on ", window.location);
}