1
0
Fork 0

Repo wipe moving to V2

This commit is contained in:
sharpshark28 2017-05-25 17:53:42 -05:00
parent a0d2903141
commit 1c0dc989e9
28 changed files with 0 additions and 15358 deletions

View file

@ -1,18 +0,0 @@
# My Spells
...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/)!
## Systems Supported
* Dungeons & Dragons 5e
* Hackmaster 4e (WIP)
### Will you support _X_ system?
Anyone who wishes to write up spells for a system in json format matching the same syntax used by the existing systems in this repo is welcome to add their system of choice.
## 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

Binary file not shown.

View file

@ -1,231 +0,0 @@
body {
background: #fafafa;
}
[data-action=print],
.printonly {
display: none;
}
.mdl-mini-footer {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
padding: .5em 1em .75em 1em
}
.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-layout__header .mdl-button .mdl-badge[data-badge] {
margin-right: 0;
}
.mdl-layout__header .mdl-button .mdl-badge[data-badge]::after {
left: -1em;
right: auto;
}
.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;
}
#description {
margin: 0;
max-height: 13em;
overflow: auto;
}
#description br:last-of-type {
display: none;
}
#description p:last-of-type {
margin-bottom: 0;
}
#copy {
width: 100%;
}
#details {
columns: 2;
padding: 0;
list-style-type: none;
}
#details strong {
text-transform: capitalize;
}
.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) {
.hide-phone {
display: none;
}
[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;
}

View file

@ -1,45 +0,0 @@
@media print {
body {
background: white;
font-size: 11pt;
box-sizing: border-box;
padding: 1em;
}
body > .printonly {
display: block;
}
body > :not(.printonly) {
display: none;
}
th {
white-space: nowrap;
padding: 1em 1em 1em 0;
text-align: left;
}
td {
vertical-align: top;
}
.spell-description p {
font-size: 8pt;
line-height: 10pt;
margin: .75em 0;
color: gray;
}
.spell-description br {
display: none;
}
#details {
font-size: 8pt;
margin: 0;
columns: 3;
padding: 0;
list-style-type: none;
}
}

View file

@ -1,123 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 9.2 KiB

418
dist/app.js vendored
View file

@ -1,418 +0,0 @@
/**
* 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));
const basicDetails = ['level', 'range', 'duration', 'casting_time', 'saving_throw', 'aoe', 'source'];
/**
* Global store and view holders
*/
let view = {};
let store = {
systems: {
data: [{
friendly: 'Dungeons & Dragons 5e',
value: 'dnd5e'
}, {
friendly: 'HackMaster 4e (WIP)',
value: 'hackmaster4e'
}]
}
};
/**
* See Matching System Friendly and Value
*/
const matchingSystemProp = (system, requested) => store.systems.data.find(d => d[requested === 'value' ? 'friendly' : 'value'] === system)[requested];
/**
* Init Local Storage
*/
const localStorageDefault = (key, val) => {
if (localStorage.getItem(key) === null) localStorage.setItem(key, val);
};
let defaults = {
tableSortName: 'name',
tableSortRev: false,
system: 'dnd5e',
classes: [],
search: ''
};
for (let cur in defaults) localStorageDefault(cur, defaults[cur]);
if (window.location.hash) {
let urlSystem = window.location.hash.substr(1).split('/')[0];
localStorage.setItem('system', store.systems.data.find(d => d.value === urlSystem).value);
}
/**
* 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 Systems
*/
store.systems.current = matchingSystemProp(localStorage.getItem('system'), 'friendly');
view.systems_list = Monkberry.render(system_list, el('system-list'));
view.systems_list.update(store.systems);
/**
* Render Spell List
*/
view.spell_list = Monkberry.render(spell_list, el('spell-list'));
view.spell_list.update({ data: false });
/**
* Render Spell Print List
*/
view.spell_list_print = Monkberry.render(spell_list_print, el('spell-list-print'));
view.spell_list_print.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({ data: false });
/**
* 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 => ` _${ o.trim() }_ `);
});
str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => ` **${ o.trim() }** `);
return str;
};
/**
* Description Prettifier
*/
const descriptionPrettifier = description => {
let md = new Remarkable();
description = Array.isArray(description) ? description.join('\n') : description;
description = emphasis(description);
description = md.render(description);
description = description.replace(/\n/g, '<br>');
return description;
};
/**
* Init Spells
*/
const initSpells = s => s.map((spell, i) => {
spell.selected = false;
spell.ranking = 0;
spell.level = parseInt(spell.level) ? spell.level : 0;
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 = clone(store.spells.find(spell => name === spell.name));
data.description = descriptionPrettifier(data.description);
data.details = basicDetails.map(detail => {
if (data[detail]) {
return {
label: detail.replace('_', ' '),
value: data[detail]
};
}
});
if (data.components && data.components.raw) {
data.details.push({ label: 'components', value: data.components.raw });
}
view.spell_details.update({
data,
url: window.location.href
});
componentHandler.upgradeDom();
$('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' }));
}
};
/**
* Render Print Page
*/
const renderPrint = () => {
let selectedSpells = $('form[data-selected]')
// Get array of items in form
.serializeArray()
// Find spells based on array from form
.map(sel => store.spells.find(spell => sel.value === spell.name))
// Sort by level then alphabetically
.sort((a, b) => {
let aName = a.name.toLowerCase();
let bName = b.name.toLowerCase();
if (a.level > b.level) return 1;
if (a.level < b.level) return -1;
if (aName > bName) return 1;
if (aName < bName) return -1;
return 0;
})
// Prettify Descriptions
.map(spell => {
spell = clone(spell);
spell.description = descriptionPrettifier(spell.description);
spell.details = basicDetails.map(detail => {
if (spell[detail]) {
return {
label: detail.replace('_', ' '),
value: spell[detail]
};
}
});
if (spell.components && spell.components.raw) {
spell.details.push({ label: 'components', value: spell.components.raw });
}
return spell;
});
view.spell_list_print.update({ data: selectedSpells });
if (selectedSpells.length) {
$('[data-action=print] [data-badge]').attr('data-badge', selectedSpells.length);
$('[data-action=print]').slideDown('fast');
} else {
$('[data-action=print]').slideUp('fast');
}
};
/**
* Event Bindings
*/
$('body')
// Listen for header sorts
.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);
componentHandler.upgradeDom();
})
// Listen for checkbox changes to filter spells
.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 });
componentHandler.upgradeDom();
})
// Listen to search to filter by
.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);
componentHandler.upgradeDom();
}, 0);
})
// Listen for click on spells to open details
.on('click', '[data-action-details]', e => {
let name = $(e.currentTarget).attr('data-action-details');
if (name) {
window.location.hash = matchingSystemProp(store.systems.current, 'value') + '/' + name;
} else {
window.location.hash = '';
}
spellDetails(name);
})
// Stop propogation if dontprop clicked
.on('click', '.dontprop', e => {
e.stopPropagation();
})
// Toggle All
.on('change', 'label[for=table-header] input[type=checkbox]', e => {
$(e.target).closest('form').find('[name=selected]').each(function () {
this.checked = e.target.checked;
if (this.checked) $(this).closest('label').addClass('is-checked');else $(this).closest('label').removeClass('is-checked');
});
renderPrint();
}).on('change', 'input[name=selected][type=checkbox]', renderPrint).on('click', '[data-action=print]', e => {
window.print();
});
// 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));
// System changed
$('[data-action=system]').on('change', e => {
let system = $(e.currentTarget).val();
let systemValue = matchingSystemProp(system, 'value');
window.location.hash = '';
spellDetails('');
store.systems.current = system;
localStorage.setItem('system', systemValue);
view.spell_list.update({ data: false });
view.class_list.update({ data: false });
fetchSpells(systemValue);
});
/**
* Fetch Spells
*/
const fetchSpells = (system = matchingSystemProp(store.systems.current, 'value')) => fetch(`./systems/${ system }.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);
componentHandler.upgradeDom();
if (window.location.hash) spellDetails(window.location.hash.substr(1).split('/')[1]);
return spells;
}).catch(reason => console.error('Unable to retrieve spells list:', reason));
fetchSpells();

File diff suppressed because one or more lines are too long

View file

@ -1,3 +0,0 @@
.getmdl-select .mdl-icon-toggle__label{float:right;margin-top:-30px;color:rgba(0,0,0,0.4)}.getmdl-select.is-focused .mdl-icon-toggle__label{color:#3f51b5}.getmdl-select .mdl-menu__container{width:100% !important}.getmdl-select .mdl-menu__container .mdl-menu{width:100%}
/*# sourceMappingURL=getmdl-select.min.css.map */

View file

@ -1,2 +0,0 @@
"use strict";window.onload=function(){getmdlSelect.init(".getmdl-select"),document.addEventListener("DOMNodeInserted",function(e){componentHandler.upgradeDom()},!1)};var getmdlSelect={addEventListeners:function(e){var t=e.querySelector("input"),n=e.querySelectorAll("li");[].forEach.call(n,function(e){e.onclick=function(){if(t.value=e.textContent,"createEvent"in document){var n=document.createEvent("HTMLEvents");n.initEvent("change",!1,!0),t.dispatchEvent(n)}else t.fireEvent("onchange")}})},init:function(e){var t=document.querySelectorAll(e);[].forEach.call(t,function(e){getmdlSelect.addEventListeners(e)})}};
//# sourceMappingURL=getmdl-select.min.js.map

4
dist/jquery.min.js vendored

File diff suppressed because one or more lines are too long

385
dist/monkberry.js vendored
View file

@ -1,385 +0,0 @@
/** _ _
* /\/\ ___ _ __ | | _| |__ ___ _ __ _ __ _ _
* / \ / _ \| '_ \| |/ / '_ \ / _ \ '__| '__| | | |
* / /\/\ \ (_) | | | | <| |_) | __/ | | | | |_| |
* \/ \/\___/|_| |_|_|\_\_.__/ \___|_| |_| \__, |
* |___/
*
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* Enter -> | | | | | |
* + + + +---+ +---+---+ +---+---+ + + +---+ + +---+ + + +
* | | | | | | | | | | |
* +---+---+---+---+---+ +---+---+---+---+ +---+---+ +---+ + +---+---+ +
* | | | | | | | | | |
* + + + +---+---+---+ + + +---+---+ + +---+ + +---+---+ + +
* | | | | | | | | | | |
* + +---+---+ +---+ + + +---+ + +---+---+---+---+---+ + + +---+
* | | | | | | | | | | | | | |
* + +---+ +---+ +---+---+---+ + + + + + + + +---+---+ + +
* | | | | | | | | | | | | |
* +---+---+---+ +---+ + + + +---+---+---+ +---+ +---+---+ + + +
* | | | | | | | | | | |
* + + + +---+---+---+ +---+ + + + +---+ +---+---+ +---+---+ +
* | | | | | | | | | | | | |
* + + +---+---+ +---+---+---+ +---+ +---+ + + + + + +---+ +
* | | | | | | | | | |
* +---+---+ + + +---+---+---+---+ +---+ +---+ + +---+---+ + +---+
* | | | | | | -> Exit
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
(function (document) {
/**
* Monkberry
* @class
*/
function Monkberry() {
this.parent = null;
this.nested = [];
this.nodes = [];
this.filters = null;
this.directives = null;
this.context = null;
this.unbind = null;
this.onRender = null;
this.onUpdate = null;
this.onRemove = null;
}
/**
* Render template and attach it to node.
* @param {Monkberry} template
* @param {Element} node
* @param {Object=} options
* @return {Monkberry}
*/
Monkberry.render = function (template, node, options) {
var view;
if (options && options.noCache) {
view = new template();
} else {
view = template.pool.pop() || new template();
}
if (node.nodeType == 8) {
view.insertBefore(node);
} else {
view.appendTo(node);
}
if (options) {
if (options.parent) {
view.parent = options.parent;
}
if (options.context) {
view.context = options.context;
}
if (options.filters) {
view.filters = options.filters;
}
if (options.directives) {
view.directives = options.directives;
}
}
if (view.onRender) {
view.onRender();
}
return view;
};
/**
* Prerepder template for future usage.
* @param {Monkberry} template - Template name.
* @param {Number} times - Times of prerender.
*/
Monkberry.prerender = function (template, times) {
while (times--) {
template.pool.push(new template());
}
};
/**
* Main loops processor.
*/
Monkberry.loop = function (parent, node, map, template, array, options) {
var i, j, len, keys, transform, arrayLength, childrenSize = map.length;
// Get array length, and convert object to array if needed.
if (Array.isArray(array)) {
transform = transformArray;
arrayLength = array.length;
} else {
transform = transformObject;
keys = Object.keys(array);
arrayLength = keys.length;
}
// If new array contains less items what before, remove surpluses.
len = childrenSize - arrayLength;
for (i in map.items) {
if (len-- > 0) {
map.items[i].remove();
} else {
break;
}
}
// If there is already some views, update there loop state.
j = 0;
for (i in map.items) {
map.items[i].__state__ = transform(array, keys, j, options);
j++;
}
// If new array contains more items when previous, render new views and append them.
for (j = childrenSize, len = arrayLength; j < len; j++) {
// Render new view.
var view = Monkberry.render(template, node, {parent: parent, context: parent.context, filters: parent.filters, directives: parent.directives});
// Set view hierarchy.
parent.nested.push(view);
// Remember to remove from children map on view remove.
i = map.push(view);
view.unbind = (function (i) {
return function () {
map.remove(i);
};
})(i);
// Set view state for later update in onUpdate.
view.__state__ = transform(array, keys, j, options);
}
};
/**
* Main if processor.
*/
Monkberry.cond = function (parent, node, child/*.ref*/, template, test) {
if (child.ref) { // If view was already inserted, update or remove it.
if (!test) {
child.ref.remove();
}
} else if (test) {
// Render new view.
var view = Monkberry.render(template, node, {parent: parent, context: parent.context, filters: parent.filters, directives: parent.directives});
// Set view hierarchy.
parent.nested.push(view);
// Remember to remove child ref on remove of view.
child.ref = view;
view.unbind = function () {
child.ref = null;
};
}
return test;
};
/**
* Main custom tags processor.
*/
Monkberry.insert = function (parent, node, child/*.ref*/, template, data) {
if (child.ref) { // If view was already inserted, update or remove it.
child.ref.update(data);
} else {
// Render new view.
var view = Monkberry.render(template, node, {parent: parent, context: parent.context, filters: parent.filters, directives: parent.directives});
// Set view hierarchy.
parent.nested.push(view);
// Remember to remove child ref on remove of view.
child.ref = view;
view.unbind = function () {
child.ref = null;
};
// Set view data (note what it must be after adding nodes to DOM).
view.update(data);
}
};
/**
* Remove view from DOM.
*/
Monkberry.prototype.remove = function () {
// Remove appended nodes.
var i = this.nodes.length;
while (i--) {
this.nodes[i].parentNode.removeChild(this.nodes[i]);
}
// Remove self from parent's children map or child ref.
if (this.unbind) {
this.unbind();
}
// Remove all nested views.
i = this.nested.length;
while (i--) {
this.nested[i].remove();
}
// Remove this view from parent's nested views.
if (this.parent) {
i = this.parent.nested.indexOf(this);
this.parent.nested.splice(i, 1);
this.parent = null;
}
// Call on remove callback.
if (this.onRemove) {
this.onRemove();
}
// Store view in pool for reuse in future.
this.constructor.pool.push(this);
};
/**
* @param {Element} toNode
*/
Monkberry.prototype.appendTo = function (toNode) {
for (var i = 0, len = this.nodes.length; i < len; i++) {
toNode.appendChild(this.nodes[i]);
}
};
/**
* @param {Element} toNode
*/
Monkberry.prototype.insertBefore = function (toNode) {
if (toNode.parentNode) {
for (var i = 0, len = this.nodes.length; i < len; i++) {
toNode.parentNode.insertBefore(this.nodes[i], toNode);
}
} else {
throw new Error(
"Can not insert child view into parent node. " +
"You need append your view first and then update."
);
}
};
/**
* Return rendered node, or DocumentFragment of rendered nodes if more then one root node in template.
* @returns {Element|DocumentFragment}
*/
Monkberry.prototype.createDocument = function () {
if (this.nodes.length == 1) {
return this.nodes[0];
} else {
var fragment = document.createDocumentFragment();
for (var i = 0, len = this.nodes.length; i < len; i++) {
fragment.appendChild(this.nodes[i]);
}
return fragment;
}
};
/**
* @param {string} query
* @returns {Element}
*/
Monkberry.prototype.querySelector = function (query) {
for (var i = 0; i < this.nodes.length; i++) {
if (this.nodes[i].matches && this.nodes[i].matches(query)) {
return this.nodes[i];
}
if (this.nodes[i].nodeType === 8) {
throw new Error('Can not use querySelector with non-element nodes on first level.');
}
if (this.nodes[i].querySelector) {
var element = this.nodes[i].querySelector(query);
if (element) {
return element;
}
}
}
return null;
};
/**
* Simple Map implementation with length property.
*/
function Map() {
this.items = Object.create(null);
this.length = 0;
this.next = 0;
}
Map.prototype.push = function (element) {
this.items[this.next] = element;
this.length += 1;
this.next += 1;
return this.next - 1;
};
Map.prototype.remove = function (i) {
if (i in this.items) {
delete this.items[i];
this.length -= 1;
} else {
throw new Error('You are trying to delete not existing element "' + i + '" form map.');
}
};
Map.prototype.forEach = function (callback) {
for (var i in this.items) {
callback(this.items[i]);
}
};
Monkberry.Map = Map;
//
// Helper function for working with foreach loops data.
// Will transform data for "key, value of array" constructions.
//
function transformArray(array, keys, i, options) {
if (options) {
var t = {__index__: i};
t[options.value] = array[i];
if (options.key) {
t[options.key] = i;
}
return t;
} else {
return array[i];
}
}
function transformObject(array, keys, i, options) {
if (options) {
var t = {__index__: i};
t[options.value] = array[keys[i]];
if (options.key) {
t[options.key] = keys[i];
}
return t;
} else {
return array[keys[i]];
}
}
if (typeof module !== 'undefined') {
module.exports = Monkberry;
} else {
window.Monkberry = Monkberry;
}
})(window.document);

File diff suppressed because one or more lines are too long

1241
dist/view.js vendored

File diff suppressed because it is too large Load diff

1
dist/view.js.map vendored
View file

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

View file

@ -1,116 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>My Spells</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://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="./dist/getmdl-select.min.css" />
<link rel="stylesheet" href="./assets/app.css" />
<link rel="stylesheet" href="./assets/print.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 data-template="system-list"></div>
<div class="mdl-layout-spacer"></div>
<button data-action="print" class="mdl-button mdl-js-button mdl-js-ripple-effect mdl-color-text--white">
<span class="mdl-badge" data-badge="0">
<i class="material-icons">print</i>
Print
</span>
</button>
<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">
<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">
<form class="mdl-grid" id="selected-spells" data-selected>
<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>
</form>
</div>
</main>
<footer class="mdl-mini-footer">
<div class="mdl-mini-footer__right-section">
<div class="mdl-logo hide-phone">My Spells</div>
<ul class="mdl-mini-footer__link-list">
<li>
<a class="hide-phone 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>
</li>
<li>
<a class="mdl-navigation__link" href="https://dark12222000.github.io/lootsplit/">
<i class="material-icons">local_atm</i>
Loot Split
</a>
</li>
<li>
<a class="mdl-navigation__link" href="http://paulvmoreau.github.io/BeltFedNPCs/">
<i class="material-icons">people</i>
NPC Generator
</a>
</li>
</ul>
</div>
</footer>
<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>
<table class="printonly">
<thead>
<tr>
<th>Spell Name</th>
<th>Level</th>
<th>Description</th>
</tr>
</thead>
<tbody data-template="spell-list-print">
</tbody>
</table>
<script src="https://code.getmdl.io/1.1.3/material.min.js"></script>
<script src="./dist/getmdl-select.min.js"></script>
<script src="./dist/jquery.min.js"></script>
<script src="./dist/monkberry.js"></script>
<script src="./dist/remarkable.min.js"></script>
<script src="./dist/clipboard.min.js"></script>
<script src="./dist/view.js"></script>
<script src="./dist/app.js"></script>
</body>
</html>

133
ogl.html
View file

@ -1,133 +0,0 @@
<html>
<head>
<title>Open Game License v0.1 Simplified</title>
</head>
<body>
<center><a href="./index.html">OGF Main</a> |
<a href="./licenses.html">List of Licenses</a> |
Open Game License Text</center>
<P>THIS LICENSE IS APPROVED FOR GENERAL USE. PERMISSION TO DISTRIBUTE THIS LICENSE
IS MADE BY WIZARDS OF THE COAST!</P>
<P>OPEN GAME LICENSE Version 1.0a</P>
<P>The following text is the property of Wizards of the Coast, Inc. and is
Copyright 2000 Wizards of the Coast, Inc ("Wizards"). All Rights Reserved.</P>
<P>1. Definitions: (a)"Contributors" means the copyright and/or trademark
owners who have contributed Open Game Content; (b)"Derivative Material"
means copyrighted material including derivative works and translations
(including into other computer languages), potation, modification,
correction, addition, extension, upgrade, improvement, compilation,
abridgment or other form in which an existing work may be recast,
transformed or adapted; (c) "Distribute" means to reproduce, license, rent,
lease, sell, broadcast, publicly display, transmit or otherwise distribute;
(d)"Open Game Content" means the game mechanic and includes the methods,
procedures, processes and routines to the extent such content does not
embody the Product Identity and is an enhancement over the prior art and any
additional content clearly identified as Open Game Content by the
Contributor, and means any work covered by this License, including
translations and derivative works under copyright law, but specifically
excludes Product Identity. (e) "Product Identity" means product and product
line names, logos and identifying marks including trade dress; artifacts;
creatures characters; stories, storylines, plots, thematic elements,
dialogue, incidents, language, artwork, symbols, designs, depictions,
likenesses, formats, poses, concepts, themes and graphic, photographic and
other visual or audio representations; names and descriptions of characters,
spells, enchantments, personalities, teams, personas, likenesses and special
abilities; places, locations, environments, creatures, equipment, magical or
supernatural abilities or effects, logos, symbols, or graphic designs; and
any other trademark or registered trademark clearly identified as Product
identity by the owner of the Product Identity, and which specifically
excludes the Open Game Content; (f) "Trademark" means the logos, names,
mark, sign, motto, designs that are used by a Contributor to identify itself
or its products or the associated products contributed to the Open Game
License by the Contributor (g) "Use", "Used" or "Using" means to use,
Distribute, copy, edit, format, modify, translate and otherwise create
Derivative Material of Open Game Content. (h) "You" or "Your" means the
licensee in terms of this agreement.</P>
<P>2. The License: This License applies to any Open Game Content that contains
a notice indicating that the Open Game Content may only be Used under and in
terms of this License. You must affix such a notice to any Open Game Content
that you Use. No terms may be added to or subtracted from this License
except as described by the License itself. No other terms or conditions may
be applied to any Open Game Content distributed using this License.</P>
<P>3.Offer and Acceptance: By Using the Open Game Content You indicate Your
acceptance of the terms of this License.</P>
<P>4. Grant and Consideration: In consideration for agreeing to use this
License, the Contributors grant You a perpetual, worldwide, royalty-free,
non-exclusive license with the exact terms of this License to Use, the Open
Game Content.</P>
<P>5.Representation of Authority to Contribute: If You are contributing
original material as Open Game Content, You represent that Your
Contributions are Your original creation and/or You have sufficient rights
to grant the rights conveyed by this License.</P>
<P>6.Notice of License Copyright: You must update the COPYRIGHT NOTICE portion
of this License to include the exact text of the COPYRIGHT NOTICE of any
Open Game Content You are copying, modifying or distributing, and You must
add the title, the copyright date, and the copyright holder's name to the
COPYRIGHT NOTICE of any original Open Game Content you Distribute.</P>
<P>7. Use of Product Identity: You agree not to Use any Product Identity,
including as an indication as to compatibility, except as expressly licensed
in another, independent Agreement with the owner of each element of that
Product Identity. You agree not to indicate compatibility or
co-adaptability with any Trademark or Registered Trademark in conjunction with a work containing
Open Game Content except as expressly licensed in another, independent
Agreement with the owner of such Trademark or Registered Trademark. The use of any Product Identity
in Open Game Content does not constitute a challenge to the ownership of
that Product Identity. The owner of any Product Identity used in Open Game
Content shall retain all rights, title and interest in and to that Product
Identity.</P>
<P>8. Identification: If you distribute Open Game Content You must clearly
indicate which portions of the work that you are distributing are Open Game
Content.</P>
<P>9. Updating the License: Wizards or its designated Agents may publish
updated versions of this License. You may use any authorized version of
this License to copy, modify and distribute any Open Game Content originally
distributed under any version of this License.</P>
<P>10 Copy of this License: You MUST include a copy of this License with every
copy of the Open Game Content You Distribute.</P>
<P>11. Use of Contributor Credits: You may not market or advertise the Open
Game Content using the name of any Contributor unless You have written
permission from the Contributor to do so.</P>
<P>12 Inability to Comply: If it is impossible for You to comply with any of
the terms of this License with respect to some or all of the Open Game
Content due to statute, judicial order, or governmental regulation then You
may not Use any Open Game Material so affected.</P>
<P>13 Termination: This License will terminate automatically if You fail to
comply with all terms herein and fail to cure such breach within 30 days of
becoming aware of the breach. All sublicenses shall survive the termination
of this License.</P>
<P>14 Reformation: If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent necessary
to make it enforceable.</P>
<P>15 COPYRIGHT NOTICE<br>
Open Game License v 1.0 Copyright 2000, Wizards of the Coast, Inc.</P>
<p>
<center><a href="./index.html">OGF Main</a></Center>
</p>
</body>
</html>

View file

@ -1,31 +0,0 @@
{
"name": "my_spells_5e",
"version": "1.0.0",
"description": "5e Spells by Class",
"main": "",
"watch": {
"views": "views/*.monk"
},
"scripts": {
"postinstall": "mkdir -p dist && cp node_modules/clipboard/dist/clipboard.min.js dist && cp node_modules/jquery/dist/jquery.min.js dist && cp node_modules/monkberry/monkberry.js dist && cp node_modules/remarkable/dist/remarkable.min.js dist && cp node_modules/getmdl-select/getmdl-select.min.css dist && cp node_modules/getmdl-select/getmdl-select.min.js dist",
"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": {
"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",
"getmdl-select": "^1.0.4",
"jquery": "^3.1.0",
"monkberry": "^4.0.7",
"remarkable": "^1.6.2"
}
}

BIN
src/.DS_Store vendored

Binary file not shown.

View file

@ -1,435 +0,0 @@
/**
* 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));
const basicDetails = ['level', 'range', 'duration', 'casting_time', 'saving_throw', 'aoe', 'source'];
/**
* Global store and view holders
*/
let view = {};
let store = {
systems: {
data: [{
friendly: 'Dungeons & Dragons 5e',
value: 'dnd5e'
}, {
friendly: 'HackMaster 4e (WIP)',
value: 'hackmaster4e'
}]
}
};
/**
* See Matching System Friendly and Value
*/
const matchingSystemProp = (system, requested) => store.systems.data.find(d => d[requested === 'value' ? 'friendly' : 'value'] === system)[requested];
/**
* Init Local Storage
*/
const localStorageDefault = (key, val) => {
if (localStorage.getItem(key) === null) localStorage.setItem(key, val);
};
let defaults = {
tableSortName: 'name',
tableSortRev: false,
system: 'dnd5e',
classes: [],
search: ''
};
for (let cur in defaults) localStorageDefault(cur, defaults[cur]);
if (window.location.hash) {
let urlSystem = window.location.hash.substr(1).split('/')[0];
localStorage.setItem('system', store.systems.data.find(d => d.value === urlSystem).value);
}
/**
* 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 Systems
*/
store.systems.current = matchingSystemProp(localStorage.getItem('system'), 'friendly');
view.systems_list = Monkberry.render(system_list, el('system-list'));
view.systems_list.update(store.systems);
/**
* Render Spell List
*/
view.spell_list = Monkberry.render(spell_list, el('spell-list'));
view.spell_list.update({data: false});
/**
* Render Spell Print List
*/
view.spell_list_print = Monkberry.render(spell_list_print, el('spell-list-print'));
view.spell_list_print.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({data: false});
/**
* 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 => ` _${o.trim()}_ `);
});
str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => ` **${o.trim()}** `);
return str;
};
/**
* Description Prettifier
*/
const descriptionPrettifier = description => {
let md = new Remarkable();
description = Array.isArray(description) ? description.join('\n') : description;
description = emphasis(description);
description = md.render(description);
description = description.replace(/\n/g, '<br>');
return description;
};
/**
* Init Spells
*/
const initSpells = s => s.map((spell, i) => {
spell.selected = false;
spell.ranking = 0;
spell.level = parseInt(spell.level) ? spell.level : 0;
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 = clone(store.spells.find(spell => name === spell.name));
data.description = descriptionPrettifier(data.description);
data.details = basicDetails.map(detail => {
if (data[detail]) {
return {
label: detail.replace('_', ' '),
value: data[detail]
};
}
});
if (data.components && data.components.raw) {
data.details.push({label: 'components', value: data.components.raw});
}
view.spell_details.update({
data,
url: window.location.href
});
componentHandler.upgradeDom();
$('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'}));
}
}
/**
* Render Print Page
*/
const renderPrint = () => {
let selectedSpells = $('form[data-selected]')
// Get array of items in form
.serializeArray()
// Find spells based on array from form
.map(sel => store.spells.find(spell => sel.value === spell.name))
// Sort by level then alphabetically
.sort((a, b) => {
let aName = a.name.toLowerCase();
let bName = b.name.toLowerCase();
if (a.level > b.level) return 1;
if (a.level < b.level) return -1;
if (aName > bName) return 1;
if (aName < bName) return -1;
return 0;
})
// Prettify Descriptions
.map(spell => {
spell = clone(spell);
spell.description = descriptionPrettifier(spell.description);
spell.details = basicDetails.map(detail => {
if (spell[detail]) {
return {
label: detail.replace('_', ' '),
value: spell[detail]
};
}
});
if (spell.components && spell.components.raw) {
spell.details.push({label: 'components', value: spell.components.raw});
}
return spell;
});
view.spell_list_print.update({data: selectedSpells});
if (selectedSpells.length) {
$('[data-action=print] [data-badge]').attr('data-badge', selectedSpells.length);
$('[data-action=print]').slideDown('fast');
} else {
$('[data-action=print]').slideUp('fast');
}
};
/**
* Event Bindings
*/
$('body')
// Listen for header sorts
.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);
componentHandler.upgradeDom();
})
// Listen for checkbox changes to filter spells
.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});
componentHandler.upgradeDom();
})
// Listen to search to filter by
.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);
componentHandler.upgradeDom();
}, 0);
})
// Listen for click on spells to open details
.on('click', '[data-action-details]', e => {
let name = $(e.currentTarget).attr('data-action-details');
if (name) {
window.location.hash = matchingSystemProp(store.systems.current, 'value') + '/' + name;
} else {
window.location.hash = '';
}
spellDetails(name);
})
// Stop propogation if dontprop clicked
.on('click', '.dontprop', e => {
e.stopPropagation();
})
// Toggle All
.on('change', 'label[for=table-header] input[type=checkbox]', e => {
$(e.target).closest('form').find('[name=selected]').each(function() {
this.checked = e.target.checked;
if(this.checked) $(this).closest('label').addClass('is-checked');
else $(this).closest('label').removeClass('is-checked');
});
renderPrint();
})
.on('change', 'input[name=selected][type=checkbox]', renderPrint)
.on('click', '[data-action=print]', e => {
window.print();
});
// 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));
// System changed
$('[data-action=system]').on('change', e => {
let system = $(e.currentTarget).val();
let systemValue = matchingSystemProp(system, 'value');
window.location.hash = '';
spellDetails('');
store.systems.current = system;
localStorage.setItem('system', systemValue);
view.spell_list.update({data: false});
view.class_list.update({data: false});
fetchSpells(systemValue);
})
/**
* Fetch Spells
*/
const fetchSpells = (system = matchingSystemProp(store.systems.current, 'value')) => fetch(`./systems/${system}.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);
componentHandler.upgradeDom();
if (window.location.hash) spellDetails(window.location.hash.substr(1).split('/')[1]);
return spells;
})
.catch(reason => console.error('Unable to retrieve spells list:', reason));
fetchSpells();

View file

@ -1,19 +0,0 @@
{% 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

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

View file

@ -1,27 +0,0 @@
{% 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>
<ul id="details">
{% for detail of data.details %}
<li>
<strong>{{ detail.label }}</strong>&nbsp;{{ detail.value }}
</li>
{% endfor %}
</ul>
<p id="description">{% unsafe data.description %}</p>
<div class="mdl-textfield mdl-js-textfield" id="copy">
<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>
{% else %}
<div id="empty">
<h6 class="mdl-typography--title">
Choose a Spell
</h6>
</div>
{% endif %}

View file

@ -1,23 +0,0 @@
{% for spell of data %}
<tr>
<td class="spell-name">
<strong>
{{ spell.name }}
</strong>
</td>
<td class="spell-level">
{{ spell.level }}
</td>
<td class="spell-description">
<ul id="details">
{% for detail of spell.details %}
<li>
<strong>{{ detail.label }}</strong>&nbsp;{{ detail.value }}
</li>
{% endfor %}
</ul>
{% unsafe spell.description %}
</td>
</tr>
{% endfor %}

View file

@ -1,40 +0,0 @@
{% if data.length %}
{% for spell of data %}
{% if spell.name %}
<tr data-action-details="{{ spell.name }}">
<td class="hide-phone">
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect mdl-data-table__select dontprop">
<input type="checkbox" name="selected" value="{{ spell.name }}" class="mdl-checkbox__input dontprop" />
</label>
</td>
<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.level }}
</td>
</tr>
{% endif %}
{% 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 %}

View file

@ -1,11 +0,0 @@
{% if data %}
<div class="mdl-textfield mdl-js-textfield mdl-textfield--floating-label getmdl-select">
<input class="mdl-textfield__input" data-action="system" value="{{ current }}" type="text" id="system" readonly tabIndex="-1"/>
<label class="mdl-textfield__label mdl-color-text--white" for="system">System</label>
<ul class="mdl-menu mdl-menu--bottom-left mdl-js-menu" for="system">
{% for system of data %}
<li class="mdl-menu__item">{{ system.friendly }}</li>
{% endfor %}
</ul>
</div>
{% endif %}

View file

@ -1,28 +0,0 @@
<th class="hide-phone">
<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>
{% 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 %}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff