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