RPG.SE Chat Fudge Dice

Convert RPG.SE chat d6 to Fudge dice (dF)

当前为 2015-02-04 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name RPG.SE Chat Fudge Dice
  3. // @namespace http://userscripts.org/users/168580
  4. // @description Convert RPG.SE chat d6 to Fudge dice (dF)
  5. // @grant none
  6. // @include http://chat.stackexchange.com/rooms/*
  7. // @include http://chat.stackexchange.com/transcript/*
  8. // @version 1.4.2
  9. // ==/UserScript==
  10.  
  11. // Changelog:
  12. // 1.4.2 Repaired localStorage references.
  13. // 1.4.1 Dice script no longer activates on non-RPG.SE rooms, since those don't even have dice.
  14. // Also, a bug fix: script wouldn't save settings when it was activated in a room, so a refresh would lead to it being deactivated again.
  15. // 1.4 Script now supports activation/deactivation in any chat room
  16. // 1.3 Configurable colors
  17. // 1.2 Made to work in Transcript. Dice faces made fatter.
  18. // 1.1 ??
  19. // 1.0 Created by @C.Ross (http://userscripts.org/users/168580)
  20.  
  21. //Thanks to Sohum (http://rpg.stackexchange.com/users/792/sohum) for the chrome fix
  22. var main = function () {
  23. // load jQuery and execute the main function
  24.  
  25. var util = (function() {
  26.  
  27. function inTranscript() {
  28. return window.location.href.indexOf('/transcript/') != -1;
  29. }
  30.  
  31. // Checks to see if the chat room is on RPG.SE.
  32. // Other chat rooms don't have dice, so there's no point in running the script there.
  33. function chatroomIsOnRpgSe() {
  34. var footerHref = $('a', '#footer-logo').prop('href');
  35. return (footerHref.indexOf('rpg.stackexchange.com') >= 0);
  36. }
  37.  
  38. function getRoomId() {
  39. url = new URL(window.location);
  40.  
  41. // this split will turn into one of these for the Fate room:
  42. // ["", "room", "8403"]
  43. // ["", "transcript", "8403"]
  44. pathBits = url.pathname.split('/', 3);
  45. return pathBits[2];
  46. }
  47.  
  48. return {
  49. inTranscript: inTranscript,
  50. chatroomIsOnRpgSe: chatroomIsOnRpgSe,
  51. getRoomId: getRoomId
  52. };
  53.  
  54. })();
  55.  
  56. var fudgeConfig = (function() {
  57. var store = "fudgeConfig";
  58.  
  59. // Config (defaults)
  60. var _useColors = false;
  61. var _plusColor = '#008800';
  62. var _minusColor = '#CC0000';
  63. var _rooms = ['8403', '11']; // Fate chat room, RPG General chat
  64.  
  65. // Get/set whether to use colours
  66. function useColors(value) {
  67. if (value == undefined) return _useColors;
  68. else _useColors = value;
  69. }
  70.  
  71. // Get/set the plus colour
  72. function plusColor(value) {
  73. if (value == undefined) return _plusColor;
  74. else _plusColor = value;
  75. }
  76.  
  77. // Get/set the minus colour
  78. function minusColor(value) {
  79. if (value == undefined) return _minusColor;
  80. else _minusColor = value;
  81. }
  82.  
  83. // Check whether the colour format is valid
  84. function validateColorInput(value) {
  85. // #000 or #000000 is acceptable
  86. if (value.length != 7 && value.length != 4) return false;
  87. if (value[0] != '#') return false;
  88. return true;
  89. }
  90.  
  91. function save() {
  92. var config = {
  93. 'useColors': _useColors,
  94. 'plusColor': _plusColor,
  95. 'minusColor': _minusColor,
  96. 'rooms': _rooms
  97. };
  98. localStorage[store] = JSON.stringify(config);
  99. }
  100.  
  101. function load() {
  102. var firstRunEver = (typeof localStorage[store] == "undefined");
  103. if (firstRunEver) {
  104. // Use and store defaults.
  105. save();
  106. return;
  107. }
  108. var config = JSON.parse(localStorage[store]);
  109. _useColors = config.useColors;
  110. _plusColor = config.plusColor;
  111. _minusColor = config.minusColor;
  112.  
  113. if (config.rooms) _rooms = config.rooms;
  114. else save();
  115. }
  116.  
  117. function isActive() {
  118. roomId = util.getRoomId();
  119. return (_rooms.indexOf(roomId) >= 0);
  120. }
  121.  
  122. function activateRoom() {
  123. _rooms.push(util.getRoomId());
  124. save();
  125. }
  126.  
  127. function deactivateRoom() {
  128. var roomId = util.getRoomId();
  129. _rooms = $.grep(_rooms, function(value) {
  130. return value != roomId;
  131. });
  132. save();
  133. window.location.reload();
  134. }
  135.  
  136. return {
  137. useColors: useColors,
  138. plusColor: plusColor,
  139. minusColor: minusColor,
  140. validateColorInput: validateColorInput,
  141. save: save,
  142. load: load,
  143. isActive: isActive,
  144. activateRoom: activateRoom,
  145. deactivateRoom: deactivateRoom
  146. }
  147. })();
  148.  
  149.  
  150. // Functions that act on the chat messages themselves
  151. var chatMessages = (function() {
  152. // Replace the content of d6 dice to convert them into Fudge dice
  153. function convertDice(){
  154. $('.six-sided-die').each(function() {
  155. var $die = $(this);
  156. //It does something strange when I try to use the :not selector,
  157. // So go old school
  158. if ($die.hasClass('fate-die')){
  159. return;
  160. }
  161. var count = $('.dot:contains("•")', $die).length;
  162. $die.empty();
  163. $die.attr('data-d6-roll', count);
  164.  
  165. var $face = $('<span>')
  166. .css('display', 'table-cell')
  167. .css('vertical-align', 'middle');
  168.  
  169. if (count < 3){;
  170. $face.html('&minus;');
  171. if (fudgeConfig.useColors()) {
  172. $die.css('color', fudgeConfig.minusColor());
  173. }
  174. $die.empty().append($face)
  175. $die.addClass('fate-roll-minus');
  176. $die.attr('data-fate-roll', -1);
  177. }else if (count > 4){
  178. $face.html('+');
  179. if (fudgeConfig.useColors()) {
  180. $die.css('color', fudgeConfig.plusColor());
  181. }
  182. $die.empty().append($face);
  183. $die.addClass('fate-roll-plus');
  184. $die.attr('data-fate-roll', 1);
  185. }else {
  186. $die.text(' ');
  187. $die.attr('data-fate-roll', 0);
  188. }
  189.  
  190. $die.css('display', 'table');
  191. $die.css('text-align','center');
  192. $die.css('font-size','30px');
  193. $die.css('font-weight', 'bold');
  194.  
  195. //Add class to prevent re-processing
  196. $die.addClass('fate-die');
  197. });
  198. }
  199. // Mark Fudge dice rolls with a title representing their value
  200. function add4dFMeta(){
  201. $('.content').each(function() {
  202. var $content = $(this);
  203. if ($('.fate-die', $content).length == 4 && !$content.hasClass('.fate-roll')) {
  204. $content.addClass('.fate-roll');
  205.  
  206. var total = 0;
  207. $('.fate-die', $content).each(function() {
  208. var $die = $(this);
  209. total = total + parseInt($die.attr('data-fate-roll'));
  210. });
  211.  
  212. if (total > 0) {
  213. total = '+' + total;
  214. }
  215. $content.attr('title', '4dF = ' + total);
  216. }
  217. });
  218. }
  219.  
  220. // Update the dice on each Fudge die on screen
  221. function updateColors() {
  222. if (fudgeConfig.useColors()) {
  223. $('.fate-roll-plus').css('color', fudgeConfig.plusColor());
  224. $('.fate-roll-minus').css('color', fudgeConfig.minusColor());
  225. } else {
  226. $('.fate-roll-plus').css('color', '');
  227. $('.fate-roll-minus').css('color', '');
  228. }
  229. }
  230.  
  231. function scan() {
  232. if (!fudgeConfig.isActive()) return;
  233. convertDice();
  234. add4dFMeta();
  235. }
  236.  
  237. return {
  238. scan: scan,
  239. updateColors: updateColors
  240. };
  241.  
  242. })();
  243.  
  244. var configMenu = (function() {
  245.  
  246. // Creates the button for this script
  247. function createButton() {
  248. var $button = $('<a>')
  249. .text('fudge dice');
  250. return $button;
  251. }
  252.  
  253. // Wraps a form element in an appropriate label
  254. function wrapLabel($object, labelText) {
  255. var $lbl = $('<label>')
  256. .text(labelText)
  257. .css('vertical-align', 'middle')
  258. .prepend($object);
  259. $object.css('margin-right', '1em');
  260. return $('<p>').append($lbl);
  261. }
  262.  
  263. function createActiveOptions() {
  264. var $options = $('<div>')
  265. .prop('id', 'fudge-active-options');
  266.  
  267. var $colorToggle = $('<input>')
  268. .prop('type', 'checkbox')
  269. .css('vertical-align', 'middle')
  270. .prop('id', 'fudge-color-toggle');
  271. $options.append(wrapLabel($colorToggle, 'Use colors'));
  272.  
  273. var $picker = $('<input>')
  274. .addClass('color-picker')
  275. .prop('type', 'text')
  276. .prop('maxlength', '7')
  277. .css('width', '7em')
  278. .css('vertical-align', 'middle');
  279. var $preview = $('<span>')
  280. .addClass('color-preview')
  281. .css('display', 'inline-block')
  282. .css('width', '1em')
  283. .css('height', '1em')
  284. .css('border', '1px solid black')
  285. .css('margin-right', '0.5em')
  286. .css('vertical-align', 'middle');
  287. var $pickerSpan = $('<span>')
  288. .append($preview)
  289. .append($picker);
  290.  
  291. var $plusColorPicker = $pickerSpan.clone()
  292. .prop('id', 'fudge-plus-color');
  293. $options.append(wrapLabel($plusColorPicker, 'Plus color'));
  294.  
  295. var $minusColorPicker = $pickerSpan
  296. .prop('id', 'fudge-minus-color');
  297. $options.append(wrapLabel($minusColorPicker, 'Minus color'));
  298.  
  299. $options.append($('<button>')
  300. .prop('id', 'fudge-save')
  301. .text('Save and update')
  302. );
  303.  
  304. $options.append($('<div>')
  305. .text("Insert a three- or six-digit hex color, such as #000 or #CC00FF, then hit enter. "
  306. + "A red border means you probably entered an invalid value. "
  307. + "You can still save that value but it won't be previewed. "
  308. + "You can use a ")
  309. .append($('<a>')
  310. .text('HTML Color Picker')
  311. .prop('href', 'http://www.w3schools.com/tags/ref_colorpicker.asp')
  312. .prop('target', '_blank')
  313. )
  314. );
  315.  
  316. $options.append($('<div>')
  317. .text("To deactivate fudge dice for this room, use this (it also needs to refresh the page): ")
  318. .append($('<button>')
  319. .prop('id', 'fudge-deactivate')
  320. .text('Deactivate & refresh')
  321. )
  322. );
  323.  
  324.  
  325. return $options;
  326. }
  327.  
  328. function createInactiveOptions() {
  329. var $options = $('<div>')
  330. .prop('id', 'fudge-inactive-options');
  331.  
  332. $options.append($('<button>')
  333. .prop('id', 'fudge-activate')
  334. .text('Activate fudge dice')
  335. );
  336.  
  337. return $options;
  338. }
  339.  
  340. // Create the configuration menu element
  341. function createMenu() {
  342. var $menu = $('<section>')
  343. .css('border', '1px solid #E0DCBF')
  344. .css('background-color', '#FFF8DC')
  345. .css('padding', '10px')
  346. .css('color', '#444444')
  347. .css('margin-bottom', '1em');
  348.  
  349. $menu.append(
  350. $('<h3>')
  351. .text('Fudge dice config')
  352. .css('margin-bottom', '0.5em')
  353. );
  354.  
  355. $menu.append(createActiveOptions());
  356. $menu.append(createInactiveOptions());
  357.  
  358. $menu.hide();
  359. return $menu;
  360. }
  361.  
  362. // Updates the options to display either the active or inactive view
  363. function updateOptionsView() {
  364. if (fudgeConfig.isActive()) {
  365. $('#fudge-active-options').show();
  366. $('#fudge-inactive-options').hide();
  367. } else {
  368. $('#fudge-active-options').hide();
  369. $('#fudge-inactive-options').show();
  370. }
  371. }
  372.  
  373. // Add the config menu to the page
  374. function addConfigMenu() {
  375. var $button = createButton();
  376. $('#sidebar-menu').append($button);
  377.  
  378. var $menu = createMenu();
  379. $menu.insertAfter($('#sidebar-menu'));
  380.  
  381. $('#fudge-color-toggle').click(function() {
  382. fudgeConfig.useColors($(this).prop('checked'));
  383. });
  384.  
  385. $('.color-picker').change(function() {
  386. var valid = fudgeConfig.validateColorInput($(this).val());
  387. if (!valid)
  388. {
  389. $(this).css('border-color', 'red');
  390. return;
  391. }
  392.  
  393. $(this).css('border-color', 'green');
  394.  
  395. $('.color-preview', $(this).parent()).css('background-color', $(this).val());
  396. })
  397.  
  398. $('#fudge-color-toggle').prop('checked', fudgeConfig.useColors());
  399. $('#fudge-plus-color .color-picker').val(fudgeConfig.plusColor());
  400. $('#fudge-minus-color .color-picker').val(fudgeConfig.minusColor());
  401. $('.color-picker').change();
  402.  
  403. $('#fudge-save').click(function() {
  404. var useColors = $('#fudge-color-toggle').prop('checked');
  405. var plusColor = $('#fudge-plus-color .color-picker').val();
  406. var minusColor = $('#fudge-minus-color .color-picker').val();
  407.  
  408. fudgeConfig.useColors(useColors);
  409. fudgeConfig.plusColor(plusColor);
  410. fudgeConfig.minusColor(minusColor);
  411. fudgeConfig.save();
  412.  
  413. chatMessages.updateColors();
  414. })
  415.  
  416. updateOptionsView();
  417.  
  418. $('#fudge-activate').click(function() {
  419. fudgeConfig.activateRoom();
  420. updateOptionsView();
  421. });
  422.  
  423. $('#fudge-deactivate').click(function() {
  424. fudgeConfig.deactivateRoom();
  425. updateOptionsView();
  426. });
  427.  
  428. $button.click(function() {
  429. $menu.toggle();
  430. });
  431. }
  432.  
  433. function load() {
  434. addConfigMenu();
  435. }
  436.  
  437. return {
  438. load: load
  439. };
  440.  
  441. })();
  442.  
  443. $(window).load(function() {
  444. // Remain inactive for non-RPG.SE chat rooms
  445. if (!util.chatroomIsOnRpgSe()) return;
  446.  
  447. fudgeConfig.load();
  448. configMenu.load();
  449.  
  450. if(util.inTranscript()) {
  451. chatMessages.scan();
  452. } else {
  453. $(document).one('DOMNodeInserted', '#chat', function() {
  454. // Wait til the dice have loaded
  455. $(chatMessages.scan);
  456. });
  457.  
  458. setInterval(chatMessages.scan, 500);
  459. }
  460. });
  461.  
  462. };
  463.  
  464. var script = document.createElement('script');
  465. script.type = "text/javascript";
  466. script.id = "fudge-dice-script"
  467. script.textContent = '(' + main.toString() + ')();';
  468. document.body.appendChild(script);