Advertisement
Guest User

Untitled

a guest
Nov 20th, 2018
153
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 65.54 KB | None | 0 0
  1. "use strict";
  2. var conn, output, input, debugtrack, gameCharacter;
  3. var c = {};
  4. //-----Protocol Code
  5. function initAJAX(profile) {
  6. conn = {
  7. queue: [],
  8. busy: false,
  9. send: function (message) {
  10. this.queue.push(message);
  11. this.push();
  12. },
  13. push: function () {
  14. if (this.busy != false) {
  15. return false;
  16. }f
  17. if (this.queue.length < 1) {
  18. return false;
  19. }
  20. this.busy = this.queue[0];
  21. this.queue.shift();
  22. var x;
  23. if (window.XMLHttpRequest) { //Normal browsers
  24. x=new XMLHttpRequest();
  25. } else { //IE5/6
  26. x=new ActiveXObject('Microsoft.XMLHTTP');
  27. }
  28. x.onreadystatechange=function() {
  29. if (x.readyState==4) {
  30. if (x.status==200) {
  31. debugMsg("<span style='color: blue;'>SAFE RESPONSE:</span> " + safe_tags_replace(x.responseText), "received");
  32. doReceive(x.responseText);
  33. conn.busy = false;
  34. conn.push();
  35. } else {
  36. printScreened("<span style='color: red;'>ERROR:</span> HTTP code " + x.status, "connection error");
  37. }
  38. }
  39. }
  40. x.open("POST",profile.path, true);
  41. x.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  42. x.send("m="+this.busy);
  43. return this.busy;
  44. }
  45. };
  46. printScreened("AJAX connection initialized.", "connection");
  47. }
  48. function initWebSocket(profile) {
  49. c = profile;
  50. if (conn) {
  51. writeToConsole('Closing pre-existing connection.');
  52. wsCloseIfOpen.close(1001, 'Closing previous connection.');
  53. }
  54. if (!window.WebSocket) {
  55. printScreened("<span style='color: red;'>ERROR:</span> Your browser does not support WebSockets, or has them currently disabled.", "connection error");
  56. return false;
  57. }
  58. var wsuri = profile.protocol + "://" + profile.server + ":" + profile.port + profile.path;
  59. printUnscreened("Starting connection to: " + wsuri, "connection debug");
  60. try {
  61. conn = new WebSocket(wsuri);
  62. } catch (e) {
  63. if(e.name === "SecurityError") {
  64. printScreened("<span style='color: red;'>ERROR:</span> Your browser has blocked the connection according to its security rules. This might be caused by opening the client in an usual way. Please close this window and go to <a href='https://www.skotos.net/'>https://www.skotos.net/</a> to try again, or email <a href='mailto:ce@skotos.net'>ce@skotos.net</a> if this error persists.", "connection error");
  65. } else {
  66. printScreened("<span style='color: red;'>ERROR:</span> Your browser encountered an error while trying to connect. Please close this window and go to <a href='https://www.skotos.net/'>https://www.skotos.net/</a> to try again, or email <a href='mailto:ce@skotos.net'>ce@skotos.net</a> with the information below if this error persists.<br><br><span style='color: grey;'>Additional error info:<br>"+safe_tags_replace(e.name)+"<br>"+safe_tags_replace(e.message)+"</span>", "connection error");
  67. }
  68. return false;
  69. }
  70. //conn.binaryType = 'arraybuffer';
  71. if (true) {
  72. addEvent(conn, 'open', onWSOpen);
  73. addEvent(conn, 'close', onWSClose);
  74. addEvent(conn, 'message', onWSMessage);
  75. addEvent(conn, 'error', onWSError);
  76. } else {
  77. conn.onopen = function(evt) { onWSOpen(evt) };
  78. conn.onclose = function(evt) { onWSClose(evt) };
  79. conn.onmessage = function(evt) { onWSMessage(evt) };
  80. conn.onerror = function(evt) { onWSError(evt) };
  81. }
  82. }
  83. function onWSOpen(evt) {
  84. window.onbeforeunload = function(evt) { wsCloseIfOpen(4001, "Window unloading."); };
  85. connConnected();
  86. }
  87. function onWSClose(evt) {
  88. connDisconnected();
  89. }
  90. function ab2str(buf) {
  91. return String.fromCharCode.apply(null, new Uint16Array(buf));
  92. }
  93. function onWSMessage(evt) {
  94. if (evt.data) {
  95. if(evt.data instanceof Blob) {
  96. wsProcessBlob(evt.data);
  97. } else if(evt.data instanceof ArrayBuffer) {
  98. wsProcessArrayBuffer(evt.data);
  99. } else {
  100. wsProcessText(evt.data);
  101. }
  102. } else {
  103. debugMsg("<span style='color: blue;'>NO DATA IN RESPONSE</span> ", "received");
  104. }
  105. }
  106. function wsProcessBlob(blb) {
  107. var reader = new FileReader();
  108. addEvent(reader, "loadend", function() {
  109. doReceive(reader.result);
  110. });
  111. reader.readAsText(blb);
  112. }
  113. function wsProcessArrayBuffer(buf) {
  114. writeToConsole("buffer");
  115. doReceive(txt);
  116. }
  117. function wsProcessText(txt) {
  118. writeToConsole(txt);
  119. doReceive(txt);
  120. }
  121. function onWSError(evt) {
  122. console.log(evt);
  123. printScreened("<span style='color: red;'>WS ERROR:</span> The connection to the server encountered an error. There may be an issue with your internet connection, or the game server may be restarting or temporarily offline. You can wait a moment and try again, check your internet connection, or visit <a href='https://www.skotos.net/'>https://www.skotos.net/</a> and the forums to connfirm that the game server is up. <br><br><span style='color: grey;'>Additional error info:<br>"+safe_tags_replace(evt.name)+"<br>"+safe_tags_replace(evt.message)+"</span>", "connection error");
  124. }
  125. function wsCloseIfOpen(code, message) {
  126. if (conn && conn.readyState < 2) {
  127. conn.close(code, message);
  128. }
  129. }
  130. //-----Generic Code
  131. var tagsToReplace = {"&":"&amp;", "<":"&lt;", ">":"&gt;"};
  132. function replaceTag(tag) {
  133. return tagsToReplace[tag] || tag;
  134. }
  135. function safe_tags_replace(str) {
  136. return typeof(str) === "string" ? str.replace(/[&<>]/g, replaceTag) : str;
  137. }
  138. function replaceBadMSCharacters(text) {
  139. return text.replace(/[\u2018\u2019\u201A]/g, "\'").replace(/[\u201C\u201D\u201E]/g, "\"").replace(/\u2026/g, "...").replace(/[\u2013\u2014]/g, "-").replace(/\u02C6/g, "^").replace(/\u2039/g, "<").replace(/\u203A/g, ">").replace(/[\u02DC\u00A0]/g, " ");
  140. }
  141. function addEvent(obj, evType, fn, useCapture){
  142. if (obj.addEventListener){
  143. obj.addEventListener(evType, fn, useCapture);
  144. } else if (obj.attachEvent){
  145. obj.attachEvent("on"+evType, fn);
  146. } else {
  147. alert("Error attaching event: "+ obj + "; " + evType + "; " + fn + "; " + useCapture);
  148. }
  149. }
  150. function removeEvent(obj, evType, fn, useCapture) {
  151. if (obj.removeEventListener){
  152. obj.removeEventListener(evType, fn, useCapture);
  153. } else if (obj.detachEvent){
  154. obj.detachEvent("on"+evType, fn);
  155. } else {
  156. console.log("Error detaching event.");
  157. }
  158. }
  159. function debugMsg(text, format) {
  160. console.log(text);
  161. //printUnscreened(text, format + " debug");
  162. }
  163. function cancelEvent(e) {
  164. e.noFixFocus = true;
  165. e.stopPropagation ? e.stopPropagation() : (e.cancelBubble=true);
  166. }
  167. if (!Array.prototype.indexOf) {
  168. //Defining this for IE8 and below
  169. Array.prototype.indexOf = function (obj, fromIndex) {
  170. if (fromIndex == null) {
  171. fromIndex = 0;
  172. } else if (fromIndex < 0) {
  173. fromIndex = Math.max(0, this.length + fromIndex);
  174. }
  175. for (var i = fromIndex, j = this.length; i < j; i++) {
  176. if (this[i] === obj)
  177. return i;
  178. }
  179. return -1;
  180. };
  181. }
  182. if ( !Date.prototype.toISOString ) {
  183. //Defining this for IE8 and below
  184. ( function() {
  185. function pad(number) {
  186. var r = String(number);
  187. if ( r.length === 1 ) {
  188. r = '0' + r;
  189. }
  190. return r;
  191. }
  192. Date.prototype.toISOString = function() {
  193. return this.getUTCFullYear()
  194. + '-' + pad( this.getUTCMonth() + 1 )
  195. + '-' + pad( this.getUTCDate() )
  196. + 'T' + pad( this.getUTCHours() )
  197. + ':' + pad( this.getUTCMinutes() )
  198. + ':' + pad( this.getUTCSeconds() )
  199. + '.' + String( (this.getUTCMilliseconds()/1000).toFixed(3) ).slice( 2, 5 )
  200. + 'Z';
  201. };
  202. }() );
  203. }
  204. function addClass(node, cls) {
  205. writeToConsole("+ old: "+node.ClassName+" adding: "+cls);
  206. node.className = (node.ClassName?node.ClassName:"") + " " + cls;
  207. writeToConsole("+ new: "+node.ClassName);
  208. }
  209. function removeClass(node, cls) {
  210. writeToConsole("- old: "+node.ClassName+" removing: "+cls);
  211. if(node && node.className && node.className.indexOf(cls) >= 0) {
  212. var pattern = new RegExp('\\s*' + cls + '\\s*');
  213. node.className = node.className.replace(pattern, ' ');
  214. }
  215. writeToConsole("- new: "+node.ClassName);
  216. }
  217. //-----UI Code
  218. //-----Prefs and Init
  219. var prefs;
  220. var pref_options = {
  221. arrow_behavior: {
  222. cat: "control",
  223. type: ["options"],
  224. desc: "How the up/down arrow keys behave when the input box already has text.",
  225. def: "always_scroll",
  226. opt: {
  227. always_scroll: "Pressing the Up or Down arrow keys by themselves will always scroll through your command history. (This is the behavior of the Alice client.)",
  228. current_first: "Pressing the Up or Down arrow keys will take you to the beginnging or end of the current line; or, if you are already there, then scroll through your command history. The Ctrl key will force it to always scroll through your command history. (This is the behavior of the Zealotry client.)"
  229. }
  230. },
  231. horizontal_scroll: {
  232. cat: "layout",
  233. type: ["options"],
  234. desc: "Management of the horizontal scroll bar for the output window.",
  235. def: "normal",
  236. opt: {
  237. normal: "The horizontal scroll bar is visible if you have any extra-long lines in your scrollback.",
  238. force_wrap: "Extra long-lines are forced to wrap, even if it breaks formatting. Prevents horizontal scrollbars.",
  239. force_chop: "Extra-long lines have the extra length chopped off (except in logs). Probably not a good idea. Prevents horizontal scrollbars.",
  240. automatic: "The horizontal scroll bar is visible if your CURRENT view has any extra-long lines. (Except when scrolling up, in which case it is visible if anything in your scrollback needs it.)",
  241. full_adaptive: "Same as automatic, but keeps working when you scroll up. May slow down client. Experimental. Not yet implemented."
  242. },
  243. onChange: function(old) {
  244. var wrap = "pre";
  245. var flow = "auto";
  246. switch (prefs.horizontal_scroll) {
  247. case "normal":
  248. wrap = "pre";
  249. flow = "auto";
  250. break;
  251. case "force_wrap":
  252. wrap = "pre-wrap";
  253. flow = "auto";
  254. break;
  255. case "force_chop":
  256. wrap = "pre";
  257. flow = "hidden";
  258. //make output not have scrollbars
  259. break;
  260. case "automatic":
  261. wrap = "pre";
  262. flow = scrolledToBottom ? "hidden" : "auto";
  263. break;
  264. case "full_adaptive":
  265. wrap = "pre";
  266. flow = "hidden";
  267. break;
  268. }
  269. wrap = "#output pre {white-space:" + wrap + ";}";
  270. flow = "#output {overflow-x: " + flow + ";}";
  271. cssMods.pre_wrap = setStyle(wrap+" "+flow,cssMods.pre_wrap);
  272. if (old==="automatic") {
  273. removeEvent(window, "resize", updateAutomaticScroll);
  274. removeEvent(output, "scroll", updateAutomaticScroll);
  275. } else if (old==="full_adaptive") {
  276. removeEvent(window, "resize", updateAdaptiveScroll);
  277. removeEvent(output, "scroll", updateAdaptiveScroll);
  278. }
  279. if (prefs.horizontal_scroll==="automatic") {
  280. currentlyAdjustingScroll = false;
  281. currentAdjustingMode = false;
  282. updateAutomaticScroll();
  283. addEvent(window, "resize", updateAutomaticScroll, {passive:true});
  284. addEvent(output, "scroll", updateAutomaticScroll, {passive:true});
  285. } else if (prefs.horizontal_scroll==="full_adaptive") {
  286. currentlyAdjustingScroll = false;
  287. currentAdjustingMode = false;
  288. updateAdaptiveScroll();
  289. addEvent(window, "resize", updateAdaptiveScroll, {passive:true});
  290. addEvent(output, "scroll", updateAdaptiveScroll, {passive:true});
  291. }
  292. }
  293. },
  294. continuous_animations: {
  295. cat: "styling",
  296. type: ["options"],
  297. desc: "What version to play of animations that stay for a while.",
  298. def: "subtle_only",
  299. dis: true,
  300. opt: {
  301. subtle_only: "Don't play strong/distracting versions of continuous animations, but do play subtle versions when available.",
  302. subtle_preferred: "Play the subtle versions of continuous animated effects when available, or stronger versions if that'all that's available.",
  303. bold_preferred: "Play the strongest, most animated and/or most distracting versions of continuous animations available.",
  304. none: "Do not play continuous animated effects."
  305. }
  306. },
  307. brief_animations: {
  308. cat: "styling",
  309. type: ["options"],
  310. desc: "What version to play of animations that only appear for a moment.",
  311. def: "subtle_preferred",
  312. dis: true,
  313. opt: {
  314. subtle_only: "Don't play strong/distracting versions of brief animations, but do play subtle versions when available.",
  315. subtle_preferred: "Play the subtle versions of brief animated effects when available, or stronger versions if that'all that's available.",
  316. bold_preferred: "Play the strongest, most animated and/or most distracting versions of brief animations available.",
  317. none: "Do not play brief animated effects."
  318. }
  319. },
  320. command_history_lines: {
  321. cat: "control",
  322. type: ["integer", "nonnegative"],
  323. desc: "Number of recent commands to remember for re-entry. High numbers may affect client lag.",
  324. on0: "No limit",
  325. def: 100,
  326. onChange: function(old) { while(prefs.command_history_lines && commandHistory.length > prefs.command_history_lines) commandHistory.shift(); if (historyPosition > commandHistory.length) historyPosition = commandHistory.length;}
  327. },
  328. output_click: {
  329. cat: "control",
  330. type: ["options"],
  331. desc: "Whether to snap the cursor to the input box when you click the output area.",
  332. def: "focus_input",
  333. opt: {
  334. focus_input: "When the output area (scrollback) is clicked, keep the focus on the input box, so you can keep typing without needing to click the input box. Selecting text in the output area overrides this, putting focus on the output area (so that the text can be copied, etc.)",
  335. focus_output: "When the output area (scrollback) is clicked, move focus to the output area. This disables typing until the input box is clicked or the tab key is pressed, but may make the text easier to interact with in some browsers."
  336. }
  337. },
  338. scrollback_count: {
  339. cat: "layout",
  340. type: ["integer", "nonnegative"],
  341. desc: "Number of 'elements' (lines) to keep in scrollback. Very high numbers may affect client lag after extended play.",
  342. def: 0,
  343. on0: "No limit",
  344. onChange: function(old) { while(prefs.scrollback_count && output.childNodes.length > prefs.scrollback_count) output.removeChild(output.firstChild);}
  345. },
  346. local_echo: {
  347. cat: "layout",
  348. type: ["options"],
  349. desc: "Whether to show the commands you type, in the output area after you send them.",
  350. def: "on",
  351. opt: {
  352. on: "Commands you type appear in the output area after you send them.",
  353. off: "Commands you type are not shown at all after you send them.",
  354. temporary: "Commands you type are shown after sending them, until something new is received from the server. This makes the command visible if there is lag, but replaces it with the result once it arrives."
  355. }
  356. },
  357. echo_color: {
  358. cat: "styling",
  359. type: ["color", "noquotes"],
  360. desc: "A custom color for the local echo - your commands as they appear in the output window (if they are visible). Color names or hex codes may be used.",
  361. def: "",
  362. onChange: function(old) {
  363. var x = prefs.echo_color.replace(/['"]+/g,'');
  364. if (x) {
  365. x = "#output .inputecho {color:" + x+";}";
  366. cssMods.echo_color = setStyle(x, cssMods.echo_color);
  367. }
  368. }
  369. },
  370. theme: {
  371. cat: "styling",
  372. type: ["options"],
  373. desc: "The colors and styles used for text.",
  374. def: "light",
  375. opt: {
  376. light: "A light background wtih dark text. The standard Skotos appearance.",
  377. dark: "A dark background with light text. Considered by many to be easier on the eyes.",
  378. dark_hc: "A high-contrast variant of the dark theme.",
  379. terminal: "Old style - everything in small, monospace fonts with a dark background. Doesn't work well with most font_face settings.",
  380. },
  381. onChange: function(old) { setActiveStyleSheet(prefs.theme); }
  382. },
  383. font_face: {
  384. cat: "styling",
  385. type: ["string", "noquotes"],
  386. desc: "The custom font of the main text area. Can be any font installed on your system and available to your browser, but must be spelled correctly and the exact name it is installed as.",
  387. def: "",
  388. onChange: function(old) {
  389. var x = prefs.font_face.replace(/['"]+/g,'');
  390. if (x) x = "#core {font-family:\"" + x + "\", minionpro-regular, \"Open Sans\", Roboto, Lato, Verdana, sans-serif;}"
  391. cssMods.font_face = setStyle(x, cssMods.font_face);
  392. }
  393. },
  394. keep_last_command: {
  395. cat: "control",
  396. type: ["options"],
  397. desc: "What the input area does after you send a command.",
  398. def: "off",
  399. opt: {
  400. off: "After sending a command, the input area is cleared, ready for the next command.",
  401. on: "After sending a command, that command is kept in the input area, highlighted. As a result, pressing Enter again repeats that command. If you just start typing instead, the previous command is replaced with what you type."
  402. }
  403. },
  404. highlight_to_copy: {
  405. cat: "control",
  406. type: ["options"],
  407. desc: "Automatically copies highlighted text in the output area to the clipboard (similar to a unix terminal). Not supported by all browsers.",
  408. def: "off",
  409. opt: {
  410. off: "Highlighting text in the output window does nothing.",
  411. on: "Highlighting text in the output window copies it to the clipboard."
  412. },
  413. onChange: function(old) {
  414. if (prefs.highlight_to_copy=="on") {
  415. addEvent(output, "mouseup", copyOutputSelection, {passive:true});
  416. } else {
  417. removeEvent(output, "mouseup", copyOutputSelection, {passive:true});
  418. }
  419. }
  420. },
  421. autolog: {
  422. cat: "system",
  423. type: ["options"],
  424. desc: "Automatically start saving log files when the client starts.",
  425. def: "off",
  426. dis: true,
  427. opt: {
  428. off: "Do not automatically save logs.",
  429. localstorage: "Automatically save logs in the browser's local storage. Note that the browser will automatically delete these logs at certain events (such as clearing cache/history, or when memory is needed on mobile devices), so they should be regularly backed up elsewhere.",
  430. filesystem: "Automatically save logs to the filesystem (usually hard drive or equivalent). Requires some sort of addon/extension/plugin to enable, but when available, is less likely to run into space/deltion issues than localstorage. NOT YET ENABLED."
  431. }
  432. },
  433. prompt_savelog: {
  434. cat: "system",
  435. type: ["options"],
  436. desc: "Automatically prompt to save a log upon disconnect. (Does not work if the client is refreshed/closed without properly disconnecting; be sure to use EXIT or similar commands to disconnect first.)",
  437. def: "off",
  438. opt: {
  439. off: "Do not automatically prompt to save logs.",
  440. on: "Prompt to save log on disconnect"
  441. }
  442. },
  443. logging_path: {
  444. cat: "system",
  445. type: ["string"],
  446. desc: "For logs saved to the filesystem, this is the directory the logs are saved in. NOTE: FILESYSTEM LOGGING NOT YET ENABLED.",
  447. def: "",
  448. dis: true,
  449. },
  450. logging_format: {
  451. cat: "system",
  452. type: ["options"],
  453. desc: "Format to save logs in. HTML preserves some formatting better.",
  454. def: "html",
  455. opt: {
  456. html: "Save the logs as non-styled HTML, with layout preserved. These are best viewed in a browser.",
  457. plain: "Convert logs to plaintext. These lose some layout information, but can be viewed in any text editor."
  458. }
  459. },
  460. command_history_min: {
  461. cat: "control",
  462. type: ["integer", "nonnegative"],
  463. desc: "The minimum length a line must be to be saved in the command history. Setting it to 2, for example, lets it ignore n, s, e, w, etc., so that more important lines are kept accessible.",
  464. def: 1
  465. },
  466. text_size: {
  467. cat: "styling",
  468. type: ["integer", "nonnegative"],
  469. desc: "The size (in px) of the font used for the main display. Common values are 10-24; most browsers default to 14 or 16. Note that most browsers have a built-in way to adjust the size of the entire client, usually with key combinations like Ctrl++, Ctrl+- and Ctrl+0.",
  470. def: 0,
  471. on0: "Your browser's default",
  472. onChange: function(old) {var x = ((prefs.text_size > 7 && prefs.text_size < 501)?"#core {font-size:"+prefs.text_size+"px;}":""); cssMods.text_size = setStyle(x,cssMods.text_size);}
  473. },
  474. input_minheight: {
  475. cat: "layout",
  476. type: ["integer", "nonnegative"],
  477. desc: "The default size (in lines) of the area used for input. Will automatically cap at half the client height.",
  478. def: Math.max(2, 1 + Math.ceil((window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight) / (window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth))),
  479. onChange: function(old) {recalcInputLineHeight(true);}
  480. },
  481. hotkeys_enabled: {
  482. cat: "control",
  483. type: ["options"],
  484. desc: "Whether to have hotkeys (keys that immediately do something when pressed) enabled. These are used primarily for numpad navigation without needing to press Enter.",
  485. def: "off",
  486. opt: {
  487. off: "No hotkeys are enabled.",
  488. when_blank: "Hotkeys are only enabled when there's nothing typed in the input window.",
  489. always_on: "Hotkeys are enabled at all times."
  490. }
  491. },
  492. hide_sidebar: {
  493. cat: "layout",
  494. type: ["options"],
  495. desc: "Whether to hide one of the sidebars.",
  496. def: "none",
  497. opt: {
  498. none: "Show both sidebars.",
  499. auto: "Automatically hide the left sidebar if the client is very narrow. Not yet implemented.",
  500. left: "Hide the left sidebar.",
  501. right: "Hide the right sidebar."
  502. },
  503. onChange: function(old) {
  504. var x = "";
  505. var y = "";
  506. if (prefs.hide_sidebar=="left"||prefs.hide_sidebar=="right") {
  507. x = "#"+prefs.hide_sidebar+" {display:none;}";
  508. y = "#core {max-width: calc(100% - "+document.getElementById("right").offsetWidth+"px)}";
  509. }
  510. cssMods.hide_sidebar_bar = setStyle(x,cssMods.hide_sidebar_bar);
  511. cssMods.hide_sidebar_core = setStyle(y,cssMods.hide_sidebar_core);
  512. }
  513. }
  514. };
  515. //pref_options.sort(function(a, b) {
  516. // return a.cat > b.cat;
  517. //});
  518. function changePrefUI(pref) {
  519. //Todo: Track temporary changes for undo/save.
  520. setPref(pref, document.getElementById(pref).value, true);
  521. }
  522. function setPref(pref, val, ignore_same) {
  523. pref = pref.toLowerCase();
  524. if (typeof val == "string") {
  525. val = val.toLowerCase();
  526. }
  527. var info = pref_options[pref];
  528. if (!info) {
  529. return printUnscreened("There is no client preference named '" + safe_tags_replace(pref) +"'.", "client usererror");
  530. }
  531. var old = prefs[pref];
  532. if (old==val) {
  533. if (!ignore_same) {
  534. printUnscreened("The client preference '" + pref + "' is already set to '" + safe_tags_replace(val) + "'.", "client");
  535. }
  536. return;
  537. }
  538. switch (info.type[0]) {
  539. case "integer":
  540. val = parseInt(val, 10);
  541. if ((info.type.indexOf("nonnegative") > -1 && val < 0) || (info.type.indexOf("positive") > -1 && val < 1)) {
  542. return printUnscreened("The value " + safe_tags_replace(val) + " is too low for " + pref +".", "client usererror");
  543. }
  544. if ((info.type.indexOf("nonzero") > -1 && val == 0)) {
  545. return printUnscreened("The value for " + pref +" cannot be zero.", "client usererror");
  546. }
  547. break;
  548. case "options":
  549. if (!info.opt[val]) {
  550. return printUnscreened("The value " + safe_tags_replace(val) + " is not a valid option for " + pref +".", "client usererror");
  551. }
  552. break;
  553. case "string":
  554. if(info.type.indexOf("noquotes") > -1) {
  555. val = val.replace(/['"]+/g,'');
  556. }
  557. break;
  558. case "color":
  559. val = val.replace(/['"]+/g,'');
  560. break;
  561. default:
  562. return printUnscreened("Client error for preference " + pref + ".", "client error");
  563. }
  564. prefs[pref] = val;
  565. if (info.onChange) {
  566. info.onChange(old);
  567. }
  568. printUnscreened("Client Preference '" + pref + "' changed to '" + safe_tags_replace(val) + "'.", "client");
  569. }
  570. function openSettings() {
  571. showPrefs();
  572. }
  573. function saveSettings(clientOnly) {
  574.  
  575. }
  576. function createSettingsInterface() {
  577. var preference_names = Object.keys(pref_options);
  578. var cats = [];
  579. var name, cat, ih, classes, current_value, valid_values, val, info;
  580. var cache = {};
  581. //addComponent(newID, parentID, newClass, clickFunction, clickArgs, newContents, title)
  582. addComponent("settings-ui", "body");
  583. addComponent("settings-tabtitles", "settings-ui");
  584. addComponent("settings-tabs", "settings-ui");
  585. addComponent("settings-footer", "settings-ui");
  586. addComponent("settings-savelocal", "settings-footer", false, saveSettings, [true], "<button>Client-Only Save</button>");
  587. addComponent("settings-save", "settings-footer", false, saveSettings, [false], "<button>Save Settings</button>");
  588. for (var i=0;i<preference_names.length;i++) {
  589. name = preference_names[i];
  590. info = pref_options[name];
  591. current_value = prefs[name];
  592. cat = info.cat;
  593. if (cats.indexOf(cat) === -1) {
  594. cats.push(cat);
  595. addComponent("settingstabtitle-"+cat, "settings-tabtitles", "tabtitle", showTab, [cat], cat);
  596. addComponent("settingstab-"+cat, "settings-tabs", "tabbody");
  597. cache[cat] = "<table><caption>" + cat + " Preferences</caption><th>Preference</th><th>Value</th><th>Description</th>";
  598. }
  599. ih = "<tr><td><label for=\"setting-"+name+"\">"+name+"</label><td>";
  600. switch (info.type[0]) {
  601. case "options":
  602. ih += "<select id=\"setting-"+name+"\">";
  603. valid_values = Object.keys(info.opt);
  604. for (var j=0;j<valid_values.length;j++) {
  605. val = valid_values[j];
  606. ih += "<option title=\"" + safe_tags_replace(info.opt[valid_values[j]]) + "\"" + (val===info.def?" class=\"client-defaultopt\"":"") + (val===current_value?" selected":"") + ">" + safe_tags_replace(val) + "</option>";
  607. }
  608. break;
  609. case "color":
  610. //Todo: Abstract out the creation of span tags that identify current and default values, so the code's not being repeated so much.
  611. ih += "<input id=\"setting-"+name+"\" type=\"color\" defaultvalue=\""+info.def+"\" value=\""+current_value+"\">";
  612. break;
  613. case "integer":
  614. default:
  615. ih += "<input id=\"setting-"+name+"\" type=\"text`\" defaultvalue=\""+info.def+"\" value=\""+current_value+"\">";
  616. }
  617. ih += "</td><td>"+safe_tags_replace(info.desc)+"</td></tr>";
  618. cache[cat] += ih;
  619. }
  620. console.log(cats);
  621. for (cat in cats) {
  622. cat = cats[cat];
  623. console.log("About to fetch: settingstab-"+cat);
  624. console.log(document.getElementById("settingstab-"+cat));
  625. document.getElementById("settingstab-"+cat).innerHTML = cache[cat];
  626. console.log("Done with: settingstab-"+cat);
  627. }
  628. console.log("D");
  629. }
  630. function showTab(tab) {
  631. alert(tab);
  632. var t, title, contents;
  633. if (typeof(this) === "object" && this && this.className) {
  634. title = this;
  635. tab = this.id.substring(17);
  636. } else {
  637. title = document.getElementById("settingstabtitle-"+tab);
  638. }
  639. contents = document.getElementById("settingstab-"+tab);
  640. if (title.className.indexOf("activetabtitle") >= 0) return;
  641. for (t in document.getElementById("settings-tabtitles").children) {
  642. removeClass(t, "activetabtitle");
  643. }
  644. for (t in document.getElementById("settings-tabs").children) {
  645. removeClass(t, "activetab");
  646. }
  647. addClass(title, "activetabtitle");
  648. addClass(contents, "activetab");
  649. }
  650. function showPrefs() {
  651. //var table = ["Client Preferences:"];
  652. var table = ["<hr>"];
  653. var name;
  654. var current_value;
  655. var row;
  656. var info;
  657. var valid_values;
  658. var items;
  659. var classes;
  660. var preference_names = Object.keys(pref_options);
  661. var cat;
  662. var sortorder = {};
  663. for (var i=0;i<preference_names.length;i++){
  664. name = preference_names[i];
  665. console.log(name);
  666. cat = pref_options[name].cat;
  667. console.log(cat);
  668. items = sortorder[cat] || [];
  669. console.log(items);
  670. items.push(name);
  671. sortorder[cat] = items;
  672. console.log(sortorder);
  673. }
  674. console.log(sortorder);
  675. var preference_groups = Object.keys(sortorder);
  676. for (var q=0;q<preference_groups.length;q++) {
  677. cat = preference_groups[q];
  678. console.log(cat);
  679. preference_names = sortorder[cat];
  680. console.log(preference_names);
  681. table.push("<table>", "<caption>Client Preferences - " + cat.toUpperCase() + "</caption>", "<th>Preference</th><th>Settings</th><th>Description</th>");
  682. for (var i=0;i<preference_names.length;i++){
  683. name = preference_names[i];
  684. current_value = prefs[name];
  685. info = pref_options[name];
  686. if (info.dis) continue;
  687. row = "<tr><td class=\"client-optname\">" + safe_tags_replace(name) + "</td><td>";
  688. switch (info.type[0]) {
  689. case "options":
  690. valid_values = Object.keys(info.opt);
  691. items = [];
  692. for (var j=0;j<valid_values.length;j++) {
  693. classes = ((valid_values[j]==current_value?"client-currentopt":"") + (valid_values[j]==info.def?" client-defaultopt":"")).trim();
  694. items.push("<span title=\"" + safe_tags_replace(info.opt[valid_values[j]]) + "\"" + (classes?"class=\"" + classes + "\"":"") + ">" + safe_tags_replace(valid_values[j]) + "</span>");
  695. }
  696. row += items.join(" | ");
  697. break;
  698. case "color":
  699. //Todo: Abstract out the creation of span tags that identify current and default values, so the code's not being repeated so much.
  700. if (current_value == info.def) {
  701. row += "<span class=\"client-currentopt client-defaultopt\">" + safe_tags_replace(String(current_value)) + "<span style=\"background-color:" + safe_tags_replace(String(current_value)) + "\">&nbsp;</span></span>";
  702. } else {
  703. row += "<span class=\"client-currentopt\">" + safe_tags_replace(String(current_value)) + "<span style=\"background-color:" + safe_tags_replace(String(current_value)) + "\">&nbsp;</span></span> (<span class=\"client-defaultopt\">" + safe_tags_replace(String(info.def)) + "<span style=\"background-color:" + safe_tags_replace(String(info.def)) + "\">&nbsp;</span>)</span>)";
  704. }
  705. break;
  706. case "integer":
  707. default:
  708. if (current_value == info.def) {
  709. row += "<span class=\"client-currentopt client-defaultopt\">" + safe_tags_replace(String(current_value)) + (current_value==0&&info.on0?" ["+info.on0+"]":"") + "</span>";
  710. } else {
  711. row += "<span class=\"client-currentopt\">" + safe_tags_replace(String(current_value)) + (current_value==0&&info.on0?" ["+info.on0+"]":"") + "</span>" + " (<span class=\"client-defaultopt\">" + safe_tags_replace(String(info.def)) + (info.def==0&&info.on0?" ["+info.on0+"]":"")+ "</span>)";
  712. }
  713. }
  714. row += "</td><td>" + info.desc + (info.on0?" [0 = "+info.on0+"]":"") + "</td></tr>"
  715. table.push(row);
  716. }
  717. table.push("</table>");
  718. }
  719. table.push("<div></div>");
  720. table.push("<div>Hover over an option for more details. [<span class=\"client-currentopt\">current setting</span>] [<span class=\"client-defaultopt\">default setting</span>]</div>");
  721. table.push("<div>To change a setting, use: <strong>clientpref &lt;preferencename&gt; &lt;value&gt;</strong>. To save settings for next time you open the client, use <strong>clientpref save</strong>.</div><hr>");
  722. table = table.join("");
  723. printScreened(table, "client-info");
  724. }
  725. var hotkey_mapping = {
  726. 97: "go southwest",
  727. 98: "go south",
  728. 99: "go southeast",
  729. 100:"go west",
  730. 101:"look",
  731. 102:"go east",
  732. 103:"go northwest",
  733. 104:"go north",
  734. 105:"go northeast",
  735. 106:"examine here",
  736. 107:"go down",
  737. 109:"go up",
  738. 110:"exits",
  739. 111:"inventory"
  740. }
  741. function savePrefs() {
  742. var toSave = {};
  743. for (var p in prefs) {
  744. if (prefs[p] != pref_options[p].def) {
  745. toSave[p] = prefs[p];
  746. }
  747. }
  748. toSave = JSON.stringify(toSave);
  749. if (localStorage && localStorage.setItem) {
  750. printUnscreened("Attempting to save preferences in local storage...", "client");
  751. localStorage.setItem("prefs", toSave);
  752. if (localStorage.getItem("prefs") != toSave) {
  753. printScreened("Saving preferences to local storage failed.", "client error");
  754. } else {
  755. printScreened("Preferences appear to have been successfully saved.", "client");
  756. }
  757. } else {
  758. printUnscreened("Local storage not found, not saving preferences in local storage.", "client");
  759. }
  760. }
  761. function initPrefs() {
  762. prefs = {};
  763. for (var p in pref_options) {
  764. prefs[p] = pref_options[p].def;
  765. }
  766. if (localStorage && localStorage.getItem) {
  767. loadPrefString(localStorage.getItem("prefs"));
  768. }
  769. setActiveStyleSheet(prefs.theme);
  770. }
  771. function loadPrefString(p) {
  772. p = JSON.parse(p);
  773. for (var pref in p) {
  774. setPref(pref, p[pref], true);
  775. }
  776. }
  777. function init() {
  778. removeEvent(window, 'load', init, {once:true});
  779. output = document.getElementById("output");
  780. input = document.getElementById("commandinput");
  781. addEvent(input, "keydown", keyDown);
  782. addEvent(input, "mouseup", keyUp, {passive:true});
  783. addEvent(input, "keyup", keyUp, {passive:true});
  784. addEvent(input, "focus", restorePos, {passive:true});
  785. addEvent(window, "mouseup", fixFocus, {passive:true});
  786. addEvent(window, "resize", keepScrollPos, {passive:true});
  787. addEvent(output, "mouseup", checkOutputClick);
  788. addEvent(output, "scroll", noteScrollPos, {passive:true});
  789. addEvent(output, "paste", redirectPaste, {passive:true});
  790. initPrefs();
  791. initTheatre();
  792. recalcInputLineHeight();
  793. debugMsg("Client initialized and ready for connection.");
  794. initConnection();
  795. window.focus();
  796. c.raw = window.location == "http://test.skotos.net/orchil/marrach/marrach.htm";
  797. }
  798. //-----General UI
  799. function addComponent(newID, parentID, newClass, clickFunction, clickArgs, newContents, newTitle) {
  800. var parent = (typeof parentID == "object" ? parentID : document.getElementById(parentID));
  801. if (!parent) reportClientError("Cannot find parent " + parentID);
  802. var newComponent = document.createElement('div');
  803. parent.appendChild(newComponent);
  804. if (newID) newComponent.id = newID;
  805. if (newClass) newComponent.className = newClass;
  806. if (newContents) newComponent.innerHTML = newContents;
  807. if (newTitle) newComponent.title = newTitle;
  808. if (clickFunction) {
  809. var fun;
  810. if (typeof(clickFunction) == "string") {
  811. var def = clickFunction+'("'+clickArgs.join('","')+'")';
  812. fun = new Function(def);
  813. //writeToConsole("Writing clickFunction " + def);
  814. } else {
  815. fun = clickFunction;
  816. //writeToConsole("Writing clickFunction " + fun);
  817. newComponent.dataset.clickargs = clickArgs;
  818. }
  819. addEvent(newComponent, "click", fun, {passive:true});
  820. newComponent.style.cursor = "pointer";
  821. }
  822. return newComponent;
  823. }
  824. function connConnected() {
  825. conn.connected = true;
  826. printScreened("CONNECTED", "connection");
  827. input.contentEditable = true;
  828. input.className = input.className.replace(/\bdisabled\b/,'');
  829. input.placeholder = "Enter a command...";
  830. //input.removeAttribute("disabled");
  831. input.disabled = false;
  832. setInputValue("");
  833. fixFocus();
  834. if (!(c.raw)) sendSys("SKOTOS Orchil 0.2.3");
  835. }
  836. function connDisconnected() {
  837. conn.connected = false;
  838. input.contentEditable = false;
  839. input.className += ' disabled';
  840. input.disabled = true;
  841. printScreened("DISCONNECTED", "connection");
  842. input.placeholder = "";
  843. conn = null;
  844. if (prefs.prompt_savelog=="on" && loginDone) {
  845. saveCurrentWindow();
  846. }
  847. }
  848. function fixFocusIfUnselected(e) {
  849. if (prefs.output_click == "focus_output" ||
  850. (typeof window.getSelection != "undefined" && window.getSelection().toString()) ||
  851. (typeof document.selection != "undefined" && document.selection.type == "Text" && document.selection.createRange().text)) {
  852. cancelEvent(e);
  853. }
  854. }
  855. function checkOutputClick(e) {
  856. if (isLeftClick(e)) {
  857. //Todo: Test/debug this.
  858. //cancelEvent(e);
  859. fixFocusIfUnselected(e);
  860. } else {
  861. cancelEvent(e);
  862. }
  863. }
  864. function isLeftClick(e) {
  865. var b;
  866. e = e || window.event;
  867. if ("which" in e) //Gecko/WebKit/Opera/New IE
  868. return e.which < 2;
  869. else if ("button" in e) //Old IE/Opera
  870. return e.button < 2;
  871. return true; //We don't know, so assume yes.
  872. }
  873. function isConnected() {
  874. return input.className.indexOf("disabled") == -1;
  875. }
  876. var currentlyAdjustingScroll;
  877. var currentAdjustingMode;
  878. function updateAutomaticScroll() {
  879. if (currentlyAdjustingScroll || !(prefs.horizontal_scroll in ["automatic","full_adaptive"])) return;
  880. currentlyAdjustingScroll = true;
  881. setTimeout(adjustingScrollTimeout, 100);
  882. }
  883. function adjustingScrollTimeout() {
  884. if(!(prefs.horizontal_scroll in ["automatic","full_adaptive"])) {
  885. currentlyAdjustingScroll = false;
  886. return;
  887. }
  888. var flow = "hidden";
  889. if (scrolledToBottom || prefs.horizontal_scroll == "full_adaptive") {
  890. var bonus = 20; //Todo: Figure out a better value for this.
  891. var heightLeftToCheck = output.scrollHeight + bonus;
  892. var node;
  893. for (var i=output.childNodes.length - 1; i>=0; i--) {
  894. node = output.childNodes[i];
  895. if (node.clientWidth != node.scrollWidth) {
  896. flow = "auto";
  897. break;
  898. }
  899. heightLeftToCheck -= node.scrollHeight;
  900. if (heightLeftToCheck < 0) {
  901. flow = "hidden";
  902. break;
  903. }
  904. }
  905. } else {
  906. flow = "auto";
  907. }
  908. if (flow != currentAdjustingMode) {
  909. cssMods.pre_wrap = setStyle("#output.pre {white-space:pre;} #output {overflow-x:"+flow+";}",cssMods.pre_wrap);
  910. currentAdjustingMode = flow;
  911. }
  912. currentlyAdjustingScroll = false;
  913. }
  914. function updateAdaptiveScroll() {
  915. //Todo: actual adaptive scroll. Until then, just do automatic.
  916. //Adaptive loops through all 'visible' elements always, instead of "all bottom elements if at bottom".
  917. //Note that adaptive can be intensive in times of heavy scroll. Perhaps install a limiter.
  918. //Like start with if (adaptive_scrolling) return; adaptive_scrolling = true;
  919. //Then at the end do a setTimeout to set adaptive_scrolling = false;
  920. updateAutomaticScroll();
  921. }
  922. //-----Input
  923. function fixFocus(e) {
  924. e = e || window.event || {};
  925. if (isConnected() && !e.noFixFocus) {
  926. input.focus();
  927. }
  928. }
  929. function doSend(message, noecho) {
  930. if (!conn.connected) return;
  931. var debug = false;
  932. if (prefs.local_echo === "off") noecho = true;
  933. if(!noecho) {
  934. if (c.promptStr) {
  935. printUnscreened(message, "inputecho");
  936. //test
  937. }
  938. printUnscreened(message, "inputecho");
  939. } else if (debug) {
  940. debugMsg("UI Sending: " + safe_tags_replace(message));
  941. }
  942. scrollToBottom();
  943. if (true) {
  944. if (c.raw) writeToConsole("S:"+message+"\n");
  945. conn.send(message+"\n");
  946. } else {
  947. if (message==="\n") {
  948. console.log("It's just a newline!");
  949. message = [""];
  950. } else {
  951. message = message.replace("\r").split("\n");
  952. }
  953. for (var i=0;i<message.len;i++) {
  954. writeToConsole("S:"+message[i]+"\n");
  955. conn.send(message[i]+"\n");
  956. }
  957. }
  958. fixFocus();
  959. }
  960. function sendCommand() {
  961. var command = replaceBadMSCharacters(getInputValue());
  962. var lc = command.toLowerCase();
  963. if (lc==="clientpref"||lc==="clientprefs") {
  964. showPrefs();
  965. } else if (lc.substring(0,11)==='clientpref ') {
  966. var sppos = command.indexOf(" ", 11);
  967. if (sppos == -1) {
  968. if (command === "clientpref save") {
  969. savePrefs();
  970. } else {
  971. printUnscreened("Usage: clientpref <preferencename> <value> or clientpref save", "client usererror");
  972. }
  973. } else {
  974. setPref(command.substring(11, sppos), command.substring(sppos + 1));
  975. }
  976. } else {
  977. doSend(command);
  978. }
  979. saveHistory(command);
  980. if (prefs.keep_last_command !== "on") {
  981. historyPosition = 0;
  982. //savedInput = ""; -- did this do anything? Looks like null code.
  983. setInputValue("");
  984. resizeInput(true);
  985. } else {
  986. historyPosition = 1;
  987. input.select();
  988. }
  989. }
  990. function saveHistory(line) {
  991. if (line.length >= prefs.command_history_min && commandHistory[commandHistory.length-1] != line) {
  992. commandHistory.push(line);
  993. if (prefs.command_history_lines && commandHistory.length > prefs.command_history_lines) {
  994. commandHistory.shift();
  995. }
  996. }
  997. }
  998. function sendTitle() {
  999. if (this && typeof this == 'object') {
  1000. sendUI(this.title);
  1001. } else {
  1002. reportClientError("Clickies not supported.");
  1003. printScreened("<span style='color: red;'>ERROR:</span> Browser does not support modern event references; clicking some elements will not work.", "client error");
  1004. }
  1005. }
  1006. function sendDataCommand() {
  1007. if (this && typeof this == 'object') {
  1008. sendUI(this["data-command"]);
  1009. } else {
  1010. reportClientError("Clickies not supported.");
  1011. printScreened("<span style='color: red;'>ERROR:</span> Browser does not support modern event references; clicking some elements will not work.", "client error");
  1012. }
  1013. }
  1014. function sendUI(message) {
  1015. //debugMsg("UI SEND: " + message, "sending");
  1016. doSend(message, 1);
  1017. }
  1018. function sendSys(message) {
  1019. var debugMode = false;
  1020. doSend(message, !debugMode);
  1021. }
  1022. function keyDown(e) {
  1023. if (!isConnected()) {
  1024. e.preventDefault();
  1025. return false;
  1026. }
  1027. var k = (window.event?window.event.keyCode:(e?e.which:null));
  1028. if (k===null) return true;
  1029. switch (k) {
  1030. case 13: //Enter by itself sends commands.
  1031. if (noModifier(e)) {
  1032. sendCommand();
  1033. e.preventDefault();
  1034. return false;
  1035. }
  1036. break;
  1037. case 9: //Tabbing to other elements is not useful.
  1038. e.preventDefault();
  1039. return false;
  1040. case 33: //Page Up to scroll up the output screen.
  1041. if (noModifier(e)) {
  1042. outputPageUp();
  1043. e.preventDefault();
  1044. }
  1045. break;
  1046. case 34: //Page Down to scroll down the output screen.
  1047. if (noModifier(e)) {
  1048. outputPageDown();
  1049. e.preventDefault();
  1050. }
  1051. break;
  1052. case 38: //Up Arrow to get the previous command.
  1053. //May not detect "at selection start" for IE.
  1054. //Oh well, not important, and only matters if they change prefs anyway.
  1055. if (e.shiftKey || inputCursorLine() > 1)
  1056. break;
  1057. if (prefs.arrow_behavior == "current_first") {
  1058. if (e.ctrlKey || input.selectionStart==0) {
  1059. commandHistoryPrevious();
  1060. e.preventDefault();
  1061. } else {
  1062. input.selectionStart = 0;
  1063. }
  1064. } else if (noModifier(e)) {
  1065. commandHistoryPrevious();
  1066. e.preventDefault();
  1067. }
  1068. break;
  1069. case 40: //Down arrow to get the next command.
  1070. //May not detect "at selection start" for IE.
  1071. //Oh well, not important, and only matters if they change prefs anyway.
  1072. if (e.shiftKey || inputCursorLine() < inputLines())
  1073. break;
  1074. if (prefs.arrow_behavior == "current_first") {
  1075. if (e.ctrlKey || input.selectionStart==input.value.length) {
  1076. commandHistoryNext();
  1077. e.preventDefault();
  1078. } else {
  1079. input.selectionStart = input.value.length;
  1080. }
  1081. } else if (noModifier(e)) {
  1082. commandHistoryNext();
  1083. e.preventDefault();
  1084. }
  1085. break;
  1086. default:
  1087. if (hotkey_mapping[k] && (prefs.hotkeys_enabled == "always_on" || (prefs.hotkeys_enabled == "when_blank" && !getInputValue()))) {
  1088. doSend(hotkey_mapping[k], true);
  1089. e.preventDefault();
  1090. }
  1091. }
  1092. return true;
  1093. }
  1094. function noModifier(e) {
  1095. return !(e.ctrlKey) && !(e.shiftKey) && !(e.altKey);
  1096. }
  1097. HTMLTextAreaElement.prototype.insertAtCaret = function (text) {
  1098. text = text || '';
  1099. if (document.selection) {
  1100. // IE
  1101. this.focus();
  1102. var sel = document.selection.createRange();
  1103. sel.text = text;
  1104. } else if (this.selectionStart || this.selectionStart === 0) {
  1105. // Others
  1106. var startPos = this.selectionStart;
  1107. var endPos = this.selectionEnd;
  1108. this.value = this.value.substring(0, startPos)+text+this.value.substring(endPos, this.value.length);
  1109. this.selectionStart = startPos + text.length;
  1110. this.selectionEnd = startPos + text.length;
  1111. } else {
  1112. this.value += text;
  1113. }
  1114. };
  1115. function redirectPaste(e) {
  1116. var pastedText = undefined;
  1117. if (window.clipboardData && window.clipboardData.getData) { // IE
  1118. pastedText = window.clipboardData.getData('Text');
  1119. } else {
  1120. var clipboardData = (e.originalEvent || e).clipboardData;
  1121. if (clipboardData && clipboardData.getData)
  1122. pastedText = clipboardData.getData('text/plain');
  1123. }
  1124. if (pastedText) {
  1125. input.insertAtCaret(pastedText);
  1126. input.focus();
  1127. }
  1128. return false;
  1129. }
  1130. var scrolledToBottom = true;
  1131. var inputPos;
  1132. var commandHistory = [];
  1133. var historyPosition = 0;
  1134. var currentInput = "";
  1135. function commandHistoryPrevious() {
  1136. if (historyPosition < commandHistory.length) {
  1137. if (historyPosition == 0) currentInput = getInputValue();
  1138. historyPosition++;
  1139. setInputValue(commandHistory[commandHistory.length - historyPosition]);
  1140. }
  1141. }
  1142. function commandHistoryNext() {
  1143. if (historyPosition) {
  1144. historyPosition--;
  1145. setInputValue(historyPosition ? commandHistory[commandHistory.length - historyPosition] : currentInput);
  1146. }
  1147. }
  1148. function getInputValue() {
  1149. if (input.tagName==="TEXTAREA") {
  1150. input.placeholder = "";
  1151. return input.value;
  1152. } else {
  1153. if (document.body.innerText) {
  1154. writeToConsole("Using innerText");
  1155. return input.innerText;
  1156. } else {
  1157. writeToConsole("Using innerHTML");
  1158. return input.innerHTML.replace(/\&lt;br\&gt;/gi,"\n").replace(/(&lt;([^&gt;]+)&gt;)/gi, "");
  1159. }
  1160. }
  1161. }
  1162. function setInputValue(text) {
  1163. if (input.tagName==="TEXTAREA") {
  1164. input.value = text;
  1165. } else {
  1166. if (document.body.innerText) {
  1167. writeToConsole("Setting innerText");
  1168. input.innerText = text;
  1169. } else {
  1170. writeToConsole("Setting innerHTML");
  1171. input.innerHTML = text.replace(/\n/gi,"\&lt;br\&gt;"); //Todo: Test this.
  1172. }
  1173. }
  1174. }
  1175. function keyUp(e) {
  1176. savePos();
  1177. resizeInput();
  1178. }
  1179. function recalcInputLineHeight(force) {
  1180. var h;
  1181. //Try the easy way first.
  1182. if (input.currentStyle) {
  1183. h = input.currentStyle["line-height"];
  1184. if (h.slice(-2)=="px") {
  1185. h = Number(h.slice(0,-2));
  1186. if (!(h>0)) h = false;
  1187. } else {
  1188. h = false;
  1189. }
  1190. }
  1191. if (!h) {
  1192. //Try the 'hard' way otherwise.
  1193. var tmp = getInputValue();
  1194. setInputValue("\n\n\n\n\n\n\n\n\n");
  1195. h = input.scrollHeight / 10;
  1196. setInputValue(tmp);
  1197. }
  1198. if (force || !(c.inputLineHeight) || h!=c.inputLineHeight) {
  1199. c.inputLineHeight = h;
  1200. input.style.minHeight = (h * prefs.input_minheight) + "px";
  1201. resizeInput();
  1202. }
  1203. }
  1204. function resizeInput(reset) {
  1205. noteScrollPos();
  1206. var lheight = c.inputLineHeight;
  1207. input.style.height = "2.3em";
  1208. input.style.height = Math.min(input.scrollHeight + 4, 105) + "px";
  1209. keepScrollPos();
  1210. }
  1211. function savePos() {
  1212. if (input.tagName!=="TEXTAREA") {
  1213. inputPos = window.getSelection ? window.getSelection().getRangeAt(0) : (document.selection?document.selection.createRange():null);
  1214. }
  1215. }
  1216. function restorePos() {
  1217. if (input.tagName!=="TEXTAREA") {
  1218. if (inputPos !== null && input.innerText) {
  1219. if (window.getSelection) { //non IE and there is already a selection
  1220. var s = window.getSelection();
  1221. if (s.rangeCount > 0)
  1222. s.removeAllRanges();
  1223. s.addRange(inputPos);
  1224. } else if (document.createRange) { //non IE and no selection
  1225. window.getSelection().addRange(inputPos);
  1226. } else if (document.selection) { //IE
  1227. inputPos.select();
  1228. }
  1229. }
  1230. }
  1231. }
  1232. function inputCursorLine() {
  1233. //TODO: Make this work properly with wordwrap, instead of only detecting newline characters.
  1234. return input.value.substr(0, input.selectionStart).split("\n").length;
  1235. }
  1236. function inputLines() {
  1237. //TODO: Match any updates to inputCursorLine
  1238. return input.value.split("\n").length;
  1239. }
  1240. //-----Output
  1241. function printScreened(message, cls) {
  1242. if (!output) console.log("No output for: "+message);
  1243. //Prints a line element independent of any current streamed element.
  1244. noteScrollPos();
  1245. var ele = newLineElement("div", cls);
  1246. ele.innerHTML = message;
  1247. if (prefs.scrollback_count && output.childNodes.length > prefs.scrollback_count) {
  1248. output.removeChild(output.firstChild);
  1249. } else if (prefs.scrollback_count && output.childNodes.length == prefs.scrollback_count) {
  1250. writeToConsole("Scrollback has reached " + prefs.scrollback_count + " elements; older lines will now be removed to limit client lag.");
  1251. }
  1252. keepScrollPos();
  1253. if (prefs.horizontal_scroll==="automatic") {
  1254. updateAutomaticScroll();
  1255. }
  1256. }
  1257. function noteScrollPos() {
  1258. if (!receiveActive) scrolledToBottom = (output.scrollTop >= (output.scrollHeight - output.offsetHeight));
  1259. }
  1260. function keepScrollPos() {
  1261. if (!receiveActive && scrolledToBottom) scrollToBottom();
  1262. }
  1263. function scrollToBottom() {
  1264. output.scrollTop = output.scrollHeight;
  1265. scrolledToBottom = true;
  1266. }
  1267. function printUnscreened(message, cls) {
  1268. printScreened(safe_tags_replace(message), cls);
  1269. }
  1270. function writeToConsole(message) {
  1271. console.log(message);
  1272. //printScreened("<span style='color: orange;'>CONSOLE:</span> " + safe_tags_replace(message));
  1273. }
  1274. function plog(arg1, arg2, arg3, arg4, arg5, arg6) {
  1275. //console.log(arg1, arg2, arg3, arg4, arg5, arg6);
  1276. }
  1277. function outputPageUp() {
  1278. if (output.scrollBy) {
  1279. output.scrollBy(0,0-output.clientHeight);
  1280. } else {
  1281. output.scrollTop = output.scrollTop - output.clientHeight;
  1282. }
  1283. fixFocus();
  1284. }
  1285. function outputPageDown() {
  1286. if (output.scrollBy) {
  1287. output.scrollBy(0,output.clientHeight);
  1288. } else {
  1289. output.scrollTop = output.scrollTop + output.clientHeight;
  1290. }
  1291. fixFocus();
  1292. }
  1293. var copyToClipboardFailed = false;
  1294. function copyOutputSelection() {
  1295. if (copyToClipboardFailed ||
  1296. (typeof window.getSelection != "undefined" && !(window.getSelection().toString())) ||
  1297. (typeof document.selection != "undefined" && (document.selection.type != "Text" || !(document.selection.createRange().text)))
  1298. ) {
  1299. return;
  1300. }
  1301. var success = false;
  1302. try {
  1303. success = document.execCommand("copy");
  1304. } catch(e) {
  1305. success = false;
  1306. }
  1307. if (!success) {
  1308. copyToClipboardFailed = true;
  1309. printScreened("<span style='color: red;'>ERROR:</span> Copy-on-select unsuccessful. Make sure to use an up-to-date browser. This functionality has been disabled for this session; to turn it off permanently, enter <strong>clientpref highlight_to_copy off'.", "client error");
  1310. }
  1311. }
  1312. //-----Interpreter Code
  1313. var receiveExtra = '';
  1314. var receiveMode = 'RAW';
  1315. var currentSubElements = [];
  1316. var receiveActive = false;
  1317. var loginDone = false;
  1318. function doReceive(msg) {
  1319. //writeToConsole("ARG");
  1320. if (c.raw) writeToConsole("R:"+msg);
  1321. //debugMsg("Receive: " + msg);
  1322. noteScrollPos();
  1323. receiveActive = true;
  1324. msg = receiveExtra + msg;
  1325. var lines = msg.split('\r\n');
  1326. receiveExtra = lines.pop();
  1327. for (var i=0; i<lines.length; i++) {
  1328. var str = lines[i];
  1329. if (str.substring(0,7)==='SECRET ') {
  1330. var userName = loadCookie("user");
  1331. gameCharacter = userName;
  1332. var passWord = loadCookie("pass");
  1333. var secret = str.substring(7, str.length);
  1334. var hash = hexMD5(userName + passWord + secret);
  1335. /*sendSys("USER " + userName + "\n" +
  1336. "SECRET " + secret + "\n" +
  1337. "HASH " + hash + "\n" +
  1338. (window.charName && window.charName.length > 0 ? "CHAR " + window.charName + "\n" : "")
  1339. );*/
  1340. sendSys("USER " + userName);
  1341. sendSys("SECRET " + secret);
  1342. sendSys("HASH " + hash);
  1343. if (window.charName && window.charName.length > 0) {
  1344. sendSys("CHAR " + window.charName);
  1345. }
  1346. if (window.location == "http://test.skotos.net/orchil/marrach/marrach.htm") {
  1347. printScreened("Orchil Testing Note: If you're on Marrach and not being fully logged in, try typing the name of a character on your account.");
  1348. }
  1349. receiveMode = 'ALICECOMPAT';
  1350. loginDone = true;
  1351. } else if (str.substring(0,6)==='SKOOT ') {
  1352. var sppos = str.indexOf(" ", 6);
  1353. if (sppos == -1) {
  1354. badSkoot('?', str);/* malformed SKOOT */
  1355. } else {
  1356. doSkoot(str.substring(6, sppos), str.substring(sppos + 1));
  1357. }
  1358. } else if (str.substring(0,7)==='MAPURL ') {
  1359. var url = str.substring(7);
  1360. var img = document.getElementById("image_map_img");
  1361. if (sppos == -1 || !img) {
  1362. badSkoot('MAPURL', str);
  1363. } else {
  1364. console.log(str);
  1365. img.style.backgroundImage = "url('"+url+"')";;
  1366. }
  1367. } else {
  1368. if (str==="An error occurred while processing your login.") {
  1369. loginDone = false;
  1370. openerWin("https://www.skotos.net/user/login.php?redirect=http%3a%2f%2ftest%2eskotos%2enet%2forchil%2f");
  1371. }
  1372. processInput(str);
  1373. }
  1374. }
  1375. if (receiveExtra && receiveExtra.indexOf('SKOOT') != 0 && receiveExtra.indexOf('SECRET')) {
  1376. msg = receiveExtra;
  1377. receiveExtra = '';
  1378. processInput(msg, true);
  1379. }
  1380. receiveActive = false;
  1381. keepScrollPos();
  1382. }
  1383. function processInput(line, nonl /*=false*/) {
  1384. switch (receiveMode) {
  1385. case 'RAW': //Dangerous & Stupid
  1386. printScreened(line);
  1387. break;
  1388. case 'PLAIN': //Safe, but low on features
  1389. printUnscreened(line, "pre");
  1390. break;
  1391. case 'ANSI': //The old standby
  1392. //Figure this out later, if ever.
  1393. printUnscreened(line, "pre");
  1394. break;
  1395. case 'ALICECOMPAT': //What the servers support by default.
  1396. parseAliceCompat(line, nonl);
  1397. break;
  1398. case 'PRE': //Temporarily in a PRE tag.
  1399. parsePreTag(line, nonl);
  1400. break;
  1401. default:
  1402. alert("Bad receive mode.");
  1403. }
  1404. }
  1405. function parseAliceCompat(str, nonl) {
  1406. var tagdef;
  1407. if (!str.length && !nonl) {
  1408. newLineElement();
  1409. //addToCurrentElement("\n");
  1410. }
  1411. while (str.length) {
  1412. var pos = str.indexOf("<");
  1413. if (pos === -1) {
  1414. addToCurrentElement(str);
  1415. break;
  1416. } else {
  1417. if (pos !==0) {
  1418. addToCurrentElement(str.substring(0,pos));
  1419. }
  1420. str = str.substring(pos+1);
  1421. //At this point, we are at the start of an open or close tag.
  1422. //Now we split on > to get everying inside - name, and possibly / or attributes.
  1423. pos = str.indexOf(">");
  1424. if (pos === -1) {
  1425. receiveExtra += "<" + str; //The tag opening isn't complete; save what remains in case we get the rest later.
  1426. break;
  1427. }
  1428. tagdef = str.substring(0,pos).trim();
  1429. str = str.substring(pos+1);
  1430. if (tagdef.charAt(0) === "/") {
  1431. closeElement(tagdef.substring(1));
  1432. } else {
  1433. pos = tagdef.indexOf(" ");
  1434. if (pos === -1) {
  1435. openElement(tagdef);
  1436. } else {
  1437. openElement(tagdef.substring(0, pos), tagdef.substring(pos+1));
  1438. }
  1439. }
  1440. }
  1441. }
  1442. if (!nonl) {
  1443. plog("Closing non-multi because !nonl");
  1444. closeNonMultiElements();
  1445. }
  1446. if (prefs.horizontal_scroll==="automatic") {
  1447. updateAutomaticScroll();
  1448. }
  1449. }
  1450. function closeNonMultiElements() {
  1451. plog("Closing non-multi elements", currentSubElements);
  1452. var pos = currentSubElements.length - 1;
  1453. var tag;
  1454. while (pos>-1) {
  1455. if (["pre","ul","ol"].indexOf(currentSubElements[pos].tagName.toLowerCase()) !== -1) {
  1456. break;
  1457. }
  1458. currentSubElements.splice(-1);
  1459. plog("Removed one subelement.", currentSubElements);
  1460. pos--;
  1461. }
  1462. //Dead code, remove later.
  1463. //if(currentSubElements.length && ["PRE","UL","OL"].indexOf(currentSubElements[0].tagName) !== -1) {
  1464. // currentSubElements = [currentSubElements[0]];
  1465. //} else {
  1466. // currentSubElements = [];
  1467. //}
  1468. }
  1469. function addToCurrentElement(text) {
  1470. if (!text || text=="") {
  1471. return;
  1472. }
  1473. var cur = getCurrentSubElement();
  1474. noteScrollPos();
  1475. plog("Writing: ",text,"To: ", cur);
  1476. cur.insertAdjacentHTML('beforeend', text);
  1477. keepScrollPos();
  1478. }
  1479. function unEscape(text) {
  1480. return text.replace(/&quot;/g, '"').replace(/&gt;/g, '>').replace(/&lt;/g, '<').replace(/&amp;/g, '&');
  1481. }
  1482. function getCurrentSubElement() {
  1483. if (!currentSubElements.length || (currentSubElements.length===1 && currentSubElements[0].tagName==="PRE")) {
  1484. plog("Creating new div as current subelement.");
  1485. currentSubElements.push(newLineElement());
  1486. }
  1487. return currentSubElements[currentSubElements.length-1];
  1488. }
  1489. var tagConversions = {
  1490. "font":"span",
  1491. "span":"span",
  1492. "hr":"hr",
  1493. "b":"strong",
  1494. "strong":"strong",
  1495. "i":"em",
  1496. "em":"em",
  1497. "pre":"pre",
  1498. "ol":"ol",
  1499. "ul":"ul",
  1500. "li":"li",
  1501. "center":"div",
  1502. "a":"a",
  1503. "table":"table",
  1504. "caption":"caption",
  1505. "tbody":"tbody",
  1506. "tr":"tr",
  1507. "th":"th",
  1508. "td":"td"
  1509. };
  1510. function openElement(tag, attributes) {
  1511. var ele;
  1512. if (tag==="body") {
  1513. return applySkotosTheme(attributes);
  1514. }
  1515. if (tag==="xch_page") {
  1516. return applyXchPage(attributes);
  1517. }
  1518. var nutag = tagConversions[tag];
  1519. if (!nutag) {
  1520. return writeToConsole("Invalid tag: " + tag);
  1521. }
  1522. if (nutag==="li") {
  1523. closeElement("li", true);
  1524. }
  1525. noteScrollPos();
  1526. plog("opening: " + tag + "/" + nutag);
  1527. if (["hr","div","p","table","pre"].indexOf(nutag) !== -1) {
  1528. closeNonMultiElements();
  1529. ele = newLineElement(nutag);
  1530. } else {
  1531. ele = document.createElement(nutag);
  1532. plog("Appended new element:", ele, "To:", getCurrentSubElement());
  1533. getCurrentSubElement().appendChild(ele);
  1534. }
  1535. if(tag==="font" && attributes==="size=+1") {
  1536. debugtrack = ele;
  1537. }
  1538. currentSubElements.push(ele);
  1539. var attr = parseAttributes(attributes);
  1540. if (tag==="center") {
  1541. attr["class"] = "centertag";
  1542. }
  1543. if (attr["class"]) {
  1544. ele.className = attr["class"];
  1545. }
  1546. if (attr["style"]) {
  1547. ele.style.cssText = attr["style"];
  1548. }
  1549. if (attr["xch_cmd"]) {
  1550. ele.title=attr["xch_cmd"];
  1551. addEvent(ele, "click", sendTitle, {passive:true});
  1552. } else if (attr["title"]) {
  1553. //Optional TODO: Support titles and xch_cmds being different
  1554. ele.title=attr["title"];
  1555. }
  1556. if (tag==="hr") {
  1557. closeElement("hr"); //<hr> doesn't get inner content.
  1558. }
  1559. keepScrollPos();
  1560. }
  1561. function applyXchPage(attributes) {
  1562. if (attributes==="clear=\"text\"" || attributes==="clear=\"text\" /") {
  1563. while (output.firstChild) {
  1564. output.removeChild(output.firstChild);
  1565. }
  1566. } else {
  1567. writeToConsole("Unexpected attributes for xch_page: " + attributes);
  1568. }
  1569. }
  1570. function closeElement(tag, lastOnly /*=false*/) {
  1571. var nutag = tagConversions[tag];
  1572. plog("closing: " + tag + "/" + nutag + "; " + lastOnly);
  1573. plog(currentSubElements);
  1574. var l = currentSubElements.length;
  1575. if (l) {
  1576. if (lastOnly) {
  1577. if (currentSubElements[l-1].tagName.toLowerCase()===nutag) {
  1578. currentSubElements.splice(-1);
  1579. }
  1580. } else {
  1581. for (var i=l-1; i>=0; i--) {
  1582. if (currentSubElements[i].tagName.toLowerCase()===nutag) {
  1583. currentSubElements.splice(i, l-i);
  1584. break;
  1585. }
  1586. }
  1587. }
  1588. }
  1589. plog(currentSubElements);
  1590. plog("done closing");
  1591. }
  1592. var gencolors = {
  1593. "#646464":"grey",
  1594. "#ffffff":"white",
  1595. "#e12000":"red",
  1596. "#00cd20":"green",
  1597. "#ff9b00":"orange",
  1598. "#0020ff":"blue",
  1599. "#e119e1":"pink",
  1600. "#00cde1":"cyan",
  1601. "#000000":"black"
  1602. };
  1603. function parseAttributes(attr) {
  1604. if (!attr) {
  1605. return {};
  1606. }
  1607. if (attr==="size=+1") {
  1608. return {"class":"genbig"};
  1609. }
  1610. if (attr.substring(0,6)==="color=") {
  1611. attr = stripQuotes(attr.substring(6));
  1612. if (gencolors[attr]) {
  1613. return {"class":"gen"+gencolors[attr]};
  1614. }
  1615. return {"style":"color:"+attr};
  1616. }
  1617. if (attr.substring(0,6)==="class=") {
  1618. attr = stripQuotes(attr.substring(6));
  1619. return {"class":attr};
  1620. }
  1621. if (attr.substring(0,8)==="xch_cmd=") {
  1622. attr = stripQuotes(attr.substring(8));
  1623. return {"xch_cmd":attr};
  1624. }
  1625. if (attr.substring(0,6)==="title=") {
  1626. attr = stripQuotes(attr.substring(6));
  1627. return {"title":attr};
  1628. }
  1629. if (attr) {
  1630. reportClientError("Unknown attr: " + attr );
  1631. }
  1632. return {};
  1633. }
  1634. function stripQuotes(txt) {
  1635. var e = txt.length - 1;
  1636. var s = (txt.charAt(0)==='"' || txt.charAt(0)==="'") ? 1 : 0;
  1637. e = (txt.charAt(e)==='"' || txt.charAt(e)==="'") ? e : e -1;
  1638. return txt.substring(s, e);
  1639. }
  1640. function newLineElement(type /*="div"*/, cls) {
  1641. noteScrollPos();
  1642. if (!type) type="div";
  1643. var ele = document.createElement(type);
  1644. if (cls) {
  1645. ele.className = cls;
  1646. }
  1647. if (currentSubElements.length && currentSubElements[0].tagName==="PRE") {
  1648. //addToCurrentElement("\n");
  1649. currentSubElements[0].appendChild(ele);
  1650. } else {
  1651. output.appendChild(ele);
  1652. }
  1653. keepScrollPos();
  1654. return ele;
  1655. }
  1656. function badSkoot(num, msg) {
  1657. reportClientError('Bad SKOOT message received: ' + num + " " + msg);
  1658. }
  1659. function reportClientError(msg) {
  1660. //sendSys('@CLIENTERR ' + msg);
  1661. console.log('CLIENTERRORREPORT ' + msg);
  1662. }
  1663. function loadCookie(name) {
  1664. if (name=="user") {
  1665. //return prompt("Username (whitelisted accounts only)");
  1666. }
  1667. if (name=="pass") {
  1668. //return prompt("Password");
  1669. }
  1670. var allcookies = document.cookie;
  1671. if (!allcookies) {
  1672. window.location
  1673. //alert("no cookies");
  1674. return null;
  1675. }
  1676. var start = allcookies.indexOf(name + "=");
  1677. if (start == -1) {
  1678. alert("no cookie: '" + name + "'");
  1679. return null;
  1680. }
  1681. start += name.length + 1;
  1682. var end = allcookies.indexOf(';',start);
  1683. if (end == -1) {
  1684. end = allcookies.length;
  1685. }
  1686. //alert(allcokies);
  1687. //alert(allcookies.substring(start,end));
  1688. return allcookies.substring(start,end);
  1689. }
  1690. //-----Window Code
  1691. window.openerLoc = "";
  1692. window.openerName = "";
  1693. if (window.opener && !window.opener.closed) {
  1694. try {
  1695. //This may error out if the opener navigated away due to CORS.
  1696. window.openerLoc = window.opener.location.href;
  1697. window.openerName = window.opener.window.name;
  1698. } catch(e) {
  1699. console.log(e);
  1700. }
  1701. }
  1702. document.popups = {};
  1703. function openerWin(filename) {
  1704. if (filename != "") {
  1705. if (!window.opener || window.opener.closed) {
  1706. window.open(filename, window.openerName);
  1707. return;
  1708. } else {
  1709. window.opener.focus();
  1710. window.opener.location.href = filename;
  1711. return;
  1712. }
  1713. } else {
  1714. if (!window.opener || window.opener.closed) {
  1715. window.open(window.openerLoc, window.openerName);
  1716. return;
  1717. } else {
  1718. window.opener.focus();
  1719. return;
  1720. }
  1721. }
  1722. }
  1723. function popupArtWin(filename, windowname, windowtitle) {
  1724. var scrLeft = 16 + window.screenLeft;
  1725. var scrTop = 16 + window.screenTop;
  1726. document.popups[windowname] = {};
  1727. document.popups[windowname].src = filename;
  1728. document.popups[windowname].title = windowtitle;
  1729. var artwin = open('../art.htm', windowname, 'width=128,height=128,left=' + scrLeft + ',top=' + scrTop + 'hotkeys=no,scrollbars=no,resizable=no');
  1730. popupFollowUp(filename, artwin);
  1731. }
  1732. function popupWin(filename, windowname, remWinWdh, remWinHgt) {
  1733. var scrLeft = parseInt((screen.width / 2) - (remWinWdh / 2));
  1734. var scrTop = parseInt((screen.height / 2) - (remWinHgt / 2));
  1735. var helpwin = open(filename, windowname, 'width=' + remWinWdh + ',height=' + remWinHgt + ',left=' + scrLeft + ',top=' + scrTop + 'hotkeys=no,scrollbars=yes,resizable=yes');
  1736. popupFollowUp(filename, helpwin);
  1737. }
  1738. function popupFollowUp(filename, win) {
  1739. console.log("popup window object:");
  1740. console.log(win);
  1741. if (!win || win.closed || (typeof (win.closed))=='undefined' || (typeof (win.focus))=='undefined' || !(win.innerHeight) || !(win.innerHeight > 0)) {
  1742. printUnscreened("Error: Browser blocked the popup. Please allow popups for skotos.net or disable your popup blocker to use them.", "client error");
  1743. } else {
  1744. win.focus();
  1745. }
  1746. }
  1747. //-----Style code
  1748. function setActiveStyleSheet(title) {
  1749. var i, a, main;
  1750. for(i=0; (a = document.getElementsByTagName("link")[i]); i++) {
  1751. if(a.getAttribute("rel").indexOf("style") != -1 && a.getAttribute("title")) {
  1752. a.disabled = true;
  1753. if(a.getAttribute("title") == title) a.disabled = false;
  1754. }
  1755. }
  1756. }
  1757. function changecss(cls, key, value) {
  1758. var r = document.styleSheets[0].cssRules ? "cssRules" : "rules";
  1759. for (var i=0; i<document.styleSheets[0][r].length;i++) {
  1760. if (document.styleSheets[0][r][i].selectorText == cls) {
  1761. document.styleSheets[0][r][i].style[key] = value;
  1762. }
  1763. }
  1764. }
  1765. var cssMods = {};
  1766. var modStyleSheet = document.createElement('style');
  1767. modStyleSheet.type = 'text/css';
  1768. function updateCssMods() {
  1769. var text = "";
  1770. for (var name in cssMods) {
  1771. text += name+": {"+cssMods+"} ";
  1772. }
  1773. //Todo: Either abandon this method, or update sheet with text here.
  1774. }
  1775. function setStyle(cssText) {
  1776. var sheet = document.createElement('style');
  1777. sheet.type = 'text/css';
  1778. window.customSheet = sheet;
  1779. (document.head || document.getElementsByTagName('head')[0]).appendChild(sheet);
  1780. return (setStyle = function(cssText, node) {
  1781. noteScrollPos();
  1782. if(!node || node.parentNode !== window.customSheet) {
  1783. node = window.customSheet.appendChild(document.createTextNode(cssText));
  1784. } else {
  1785. node.nodeValue = cssText;
  1786. }
  1787. keepScrollPos();
  1788. return node;
  1789. })(cssText);
  1790. }
  1791. //-----Logging Code
  1792. function saveCurrentWindow(plain) {
  1793. var format, contents;
  1794. if (prefs.logging_format=="plain") {
  1795. format = "plain";
  1796. contents = output.innerText;
  1797. } else {
  1798. format = "html";
  1799. contents = output.innerHTML;
  1800. }
  1801. var name = logFilename(format, false);
  1802. if (confirm("Saving logfile as: " + name)) {
  1803. var blob = new Blob([contents], {type: "text/" + format + ";charset=utf-8"});
  1804. saveAs(blob, name);
  1805. }
  1806. }
  1807. function logFilename(format, auto) {
  1808. var now = new Date();
  1809. if (Date.prototype.toISOString) {
  1810. now = now.toISOString();
  1811. } else {
  1812. now = now.toDateString();
  1813. }
  1814. var serverDesc = serverCode + "_";
  1815. if(gameCharacter) serverDesc += gameCharacter + "_";
  1816. return serverDesc + now + "_" + (auto?"auto":"") + "log." + (format==="html"?"htm":"txt");
  1817. }
  1818. //-----Initialization Code
  1819. //addEvent(window, 'load', init, false);
  1820. addEvent(window, 'load', init, {once:true});
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement