Obsidian Portal
Menu
Sign In / Create Account
JavaScript is currently disabled. Obsidian Portal has a lot of really cool features that use JavaScript. You should check them out. We think you'll have a much more enjoyable experience.
Home
Campaigns
Games Nearby
Plans
Community
Help
Resources
Deck of Many Dungeons
Author:
Parmeisan
Slug:
domd
Type:
GameCharacter
System:
other
DST Source Code
HTML Template
<b class="printmsg hidden">** For full-colour print, turn on Backgrounds in the Print dialogue. **</b> <div class=loader><i class="fas fa-spinner fa-spin"></i><div class=loadtext>Loading</div></div> <div class="page"> <div class="formatBar editonly hidden"> <button type="button" class="boldButton formatbtn" title="Bold"> <i class="fas fa-bold"></i></button> <button type="button" class="italicButton formatbtn" title="Italic"> <i class="fas fa-italic"></i></button> <button type="button" class="underlineButton formatbtn" title="Underline"> <i class="fas fa-underline"></i></button> <button type="button" class="indentButton hidden" title="Indent"> </button> <button type="button" class="specialbtn formatbtn" title="Special Characters"> <i class="far fa-keyboard"></i></button> <span class="ascii"></span> </div> <datalist id="domd_colours"> <option id=domd_c_passive value="#f6c605">#f6c605</option> <option id=domd_c_fortify value="#ff9000">#ff9000</option> <option id=domd_c_attack value="#d90000">#d90000</option> <option id=domd_c_move value="#a11ede">#a11ede</option> <option id=domd_c_defend value="#2a57bd">#2a57bd</option> <option id=domd_c_trait value="#79be8a">#79be8a</option> <option id=domd_c_ally value="#007e21">#007e21</option> <option id=domd_c_boost value="#8a490f">#8a490f</option> <option id=domd_c_free value="#dddddd">#dddddd</option> <option id=domd_c_system value="#c6c6c6">#c6c6c6</option> <option id=domd_c_promotional value="#6900ab">#6900ab</option> </datalist> <div class="basic_info"> <div class="page_col"> <div class="card_outer main_card center"> <div class="cat_container"><span class="dsf dsf_campaign rotateLeft"></span></div> <div class="card_inner center"> <span class="dsf dsf_avatar_image readonly"></span> <div class="strip"> <span class="dsf dsf_name readonly">(Auto)</span> <span class="dsf dsf_player readonly">(Auto)</span> </div> <div class="desc_holder"> <span class="dsf dsf_intro_text edit">Name, image and qualities load from main properties. Add a tag called "enemy" for enemies.<br><br>Click to edit this text.</span> </div> <span class="dsf dsf_dst_author readonly hidden"></span> <span class="dsf dsf_bio readonly hidden"></span> </div> </div> </div> <div class="page_col"> <div class="card_outer delete_outer editonly" title="New"> <div class="interface trash dsticky" title="Trash"><i class="fas fa-trash"></i></div> <div class="delete_arrow"><i class="fas fa-share"></i></div> <div class="delete_text">Drag Card<br>Here to<br>Delete</div> </div> </div> <div class="page_col"> <div class="card_outer add_outer editonly" title="New"> <div class="interface add addto_deck dsticky" title="New"><i class="fas fa-plus"></i></div> <div class="add_arrow"><i class="fas fa-share"></i></div> <div class=add_text>Click to<br>Create<br>New Card</div> </div> </div> <div class="buttons"> <div class="interface check readonly hidden autobalance autobalance_deck">0</div> </div> </div><!--/basic_info--> <ul class="group list list_deck"> <div class="page_col column"> <li class="proto item card"> <div class="card_outer center"> <div class="interface backbtn tomethod hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn todeck hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn tocat hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn toabil hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn totier hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn tocust hidden"><i class="fas fa-window-close"></i></div> <div class="interface backbtn fromimg hidden"><i class="fas fa-caret-left"></i></div> <div class="interface backbtn fromcoll hidden"><i class="fas fa-caret-left"></i></div> <div class="interface handle"><i class="fas fa-hand-rock"></i></div> <div class="interface x_boxes hidden"> <div class="x_box dslf dslf_xTL xbT xbL">0</div> <div class="x_box dslf dslf_xTR xbT xbR">0</div> <div class="x_box dslf dslf_xBL xbB xbL">0</div> <div class="x_box dslf dslf_xBR xbB xbR">0</div> </div> <div class="x_Xs hidden"> <div class="x_X xbT xbL"></div> <div class="x_X xbT xbR"></div> <div class="x_X xbB xbL"></div> <div class="x_X xbB xbR"></div> </div> <div class="cat_container"><span class="dslf dslf_category cust rotateLeft hidden"></span></div> <span class="dslf dslf_tier cust hidden">1</span> <div class="card_inner center"> <span class="dslf dslf_card_id edit hidden">[Enter ID]</span> <span class="dslf dslf_state readonly">DEFAULT</span> <span class="palette_icon readonly hidden"><i class="fas fa-palette"></i></span> <span class="dslf dslf_colour hidden">#009933</span> <input type="color" class="colour_picker readonly hidden" list=domd_colours /> <div class="stats"> <div class="statitem statimgL statroll"> <img src="//wilysteed.com/domd/icons/stats/roll.png" alt="Roll" /></div> <div class="statitem statvalL statroll"> <span class="dslf dslf_roll cust"></span></div> <div class="statitem statimgL statrng"> <img src="//wilysteed.com/domd/icons/stats/range.png" alt="Range" /></div> <div class="statitem statvalL statrng"> <span class="dslf dslf_range cust"></span></div> <div class="statitem statimgL statarea"> <img src="//wilysteed.com/domd/icons/stats/area.png" alt="Area" /></div> <div class="statitem statvalL statarea"> <span class="dslf dslf_area cust"></span></div> <div class="statitem statimgR statres"> <img src="//wilysteed.com/domd/icons/stats/resolve.png" alt="Resolve" /></div> <div class="statitem statvalR statres"> <span class="dslf dslf_resolve cust"></span></div> </div> <span class="dslf dslf_dck hidden"></span> <span class="dslf dslf_print_mode hidden"></span> <span class="dslf dslf_img_src hidden"></span> <i class="interface img_selector ghostly hidden far fa-image"></i> <i class="interface coll_selector ghostly hidden fas fa-pencil-alt"></i> <img class="img_display hidden" alt="Art" /> <div class="strip hidden"> <span class="dslf dslf_ability cust hidden"></span> <span class="dslf dslf_tags cust hidden"></span> </div> <div class="desc_holder"> <span class="dslf dslf_card_text cust hidden"></span> <span class="card_html hidden highlight readonly"></span> </div> <div class="coll_holder"></div> </div> <span class="dslf dslf_skbonus cust hidden"></span> <div class="menu"> <div class="menutext hidden"> <span class="mt_header"></span> <span class="mt_long"></span> </div> <div class="menuimgtext hidden"> <img class="img_editdisplay ghostimg" alt="Add Art" /> <b>Choose an Image</b> <i>(Experimental feature)</i> <br>For security reasons, we cannot accept just any URL. However, there are 3 options that should work: <ul><li><a href="https://game-icons.net/1x1/delapouite/orc-head.html" target="_blank">game-icons.net</a> (opens in new tab/window). A sizable collection of game-based images. Find one you like and copy the entire URL. Modifications will be ignored.</li> <li>Images from your campaign's media library. Click the <i class="fas fa-edit"></i> icon to see the URL.</li> <li>If you're very very brave, you can inspect the source code and use the URL of images on base-game cards.</li></ul> <textarea class="img_editbox"></textarea> </div> <div class="menuselect seldeck"></div> <div class="menuselect selcat"></div> <div class="menuselect selabil"></div> <div class="menuselect seltier"></div> <div class="menubtn byid hidden">By ID <i class="fas fa-key"></i></div> <div class="menubtn bycat hidden">By Category <i class="fas fa-list-ol"></i></div> <div class="menubtn crcust hidden">Create Custom <i class="fas fa-edit"></i></div> <div class="menubtn cby1 hidden">Single Ability <i class="fas fa-stop"></i></div> <div class="menubtn cby4 hidden">Collection <i class="fas fa-th-large"></i></div> </div> </div> </li> </div> <div class="page_col column"></div> <div class="page_col column"></div> <span class="oldfields hidden">[{"card_id":"card_id"}]</span> <span class="dsf dsf_deck readonly hidden"></span> </ul><!--/deck--> </div><!--/page--> <div class="editonly">Tips:<ul> <li>Re-order or delete cards by clicking on the "handle" (hand icon) and dragging.</li> <li>Disable some actions from multi-action cards by clicking on the squares below the handle.</li> <li>To print, <b><u>leave edit mode</u></b> (by saving) and turn on Backgrounds in the print dialogue.</li> <li>At the moment of this writing, Firefox does not fully support formatting or special characters, does not navigate well within card text, does not display the preset main-game colour options on the colour picker, and does not correctly allow selection of non-edit-mode card text. Edge fails on only the last, and all of these features work in Chrome. </ul></div>
CSS
@media print { html body * { visibility: hidden; } @page { size: auto portrait; width: 8.5in; height: 11in; mso-header-margin:0in; mso-footer-margin:0in; mso-paper-source:0; margin: 0.21in 0.24in 0.21in 0.3in; overflow: visible;f } .ds_domd .page { position: absolute; top: -3.75in; left: 0in; overflow: visible !important; } .ds_domd .main_card { display: none !important; } .ds_domd .page_col { padding: 0px !important; } } @media all { @import url(https://chainsawxiv.github.io/DST/common/css/csx_exalted_common.css); @font-face { font-family: DoMD'; font-style: normal; font-weight: normal; stretch: 100; src: url('//wilysteed.com/domd/web-resources/font-kit/FreeSans-DoMD.otf') format('otf'), url('//wilysteed.com/domd/web-resources/font-kit/FreeSans-DoMD.ttf') format('ttf'), url('//wilysteed.com/domd/web-resources/font-kit/FreeSans-DoMD.woff2') format('woff2'), url('//wilysteed.com/domd/web-resources/font-kit/FreeSans-DoMD.woff') format('woff'); } .ds_domd .page, .ds_domd .page * { visibility: initial; } .ds_domd .printmsg { margin-left: 1in; } .ds_domd .loader { width: 2in; z-index: 999; position: fixed; top: 0; bottom: 0; left: 0; right: 0; margin: auto; background: #fffd; height: 2in; border: solid black thin; box-shadow: 1px 1px 1px 1px #666; } .ds_domd .fa-spinner { font-size: 1.5in; margin: 0.25in; color: #888; } .ds_domd .loadtext { position: relative; top: -1.125in; } /* FIXME: Sadly, this not only doesn't work in Firefox, but breaks the arrow keys in Firefox. .ds_domd .cust, .ds_domd .highlight { user-select: all; -webkit-user-select: all; -moz-user-select: all; -ms-user-select: all; } */ .ds_domd .debug { font-size: 150%; color: purple; font-weight: bold; z-index: 999; display: block !important; } /* (Almost) everything in the desc_holder should be font-family DoMD.... */ .ds_domd .desc_holder, .ds_domd .desc_holder * { font-family: "DoMD"; } /* The exception is anything in the FontAwesome classes */ .ds_domd .desc_holder .fa, .ds_domd .desc_holder .far ,.ds_domd .desc_holder .fas { font-family:"Font Awesome 5 Free" } .ds_domd .desc_holder ul, .ds_domd .desc_holder ol { padding-left: 0px; margin-left: 0px; } .ds_domd .interface, .ds_domd .formatbtn, .ds_domd .asciibtn, .ds_domd .menubtn, .loadtext { text-align: center; vertical-align: middle; z-index: 99; } .ds_domd .img_selector, .ds_domd .coll_selector { position: absolute; left: 0.74in; z-index: 50; } .ds_domd .img_selector { top: 0.24in; } .ds_domd .coll_selector { top: 0.9in; } .ds_domd .img_editbox { z-index: 1; position: absolute; bottom: -0.1in; height: 0.55in; width: 1.89in; font-size: 125%; color: blue; text-decoration: underline; } .ds_domd .menubtn .fas { float: left; padding: 5px; margin-right: -50px; } .ds_domd .menuimgtext { font-size: 65%; } .ds_domd .menuimgtext li { border-top: thin dotted gray; } .ds_domd .formatbtn, .ds_domd .asciibtn, .ds_domd .menubtn, .ds_domd .menuopt, ds_domd .menuval { line-height: 25px; font-size: 15px; width: 25px; height: 25px; color: black; background-color: #e4dec6; margin: 2px; box-shadow: 1px 1px 2px 1px black; } .ds_domd .ascii { display: grid; grid-template-rows: repeat(2, 25px); grid-template-columns: repeat(6, 25px); grid-column: 4; grid-row: 1; } .ds_domd .asciibtn { background-color: #eceae1; margin-left: -1px; } .ds_domd .specialbtn { grid-column: 4; grid-row: 1; } .ds_domd .menubtn, .ds_domd .menuopt, ds_domd .menuval { height: 12%; width: 90%; margin: 2.5%; align-content: center; display: grid; } .ds_domd .menuval .fas { width: 25px; height: 25px; position: absolute; right: 0.15in; font-size: 150%; } .ds_domd .interface { margin: 10px; line-height: 50px; font-size: 30px; width: 50px; height: 50px; box-shadow: 1px 1px 3px 2px #000000; background-color: #FFFFFF; color: #000000; } .ds_domd .ghostly { /*box-shadow: 1px 1px 3px 2px #00000099 !important;*/ background-color: #FFFFFF99 !important; color: #00000099 !important; } .ds_domd .img_selector { box-shadow: 1px 1px 3px 2px #00000099 !important; } .ds_domd .ghostimg { opacity: 0.2; z-index: 15; } .ds_domd .statitem .cust{ padding: 1px 8px 1px 8px;/*min-width: 0.25in;*/ } .ds_domd .tocust .fas { color: red; } .ds_domd .cust .dslf_card_text { width: 100%; height: 100%; } .ds_domd .edit, .ds_domd .menuval { box-shadow: inset 1px 1px 3px 0px black; /*background: white;*/ /*padding: 3px;*/ } .ds_domd .main_card { background-color: #009933; } .ds_domd .page_col { /*border: thin black dotted;*/ padding: 5px; } .ds_domd .add_outer { background-color: #d0efd1; /*outline: thick #8dd891 dashed !important;*/ } .ds_domd .delete_outer { background-color: rgb(255 235 235); /*outline: thick #ffb8b8 dashed !important;*/ } .ds_domd .add_outer, .ds_domd .delete_outer { margin: 0px !important; padding: 0px !important; outline-offset: -4px; outline: 3px #00000020 dashed; } .ds_domd .add_text, .ds_domd .delete_text { font-size: 200%; font-family: 'Open Sans'; font-weight: bold; width: fit-content; padding-top: 1in; text-align: center; width: 100%; } .ds_domd ul, .ds_domd ol, .ds_domd dl { /* Somewhere else this is set as 1.6 and leaving it as that or overwriting it in add_text will for some strange reason cause add_text to have a hanging first-line margin... */ line-height: 1.5; } .ds_domd .add_text, .ds_domd .add_arrow { color: #8dd891; } .ds_domd .delete_text, .ds_domd .delete_arrow { color: #ffb8b8; } .ds_domd .add_arrow, .ds_domd .delete_arrow { float: right; font-size: 50px; padding: 10px; } .ds_domd .interface.add { float: right; background-color: #8dd891; } .ds_domd .interface.trash { float: right; background-color: #ffb8b8; } .ds_domd .interface.balance { background-color: #ffd900; } .ds_domd .interface.handle, .ds_domd .interface.backbtn { position: absolute; top: 5px; z-index: 5; } .ds_domd .interface.handle { right: 5px; } .ds_domd .interface.backbtn { left: 5px; } .ds_domd .interface.x_boxes { z-index: 3; position: absolute; right: 5px; top: 65px; } .ds_domd .x_box { background-color: white; color: transparent; width: 24px; height: 24px; border: thin black solid; position: absolute; } .ds_domd .x_X { position: absolute; font-size: 1.25in; padding: 0px 35px; color: white; -webkit-text-stroke: #9c2e2e 3px; font-weight: bold; z-index: 1; } .ds_domd .xbL { left: -1px; } .ds_domd .xbR { right: -1px; } .ds_domd .xbT { top: -1px; } .ds_domd .xbB { bottom: -1px; } /*.ds_domd .interface.get_images { background-color: green; position: absolute; right: -25px; bottom: -25px; }*/ .ds_domd .page, .ds_domd .buttons, .ds_domd .list_deck, .ds_domd .basic_info, .ds_domd .menu { display: flex; } .ds_domd .page, .ds_domd .menu { flex-direction: column; } .ds_domd .basic_info, .ds_domd .list_deck { flex-direction: row; } .ds_domd .card_outer { font-family: sans-serif; width: 2.5in; height: 3.5in; padding: 0in; border: 1px solid #00000048; border-radius: 10px; overflow: hidden; font-size: 10pt; background-size: 3.1in; background-position: center; position: relative; box-shadow: 2px 2px 4px 0px #00000099; } .ds_domd .center { display: flex; flex-direction: column; align-items: center; } .ds_domd .card_inner, .ds_domd .menu { position: absolute; top: 0.25in; left: 0.25in; width: 2.0in; height: 3.0in !important; background: white !important; border-radius: 8px; z-index: 0; } .ds_domd .coll_holder { position: relative; top: -0.25in; left: -0.25in; } .ds_domd .card_inner { align-items: flex-start; } .ds_domd .coll_abil { transform: scale(0.5); } .ds_domd .cTL { transform-origin: top left; } .ds_domd .cTR { transform-origin: top right; } .ds_domd .cBL { transform-origin: bottom left; } .ds_domd .cBR { transform-origin: bottom right; } .ds_domd .menu { align-items: center; padding-top: .5in; height: 2.5in !important; } .ds_domd .card .card_outer { background-color: #009933; } .ds_domd .dslf_state { position: absolute; display: none; } .ds_domd .basic_info a { color: black; } .ds_domd .dslf_category, .ds_domd .dsf_campaign { font-weight: bold; } .ds_domd .stats { display: grid; width: 100%; grid-template-columns: 25px 25px auto 25px 25px; grid-template-rows: 22px 22px 22px; place-items: center; font-weight: bold; position: absolute; z-index: 3; } .ds_domd .statitem { display: none; width: 20px; height: 20px; } .ds_domd .statimgL { grid-column: 1; } .ds_domd .statvalL { grid-column: 2; } .ds_domd .statimgR { grid-column: 5; } .ds_domd .statvalR { grid-column: 4; } .ds_domd .statroll { grid-row: 1; } .ds_domd .statrng { grid-row: 2; } .ds_domd .statarea { grid-row: 3; } .ds_domd .statres { grid-row: 1; } .ds_domd .dslf_roll { color: #4301c8; } .ds_domd .dslf_range { color: #3a9709; } .ds_domd .dslf_area { color: #d31c00; } .ds_domd .dslf_resolve { color: #dd6e00; } .ds_domd .strip { position: absolute; top: 1.12in; height: .38in; width: 100%; background: #00993399; border-style: solid none solid none; border-width: 0.01in; font-size: 125%; line-height: 90%; } .ds_domd .colour_picker { position: absolute; right: -0.25in; top: 0.8in; width: 0.55in; height: 0.3in; padding: 0px; appearance: unset; /* removes arrow */ background-color: transparent; border: none; z-index: 20; } .ds_domd .palette_icon { position: absolute; right: 0.05in; top: 0.85in; z-index: 21; -webkit-text-stroke: black 1px; color: white; pointer-events: none; } .ds_domd .dslf_ability, .ds_domd .dsf_name { padding-left: .05in; font-weight: bold; } .ds_domd .dslf_tags, .ds_domd .dsf_player { position: absolute; top: 0.2in; left: 0in; width: 100%; font-size: 50%; padding-left: .05in; font-style: italic; } .ds_domd .dslf_tier { position: absolute; left: 0in; top: 0.25in; background-color: white; /*border-color: #00993399; border-width: 2px; border-style: solid;*/ /* not working */ border-radius: 20px; width: 20px; height: 20px; place-content: center; display: grid; font-weight: bolder; font-size: 150%; } .ds_domd .dsf_bio { padding: 5px; } .ds_domd .trait_list { color: white; position: absolute; bottom: -15px; left: 0px; } .ds_domd .desc_holder { position: absolute; height: 1.6in; top: 1.52in; text-align: left; font-size: 75%; width: 1.95in; padding: 0px 2px; } .ds_domd .dsf_intro_text, .ds_domd .card_html { position: absolute; height: 1.6in; bottom: 0in; } .ds_domd .dslf_card_id, .ds_domd .dslf_skbonus { color: white; background-color: transparent; text-shadow: 1px 1px gray; position: absolute; font-weight: bold; font-size: 75%; width: 0.5in; top: 3.0in; z-index: 3; } .ds_domd .dslf_skbonus { left: 0.25in; top: 3.27in; text-align: left; width: 1.5in; height: 0.3in; line-height: 1em; font-size: 70%; } .ds_domd .dslf_card_id { right: 0px; text-align: right; } .ds_domd .menuselect { z-index: 3; width: 100%; overflow-y: scroll; overflow-x: hidden; } .ds_domd .menuopt { margin: 0px; height: 25px; width: 100%; } .ds_domd .mt_header { font-weight: bold; /*text-transform: uppercase;*/ margin-left: -15px; } .ds_domd .mt_long { font-size: 60%; font-style: italic; } .ds_domd .dsf_name, .ds_domd .dsf_campaign { cursor: pointer; pointer-events: none; } .ds_domd .dsf_avatar_image img { height: 1.1in; } .ds_domd .img_display, .ds_domd .img_editdisplay, .ds_domd .dsf_avatar_image { height: 1.1in; position: absolute; top: 0.01in; left: 0.46in; pointer-events: none; } .ds_domd .list li.card { list-style: none; } /* Never display the formatting bar EXCEPT for the intro text */ .ds_domd .desc_holder .formatBar { position: absolute; top: -0.75in !important /*1.83in !important*/; left: -0.25in !important; display: grid !important; grid-template-rows: repeat(1, 30px); grid-template-columns: repeat(4, 30px); background: #fff4f1cc; width: 242px; height: 55px; box-shadow: 1px 1px 2px 1px black; scale: 85%; } .ds_domd .formatBar { display: none !important; } .ds_domd .activeField {} .ds_domd .editonly {} .ds_domd .hidden, .ds_domd .proto { display: none; } .ds_domd .ccShow { visibility: visible; transform: scale(0.75); } .ds_domd .ccHide { visibility: hidden; transform: unset; width: 0; height: 0; margin: 0; padding: 0; } .ds_domd .cat_container { left: 0.04in; top: 0.6in; position: absolute; } .ds_domd .stuck { z-index: 20; position: fixed; top: 50px; } .ds_domd .rotateLeft { -webkit-transform-origin: right top; -moz-transform: rotate(-90deg); -moz-transform-origin: right top; -ms-transform: rotate(-90deg); -ms-transform-origin: right top; -o-transform: rotate(-90deg); -o-transform-origin: right top; transform: rotate(-90deg); transform-origin: right top; filter: progid:dximagetransform.microsoft.basicimage(rotation=1); width: 2.1in; text-align: right; white-space: nowrap; line-height: .3in; font-size: .2in; position: absolute; left: -2.18in; } }
JavaScript
// Global vars var domd_edit_mode = true; var domd_arrCards = {}; var domd_arrDecks = []; var domd_sticky_posn; var domd_arrStickyRight = []; csx_opts = { 'setupCallback': function(item){domd_listSetup(item);}, 'defaultFieldValue':'', 'defaultContext': document.querySelector(".ds_domd"), 'uiContainer': document.querySelector(".ds_domd"), 'isEditable':true }; // Constants var domd_CSV_CARD_ID = 1; // TODO: If const works for splitter, then these should all be const var domd_GAMEICONS = "https://game-icons.net/icons/000000/transparent/1x1/"; var domd_NOTFOUND = "https://game-icons.net/1x1/delapouite/perspective-dice-six-faces-random.html"; const domd_SPLITTER = "~,~" // For saving & loading elements in custom collections const domd_arrStates = { METHOD: "METHOD", ID_FINAL: "ID_FINAL", CAT_DECK: "DECK", CAT_TIER: "TIER", CAT_CATEGORY: "CATEGORY", CAT_ABILITY: "CAT_ABIL", CAT_FINAL: "CAT_FINAL", CUSTOM_METHOD: "CUST_METHOD", CUSTOM_CARD: "CUST_FINAL", CUSTOM_COLLECTION: "CUST_COLL_FINAL", COLL_EDIT: "COLL_EDIT" }; // ======================= DATA =========================================================================== function domd_getCardDump(data) { $(document).ready(function() { $.ajax({ type: "GET", url: "//wilysteed.com/domd/cards/dump.php", dataType: "text", success: function(data) { domd_processDump(data); } }); }); } // Put the card data into a named 2D array. It can be accessed like so: arrCards["L-L04-06"]["Deck"] function domd_processDump(data) { $(".loader").show(); var lines = data.split(/\r\n|\n/); var headings = domd_splitCSV(lines[0]); // We have the headings, now get the card data for (var i = 1; i < lines.length; i += 1) { var row = domd_splitCSV(lines[i]); var card_id = row[domd_CSV_CARD_ID]; var data = {}; // An unnamed array is easy, but let's use our headings for (var j = 0; j < row.length; j += 1) { data[headings[j]] = row[j]; if (headings[j] == "Deck") { domd_arrDecks.push(row[j]); } } if (!domd_isnull(card_id) && card_id.substr(1,1) == "-" && headings["Print"] != "9" && headings["Print"] != "0") domd_arrCards[card_id] = data; } domd_log("Processed " + Object.keys(domd_arrCards).length + " cards."); domd_arrDecks = domd_sortUnique(domd_arrDecks); domd_updateEverything(); $(".loader").hide(); } function domd_setPropsFromDump(card) { var found = false; var state = domd_getCardProp(card, "state"); var card_id_prop = domd_getCardProp(card, "card_id"); var len = card_id_prop.length; var card_id = (card_id_prop.substr(0, len - 1).toUpperCase() + card_id_prop[len - 1]).trim(); domd_log("setPropsFromDump " + card_id + " state=" + state); if (state == domd_arrStates.CAT_FINAL || state == domd_arrStates.ID_FINAL) { var data = domd_arrCards[card_id]; if (!domd_isnull(data)) { try { if (card_id != card_id_prop) domd_setCardProp(card, "card_id", card_id); domd_setCardProp(card, "tier", data["Tier"]); domd_setCardProp(card, "dck", data["Deck"]); domd_setCardProp(card, "category", data["Category"]); domd_setCardProp(card, "ability", data["Ability"]); domd_setCardProp(card, "card_text", data["Text"]); domd_setCardProp(card, "tags", data["Tags"]); domd_setCardProp(card, "img_src", "//wilysteed.com/domd/icons/" + //_ data["Main_Image"] + ".png"); domd_setCardProp(card, "colour", data["Front_Colour"]); domd_setCardProp(card, "print_mode", data["Print_Mode"]); domd_setCardProp(card, "skbonus", data["SkBonus"]); domd_setCardProp(card, "roll", data["Roll"]); domd_setCardProp(card, "range", data["Range"]); domd_setCardProp(card, "area", data["Area"]); domd_setCardProp(card, "resolve", data["Cost"]); found = true; } catch (err) { domd_log(err); } } } return found; } function domd_toggleX() { var x = $(this); var val = domd_nvl(x.text(), "0"); x.text(val == "X" ? "0" : "X"); domd_XColour(x); domd_updateCardDisplay(x.closest(".card_outer")); } function domd_XColour(x) { var val = domd_nvl(x.text(), "0"); x.css("background-color", (val == "X" ? "#da4354" : "white")); } function domd_bigX(card, ids) { var x =card.find(".x_box"+ids); card.find(".x_X"+ids).text(x.text() == "X" ? "X" : ""); domd_XColour(x); } function domd_toggleASCII() { $(".ds_domd .ascii").toggle(); } // The CSV will have quotes around every item and commas in between function domd_splitCSV(line) { return line.substring(1, line.length-1).split('","'); } function domd_fillSelect(card, sel, arr, field, newstate, selectVals) { // If there's only one option, select that and then return false if (arr.length == 1) { selectVals[field] = arr[0]; domd_setCardProp(card, field, arr[0]); selectVals.state = newstate; domd_setCardProp(card, "state", newstate); return false; } // Otherwise, fill the select list card.find(sel).empty(); //domd_log("=== filling " + sel + "/" + field + "/" + newstate); card.find(sel).append("<div class='menuval'>- Select -<i class='fas fa-caret-square-down'></i></div>"); for (var i = 0; i < arr.length; i += 1) { card.find(sel).append("<div class='menuopt opt" + field + "'>" + arr[i] + "</div>"); } $(".menuopt.opt" + field).off(); $(".menuopt.opt" + field).on("click", function(event) { var me = $(event.target); var card = $(event.target).closest(".card"); card.find(".menuval").text(me.text()); domd_setCardProp(card, field, me.text()); domd_setState(card, newstate); }); return true; } function domd_setPropsForCustomCollection(card) { // Many properties will exist for each ability in a collection, and these are stored as // a list within the actual card prior to being saved. Thus, we also need to load it in. // This procedure is called immediately after creating the collection elements. var state = domd_getCardProp(card, "state"); var card_id = domd_getCardProp(card, "card_id"); if (state == domd_arrStates.CUSTOM_COLLECTION) { var elems = card.find(".card_inner"); // The 4 inner cards var fields = []; // The list of fields we need to copy over, calculated here: $.each($(".cust"), function(idx, obj) { var classes = $(obj).attr("class").split(" "); $.each(classes, function(idx, val) { if (val.startsWith("dslf_")) fields.push(val.replace("dslf_", "")); }); }); domd_setCardProp(obj, field, arrVals[idx + 1]); $.each(fields, function(idx, field) { domd_setPropFieldCC(card, elems, field); }); // And a few others not in the "dslf_" group - FIXME aren't they, though? and what about card_html? //domd_setPropFieldCC(card, elems, "img_src"); //domd_setPropFieldCC(card, elems, "colour"); //domd_setPropFieldCC(card, elems, "print_mode"); } } function domd_setPropFieldCC(card, elems, field) { var arrVals = domd_splitCSV(domd_getCardProp(card, field)); domd_setCardProp(card, field, arrVals[0]); $.each(elems, function(idx, obj) { domd_setCardProp(obj, field, arrVals[idx + 1]); }); } function domd_getProp(s) { return $(".dsf_" + s).text(); } function domd_setProp(s, val) { $(".dsf_" + s).text(val); } function domd_getCardProp(card, s) { return card.find(".dslf_" + s).first().text(); } function domd_setCardProp(card, s, val) { card.find(".dslf_" + s).first().text(val); } // ======================= STATE ========================================================================== function domd_setState(btn, state) { card = btn.closest(".card"); domd_setCardProp(card, "state", state); //domd_setPropsFromDump(card); // this will happen in update -- *after* the ID is determined from the category domd_updateCardDisplay(card); } function domd_dataPostLoad(opts) { domd_log("Post-load"); domd_addIncludes(); $(".dslf_state:contains(DEFAULT)").text(domd_arrStates.METHOD); domd_getCardDump(); // Edit mode prep domd_edit_mode = opts.isEditable; if (domd_edit_mode) { domd_prepASCII(); // For some reason, the icons won't display properly on these unless they are "hidden" and then shown $(".byid").toggle(true); $(".bycat").toggle(true); $(".crcust").toggle(true); $(".ds_domd .specialbtn").live("click", domd_toggleASCII); $(".ds_domd .specialbtn").live("mousedown", function () { this.parentNode.editNode.allowBlur = false; }); $(".ds_domd .specialbtn").live("mouseup", function () { this.parentNode.editNode.allowBlur = true; }); // Enter should submit (and not input "<br>" into the fields) $(".activeField").live("keypress", function (event) { if (event.keyCode === 13) { event.target.blur(); return false; } return true; }); $(".ds_domd .dslf_card_id.edit").live("input", function(event) { var card = $(event.target).closest(".card"); domd_undoCollection(card); domd_setPropsFromDump(card); domd_updateCardDisplay(card); }); $(".ds_domd .img_editbox").live("input", function(event) { var card = $(event.target).closest(".card"); // Update menu image (the one the user is looking at as they type) // The appropriate card/coll image will be updated on fromimg domd_updateImageDisplay(card, ".img_editbox", ".img_editdisplay"); }); $(".ds_domd .addto_deck").live("click", domd_balanceColumns); $(".ds_domd .x_box").live("click", domd_toggleX); } else { $(".printmsg").show(); $(".card_outer").css("pointer-events", "none"); $("link[media=screen]").prop("media", "all"); } } function domd_clickEvent(obj, state) { $(obj).live("click", function(event) { var me = $(event.target); domd_setState(me, state); }); } // ======================= DISPLAY ======================================================================== function domd_updateCardDisplay(card) { // What's displayed or not displayed depends entirely on mode (view/edit) and state var card_id = domd_getCardProp(card, "card_id"); // We need to alter these inside of functions, so make them an object (then we can pass by reference). var selectVals = { back: domd_getCardProp(card, "state"), // Where to go back to state: domd_getCardProp(card, "state"), deck: domd_getCardProp(card, "dck"), cat: domd_getCardProp(card, "category"), ability: domd_getCardProp(card, "ability"), tier: domd_getCardProp(card, "tier") }; domd_log("Displaying card " + card_id + " / " + selectVals.ability); // Fill dropdown arrays (these are based on previous dropdown selections) if (domd_edit_mode) switch (selectVals.state) { case domd_arrStates.CAT_DECK: if (domd_fillSelect(card, ".seldeck", domd_arrDecks, "dck", domd_arrStates.CAT_CATEGORY, selectVals)) break; // if there's only one option, fillselect picks it and then returns false. Carry on. case domd_arrStates.CAT_CATEGORY: var arrCats = [ ]; $.each(domd_arrCards, function (idx, el) { if (el["Deck"] == selectVals.deck) arrCats.push(el["Category"]); }); arrCats = domd_sortUnique(arrCats); if (domd_fillSelect(card, ".selcat", arrCats, "category", domd_arrStates.CAT_ABILITY, selectVals)) break; case domd_arrStates.CAT_ABILITY: var arrAbils = [ ]; $.each(domd_arrCards, function (idx, el) { if (el["Deck"] == selectVals.deck && el["Category"] == selectVals.cat) arrAbils.push(el["Ability"]); }); arrAbils = domd_sortUnique(arrAbils); if (domd_fillSelect(card, ".selabil", arrAbils, "ability", domd_arrStates.CAT_TIER, selectVals)) break; case domd_arrStates.CAT_TIER: var arrTiers = [ ]; $.each(domd_arrCards, function (idx, el) { if (el["Deck"] == selectVals.deck && el["Category"] == selectVals.cat && el["Ability"] == selectVals.ability) arrTiers.push(el["Tier"]); }); arrTiers = domd_sortUnique(arrTiers); if (domd_fillSelect(card, ".seltier", arrTiers, "tier", domd_arrStates.CAT_FINAL, selectVals)) break; case domd_arrStates.CAT_FINAL: $.each(domd_arrCards, function (idx, el) { if (el["Deck"] == selectVals.deck && el["Category"] == selectVals.cat && el["Ability"] == selectVals.ability && el["Tier"] == selectVals.tier) { card_id = el["Card_ID"]; } }); domd_undoCollection(card); domd_setCardProp(card, "card_id", card_id); // don't break! case domd_arrStates.ID_FINAL: domd_setPropsFromDump(card); break; } domd_log("Now " + card_id + " / " + selectVals.ability); // Display stuff based on the state var state = selectVals.state; var backstate = selectVals.back; var coll_edit = state.startsWith("COLL_EDIT") && !state.endsWith("_IMG"); var edit_all = (state == domd_arrStates.CUSTOM_CARD || coll_edit);//state.startsWith("COLL_EDIT")); var coll = domd_isCollection(card); // Is part of a collection var cc = domd_isCCEdit(card); // If part of a custom collection, which ID? (-1 for none) var menu = (state == domd_arrStates.METHOD || state == domd_arrStates.CAT_DECK || state == domd_arrStates.CAT_CATEGORY || state == domd_arrStates.CAT_ABILITY || state == domd_arrStates.CAT_TIER || state == domd_arrStates.CUSTOM_METHOD || /*(state.startsWith("COLL_MENU") && !state.endsWith("_FINAL")) ||*/ state.endsWith("_IMG")); card.find(".card_inner").toggle(!menu || state.endsWith("_IMG")); card.find(".stats").toggle(!menu && !state.endsWith("_IMG")); card.find(".card_inner").toggleClass("ghostly", domd_edit_mode && state.endsWith("_IMG")); card.find(".img_selector").toggle(domd_edit_mode && edit_all); card.find(".coll_selector").toggle(domd_edit_mode && state == domd_arrStates.CUSTOM_COLLECTION); card.find(".dslf_category").show(); card.find(".dslf_tier").css("display", "grid"); card.find(".x_Xs").toggle(coll && state.endsWith("_FINAL")); card.find(".x_boxes").toggle(domd_edit_mode && coll && state.endsWith("_FINAL")); //card.find(".strip").toggle(!coll); // If we're editting one piece of a custom card collection, determine which card_inner(s) to show card.find(".card_inner").each(function(index, obj) { // We can't simply hide the main card, as it contains the others. ccHide should handle that, but the .page * visibility is messing with its children $(obj).toggleClass("ccShow", cc >= 0 && index == cc); $(obj).toggleClass("ccHide", cc >= 0 && index != cc); //$(obj).children().toggleClass("ccHide", cc >= 0 && index != cc); //if (cc >= 0 && index != cc) $(obj).find(".img_selector").css("visibility", "hidden"); // Necessary due to .page * //if (cc >= 0 && index != cc) $(obj).children().css("visibility", "hidden"); // Necessary due to .page * }); card.find(".cat_container").toggle(cc < 1); card.find(".dslf_tier").toggle(cc < 1); card.find(".dslf_skbonus").toggle(true); domd_setColour(card); card.find(".palette_icon").toggle(domd_edit_mode && (edit_all || cc >= 0)); card.find(".colour_picker").toggle(domd_edit_mode && (edit_all || cc >= 0)); card.find(".colour_picker").prop("value", domd_formatColour(domd_getCardProp(card, "colour"))); if (domd_edit_mode) card.find(".interface:visible").addClass("ghostly"); card.find(".stats").css("top", (domd_edit_mode ? "0.45" : "0") + "in"); // Menu/Buttons card.find(".menu").toggle(domd_edit_mode && menu); card.find(".tomethod").toggle(domd_edit_mode && (state == domd_arrStates.ID_FINAL || state == domd_arrStates.CAT_DECK || state == domd_arrStates.CUSTOM_METHOD)); card.find(".todeck").toggle(domd_edit_mode && backstate.endsWith(domd_arrStates.CAT_CATEGORY)); card.find(".tocat").toggle(domd_edit_mode && backstate.endsWith(domd_arrStates.CAT_ABILITY)); card.find(".toabil").toggle(domd_edit_mode && backstate.endsWith(domd_arrStates.CAT_TIER)); card.find(".totier").toggle(domd_edit_mode && backstate == domd_arrStates.CAT_FINAL); card.find(".tocust").toggle(domd_edit_mode && (state == domd_arrStates.CUSTOM_CARD || state == domd_arrStates.CUSTOM_COLLECTION)); card.find(".fromimg").toggle(domd_edit_mode && state.endsWith("_IMG")); card.find(".fromcoll").toggle(domd_edit_mode && coll_edit); card.find(".byid").toggle(domd_edit_mode && state == domd_arrStates.METHOD); card.find(".bycat").toggle(domd_edit_mode && state == domd_arrStates.METHOD); card.find(".crcust").toggle(domd_edit_mode && state == domd_arrStates.METHOD); card.find(".seldeck").toggle(domd_edit_mode && state == domd_arrStates.CAT_DECK); card.find(".selcat").toggle(domd_edit_mode && state == domd_arrStates.CAT_CATEGORY); card.find(".selabil").toggle(domd_edit_mode && state == domd_arrStates.CAT_ABILITY); card.find(".seltier").toggle(domd_edit_mode && state == domd_arrStates.CAT_TIER); // Display inner card(s) var displaying = (!domd_edit_mode || state.endsWith("_FINAL") || coll_edit); if (displaying) { /*if (coll_edit && cc >= 0) domd_showCardInner(card.find(".card_inner").eq(cc), card_id); else */domd_showCardInner(card, card_id); } else { var header, long, icon; switch (state) { case domd_arrStates.CAT_DECK: header = "Deck"; icon = "arrow-left"; long = "Found at the top-left of the card back."; break; case domd_arrStates.CAT_CATEGORY: header = "Category"; icon = "arrow-left"; long = "Found at the top-left of the card front."; break; case domd_arrStates.CAT_ABILITY: header = "Ability"; icon = "arrow-down"; long = "Found at the center of the card."; break; case domd_arrStates.CAT_TIER: header = "Tier"; icon = "reply"; long = "Found at the top-left of the card front."; break; default: header = ""; } card.find(".mt_header").html("<i class='fas fa-" + icon + "'> " + header); card.find(".mt_long").html(long); card.find(".menutext").toggle(!domd_isnull(header)); card.find(".menuimgtext").toggle(state.endsWith("_IMG")); card.find(".img_display").toggle(cc >= 0 || state.endsWith("_FINAL")); card.find(".img_display").toggleClass("ghostimg", cc >= 0); //domd_updateImageDisplay(card); var zidx = (state.endsWith("_IMG") ? "unset" : "0"); card.find(".card_inner").css("z-index", zidx); card.find(".menu").css("z-index", zidx); card.find(".cby1").toggle(state == domd_arrStates.CUSTOM_METHOD); card.find(".cby4").toggle(state == domd_arrStates.CUSTOM_METHOD); card.find(".interface").not(".img_selector").not(".coll_selector").removeClass("ghostly"); } if (domd_edit_mode) { var fields = card.find(".cust"); var coll_state = (state == domd_arrStates.CUSTOM_COLLECTION); var edit_all = (state == domd_arrStates.CUSTOM_CARD || coll_edit);//state.startsWith("COLL_EDIT")); var edit_img = state.endsWith("_IMG"); // FIXME This can be a lot cleaner if it really only needs to run when true // But let's make sure I haven't broken anything by doing this (note if changing back, you'll break tags et al...) if (edit_all) { card.find(".card_html").toggle(!edit_all); fields.toggleClass("hidden", !edit_all); fields.toggle(edit_all); card.find(".statitem").toggle(edit_all); } card.find(".dslf_img_src").toggleClass("edit", edit_img); if (coll_state) { // may override values from edit_all, that's okay card.find(".card_html").show(); card.find(".dslf_card_text").hide(); } fields.toggleClass("edit", edit_all); // If this happens outside the if, we can't unrender (aka save) if (edit_all || edit_img) csx_edit(document.querySelector(".ds_domd")); // If this does, ID types backwards } // Fill in the Xs domd_bigX(card, ".xbT.xbL"); domd_bigX(card, ".xbT.xbR"); domd_bigX(card, ".xbB.xbL"); domd_bigX(card, ".xbB.xbR"); } function domd_showCardInner(card, card_id) { var coll = domd_isCollection(card); card.find(".strip").toggle(!coll); card.find(".desc_holder").toggle(!coll); var state = domd_getCardProp(card, "state"); // Find out whether it SHOULD be a collection, then find out if it is. // We may need to create or undo a collection. Then display the card. var subcards = card.find(".card_inner"); domd_log("showCardInner, card_id=" + card_id + ", coll=" + coll); if (!coll) { domd_undoCollection(card); card.find(".statitem").css("display", "flex"); card.find(".stats").show(); card.find(".dslf_ability").show(); card.find(".dslf_tags").show(); if (domd_getCardProp(card, "print_mode") == "large") { card.find(".card_html").css("font-size", "140%"); } var srcObj = card.find(".dslf_img_src"); if (domd_isnull(srcObj.text())) srcObj.text(domd_NOTFOUND); srcObj.toggle(domd_edit_mode && state.endsWith("_IMG")); domd_updateImageDisplay(card, ".dslf_img_src", ".img_display"); domd_setColour(card); card.find(".img_display").show(); card.find(".statroll").toggle(domd_has(domd_getCardProp(card, "roll"))) card.find(".statrng").toggle(domd_has(domd_getCardProp(card, "range"))) card.find(".statarea").toggle(domd_has(domd_getCardProp(card, "area"))) card.find(".statres").toggle(domd_has(domd_getCardProp(card, "resolve"))); var state = domd_getCardProp(card, "state"); var showHTML = !state.startsWith("CUST_") || !domd_edit_mode; card.find(".card_html").html(domd_getCardProp(card, "card_text")); card.find(".card_html").toggle(showHTML); card.find(".dslf_card_text").toggle(!showHTML); } else { var collCards = {}; if (subcards.length == 1) { // We need to create the new inner cards var collTemplate = card.find(".card_inner").clone(); collTemplate.find(".coll_holder").remove(); collTemplate.addClass("coll_abil"); //var colour = card.find(".dslf_colour"); //collTemplate.append(colour.clone()); collCards["a"] = collTemplate; collCards["b"] = collTemplate.clone(); collCards["c"] = collTemplate.clone(); collCards["d"] = collTemplate.clone(); collCards["a"].addClass("cTL"); collCards["b"].addClass("cTR"); collCards["c"].addClass("cBL"); collCards["d"].addClass("cBR"); } else { // We will never do collections inside collections. It should work, but it's a bad idea all around. collCards["a"] = subcards.eq(1); collCards["b"] = subcards.eq(2); collCards["c"] = subcards.eq(3); collCards["d"] = subcards.eq(4); } $.each(collCards, function(idx, el) { var newid = card_id + idx; //domd_log("Collection: Card " + newid); domd_setCardProp(el, "card_id", newid); card.find(".coll_holder").append(el); domd_setCardProp(el, "state", domd_getCardProp(card, "state")); var newabil = "x"; //This will get set later, the important thing for now is that it not start with COLLECTION domd_setCardProp(el, "ability", newabil); var success = domd_setPropsFromDump(el); if (!success) { el.hide(); } domd_showCardInner(el, newid); // A bit recursive. Just one level. }); card.find(".stats:first").hide(); } domd_showFirstOnly(card, "card_id", state == domd_arrStates.ID_FINAL || state == domd_arrStates.CAT_FINAL, domd_edit_mode && state == domd_arrStates.ID_FINAL); card.find(".coll_holder").find(".dslf").addClass("dsarr"); } function domd_undoCollection(card) { var subcards = card.find(".card_inner"); if (!domd_isnull(subcards) && subcards.length > 1) { domd_log("Bad collection! Undo!"); var collTemplate = $(subcards[1]).clone(); collTemplate.removeClass("coll_abil"); collTemplate.removeClass("cTL"); collTemplate.append("<div class=coll_holder></div>"); card.find(".card_inner").html(collTemplate.html()); //cid = card.find(".dslf_card_id"); //cid.val(cid.val().replace("a", "")); } } function domd_showFirstOnly(card, prop, show, edit) { card.find(".dslf_" + prop).hide(); // Hide inner-collection cards by hiding everything then showing the first card.find(".dslf_" + prop + ":first").toggle(show); card.find(".dslf_" + prop + ":first").toggleClass("edit", edit); } function domd_updateImageDisplay(card, src_class, img_class) { var src = domd_nvl(card.find(src_class).text(), card.find(src_class).val()); if (!domd_isnull(src)) { var img = card.find(img_class); // There are too many ways to XSS with images, so we accept only very specific URLs // https://owasp.org/www-community/xss-filter-evasion-cheatsheet var giRegex = /^https?:\/\/game-icons\.net\/.*\/([\w-]+\/[\w-]+)\.html$/; var wsRegex = /^(https?:)?(\/\/wilysteed\.com\/domd\/icons\/[\w-]+\/[\w-]+.png)$/; var opRegex = /^https?:(\/\/\w+\.cloudfront\.net\/campaigns\/[0-9]+\/assets\/[0-9]+\/[\w-]+\.\w+\?[0-9]+)$/; var match = src.match(opRegex, 'ig'); if (!domd_isnull(match)) domd_setImage(img, match[1]); else { match = src.match(wsRegex, 'ig'); if (!domd_isnull(match)) domd_setImage(img, match[2]); else { match = src.match(giRegex, 'ig'); if (!domd_isnull(match)) { // Need to convert this one first match = domd_GAMEICONS + match[1] + ".svg"; domd_setImage(img, match); } else { match = domd_NOTFOUND.match(giRegex, 'ig'); match =domd_GAMEICONS + match[1] + ".svg"; img.prop("src", match); } } }} } function domd_isCollection(card) { return domd_getCardProp(card, "ability").startsWith("COLLECTION"); } // Given an element from inside a collection's inner_class, returns the name of the dslf_ class associated with it function domd_getMainClass(o) { var allClasses = o.attr("class").split(/\s+/); var mainClass; $(allClasses).each(function(i, s) { if (s.startsWith("dslf_")) mainClass = s; }); return mainClass; } function domd_isCCEdit(card) { var state = domd_getCardProp(card, "state"); if (state.startsWith("COLL_EDIT")) return Number(state.substr(-1)); else return -1; } function domd_setColour(card) { // Card border var colour = domd_formatColour(domd_getCardProp(card, "colour")); var collHolder = card.find(".coll_holder").length == 1; var parent = card.closest(".card"); var notColl = parent.find(".card_inner").length == 1; if (notColl || collHolder) { var outer = parent.find(".card_outer").first(); //outer.css("background-color", colour); domd_overrideCSS(outer, "background-color", colour); } // Inner components var strip = card.find(".strip:first"); domd_overrideCSS(strip, "border-color", colour); domd_overrideCSS(strip, "background", colour + "99" /* transparency */); var tier = card.closest(".card").find(".dslf_tier"); domd_overrideCSS(tier, "background-color", "white"); tier.css("border-color", colour + "99" /* transparency */); } // ======================= SETUP ========================================================================== function domd_listSetup(context){ // Provide default context if (context == undefined) context = csx_opts.defaultContext; // Do setup for the interfaces that you're using csx_list(context); csx_edit(context); csx_check(context); } function domd_balanceColumns() { setTimeout(domd_balanceDelayed, 50); } function domd_balanceDelayed() { var me = $(".list_deck")[0]; var it_sel = "li.item:not(.proto)" var items = me.querySelectorAll(it_sel); var num_cols = me.columns.length; var num_rows = 0; var col_size = [] // It's just easier and more maintainable to loop through once and find some info that I'll need for (var col = 0; col < num_cols; col += 1) { col_size[col] = me.columns[col].querySelectorAll(it_sel).length; num_rows = Math.max(num_rows, col_size[col]); } // Now we go through again & actually add the items var add_col = 0; for (var row = 0; row < num_rows; row += 1) { for (var col = 0; col < num_cols; col += 1) { domd_log("Column " + col + " add row " + row + " col size " + me.columns[col].children.length); // Since we're removing & appending as we go, we actually always want row #0 var card = $(me.columns[col]).find(it_sel).eq(0); if (!domd_isnull(card) && row < col_size[col]) { var abil = card.find(".dslf_ability")[0].textContent; $(me.columns[add_col]).append(card.detach()); domd_log("Added " + abil + " to col " + add_col); add_col = (add_col + 1) % num_cols; } } } } function domd_addIncludes() { var includes = document.createElement('script'); includes.type = 'text/javascript'; includes.src = 'https://chainsawxiv.github.io/DST/common/js/csx_exalted_common.js'; includes.onload = function() { // Callback to use what you just loaded csx_opts.setupCallback(); }; document.body.appendChild(includes); // Other one-time setup domd_centerCols(); domd_sticky_posn = domd_getTop(".basic_info") - domd_getTop("body"); $(".dsticky").each(function(index, elem) { domd_arrStickyRight.push($(elem)[0].getBoundingClientRect().right); }); } function domd_dataPreSave() { // Default the context if not set var context = csx_opts.defaultContext; // Collections must be treated specially, as there are up to 5 elements in each // card for each value (e.g. the main card colour, used in the border, and the // inner card colours, used with the ability/tags). In many cases these are all // the same value, or we only care about the main or only the inner. But // we might as well just save them all and load them back in while loading. var main_inner; $(".card_outer .card_inner").each (function() { var iscoll = domd_isCollection($(this)); domd_log("======== Found .card_inner " + iscoll + " ========"); if (iscoll) main_inner = $(this); else { $(this).find(".dsarr").each (function() { var myClass = domd_getMainClass($(this)); var main_elem = main_inner.find("." + myClass + ":first"); main_elem.text(main_elem.text() + domd_SPLITTER + $(this).text()); domd_log("Mine " + $(this).text()); domd_log("Main " + main_elem.text()); }); } }); // Now we've handled that - we shouldn't try to save the sub-cards, just the main ones $(".coll_holder").html(""); // Bake everything down to its field values var edits = context.querySelectorAll('.dsf:not(.readonly),.edit'); for (var i = 0; i < edits.length; i++) { //domd_log(edits[i]); edits[i].unrender(); } var lists = context.querySelectorAll('.list'); for (var i = 0; i < lists.length; i++) // Just the one for now, but it's possible to add more lists[i].unrender(); } function domd_updateEverything() { domd_updateBasicInfo(); $(".list_deck").find(".card").not(".proto").each(function () { // Find the image based on the Card ID var card = $(this);//.find(".card_outer"); domd_setPropsFromDump(card); // Re-distribute collection values (see PreSave for explanation) // updateCardDisplay beforehand so that it creates the collection cards as clones of the main card domd_updateCardDisplay(card); // Then updateCardDisplay again after because now the proper values are set var main_inner; card.find(".card_inner").each (function() { var state = domd_getCardProp($(this), "state"); var custom = state.startsWith("CUST_"); var iscoll = domd_isCollection($(this)); if (iscoll) main_inner = $(this); else { $(this).find(".dsarr").each (function() { var myClass = domd_getMainClass($(this)); //var main_elem = main_inner.find(".dslf." + myClass); var main_elem = main_inner.find("." + myClass + ":first"); // We need to take the second value (first is the final main value) // and then remove that value from the main "array" var arr = main_elem.text().split(domd_SPLITTER) $(this).text(arr[1]); arr = domd_popElem(arr, 1); main_elem.text(arr.join(domd_SPLITTER)); }); } }); domd_updateCardDisplay(card); }); // Set events // We attach the events by script so that we can use the state constants // We do it now and use "live" so that it includes cards that don't exist yet domd_clickEvent(".tomethod", domd_arrStates.METHOD); domd_clickEvent(".todeck", domd_arrStates.CAT_DECK); domd_clickEvent(".tocat", domd_arrStates.CAT_CATEGORY); domd_clickEvent(".toabil", domd_arrStates.CAT_ABILITY); domd_clickEvent(".totier", domd_arrStates.CAT_TIER); domd_clickEvent(".byid", domd_arrStates.ID_FINAL); domd_clickEvent(".bycat", domd_arrStates.CAT_DECK); // TEMPORARILY DISABLING CUSTOM COLLECTIONS // I can't get this working in time for blind playtesting, so I am going to short-circuit "Create Custom" to show the "Custom -> Single" result domd_clickEvent(".crcust", domd_arrStates.CUSTOM_CARD); domd_clickEvent(".tocust", domd_arrStates.METHOD); /*domd_clickEvent(".crcust", domd_arrStates.CUSTOM_METHOD); domd_clickEvent(".cby1", domd_arrStates.CUSTOM_CARD); // Do these separately because they require ability to be altered $(".cby4").live("click", function(event) { var me = $(event.target); var card = me.closest(".card"); var abil = domd_getCardProp(card, "ability"); if (!abil.startsWith("COLLECTION:")) domd_setCardProp(card, "ability", "COLLECTION:" + abil); domd_setState(me, domd_arrStates.CUSTOM_COLLECTION); }); $(".tocust").live("click", function(event) { var me = $(event.target); var card = me.closest(".card"); domd_setCardProp(card, "ability", domd_getCardProp(card, "ability").replace("COLLECTION:", "")); domd_setState(me, domd_arrStates.CUSTOM_METHOD); });*/ $(".coll_selector").live("click", function(event) { var me = $(event.target); var card = me.closest(".card"); var idx = card.find(".coll_selector").index(me); domd_setState(card, domd_arrStates.COLL_EDIT + idx); }); domd_clickEvent(".fromcoll", domd_arrStates.CUSTOM_COLLECTION); $(".img_selector").live("click", function(event) { var me = $(event.target); var inner = me.closest(".card_inner"); var card = me.closest(".card"); var currState = domd_getCardProp(inner, "state"); domd_setState(me, currState + "_IMG"); // Recover the correct image to display var loc = domd_getCurrLoc(currState); var src = card.find(".dslf_img_src").eq(loc).text(); card.find(".img_editbox").val(domd_nvl(src, domd_NOTFOUND)); domd_updateImageDisplay(card, ".img_editbox", ".img_editdisplay"); }); $(".colour_picker").live("change", function (event) { var me = $(event.target); var inner = me.closest(".card_inner"); domd_setCardProp(inner, "colour", this.value); domd_setColour(inner); }); $(".fromimg").live("click", function(event) { var me = $(event.target); var card = me.closest(".card"); var state = card.find(".dslf_state:first").text(); var newState =state.replace("_IMG", ""); var loc = domd_getCurrLoc(state); var src = card.find(".img_editbox").val(); var inner = card.find(".card_inner").eq(loc); inner.find(".dslf_img_src").text(src); domd_updateImageDisplay(inner, ".dslf_img_src", ".img_display"); domd_setState($(event.target), newState); }); } function domd_getCurrLoc(state) { // Find the last character (excluding _IMG if it exists) var s = state.replace("_IMG", "").slice(-1); var loc = 0; if ($.inArray(s, ["1", "2", "3", "4"]) >= 0) loc = Number(s); return loc; } function domd_updateBasicInfo() { domd_log("update basic info"); // Some things are only visible in edit mode: if (!domd_edit_mode) { $(".interface").css("display", "none"); $(".editonly").css("display", "none"); $(".edit").removeClass("edit"); } // Is enemy? Changes background, etc is_enemy = ($(".as-selections li:contains(enemy)").length + $(".tag-link:contains(enemy)").length) > 0; if (is_enemy) { $(".main_card").css("background-color", "black"); $(".main_card a").css("color", "white"); var strip = $(".main_card .strip"); strip.css("background", "#66666699"); domd_overrideCSS(strip, "border-color", "black"); } // "Player" text var ptext; var pc = $("label[for=game_character_is_pc] .checkbox").hasClass("checked"); if (pc) { var player = domd_getProp("player"); if (domd_isnull(player)) { ptext = "Player: " + player; } } else { ptext = (is_enemy ? "Enemy" : "NPC"); } domd_setProp("player", ptext); } function domd_prepASCII() { var arrChars = Array('Heal', '¢', 'Wound', '£', 'Resolve', '¥', 'Move', '¦', 'Roll', '§', 'Duration', '¨', 'Disable', '«', 'Effect', '©', 'Warning', 'ª', 'Element', '®', 'Negate', '¬'); var template = $(".ds_domd .specialbtn"); var btn = template.clone(); btn.removeClass("formatbtn"); btn.removeClass("specialbtn"); btn.addClass("asciibtn"); var ascii = $(".ds_domd .ascii"); ascii.hide(); for (var i = 0; i < arrChars.length; i += 2) { btn.html(arrChars[i+1]); ascii.append(btn.clone()); } $(".ds_domd .asciibtn:first").css("grid-column-start", "2"); $(".ds_domd .asciibtn").live("click", function(event) { var symbol = $(event.target).text(); domd_insertTextAtCaret(symbol); }); $(".ds_domd .asciibtn").live("mousedown", function (event) { event.target.parentNode.parentNode.editNode.allowBlur = false; }); $(".ds_domd .asciibtn").live("mouseup", function (event) { event.target.parentNode.parentNode.editNode.allowBlur = true; }); } function domd_centerCols() { var container = $(".dynamic_sheet_container"); var card_w = $(".card:first").width(); var padding = $(".page_col:first").css("padding-left").replace("px",""); if (container.width() < card_w * 3 + padding * 4) container.addClass("center"); } // ======================= LIBRARY ======================================================================== function domd_isnull(o) { try { return typeof o == 'undefined' || o.length == 0; } catch (err) { if (err instanceof TypeError) return true; else throw(err); // We don't know what this means, let the calling code catch it } } function domd_nvl(o, val) { return (domd_isnull(o) ? val : o); } function domd_has(o) { return (!domd_isnull(o) && o >= 1); } function domd_log(s) { if (false) console.log(s); } function domd_sortUnique(arr) { // Unique doesn't seem to work if it's not sorted first. // And then it changes the order, so I have to do it again. arr.sort(); $.unique(arr); arr.sort(); return arr; } function domd_formatColour(colour) { // Check for a 6-digit colour code, missing the # symbol (but leave six-letter codes like "yellow") if (colour.match(/^[a-f0-9]{6}$/, 'ig')) colour = "#" + colour; return colour; } // Removes the nth element of an array function domd_popElem(arr, n) { var retArr = []; for (var a = 0; a < arr.length; a += 1) { if (a != n) retArr[retArr.length] = arr[a]; } return retArr; } function domd_getTop(s) { return $(s)[0].getBoundingClientRect().top; } // https://stackoverflow.com/questions/26663602/make-div-stick-to-top-of-page-after-scrolling-past-another-div/26664059 $(window).scroll(function() { var distanceFromTop = $(this).scrollTop(); if (distanceFromTop >= domd_sticky_posn) { $(".dsticky").addClass("stuck"); // Animate the motion so that the user can clearly see what is happening. // It will only happen the first time, since "right" keeps this value - but that's okay. // Also, apparently I can't use "this" or an element parameter here, so I have to redo the selector. ~shrug~ $(".dsticky").each(function (index, elem) { var r = (index * 60) + "px"; $(".dsticky").eq(index).animate({ right: r }, 1000); }); } else { /*$(".dsticky").each(function (index, elem) { var r = Math.round(domd_arrStickyRight[index]) + "px"; $(".dsticky").eq(index).animate({ right: r }, 1000); });*/ $(".dsticky").removeClass("stuck"); } }); // https://stackoverflow.com/questions/2920150/insert-text-at-cursor-in-a-content-editable-div function domd_insertTextAtCaret(text) { var sel, range; if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { range = sel.getRangeAt(0); range.deleteContents(); var textNode = document.createTextNode(text); range.insertNode(textNode); } } else if (document.selection && document.selection.createRange) { document.selection.createRange().text = text; } } // https://stackoverflow.com/questions/38454240/using-css-important-with-javascript/38454576 function domd_overrideCSS(obj, prop, val) { if (!domd_isnull(obj[0])) obj[0].style.setProperty(prop, val, "important"); } // https://stackoverflow.com/questions/9815762/detect-when-an-image-fails-to-load-in-javascript function domd_setImage(img, url) { var tester=new Image(); tester.onload=function() { domd_mainImage(img,url) }; tester.onerror=function() { domd_backupImage(img,url) }; tester.src=url; } function domd_mainImage(img, url) { img.prop("src", url); } function domd_backupImage(img, url) { try { var card_id = img.closest(".card_inner").find(".dslf_card_id").text(); img.css("border", "thick red dotted"); // TODO Remove this before publishing img.prop("src", "https://game-icons.net/icons/000000/transparent/1x1/" + domd_arrCards[card_id]["Icon"] + ".svg"); } catch(err) { img.prop("src", domd_NOTFOUND); } }
Submit Notes
Character sheet for a custom (not yet published) system, tentatively titled Deck of Many Dungeons. I have a second version with significant work already put into it, however there are some issues in that version that I need to work out. In the meantime, I would really like there to be a public version that can create characters & display the characters that I have been creating for my free modules. I have a handful of known issues which I believe are not worth fixing at this time: * Special characters are highlighted when inserted, which is annoying. I've spent a lot of time trying to change the caret position, but nothing seems to work. * You have to click twice when backing out if Ability is skipped (but only Ability). This is rare, fixing it is likely to introduce new bugs, and most people will just assume the click didn't take and click again. I intend to address this for the second version. * There are several features unavailable in Firefox. All of these features are used in creating custom cards only; normal operation and viewing have no issues. I have included a warning to this effect in the Edit Mode instructions. Nevertheless, this should be a lot more stable & functional than the version I tried to submit last year.
Back