jekyll-dice-tray 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 808fa6526e8321dc1118a09748a1e24818889bf88af1ecdf729b9a423c6b7291
4
- data.tar.gz: d56b7f208211cb62b0ec536c41fc2a51d74756b99fbbbed4a9db6c10e42204cb
3
+ metadata.gz: 4c1d6ec0bd75b534e1e9065d50c819cb075cfc38f730123263387edf464af7d6
4
+ data.tar.gz: a4c84f2d04c3459cb7c7ee1aadfc650fecdbeb24fbc8dc0ffbe7edd77831218b
5
5
  SHA512:
6
- metadata.gz: e58e36fe768267500d132ec2a7d293ed49ef84421ddf39738bde6e1ef4f0d36d38cfee77f8322c00474888c567a20ba4285a6b2772f905c8b502c6527bbdffd3
7
- data.tar.gz: fd4dc912ec0a339d843d3cedde8c33c418f0804e3b4db2609025e9dd49e8ec181c6a339ae37232d802979f8f274aa9c242430d3b912f68c0197d2ae035b008ba
6
+ metadata.gz: 851289f4145e40ee533165363dee941f88f9ac816a3038a09e79e450485219f1963a0d47e63749ef413f0b1476e732f708ec84ea647a46eb7ffc26ff1725a59c
7
+ data.tar.gz: 5cc132b97b1af190917d1d7c6b7c0134b3968f54e8e02e32f468c5e411ccca0aced928d7ef951873a10c5a443efd09010bb73cde7b8f3fbcb5b289f307dd9f6b
data/README.md CHANGED
@@ -5,6 +5,8 @@ A Jekyll plugin gem that:
5
5
  - Adds an overlay dice tray that accepts dice rolling input
6
6
  - Recognizes dice expressions in rendered pages like `d4`, `1d6`, `1d20+1` and turns them into clickable links that roll in the tray
7
7
  - Recognizes THAC0-style bracket modifiers like `THAC0 18 [+1]` or `18 [+1]` in a THAC0 table row/column; the `+1` inside the brackets is clickable and rolls `d20+1` (other `[+n]` text is left alone)
8
+ - Recognizes chance notation like `2-in-6` (roll a d6; 1–2 succeed in green, higher results fail in red)
9
+ - Clicking a lookup-table die in the header row (e.g. `1d12`) rolls and shows the matching encounter row
8
10
  - Persists input history, result history, minimize status
9
11
  - `/help` - help
10
12
  - `/clear` - clears results and input history
@@ -114,12 +114,67 @@
114
114
  color: rgba(0, 0, 0, 0.45);
115
115
  }
116
116
 
117
+ #jekyll-dice-tray .jdt-success {
118
+ color: #1a7f37;
119
+ }
120
+
121
+ #jekyll-dice-tray .jdt-failure {
122
+ color: #cf222e;
123
+ }
124
+
117
125
  #jekyll-dice-tray .jdt-entry .jdt-details {
118
126
  margin-top: 4px;
119
127
  color: rgba(0, 0, 0, 0.55);
120
128
  font-size: 12px;
121
129
  }
122
130
 
131
+ #jekyll-dice-tray .jdt-table-context {
132
+ margin-top: 6px;
133
+ padding-top: 6px;
134
+ border-top: 1px dashed rgba(0, 0, 0, 0.08);
135
+ }
136
+
137
+ #jekyll-dice-tray .jdt-table-row {
138
+ font-size: 12px;
139
+ line-height: 1.4;
140
+ color: rgba(0, 0, 0, 0.72);
141
+ }
142
+
143
+ #jekyll-dice-tray .jdt-table-row + .jdt-table-row {
144
+ margin-top: 2px;
145
+ }
146
+
147
+ #jekyll-dice-tray .jdt-table-header {
148
+ font-weight: 600;
149
+ color: rgba(0, 0, 0, 0.55);
150
+ }
151
+
152
+ #jekyll-dice-tray .jdt-table-sep {
153
+ color: rgba(0, 0, 0, 0.45);
154
+ }
155
+
156
+ #jekyll-dice-tray .jdt-table-value {
157
+ font-family: inherit;
158
+ }
159
+
160
+ #jekyll-dice-tray .jdt-table-value a,
161
+ #jekyll-dice-tray .jdt-table-header a {
162
+ text-decoration: underline;
163
+ text-underline-offset: 2px;
164
+ }
165
+
166
+ #jekyll-dice-tray .jdt-table-value a.dice-tray-roll {
167
+ color: #ff0000;
168
+ }
169
+
170
+ #jekyll-dice-tray .jdt-table-value a:visited {
171
+ color: inherit;
172
+ }
173
+
174
+ #jekyll-dice-tray .jdt-table-value a.dice-tray-roll:visited {
175
+ color: #ff0000;
176
+ }
177
+
123
178
  a.dice-tray-roll {
124
179
  text-decoration: underline;
125
180
  text-underline-offset: 2px;
@@ -168,6 +223,22 @@ a.dice-tray-roll:visited {
168
223
  color: rgba(255, 255, 255, 0.6);
169
224
  }
170
225
 
226
+ #jekyll-dice-tray .jdt-table-context {
227
+ border-top-color: rgba(255, 255, 255, 0.1);
228
+ }
229
+
230
+ #jekyll-dice-tray .jdt-table-row {
231
+ color: rgba(255, 255, 255, 0.78);
232
+ }
233
+
234
+ #jekyll-dice-tray .jdt-table-header {
235
+ color: rgba(255, 255, 255, 0.62);
236
+ }
237
+
238
+ #jekyll-dice-tray .jdt-table-sep {
239
+ color: rgba(255, 255, 255, 0.48);
240
+ }
241
+
171
242
  #jekyll-dice-tray .jdt-result .jdt-rolls {
172
243
  color: rgba(255, 255, 255, 0.65);
173
244
  }
@@ -179,5 +250,13 @@ a.dice-tray-roll:visited {
179
250
  #jekyll-dice-tray .jdt-result .jdt-vs-sep {
180
251
  color: rgba(255, 255, 255, 0.48);
181
252
  }
253
+
254
+ #jekyll-dice-tray .jdt-success {
255
+ color: #3fb950;
256
+ }
257
+
258
+ #jekyll-dice-tray .jdt-failure {
259
+ color: #f85149;
260
+ }
182
261
  }
183
262
 
@@ -93,6 +93,28 @@
93
93
  };
94
94
  }
95
95
 
96
+ var chance = compact.match(/^(\d{1,3})-in-(\d{1,4})$/i);
97
+ if (chance) {
98
+ var successOn = parseInt(chance[1], 10);
99
+ var sides = parseInt(chance[2], 10);
100
+
101
+ if (!Number.isFinite(successOn) || !Number.isFinite(sides)) {
102
+ return { kind: "invalid", raw: s };
103
+ }
104
+ if (successOn < 1) successOn = 1;
105
+ if (successOn > 100) successOn = 100;
106
+ if (sides < 2) sides = 2;
107
+ if (sides > 10000) sides = 10000;
108
+ if (successOn > sides) return { kind: "invalid", raw: s };
109
+
110
+ return {
111
+ kind: "chance",
112
+ success_on: successOn,
113
+ sides: sides,
114
+ normalized: String(successOn) + "-in-" + String(sides),
115
+ };
116
+ }
117
+
96
118
  var m = compact.match(/^(\d{0,3})d(\d{1,4})([+-]\d{1,5})?$/i);
97
119
  if (!m) return { kind: "invalid", raw: s };
98
120
 
@@ -126,6 +148,118 @@
126
148
  }
127
149
 
128
150
  function mountTray(root) {
151
+ function normalizeCellText(node) {
152
+ return String(node && node.textContent ? node.textContent : "")
153
+ .replace(/\s+/g, " ")
154
+ .trim();
155
+ }
156
+
157
+ function cellInnerHtml(node) {
158
+ return node && node.innerHTML ? node.innerHTML.trim() : "";
159
+ }
160
+
161
+ function findTableHeaderRow(table) {
162
+ if (!table) return null;
163
+ var thead = table.querySelector("thead");
164
+ if (thead) {
165
+ var theadRows = thead.querySelectorAll("tr");
166
+ if (theadRows.length) return theadRows[theadRows.length - 1];
167
+ }
168
+ var rows = table.querySelectorAll("tr");
169
+ for (var i = 0; i < rows.length; i++) {
170
+ if (rows[i].querySelector("th")) return rows[i];
171
+ }
172
+ return table.querySelector("tr");
173
+ }
174
+
175
+ function firstColumnNumber(row) {
176
+ if (!row) return null;
177
+ var cell = row.querySelector("td, th");
178
+ if (!cell) return null;
179
+ var text = normalizeCellText(cell);
180
+ if (!/^\d+$/.test(text)) return null;
181
+ return parseInt(text, 10);
182
+ }
183
+
184
+ function isLookupTable(table, headerRow) {
185
+ if (!table || !headerRow) return false;
186
+ var rows = table.querySelectorAll("tr");
187
+ var matches = 0;
188
+ for (var i = 0; i < rows.length; i++) {
189
+ if (rows[i] === headerRow) continue;
190
+ if (firstColumnNumber(rows[i]) !== null) matches++;
191
+ }
192
+ return matches >= 2;
193
+ }
194
+
195
+ function findLookupRow(table, headerRow, rollTotal) {
196
+ var rows = table.querySelectorAll("tr");
197
+ for (var i = 0; i < rows.length; i++) {
198
+ var row = rows[i];
199
+ if (row === headerRow) continue;
200
+ var n = firstColumnNumber(row);
201
+ if (n !== null && n === rollTotal) return row;
202
+ }
203
+ return null;
204
+ }
205
+
206
+ function getLookupTableContext(anchor, rollTotal) {
207
+ if (!anchor || !anchor.closest || rollTotal == null) return null;
208
+ var cell = anchor.closest("td, th");
209
+ if (!cell) return null;
210
+ var clickRow = cell.closest("tr");
211
+ if (!clickRow) return null;
212
+ var table = clickRow.closest("table");
213
+ if (!table) return null;
214
+
215
+ var headerRow = findTableHeaderRow(table);
216
+ if (!headerRow || !headerRow.contains(cell)) return null;
217
+ if (!isLookupTable(table, headerRow)) return null;
218
+
219
+ var matchedRow = findLookupRow(table, headerRow, rollTotal);
220
+ if (!matchedRow) return null;
221
+
222
+ var headerCells = Array.prototype.slice.call(headerRow.querySelectorAll("td, th"));
223
+ var valueCells = Array.prototype.slice.call(matchedRow.querySelectorAll("td, th"));
224
+ var columns = [];
225
+
226
+ for (var c = 0; c < valueCells.length; c++) {
227
+ var headerCell = headerCells[c];
228
+ var valueCell = valueCells[c];
229
+ columns.push({
230
+ header: headerCell ? normalizeCellText(headerCell) : "",
231
+ header_html: headerCell ? cellInnerHtml(headerCell) : "",
232
+ value: normalizeCellText(valueCell),
233
+ value_html: cellInnerHtml(valueCell),
234
+ });
235
+ }
236
+
237
+ return columns.length ? { columns: columns } : null;
238
+ }
239
+
240
+ function appendTableContext(entry, tableContext) {
241
+ if (!tableContext || !tableContext.columns || !tableContext.columns.length) return;
242
+
243
+ var ctx = el("div", { class: "jdt-table-context" });
244
+ tableContext.columns.forEach(function (col) {
245
+ if (!col || (!col.value && !col.value_html)) return;
246
+ var line = el("div", { class: "jdt-table-row" });
247
+ if (col.header || col.header_html) {
248
+ var headerSpan = el("span", { class: "jdt-table-header" });
249
+ if (col.header_html) headerSpan.innerHTML = col.header_html;
250
+ else headerSpan.textContent = col.header;
251
+ line.appendChild(headerSpan);
252
+ line.appendChild(el("span", { class: "jdt-table-sep" }, ": "));
253
+ }
254
+ var valueSpan = el("span", { class: "jdt-table-value" });
255
+ if (col.value_html) valueSpan.innerHTML = col.value_html;
256
+ else valueSpan.textContent = col.value || "";
257
+ line.appendChild(valueSpan);
258
+ ctx.appendChild(line);
259
+ });
260
+ if (ctx.childNodes.length) entry.appendChild(ctx);
261
+ }
262
+
129
263
  var toggle = qs(".jdt-toggle", root);
130
264
  var body = qs(".jdt-body", root);
131
265
  var input = qs(".jdt-input", root);
@@ -237,10 +371,31 @@
237
371
  item.right_label || "",
238
372
  item.mod || 0,
239
373
  item.time || "",
240
- item.mode || "high"
374
+ item.mode || "high",
375
+ item.table_context || null,
376
+ true
377
+ );
378
+ } else if (item.kind === "chance") {
379
+ addChanceEntry(
380
+ item.expr || "",
381
+ item.roll,
382
+ item.success_on,
383
+ item.sides,
384
+ !!item.success,
385
+ item.time || "",
386
+ item.table_context || null,
387
+ true
241
388
  );
242
389
  } else if (item.kind === "roll") {
243
- addRollEntry(item.expr || "", item.total, item.rolls || [], item.mod || 0, item.time || "");
390
+ addRollEntry(
391
+ item.expr || "",
392
+ item.total,
393
+ item.rolls || [],
394
+ item.mod || 0,
395
+ item.time || "",
396
+ item.table_context || null,
397
+ true
398
+ );
244
399
  }
245
400
  });
246
401
  log.scrollTop = log.scrollHeight;
@@ -264,10 +419,41 @@
264
419
  pushHistory({ kind: "system", title: title, body: body, time: timeStr });
265
420
  }
266
421
 
267
- function addRollEntry(expr, total, rolls, mod, timeStr) {
422
+ function addChanceEntry(expr, roll, successOn, sides, success, timeStr, tableContext, skipHistory) {
423
+ var entry = el("div", { class: "jdt-entry", title: expr });
424
+ var colorClass = success ? "jdt-success" : "jdt-failure";
425
+ var result = el("div", { class: "jdt-result" });
426
+
427
+ result.appendChild(el("strong", { class: colorClass }, String(roll)));
428
+
429
+ var bracket = el("span", { class: "jdt-rolls" }, " [");
430
+ bracket.appendChild(el("span", { class: colorClass }, String(roll)));
431
+ bracket.appendChild(el("span", null, "]"));
432
+ result.appendChild(bracket);
433
+
434
+ entry.appendChild(result);
435
+ appendTableContext(entry, tableContext);
436
+
437
+ log.appendChild(entry);
438
+ log.scrollTop = log.scrollHeight;
439
+
440
+ if (skipHistory) return;
441
+
442
+ var item = {
443
+ kind: "chance",
444
+ expr: expr,
445
+ roll: roll,
446
+ success_on: successOn,
447
+ sides: sides,
448
+ success: success,
449
+ time: timeStr,
450
+ };
451
+ if (tableContext) item.table_context = tableContext;
452
+ pushHistory(item);
453
+ }
454
+
455
+ function addRollEntry(expr, total, rolls, mod, timeStr, tableContext, skipHistory) {
268
456
  var entry = el("div", { class: "jdt-entry", title: expr });
269
- // expression
270
- //entry.appendChild(el("div", { class: "jdt-expr" }, expr));
271
457
 
272
458
  var result = el("div", { class: "jdt-result" });
273
459
  result.appendChild(el("strong", null, String(total)));
@@ -277,18 +463,20 @@
277
463
  }
278
464
  result.appendChild(el("span", { class: "jdt-rolls" }, " " + rollsText));
279
465
 
280
- // result
281
466
  entry.appendChild(result);
467
+ appendTableContext(entry, tableContext);
282
468
 
283
- // time
284
- //entry.appendChild(el("div", { class: "jdt-details" }, timeStr));
285
- log.appendChild(entry); // newest at bottom
469
+ log.appendChild(entry);
286
470
  log.scrollTop = log.scrollHeight;
287
471
 
288
- pushHistory({ kind: "roll", expr: expr, total: total, rolls: rolls, mod: mod, time: timeStr });
472
+ if (skipHistory) return;
473
+
474
+ var item = { kind: "roll", expr: expr, total: total, rolls: rolls, mod: mod, time: timeStr };
475
+ if (tableContext) item.table_context = tableContext;
476
+ pushHistory(item);
289
477
  }
290
478
 
291
- function addBestOf2Entry(expr, chosenTotal, leftRolls, rightRolls, leftIsWinner, leftLabel, rightLabel, mod, timeStr, mode) {
479
+ function addBestOf2Entry(expr, chosenTotal, leftRolls, rightRolls, leftIsWinner, leftLabel, rightLabel, mod, timeStr, mode, tableContext, skipHistory) {
292
480
  var entry = el("div", { class: "jdt-entry", title: expr });
293
481
  entry.appendChild(el("div", { class: "jdt-expr" }, expr));
294
482
 
@@ -318,12 +506,15 @@
318
506
  }
319
507
 
320
508
  entry.appendChild(result);
509
+ appendTableContext(entry, tableContext);
321
510
  entry.appendChild(el("div", { class: "jdt-details" }, timeStr));
322
511
 
323
512
  log.appendChild(entry);
324
513
  log.scrollTop = log.scrollHeight;
325
514
 
326
- pushHistory({
515
+ if (skipHistory) return;
516
+
517
+ var item = {
327
518
  kind: "bestof2",
328
519
  expr: expr,
329
520
  chosen_total: chosenTotal,
@@ -335,24 +526,44 @@
335
526
  mode: mode,
336
527
  mod: mod,
337
528
  time: timeStr,
338
- });
529
+ };
530
+ if (tableContext) item.table_context = tableContext;
531
+ pushHistory(item);
339
532
  }
340
533
 
341
534
  function showHelp() {
342
535
  addSystemEntry(
343
- "Usage: 1d6, d4, 2d8+1",
344
- "Click linked dice like 1d20+5 or bracket modifiers like [+1] (rolls d20+1). Commands: /help, /clear",
536
+ "Usage: 1d6, d4, 2d8+1, 2-in-6",
537
+ "Click linked dice like 1d20+5, chance like 2-in-6 (roll d6, 1-2 succeed), or bracket modifiers like [+1] (rolls d20+1). Clicking a table die (e.g. 1d12) shows the matching row. Commands: /help, /clear",
345
538
  nowTime()
346
539
  );
347
540
  }
348
541
 
349
- function doRoll(raw) {
542
+ function doRoll(raw, meta) {
543
+ meta = meta || {};
544
+ var anchor = meta.anchor || null;
350
545
  var p = parseExpr(raw);
351
546
  if (p.kind === "empty") return;
352
547
  if (p.kind === "help") return showHelp();
353
548
  if (p.kind === "clear") return clearStorageAndUi();
354
- if (p.kind !== "roll" && p.kind !== "bestof2") {
355
- addSystemEntry("Unrecognized roll: " + p.raw, "Try: 1d6, d4, 2d8+1 or /help", nowTime());
549
+ if (p.kind !== "roll" && p.kind !== "bestof2" && p.kind !== "chance") {
550
+ addSystemEntry("Unrecognized roll: " + p.raw, "Try: 1d6, d4, 2-in-6, 2d8+1 or /help", nowTime());
551
+ return;
552
+ }
553
+
554
+ if (p.kind === "chance") {
555
+ var chanceRoll = rollDice(1, p.sides);
556
+ var roll = chanceRoll.rolls[0];
557
+ var success = roll <= p.success_on;
558
+ addChanceEntry(
559
+ p.normalized,
560
+ roll,
561
+ p.success_on,
562
+ p.sides,
563
+ success,
564
+ nowTime(),
565
+ getLookupTableContext(anchor, roll)
566
+ );
356
567
  return;
357
568
  }
358
569
 
@@ -377,14 +588,15 @@
377
588
  rightLabel,
378
589
  p.mod,
379
590
  nowTime(),
380
- mode
591
+ mode,
592
+ getLookupTableContext(anchor, chosenTotal)
381
593
  );
382
594
  return;
383
595
  }
384
596
 
385
597
  var r = rollDice(p.count, p.sides);
386
598
  var total = r.total + p.mod;
387
- addRollEntry(p.normalized, total, r.rolls, p.mod, nowTime());
599
+ addRollEntry(p.normalized, total, r.rolls, p.mod, nowTime(), getLookupTableContext(anchor, total));
388
600
  }
389
601
 
390
602
  toggle.addEventListener("click", function () {
@@ -446,7 +658,7 @@
446
658
  if (!expr) return;
447
659
  e.preventDefault();
448
660
  setExpanded(true);
449
- doRoll(expr);
661
+ doRoll(expr, { anchor: a });
450
662
  });
451
663
 
452
664
  // Public API
@@ -14,6 +14,14 @@ module Jekyll
14
14
  (?![A-Za-z0-9_])
15
15
  /x
16
16
 
17
+ CHANCE_RE = /
18
+ (?<![A-Za-z0-9_])
19
+ \d{1,3}
20
+ \s*-\s*in\s*-\s*
21
+ \d{1,4}
22
+ (?![A-Za-z0-9_])
23
+ /ix
24
+
17
25
  THAC0_WORD_RE = /THAC0/i
18
26
 
19
27
  BRACKET_INNER_RE = /
@@ -105,6 +113,17 @@ module Jekyll
105
113
  }
106
114
  end
107
115
 
116
+ text.to_enum(:scan, CHANCE_RE).each do
117
+ m = Regexp.last_match
118
+ matches << {
119
+ start: m.begin(0),
120
+ end: m.end(0),
121
+ expr: m[0].gsub(/\s+/, ""),
122
+ label_start: m.begin(0),
123
+ label_end: m.end(0),
124
+ }
125
+ end
126
+
108
127
  text.to_enum(:scan, THAC0_INLINE_RE).each do
109
128
  matches << bracket_mod_hit(text, Regexp.last_match)
110
129
  end
@@ -1,6 +1,6 @@
1
1
  module Jekyll
2
2
  module DiceTray
3
- VERSION = "0.1.0"
3
+ VERSION = "0.2.0"
4
4
  end
5
5
  end
6
6
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-dice-tray
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - directsun