diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d13953c Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..bd80c6a --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# My Spells (5e) +...is a user friendly mobile responsive app designed to do one thing well: _View Spells_. + +[Check it out on github.io](https://sharpshark28.github.io/my_spells/)! + +## For Developers +1. Clone/Fork Repo +2. `npm install` +3. `npm run develop` + +You may wish to run a local http server such as [http-server](https://github.com/indexzero/http-server). diff --git a/assets/.DS_Store b/assets/.DS_Store new file mode 100644 index 0000000..09b8f95 Binary files /dev/null and b/assets/.DS_Store differ diff --git a/assets/app.css b/assets/app.css new file mode 100644 index 0000000..3fe3d5f --- /dev/null +++ b/assets/app.css @@ -0,0 +1,181 @@ +body { + background: #fafafa; +} + +.do-nothing { + pointer-events: none; +} + +.mdl-layout-title i { + position: relative; + bottom: -.125em; +} + +#empty { + padding-bottom: 100%; + text-align: center; + background: url('wand.svg') no-repeat top center; +} + +.mdl-navigation .mdl-textfield__label i { + vertical-align: top; + font-size: 1.25em; +} + +.mdl-navigation .mdl-textfield__input { + box-sizing: border-box; + padding-left: 1em; + padding-right: 1em; +} + +.mdl-navigation .mdl-textfield__label { + padding-left: 1em; +} + +ul, +ol { + padding: 0 1em; +} + +.text-center { + text-align: center; +} + +.left { + float: left; +} + +.right { + float: right; +} + +.page-content { + position: relative; +} + +.page-content .mdl-grid { + padding: 0; +} + +.page-content .mdl-cell { + margin: 0; +} + +[data-template=spell-details] { + transition: margin-top 500ms ease-out; + padding: 0 2rem; +} + +[data-template=spell-details] .description { + max-height: 13em; + overflow: auto; +} + +.mdl-layout__header [data-action-details=""] { + display: none; + position: absolute; + left: 0; + z-index: 10; + height: 4rem; + width: 4rem; +} + +.mdl-layout__header [data-action-details=""] i { + position: absolute; + top: 1rem; + left: 1rem; +} + +[data-template=spell-details] [data-action-details=""] { + position: absolute; + top: .5rem; + right: .5rem; +} + +@media (max-width: 840px) { + [data-template=spell-details] { + margin-top: 0 !important; + } + + #spell-list-container, + [data-template=spell-details] { + transition: left 250ms; + } + + #spell-list-container { + position: absolute; + width: 100%; + left: 0; + } + + body.details #spell-list-container { + left: -95%; + } + + body.details #spell-list-container::after { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + background: black; + opacity: .25; + } + + [data-template=spell-details] { + position: fixed; + width: 95%; + left: 100%; + overflow: auto; + max-height: calc(100% - 56px) + } + + body.details [data-template=spell-details] { + left: 5%; + } + + body.details .mdl-layout__header [data-action-details=""] { + display: block; + } + + body.details .mdl-layout__header .mdl-layout__drawer-button { + display: none; + } + + body.details [data-template=spell-details] [data-action-details=""] { + display: none; + } +} + +[data-action-details] { + cursor: pointer; +} + +[data-action-sort], +[data-template=class-list] label, +.classes, +.spell-level, +.spell-name, +.spell-school { + text-transform: capitalize; +} + +[data-action-sort] { + cursor: pointer; + user-select: none; +} + +[data-action-sort]:hover { + background: #eee; +} + +[data-action-sort] i.material-icons.mdl-list__item-icon { + position: relative; + bottom: -.25em; + color: inherit; +} + +#share-url { + padding-left: 2em; +} diff --git a/assets/wand.svg b/assets/wand.svg new file mode 100644 index 0000000..cc66a6e --- /dev/null +++ b/assets/wand.svg @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dist/.DS_Store b/dist/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/dist/.DS_Store differ diff --git a/dist/app.js b/dist/app.js new file mode 100644 index 0000000..d5f82bd --- /dev/null +++ b/dist/app.js @@ -0,0 +1,279 @@ +/** + * Misc helper functions + */ +Object.values = x => Object.keys(x).reduce((y, z) => y.push(x[z]) && y, []); +const debounce = (func, wait, immediate) => { + let timeout; + return function () { + let context = this, + args = arguments; + let later = function () { + timeout = null; + if (!immediate) func.apply(context, args); + }; + let callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +}; +const el = id => $(`[data-template=${ id }]`)[0] || console.error('Unable to render to', id); +const clone = obj => JSON.parse(JSON.stringify(obj)); + +/** + * Global store and view holders + */ +let store = {}; +let view = {}; + +/** + * Init Local Storage + */ +const localStorageDefault = (key, val) => { + if (localStorage.getItem(key) === null) localStorage.setItem(key, val); +}; +let defaults = { + tableSortName: 'name', + tableSortRev: false, + classes: [], + search: '' +}; +for (let cur in defaults) localStorageDefault(cur, defaults[cur]); + +/** + * Render Table Sort + */ +store.tableSort = { + data: ['name', 'school', 'level'], + current: localStorage.getItem('tableSortName'), + rev: localStorage.getItem('tableSortRev') !== 'false' +}; +view.table_sort = Monkberry.render(table_sort, el('table-sort')); +view.table_sort.update(store.tableSort); + +/** + * Render Spell List + */ +view.spell_list = Monkberry.render(spell_list, el('spell-list')); +view.spell_list.update({}); + +/** + * Render Spell Details + */ +view.spell_details = Monkberry.render(spell_details, el('spell-details')); +view.spell_details.update({ data: {} }); + +/** + * Render Class List + */ +store.classes = { + data: [], + current: localStorage.getItem('classes') ? localStorage.getItem('classes').split(',') : [] +}; +view.class_list = Monkberry.render(class_list, el('class-list')); +view.class_list.update(store.classes); + +/** + * Render Search + */ +store.search = localStorage.getItem('search'); +view.search_field = Monkberry.render(search_field, el('search-field')); +view.search_field.update({ data: store.search }); + +/** + * Discover Classes + */ +const discoverClasses = spells => { + let classes = []; + spells.forEach(spell => { + if (!spell.classes) return; + spell.classes.forEach(current => { + if (!classes.includes(current)) classes.push(current); + }); + }); + return classes.sort((a, b) => a > b); +}; + +/** + * Emphasis on important string bits + * @param {string} string + */ +const emphasis = str => { + let keywords = ['constitution', 'con', 'intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check']; + keywords.forEach(word => { + let r = new RegExp(` ${ word } `, 'gi'); + str = str.replace(r, o => `${ o }`); + }); + + str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `${ o }`); + return str; +}; + +/** + * Init Spells + */ +const initSpells = s => s.map((spell, i) => { + spell.selected = false; + spell.ranking = 0; + spell.level = parseInt(spell.level) ? spell.level : 0; + spell.prettyLevel = spell.level === 0 ? 'C' : spell.level; + return spell; +}); + +/** + * Sort Spells + */ +const sortSpells = (s, sortBy, reverse) => s.sort((a, b) => { + let hasFilters = store.classes.current.length || store.search.length; + let by = sortBy || hasFilters ? 'ranking' : store.tableSort.current; + let rev = reverse || hasFilters ? false : store.tableSort.rev; + if (by) { + if (a[by] < b[by]) return rev ? 1 : -1; + if (a[by] > b[by]) return rev ? -1 : 1; + return 0; + } +}); + +/** + * Search Spells + * @param {Array} spells + * @param {String} ex 'acid spray' + * @return {Array} filtered spells + */ +const searchSpells = (spells, search) => { + // Convert search to array of words + search = search.split(' '); + // Clone spells so we don't affect the original + spells = clone(spells); + // Reset rankings + spells = spells.map(s => { + s.ranking = 0; + return s; + }); + // Rank spells by # of occurances of search terms + spells = spells.map(spell => { + search.forEach(term => { + let spellText = Object.values(spell).join(' '); + let regFind = new RegExp(term, 'gi'); + spell.ranking += (spellText.match(regFind) || []).length; + }); + return spell; + }); + // Return spells that matched at least something + return spells.filter(spell => spell.ranking); +}; + +/** + * Filter Spells by Class + */ +const filterSpellsByClass = (spells, classes) => { + // If no classes, default to all classes + classes = classes.length ? classes : store.classes.data; + // Clone spells so we don't affect the original + spells = clone(spells); + return spells.filter(spell => { + let spellClasses = spell.classes.join(' '); + let match = false; + classes.forEach(c => { + if (spellClasses.indexOf(c) >= 0) { + match = true; + return true; + } + }); + return match; + }); +}; + +/** + * Apply Filters + * @returns {Array} of spells ranked based on searches and filters + */ +let applyFilters = () => sortSpells(searchSpells(filterSpellsByClass(store.spells, store.classes.current), store.search)); + +/** + * Spell Details Updating + */ +const spellDetails = name => { + if (!name) { + view.spell_details.update({ data: {} }); + $('body').removeClass('details'); + } else { + let data = store.spells.find(spell => name === spell.name); + data.description = emphasis(data.description); + view.spell_details.update({ + data, + url: window.location.href + }); + $('body').addClass('details'); + let clipboard = new Clipboard('.copy-to-clipboard'); + clipboard.on('success', e => $('#toast')[0].MaterialSnackbar.showSnackbar({ message: 'Copied link' })).on('error', e => $('#toast')[0].MaterialSnackbar.showSnackbar({ message: 'Sorry! Unable to copy link' })); + } +}; +/** + * Event Bindings + */ +// Listen for header sorts +$('body').on('click', '[data-action-sort]', e => { + let name = $(e.currentTarget).attr('data-action-sort'); + let rev = store.tableSort.current === name && !store.tableSort.rev; + store.tableSort.current = name; + store.tableSort.rev = rev; + localStorage.setItem('tableSortName', name); + localStorage.setItem('tableSortRev', rev); + view.spell_list.update({ data: sortSpells(store.spells) }); + view.table_sort.update(store.tableSort); +}); + +// Listen for checkbox changes to filter spells +$('body').on('change', '[data-action-classtoggle]', e => { + let name = $(e.currentTarget).attr('data-action-classtoggle'); + let add = $(e.currentTarget).prop('checked'); + let index = store.classes.current.indexOf(name); + if (index === -1 && add) { + store.classes.current.push(name); + } else if (!add) store.classes.current.splice(index, 1); + store.tableSort.current = store.classes.current.length || store.search.length ? 'ranking' : null; + localStorage.setItem('tableSortName', store.tableSort.current); + localStorage.setItem('classes', store.classes.current); + view.spell_list.update({ data: applyFilters() }); + view.table_sort.update({ current: store.tableSort.current }); +}); + +// Listen to search to filter by +$('body').on('change keyup cut paste', '[data-action-search]', e => { + setTimeout(() => { + // Delay for value to change + store.search = $(e.currentTarget).val(); + store.tableSort.current = store.search.length || store.classes.current.length ? 'ranking' : null; + store.tableSort.rev = false; + localStorage.setItem('search', store.search); + localStorage.setItem('tableSortName', store.tableSort.current); + localStorage.setItem('tableSortRev', store.tableSort.rev); + view.spell_list.update({ data: applyFilters() }); + view.table_sort.update(store.tableSort); + }, 0); +}); + +// Listen for click on spells to open details +$('body').on('click', '[data-action-details]', e => { + let name = $(e.currentTarget).attr('data-action-details'); + window.location.hash = name; + spellDetails(name); +}); + +// Article Scroll with User +$('.mdl-layout__content').on('scroll', debounce(() => { + let distance = $('.mdl-layout__content')[0].scrollTop; + $('[data-template=spell-details]').css('margin-top', distance); +}, 10)); + +/** + * Fetch Spells + */ +fetch('./spells.json').then(response => response.json()).then(spells => initSpells(spells)).then(spells => { + store.spells = spells; + store.classes.data = discoverClasses(spells); + view.spell_list.update({ data: applyFilters() }); + view.class_list.update(store.classes); + if (window.location.hash) spellDetails(window.location.hash.substr(1)); +}).catch(reason => console.error('Unable to retrieve spells list:', reason)); diff --git a/dist/view.js b/dist/view.js new file mode 100644 index 0000000..3bc0f21 --- /dev/null +++ b/dist/view.js @@ -0,0 +1,974 @@ + +/** + * @class + */ +function class_list() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('if'); + var child0 = {}; + var child1 = {}; + + // Update functions + this.__update__ = { + data: function (data) { + var result; + result = Monkberry.cond(_this, for0, child0, class_list_if0, data); + Monkberry.cond(_this, for0, child1, class_list_else1, !result); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child1.ref) { + child1.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [for0]; +} +class_list.prototype = Object.create(Monkberry.prototype); +class_list.prototype.constructor = class_list; +class_list.pool = []; +class_list.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function class_list_if0() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('for'); + var children0 = new Monkberry.Map(); + + // Update functions + this.__update__ = { + data: function (data) { + Monkberry.loop(_this, for0, children0, class_list_if0_for0, data, {"value":"cur"}); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + children0.forEach(function (view) { + view.update(__data__); + view.update(view.__state__); + }); + }; + + // Set root nodes + this.nodes = [for0]; +} +class_list_if0.prototype = Object.create(Monkberry.prototype); +class_list_if0.prototype.constructor = class_list_if0; +class_list_if0.pool = []; +class_list_if0.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function class_list_if0_for0() { + Monkberry.call(this); + this.__cache__ = {}; + this.__state__ = {}; + var _this = this; + + // Create elements + var label0 = document.createElement('label'); + var div1 = document.createElement('div'); + var for0 = document.createComment('if'); + var child0 = {}; + var child1 = {}; + var span2 = document.createElement('span'); + var text3 = document.createTextNode(''); + + // Construct dom + span2.appendChild(text3); + span2.setAttribute("class", "mdl-switch__label"); + div1.appendChild(for0); + div1.appendChild(span2); + div1.setAttribute("class", "mdl-switch mdl-js-switch mdl-js-ripple-effect"); + label0.appendChild(div1); + label0.setAttribute("class", "mdl-navigation__link"); + + // Update functions + this.__update__ = { + cur_current: function (cur, current) { + var result; + result = Monkberry.cond(_this, for0, child0, class_list_if0_for0_if0, current.includes(cur)); + Monkberry.cond(_this, for0, child1, class_list_if0_for0_else1, !result); + }, + cur: function (cur) { + text3.textContent = cur; + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child1.ref) { + child1.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [label0]; +} +class_list_if0_for0.prototype = Object.create(Monkberry.prototype); +class_list_if0_for0.prototype.constructor = class_list_if0_for0; +class_list_if0_for0.pool = []; +class_list_if0_for0.prototype.update = function (__data__) { + if (__data__.cur !== undefined && __data__.__index__ !== undefined) { + this.__cache__.cur = __data__.cur; + this.__update__.cur(__data__.cur); + } + if (__data__.current !== undefined) { + this.__cache__.current = __data__.current; + } + if (this.__cache__.cur !== undefined && this.__cache__.current !== undefined) { + this.__update__.cur_current(this.__cache__.cur, this.__cache__.current); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function class_list_if0_for0_if0() { + Monkberry.call(this); + + // Create elements + var input0 = document.createElement('input'); + + // Construct dom + input0.checked = true; + input0.setAttribute("name", "class"); + input0.setAttribute("type", "checkbox"); + input0.setAttribute("class", "mdl-switch__input"); + + // Update functions + this.__update__ = { + cur: function (cur) { + input0.setAttribute("data-action-classtoggle", cur);; + } + }; + + // Set root nodes + this.nodes = [input0]; +} +class_list_if0_for0_if0.prototype = Object.create(Monkberry.prototype); +class_list_if0_for0_if0.prototype.constructor = class_list_if0_for0_if0; +class_list_if0_for0_if0.pool = []; +class_list_if0_for0_if0.prototype.update = function (__data__) { + if (__data__.cur !== undefined) { + this.__update__.cur(__data__.cur); + } +}; + +/** + * @class + */ +function class_list_if0_for0_else1() { + Monkberry.call(this); + + // Create elements + var input0 = document.createElement('input'); + + // Construct dom + input0.setAttribute("name", "class"); + input0.setAttribute("type", "checkbox"); + input0.setAttribute("class", "mdl-switch__input"); + + // Update functions + this.__update__ = { + cur: function (cur) { + input0.setAttribute("data-action-classtoggle", cur);; + } + }; + + // Set root nodes + this.nodes = [input0]; +} +class_list_if0_for0_else1.prototype = Object.create(Monkberry.prototype); +class_list_if0_for0_else1.prototype.constructor = class_list_if0_for0_else1; +class_list_if0_for0_else1.pool = []; +class_list_if0_for0_else1.prototype.update = function (__data__) { + if (__data__.cur !== undefined) { + this.__update__.cur(__data__.cur); + } +}; + +/** + * @class + */ +function class_list_else1() { + Monkberry.call(this); + + // Create elements + var div0 = document.createElement('div'); + + // Construct dom + div0.setAttribute("class", "mdl-spinner mdl-js-spinner is-active"); + + // Set root nodes + this.nodes = [div0]; +} +class_list_else1.prototype = Object.create(Monkberry.prototype); +class_list_else1.prototype.constructor = class_list_else1; +class_list_else1.pool = []; +class_list_else1.prototype.update = function (__data__) { +}; + +window.class_list = class_list; + +/** + * @class + */ +function search_field() { + Monkberry.call(this); + + // Create elements + var input0 = document.createElement('input'); + + // Construct dom + input0.setAttribute("class", "mdl-textfield__input"); + input0.setAttribute("data-action-search", ""); + input0.setAttribute("type", "text"); + input0.setAttribute("name", "sample"); + input0.id = "fixed-header-drawer-exp"; + + // Update functions + this.__update__ = { + data: function (data) { + input0.value = data;; + } + }; + + // Set root nodes + this.nodes = [input0]; +} +search_field.prototype = Object.create(Monkberry.prototype); +search_field.prototype.constructor = search_field; +search_field.pool = []; +search_field.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } +}; + +window.search_field = search_field; + +var __unsafe = function unsafe(root, nodes, html) {var node,j,i = nodes.length,element = document.createElement('div');element.innerHTML = html;while (i-- > 0) {nodes[i].parentNode.removeChild(nodes.pop());}for (i = j = element.childNodes.length - 1; j >= 0; j--) {nodes.push(element.childNodes[j]);}++i;if (root.nodeType == 8) {if (root.parentNode) while (i-- > 0) {root.parentNode.insertBefore(nodes[i], root);} else throw "Can not insert child view into parent node. You need append your view first and then update.";} else while (i-- > 0) {root.appendChild(nodes[i]);}}; + +/** + * @class + */ +function spell_details() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('if'); + var child0 = {}; + var child1 = {}; + + // Update functions + this.__update__ = { + data: function (data) { + var result; + result = Monkberry.cond(_this, for0, child0, spell_details_if0, data.name); + Monkberry.cond(_this, for0, child1, spell_details_else1, !result); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child1.ref) { + child1.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [for0]; +} +spell_details.prototype = Object.create(Monkberry.prototype); +spell_details.prototype.constructor = spell_details; +spell_details.pool = []; +spell_details.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function spell_details_if0() { + Monkberry.call(this); + var _this = this; + + // Create elements + var button0 = document.createElement('button'); + var i1 = document.createElement('i'); + var h52 = document.createElement('h5'); + var text3 = document.createTextNode(''); + var p4 = document.createElement('p'); + var unsafeNodes0 = []; + var div5 = document.createElement('div'); + var ul6 = document.createElement('ul'); + var li7 = document.createElement('li'); + var strong8 = document.createElement('strong'); + var text9 = document.createTextNode(''); + var li10 = document.createElement('li'); + var strong11 = document.createElement('strong'); + var text12 = document.createTextNode(''); + var li13 = document.createElement('li'); + var strong14 = document.createElement('strong'); + var text15 = document.createTextNode(''); + var for0 = document.createComment('if'); + var child0 = {}; + var ul16 = document.createElement('ul'); + var li17 = document.createElement('li'); + var strong18 = document.createElement('strong'); + var text19 = document.createTextNode(''); + var li20 = document.createElement('li'); + var strong21 = document.createElement('strong'); + var text22 = document.createTextNode('Cantrip'); + var li23 = document.createElement('li'); + var strong24 = document.createElement('strong'); + var span25 = document.createElement('span'); + var text26 = document.createTextNode(''); + var for1 = document.createComment('if'); + var child2 = {}; + var div27 = document.createElement('div'); + var label28 = document.createElement('label'); + var i29 = document.createElement('i'); + var input30 = document.createElement('input'); + + // Construct dom + i1.appendChild(document.createTextNode("close")); + i1.setAttribute("class", "material-icons"); + button0.appendChild(i1); + button0.setAttribute("data-action-details", ""); + button0.setAttribute("class", "mdl-button mdl-js-button mdl-button--fab mdl-button--mini-fab"); + h52.appendChild(text3); + h52.setAttribute("class", "mdl-typography--display-1 mdl-color-text--teal-600"); + p4.setAttribute("class", "description"); + strong8.appendChild(document.createTextNode("Range: ")); + li7.appendChild(strong8); + li7.appendChild(text9); + strong11.appendChild(document.createTextNode("Casting Time: ")); + li10.appendChild(strong11); + li10.appendChild(text12); + strong14.appendChild(document.createTextNode("Duration: ")); + li13.appendChild(strong14); + li13.appendChild(text15); + ul6.appendChild(li7); + ul6.appendChild(li10); + ul6.appendChild(li13); + ul6.appendChild(for0); + ul6.setAttribute("class", "right mdl-cell mdl-cell--6-col"); + strong18.appendChild(document.createTextNode("School: ")); + li17.appendChild(strong18); + li17.appendChild(text19); + strong21.appendChild(document.createTextNode("Spell Level: ")); + li20.appendChild(strong21); + li20.appendChild(text22); + strong24.appendChild(document.createTextNode("Class: ")); + span25.appendChild(text26); + span25.setAttribute("class", "classes"); + li23.appendChild(strong24); + li23.appendChild(span25); + ul16.appendChild(li17); + ul16.appendChild(li20); + ul16.appendChild(li23); + ul16.appendChild(for1); + ul16.setAttribute("class", "left mdl-cell mdl-cell--6-col"); + i29.appendChild(document.createTextNode("content_copy")); + i29.setAttribute("class", "material-icons"); + label28.appendChild(i29); + label28.setAttribute("class", "mdl-button mdl-js-button mdl-button--icon copy-to-clipboard"); + label28.setAttribute("for", "share-url"); + label28.setAttribute("data-clipboard-target", "#share-url"); + input30.setAttribute("readonly", ""); + input30.setAttribute("class", "mdl-textfield__input"); + input30.setAttribute("type", "text"); + input30.id = "share-url"; + div27.appendChild(label28); + div27.appendChild(input30); + div27.setAttribute("class", "mdl-textfield mdl-js-textfield"); + div5.appendChild(ul6); + div5.appendChild(ul16); + div5.appendChild(div27); + div5.setAttribute("class", "mdl-grid"); + + // Update functions + this.__update__ = { + data: function (data) { + text3.textContent = data.name; + __unsafe(p4, unsafeNodes0, data.description); + text9.textContent = data.range; + text12.textContent = data.casting_time; + text15.textContent = data.duration; + Monkberry.cond(_this, for0, child0, spell_details_if0_if0, data.ritual); + text19.textContent = data.school; + text22.textContent = (data.level) || ('Cantrip'); + text26.textContent = data.classes.join(', '); + Monkberry.cond(_this, for1, child2, spell_details_if0_if2, (data.components) && (data.components.raw)); + }, + url: function (url) { + input30.value = url;; + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child2.ref) { + child2.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [button0, h52, p4, div5]; +} +spell_details_if0.prototype = Object.create(Monkberry.prototype); +spell_details_if0.prototype.constructor = spell_details_if0; +spell_details_if0.pool = []; +spell_details_if0.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + if (__data__.url !== undefined) { + this.__update__.url(__data__.url); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function spell_details_if0_if0() { + Monkberry.call(this); + + // Create elements + var li0 = document.createElement('li'); + + // Construct dom + li0.appendChild(document.createTextNode("Ritual")); + + // Set root nodes + this.nodes = [li0]; +} +spell_details_if0_if0.prototype = Object.create(Monkberry.prototype); +spell_details_if0_if0.prototype.constructor = spell_details_if0_if0; +spell_details_if0_if0.pool = []; +spell_details_if0_if0.prototype.update = function (__data__) { +}; + +/** + * @class + */ +function spell_details_if0_if2() { + Monkberry.call(this); + + // Create elements + var li0 = document.createElement('li'); + var strong1 = document.createElement('strong'); + var text2 = document.createTextNode(''); + + // Construct dom + strong1.appendChild(document.createTextNode("Components: ")); + li0.appendChild(strong1); + li0.appendChild(text2); + + // Update functions + this.__update__ = { + data: function (data) { + text2.textContent = data.components.raw; + } + }; + + // Set root nodes + this.nodes = [li0]; +} +spell_details_if0_if2.prototype = Object.create(Monkberry.prototype); +spell_details_if0_if2.prototype.constructor = spell_details_if0_if2; +spell_details_if0_if2.pool = []; +spell_details_if0_if2.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } +}; + +/** + * @class + */ +function spell_details_else1() { + Monkberry.call(this); + + // Create elements + var div0 = document.createElement('div'); + var h61 = document.createElement('h6'); + + // Construct dom + h61.appendChild(document.createTextNode(" Choose a Spell ")); + h61.setAttribute("class", "mdl-typography--title"); + div0.appendChild(h61); + div0.id = "empty"; + + // Set root nodes + this.nodes = [div0]; +} +spell_details_else1.prototype = Object.create(Monkberry.prototype); +spell_details_else1.prototype.constructor = spell_details_else1; +spell_details_else1.pool = []; +spell_details_else1.prototype.update = function (__data__) { +}; + +window.spell_details = spell_details; + +/** + * @class + */ +function spell_list() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('if'); + var child0 = {}; + var child1 = {}; + + // Update functions + this.__update__ = { + data: function (data) { + var result; + result = Monkberry.cond(_this, for0, child0, spell_list_if0, data.length); + Monkberry.cond(_this, for0, child1, spell_list_else1, !result); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child1.ref) { + child1.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [for0]; +} +spell_list.prototype = Object.create(Monkberry.prototype); +spell_list.prototype.constructor = spell_list; +spell_list.pool = []; +spell_list.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function spell_list_if0() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('for'); + var children0 = new Monkberry.Map(); + + // Update functions + this.__update__ = { + data: function (data) { + Monkberry.loop(_this, for0, children0, spell_list_if0_for0, data, {"value":"spell"}); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + children0.forEach(function (view) { + view.update(__data__); + view.update(view.__state__); + }); + }; + + // Set root nodes + this.nodes = [for0]; +} +spell_list_if0.prototype = Object.create(Monkberry.prototype); +spell_list_if0.prototype.constructor = spell_list_if0; +spell_list_if0.pool = []; +spell_list_if0.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function spell_list_if0_for0() { + Monkberry.call(this); + this.__state__ = {}; + + // Create elements + var tr0 = document.createElement('tr'); + var td1 = document.createElement('td'); + var strong2 = document.createElement('strong'); + var text3 = document.createTextNode(''); + var td4 = document.createElement('td'); + var text5 = document.createTextNode(''); + var td6 = document.createElement('td'); + var text7 = document.createTextNode(''); + + // Construct dom + strong2.appendChild(text3); + td1.appendChild(strong2); + td1.setAttribute("class", "spell-name mdl-data-table__cell--non-numeric"); + td4.appendChild(text5); + td4.setAttribute("class", "spell-school mdl-data-table__cell--non-numeric"); + td6.appendChild(text7); + td6.setAttribute("class", "spell-level"); + tr0.appendChild(td1); + tr0.appendChild(td4); + tr0.appendChild(td6); + + // Update functions + this.__update__ = { + spell: function (spell) { + text3.textContent = spell.name; + text5.textContent = spell.school; + text7.textContent = spell.prettyLevel; + tr0.setAttribute("data-action-details", spell.name);; + } + }; + + // Set root nodes + this.nodes = [tr0]; +} +spell_list_if0_for0.prototype = Object.create(Monkberry.prototype); +spell_list_if0_for0.prototype.constructor = spell_list_if0_for0; +spell_list_if0_for0.pool = []; +spell_list_if0_for0.prototype.update = function (__data__) { + if (__data__.spell !== undefined && __data__.__index__ !== undefined) { + this.__update__.spell(__data__.spell); + } +}; + +/** + * @class + */ +function spell_list_else1() { + Monkberry.call(this); + var _this = this; + + // Create elements + var tr0 = document.createElement('tr'); + var td1 = document.createElement('td'); + var child0 = {}; + var child1 = {}; + + // Construct dom + td1.setAttribute("colspan", "4"); + tr0.appendChild(td1); + tr0.setAttribute("class", "do-nothing"); + + // Update functions + this.__update__ = { + data: function (data) { + var result; + result = Monkberry.cond(_this, td1, child0, spell_list_else1_if0, data); + Monkberry.cond(_this, td1, child1, spell_list_else1_else1, !result); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child1.ref) { + child1.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [tr0]; +} +spell_list_else1.prototype = Object.create(Monkberry.prototype); +spell_list_else1.prototype.constructor = spell_list_else1; +spell_list_else1.pool = []; +spell_list_else1.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function spell_list_else1_if0() { + Monkberry.call(this); + + // Create elements + var div0 = document.createElement('div'); + var i1 = document.createElement('i'); + var h52 = document.createElement('h5'); + var h63 = document.createElement('h6'); + + // Construct dom + i1.appendChild(document.createTextNode(" warning ")); + i1.setAttribute("class", "material-icons mdl-list__item-icon mdl-color-text--orange-600"); + h52.appendChild(document.createTextNode("No Results")); + h63.appendChild(document.createTextNode("Try refining your filters in the sidebar.")); + div0.appendChild(i1); + div0.appendChild(h52); + div0.appendChild(h63); + div0.setAttribute("class", "text-center"); + + // Set root nodes + this.nodes = [div0]; +} +spell_list_else1_if0.prototype = Object.create(Monkberry.prototype); +spell_list_else1_if0.prototype.constructor = spell_list_else1_if0; +spell_list_else1_if0.pool = []; +spell_list_else1_if0.prototype.update = function (__data__) { +}; + +/** + * @class + */ +function spell_list_else1_else1() { + Monkberry.call(this); + + // Create elements + var div0 = document.createElement('div'); + + // Construct dom + div0.setAttribute("class", "mdl-spinner mdl-js-spinner is-active"); + + // Set root nodes + this.nodes = [div0]; +} +spell_list_else1_else1.prototype = Object.create(Monkberry.prototype); +spell_list_else1_else1.prototype.constructor = spell_list_else1_else1; +spell_list_else1_else1.pool = []; +spell_list_else1_else1.prototype.update = function (__data__) { +}; + +window.spell_list = spell_list; + +/** + * @class + */ +function table_sort() { + Monkberry.call(this); + var _this = this; + + // Create elements + var for0 = document.createComment('for'); + var children0 = new Monkberry.Map(); + + // Update functions + this.__update__ = { + data: function (data) { + Monkberry.loop(_this, for0, children0, table_sort_for0, data, {"value":"name"}); + } + }; + + // On update actions + this.onUpdate = function (__data__) { + children0.forEach(function (view) { + view.update(__data__); + view.update(view.__state__); + }); + }; + + // Set root nodes + this.nodes = [for0]; +} +table_sort.prototype = Object.create(Monkberry.prototype); +table_sort.prototype.constructor = table_sort; +table_sort.pool = []; +table_sort.prototype.update = function (__data__) { + if (__data__.data !== undefined) { + this.__update__.data(__data__.data); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function table_sort_for0() { + Monkberry.call(this); + this.__cache__ = {}; + this.__state__ = {}; + var _this = this; + + // Create elements + var th0 = document.createElement('th'); + var i1 = document.createElement('i'); + var for0 = document.createComment('if'); + var child0 = {}; + var for1 = document.createComment('if'); + var child2 = {}; + var for2 = document.createComment('if'); + var child4 = {}; + var span2 = document.createElement('span'); + var text3 = document.createTextNode(''); + var i4 = document.createElement('i'); + var text5 = document.createTextNode(''); + + // Construct dom + i1.appendChild(for0); + i1.appendChild(for1); + i1.appendChild(for2); + i1.setAttribute("class", "material-icons mdl-list__item-icon"); + span2.appendChild(document.createTextNode(" ")); + span2.appendChild(text3); + i4.appendChild(text5); + i4.setAttribute("class", "material-icons mdl-color-text--teal-600 mdl-list__item-icon "); + th0.appendChild(i1); + th0.appendChild(span2); + th0.appendChild(i4); + th0.setAttribute("class", "mdl-data-table__cell--non-numeric "); + + // Update functions + this.__update__ = { + name: function (name) { + Monkberry.cond(_this, for0, child0, table_sort_for0_if0, (name) === ('level')); + Monkberry.cond(_this, for1, child2, table_sort_for0_if2, (name) === ('name')); + Monkberry.cond(_this, for2, child4, table_sort_for0_if4, (name) === ('school')); + text3.textContent = name; + th0.setAttribute("data-action-sort", name);; + }, + rev: function (rev) { + text5.textContent = (rev) ? 'keyboard_arrow_up' : 'keyboard_arrow_down'; + }, + current_name: function (current, name) { + i4.setAttribute("class", ("material-icons mdl-color-text--teal-600 mdl-list__item-icon ") + (((name) === (current)) ? 'mdl-color-text--teal-600' : 'mdl-color-text--grey-300'));; + }, + current: function (current) { + th0.setAttribute("class", ("mdl-data-table__cell--non-numeric ") + (((current) === ('ranking')) ? 'mdl-color-text--grey-200 do-nothing' : ''));; + } + }; + + // On update actions + this.onUpdate = function (__data__) { + if (child0.ref) { + child0.ref.update(__data__); + } + if (child2.ref) { + child2.ref.update(__data__); + } + if (child4.ref) { + child4.ref.update(__data__); + } + }; + + // Set root nodes + this.nodes = [th0]; +} +table_sort_for0.prototype = Object.create(Monkberry.prototype); +table_sort_for0.prototype.constructor = table_sort_for0; +table_sort_for0.pool = []; +table_sort_for0.prototype.update = function (__data__) { + if (__data__.name !== undefined && __data__.__index__ !== undefined) { + this.__cache__.name = __data__.name; + this.__update__.name(__data__.name); + } + if (__data__.rev !== undefined) { + this.__update__.rev(__data__.rev); + } + if (__data__.current !== undefined) { + this.__cache__.current = __data__.current; + this.__update__.current(__data__.current); + } + if (this.__cache__.current !== undefined && this.__cache__.name !== undefined) { + this.__update__.current_name(this.__cache__.current, this.__cache__.name); + } + this.onUpdate(__data__); +}; + +/** + * @class + */ +function table_sort_for0_if0() { + Monkberry.call(this); + + // Set root nodes + this.nodes = [document.createTextNode(" exposure ")]; +} +table_sort_for0_if0.prototype = Object.create(Monkberry.prototype); +table_sort_for0_if0.prototype.constructor = table_sort_for0_if0; +table_sort_for0_if0.pool = []; +table_sort_for0_if0.prototype.update = function (__data__) { +}; + +/** + * @class + */ +function table_sort_for0_if2() { + Monkberry.call(this); + + // Set root nodes + this.nodes = [document.createTextNode(" flash_on ")]; +} +table_sort_for0_if2.prototype = Object.create(Monkberry.prototype); +table_sort_for0_if2.prototype.constructor = table_sort_for0_if2; +table_sort_for0_if2.pool = []; +table_sort_for0_if2.prototype.update = function (__data__) { +}; + +/** + * @class + */ +function table_sort_for0_if4() { + Monkberry.call(this); + + // Set root nodes + this.nodes = [document.createTextNode(" school ")]; +} +table_sort_for0_if4.prototype = Object.create(Monkberry.prototype); +table_sort_for0_if4.prototype.constructor = table_sort_for0_if4; +table_sort_for0_if4.pool = []; +table_sort_for0_if4.prototype.update = function (__data__) { +}; + +window.table_sort = table_sort; +//# sourceMappingURL=view.js.map diff --git a/dist/view.js.map b/dist/view.js.map new file mode 100644 index 0000000..0870251 --- /dev/null +++ b/dist/view.js.map @@ -0,0 +1 @@ +{"version":3,"sources":["class-list.monk","search-field.monk","spell-details.monk","spell-list.monk","table-sort.monk"],"names":[],"mappings":"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","file":"view.js"} \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..b716fca --- /dev/null +++ b/index.html @@ -0,0 +1,81 @@ + + + + My Spells 5e + + + + + + + + +
+
+ +
+ +
+
+
+ + + + + +
+
+
+
+
+ +
+
+ +
+
+ + + + + + + + + diff --git a/package.json b/package.json index ccf7ef4..72363df 100644 --- a/package.json +++ b/package.json @@ -3,11 +3,26 @@ "version": "1.0.0", "description": "5e Spells by Class", "main": "", + "watch": { + "views": "views/*.monk" + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "views": "monkberry src/views/*.monk --source-map --output dist/view.js", + "js": "babel src/app.js --out-file dist/app.js", + "develop": "nodemon --watch src -e monk,js --exec 'npm run views && npm run js'" }, "author": "Joe Wroten ", "license": "ISC", - "devDependencies": {}, - "dependencies": {} + "devDependencies": { + "babel-cli": "^6.11.4", + "babel-plugin-transform-runtime": "^6.12.0", + "babel-preset-es2017": "^1.6.1", + "nodemon": "^1.10.0", + "npm-watch": "^0.1.5" + }, + "dependencies": { + "clipboard": "^1.5.12", + "jquery": "^3.1.0", + "monkberry": "^4.0.7" + } } diff --git a/public/README.md b/public/README.md deleted file mode 100644 index e69de29..0000000 diff --git a/public/app.css b/public/app.css deleted file mode 100644 index 72ae94b..0000000 --- a/public/app.css +++ /dev/null @@ -1,73 +0,0 @@ -.has-tooltip { - cursor: help; -} - -.mdl-dialog.card { - width: 500px; - padding: 0; - background: transparent; -} - -.mdl-dialog .mdl-card { - overflow: visible; - width: 100%; -} - -/*.mdl-dialog [data-id-spelldetail=description] {*/ - /*max-height: 3.5em;*/ - /*overflow-y: scroll;*/ -/*}*/ - -#spell-list [data-action-dialog] { - cursor: pointer; -} - -#class-list label span { - text-transform: capitalize; -} - -.left { - float: left; -} - -.left.clear { - clear: left; -} - -.right { - float: right; -} - -.right.clear { - clear: right; -} - -.clear { - clear: both; -} - -.text-right { - text-align: right; -} - -.text-left { - text-align: left; -} - -.text-center { - text-align: center; -} - -[data-id-spelldetail="components"] { - list-style-type: none; - margin: 0; - padding: 0; -} - -[data-id-spelldetail="components"] li:not(:last-of-type) { - padding-right: .5em; -} - -[data-id-spelldetail="components"] li:not(:last-of-type)::after { - content: ',' -} diff --git a/public/app.js b/public/app.js deleted file mode 100644 index 28ac9d5..0000000 --- a/public/app.js +++ /dev/null @@ -1,283 +0,0 @@ -/** - * Capitalize Helper - **/ -let capitalize = str => str ? str.charAt(0).toUpperCase() + str.slice(1) : ''; - -/** - * Tooltip Counter - */ -let tooltipCount = 1; -let tooltipCounter = () => tooltipCount++; - -/** - * Discover Classes - */ -const DISCOVERCLASSES = spells => { - let classes = []; - spells.forEach(spell => { - if (!spell.classes) return; - spell.classes.forEach(current => { - if (!classes.includes(current)) classes.push(current); - }); - }); - classes = classes.sort((a, b) => a > b); - return {spells, classes} -}; - -/** - * Dom Helper - */ -const DOMHELPER = { - emphasis (str) { - let keywords = ['intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check']; - keywords.forEach(word => { - let r = new RegExp(` ${word} `, 'gi'); - str = str.replace(r, o => `${o}`); - }); - - str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `${o}`); - return str; - }, - level (lvl) { - let cantrip = lvl.toLowerCase() === 'cantrip'; - let invalid = isNaN(lvl) && !cantrip; - if (invalid) return '?'; - else return cantrip ? 'C' : lvl; - }, - components (components) { - let dom = ''; - let types = ['verbal', 'somatic', 'material']; - let typesDescriptions = [ - 'Spoken word/prayer. Mouth must be free and not mute.', - 'Physical gestures, usually hand movements. Must not be bound.', - 'Items consumed upon casting spell.' - ]; - - types.forEach((type, i) => { - if (!!components[type]) { - let currentTooltip = tooltipCounter(); - dom += ` -
  • ${types[i].charAt(0).toUpperCase()}
  • - - `; - } - }); - return dom; - }, - classes (classes) { - let dom = ''; - classes.forEach(x => { - dom += `${capitalize(x)} `; - }); - return dom; - } -}; - -/** - * List of Selected Spells - */ -const SELECTEDSPELLS = () => $('#form-spells').serializeArray().map(spell => parseInt(spell.value)); - -/** - * Add Spells to Page - */ -const RENDERSPELLS = (x) => { - let dom = ''; - let spells = x ? x.spells.slice(0) : data; - spells = CLASSSPELLS(spells); - spells = SEARCHSPELLS(spells); - spells = spells.sort((a, b) => b.ranking - a.ranking); - spells.forEach(spell => { - let el = $('#spell-item').html(); - let $el = $(el); - - $('[data-id-spellitem=level]', $el)[0].innerHTML - = DOMHELPER.level(spell.level); - $('[data-id-spellitem=name]', $el)[0].innerHTML - = spell.name; - $('[data-id-spellitem=school]', $el)[0].innerHTML - = capitalize(spell.school); - $('[data-id-spellitem=checkbox]', $el).attr('value', spell.id).attr('id', `sel-${spell.id}`); - $('[data-id-spellitem=checklabel]', $el).attr('for', `sel-${spell.id}`); - $el.attr('data-action-dialog', spell.id); - - dom += $el.prop('outerHTML'); - }); - $('#spell-list').html(dom); - componentHandler.upgradeDom(); - return data; -}; - -/** - * Render Spell Details to Dialog - */ -const RENDERSPELLDIALOG = spell => { - let el = $('#spell-detail').html(); - let $el = $(`
    ${el}
    `); - - $('[data-id-spelldetail=name]', $el)[0].innerHTML - = spell.name; - $('[data-action-select]', $el).attr('data-action-select', spell.id); - $('[data-action-select] input[type="checkbox"]', $el).attr('checked', SELECTEDSPELLS().includes(spell.id)); - if (spell.description) { - $('[data-id-spelldetail=description]', $el)[0].innerHTML - = DOMHELPER.emphasis(spell.description); - } - if (spell.tags) { - $('[data-id-spelldetail=classes]', $el)[0].innerHTML - = DOMHELPER.classes(spell.classes); - } - if (spell.components) { - $('[data-id-spelldetail=components]', $el)[0].innerHTML - = DOMHELPER.components(spell.components); - } - if (spell.components.materials_needed) { - $('[data-id-spelldetail=materials]', $el)[0].innerHTML - = spell.components.materials_needed.join(', '); - } - if (spell.level) { - $('[data-id-spelldetail=level]', $el)[0].innerHTML - = 'Level ' + spell.level; - } - if (spell.duration) { - $('[data-id-spelldetail=duration]', $el)[0].innerHTML - = spell.duration; - } - if (spell.casting_time) { - $('[data-id-spelldetail=castingtime]', $el)[0].innerHTML - = spell.casting_time; - } - if (spell.range) { - $('[data-id-spelldetail=range]', $el)[0].innerHTML - = spell.range; - } - - $('#spell-detail-container').html($el.prop('outerHTML')); - componentHandler.upgradeDom(); -}; - - -/** - * Fetch Classes from Spells - */ -const RENDERCLASSES = data => { - let dom = ''; - data.classes.forEach(item => { - let el = $('#class-toggle').html(); - let $el = $(el); - - $('[data-action-classtoggle]', $el).attr('value', item); - $('[data-id-classtoggle=name]', $el)[0].innerHTML = item; - dom += $el.prop('outerHTML'); - }); - $('#class-list').html(dom); - - return data; -}; - -const SEARCHSPELLS = (spells, search = $('[data-action-search]').val()) => { - search = search.toLowerCase(); - if (search.length >= 3) { - spells = spells.filter(spell => { - let regFind = new RegExp(search, 'gi'); - let spellName = spell.name.toLowerCase(); - let spellDescription = spell.description ? spell.description.toLowerCase() : ''; - let spellMaterials = spell.components && spell.components.materials_needed || []; - - let matches = 0 - + spellName.indexOf(search) > -1 ? 20 : 0 - + (spellDescription.match(regFind) || []).length - + (spellMaterials).filter(com => com.indexOf(search) > -1).length * 10; - - spell.ranking = matches; - return spell.ranking > 0; - }); - } - return spells; -}; - -const CLASSSPELLS = (spells, classes) => { - classes = classes || $('#class-list').serializeArray().map(x => x.value); - return classes.length ? spells.filter(spell => { - for(let i = 0; i < classes.length; i++) { - if (spell.classes.indexOf(classes[i]) >= 0) return true; - } - }) : spells; -}; - -/** - * Retrieve Spells - */ -let data; - -fetch('./spells.json') - .then(response => response.json()) - .then(spells => spells.map((spell, i) => { - spell.id = i; - spell.ranking = 0; // Default to equal ranking - return spell; - })) - .then(spells => data = spells) - .then(spells => DISCOVERCLASSES(spells)) - .then(data => RENDERCLASSES(data)) - .then(data => RENDERSPELLS(data)) - .catch(reason => console.error('Unable to retrieve spells list:', reason)); - -/** - * Details Popup - */ -let $dialog = $('dialog'); - -if (!$dialog[0].showModal) { - dialogPolyfill.registerDialog($dialog); -} - -$('#spell-list').on('click', '[data-action-dialog]', e => { - let id = $(e.target).closest('tr').data('action-dialog'); - RENDERSPELLDIALOG(data[id]); - $dialog[0].showModal(); -}).on('click', '.dontprop', e => { - e.stopPropagation(); -}); - -$dialog.on('click', '[data-action-close]', () => { - $dialog[0].close(); -}); - -/** - * Bind Checkbox Selection Action - */ -$('body').on('change', '[data-action-select] input', e => { - let id = $(e.target).closest('label').attr('data-action-select'); - if (id) $('#sel-' + id).click(); -}); - -/** - * Bind Toggle All Checkbox - */ -$('label[for=table-header]').on('change', 'input[type="checkbox"]', e => { - $(e.target).closest('form').find('[data-id-spellitem=checkbox]').each(function() { - this.checked = e.target.checked; - if(this.checked) $(this).closest('label').addClass('is-checked'); - else $(this).closest('label').removeClass('is-checked'); - }) -}); - -/** - * Bind Class Filter Switches - */ -$('body').on('change', '[data-action-classtoggle]', e => { - RENDERSPELLS(); -}); - -/** - * Search - */ -$('body').on('change keyup cut paste', '[data-action-search]', e => { - setTimeout(function() { - RENDERSPELLS(); - }, 0); -}); diff --git a/public/index.html b/public/index.html deleted file mode 100644 index 63c9446..0000000 --- a/public/index.html +++ /dev/null @@ -1,134 +0,0 @@ - - - - My Spells 5e - - - - - - - - -
    -
    -
    -
    -
    - -
    - -
    -
    -
    -
    - -
    -
    -
    - - - - - - - - - - - - - - -
    - - LevelNameSchool
    -
    -
    -
    -
    -
    -
    - - -
    -
    - - - - - - - - - - - - - - - diff --git a/public/redux.min.js b/public/redux.min.js deleted file mode 100644 index 883f76c..0000000 --- a/public/redux.min.js +++ /dev/null @@ -1 +0,0 @@ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Redux=e():t.Redux=e()}(this,function(){return function(t){function e(r){if(n[r])return n[r].exports;var o=n[r]={exports:{},id:r,loaded:!1};return t[r].call(o.exports,o,o.exports,e),o.loaded=!0,o.exports}var n={};return e.m=t,e.c=n,e.p="",e(0)}([function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}e.__esModule=!0,e.compose=e.applyMiddleware=e.bindActionCreators=e.combineReducers=e.createStore=void 0;var o=n(2),i=r(o),u=n(7),c=r(u),f=n(6),a=r(f),s=n(5),d=r(s),l=n(1),p=r(l),y=n(3);r(y);e.createStore=i["default"],e.combineReducers=c["default"],e.bindActionCreators=a["default"],e.applyMiddleware=d["default"],e.compose=p["default"]},function(t,e){"use strict";function n(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];if(0===e.length)return function(t){return t};var r=function(){var t=e[e.length-1],n=e.slice(0,-1);return{v:function(){return n.reduceRight(function(t,e){return e(t)},t.apply(void 0,arguments))}}}();return"object"==typeof r?r.v:void 0}e.__esModule=!0,e["default"]=n},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e,n){function r(){b===h&&(b=h.slice())}function i(){return v}function c(t){if("function"!=typeof t)throw Error("Expected listener to be a function.");var e=!0;return r(),b.push(t),function(){if(e){e=!1,r();var n=b.indexOf(t);b.splice(n,1)}}}function s(t){if(!(0,u["default"])(t))throw Error("Actions must be plain objects. Use custom middleware for async actions.");if(void 0===t.type)throw Error('Actions may not have an undefined "type" property. Have you misspelled a constant?');if(x)throw Error("Reducers may not dispatch actions.");try{x=!0,v=y(v,t)}finally{x=!1}for(var e=h=b,n=0;e.length>n;n++)e[n]();return t}function d(t){if("function"!=typeof t)throw Error("Expected the nextReducer to be a function.");y=t,s({type:a.INIT})}function l(){var t,e=c;return t={subscribe:function(t){function n(){t.next&&t.next(i())}if("object"!=typeof t)throw new TypeError("Expected the observer to be an object.");n();var r=e(n);return{unsubscribe:r}}},t[f["default"]]=function(){return this},t}var p;if("function"==typeof e&&void 0===n&&(n=e,e=void 0),void 0!==n){if("function"!=typeof n)throw Error("Expected the enhancer to be a function.");return n(o)(t,e)}if("function"!=typeof t)throw Error("Expected the reducer to be a function.");var y=t,v=e,h=[],b=h,x=!1;return s({type:a.INIT}),p={dispatch:s,subscribe:c,getState:i,replaceReducer:d},p[f["default"]]=l,p}e.__esModule=!0,e.ActionTypes=void 0,e["default"]=o;var i=n(4),u=r(i),c=n(11),f=r(c),a=e.ActionTypes={INIT:"@@redux/INIT"}},function(t,e){"use strict";function n(t){"undefined"!=typeof console&&"function"==typeof console.error&&console.error(t);try{throw Error(t)}catch(e){}}e.__esModule=!0,e["default"]=n},function(t,e,n){function r(t){if(!u(t)||l.call(t)!=c||i(t))return!1;var e=o(t);if(null===e)return!0;var n=s.call(e,"constructor")&&e.constructor;return"function"==typeof n&&n instanceof n&&a.call(n)==d}var o=n(8),i=n(9),u=n(10),c="[object Object]",f=Object.prototype,a=Function.prototype.toString,s=f.hasOwnProperty,d=a.call(Object),l=f.toString;t.exports=r},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(){for(var t=arguments.length,e=Array(t),n=0;t>n;n++)e[n]=arguments[n];return function(t){return function(n,r,o){var u=t(n,r,o),f=u.dispatch,a=[],s={getState:u.getState,dispatch:function(t){return f(t)}};return a=e.map(function(t){return t(s)}),f=c["default"].apply(void 0,a)(u.dispatch),i({},u,{dispatch:f})}}}e.__esModule=!0;var i=Object.assign||function(t){for(var e=1;arguments.length>e;e++){var n=arguments[e];for(var r in n)Object.prototype.hasOwnProperty.call(n,r)&&(t[r]=n[r])}return t};e["default"]=o;var u=n(1),c=r(u)},function(t,e){"use strict";function n(t,e){return function(){return e(t.apply(void 0,arguments))}}function r(t,e){if("function"==typeof t)return n(t,e);if("object"!=typeof t||null===t)throw Error("bindActionCreators expected an object or a function, instead received "+(null===t?"null":typeof t)+'. Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?');for(var r=Object.keys(t),o={},i=0;r.length>i;i++){var u=r[i],c=t[u];"function"==typeof c&&(o[u]=n(c,e))}return o}e.__esModule=!0,e["default"]=r},function(t,e,n){"use strict";function r(t){return t&&t.__esModule?t:{"default":t}}function o(t,e){var n=e&&e.type,r=n&&'"'+n+'"'||"an action";return"Given action "+r+', reducer "'+t+'" returned undefined. To ignore an action, you must explicitly return the previous state.'}function i(t){Object.keys(t).forEach(function(e){var n=t[e],r=n(void 0,{type:c.ActionTypes.INIT});if(void 0===r)throw Error('Reducer "'+e+'" returned undefined during initialization. If the state passed to the reducer is undefined, you must explicitly return the initial state. The initial state may not be undefined.');var o="@@redux/PROBE_UNKNOWN_ACTION_"+Math.random().toString(36).substring(7).split("").join(".");if(void 0===n(void 0,{type:o}))throw Error('Reducer "'+e+'" returned undefined when probed with a random type. '+("Don't try to handle "+c.ActionTypes.INIT+' or other actions in "redux/*" ')+"namespace. They are considered private. Instead, you must return the current state for any unknown actions, unless it is undefined, in which case you must return the initial state, regardless of the action type. The initial state may not be undefined.")})}function u(t){for(var e=Object.keys(t),n={},r=0;e.length>r;r++){var u=e[r];"function"==typeof t[u]&&(n[u]=t[u])}var c,f=Object.keys(n);try{i(n)}catch(a){c=a}return function(){var t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},e=arguments[1];if(c)throw c;for(var r=!1,i={},u=0;f.length>u;u++){var a=f[u],s=n[a],d=t[a],l=s(d,e);if(void 0===l){var p=o(a,e);throw Error(p)}i[a]=l,r=r||l!==d}return r?i:t}}e.__esModule=!0,e["default"]=u;var c=n(2),f=n(4),a=(r(f),n(3));r(a)},function(t,e){function n(t){return r(Object(t))}var r=Object.getPrototypeOf;t.exports=n},function(t,e){function n(t){var e=!1;if(null!=t&&"function"!=typeof t.toString)try{e=!!(t+"")}catch(n){}return e}t.exports=n},function(t,e){function n(t){return!!t&&"object"==typeof t}t.exports=n},function(t,e,n){(function(e){"use strict";t.exports=n(12)(e||window||this)}).call(e,function(){return this}())},function(t,e){"use strict";t.exports=function(t){var e,n=t.Symbol;return"function"==typeof n?n.observable?e=n.observable:(e="function"==typeof n["for"]?n["for"]("observable"):n("observable"),n.observable=e):e="@@observable",e}}])}); \ No newline at end of file diff --git a/public/spellschanger.js b/public/spellschanger.js deleted file mode 100644 index 6724a31..0000000 --- a/public/spellschanger.js +++ /dev/null @@ -1,18 +0,0 @@ -'use strict'; - -let newSpells = []; -let spells = require('./spells.json'); -//spells.forEach(spell => { - //if (spell.tags) { - //spell.classes = []; - //spell.tags.forEach(tag => { - //let classes = ['cleric', 'fighter', 'rogue', 'wizard', 'barbarian', 'bard', 'druid', 'monk', 'paladin', 'ranger', 'sorcerer', 'warlock']; - //classes.forEach(x => { - //if (tag.indexOf(x) > -1) spell.classes.push(x); - //}); - //}); - //} - //newSpells.push(spell); -//}); - -require('fs').writeFileSync('./newSpells.json', JSON.stringify(newSpells)); diff --git a/public/spells.json b/spells.json similarity index 100% rename from public/spells.json rename to spells.json diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000..d2672f6 Binary files /dev/null and b/src/.DS_Store differ diff --git a/src/app.js b/src/app.js new file mode 100644 index 0000000..00a8354 --- /dev/null +++ b/src/app.js @@ -0,0 +1,293 @@ +/** + * Misc helper functions + */ +Object.values = x => + Object.keys(x).reduce((y, z) => + y.push(x[z]) && y, []); +const debounce = (func, wait, immediate) => { + let timeout; + return function() { + let context = this, args = arguments; + let later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + let callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) func.apply(context, args); + }; +}; +const el = id => $(`[data-template=${id}]`)[0] || console.error('Unable to render to', id); +const clone = obj => JSON.parse(JSON.stringify(obj)); + +/** + * Global store and view holders + */ +let store = {}; +let view = {}; + +/** + * Init Local Storage + */ +const localStorageDefault = (key, val) => { + if (localStorage.getItem(key) === null) localStorage.setItem(key, val); +}; +let defaults = { + tableSortName: 'name', + tableSortRev: false, + classes: [], + search: '' +}; +for (let cur in defaults) localStorageDefault(cur, defaults[cur]); + +/** + * Render Table Sort + */ +store.tableSort = { + data: ['name', 'school', 'level'], + current: localStorage.getItem('tableSortName'), + rev: localStorage.getItem('tableSortRev') !== 'false' +}; +view.table_sort = Monkberry.render(table_sort, el('table-sort')); +view.table_sort.update(store.tableSort); + +/** + * Render Spell List + */ +view.spell_list = Monkberry.render(spell_list, el('spell-list')); +view.spell_list.update({}); + +/** + * Render Spell Details + */ +view.spell_details = Monkberry.render(spell_details, el('spell-details')); +view.spell_details.update({data: {}}); + +/** + * Render Class List + */ +store.classes = { + data: [], + current: localStorage.getItem('classes') ? localStorage.getItem('classes').split(',') : [] +}; +view.class_list = Monkberry.render(class_list, el('class-list')); +view.class_list.update(store.classes); + +/** + * Render Search + */ +store.search = localStorage.getItem('search'); +view.search_field = Monkberry.render(search_field, el('search-field')); +view.search_field.update({data: store.search}); + +/** + * Discover Classes + */ +const discoverClasses = spells => { + let classes = []; + spells.forEach(spell => { + if (!spell.classes) return; + spell.classes.forEach(current => { + if (!classes.includes(current)) classes.push(current); + }); + }); + return classes.sort((a, b) => a > b); +}; + +/** + * Emphasis on important string bits + * @param {string} string + */ +const emphasis = str => { + let keywords = ['constitution', 'con', 'intelligence', 'int', 'wisdom', 'wis', 'strength', 'str', 'dexterity', 'dex', 'charisma', 'cha', 'comeliness', 'com', 'saving throw', 'ability check', 'skill check']; + keywords.forEach(word => { + let r = new RegExp(` ${word} `, 'gi'); + str = str.replace(r, o => `${o}`); + }); + + str = str.replace(/[\s()<>]+\d+d*\d*(th)*[\s()<>]+/gi, o => `${o}`); + return str; +}; + +/** + * Init Spells + */ +const initSpells = s => s.map((spell, i) => { + spell.selected = false; + spell.ranking = 0; + spell.level = parseInt(spell.level) ? spell.level : 0; + spell.prettyLevel = spell.level === 0 ? 'C' : spell.level; + return spell; +}); + +/** + * Sort Spells + */ +const sortSpells = (s, sortBy, reverse) => s.sort((a, b) => { + let hasFilters = store.classes.current.length || store.search.length; + let by = sortBy || hasFilters ? 'ranking' : store.tableSort.current; + let rev = reverse || hasFilters ? false : store.tableSort.rev; + if (by) { + if (a[by] < b[by]) return rev ? 1 : -1; + if (a[by] > b[by]) return rev ? -1 : 1; + return 0; + } +}); + +/** + * Search Spells + * @param {Array} spells + * @param {String} ex 'acid spray' + * @return {Array} filtered spells + */ +const searchSpells = (spells, search) => { + // Convert search to array of words + search = search.split(' '); + // Clone spells so we don't affect the original + spells = clone(spells); + // Reset rankings + spells = spells.map(s => { + s.ranking = 0; + return s; + }); + // Rank spells by # of occurances of search terms + spells = spells.map(spell => { + search.forEach(term => { + let spellText = Object.values(spell).join(' '); + let regFind = new RegExp(term, 'gi'); + spell.ranking += (spellText.match(regFind) || []).length; + }); + return spell; + }); + // Return spells that matched at least something + return spells.filter(spell => spell.ranking); +}; + +/** + * Filter Spells by Class + */ +const filterSpellsByClass = (spells, classes) => { + // If no classes, default to all classes + classes = classes.length ? classes : store.classes.data; + // Clone spells so we don't affect the original + spells = clone(spells); + return spells.filter(spell => { + let spellClasses = spell.classes.join(' '); + let match = false; + classes.forEach(c => { + if (spellClasses.indexOf(c) >= 0) { + match = true; + return true; + } + }); + return match; + }); +}; + +/** + * Apply Filters + * @returns {Array} of spells ranked based on searches and filters + */ +let applyFilters = () => sortSpells( + searchSpells( + filterSpellsByClass( + store.spells, + store.classes.current + ), + store.search + ) +); + +/** + * Spell Details Updating + */ +const spellDetails = name => { + if (!name) { + view.spell_details.update({data: {}}); + $('body').removeClass('details'); + } else { + let data = store.spells.find(spell => name === spell.name); + data.description = emphasis(data.description); + view.spell_details.update({ + data, + url: window.location.href + }); + $('body').addClass('details'); + let clipboard = new Clipboard('.copy-to-clipboard'); + clipboard + .on('success', e => $('#toast')[0].MaterialSnackbar.showSnackbar({message: 'Copied link'})) + .on('error', e => $('#toast')[0].MaterialSnackbar.showSnackbar({message: 'Sorry! Unable to copy link'})); + } +} +/** + * Event Bindings + */ +// Listen for header sorts +$('body').on('click', '[data-action-sort]', e => { + let name = $(e.currentTarget).attr('data-action-sort'); + let rev = store.tableSort.current === name && !store.tableSort.rev; + store.tableSort.current = name; + store.tableSort.rev = rev; + localStorage.setItem('tableSortName', name); + localStorage.setItem('tableSortRev', rev); + view.spell_list.update({data: sortSpells(store.spells)}); + view.table_sort.update(store.tableSort); +}); + +// Listen for checkbox changes to filter spells +$('body').on('change', '[data-action-classtoggle]', e => { + let name = $(e.currentTarget).attr('data-action-classtoggle'); + let add = $(e.currentTarget).prop('checked'); + let index = store.classes.current.indexOf(name); + if (index === -1 && add) { + store.classes.current.push(name); + } else if (!add) store.classes.current.splice(index, 1); + store.tableSort.current = store.classes.current.length || store.search.length ? 'ranking' : null; + localStorage.setItem('tableSortName', store.tableSort.current); + localStorage.setItem('classes', store.classes.current); + view.spell_list.update({data: applyFilters()}); + view.table_sort.update({current: store.tableSort.current}); +}); + +// Listen to search to filter by +$('body').on('change keyup cut paste', '[data-action-search]', e => { + setTimeout(() => { // Delay for value to change + store.search = $(e.currentTarget).val(); + store.tableSort.current = store.search.length || store.classes.current.length ? 'ranking' : null; + store.tableSort.rev = false; + localStorage.setItem('search', store.search); + localStorage.setItem('tableSortName', store.tableSort.current); + localStorage.setItem('tableSortRev', store.tableSort.rev); + view.spell_list.update({data: applyFilters()}); + view.table_sort.update(store.tableSort); + }, 0); +}); + +// Listen for click on spells to open details +$('body').on('click', '[data-action-details]', e => { + let name = $(e.currentTarget).attr('data-action-details'); + window.location.hash = name; + spellDetails(name); +}); + +// Article Scroll with User +$('.mdl-layout__content').on('scroll', debounce(() => { + let distance = $('.mdl-layout__content')[0].scrollTop; + $('[data-template=spell-details]').css('margin-top', distance); +}, 10)); + +/** + * Fetch Spells + */ +fetch('./spells.json') + .then(response => response.json()) + .then(spells => initSpells(spells)) + .then(spells => { + store.spells = spells; + store.classes.data = discoverClasses(spells); + view.spell_list.update({data: applyFilters()}); + view.class_list.update(store.classes); + if (window.location.hash) spellDetails(window.location.hash.substr(1)); + }) + .catch(reason => console.error('Unable to retrieve spells list:', reason)); diff --git a/src/views/class-list.monk b/src/views/class-list.monk new file mode 100644 index 0000000..7fcc13e --- /dev/null +++ b/src/views/class-list.monk @@ -0,0 +1,19 @@ +{% if data %} + {% for cur of data %} + + {% endfor %} +{% else %} +
    +{% endif %} + diff --git a/src/views/search-field.monk b/src/views/search-field.monk new file mode 100644 index 0000000..97d373c --- /dev/null +++ b/src/views/search-field.monk @@ -0,0 +1 @@ + diff --git a/src/views/spell-details.monk b/src/views/spell-details.monk new file mode 100644 index 0000000..f844ca6 --- /dev/null +++ b/src/views/spell-details.monk @@ -0,0 +1,59 @@ +{% if data.name %} + +
    {{ data.name }}
    +

    {% unsafe data.description %}

    +
    + + + +
    + + +
    +
    +{% else %} +
    +
    + Choose a Spell +
    +
    +{% endif %} diff --git a/src/views/spell-list.monk b/src/views/spell-list.monk new file mode 100644 index 0000000..dc9367e --- /dev/null +++ b/src/views/spell-list.monk @@ -0,0 +1,33 @@ +{% if data.length %} + {% for spell of data %} + + + + {{ spell.name }} + + + + {{ spell.school }} + + + {{ spell.prettyLevel }} + + + {% endfor %} +{% else %} + + + {% if data %} +
    + + warning + +
    No Results
    +
    Try refining your filters in the sidebar.
    +
    + {% else %} +
    + {% endif %} + + +{% endif %} diff --git a/src/views/table-sort.monk b/src/views/table-sort.monk new file mode 100644 index 0000000..fe2818d --- /dev/null +++ b/src/views/table-sort.monk @@ -0,0 +1,23 @@ +{% for name of data %} + + + {% if name === 'level' %} + exposure + {% endif %} + {% if name === 'name' %} + flash_on + {% endif %} + {% if name === 'school' %} + school + {% endif %} + + + +   {{ name }} + + + + {{ rev ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }} + + +{% endfor %}