KoLE2-alpha

Misc enhancements for kingdom of loathing

  1. // ==UserScript==
  2. // @name KoLE2-alpha
  3. // @namespace fnoot/kol/kole2-alpha
  4. // @description Misc enhancements for kingdom of loathing
  5. // @include http://www.kingdomofloathing.com/*
  6. // @include https://www.kingdomofloathing.com/*
  7. // @include https://kingdomofloathing.com/*
  8. // @version 0.2.4.1
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_deleteValue
  12. // ==/UserScript==
  13. /*
  14.  
  15. changes 0.2.3 > 0.2.4
  16. - fixed HP detection (for min autofight hp) in compact mode char pane
  17. - 'auto button' option; choose choice adventure buttons to auto-click
  18. - (.4.1) Fixed an incorrect title
  19.  
  20. changes 0.2.2 > 0.2.3
  21. - 'Watch text' option; halt and show alert when specified text appears
  22. - increased daily reminder click delay to account for unpredictable time before the buttons work
  23. - improved example topbar theme
  24. - fixed sword nullification (thanks to Marge for the Talkie for testing)
  25. - improved styling here and there
  26.  
  27. changes 0.2.1 > 0.2.2
  28. - quick "Auto Fight" checkbox
  29. - auto daily reminder clicking
  30.  
  31. changes 0.2 > 0.2.1
  32. - includes new https url
  33.  
  34. changes 0.1 > 0.2
  35. - Topbar icon theming
  36. - Auto fight min HP% setting
  37. - /qs * <item> to sell all
  38.  
  39. */
  40. // kole 1 (defunct): http://userscripts.org/scripts/show/149194
  41.  
  42. (function() {
  43. var top = unsafeWindow.top; // :(
  44.  
  45. // debug
  46. // GM_deleteValue("kole2Settings");
  47. // GM_deleteValue("iconThemes");
  48.  
  49. var defaultIconThemes = {
  50. example: {
  51. "displayName": "Example Theme (0.2.3)",
  52. "bg": "#e6e6e6",
  53. "icons": {
  54. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/map.gif": "http://files.softicons.com/download/application-icons/pixelophilia-2-icons-by-omercetin/png/32/map.png",
  55. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/backpack.gif": "http://findicons.com/files/icons/1334/take_a_hike/32/backpack.png",
  56. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/donate.gif": "http://www.myiconfinder.com/uploads/iconsets/32-32-eea92a656253619edb72bf32fbe14ef9-bag.png",
  57. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/book3.gif": "http://vignette1.wikia.nocookie.net/tibia/images/9/96/Spellbook_of_Dark_Mysteries.gif/revision/latest?cb=20080620235058&path-prefix=en",
  58. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/pliers.gif": "https://cdn4.iconfinder.com/data/icons/harwdware-tools-v2/512/water_pump_pliers_tool-32.png",
  59. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/envelope.gif": "http://files.softicons.com/download/toolbar-icons/iconza-light-blue-icons-by-turbomilk/png/32/mail.png",
  60. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/blackwrench.gif": "http://findicons.com/files/icons/2262/android_developer_common_icon_set_ii/32/options_selected.png",
  61. "https://s3.amazonaws.com/images.kingdomofloathing.com/itemimages/chat.gif": "http://findicons.com/files/icons/1620/crystal_project/32/delete_group.png"
  62. }
  63. }
  64. };
  65.  
  66. var saveIconThemes = function() {
  67. GM_setValue("iconThemes", JSON.stringify(top.kole.iconThemes));
  68. };
  69. var setOption = function(name, value) {
  70. GM_setValue(name, JSON.stringify(value));
  71. };
  72.  
  73. var getOption = function(name) {
  74. var v = GM_getValue(name, JSON.stringify(configOptions[name].default));
  75. return JSON.parse(v);
  76. };
  77.  
  78. var closePanel = null;
  79.  
  80. var Notification = window.Notification || window.mozNotification || window.webkitNotification;
  81. var desktopNotify = function desktopNotify(title, options, properties) {
  82. if (!Notification) return;
  83. if (Notification.permission !== "granted") return;
  84. var notification = new Notification(title, options);
  85. if (properties) for (var k in properties) if (properties.hasOwnProperty(k)) notification[k] = properties[k];
  86. };
  87.  
  88.  
  89. var controlTypes = {
  90. input: function(name, spec, parent) {
  91. var input = crel("input", spec.style ? spec.style : {}, parent);
  92. input.id = "kole_config_" + name;
  93. var setValue = function() {
  94. if (spec.onchange) spec.onchange(input.value);
  95. setOption(name, input.value);
  96. };
  97. input.value = getOption(name);
  98. input.onkeyup = setValue;
  99. input.onpaste = setValue;
  100. },
  101. yesno: function(name, spec, parent) {
  102. var select = crel("select", {}, parent);
  103. select.id = "kole_config_" + name;
  104. with(crel("option", {}, select)) {
  105. innerHTML = "Yes";
  106. value = 1;
  107. }
  108. with(crel("option", {}, select)) {
  109. innerHTML = "No";
  110. value = 0;
  111. }
  112. select.selectedIndex = getOption(name) ? 0 : 1;
  113. select.onchange = function() {
  114. setOption(name, this.selectedIndex == 0);
  115. };
  116. },
  117. check: function(name, spec, parent) {
  118. var cbox = crel("input", {}, parent);
  119. cbox.checked = getOption(name);
  120. cbox.id = "kole_config_" + name;
  121. cbox.type = "checkbox";
  122. cbox.onclick = function() {
  123. setOption(name, this.checked);
  124. if (spec.onchange) spec.onchange(this.checked);
  125. }
  126. },
  127. spin: function(name, spec, parent) {
  128. var min = spec.range[0],
  129. max = spec.range[1];
  130. var value = getOption(name);
  131. if (isNaN(value)) value = configOptions[name].default;
  132. var displayCallback = spec.displayCallback || function(v) {
  133. return v;
  134. };
  135. var downButton = crel("button", {}, parent);
  136. downButton.innerHTML = "&lt;";
  137. var valueSpan = crel("span", {
  138. display: "inline-block",
  139. "margin": "0 6px",
  140. "width": "50px",
  141. "text-align": "center"
  142. }, parent);
  143. var fixPrecision = function() {
  144. value = Math.round(value * 100) / 100;
  145. }
  146. valueSpan.innerHTML = displayCallback(value);
  147. var upButton = crel("button", {}, parent);
  148. upButton.innerHTML = "&gt;";
  149. downButton.onclick = function() {
  150. value -= spec.step;
  151. fixPrecision();
  152. if (value < min) value = min;
  153. valueSpan.innerHTML = displayCallback(value);
  154. setOption(name, value);
  155. if (spec.onchange) spec.onchange(value);
  156. };
  157. upButton.onclick = function() {
  158. value += spec.step;
  159. fixPrecision();
  160. if (value > max) value = max;
  161. valueSpan.innerHTML = displayCallback(value);
  162. setOption(name, value);
  163. if (spec.onchange) spec.onchange(value);
  164. };
  165. },
  166. button: function(name, spec, parent) {
  167. with(crel("a", {}, parent)) {
  168. onclick = function() {
  169. spec.onclick();
  170. return false;
  171. };
  172. href = "#" + name;
  173. innerHTML = spec.buttonCaption;
  174. }
  175. },
  176. select: function(name, spec, parent) {
  177. var ctrl = crel("select", {}, parent);
  178. var options = typeof spec.options == "function" ? spec.options() : spec.options;
  179. var idx = 0;
  180. var selIndex = 0;
  181. for (var value in options) {
  182. var opt = crel("option", {}, ctrl);
  183. opt.innerHTML = options[value];
  184. opt.value = value;
  185. if (value == getOption("iconTheme")) selIndex = idx;
  186. idx++;
  187. }
  188. ctrl.selectedIndex = selIndex;
  189. ctrl.onchange = function() {
  190. setOption(name, ctrl.options[ctrl.selectedIndex].value);
  191. if (spec.onchange) spec.onchange(value);
  192. };
  193. },
  194. raw: function(name, spec, parent) {
  195. parent.innerHTML = spec.html;
  196. }
  197. };
  198.  
  199. var getSetting = function(name) {
  200. console.trace();
  201. throw new Error("Obsolete getSetting()");
  202. };
  203.  
  204. var itemLookup = function(fuzzyName) {
  205. if (!top.kole) return null;
  206. var matches = [];
  207. var item = null;
  208. for (var name in top.kole.itemIds) {
  209. if (name.trim().toUpperCase().indexOf(fuzzyName.toUpperCase()) > -1) {
  210. if (name.toLowerCase() == fuzzyName.toLowerCase()) {
  211. return {
  212. name: name,
  213. id: top.kole.itemIds[name]
  214. };
  215. }
  216. matches.push({
  217. name: name,
  218. id: top.kole.itemIds[name]
  219. });
  220. }
  221. }
  222. if (matches.length > 1) {
  223. return {
  224. error: "Multiple matches for \"" + fuzzyName + "\". Please be more specific."
  225. };
  226. } else if (matches.length == 1) {
  227. return matches[0];
  228. }
  229. return null;
  230. };
  231.  
  232. var configOptions = {
  233. hoverHints: {
  234. default: true,
  235. control: "check",
  236. caption: "Hover hints",
  237. description: "Shows information about an item/effect/icon when the mouse pointer hovers over it"
  238. },
  239. hintDelay: {
  240. default: 225,
  241. caption: "Hint delay (milliseconds)",
  242. description: "Specifies how long you need to hover over an item/effect/icon before its description is shown",
  243. control: "spin",
  244. range: [0, 2000],
  245. step: 25
  246. },
  247. darkness: {
  248. default: 0.2,
  249. caption: "Darkness",
  250. description: "Darkens the whole game; great for headaches and photosensitives!",
  251. control: "spin",
  252. range: [0, 0.8],
  253. step: 0.1,
  254. onchange: function(value) {
  255. top.kole.setDarkness(value);
  256. },
  257. displayCallback: function(value) {
  258. return ((value / 0.8) * 100).toFixed(1).replace(/(\d+)\.0+$/, '$1') + "%";
  259. }
  260. },
  261. stayLoggedIn: {
  262. default: true,
  263. caption: "Stay logged in",
  264. description: "Defeats the timeout that logs you out after an idle period by sending a dummy request every two minutes",
  265. control: "check",
  266. onchange: function(value) {
  267. if (value) xhr("main.php", function() {}, false);
  268. }
  269. },
  270. nullifySword: {
  271. default: true,
  272. caption: "Fix prepositions",
  273. description: "Undoes the effect of the Sword - may no longer work",
  274. control: "check"
  275. },
  276. autoDaily: {
  277. default: false,
  278. caption: "Automatic daily reminders",
  279. description: "Automatically clicks the daily reminder buttons",
  280. control: "check"
  281. },
  282. iconTheme: {
  283. default: "",
  284. caption: "Topbar icon theme",
  285. control: "select",
  286. description: "Replaces the icons on the topbar with a custom theme",
  287. options: function() {
  288. var options = {
  289. "": "Vanilla"
  290. };
  291. for (themeName in unsafeWindow.top.kole.iconThemes) {
  292. options[themeName] = unsafeWindow.top.kole.iconThemes[themeName].displayName
  293. }
  294. return options;
  295. }
  296. },
  297. manageIconThemes: {
  298. caption: "Manage icon themes",
  299. description: "Import, delete and edit topbar icon themes",
  300. control: "button",
  301. buttonCaption: "Manage",
  302. onclick: function() {
  303. var reshow = function() {
  304. var pop = crel("div");
  305. var vanillaManage = crel("div");
  306. var newThemeButton = crel("a", {}, vanillaManage);
  307. crel("span", {}, vanillaManage).innerHTML = " ";
  308. newThemeButton.innerHTML = "New theme";
  309. newThemeButton.href = "#";
  310. newThemeButton.onclick = function() {
  311. managePoop.close();
  312. editIconTheme("", reshow);
  313. };
  314. var importButton = crel("a", {}, vanillaManage);
  315. crel("span", {}, vanillaManage).innerHTML = " ";
  316. importButton.innerHTML = "Import";
  317. importButton.href = "#";
  318. importButton.onclick = function() {
  319. managePoop.close();
  320. importIconTheme(reshow);
  321. };
  322. crel("h1", {
  323. margin: "0 0 12px 0",
  324. "font-weight": "100"
  325. }, pop).innerHTML = "Topbar Icon Themes";
  326. var themeList = [
  327. ["Vanilla", vanillaManage]
  328. ];
  329. for (var name in unsafeWindow.top.kole.iconThemes) {
  330. var manage = crel("div");
  331. var editButton = crel("a", {}, manage);
  332. crel("span", {}, manage).innerHTML = " ";
  333. editButton.innerHTML = "Edit";
  334. editButton.href = "#";
  335. var deleteButton = crel("a", {}, manage);
  336. crel("span", {}, manage).innerHTML = " ";
  337. deleteButton.innerHTML = "Delete";
  338. deleteButton.href = "#";
  339. var exportButton = crel("a", {}, manage);
  340. crel("span", {}, manage).innerHTML = " ";
  341. exportButton.innerHTML = "Export";
  342. exportButton.href = "#";
  343. with({
  344. name: name,
  345. theme: unsafeWindow.top.kole.iconThemes[name]
  346. }) {
  347. deleteButton.onclick = function() {
  348. if (!confirm("Delete this theme?")) return;
  349. delete top.kole.iconThemes[name];
  350. if (name == getOption("iconTheme")) {
  351. setOption("iconTheme", "");
  352. closePanel();
  353. openPanel();
  354. saveSettings();
  355. }
  356. saveIconThemes();
  357. managePoop.close();
  358. reshow();
  359. return false;
  360. };
  361. editButton.onclick = function() {
  362. editIconTheme(name, reshow);
  363. managePoop.close();
  364. return false;
  365. };
  366. exportButton.onclick = function() {
  367. poop("<h1 style='font-weight:100; margin:0 0 12px 0'>Export code for " + theme.displayName + "</h1>" + "<textarea onclick='this.select()' style='width:100%; background:rgba(0,0,0,0.2); height:200px' readonly='readonly'>" + exportIconTheme(name).replace(/\</g, "&lt;").replace(/\>/g, "&gt;") + "</textarea><p>Copy and share the above code to distribute your theme.</p>");
  368. return false;
  369. };
  370. }
  371.  
  372. themeList.push([top.kole.iconThemes[name].displayName, manage]);
  373. }
  374. pop.appendChild(tabulate(themeList, "rgba(0,0,0,0.06"));
  375. var managePoop = poop(pop);
  376. }; // /reshow
  377. reshow();
  378. }
  379. },
  380. autoFight: {
  381. default: false,
  382. caption: "Automatic fighting",
  383. description: "Always clicks the last item in the combat bar or \"Adventure again\" when available; requires combat bar enabled in KoL options",
  384. control: "check",
  385. onchange: function(val){
  386. quickAF.checked = val;
  387. }
  388. },
  389. autoFightDelay: {
  390. default: 4000,
  391. caption: "Automatic fighting delay",
  392. description: "Defines how long to wait before taking an automatic action in a fight",
  393. control: "spin",
  394. range: [500, 10000],
  395. step: 500,
  396. displayCallback: function(v) {
  397. return (v / 1000) + "sec";
  398. }
  399. },
  400. autoFightMinHp: {
  401. default: 40,
  402. caption: "Auto fighting minimum HP",
  403. description: "Cancels automatic fighting when HP drops below this",
  404. control: "spin",
  405. range: [0, 100],
  406. step: 5,
  407. displayCallback: function(v) {
  408. return v + "%";
  409. }
  410. },
  411. autoButton: {
  412. caption: "Automatic Buttons",
  413. description: "Choose which buttons to auto-click in choice adventures",
  414. control: "button",
  415. buttonCaption: "Manage",
  416. onclick: function(){
  417. var pop = crel("div");
  418. crel("h1", {
  419. margin: "0 0 12px 0",
  420. "font-weight": "100"
  421. }, pop).innerHTML = "Automatic Buttons";
  422. crel("p", {
  423. "font-size": "small"
  424. }, pop).innerHTML = "Buttons listed below will be automatically clicked when they appear. Enter one button caption per line. Button captions are case-sensitive.";
  425. var buttonsStr = GM_getValue("autoButtons")
  426. if(typeof buttonsStr == "undefined") buttonsStr = "";
  427. var ta = crel("textarea", {
  428. width: "100%",
  429. height: "24vh"
  430. }, pop);
  431. ta.value = buttonsStr;
  432. var bottom = crel("div", {"text-align": "right"}, pop);
  433. var ok = crel("button", {}, bottom);
  434. ok.innerHTML = "OK";
  435. var popup = poop(pop);
  436. ok.onclick = function(){
  437. GM_setValue("autoButtons", ta.value);
  438. popup.close();
  439. };
  440. }
  441. },
  442. autoButtonDelay: {
  443. default: 4000,
  444. caption: "Automatic button delay",
  445. description: "Defines how long to wait before automatically clicking a choice adventure button",
  446. control: "spin",
  447. range: [500, 10000],
  448. step: 500,
  449. displayCallback: function(v) {
  450. return (v / 1000) + "sec";
  451. }
  452. },
  453. watchText: {
  454. default: "",
  455. caption: "Watch text",
  456. description: "Watch for text on the page; shows notification and stops autofight. Separate strings with |",
  457. control: "input",
  458. style: {width: "95%"},
  459. onchange: function(val){
  460. if(val && Notification) Notification.requestPermission();
  461. }
  462. },
  463. chatHelp: {
  464. caption: "Chat commands",
  465. buttonCaption: "Show",
  466. "description": "Shows a list of chat commands added by KoLE",
  467. "control": "button",
  468. onclick: function() {
  469. var pop = crel("div");
  470. crel("h1", {
  471. margin: "0 0 12px 0",
  472. "font-weight": "100"
  473. }, pop).innerHTML = "KoLE Chat Commands";
  474. crel("p", {
  475. "font-size": "small"
  476. }, pop).innerHTML = "These commands require <i>Modern</i> chat version selected in <a href='account.php?tab=chat'>KoL options</a>";
  477. pop.appendChild(tabulate([
  478. ["<code>/wiki &lt;searchterm&gt;</code>", "Search Coldfront KoL wiki"],
  479. ["<code>/qs [amount] &lt;itemname&gt;</code>", "Quicksell item; default amount is 1"],
  480. ["<code>/qs * &lt;itemname&gt;</code>", "Quicksell all of an item"]
  481. ], "rgba(0,0,0,0.06"));
  482. poop(pop);
  483. }
  484. },
  485. donate: {
  486. caption: "Appreciation",
  487. control: "raw",
  488. description: "Has this been useful?",
  489. html: '<form target="_blank" action="https://www.paypal.com/cgi-bin/webscr" method="post" target="_top" style="margin:none;padding:none;display:inline">' +
  490. '<input type="hidden" name="cmd" value="_s-xclick">' +
  491. '<input type="hidden" name="hosted_button_id" value="G33Q3HVDX4G3Y">' +
  492. '<input type="submit" value="Show via PayPal" src="https://www.paypalobjects.com/en_GB/i/btn/btn_donate_SM.gif" border="0" name="submit">' +
  493. '<img alt="" border="0" src="https://www.paypalobjects.com/en_GB/i/scr/pixel.gif" width="1" height="1">' +
  494. '</form>'
  495. }
  496.  
  497. };
  498.  
  499.  
  500.  
  501.  
  502. var prepositions = ["about", "above", "across", "after", "against", "along", "among", "around", "at", "before",
  503. "behind", "below", "beneath", "beside", "between", "beyond", "by", "down", "during", "except",
  504. "for", "from", "in", "inside", "into", "like", "near", "of", "off", "on", "onto", "out", "outside",
  505. "over", "past", "through", "throughout", "to", "under", "up", "upon", "with", "within", "without"
  506. ];
  507.  
  508. var OLDnullifySword = function(s) {
  509. for (var i = 0; i < prepositions.length; i++) {
  510. var prep = prepositions[i];
  511. var search = new RegExp(" " + prep, "g");
  512. s = s.replace(search, "\x09" + prep);
  513. var search = new RegExp(prep + " ", "g");
  514. s = s.replace(search, prep + "\x09");
  515. }
  516. return s;
  517. };
  518. var nullifySword = function(s) {
  519. var sneakySpace = "\x09";
  520. for (var i = 0; i < prepositions.length; i++) {
  521. var prep = prepositions[i];
  522. var search = new RegExp(" " + prep, "g");
  523. s = s.replace(search, sneakySpace + prep);
  524. search = new RegExp(prep + " ", "g");
  525. s = s.replace(search, prep + sneakySpace + " ");
  526. }
  527. return s;
  528. };
  529.  
  530. var tabulate = function(data, altColour, cellCallback) {
  531. var table = crel("table", {
  532. width: "100%",
  533. "font-size": "inherit"
  534. });
  535. table.setAttribute("cellspacing", 0);
  536. table.setAttribute("cellpadding", 4);
  537. if (!cellCallback) cellCallback = function(v) {
  538. return v;
  539. };
  540. var tb = crel("tbody", {}, table);
  541. if (data.length == 0) return table;
  542. var alt = false;
  543. for (var i = 0; i < data.length; i++) {
  544. var row = data[i];
  545. var tr = crel("tr", {}, tb);
  546. if (alt) tr.style.background = altColour;
  547. alt = !alt;
  548. for (var x = 0; x < row.length; x++) {
  549. var td = crel("td", {}, tr);
  550. var cbResult = cellCallback(row[x]);
  551. if (typeof cbResult == "string") {
  552. td.innerHTML = cbResult;
  553. } else {
  554. td.appendChild(cbResult);
  555. }
  556. }
  557. }
  558. return table;
  559. };
  560.  
  561. var styleClassIdx = 0;
  562. var applyStyles = function(el, styles, pseudo){
  563. var style = document.createElement("style");
  564. if(typeof pseudo == "undefined") pseudo = "";
  565. if(pseudo != '') pseudo = ":" + pseudo;
  566. var className = "hover_styles";
  567. while(document.querySelectorAll("."+className).length > 0){
  568. className = "hover_style_" + styleClassIdx;
  569. styleClassIdx++;
  570. }
  571. var styleBlock = "";
  572. var keys = Object.keys(styles);
  573. for(var i = 0; i < keys.length; i++){ var key = keys[i];
  574. styleBlock += key + ":" + styles[key] + ";";
  575. }
  576. style.appendChild(document.createTextNode("." + className + pseudo + "{" + styleBlock + "}"));
  577. document.body.appendChild(style);
  578. el.classList.add(className);
  579. };
  580.  
  581. // document.createElement > applyStyles > parent.append shortcut
  582. var crel = function(tag, styles, parent) {
  583. var el = document.createElement(tag);
  584. if (typeof styles != "undefined") applyStyles(el, styles);
  585. if (parent === null) {
  586. console.warn("parent is null for", tag);
  587. console.trace();
  588. return;
  589. }
  590. if (typeof parent != "undefined") parent.appendChild(el);
  591. return el;
  592. };
  593.  
  594. var elX = function(el) {
  595. return el.offsetParent ? elX(el.offsetParent) + el.offsetLeft : el.offsetLeft;
  596. };
  597. var elY = function(el) {
  598. return el.offsetParent ? elY(el.offsetParent) + el.offsetTop : el.offsetTop;
  599. };
  600.  
  601.  
  602. if (window !== top) {
  603. var darkCover = crel("div", {
  604. "position": "fixed",
  605. "top": "0",
  606. "left": "0",
  607. "right": "0",
  608. "bottom": "0",
  609. "background": "#000",
  610. "opacity": 0,
  611. "pointer-events": "none",
  612. "z-index": 999999,
  613. }, document.body);
  614. var hintBox = crel("div", {
  615. "position": "absolute",
  616. "width": "260px",
  617. "padding": "12px 0px",
  618. "border": "1px #bbb solid",
  619. "border-radius": "5px",
  620. "box-shadow": "2px 2px 3px rgba(0,0,0,0.4)",
  621. "pointer-events": "none",
  622. "background": "#fff",
  623. "opacity": 0,
  624. "transform": "scale(0.1,0.1)",
  625. "transition": "100ms opacity ease-out, 100ms transform ease-out, 100ms -moz-transform ease-out, 100ms -webkit-transform ease-out",
  626. "z-index": 999998,
  627. "font-size": "small"
  628. }, document.body);
  629. setTimeout(function() {
  630. darkCover.style.transition = "600ms opacity";
  631. });
  632.  
  633. var initialHintWidth = hintBox.clientWidth;
  634. var showHint = function(forEl, html) {
  635. forX = elX(forEl);
  636. forY = elY(forEl);
  637. hintBox.innerHTML = html;
  638. hintBox.style.width = initialHintWidth + "px";
  639. hintBox.style.left = forX + forEl.clientWidth + 12 + "px";
  640. if ((elX(hintBox) + hintBox.clientWidth) > document.body.clientWidth) {
  641. hintBox.style.width = document.body.clientWidth - elX(hintBox) + "px";
  642. }
  643. hintBox.style.top = (forY + (hintBox.clientHeight * 1.5)) > document.body.scrollHeight ? document.body.scrollHeight - (hintBox.clientHeight * 1.5) + "px" : forY + "px";
  644. crel("div", {
  645. background: "rgba(100, 150, 255,0.04)",
  646. "position": "absolute",
  647. "box-shadow": "0 0 22px rgba(100, 150, 255,0.09)",
  648. "top": "42px",
  649. "left": "0",
  650. "right": "0",
  651. "bottom": "0"
  652. }, hintBox);
  653. applyStyles(hintBox, {
  654. opacity: 1,
  655. transform: "scale(1,1)"
  656. });
  657. if(elY(hintBox) + hintBox.clientHeight > hintBox.parentNode.clientHeight + hintBox.parentNode.scrollTop){
  658. hintBox.style.top = hintBox.parentNode.clientHeight - (hintBox.clientHeight + 16) + hintBox.parentNode.scrollTop + "px";
  659. }
  660. hintElement = forEl;
  661. };
  662.  
  663. var hintTimer = null;
  664. var hintElement = null;
  665.  
  666. var xhr = function(url, callback, cached) {
  667. if (cached && typeof top.kole.xhrCache[url] != "undefined") {
  668. setTimeout(function() {
  669. callback(top.kole.xhrCache[url]);
  670. }, 0);
  671. return {
  672. cancel: function() {}
  673. };
  674. };
  675. var canceled = false;
  676. var req = new XMLHttpRequest();
  677. var stateChange = function() {
  678. if (this.readyState == 4) {
  679. top.kole.xhrCache[url] = this.responseText;
  680. if (!canceled)
  681. callback(this.responseText);
  682. req.removeEventListener("readystatechange", stateChange);
  683. }
  684. };
  685. req.addEventListener("readystatechange", stateChange, false);
  686. req.open("GET", url, true);
  687. req.send();
  688. return {
  689. cancel: function() {
  690. canceled = true;
  691. }
  692. }
  693. };
  694.  
  695. var extractDescription = function(url, callback, cached) {
  696. return xhr(url, function(response) {
  697. var tempEl = crel("div");
  698. tempEl.innerHTML = response;
  699. var scripts = tempEl.querySelectorAll("script");
  700. for (var i = scripts.length - 1; i >= 0; i--) scripts[i].parentNode.removeChild(scripts[i]);
  701. callback(tempEl.querySelectorAll("#description")[0].innerHTML);
  702. }, cached);
  703. };
  704.  
  705. var cancelLastHintCallback = function() {};
  706.  
  707. var cancelHint = function() {
  708. cancelLastHintCallback();
  709. if (hintTimer !== null) clearTimeout(hintTimer);
  710. applyStyles(hintBox, {
  711. opacity: 0,
  712. transform: "scale(0.9,0.9)"
  713. });
  714. };
  715.  
  716. // htmlCallback(done(html)) should return {cancel:function(){...}}
  717. var setHintTimer = function(el, htmlCallback, ___args) {
  718. cancelHint();
  719. // show when both timer AND callback async complete
  720. var asyncRemaining = 2;
  721. var hintHtml = "";
  722. var asyncDone = function() {
  723. asyncRemaining--;
  724. if (asyncRemaining == 0) {
  725. showHint(el, hintHtml);
  726. }
  727. };
  728. hintTimer = setTimeout(asyncDone, getOption("hintDelay"));
  729. var cancelLastHintCallback = htmlCallback(function(html) {
  730. hintHtml = html;
  731. asyncDone();
  732. }).cancel;
  733. };
  734. } // !top
  735.  
  736. if (unsafeWindow == unsafeWindow.top) {
  737. var whenReady = function(cb) {
  738. for (var i = 0; i < unsafeWindow.frames.length; i++)
  739. if (typeof unsafeWindow.frames[i].kole == "undefined") {
  740. setTimeout(function() {
  741. whenReady(cb);
  742. }, 200);
  743. return;
  744. }
  745. cb();
  746. }
  747. if (typeof this.kole != "undefined") {
  748. alert("A browser plugin or KoL update is conflicting with KoLE");
  749. return;
  750. }
  751. unsafeWindow.kole = null;
  752. whenReady(function() {
  753. var GM_itemIds = GM_getValue("itemIds");
  754. var GM_iconThemes = GM_getValue("iconThemes");
  755. if(typeof GM_iconThemes != "undefined") GM_iconThemes = JSON.parse(GM_iconThemes);
  756. if(GM_iconThemes && Object.keys(GM_iconThemes).length == 1 && GM_iconThemes.hasOwnProperty("example")){
  757. GM_deleteValue("iconThemes");
  758. GM_iconThemes = undefined;
  759. }
  760. unsafeWindow.kole = {
  761. setDarkness: function(darkness) {
  762. for (var i = 0; i < frames.length; i++) {
  763. unsafeWindow.frames[i].window.kole.setDarkness(darkness);
  764. }
  765. },
  766. itemIds: typeof GM_itemIds == "undefined" ? {} : JSON.parse(GM_itemIds),
  767. iconThemes: typeof GM_iconThemes == "undefined" ? defaultIconThemes : GM_iconThemes,
  768. xhrCache: {}
  769. };
  770. });
  771. } else {
  772. unsafeWindow.kole = {
  773. setDarkness: function(darkness) {
  774. darkCover.style.opacity = darkness + 0.00001; // fixes gre render bug
  775. },
  776. top: top.kole
  777. };
  778. unsafeWindow.kole.setDarkness(getOption("darkness"));
  779. }
  780.  
  781. var poop = function(htmlOrElement, onclose) {
  782. var cover = crel("div", {
  783. position: "fixed",
  784. top: 0,
  785. left: 0,
  786. right: 0,
  787. bottom: 0,
  788. background: "rgba(0,0,0,0.3)",
  789. opacity: 0,
  790. transition: "200ms all",
  791. "z-index": 9002
  792. });
  793. var win = crel("div", {
  794. width: "66vw",
  795. "min-width": "480px",
  796. "max-width": "600px",
  797. margin: "34vh auto 0 auto",
  798. background: "#eee",
  799. position: "relative",
  800. transform: "scale(1,5,1.5) translateY(-30%)",
  801. "max-height": "78%",
  802. "overflow": "auto",
  803. opacity: 0,
  804. transition: "400ms all",
  805. "box-shadow": "1px 1px 6px rgba(0,0,0,0.4)"
  806. }, cover);
  807. var inner = crel("div", {
  808. padding: "12px"
  809. }, win);
  810. var winner = crel("div", {
  811. padding: "12px"
  812. }, inner);
  813. if (typeof htmlOrElement == "string") {
  814. winner.innerHTML = htmlOrElement;
  815. } else {
  816. winner.appendChild(htmlOrElement)
  817. }
  818. var close = function() {
  819. cover.style.opacity = 0;
  820. applyStyles(win, {
  821. "margin-top": "20%",
  822. opacity: 0
  823. });
  824. setTimeout(function() {
  825. document.body.removeChild(cover);
  826. }, 600);
  827. if (onclose) onclose();
  828. }
  829. var closeButton = crel("button", {
  830. position: "absolute",
  831. top: 0,
  832. right: 0,
  833. border: "none",
  834. background: "#fff",
  835. "padding": "0 0.8em",
  836. "font-size": "1.2em"
  837. }, win);
  838. applyStyles(closeButton, {
  839. "background": "#f88"
  840. }, "hover");
  841. with(closeButton) {
  842. onclick = close;
  843. innerHTML = "&#x2716;";
  844. }
  845. document.body.appendChild(cover);
  846. setTimeout(function() {
  847. cover.style.opacity = 1;
  848. applyStyles(win, {
  849. "transform": "scale(1,1) translateY(-50%)",
  850. opacity: 1
  851. });
  852. }, 100);
  853. return {
  854. close: close
  855. };
  856. };
  857.  
  858. var timedClick = function(el, delay, cancelCaption, oncancel) {
  859. var cancelButton = crel("button", {
  860. background: "#fff",
  861. border: "2px #000 solid",
  862. transition: delay + "ms box-shadow linear",
  863. "box-shadow": "inset 0 0 0 rgba(255,0,0,0.9)",
  864. position: "fixed",
  865. bottom: 0,
  866. right: 0,
  867. padding: "4px",
  868. width: "200px"
  869. }, document.body);
  870. applyStyles(el, {
  871. transition: delay + "ms box-shadow linear, " + delay + "ms border-color linear",
  872. "box-shadow": "inset 0 0 0 rgba(0,255,0,0.5)",
  873. "border-color": "rgba(0,255,0,0)"
  874. });
  875. cancelButton.innerHTML = cancelCaption;
  876. var canceled = false;
  877. var timer = setTimeout(function() {
  878. if(!canceled) el.click();
  879. }, delay + 10);
  880. setTimeout(function() {
  881. cancelButton.style.boxShadow = "inset 208px 0 0 rgba(255,0,0,1)";
  882. el.style.boxShadow = "inset " + el.scrollWidth + "px 0 0 rgba(0,255,0,0.3)";
  883. el.style.borderColor = "rgba(0,255,0,1)";
  884. }, 10);
  885. cancelButton.onclick = function() {
  886. canceled = true;
  887. if (oncancel) oncancel();
  888. document.body.removeChild(cancelButton);
  889. };
  890. return timer;
  891. };
  892.  
  893. if (window.name == "mainpane") {
  894. if (!document) throw new Error("no document");
  895. if (!document.body) throw new Error("document has no body");
  896. var quickAFLabel = crel("label", {
  897. "position": "fixed",
  898. "top": 0,
  899. "right": "64px",
  900. "background": "#eee"
  901. }, document.body);
  902. var quickAF = crel("input", {}, quickAFLabel);
  903. quickAF.id = "quickAF";
  904. quickAF.checked = getOption("autoFight");
  905. quickAF.type = "checkbox";
  906. quickAF.onchange = function(){
  907. if(!quickAF.checked && autoFightTimer !== null){
  908. clearTimeout(autoFightTimer);
  909. autoFightTimer = null;
  910. }
  911. setOption("autoFight", quickAF.checked);
  912. };
  913. var quickAFText = crel("span", {}, quickAFLabel);
  914. quickAFText.innerHTML = "Auto Fight";
  915. var watchText = getOption("watchText");
  916. if(watchText !== ""){
  917. var watches = watchText.split("|");
  918. for(var i = 0; i < watches.length; i++){
  919. if(document.body.innerHTML.indexOf(watches[i]) !== -1){
  920. setOption("autoFight", false);
  921. quickAF.checked = false;
  922. if(!Notification){
  923. alert("Text found: " + watches[i]);
  924. } else{
  925. poop("Text found: " + watches[i]);
  926. desktopNotify("[KoLE] Text found: " + watches[i]);
  927. }
  928. break;
  929. }
  930. }
  931. }
  932.  
  933. var openPanel = function() {
  934. if (!top.kole) return;
  935. if (closePanel !== null) {
  936. console.warn("Panel already open?");
  937. return;
  938. }
  939. koleButton.disabled = true;
  940. var panel = crel("div", {
  941. "position": "fixed",
  942. "top": "0",
  943. "left": "0",
  944. "right": "0",
  945. "max-height": "80%",
  946. "box-shadow": "0 0 8px rgba(0,0,0,0.6)",
  947. "border-bottom": "1px #888 solid",
  948. "transform": "scale(0.1,0.1)",
  949. "transform-origin": "100% 0",
  950. "padding": "12px",
  951. "overflow": "auto",
  952. "opacity": 0,
  953. "z-index": 9001,
  954. "background": "#eef",
  955. "transition": "320ms all ease-in"
  956. }, document.body);
  957. crel("h1", {
  958. "font-weight": "100"
  959. }, panel).innerHTML = "KoLE Settings";
  960. var settingsTable = crel("table", {
  961. "font-size": "small",
  962. "width": "100%",
  963. }, panel);
  964. settingsTable.setAttribute("cellpadding", 4);
  965. settingsTable.setAttribute("cellspacing", 0);
  966. var tbody = crel("tbody", {}, settingsTable);
  967. var alt = false;
  968. for (var name in configOptions) {
  969. alt = !alt;
  970. var spec = configOptions[name];
  971. var tr = crel("tr", {
  972. background: alt ? "rgba(255,255,255,0.5)" : "transparent"
  973. }, tbody);
  974. var labelCell = crel("td", {
  975. height: "2em",
  976. "width": "200px",
  977. "vertical-align": "middle"
  978. }, tr);
  979. var label = crel("label", {}, labelCell);
  980. label.innerHTML = spec.caption;
  981. label.setAttribute("for", "kole_config_" + name);
  982. var editCell = crel("td", {
  983. "vertical-align": "middle"
  984. }, tr);
  985. controlTypes[spec.control](name, spec, editCell);
  986. if (spec.description) {
  987. var descTr = crel("tr", {
  988. "font-size": "0.8em",
  989. "color": "#666",
  990. background: alt ? "rgba(255,255,255,0.5)" : "transparent"
  991. }, tbody);
  992. var descTd = crel("td", {}, descTr);
  993. descTd.innerHTML = spec.description;
  994. descTd.setAttribute("colspan", 2);
  995. }
  996. }
  997. with(crel("p", {
  998. "font-size": "small"
  999. }, panel)) {
  1000. innerHTML = "Kingdom of Loathing Enhancement <b>alpha</b> by <a href='showplayer.php?who=2362564'>fnoot</a><br>This is an <b>alpha testing</b> version; please report any problems by kmail.";
  1001. }
  1002. var closeButton = crel("button", {
  1003. position: "absolute",
  1004. top: 0,
  1005. right: 0,
  1006. background: "#fff",
  1007. border: "none",
  1008. "font-size": "18px",
  1009. "padding": "0 .8em"
  1010. }, panel);
  1011. applyStyles(closeButton, {
  1012. "background": "#f88"
  1013. }, "hover");
  1014. closeButton.innerHTML = "&#x2716;";
  1015. closePanel = function() {
  1016. closePanel = null;
  1017. koleButton.disabled = false;
  1018. applyStyles(panel, {
  1019. opacity: 0,
  1020. "pointer-events": "none",
  1021. transform: "scale(0.02,0.02)"
  1022. });
  1023. setTimeout(function() {
  1024. document.body.removeChild(panel);
  1025. panel = null;
  1026. }, 1000);
  1027. };
  1028. panel.onscroll = function(e){
  1029. applyStyles(closeButton, {top: panel.scrollTop + "px"});
  1030. };
  1031. closeButton.onclick = closePanel;
  1032. setTimeout(function() {
  1033. applyStyles(panel, {
  1034. opacity: 1,
  1035. transform: "scale(1,1)"
  1036. });
  1037. }, 100)
  1038. };
  1039. var koleButton = crel("button", {
  1040. "position": "fixed",
  1041. "top": "0",
  1042. "right": "0",
  1043. "z-index": 9000,
  1044. "border": "none",
  1045. "background": "#eee",
  1046. "padding": "3px 6px",
  1047. "transition": "400ms transform ease-out, 300ms opacity ease-out, 400ms 300ms color ease-out, 600ms background ease-out",
  1048. "transform-origin": "100% 0",
  1049. "color": "#000"
  1050. }, document.body);
  1051. applyStyles(koleButton, {
  1052. background: "#adf"
  1053. }, "hover");
  1054. applyStyles(koleButton, {
  1055. transition: "400ms transform ease-in, 300ms opacity ease-in, 200ms color ease-in",
  1056. background: "#adf",
  1057. opacity: 0,
  1058. transform: "scale(6,6)",
  1059. color: "rgba(0,0,0,0)"
  1060. }, "disabled");
  1061. with(koleButton) {
  1062. innerHTML = "KoLE";
  1063. onclick = openPanel;
  1064. }
  1065. var autoFightTimer = null;
  1066.  
  1067. if (getOption("autoFight")) {
  1068. (function() { // auto fighting
  1069. if (!top.kole) return;
  1070. var hpPercent = (top.kole.hp / top.kole.maxHp) * 100;
  1071. if (hpPercent < getOption("autoFightMinHp")) {
  1072. setOption("autoFight", false);
  1073. return;
  1074. }
  1075. var links = document.querySelectorAll("a");
  1076. var adventureAgainRegex = /Adventure Again \(|Fight Again \(/;
  1077. for (var i = 0; i < links.length; i++) {
  1078. if (adventureAgainRegex.test(links[i].innerHTML)) {
  1079. autoFightTimer = timedClick(links[i], getOption("autoFightDelay"), "Cancel automatic fighting", function() {
  1080. setOption("autoFight", false);
  1081. quickAF.checked = false;
  1082. });
  1083. return;
  1084. }
  1085. }
  1086. var button12 = document.getElementById("button12");
  1087. if (button12 != null) {
  1088. autoFightTimer = timedClick(button12, getOption("autoFightDelay"), "Cancel automatic fighting", function() {
  1089. setOption("autoFight", false);
  1090. quickAF.checked = false;
  1091. });
  1092. }
  1093. })();
  1094. }
  1095. if(getOption("autoDaily")){
  1096. var buttons = document.body.querySelectorAll(".bfast");
  1097. if(buttons.length > 0){
  1098. timedClick(buttons[0], 1500, "Cancel Auto Daily", function(){
  1099. setOption("autoDaily", false);
  1100. });
  1101. setTimeout(function(){
  1102. location.reload();
  1103. }, 2500);
  1104. }
  1105. }
  1106. var autoButtons = GM_getValue("autoButtons");
  1107. if(autoButtons && autoButtons !== "")(function(){
  1108. var buttons = document.querySelectorAll("input[type=submit],value");
  1109. var captions = autoButtons.split(/[\r\n]+/);
  1110. for(var i = 0; i < buttons.length; i++){
  1111. var button = buttons[i];
  1112. var cap = button[button.tagName.toUpperCase() == "INPUT" ? "value" : "innerHTML"];
  1113. if(captions.indexOf(cap) !== -1) timedClick(buttons[i], getOption('autoButtonDelay'), "Cancel Auto-button", function(){
  1114. GM_setValue("autoButtons", captions.filter(function(caption){ return cap !== caption }).join("\n"));
  1115. });
  1116. }
  1117. })();
  1118.  
  1119. setInterval(function() {
  1120. if (getOption("stayLoggedIn")) {
  1121. xhr("main.php", function() {}, false);
  1122. }
  1123. }, 1000 * 60 * 2);
  1124. var scanItems = function() {
  1125. if (top.kole == null) return;
  1126. var itemIds = top.kole.itemIds;
  1127. var learnedItems = false;
  1128. var itemCells = document.querySelectorAll(".stuffbox table.item .ircm");
  1129. for (var i = 0; i < itemCells.length; i++) {
  1130. var cell = itemCells[i];
  1131. var itemName = cell.innerHTML;
  1132. if (typeof itemIds[itemName] == "undefined") {
  1133. learnedItems = true;
  1134. itemIds[itemName] = cell.parentNode.id.replace(/i/, '');
  1135. }
  1136. }
  1137. if (learnedItems) GM_setValue("itemIds", JSON.stringify(itemIds));
  1138. };
  1139. setInterval(scanItems, 3500);
  1140.  
  1141. } // /mainpane
  1142.  
  1143. var chatXhr = function(msg, callback) {
  1144. var url = '/submitnewchat.php?playerid=' + unsafeWindow.playerid + '&pwd=' + unsafeWindow.pwdhash + '&graf=' + encodeURIComponent(msg) + '&j=1';
  1145. xhr(url, function(response) {
  1146. callback(response);
  1147. });
  1148. };
  1149.  
  1150. var getItemCount = function(item, callback) {
  1151. chatXhr("/count " + item, function(res) {
  1152. var countMatches = res.match(/You have (\d+)/);
  1153. if (countMatches && countMatches.length > 0) {
  1154. callback(countMatches[1]);
  1155. } else {
  1156. callback(0);
  1157. }
  1158. });
  1159. };
  1160.  
  1161. var hoverLinks = function() {
  1162. window.addEventListener("load", function() {
  1163. crel("style", {}, document.querySelectorAll("head")[0]).textContent = "a{opacity:0.8;} a:hover{opacity:1;}";
  1164. }, false);
  1165. }
  1166.  
  1167. if (window.name == "menupane") {
  1168. hoverLinks();
  1169. var lastAppliedIconTheme = "";
  1170. var replaceImages = function() {
  1171. var theme;
  1172. if (!unsafeWindow.top.kole) {
  1173. setTimeout(replaceImages, 100);
  1174. return;
  1175. }
  1176. var themeName = getOption("iconTheme");
  1177. if (themeName == lastAppliedIconTheme) return;
  1178. lastAppliedIconTheme = themeName;
  1179. if (themeName == "") {
  1180. theme = {
  1181. icons: {}
  1182. };
  1183. } else {
  1184. if (typeof unsafeWindow.top.kole.iconThemes[themeName] == "undefined") {
  1185. setOption("iconTheme", "");
  1186. console.warn("Can't find icon theme " + themeName);
  1187. return;
  1188. }
  1189. theme = unsafeWindow.top.kole.iconThemes[themeName];
  1190. }
  1191. var imgs = document.querySelectorAll("img");
  1192. document.body.style.background = typeof theme.bg == "undefined" ? "#fff" : theme.bg;
  1193. for (var i = 0; i < imgs.length; i++) {
  1194. if (typeof imgs[i].originalSrc == "undefined") imgs[i].originalSrc = imgs[i].src;
  1195. var src = imgs[i].originalSrc;
  1196. if (typeof theme.icons[src] != "undefined") {
  1197. //var width = imgs[i].width;
  1198. imgs[i].src = theme.icons[src];
  1199. imgs[i].width = 30;
  1200. } else {
  1201. imgs[i].src = imgs[i].originalSrc;
  1202. }
  1203. }
  1204. };
  1205. replaceImages();
  1206. setInterval(replaceImages, 500);
  1207. }
  1208.  
  1209. if (window.name == "charpane") {
  1210. hoverLinks();
  1211. var getHp = function() {
  1212. if (!top.kole) {
  1213. setTimeout(getHp, 200);
  1214. return;
  1215. }
  1216. var blacks = document.querySelectorAll(".black");
  1217. for (var i = 0; i < blacks.length; i++) {
  1218. var sib = blacks[i];
  1219. while (sib && sib.tagName.toLowerCase() != "img") {
  1220. sib = sib.previousSibling;
  1221. }
  1222. if(!sib){
  1223. // compact charpane?
  1224. var tr = blacks[i].parentNode.parentNode; // td > tr
  1225. var tds = tr.querySelectorAll("td");
  1226. var hptds = [].slice.call(tds, 0).filter(function(td){
  1227. var imgs = td.querySelectorAll("img");
  1228. return [].slice.call(imgs, 0).filter(function(img){
  1229. return img.title == "Hit Points";
  1230. }).length > 0;
  1231. });
  1232. if(hptds.length > 0){
  1233. var blacks = hptds[0].nextSibling.querySelectorAll(".black");
  1234. if(blacks.length > 0){
  1235. var matches = blacks[0].innerHTML.match(/(\d+)&nbsp;\/&nbsp;(\d+)/);
  1236. top.kole.hp = matches[1];
  1237. top.kole.maxHp = matches[2];
  1238. var percent = (top.kole.hp / top.kole.maxHp) * 100;
  1239. if (percent < getOption("autoFightMinHp")) {
  1240. blacks[i].style.background = "rgba(255,0,0,0.2)";
  1241. }
  1242. return;
  1243. }
  1244. }
  1245. }
  1246. if (sib && sib.title == "Hit Points") {
  1247. var matches = blacks[i].innerHTML.match(/(\d+)&nbsp;\/&nbsp;(\d+)/);
  1248. top.kole.hp = matches[1];
  1249. top.kole.maxHp = matches[2];
  1250. var percent = (top.kole.hp / top.kole.maxHp) * 100;
  1251. if (percent < getOption("autoFightMinHp")) {
  1252. blacks[i].style.background = "rgba(255,0,0,0.2)";
  1253. }
  1254. return;
  1255. }
  1256. }
  1257. top.kole.hp = 1;
  1258. top.kole.maxHp = 1;
  1259. console.warn("[kole] Couldn't find HP");
  1260. };
  1261. getHp();
  1262. }
  1263.  
  1264. if (window.name == "chatpane") {
  1265. hoverLinks();
  1266.  
  1267. var chatLog = function(s) {
  1268. var activeWindow = unsafeWindow.$$('.chatdisplay:visible')[0];
  1269. var toscroll = (activeWindow.scrollHeight - (activeWindow.scrollTop + activeWindow.offsetHeight) < 4);
  1270. var msg = crel("div", {
  1271. color: "#090"
  1272. });
  1273. msg.innerHTML = "<span style='color:#0c0'><span style='opacity:0.4'>[</span>kole<span style='opacity:0.4'>]</span></span> " + s;
  1274. activeWindow.appendChild(msg);
  1275. if (toscroll) {
  1276. activeWindow.scrollTop = activeWindow.scrollHeight;
  1277. }
  1278. };
  1279. var chatCommands = {
  1280. wiki: function(args) {
  1281. window.open("http://kol.coldfront.net/thekolwiki/index.php/Special:Search?search=" + encodeURIComponent(args.trim()) + "&go=Go");
  1282. },
  1283. qs: function(args) {
  1284. args = args.trim();
  1285. var amountMatches = args.match(/(\d+|\*)\s+([\w\s]+)/);
  1286. if (amountMatches && amountMatches.length > 1) {
  1287. var amount = amountMatches[1];
  1288. args = amountMatches[2];
  1289. } else {
  1290. var amount = 1;
  1291. }
  1292. var item = itemLookup(args);
  1293. if (item === null) {
  1294. chatLog("KoLE doesn't know that item's ID! Teach it by opening your inventory.");
  1295. } else if (item.error) {
  1296. chatLog("" + item.error);
  1297. } else {
  1298. if (amount == "*") {
  1299. chatLog("Retrieving item count");
  1300. getItemCount(item.name, function(count) {
  1301. if (count == 0) {
  1302. chatLog("You don't have any of those!");
  1303. }
  1304. chatLog("Quickselling " + count + " x " + item.name);
  1305. unsafeWindow.dojax("sellstuff.php?action=sell&ajax=1&type=quant&whichitem%5B%5D=" + item.id + "&howmany=" + count + "&pwd=" + unsafeWindow.pwdhash);
  1306. });
  1307. } else {
  1308. chatLog("Quickselling " + amount + " x " + item.name);
  1309. unsafeWindow.dojax("sellstuff.php?action=sell&ajax=1&type=quant&whichitem%5B%5D=" + item.id + "&howmany=" + amount + "&pwd=" + unsafeWindow.pwdhash);
  1310. }
  1311. }
  1312. },
  1313. jump: function(args) {
  1314. chatLog("Weeeeeeeee");
  1315. }
  1316. }
  1317.  
  1318. var previousOnload = window.onload;
  1319. window.onload = function() {
  1320. if (previousOnload) previousOnload.apply(this, arguments);
  1321. var oldForm = document.getElementById('InputForm');
  1322. if (oldForm == null) return;
  1323. oldForm = oldForm.parentNode;
  1324. newForm = oldForm.cloneNode(true);
  1325. newForm.style.background = "red";
  1326. oldForm.parentNode.replaceChild(newForm, oldForm);
  1327. var $inp = unsafeWindow.$$("#graf");
  1328. unsafeWindow.$inp = $inp;
  1329. newForm.onsubmit = function(ev) {
  1330. ev.preventDefault();
  1331. var inp = $inp.val();
  1332. var matches = inp.match(/^\/(\w+)(\s+(.*))?/);
  1333. if (matches && matches.length > 0) {
  1334. var cmd = matches[1];
  1335. if (typeof chatCommands[cmd] != "undefined") {
  1336. $inp.val("");
  1337. chatCommands[cmd].call(this, matches.length > 1 ? matches[2] : undefined);
  1338. return;
  1339. }
  1340. }
  1341. $inp.val(getOption("nullifySword") ? nullifySword($inp.val()) : $inp.val());
  1342. unsafeWindow.submitchat(ev);
  1343. };
  1344. };
  1345. };
  1346.  
  1347. var applyHoverHints = function() {
  1348. var els = document.querySelectorAll("a,img");
  1349. var onclickRegex = /\b(descitem|eff|javascript:poop)\("?([\w\.\?\=]+)"?\b(\s*,\s*(\w+)\b\))?/;
  1350. for (var i = 0; i < els.length; i++) {
  1351. var funcUrls = {
  1352. descitem: "desc_item.php?whichitem=",
  1353. eff: "desc_effect.php?whicheffect=",
  1354. "javascript:poop": ""
  1355. };
  1356. var onclick = els[i].getAttribute("onclick") + "";
  1357. var matches = onclick.match(onclickRegex);
  1358. if (onclick && matches && (matches.length > 0)) {
  1359. if (typeof els[i]['@kole2_hoverhint_init'] == "undefined") {
  1360. els[i]['@kole2_hoverhint_init'] = true;
  1361. els[i].style.cursor = "help";
  1362. els[i].title = "";
  1363. with({
  1364. func: matches[1],
  1365. itemId: matches[2],
  1366. otherPlayer: matches[4]
  1367. }) {
  1368. els[i].addEventListener("mouseenter", function() {
  1369. cancelHint();
  1370. if (!getOption("hoverHints")) return;
  1371. hintElement = this;
  1372. var query = typeof otherPlayer == "undefined" ? itemId : itemId + "&otherplayer=" + otherPlayer;
  1373. setHintTimer(this, function(callback) {
  1374. return extractDescription(funcUrls[func] + query, callback, true);
  1375. });
  1376. }, false);
  1377. els[i].addEventListener("mouseout", function() {
  1378. if (this == hintElement) cancelHint();
  1379. }, false);
  1380. } // with
  1381. } // if not init
  1382. } // if onclick match
  1383. } // for i in els
  1384. }; // applyHoverHints()
  1385.  
  1386. if (window !== top) {
  1387. applyHoverHints();
  1388. setInterval(applyHoverHints, 2500);
  1389. }
  1390.  
  1391. var editIconTheme = function(name, ondone) {
  1392. var startingTheme = getOption("iconTheme");
  1393. var blankSrc = "http://images.kingdomofloathing.com/pixel.gif";
  1394. // temporarily disable theme so Copy Link Location gets the correct URL
  1395. setOption("iconTheme", "");
  1396. // top.kole.userSettings.iconTheme = "";
  1397. var theme = name == "" ? {
  1398. displayName: "New Theme",
  1399. icons: {}
  1400. } : top.kole.iconThemes[name];
  1401. if (typeof theme == "undefined") theme = {
  1402. displayName: "",
  1403. icons: {}
  1404. };
  1405. var editor = crel("div", {});
  1406. var tableRows = [
  1407. ["<b>Original Image Location</b>", "<b>Icon</b>"]
  1408. ];
  1409.  
  1410. var createImg = function(src) {
  1411. var img = crel("img", {
  1412. cursor: "pointer",
  1413. "vertical-align": "middle"
  1414. });
  1415. img.width = 30;
  1416. img.height = 30;
  1417. img.src = src;
  1418. img.onclick = function() {
  1419. var fileSelect = crel("input");
  1420. var img = this;
  1421. fileSelect.setAttribute("type", "file");
  1422. fileSelect.onchange = function(ev) {
  1423. var reader = new FileReader();
  1424. reader.onloadend = function() {
  1425. img.src = reader.result;
  1426. };
  1427. var file = this.files[0];
  1428. reader.readAsDataURL(file)
  1429. };
  1430. fileSelect.click();
  1431. };
  1432. return img;
  1433. };
  1434. var createInput = function(value) {
  1435. var input = crel("input", {
  1436. width: '340px',
  1437. "font-size": "0.8em"
  1438. });
  1439. input.value = value;
  1440. return input;
  1441. };
  1442. var createDeleteButton = function() {
  1443. var deleteButton = crel("button", {
  1444. border: "none"
  1445. });
  1446. deleteButton.innerHTML = "X";
  1447. deleteButton.onclick = function() {
  1448. if (!confirm("Delete this icon?")) return;
  1449. var row = this.parentNode.parentNode.parentNode;
  1450. row.parentNode.removeChild(row);
  1451. reapplyAlt();
  1452. };
  1453. return deleteButton;
  1454. };
  1455.  
  1456. for (var orig in theme.icons) {
  1457. var right = crel("div");
  1458. right.appendChild(createImg(theme.icons[orig]));
  1459. right.appendChild(createDeleteButton());
  1460. tableRows.push([createInput(orig), right]);
  1461. }
  1462.  
  1463. var table = tabulate(tableRows);
  1464. var reapplyAlt = function() {
  1465. var trs = table.querySelectorAll("tr");
  1466. for (var i = 0; i < trs.length; i++) trs[i].style.background = i % 2 == 1 ? "rgba(0,0,0,0.05)" : "transparent";
  1467. };
  1468. reapplyAlt();
  1469. crel("h1", {
  1470. "font-weight": 100,
  1471. margin: "0 0 12px 0"
  1472. }, editor).innerHTML = "Editing " + theme.displayName;
  1473. var uniqueNameInput = crel("input", {
  1474. width: "200px"
  1475. });
  1476. uniqueNameInput.value = name;
  1477. var displayNameInput = crel("input", {
  1478. width: "200px"
  1479. });
  1480. displayNameInput.value = theme.displayName;
  1481. editor.appendChild(tabulate([
  1482. ["Unique name", uniqueNameInput],
  1483. ["Display name", displayNameInput]
  1484. ], "rgba(0,0,0,0.05)"));
  1485.  
  1486.  
  1487. editor.appendChild(table);
  1488. var addButton = crel("button", {
  1489. "float": "right"
  1490. }, editor);
  1491. addButton.innerHTML = "+";
  1492. addButton.onclick = function() {
  1493. var tr = crel("tr", {}, table);
  1494. var tdl = crel("td", {}, tr);
  1495. var tdr = crel("td", {}, tr);
  1496. var right = crel("div", {}, tdr);
  1497. tdl.appendChild(createInput(""));
  1498. right.appendChild(createImg(blankSrc));
  1499. right.appendChild(createDeleteButton());
  1500. table.appendChild(tr);
  1501. };
  1502.  
  1503. var p = crel("p", {}, editor);
  1504. p.innerHTML = "Click an image to select a replacement. ";
  1505. var helpLink = crel("a", {}, p);
  1506. helpLink.innerHTML = "Help";
  1507. helpLink.href = "#";
  1508. helpLink.onclick = function() {
  1509. poop("<h1 style='font-weight:100; margin:0 0 12px 0'>Theme Editor Help</h1><ol style='font-size:small'>" + "<li>Click the + button to add replacements</li>" + "<li>Right-click on a topbar icon and choose 'Copy Image Location'</li>" + "<li>Paste into the input on the left</li>" + "<li>Left-click on the icon on the right to choose a replacement</li>" + "<li>Give your theme a unique name and a fancy display name</li>" + "<li>????</li>" + "<li>Profit!</li>" + "</ol>" + "<p>Unique names are like filenames. If you change it to something new you'll create a new theme. If you change it to something that exists, you'll overwrite it.</p>");
  1510. return false;
  1511. };
  1512.  
  1513. var saveButton = crel("Button", {}, editor);
  1514. var saved = false;
  1515. saveButton.innerHTML = "Save";
  1516. saveButton.onclick = function() {
  1517. if (uniqueNameInput.value.trim() == "") {
  1518. poop("Please supply a unique name");
  1519. return;
  1520. }
  1521. if (displayNameInput.value.trim() == "") {
  1522. poop("Please supply a display name");
  1523. return;
  1524. }
  1525. var rows = table.querySelectorAll("tr");
  1526. var theme = {
  1527. displayName: displayNameInput.value,
  1528. icons: {}
  1529. };
  1530. for (var i = 1; i < rows.length; i++) {
  1531. var orig = rows[i].querySelectorAll("input")[0].value;
  1532. var repl = rows[i].querySelectorAll("img")[0].src;
  1533. if (orig && repl != blankSrc) {
  1534. theme.icons[orig] = repl;
  1535. }
  1536. }
  1537. top.kole.iconThemes[uniqueNameInput.value] = theme;
  1538. saveIconThemes();
  1539. saved = true;
  1540. editorPoop.close();
  1541. };
  1542.  
  1543. var editorPoop = poop(editor, function() {
  1544. if (saved) {
  1545. if (uniqueNameInput.value != startingTheme) {
  1546. setOption("iconTheme", startingTheme);
  1547. }
  1548. if (closePanel) closePanel();
  1549. openPanel();
  1550. } else {
  1551. setOption("iconTheme", startingTheme);
  1552. }
  1553. if (ondone) ondone();
  1554. });
  1555. };
  1556.  
  1557. var exportIconTheme = function(name) {
  1558. var theme = top.kole.iconThemes[name];
  1559. if (!theme) throw new Error("No such theme: " + name);
  1560. var exported = {
  1561. displayName: theme.displayName,
  1562. uniqueName: name,
  1563. icons: theme.icons
  1564. };
  1565. return JSON.stringify(exported);
  1566. };
  1567.  
  1568. var importIconTheme = function(onclose) {
  1569. var pop = crel("div");
  1570. crel("h1", {
  1571. margin: "0 0 12px 0",
  1572. "font-weight": "100"
  1573. }, pop).innerHTML = "Import Icon Theme";
  1574. var textarea = crel("textarea", {
  1575. "width": "100%",
  1576. height: "200px",
  1577. background: "#fff"
  1578. }, pop);
  1579. textarea.placeholder = "Paste the theme code here";
  1580. var button = crel("button", {}, pop);
  1581. button.innerHTML = "Import";
  1582. button.onclick = function() {
  1583. try {
  1584. if (textarea.value.trim().substr(0, 1) !== "{") throw new Error("Import data must start with {");
  1585. var theme = JSON.parse(textarea.value);
  1586. var name = theme.uniqueName;
  1587. if (!theme.icons) throw new error("Missing theme.icons");
  1588. top.kole.iconThemes[name] = theme;
  1589. saveIconThemes();
  1590. importPoop.close();
  1591. if (name == getOption("iconTheme")) {
  1592. // current theme was changed - reset current to vanilla to clear menupane's cache
  1593. // alternatively, cache by theme obj rather than name?
  1594. top.kol.userSettings.iconTheme = "";
  1595. }
  1596. if (closePanel) {
  1597. closePanel();
  1598. openPanel();
  1599. }
  1600. } catch (ex) {
  1601. console.warn(ex.message);
  1602. poop("Oops! That theme code doesn't seem quite right!<br><code style='font-size:0.8em'>" + ex.message + "</code>");
  1603. return;
  1604. }
  1605. };
  1606. var importPoop = poop(pop, onclose);
  1607. };
  1608.  
  1609. })();