1
0
Fork 0

Refactored

This commit is contained in:
Joe Wroten 2016-07-24 19:56:24 -05:00 committed by sharpshark28
parent f9867525d3
commit 1f17d344fa
25 changed files with 2096 additions and 512 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

11
README.md Normal file
View file

@ -0,0 +1,11 @@
# My Spells (5e)
...is a user friendly mobile responsive app designed to do one thing well: _View Spells_.
[Check it out on github.io](https://sharpshark28.github.io/my_spells/)!
## For Developers
1. Clone/Fork Repo
2. `npm install`
3. `npm run develop`
You may wish to run a local http server such as [http-server](https://github.com/indexzero/http-server).

BIN
assets/.DS_Store vendored Normal file

Binary file not shown.

181
assets/app.css Normal file
View file

@ -0,0 +1,181 @@
body {
background: #fafafa;
}
.do-nothing {
pointer-events: none;
}
.mdl-layout-title i {
position: relative;
bottom: -.125em;
}
#empty {
padding-bottom: 100%;
text-align: center;
background: url('wand.svg') no-repeat top center;
}
.mdl-navigation .mdl-textfield__label i {
vertical-align: top;
font-size: 1.25em;
}
.mdl-navigation .mdl-textfield__input {
box-sizing: border-box;
padding-left: 1em;
padding-right: 1em;
}
.mdl-navigation .mdl-textfield__label {
padding-left: 1em;
}
ul,
ol {
padding: 0 1em;
}
.text-center {
text-align: center;
}
.left {
float: left;
}
.right {
float: right;
}
.page-content {
position: relative;
}
.page-content .mdl-grid {
padding: 0;
}
.page-content .mdl-cell {
margin: 0;
}
[data-template=spell-details] {
transition: margin-top 500ms ease-out;
padding: 0 2rem;
}
[data-template=spell-details] .description {
max-height: 13em;
overflow: auto;
}
.mdl-layout__header [data-action-details=""] {
display: none;
position: absolute;
left: 0;
z-index: 10;
height: 4rem;
width: 4rem;
}
.mdl-layout__header [data-action-details=""] i {
position: absolute;
top: 1rem;
left: 1rem;
}
[data-template=spell-details] [data-action-details=""] {
position: absolute;
top: .5rem;
right: .5rem;
}
@media (max-width: 840px) {
[data-template=spell-details] {
margin-top: 0 !important;
}
#spell-list-container,
[data-template=spell-details] {
transition: left 250ms;
}
#spell-list-container {
position: absolute;
width: 100%;
left: 0;
}
body.details #spell-list-container {
left: -95%;
}
body.details #spell-list-container::after {
content: '';
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
background: black;
opacity: .25;
}
[data-template=spell-details] {
position: fixed;
width: 95%;
left: 100%;
overflow: auto;
max-height: calc(100% - 56px)
}
body.details [data-template=spell-details] {
left: 5%;
}
body.details .mdl-layout__header [data-action-details=""] {
display: block;
}
body.details .mdl-layout__header .mdl-layout__drawer-button {
display: none;
}
body.details [data-template=spell-details] [data-action-details=""] {
display: none;
}
}
[data-action-details] {
cursor: pointer;
}
[data-action-sort],
[data-template=class-list] label,
.classes,
.spell-level,
.spell-name,
.spell-school {
text-transform: capitalize;
}
[data-action-sort] {
cursor: pointer;
user-select: none;
}
[data-action-sort]:hover {
background: #eee;
}
[data-action-sort] i.material-icons.mdl-list__item-icon {
position: relative;
bottom: -.25em;
color: inherit;
}
#share-url {
padding-left: 2em;
}

123
assets/wand.svg Normal file
View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1"
id="svg7404" xmlns:svg="http://www.w3.org/2000/svg" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="232 -592.3 824.9 1155"
style="enable-background:new 232 -592.3 824.9 1155;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EDF9F7;}
.st1{fill:#7BCBBF;}
.st2{fill:#9BD8D0;}
.st3{fill:#CBE9E4;}
.st4{fill:#6EC3BB;}
.st5{fill:#FFFFFF;}
.st6{fill:#EED7B2;}
.st7{fill:#DAC4BF;}
.st8{fill:#CCDBB9;}
.st9{fill:#DBEFBE;}
.st10{fill:#D4E6BC;}
.st11{fill:#F9FBF5;}
.st12{fill:#D6C2BE;}
.st13{fill:#E0E4DD;}
.st14{fill:#B3D3C8;}
.st15{fill:#9FCABF;}
.st16{fill:#D3DBD9;}
.st17{fill:#A6C9C4;}
.st18{fill:#BCD1CD;}
.st19{fill:#92C0BB;}
.st20{fill:#DFEAE9;}
</style>
<g>
<path class="st0" d="M544.6-229L544.6-229L544.6-229 M501.7-241.2L501.7-241.2l-19.1,22.8l31.1,13c0.1-1.1,0.4-2.1,1.1-2.9
c1-1.2,2.4-1.8,4-1.8c1.4,0,2.9,0.5,4.1,1.5c0.9,0.7,1.5,1.6,1.9,2.6l19.4-23.3L501.7-241.2"/>
<polyline class="st1" points="474.8,-209.1 447.4,-176.2 466.8,-136.4 489.6,-163.7 465.5,-197.8 474.9,-209.1 474.8,-209.1 "/>
<path class="st2" d="M544.5-229.4l-0.2,0.2l-19.4,23.3c0.7,1.8,0.5,3.9-0.7,5.4c-0.3,0.4-0.7,0.7-1.1,0.9l21.1,32.6l19.3-23.1
L544.6-229l0,0L544.5-229.4"/>
<polyline class="st3" points="489.9,-163.8 489.7,-163.6 489.6,-163.7 466.8,-136.4 466.7,-136.2 508.6,-124.3 508.6,-124.3
527,-146.3 489.9,-163.8 "/>
<path class="st2" d="M482.6-218.4l-7.8,9.3l-9.4,11.3l24.2,34.1l0.1,0.1l0.2-0.2l-0.2-0.1l29.3-35c-1-0.2-2-0.7-2.9-1.4
c-1.6-1.3-2.4-3.3-2.3-5.1L482.6-218.4L482.6-218.4"/>
<path class="st4" d="M523.1-199.6c-0.8,0.6-1.9,0.8-2.9,0.8c-0.4,0-0.8,0-1.2-0.1l-29.3,35l0.2,0.1l37.1,17.5l8.2-9.7l9.1-10.9
L523.1-199.6"/>
<path class="st5" d="M518.9-210c-1.5,0-3,0.6-4,1.8c-0.7,0.8-1.1,1.9-1.1,2.9c-0.1,1.8,0.7,3.7,2.3,5.1c0.9,0.7,1.9,1.2,2.9,1.4
c0.4,0.1,0.8,0.1,1.2,0.1c1,0,2.1-0.3,2.9-0.8c0.4-0.3,0.7-0.6,1.1-0.9c1.2-1.5,1.4-3.5,0.7-5.4c-0.4-1-1-1.9-1.9-2.6
C521.7-209.5,520.3-210,518.9-210"/>
<path class="st6" d="M874.4,91.9c-2,9.3-4.7,18-7.1,25.7l133.5,111.7l-2.3,2.7l0,0l13.2-15.9L874.4,91.9 M577.3-176.6l2.2,53.4
L624-86.1c4.1-1.9,9-2.9,14.2-2.9c3.2,0,6.5,0.4,9.6,1.2c13,3.4,19.8,12.9,15,21.2c-0.3,0.5-0.6,1-1,1.5c-0.7,2.4-1.5,5.5-2.1,8.9
L839.8,94.6c0-0.9,0-1.9-0.1-2.8l0,0c0,0,0,0,0,0c0.3-13.5,2-27.5-1.3-32.3L577.3-176.6"/>
<path class="st7" d="M867.3,117.6c-2.8,8.9-5.1,16.4-5.1,21.9c-0.1,3.7,0.4,6.8,1.2,9.5L987.6,245l10.9-13.1l2.3-2.7L867.3,117.6
M659.7-56.2c-3,16.4-2.7,42.8,21.9,63.4c2.2,1.8,4.6,3.4,7,4.5l67.1,52.1l0,0c-0.5,0.2-1,0.4-1.5,0.7l87.3,67.6
c-0.9-10.2-1.3-22.6-1.7-37.4L659.7-56.2 M579.6-123.3l0.1,2.5l-55.1,7.4L614-44.1c-0.6-5.7-0.7-10.8-0.7-15
c0-8.5,0.7-13.6,0.7-13.6s0-0.1,0.1-0.3c-0.1-2,0.4-4,1.5-6c1.7-3,4.7-5.4,8.4-7.1L579.6-123.3"/>
<path class="st8" d="M839.8,91.7L839.8,91.7c0,1,0,1.9,0.1,2.8c0.4,14.8,0.8,27.2,1.7,37.4c1.9,21.5,6,33.3,17.1,36.7
c3.4,1.1,6.4,1.5,9,1.5c4.4,0,7.6-1.2,9.9-2.6c-2.3,1.4-5.5,2.6-9.9,2.6c-2.6,0-5.6-0.4-9-1.5C841.9,163.6,841,139.4,839.8,91.7
M806.6-3.9c-10.8,0-21.9,2-32.8,4.7l23.9,21.6c1,0,2.1-0.1,3.1-0.1c21.4,0,38,9.1,49.3,22.5c12.8,15.3,3.1,56.1,3.9,79
c0.9,22.4-5.4,36.8,27.2,40.6c0.4-0.5,0.6-0.8,0.6-0.8s-14.1-0.8-18.4-14.8c-0.8-2.7-1.3-5.8-1.2-9.5c0.1-5.4,2.4-13,5.1-21.9
c2.4-7.7,5.1-16.4,7.1-25.7c5.4-25.7,5-55.8-24.9-80.8C836.3,0,821.7-3.9,806.6-3.9 M613.2-59.1c0,4.2,0.2,9.3,0.7,15
c2.5,25.9,12.6,64.3,47.8,93.6c20.4,17,39.5,23,56.7,23c13.2,0,25.2-3.5,35.8-8.1c0.5-0.2,1-0.4,1.5-0.7l0,0
c-10.9,4.9-23.5,8.7-37.2,8.7c-17.2,0-36.3-5.9-56.7-23C618.9,13.7,613.2-35.6,613.2-59.1 M661.9-65.1c-4.1,5.4-12.5,8.6-21.6,8.6
c-1.7,0-3.4-0.1-5.1-0.3c-0.8,25.1,5.1,61.8,29.9,82.4c10.6,8.8,25.2,11.8,40.9,11.8c4.8,0,9.7-0.3,14.7-0.8l35,27.1l-67.1-52.1
c-2.3-1.1-4.7-2.6-7-4.5c-24.6-20.5-24.9-46.9-21.9-63.4C660.3-59.7,661.1-62.7,661.9-65.1"/>
<path class="st9" d="M800.8,22.4c-1,0-2.1,0-3.1,0.1c0,0-0.1,0-0.1,0l40,36c0.3,0.3,0.6,0.6,0.8,0.9c3.3,4.9,1.6,18.9,1.3,32.3l0,0
c1.2,47.7,2.2,71.8,18.9,77c3.4,1.1,6.4,1.5,9,1.5c4.4,0,7.6-1.2,9.9-2.6c1.8-1.1,3-2.3,3.7-3.1c0,0,0,0,0,0
c-32.6-3.8-26.3-18.2-27.2-40.6c-0.8-22.9,8.9-63.7-3.9-79C838.8,31.5,822.2,22.4,800.8,22.4 M614.1-73c-0.1,0.2-0.1,0.3-0.1,0.3
s-0.7,5.1-0.7,13.6c0,23.6,5.7,72.8,48.5,108.6c20.4,17,39.5,23,56.7,23c13.8,0,26.3-3.8,37.2-8.7l0,0l-35-27.1
c-4.9,0.5-9.9,0.8-14.7,0.8c-15.7,0-30.3-3.1-40.9-11.8c-24.8-20.7-30.8-57.4-29.9-82.4c-1.5-0.2-3-0.5-4.5-0.9
C620.6-60.3,614.3-66.5,614.1-73"/>
<path class="st10" d="M638.2-59.6c-2,0-4.1-0.7-5.9-2.2c-3.7-3.1-4.4-8.2-1.7-11.4c1.4-1.7,3.5-2.5,5.7-2.5c2,0,4.1,0.7,5.9,2.2
c3.7,3.1,4.4,8.2,1.7,11.4C642.5-60.5,640.4-59.6,638.2-59.6 M638.2-89c-5.2,0-10.1,1-14.2,2.9c-3.7,1.7-6.7,4.2-8.4,7.1
c-1.1,2-1.6,4-1.5,6c0.3,6.5,6.6,12.7,16.5,15.3c1.5,0.4,3,0.7,4.5,0.9c1.7,0.2,3.4,0.3,5.1,0.3c9.1,0,17.5-3.2,21.6-8.6
c0.4-0.5,0.7-1,1-1.5c4.7-8.3-2-17.8-15-21.2C644.6-88.6,641.4-89,638.2-89"/>
<path class="st11" d="M636.3-75.8c-2.2,0-4.3,0.9-5.7,2.5c-2.7,3.3-2,8.4,1.7,11.4c1.8,1.5,3.9,2.2,5.9,2.2c2.2,0,4.3-0.9,5.7-2.5
c2.7-3.3,2-8.4-1.7-11.4C640.5-75.1,638.4-75.8,636.3-75.8"/>
<polyline class="st12" points="577.3,-178.5 549.7,-145.7 522.2,-113.1 524.5,-113.4 579.7,-120.8 579.6,-123.3 577.3,-176.6
577.3,-178.5 "/>
<polyline class="st13" points="563.6,-190 563.5,-190 564.1,-188.9 537.9,-157.6 550.6,-147 577.1,-178.7 563.6,-190 "/>
<polyline class="st14" points="563.5,-190 544.2,-166.9 544.4,-166.7 537.2,-158.2 537.9,-157.6 564.1,-188.9 563.5,-190 "/>
<polyline class="st15" points="544.2,-166.9 535.1,-156 537,-158.3 537.2,-158.2 544.4,-166.7 544.2,-166.9 "/>
<polyline class="st16" points="537.9,-157.6 536.7,-156.1 536.7,-156.1 509.7,-124 508.6,-124.3 522.1,-113 550.6,-147
537.9,-157.6 "/>
<polyline class="st17" points="537.2,-158.2 536.6,-157.4 537.3,-156.8 536.7,-156.1 536.7,-156.1 537.9,-157.6 537.2,-158.2 "/>
<polyline class="st18" points="536.6,-157.4 527.2,-146.2 527,-146.3 508.6,-124.3 508.6,-124.3 509.7,-124 536.7,-156.1
537.3,-156.8 536.6,-157.4 "/>
<polyline class="st19" points="537,-158.3 535.1,-156 527,-146.3 527.2,-146.2 536.6,-157.4 537.2,-158.2 537,-158.3 "/>
<polygon class="st20" points="362.8,-106.5 366.8,-137.1 394.1,-151.2 366.3,-164.3 361.4,-194.7 340.3,-172.4 309.9,-177.1
324.6,-150 310.7,-122.6 341,-128.3 "/>
<polygon class="st20" points="394.7,-386.8 409.4,-403.2 431.2,-400.3 420.2,-419.4 429.7,-439.2 408.2,-434.6 392.3,-449.7
390,-427.9 370.7,-417.4 390.7,-408.5 "/>
<polygon class="st20" points="457,20.4 461.6,-14.7 493.1,-30.9 461.1,-46.1 455.4,-81.1 431,-55.3 396,-60.7 413,-29.6 397,2
431.8,-4.5 "/>
<polygon class="st20" points="634.6,-418.3 636.7,-434.1 650.9,-441.4 636.5,-448.2 633.9,-463.9 623,-452.4 607.2,-454.8
614.9,-440.8 607.7,-426.6 623.3,-429.5 "/>
<polygon class="st20" points="803.5,-10.6 800.3,-33.9 817.8,-49.6 794.6,-53.9 785,-75.3 773.8,-54.7 750.5,-52.1 766.7,-35.1
761.9,-12.1 783.1,-22.3 "/>
<polygon class="st20" points="608.2,41.7 610.5,23.5 626.9,15.1 610.3,7.2 607.4,-10.9 594.7,2.5 576.6,-0.4 585.4,15.8
577.1,32.1 595.1,28.8 "/>
<polygon class="st20" points="267.8,88.3 269.8,72.5 284,65.2 269.6,58.4 267,42.6 256.1,54.2 240.4,51.8 248,65.8 240.8,80
256.5,77.1 "/>
<polygon class="st20" points="489.7,348 497.5,334.2 513.4,332.8 502.6,321 506.2,305.5 491.7,312.1 478,303.9 479.8,319.7
467.8,330.2 483.4,333.4 "/>
<polygon class="st20" points="989.4,-441.9 997.3,-455.7 1013.1,-457.1 1002.4,-468.9 1005.9,-484.4 991.4,-477.8 977.8,-486
979.6,-470.2 967.5,-459.7 983.2,-456.5 "/>
<polygon class="st20" points="934.4,137.9 939.1,126.4 951.4,123.8 941.9,115.7 943.1,103.3 932.5,109.8 921.1,104.8 924,116.9
915.7,126.2 928.2,127.1 "/>
<polygon class="st20" points="326.5,108.6 324.9,96.3 334.1,87.9 321.8,85.6 316.8,74.3 310.8,85.2 298.5,86.6 307.1,95.6
304.5,107.8 315.7,102.4 "/>
<polygon class="st20" points="530.6,104.7 532,93.7 541.9,88.7 531.9,83.9 530.1,73 522.5,81 511.6,79.3 516.9,89 511.9,98.9
522.7,96.9 "/>
<polygon class="st20" points="742.1,-272 745.1,-282.7 755.5,-286.3 746.3,-292.4 746.1,-303.5 737.4,-296.6 726.8,-299.8
730.7,-289.5 724.3,-280.4 735.4,-280.9 "/>
<polygon class="st20" points="939.7,-208.2 950.1,-211.9 959.1,-205.5 958.7,-216.6 967.5,-223.3 956.9,-226.3 953.4,-236.7
947.2,-227.6 936.1,-227.4 943,-218.7 "/>
<polygon class="st20" points="825.7,-570.5 829.8,-574.5 835.4,-573.5 832.8,-578.6 835.5,-583.7 829.9,-582.8 825.9,-587
825,-581.3 819.8,-578.8 824.9,-576.2 "/>
<polygon class="st20" points="862.9,475.8 867,471.8 872.6,472.8 870,467.7 872.7,462.6 867.1,463.5 863.1,459.3 862.2,465
857,467.5 862.1,470.1 "/>
<polygon class="st20" points="503.3,-554 507.4,-558 513.1,-557 510.5,-562.1 513.2,-567.2 507.5,-566.3 503.6,-570.5
502.7,-564.8 497.4,-562.3 502.6,-559.7 "/>
<polygon class="st20" points="706.5,302 710.6,297.9 716.3,299 713.6,293.8 716.4,288.7 710.7,289.6 706.7,285.5 705.8,291.1
700.6,293.6 705.7,296.2 "/>
<polygon class="st20" points="328,488.5 333.8,487.9 337.6,492.2 338.7,486.5 344,484.2 339,481.4 338.4,475.7 334.2,479.6
328.6,478.3 331,483.6 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
dist/.DS_Store vendored Normal file

Binary file not shown.

279
dist/app.js vendored Normal file
View file

@ -0,0 +1,279 @@
/**
* Misc helper functions
*/
Object.values = x => Object.keys(x).reduce((y, z) => y.push(x[z]) && y, []);
const debounce = (func, wait, immediate) => {
let timeout;
return function () {
let context = this,
args = arguments;
let later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
const el = id => $(`[data-template=${ id }]`)[0] || console.error('Unable to render to', id);
const clone = obj => JSON.parse(JSON.stringify(obj));
/**
* Global store and view holders
*/
let store = {};
let view = {};
/**
* Init Local Storage
*/
const localStorageDefault = (key, val) => {
if (localStorage.getItem(key) === null) localStorage.setItem(key, val);
};
let defaults = {
tableSortName: 'name',
tableSortRev: false,
classes: [],
search: ''
};
for (let cur in defaults) localStorageDefault(cur, defaults[cur]);
/**
* Render Table Sort
*/
store.tableSort = {
data: ['name', 'school', 'level'],
current: localStorage.getItem('tableSortName'),
rev: localStorage.getItem('tableSortRev') !== 'false'
};
view.table_sort = Monkberry.render(table_sort, el('table-sort'));
view.table_sort.update(store.tableSort);
/**
* Render Spell List
*/
view.spell_list = Monkberry.render(spell_list, el('spell-list'));
view.spell_list.update({});
/**
* Render Spell Details
*/
view.spell_details = Monkberry.render(spell_details, el('spell-details'));
view.spell_details.update({ data: {} });
/**
* Render Class List
*/
store.classes = {
data: [],
current: localStorage.getItem('classes') ? localStorage.getItem('classes').split(',') : []
};
view.class_list = Monkberry.render(class_list, el('class-list'));
view.class_list.update(store.classes);
/**
* Render Search
*/
store.search = localStorage.getItem('search');
view.search_field = Monkberry.render(search_field, el('search-field'));
view.search_field.update({ data: store.search });
/**
* Discover Classes
*/
const discoverClasses = spells => {
let classes = [];
spells.forEach(spell => {
if (!spell.classes) return;
spell.classes.forEach(current => {
if (!classes.includes(current)) classes.push(current);
});
});
return classes.sort((a, b) => a > b);
};
/**
* Emphasis on important string bits
* @param {string} string
*/
const emphasis = str => {
let keywords = ['constitution', 'con', 'intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check'];
keywords.forEach(word => {
let r = new RegExp(` ${ word } `, 'gi');
str = str.replace(r, o => `<em class="mdl-color-text--teal-600">${ o }</em>`);
});
str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `<strong>${ o }</strong>`);
return str;
};
/**
* Init Spells
*/
const initSpells = s => s.map((spell, i) => {
spell.selected = false;
spell.ranking = 0;
spell.level = parseInt(spell.level) ? spell.level : 0;
spell.prettyLevel = spell.level === 0 ? 'C' : spell.level;
return spell;
});
/**
* Sort Spells
*/
const sortSpells = (s, sortBy, reverse) => s.sort((a, b) => {
let hasFilters = store.classes.current.length || store.search.length;
let by = sortBy || hasFilters ? 'ranking' : store.tableSort.current;
let rev = reverse || hasFilters ? false : store.tableSort.rev;
if (by) {
if (a[by] < b[by]) return rev ? 1 : -1;
if (a[by] > b[by]) return rev ? -1 : 1;
return 0;
}
});
/**
* Search Spells
* @param {Array} spells
* @param {String} ex 'acid spray'
* @return {Array} filtered spells
*/
const searchSpells = (spells, search) => {
// Convert search to array of words
search = search.split(' ');
// Clone spells so we don't affect the original
spells = clone(spells);
// Reset rankings
spells = spells.map(s => {
s.ranking = 0;
return s;
});
// Rank spells by # of occurances of search terms
spells = spells.map(spell => {
search.forEach(term => {
let spellText = Object.values(spell).join(' ');
let regFind = new RegExp(term, 'gi');
spell.ranking += (spellText.match(regFind) || []).length;
});
return spell;
});
// Return spells that matched at least something
return spells.filter(spell => spell.ranking);
};
/**
* Filter Spells by Class
*/
const filterSpellsByClass = (spells, classes) => {
// If no classes, default to all classes
classes = classes.length ? classes : store.classes.data;
// Clone spells so we don't affect the original
spells = clone(spells);
return spells.filter(spell => {
let spellClasses = spell.classes.join(' ');
let match = false;
classes.forEach(c => {
if (spellClasses.indexOf(c) >= 0) {
match = true;
return true;
}
});
return match;
});
};
/**
* Apply Filters
* @returns {Array} of spells ranked based on searches and filters
*/
let applyFilters = () => sortSpells(searchSpells(filterSpellsByClass(store.spells, store.classes.current), store.search));
/**
* Spell Details Updating
*/
const spellDetails = name => {
if (!name) {
view.spell_details.update({ data: {} });
$('body').removeClass('details');
} else {
let data = store.spells.find(spell => name === spell.name);
data.description = emphasis(data.description);
view.spell_details.update({
data,
url: window.location.href
});
$('body').addClass('details');
let clipboard = new Clipboard('.copy-to-clipboard');
clipboard.on('success', e => $('#toast')[0].MaterialSnackbar.showSnackbar({ message: 'Copied link' })).on('error', e => $('#toast')[0].MaterialSnackbar.showSnackbar({ message: 'Sorry! Unable to copy link' }));
}
};
/**
* Event Bindings
*/
// Listen for header sorts
$('body').on('click', '[data-action-sort]', e => {
let name = $(e.currentTarget).attr('data-action-sort');
let rev = store.tableSort.current === name && !store.tableSort.rev;
store.tableSort.current = name;
store.tableSort.rev = rev;
localStorage.setItem('tableSortName', name);
localStorage.setItem('tableSortRev', rev);
view.spell_list.update({ data: sortSpells(store.spells) });
view.table_sort.update(store.tableSort);
});
// Listen for checkbox changes to filter spells
$('body').on('change', '[data-action-classtoggle]', e => {
let name = $(e.currentTarget).attr('data-action-classtoggle');
let add = $(e.currentTarget).prop('checked');
let index = store.classes.current.indexOf(name);
if (index === -1 && add) {
store.classes.current.push(name);
} else if (!add) store.classes.current.splice(index, 1);
store.tableSort.current = store.classes.current.length || store.search.length ? 'ranking' : null;
localStorage.setItem('tableSortName', store.tableSort.current);
localStorage.setItem('classes', store.classes.current);
view.spell_list.update({ data: applyFilters() });
view.table_sort.update({ current: store.tableSort.current });
});
// Listen to search to filter by
$('body').on('change keyup cut paste', '[data-action-search]', e => {
setTimeout(() => {
// Delay for value to change
store.search = $(e.currentTarget).val();
store.tableSort.current = store.search.length || store.classes.current.length ? 'ranking' : null;
store.tableSort.rev = false;
localStorage.setItem('search', store.search);
localStorage.setItem('tableSortName', store.tableSort.current);
localStorage.setItem('tableSortRev', store.tableSort.rev);
view.spell_list.update({ data: applyFilters() });
view.table_sort.update(store.tableSort);
}, 0);
});
// Listen for click on spells to open details
$('body').on('click', '[data-action-details]', e => {
let name = $(e.currentTarget).attr('data-action-details');
window.location.hash = name;
spellDetails(name);
});
// Article Scroll with User
$('.mdl-layout__content').on('scroll', debounce(() => {
let distance = $('.mdl-layout__content')[0].scrollTop;
$('[data-template=spell-details]').css('margin-top', distance);
}, 10));
/**
* Fetch Spells
*/
fetch('./spells.json').then(response => response.json()).then(spells => initSpells(spells)).then(spells => {
store.spells = spells;
store.classes.data = discoverClasses(spells);
view.spell_list.update({ data: applyFilters() });
view.class_list.update(store.classes);
if (window.location.hash) spellDetails(window.location.hash.substr(1));
}).catch(reason => console.error('Unable to retrieve spells list:', reason));

974
dist/view.js vendored Normal file
View file

@ -0,0 +1,974 @@
/**
* @class
*/
function class_list() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('if');
var child0 = {};
var child1 = {};
// Update functions
this.__update__ = {
data: function (data) {
var result;
result = Monkberry.cond(_this, for0, child0, class_list_if0, data);
Monkberry.cond(_this, for0, child1, class_list_else1, !result);
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child1.ref) {
child1.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [for0];
}
class_list.prototype = Object.create(Monkberry.prototype);
class_list.prototype.constructor = class_list;
class_list.pool = [];
class_list.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function class_list_if0() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('for');
var children0 = new Monkberry.Map();
// Update functions
this.__update__ = {
data: function (data) {
Monkberry.loop(_this, for0, children0, class_list_if0_for0, data, {"value":"cur"});
}
};
// On update actions
this.onUpdate = function (__data__) {
children0.forEach(function (view) {
view.update(__data__);
view.update(view.__state__);
});
};
// Set root nodes
this.nodes = [for0];
}
class_list_if0.prototype = Object.create(Monkberry.prototype);
class_list_if0.prototype.constructor = class_list_if0;
class_list_if0.pool = [];
class_list_if0.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function class_list_if0_for0() {
Monkberry.call(this);
this.__cache__ = {};
this.__state__ = {};
var _this = this;
// Create elements
var label0 = document.createElement('label');
var div1 = document.createElement('div');
var for0 = document.createComment('if');
var child0 = {};
var child1 = {};
var span2 = document.createElement('span');
var text3 = document.createTextNode('');
// Construct dom
span2.appendChild(text3);
span2.setAttribute("class", "mdl-switch__label");
div1.appendChild(for0);
div1.appendChild(span2);
div1.setAttribute("class", "mdl-switch mdl-js-switch mdl-js-ripple-effect");
label0.appendChild(div1);
label0.setAttribute("class", "mdl-navigation__link");
// Update functions
this.__update__ = {
cur_current: function (cur, current) {
var result;
result = Monkberry.cond(_this, for0, child0, class_list_if0_for0_if0, current.includes(cur));
Monkberry.cond(_this, for0, child1, class_list_if0_for0_else1, !result);
},
cur: function (cur) {
text3.textContent = cur;
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child1.ref) {
child1.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [label0];
}
class_list_if0_for0.prototype = Object.create(Monkberry.prototype);
class_list_if0_for0.prototype.constructor = class_list_if0_for0;
class_list_if0_for0.pool = [];
class_list_if0_for0.prototype.update = function (__data__) {
if (__data__.cur !== undefined && __data__.__index__ !== undefined) {
this.__cache__.cur = __data__.cur;
this.__update__.cur(__data__.cur);
}
if (__data__.current !== undefined) {
this.__cache__.current = __data__.current;
}
if (this.__cache__.cur !== undefined && this.__cache__.current !== undefined) {
this.__update__.cur_current(this.__cache__.cur, this.__cache__.current);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function class_list_if0_for0_if0() {
Monkberry.call(this);
// Create elements
var input0 = document.createElement('input');
// Construct dom
input0.checked = true;
input0.setAttribute("name", "class");
input0.setAttribute("type", "checkbox");
input0.setAttribute("class", "mdl-switch__input");
// Update functions
this.__update__ = {
cur: function (cur) {
input0.setAttribute("data-action-classtoggle", cur);;
}
};
// Set root nodes
this.nodes = [input0];
}
class_list_if0_for0_if0.prototype = Object.create(Monkberry.prototype);
class_list_if0_for0_if0.prototype.constructor = class_list_if0_for0_if0;
class_list_if0_for0_if0.pool = [];
class_list_if0_for0_if0.prototype.update = function (__data__) {
if (__data__.cur !== undefined) {
this.__update__.cur(__data__.cur);
}
};
/**
* @class
*/
function class_list_if0_for0_else1() {
Monkberry.call(this);
// Create elements
var input0 = document.createElement('input');
// Construct dom
input0.setAttribute("name", "class");
input0.setAttribute("type", "checkbox");
input0.setAttribute("class", "mdl-switch__input");
// Update functions
this.__update__ = {
cur: function (cur) {
input0.setAttribute("data-action-classtoggle", cur);;
}
};
// Set root nodes
this.nodes = [input0];
}
class_list_if0_for0_else1.prototype = Object.create(Monkberry.prototype);
class_list_if0_for0_else1.prototype.constructor = class_list_if0_for0_else1;
class_list_if0_for0_else1.pool = [];
class_list_if0_for0_else1.prototype.update = function (__data__) {
if (__data__.cur !== undefined) {
this.__update__.cur(__data__.cur);
}
};
/**
* @class
*/
function class_list_else1() {
Monkberry.call(this);
// Create elements
var div0 = document.createElement('div');
// Construct dom
div0.setAttribute("class", "mdl-spinner mdl-js-spinner is-active");
// Set root nodes
this.nodes = [div0];
}
class_list_else1.prototype = Object.create(Monkberry.prototype);
class_list_else1.prototype.constructor = class_list_else1;
class_list_else1.pool = [];
class_list_else1.prototype.update = function (__data__) {
};
window.class_list = class_list;
/**
* @class
*/
function search_field() {
Monkberry.call(this);
// Create elements
var input0 = document.createElement('input');
// Construct dom
input0.setAttribute("class", "mdl-textfield__input");
input0.setAttribute("data-action-search", "");
input0.setAttribute("type", "text");
input0.setAttribute("name", "sample");
input0.id = "fixed-header-drawer-exp";
// Update functions
this.__update__ = {
data: function (data) {
input0.value = data;;
}
};
// Set root nodes
this.nodes = [input0];
}
search_field.prototype = Object.create(Monkberry.prototype);
search_field.prototype.constructor = search_field;
search_field.pool = [];
search_field.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
};
window.search_field = search_field;
var __unsafe = function unsafe(root, nodes, html) {var node,j,i = nodes.length,element = document.createElement('div');element.innerHTML = html;while (i-- > 0) {nodes[i].parentNode.removeChild(nodes.pop());}for (i = j = element.childNodes.length - 1; j >= 0; j--) {nodes.push(element.childNodes[j]);}++i;if (root.nodeType == 8) {if (root.parentNode) while (i-- > 0) {root.parentNode.insertBefore(nodes[i], root);} else throw "Can not insert child view into parent node. You need append your view first and then update.";} else while (i-- > 0) {root.appendChild(nodes[i]);}};
/**
* @class
*/
function spell_details() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('if');
var child0 = {};
var child1 = {};
// Update functions
this.__update__ = {
data: function (data) {
var result;
result = Monkberry.cond(_this, for0, child0, spell_details_if0, data.name);
Monkberry.cond(_this, for0, child1, spell_details_else1, !result);
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child1.ref) {
child1.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [for0];
}
spell_details.prototype = Object.create(Monkberry.prototype);
spell_details.prototype.constructor = spell_details;
spell_details.pool = [];
spell_details.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function spell_details_if0() {
Monkberry.call(this);
var _this = this;
// Create elements
var button0 = document.createElement('button');
var i1 = document.createElement('i');
var h52 = document.createElement('h5');
var text3 = document.createTextNode('');
var p4 = document.createElement('p');
var unsafeNodes0 = [];
var div5 = document.createElement('div');
var ul6 = document.createElement('ul');
var li7 = document.createElement('li');
var strong8 = document.createElement('strong');
var text9 = document.createTextNode('');
var li10 = document.createElement('li');
var strong11 = document.createElement('strong');
var text12 = document.createTextNode('');
var li13 = document.createElement('li');
var strong14 = document.createElement('strong');
var text15 = document.createTextNode('');
var for0 = document.createComment('if');
var child0 = {};
var ul16 = document.createElement('ul');
var li17 = document.createElement('li');
var strong18 = document.createElement('strong');
var text19 = document.createTextNode('');
var li20 = document.createElement('li');
var strong21 = document.createElement('strong');
var text22 = document.createTextNode('Cantrip');
var li23 = document.createElement('li');
var strong24 = document.createElement('strong');
var span25 = document.createElement('span');
var text26 = document.createTextNode('');
var for1 = document.createComment('if');
var child2 = {};
var div27 = document.createElement('div');
var label28 = document.createElement('label');
var i29 = document.createElement('i');
var input30 = document.createElement('input');
// Construct dom
i1.appendChild(document.createTextNode("close"));
i1.setAttribute("class", "material-icons");
button0.appendChild(i1);
button0.setAttribute("data-action-details", "");
button0.setAttribute("class", "mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab");
h52.appendChild(text3);
h52.setAttribute("class", "mdl-typography--display-1 mdl-color-text--teal-600");
p4.setAttribute("class", "description");
strong8.appendChild(document.createTextNode("Range: "));
li7.appendChild(strong8);
li7.appendChild(text9);
strong11.appendChild(document.createTextNode("Casting Time: "));
li10.appendChild(strong11);
li10.appendChild(text12);
strong14.appendChild(document.createTextNode("Duration: "));
li13.appendChild(strong14);
li13.appendChild(text15);
ul6.appendChild(li7);
ul6.appendChild(li10);
ul6.appendChild(li13);
ul6.appendChild(for0);
ul6.setAttribute("class", "right mdl-cell mdl-cell--6-col");
strong18.appendChild(document.createTextNode("School: "));
li17.appendChild(strong18);
li17.appendChild(text19);
strong21.appendChild(document.createTextNode("Spell Level: "));
li20.appendChild(strong21);
li20.appendChild(text22);
strong24.appendChild(document.createTextNode("Class: "));
span25.appendChild(text26);
span25.setAttribute("class", "classes");
li23.appendChild(strong24);
li23.appendChild(span25);
ul16.appendChild(li17);
ul16.appendChild(li20);
ul16.appendChild(li23);
ul16.appendChild(for1);
ul16.setAttribute("class", "left mdl-cell mdl-cell--6-col");
i29.appendChild(document.createTextNode("content_copy"));
i29.setAttribute("class", "material-icons");
label28.appendChild(i29);
label28.setAttribute("class", "mdl-button mdl-js-button mdl-button--icon copy-to-clipboard");
label28.setAttribute("for", "share-url");
label28.setAttribute("data-clipboard-target", "#share-url");
input30.setAttribute("readonly", "");
input30.setAttribute("class", "mdl-textfield__input");
input30.setAttribute("type", "text");
input30.id = "share-url";
div27.appendChild(label28);
div27.appendChild(input30);
div27.setAttribute("class", "mdl-textfield mdl-js-textfield");
div5.appendChild(ul6);
div5.appendChild(ul16);
div5.appendChild(div27);
div5.setAttribute("class", "mdl-grid");
// Update functions
this.__update__ = {
data: function (data) {
text3.textContent = data.name;
__unsafe(p4, unsafeNodes0, data.description);
text9.textContent = data.range;
text12.textContent = data.casting_time;
text15.textContent = data.duration;
Monkberry.cond(_this, for0, child0, spell_details_if0_if0, data.ritual);
text19.textContent = data.school;
text22.textContent = (data.level) || ('Cantrip');
text26.textContent = data.classes.join(', ');
Monkberry.cond(_this, for1, child2, spell_details_if0_if2, (data.components) && (data.components.raw));
},
url: function (url) {
input30.value = url;;
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child2.ref) {
child2.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [button0, h52, p4, div5];
}
spell_details_if0.prototype = Object.create(Monkberry.prototype);
spell_details_if0.prototype.constructor = spell_details_if0;
spell_details_if0.pool = [];
spell_details_if0.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
if (__data__.url !== undefined) {
this.__update__.url(__data__.url);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function spell_details_if0_if0() {
Monkberry.call(this);
// Create elements
var li0 = document.createElement('li');
// Construct dom
li0.appendChild(document.createTextNode("Ritual"));
// Set root nodes
this.nodes = [li0];
}
spell_details_if0_if0.prototype = Object.create(Monkberry.prototype);
spell_details_if0_if0.prototype.constructor = spell_details_if0_if0;
spell_details_if0_if0.pool = [];
spell_details_if0_if0.prototype.update = function (__data__) {
};
/**
* @class
*/
function spell_details_if0_if2() {
Monkberry.call(this);
// Create elements
var li0 = document.createElement('li');
var strong1 = document.createElement('strong');
var text2 = document.createTextNode('');
// Construct dom
strong1.appendChild(document.createTextNode("Components: "));
li0.appendChild(strong1);
li0.appendChild(text2);
// Update functions
this.__update__ = {
data: function (data) {
text2.textContent = data.components.raw;
}
};
// Set root nodes
this.nodes = [li0];
}
spell_details_if0_if2.prototype = Object.create(Monkberry.prototype);
spell_details_if0_if2.prototype.constructor = spell_details_if0_if2;
spell_details_if0_if2.pool = [];
spell_details_if0_if2.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
};
/**
* @class
*/
function spell_details_else1() {
Monkberry.call(this);
// Create elements
var div0 = document.createElement('div');
var h61 = document.createElement('h6');
// Construct dom
h61.appendChild(document.createTextNode(" Choose a Spell "));
h61.setAttribute("class", "mdl-typography--title");
div0.appendChild(h61);
div0.id = "empty";
// Set root nodes
this.nodes = [div0];
}
spell_details_else1.prototype = Object.create(Monkberry.prototype);
spell_details_else1.prototype.constructor = spell_details_else1;
spell_details_else1.pool = [];
spell_details_else1.prototype.update = function (__data__) {
};
window.spell_details = spell_details;
/**
* @class
*/
function spell_list() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('if');
var child0 = {};
var child1 = {};
// Update functions
this.__update__ = {
data: function (data) {
var result;
result = Monkberry.cond(_this, for0, child0, spell_list_if0, data.length);
Monkberry.cond(_this, for0, child1, spell_list_else1, !result);
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child1.ref) {
child1.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [for0];
}
spell_list.prototype = Object.create(Monkberry.prototype);
spell_list.prototype.constructor = spell_list;
spell_list.pool = [];
spell_list.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function spell_list_if0() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('for');
var children0 = new Monkberry.Map();
// Update functions
this.__update__ = {
data: function (data) {
Monkberry.loop(_this, for0, children0, spell_list_if0_for0, data, {"value":"spell"});
}
};
// On update actions
this.onUpdate = function (__data__) {
children0.forEach(function (view) {
view.update(__data__);
view.update(view.__state__);
});
};
// Set root nodes
this.nodes = [for0];
}
spell_list_if0.prototype = Object.create(Monkberry.prototype);
spell_list_if0.prototype.constructor = spell_list_if0;
spell_list_if0.pool = [];
spell_list_if0.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function spell_list_if0_for0() {
Monkberry.call(this);
this.__state__ = {};
// Create elements
var tr0 = document.createElement('tr');
var td1 = document.createElement('td');
var strong2 = document.createElement('strong');
var text3 = document.createTextNode('');
var td4 = document.createElement('td');
var text5 = document.createTextNode('');
var td6 = document.createElement('td');
var text7 = document.createTextNode('');
// Construct dom
strong2.appendChild(text3);
td1.appendChild(strong2);
td1.setAttribute("class", "spell-name mdl-data-table__cell--non-numeric");
td4.appendChild(text5);
td4.setAttribute("class", "spell-school mdl-data-table__cell--non-numeric");
td6.appendChild(text7);
td6.setAttribute("class", "spell-level");
tr0.appendChild(td1);
tr0.appendChild(td4);
tr0.appendChild(td6);
// Update functions
this.__update__ = {
spell: function (spell) {
text3.textContent = spell.name;
text5.textContent = spell.school;
text7.textContent = spell.prettyLevel;
tr0.setAttribute("data-action-details", spell.name);;
}
};
// Set root nodes
this.nodes = [tr0];
}
spell_list_if0_for0.prototype = Object.create(Monkberry.prototype);
spell_list_if0_for0.prototype.constructor = spell_list_if0_for0;
spell_list_if0_for0.pool = [];
spell_list_if0_for0.prototype.update = function (__data__) {
if (__data__.spell !== undefined && __data__.__index__ !== undefined) {
this.__update__.spell(__data__.spell);
}
};
/**
* @class
*/
function spell_list_else1() {
Monkberry.call(this);
var _this = this;
// Create elements
var tr0 = document.createElement('tr');
var td1 = document.createElement('td');
var child0 = {};
var child1 = {};
// Construct dom
td1.setAttribute("colspan", "4");
tr0.appendChild(td1);
tr0.setAttribute("class", "do-nothing");
// Update functions
this.__update__ = {
data: function (data) {
var result;
result = Monkberry.cond(_this, td1, child0, spell_list_else1_if0, data);
Monkberry.cond(_this, td1, child1, spell_list_else1_else1, !result);
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child1.ref) {
child1.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [tr0];
}
spell_list_else1.prototype = Object.create(Monkberry.prototype);
spell_list_else1.prototype.constructor = spell_list_else1;
spell_list_else1.pool = [];
spell_list_else1.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function spell_list_else1_if0() {
Monkberry.call(this);
// Create elements
var div0 = document.createElement('div');
var i1 = document.createElement('i');
var h52 = document.createElement('h5');
var h63 = document.createElement('h6');
// Construct dom
i1.appendChild(document.createTextNode(" warning "));
i1.setAttribute("class", "material-icons mdl-list__item-icon mdl-color-text--orange-600");
h52.appendChild(document.createTextNode("No Results"));
h63.appendChild(document.createTextNode("Try refining your filters in the sidebar."));
div0.appendChild(i1);
div0.appendChild(h52);
div0.appendChild(h63);
div0.setAttribute("class", "text-center");
// Set root nodes
this.nodes = [div0];
}
spell_list_else1_if0.prototype = Object.create(Monkberry.prototype);
spell_list_else1_if0.prototype.constructor = spell_list_else1_if0;
spell_list_else1_if0.pool = [];
spell_list_else1_if0.prototype.update = function (__data__) {
};
/**
* @class
*/
function spell_list_else1_else1() {
Monkberry.call(this);
// Create elements
var div0 = document.createElement('div');
// Construct dom
div0.setAttribute("class", "mdl-spinner mdl-js-spinner is-active");
// Set root nodes
this.nodes = [div0];
}
spell_list_else1_else1.prototype = Object.create(Monkberry.prototype);
spell_list_else1_else1.prototype.constructor = spell_list_else1_else1;
spell_list_else1_else1.pool = [];
spell_list_else1_else1.prototype.update = function (__data__) {
};
window.spell_list = spell_list;
/**
* @class
*/
function table_sort() {
Monkberry.call(this);
var _this = this;
// Create elements
var for0 = document.createComment('for');
var children0 = new Monkberry.Map();
// Update functions
this.__update__ = {
data: function (data) {
Monkberry.loop(_this, for0, children0, table_sort_for0, data, {"value":"name"});
}
};
// On update actions
this.onUpdate = function (__data__) {
children0.forEach(function (view) {
view.update(__data__);
view.update(view.__state__);
});
};
// Set root nodes
this.nodes = [for0];
}
table_sort.prototype = Object.create(Monkberry.prototype);
table_sort.prototype.constructor = table_sort;
table_sort.pool = [];
table_sort.prototype.update = function (__data__) {
if (__data__.data !== undefined) {
this.__update__.data(__data__.data);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function table_sort_for0() {
Monkberry.call(this);
this.__cache__ = {};
this.__state__ = {};
var _this = this;
// Create elements
var th0 = document.createElement('th');
var i1 = document.createElement('i');
var for0 = document.createComment('if');
var child0 = {};
var for1 = document.createComment('if');
var child2 = {};
var for2 = document.createComment('if');
var child4 = {};
var span2 = document.createElement('span');
var text3 = document.createTextNode('');
var i4 = document.createElement('i');
var text5 = document.createTextNode('');
// Construct dom
i1.appendChild(for0);
i1.appendChild(for1);
i1.appendChild(for2);
i1.setAttribute("class", "material-icons mdl-list__item-icon");
span2.appendChild(document.createTextNode(" "));
span2.appendChild(text3);
i4.appendChild(text5);
i4.setAttribute("class", "material-icons mdl-color-text--teal-600 mdl-list__item-icon ");
th0.appendChild(i1);
th0.appendChild(span2);
th0.appendChild(i4);
th0.setAttribute("class", "mdl-data-table__cell--non-numeric ");
// Update functions
this.__update__ = {
name: function (name) {
Monkberry.cond(_this, for0, child0, table_sort_for0_if0, (name) === ('level'));
Monkberry.cond(_this, for1, child2, table_sort_for0_if2, (name) === ('name'));
Monkberry.cond(_this, for2, child4, table_sort_for0_if4, (name) === ('school'));
text3.textContent = name;
th0.setAttribute("data-action-sort", name);;
},
rev: function (rev) {
text5.textContent = (rev) ? 'keyboard_arrow_up' : 'keyboard_arrow_down';
},
current_name: function (current, name) {
i4.setAttribute("class", ("material-icons mdl-color-text--teal-600 mdl-list__item-icon ") + (((name) === (current)) ? 'mdl-color-text--teal-600' : 'mdl-color-text--grey-300'));;
},
current: function (current) {
th0.setAttribute("class", ("mdl-data-table__cell--non-numeric ") + (((current) === ('ranking')) ? 'mdl-color-text--grey-200 do-nothing' : ''));;
}
};
// On update actions
this.onUpdate = function (__data__) {
if (child0.ref) {
child0.ref.update(__data__);
}
if (child2.ref) {
child2.ref.update(__data__);
}
if (child4.ref) {
child4.ref.update(__data__);
}
};
// Set root nodes
this.nodes = [th0];
}
table_sort_for0.prototype = Object.create(Monkberry.prototype);
table_sort_for0.prototype.constructor = table_sort_for0;
table_sort_for0.pool = [];
table_sort_for0.prototype.update = function (__data__) {
if (__data__.name !== undefined && __data__.__index__ !== undefined) {
this.__cache__.name = __data__.name;
this.__update__.name(__data__.name);
}
if (__data__.rev !== undefined) {
this.__update__.rev(__data__.rev);
}
if (__data__.current !== undefined) {
this.__cache__.current = __data__.current;
this.__update__.current(__data__.current);
}
if (this.__cache__.current !== undefined && this.__cache__.name !== undefined) {
this.__update__.current_name(this.__cache__.current, this.__cache__.name);
}
this.onUpdate(__data__);
};
/**
* @class
*/
function table_sort_for0_if0() {
Monkberry.call(this);
// Set root nodes
this.nodes = [document.createTextNode(" exposure ")];
}
table_sort_for0_if0.prototype = Object.create(Monkberry.prototype);
table_sort_for0_if0.prototype.constructor = table_sort_for0_if0;
table_sort_for0_if0.pool = [];
table_sort_for0_if0.prototype.update = function (__data__) {
};
/**
* @class
*/
function table_sort_for0_if2() {
Monkberry.call(this);
// Set root nodes
this.nodes = [document.createTextNode(" flash_on ")];
}
table_sort_for0_if2.prototype = Object.create(Monkberry.prototype);
table_sort_for0_if2.prototype.constructor = table_sort_for0_if2;
table_sort_for0_if2.pool = [];
table_sort_for0_if2.prototype.update = function (__data__) {
};
/**
* @class
*/
function table_sort_for0_if4() {
Monkberry.call(this);
// Set root nodes
this.nodes = [document.createTextNode(" school ")];
}
table_sort_for0_if4.prototype = Object.create(Monkberry.prototype);
table_sort_for0_if4.prototype.constructor = table_sort_for0_if4;
table_sort_for0_if4.pool = [];
table_sort_for0_if4.prototype.update = function (__data__) {
};
window.table_sort = table_sort;
//# sourceMappingURL=view.js.map

1
dist/view.js.map vendored Normal file
View file

@ -0,0 +1 @@
{"version":3,"sources":["class-list.monk","search-field.monk","spell-details.monk","spell-list.monk","table-sort.monk"],"names":[],"mappings":"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","file":"view.js"}

81
index.html Normal file
View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<title>My Spells 5e</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.teal-pink.min.css" />
<link rel="stylesheet" href="./assets/app.css" />
</head>
<body>
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<div class="mdl-layout-spacer"></div>
<nav class="mdl-navigation">
<a class="mdl-navigation__link" href="https://dark12222000.github.io/lootsplit/">
<i class="material-icons">local_atm</i>
Loot Split
</a>
<a class="mdl-navigation__link" href="http://paulvmoreau.github.io/BeltFedNPCs/">
<i class="material-icons">people</i>
NPC Generator
</a>
<a class="mdl-navigation__link mdl-color-text--teal-100" href="https://github.com/sharpshark28/my_spells">
<i class="material-icons">code</i>
Fork Me On Github
</a>
</nav>
<div data-action-details="" role="button" tabindex="0"><i class="material-icons">keyboard_arrow_left</i></div>
</div>
</header>
<aside class="mdl-layout__drawer">
<span class="mdl-layout-title mdl-color-text--pink-600">
<i class="material-icons">whatshot</i>
My Spells
</span>
<nav class="mdl-navigation">
<form>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label">
<div data-template="search-field"></div>
<label class="mdl-textfield__label" for="sample3">
<i class="material-icons">search</i>
Search
</label>
</div>
</form>
<form data-template="class-list">
</form>
</nav>
</aside>
<main class="mdl-layout__content">
<div class="page-content">
<div class="mdl-grid">
<table id="spell-list-container" class="mdl-cell mdl-cell--12-col mdl-cell--6-col-desktop mdl-data-table mdl-shadow--2dp">
<thead>
<tr data-template="table-sort"></tr>
</thead>
<tbody data-template="spell-list"></tbody>
</table>
<article data-template="spell-details" class="mdl-cell mdl-cell--12-col mdl-cell--6-col-desktop mdl-color-text--grey-600"></article>
</div>
</div>
</main>
<div id="toast" class="mdl-js-snackbar mdl-snackbar">
<div class="mdl-snackbar__text"></div>
<button class="mdl-snackbar__action" type="button"></button>
</div>
</div>
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<script src="./node_modules/jquery/dist/jquery.min.js"></script>
<script src="./node_modules/monkberry/monkberry.js"></script>
<script src="./node_modules/clipboard/dist/clipboard.min.js"></script>
<script src="./dist/view.js"></script>
<script src="./dist/app.js"></script>
</body>
</html>

View file

@ -3,11 +3,26 @@
"version": "1.0.0",
"description": "5e Spells by Class",
"main": "",
"watch": {
"views": "views/*.monk"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"views": "monkberry src/views/*.monk --source-map --output dist/view.js",
"js": "babel src/app.js --out-file dist/app.js",
"develop": "nodemon --watch src -e monk,js --exec 'npm run views && npm run js'"
},
"author": "Joe Wroten <sharpshark28@gmail.com>",
"license": "ISC",
"devDependencies": {},
"dependencies": {}
"devDependencies": {
"babel-cli": "^6.11.4",
"babel-plugin-transform-runtime": "^6.12.0",
"babel-preset-es2017": "^1.6.1",
"nodemon": "^1.10.0",
"npm-watch": "^0.1.5"
},
"dependencies": {
"clipboard": "^1.5.12",
"jquery": "^3.1.0",
"monkberry": "^4.0.7"
}
}

View file

View file

@ -1,73 +0,0 @@
.has-tooltip {
cursor: help;
}
.mdl-dialog.card {
width: 500px;
padding: 0;
background: transparent;
}
.mdl-dialog .mdl-card {
overflow: visible;
width: 100%;
}
/*.mdl-dialog [data-id-spelldetail=description] {*/
/*max-height: 3.5em;*/
/*overflow-y: scroll;*/
/*}*/
#spell-list [data-action-dialog] {
cursor: pointer;
}
#class-list label span {
text-transform: capitalize;
}
.left {
float: left;
}
.left.clear {
clear: left;
}
.right {
float: right;
}
.right.clear {
clear: right;
}
.clear {
clear: both;
}
.text-right {
text-align: right;
}
.text-left {
text-align: left;
}
.text-center {
text-align: center;
}
[data-id-spelldetail="components"] {
list-style-type: none;
margin: 0;
padding: 0;
}
[data-id-spelldetail="components"] li:not(:last-of-type) {
padding-right: .5em;
}
[data-id-spelldetail="components"] li:not(:last-of-type)::after {
content: ','
}

View file

@ -1,283 +0,0 @@
/**
* Capitalize Helper
**/
let capitalize = str => str ? str.charAt(0).toUpperCase() + str.slice(1) : '';
/**
* Tooltip Counter
*/
let tooltipCount = 1;
let tooltipCounter = () => tooltipCount++;
/**
* Discover Classes
*/
const DISCOVERCLASSES = spells => {
let classes = [];
spells.forEach(spell => {
if (!spell.classes) return;
spell.classes.forEach(current => {
if (!classes.includes(current)) classes.push(current);
});
});
classes = classes.sort((a, b) => a > b);
return {spells, classes}
};
/**
* Dom Helper
*/
const DOMHELPER = {
emphasis (str) {
let keywords = ['intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check'];
keywords.forEach(word => {
let r = new RegExp(` ${word} `, 'gi');
str = str.replace(r, o => `<em class="mdl-color-text--teal-600">${o}</em>`);
});
str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `<strong>${o}</strong>`);
return str;
},
level (lvl) {
let cantrip = lvl.toLowerCase() === 'cantrip';
let invalid = isNaN(lvl) && !cantrip;
if (invalid) return '?';
else return cantrip ? 'C' : lvl;
},
components (components) {
let dom = '';
let types = ['verbal', 'somatic', 'material'];
let typesDescriptions = [
'Spoken word/prayer. Mouth must be free and not mute.',
'Physical gestures, usually hand movements. Must not be bound.',
'Items consumed upon casting spell.'
];
types.forEach((type, i) => {
if (!!components[type]) {
let currentTooltip = tooltipCounter();
dom += `
<li class="left has-tooltip" id="tooltip-${currentTooltip}">${types[i].charAt(0).toUpperCase()}</li>
<label class="mdl-tooltip" for="tooltip-${currentTooltip}">
<strong>${capitalize(type)}</strong><br />
${typesDescriptions[i]}
</label>
`;
}
});
return dom;
},
classes (classes) {
let dom = '';
classes.forEach(x => {
dom += `<a>${capitalize(x)}</a>&nbsp;`;
});
return dom;
}
};
/**
* List of Selected Spells
*/
const SELECTEDSPELLS = () => $('#form-spells').serializeArray().map(spell => parseInt(spell.value));
/**
* Add Spells to Page
*/
const RENDERSPELLS = (x) => {
let dom = '';
let spells = x ? x.spells.slice(0) : data;
spells = CLASSSPELLS(spells);
spells = SEARCHSPELLS(spells);
spells = spells.sort((a, b) => b.ranking - a.ranking);
spells.forEach(spell => {
let el = $('#spell-item').html();
let $el = $(el);
$('[data-id-spellitem=level]', $el)[0].innerHTML
= DOMHELPER.level(spell.level);
$('[data-id-spellitem=name]', $el)[0].innerHTML
= spell.name;
$('[data-id-spellitem=school]', $el)[0].innerHTML
= capitalize(spell.school);
$('[data-id-spellitem=checkbox]', $el).attr('value', spell.id).attr('id', `sel-${spell.id}`);
$('[data-id-spellitem=checklabel]', $el).attr('for', `sel-${spell.id}`);
$el.attr('data-action-dialog', spell.id);
dom += $el.prop('outerHTML');
});
$('#spell-list').html(dom);
componentHandler.upgradeDom();
return data;
};
/**
* Render Spell Details to Dialog
*/
const RENDERSPELLDIALOG = spell => {
let el = $('#spell-detail').html();
let $el = $(`<div>${el}</div>`);
$('[data-id-spelldetail=name]', $el)[0].innerHTML
= spell.name;
$('[data-action-select]', $el).attr('data-action-select', spell.id);
$('[data-action-select] input[type="checkbox"]', $el).attr('checked', SELECTEDSPELLS().includes(spell.id));
if (spell.description) {
$('[data-id-spelldetail=description]', $el)[0].innerHTML
= DOMHELPER.emphasis(spell.description);
}
if (spell.tags) {
$('[data-id-spelldetail=classes]', $el)[0].innerHTML
= DOMHELPER.classes(spell.classes);
}
if (spell.components) {
$('[data-id-spelldetail=components]', $el)[0].innerHTML
= DOMHELPER.components(spell.components);
}
if (spell.components.materials_needed) {
$('[data-id-spelldetail=materials]', $el)[0].innerHTML
= spell.components.materials_needed.join(', ');
}
if (spell.level) {
$('[data-id-spelldetail=level]', $el)[0].innerHTML
= 'Level ' + spell.level;
}
if (spell.duration) {
$('[data-id-spelldetail=duration]', $el)[0].innerHTML
= spell.duration;
}
if (spell.casting_time) {
$('[data-id-spelldetail=castingtime]', $el)[0].innerHTML
= spell.casting_time;
}
if (spell.range) {
$('[data-id-spelldetail=range]', $el)[0].innerHTML
= spell.range;
}
$('#spell-detail-container').html($el.prop('outerHTML'));
componentHandler.upgradeDom();
};
/**
* Fetch Classes from Spells
*/
const RENDERCLASSES = data => {
let dom = '';
data.classes.forEach(item => {
let el = $('#class-toggle').html();
let $el = $(el);
$('[data-action-classtoggle]', $el).attr('value', item);
$('[data-id-classtoggle=name]', $el)[0].innerHTML = item;
dom += $el.prop('outerHTML');
});
$('#class-list').html(dom);
return data;
};
const SEARCHSPELLS = (spells, search = $('[data-action-search]').val()) => {
search = search.toLowerCase();
if (search.length >= 3) {
spells = spells.filter(spell => {
let regFind = new RegExp(search, 'gi');
let spellName = spell.name.toLowerCase();
let spellDescription = spell.description ? spell.description.toLowerCase() : '';
let spellMaterials = spell.components && spell.components.materials_needed || [];
let matches = 0
+ spellName.indexOf(search) > -1 ? 20 : 0
+ (spellDescription.match(regFind) || []).length
+ (spellMaterials).filter(com => com.indexOf(search) > -1).length * 10;
spell.ranking = matches;
return spell.ranking > 0;
});
}
return spells;
};
const CLASSSPELLS = (spells, classes) => {
classes = classes || $('#class-list').serializeArray().map(x => x.value);
return classes.length ? spells.filter(spell => {
for(let i = 0; i < classes.length; i++) {
if (spell.classes.indexOf(classes[i]) >= 0) return true;
}
}) : spells;
};
/**
* Retrieve Spells
*/
let data;
fetch('./spells.json')
.then(response => response.json())
.then(spells => spells.map((spell, i) => {
spell.id = i;
spell.ranking = 0; // Default to equal ranking
return spell;
}))
.then(spells => data = spells)
.then(spells => DISCOVERCLASSES(spells))
.then(data => RENDERCLASSES(data))
.then(data => RENDERSPELLS(data))
.catch(reason => console.error('Unable to retrieve spells list:', reason));
/**
* Details Popup
*/
let $dialog = $('dialog');
if (!$dialog[0].showModal) {
dialogPolyfill.registerDialog($dialog);
}
$('#spell-list').on('click', '[data-action-dialog]', e => {
let id = $(e.target).closest('tr').data('action-dialog');
RENDERSPELLDIALOG(data[id]);
$dialog[0].showModal();
}).on('click', '.dontprop', e => {
e.stopPropagation();
});
$dialog.on('click', '[data-action-close]', () => {
$dialog[0].close();
});
/**
* Bind Checkbox Selection Action
*/
$('body').on('change', '[data-action-select] input', e => {
let id = $(e.target).closest('label').attr('data-action-select');
if (id) $('#sel-' + id).click();
});
/**
* Bind Toggle All Checkbox
*/
$('label[for=table-header]').on('change', 'input[type="checkbox"]', e => {
$(e.target).closest('form').find('[data-id-spellitem=checkbox]').each(function() {
this.checked = e.target.checked;
if(this.checked) $(this).closest('label').addClass('is-checked');
else $(this).closest('label').removeClass('is-checked');
})
});
/**
* Bind Class Filter Switches
*/
$('body').on('change', '[data-action-classtoggle]', e => {
RENDERSPELLS();
});
/**
* Search
*/
$('body').on('change keyup cut paste', '[data-action-search]', e => {
setTimeout(function() {
RENDERSPELLS();
}, 0);
});

View file

@ -1,134 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>My Spells 5e</title>
<meta charset="UTF-8">
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=Roboto:300,400,500,700" type="text/css">
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
<link rel="stylesheet" href="https://code.getmdl.io/1.1.3/material.teal-pink.min.css" />
<link rel="stylesheet" href="./app.css" />
</head>
<body>
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-drawer mdl-layout--fixed-header">
<header class="mdl-layout__header">
<div class="mdl-layout__header-row">
<div class="mdl-layout-spacer"></div>
<div class="mdl-textfield mdl-js-textfield mdl-textfield--expandable mdl-textfield--floating-label mdl-textfield--align-right">
<label class="mdl-button mdl-js-button mdl-button--icon" for="fixed-header-drawer-exp">
<i class="material-icons">search</i>
</label>
<div class="mdl-textfield__expandable-holder">
<input class="mdl-textfield__input" data-action-search type="text" name="sample" id="fixed-header-drawer-exp" />
</div>
</div>
</div>
</header>
<aside class="mdl-layout__drawer">
<span class="mdl-layout-title">My Spells</span>
<nav class="mdl-navigation">
<form id="class-list">
</form>
</nav>
</aside>
<main class="mdl-layout__content">
<div class="page-content">
<form id="form-spells">
<table class="mdl-data-table mdl-shadow--2dp">
<thead>
<tr>
<th>
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select" for="table-header">
<input type="checkbox" id="table-header" class="mdl-checkbox__input" />
</label>
</th>
<th>Level</th>
<th class="mdl-data-table__cell--non-numeric">Name</th>
<th class="mdl-data-table__cell--non-numeric">School</th>
</tr>
</thead>
<tbody id="spell-list">
<tr>
<td colspan="2">
<div id="loader" class="mdl-spinner mdl-js-spinner is-active"></div>
</td>
</tr>
</tbody>
</table>
</form>
</div>
</main>
</div>
<dialog class="mdl-dialog card">
<article id="spell-detail-container"></article>
</dialog>
<!-- SPELL DETAILS -->
<script id="spell-detail" type="x-template">
<div class="mdl-card">
<div class="mdl-card__title mdl-color--pink-600 mdl-color-text--white">
<h2 class="mdl-card__title-text" data-id-spelldetail="name"></h2>
</div>
<div class="mdl-card__supporting-text">
<p data-id-spelldetail="classes"></p>
<p data-id-spelldetail="description"></p>
<div class="left">
<div data-id-spelldetail="level"></div>
<div data-id-spelldetail="castingtime"></div>
<div data-id-spelldetail="duration"></div>
</div>
<div class="right">
<div class="right" data-id-spelldetail="range"></div>
<ul class="right clear" data-id-spelldetail="components"></ul>
<div class="clear" data-id-spelldetail="materials"></div>
</div>
</div>
<div class="mdl-card__menu">
<button data-action-close class="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i class="material-icons mdl-color-text--white">close</i>
</button>
</div>
<footer class="mdl-card__actions mdl-card--border">
<label data-action-select class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect">
<input type="checkbox" class="mdl-checkbox__input">
</label>
<!--<a class="mdl-button mdl-button--colored mdl-js-button mdl-js-ripple-effect">-->
<!--TODO-->
<!--</a>-->
</footer>
</div>
</script>
<!-- SPELL -->
<script id="spell-item" type="x-template">
<tr data-action-dialog>
<td>
<label data-id-spellitem="checklabel" class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select dontprop">
<input type="checkbox" name="id" value="" data-id-spellitem="checkbox" class="mdl-checkbox__input dontprop" />
</label>
</td>
<td data-id-spellitem="level"></td>
<td class="mdl-data-table__cell--non-numeric">
<strong data-id-spellitem="name"></strong>
</td>
<td data-id-spellitem="school" class="mdl-data-table__cell--non-numeric"></td>
</tr>
</script>
<script id="class-toggle" type="x-template">
<label class="mdl-navigation__link">
<div class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
<input data-action-classtoggle name="class" type="checkbox" class="mdl-switch__input">
<span data-id-classtoggle="name" class="mdl-switch__label"></span>
</div>
</label>
</script>
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<script src="https://code.jquery.com/jquery-3.0.0.min.js" integrity="sha256-JmvOoLtYsmqlsWxa7mDSLMwa6dZ9rrIdtrrVYRnDRH0=" crossorigin="anonymous"></script>
<script src="./app.js"></script>
</body>
</html>

1
public/redux.min.js vendored

File diff suppressed because one or more lines are too long

View file

@ -1,18 +0,0 @@
'use strict';
let newSpells = [];
let spells = require('./spells.json');
//spells.forEach(spell => {
//if (spell.tags) {
//spell.classes = [];
//spell.tags.forEach(tag => {
//let classes = ['cleric', 'fighter', 'rogue', 'wizard', 'barbarian', 'bard', 'druid', 'monk', 'paladin', 'ranger', 'sorcerer', 'warlock'];
//classes.forEach(x => {
//if (tag.indexOf(x) > -1) spell.classes.push(x);
//});
//});
//}
//newSpells.push(spell);
//});
require('fs').writeFileSync('./newSpells.json', JSON.stringify(newSpells));

BIN
src/.DS_Store vendored Normal file

Binary file not shown.

293
src/app.js Normal file
View file

@ -0,0 +1,293 @@
/**
* Misc helper functions
*/
Object.values = x =>
Object.keys(x).reduce((y, z) =>
y.push(x[z]) && y, []);
const debounce = (func, wait, immediate) => {
let timeout;
return function() {
let context = this, args = arguments;
let later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
const el = id => $(`[data-template=${id}]`)[0] || console.error('Unable to render to', id);
const clone = obj => JSON.parse(JSON.stringify(obj));
/**
* Global store and view holders
*/
let store = {};
let view = {};
/**
* Init Local Storage
*/
const localStorageDefault = (key, val) => {
if (localStorage.getItem(key) === null) localStorage.setItem(key, val);
};
let defaults = {
tableSortName: 'name',
tableSortRev: false,
classes: [],
search: ''
};
for (let cur in defaults) localStorageDefault(cur, defaults[cur]);
/**
* Render Table Sort
*/
store.tableSort = {
data: ['name', 'school', 'level'],
current: localStorage.getItem('tableSortName'),
rev: localStorage.getItem('tableSortRev') !== 'false'
};
view.table_sort = Monkberry.render(table_sort, el('table-sort'));
view.table_sort.update(store.tableSort);
/**
* Render Spell List
*/
view.spell_list = Monkberry.render(spell_list, el('spell-list'));
view.spell_list.update({});
/**
* Render Spell Details
*/
view.spell_details = Monkberry.render(spell_details, el('spell-details'));
view.spell_details.update({data: {}});
/**
* Render Class List
*/
store.classes = {
data: [],
current: localStorage.getItem('classes') ? localStorage.getItem('classes').split(',') : []
};
view.class_list = Monkberry.render(class_list, el('class-list'));
view.class_list.update(store.classes);
/**
* Render Search
*/
store.search = localStorage.getItem('search');
view.search_field = Monkberry.render(search_field, el('search-field'));
view.search_field.update({data: store.search});
/**
* Discover Classes
*/
const discoverClasses = spells => {
let classes = [];
spells.forEach(spell => {
if (!spell.classes) return;
spell.classes.forEach(current => {
if (!classes.includes(current)) classes.push(current);
});
});
return classes.sort((a, b) => a > b);
};
/**
* Emphasis on important string bits
* @param {string} string
*/
const emphasis = str => {
let keywords = ['constitution', 'con', 'intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check'];
keywords.forEach(word => {
let r = new RegExp(` ${word} `, 'gi');
str = str.replace(r, o => `<em class="mdl-color-text--teal-600">${o}</em>`);
});
str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `<strong>${o}</strong>`);
return str;
};
/**
* Init Spells
*/
const initSpells = s => s.map((spell, i) => {
spell.selected = false;
spell.ranking = 0;
spell.level = parseInt(spell.level) ? spell.level : 0;
spell.prettyLevel = spell.level === 0 ? 'C' : spell.level;
return spell;
});
/**
* Sort Spells
*/
const sortSpells = (s, sortBy, reverse) => s.sort((a, b) => {
let hasFilters = store.classes.current.length || store.search.length;
let by = sortBy || hasFilters ? 'ranking' : store.tableSort.current;
let rev = reverse || hasFilters ? false : store.tableSort.rev;
if (by) {
if (a[by] < b[by]) return rev ? 1 : -1;
if (a[by] > b[by]) return rev ? -1 : 1;
return 0;
}
});
/**
* Search Spells
* @param {Array} spells
* @param {String} ex 'acid spray'
* @return {Array} filtered spells
*/
const searchSpells = (spells, search) => {
// Convert search to array of words
search = search.split(' ');
// Clone spells so we don't affect the original
spells = clone(spells);
// Reset rankings
spells = spells.map(s => {
s.ranking = 0;
return s;
});
// Rank spells by # of occurances of search terms
spells = spells.map(spell => {
search.forEach(term => {
let spellText = Object.values(spell).join(' ');
let regFind = new RegExp(term, 'gi');
spell.ranking += (spellText.match(regFind) || []).length;
});
return spell;
});
// Return spells that matched at least something
return spells.filter(spell => spell.ranking);
};
/**
* Filter Spells by Class
*/
const filterSpellsByClass = (spells, classes) => {
// If no classes, default to all classes
classes = classes.length ? classes : store.classes.data;
// Clone spells so we don't affect the original
spells = clone(spells);
return spells.filter(spell => {
let spellClasses = spell.classes.join(' ');
let match = false;
classes.forEach(c => {
if (spellClasses.indexOf(c) >= 0) {
match = true;
return true;
}
});
return match;
});
};
/**
* Apply Filters
* @returns {Array} of spells ranked based on searches and filters
*/
let applyFilters = () => sortSpells(
searchSpells(
filterSpellsByClass(
store.spells,
store.classes.current
),
store.search
)
);
/**
* Spell Details Updating
*/
const spellDetails = name => {
if (!name) {
view.spell_details.update({data: {}});
$('body').removeClass('details');
} else {
let data = store.spells.find(spell => name === spell.name);
data.description = emphasis(data.description);
view.spell_details.update({
data,
url: window.location.href
});
$('body').addClass('details');
let clipboard = new Clipboard('.copy-to-clipboard');
clipboard
.on('success', e => $('#toast')[0].MaterialSnackbar.showSnackbar({message: 'Copied link'}))
.on('error', e => $('#toast')[0].MaterialSnackbar.showSnackbar({message: 'Sorry! Unable to copy link'}));
}
}
/**
* Event Bindings
*/
// Listen for header sorts
$('body').on('click', '[data-action-sort]', e => {
let name = $(e.currentTarget).attr('data-action-sort');
let rev = store.tableSort.current === name && !store.tableSort.rev;
store.tableSort.current = name;
store.tableSort.rev = rev;
localStorage.setItem('tableSortName', name);
localStorage.setItem('tableSortRev', rev);
view.spell_list.update({data: sortSpells(store.spells)});
view.table_sort.update(store.tableSort);
});
// Listen for checkbox changes to filter spells
$('body').on('change', '[data-action-classtoggle]', e => {
let name = $(e.currentTarget).attr('data-action-classtoggle');
let add = $(e.currentTarget).prop('checked');
let index = store.classes.current.indexOf(name);
if (index === -1 && add) {
store.classes.current.push(name);
} else if (!add) store.classes.current.splice(index, 1);
store.tableSort.current = store.classes.current.length || store.search.length ? 'ranking' : null;
localStorage.setItem('tableSortName', store.tableSort.current);
localStorage.setItem('classes', store.classes.current);
view.spell_list.update({data: applyFilters()});
view.table_sort.update({current: store.tableSort.current});
});
// Listen to search to filter by
$('body').on('change keyup cut paste', '[data-action-search]', e => {
setTimeout(() => { // Delay for value to change
store.search = $(e.currentTarget).val();
store.tableSort.current = store.search.length || store.classes.current.length ? 'ranking' : null;
store.tableSort.rev = false;
localStorage.setItem('search', store.search);
localStorage.setItem('tableSortName', store.tableSort.current);
localStorage.setItem('tableSortRev', store.tableSort.rev);
view.spell_list.update({data: applyFilters()});
view.table_sort.update(store.tableSort);
}, 0);
});
// Listen for click on spells to open details
$('body').on('click', '[data-action-details]', e => {
let name = $(e.currentTarget).attr('data-action-details');
window.location.hash = name;
spellDetails(name);
});
// Article Scroll with User
$('.mdl-layout__content').on('scroll', debounce(() => {
let distance = $('.mdl-layout__content')[0].scrollTop;
$('[data-template=spell-details]').css('margin-top', distance);
}, 10));
/**
* Fetch Spells
*/
fetch('./spells.json')
.then(response => response.json())
.then(spells => initSpells(spells))
.then(spells => {
store.spells = spells;
store.classes.data = discoverClasses(spells);
view.spell_list.update({data: applyFilters()});
view.class_list.update(store.classes);
if (window.location.hash) spellDetails(window.location.hash.substr(1));
})
.catch(reason => console.error('Unable to retrieve spells list:', reason));

19
src/views/class-list.monk Normal file
View file

@ -0,0 +1,19 @@
{% if data %}
{% for cur of data %}
<label class="mdl-navigation__link">
<div class="mdl-switch mdl-js-switch mdl-js-ripple-effect">
{% if current.includes(cur) %}
<input checked data-action-classtoggle="{{ cur }}" name="class" type="checkbox" class="mdl-switch__input" />
{% else %}
<input data-action-classtoggle="{{ cur }}" name="class" type="checkbox" class="mdl-switch__input" />
{% endif %}
<span class="mdl-switch__label">
{{ cur }}
</span>
</div>
</label>
{% endfor %}
{% else %}
<div class="mdl-spinner mdl-js-spinner is-active"></div>
{% endif %}

View file

@ -0,0 +1 @@
<input value="{{ data }}" class="mdl-textfield__input" data-action-search type="text" name="sample" id="fixed-header-drawer-exp" />

View file

@ -0,0 +1,59 @@
{% if data.name %}
<button data-action-details="" class="mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab">
<i class="material-icons">close</i>
</button>
<h5 class="mdl-typography--display-1 mdl-color-text--teal-600">{{ data.name }}</h5>
<p class="description">{% unsafe data.description %}</p>
<div class="mdl-grid">
<ul class="right mdl-cell mdl-cell--6-col">
<li>
<strong>Range: </strong>
{{ data.range }}
</li>
<li>
<strong>Casting Time: </strong>
{{ data.casting_time }}
</li>
<li>
<strong>Duration: </strong>
{{ data.duration }}
</li>
{% if data.ritual %}
<li>Ritual</li>
{% endif %}
</ul>
<ul class="left mdl-cell mdl-cell--6-col">
<li>
<strong>School: </strong>
{{ data.school }}
</li>
<li>
<strong>Spell Level: </strong>
{{ data.level || 'Cantrip' }}
</li>
<li>
<strong>Class: </strong>
<span class="classes">{{ data.classes.join(', ') }}</span>
</li>
{% if data.components && data.components.raw %}
<li>
<strong>Components: </strong>
{{ data.components.raw }}
</li>
{% endif %}
</ul>
<div class="mdl-textfield mdl-js-textfield">
<label class="mdl-button mdl-js-button mdl-button--icon copy-to-clipboard" for="share-url" data-clipboard-target="#share-url">
<i class="material-icons">content_copy</i>
</label>
<input readonly class="mdl-textfield__input" type="text" id="share-url" value="{{ url }}">
</div>
</div>
{% else %}
<div id="empty">
<h6 class="mdl-typography--title">
Choose a Spell
</h6>
</div>
{% endif %}

33
src/views/spell-list.monk Normal file
View file

@ -0,0 +1,33 @@
{% if data.length %}
{% for spell of data %}
<tr data-action-details="{{ spell.name }}">
<td class="spell-name mdl-data-table__cell--non-numeric">
<strong>
{{ spell.name }}
</strong>
</td>
<td class="spell-school mdl-data-table__cell--non-numeric">
{{ spell.school }}
</td>
<td class="spell-level">
{{ spell.prettyLevel }}
</td>
</tr>
{% endfor %}
{% else %}
<tr class="do-nothing">
<td colspan="4">
{% if data %}
<div class="text-center">
<i class="material-icons mdl-list__item-icon mdl-color-text--orange-600">
warning
</i>
<h5>No Results</h5>
<h6>Try refining your filters in the sidebar.</h6>
</div>
{% else %}
<div class="mdl-spinner mdl-js-spinner is-active"></div>
{% endif %}
</td>
</tr>
{% endif %}

23
src/views/table-sort.monk Normal file
View file

@ -0,0 +1,23 @@
{% for name of data %}
<th data-action-sort="{{ name }}" class="mdl-data-table__cell--non-numeric {{ current === 'ranking' ? 'mdl-color-text--grey-200 do-nothing' : '' }}">
<i class="material-icons mdl-list__item-icon">
{% if name === 'level' %}
exposure
{% endif %}
{% if name === 'name' %}
flash_on
{% endif %}
{% if name === 'school' %}
school
{% endif %}
</i>
<span>
&nbsp; {{ name }}
</span>
<i class="material-icons mdl-color-text--teal-600 mdl-list__item-icon {{ name === current ? 'mdl-color-text--teal-600' : 'mdl-color-text--grey-300' }}">
{{ rev ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}
</i>
</th>
{% endfor %}