<div id="root"></div> <script type="module"> import _ from "https://dev.jspm.io/lodash"; import React from "https://dev.jspm.io/react/index.js"; import ReactDOM from "https://dev.jspm.io/react-dom/index.js"; import ReactDOMServer from "https://dev.jspm.io/react-dom/server.js"; let Util = { ParsePassage:inRef=> { let indexNumeric = inRef.lastIndexOf(" "); let partBook = inRef.substring(0, indexNumeric); let chapterVerse = inRef.substring(indexNumeric+1).split(":"); return [partBook, chapterVerse[0], chapterVerse[1]]; } }; let h = React.createElement; let ElemApp = props => { return h("div", null, [ h(ElemTree, {key:"tree1", tree:App.State.Tree}), h(ElemTree, {key:"tree2", tree:App.State.Bible}), h("h4", {key:0}, "topics"), h(ElemTopics, {key:1}), h(ElemItems, {key:3}) ] ); }; let ElemItems = props => { let list = App.State.Pages.All[App.State.Pages.Active].map( i=>h(ElemItem, {...i, key:i.id} ) ); let pages = h(ElemPager, {count:App.State.Pages.All.length, active:App.State.Pages.Active}, null); return h("div", null, [ h("h4", {key:0}, App.State.Items.Active.length+" results"), h("div", {key:1}, pages), h("div", {key:2}, list), h("div", {key:3}, pages) ]); } let ElemItem = props => { return h("div", {key:0}, [ h("div", {key:1}, [ h("span", {key:0}, props.title), h("em", {key:1}, props.id) ] ), h("small", {key:3}, props.bible) ]) }; let ElemPager = ({count, active}) => { var children; var blockIntro, blockAt, blockOutro; var hitLeft, hitRight; let renderRange = (inStart, inStop) => { let output = []; for(let i=inStart; i<=inStop; i++) { output.push(h("button", {key:i, onClick:e=>App.Update.Page(i)}, i+1)); } return output; } let renderSpacer = () => h("span", {key:Math.random()}, "…"); blockIntro = [0, 1, 2]; blockAt = [active-1, active, active+1]; blockOutro = [count-3, count-2, count-1]; hitLeft = _.last(blockIntro) >= _.head(blockAt); hitRight = _.last(blockAt) >= _.head(blockOutro); if(count < 2) { children = []; } else { if( (hitLeft && hitRight) || count < 7) { children = renderRange(_.head(blockIntro), _.last(blockOutro)) /* merge all */ } else if(!hitLeft && hitRight) { children = [ ...renderRange(_.head(blockIntro), _.last(blockIntro)), renderSpacer(), ...renderRange(Math.min(_.head(blockAt), _.head(blockOutro)), _.last(blockOutro)) /* merge end */ ]; } else if(hitLeft && !hitRight) { children = [ ...renderRange(_.head(blockIntro), Math.max(_.last(blockAt), _.last(blockIntro)) ), /* merge beginning */ renderSpacer(), ...renderRange(_.head(blockOutro), _.last(blockOutro)) ]; } else if(!hitLeft && !hitRight) { children = [ ...renderRange(_.head(blockIntro), _.last(blockIntro)), renderSpacer(), ...renderRange(_.head(blockAt), _.last(blockAt)), renderSpacer(), ...renderRange(_.head(blockOutro), _.last(blockOutro)) ]; } } return h("div", {}, children); }; let ElemTopics = props => { let children = App.State.Topics.All.map( t=> { return h(ElemTopic, { onClick:e=>App.Update.Topic(t), key:t.id, label:t.display, active:t.active }); }); return h("div", null, children); }; let ElemTopic = props => { let label = props.active? props.label+" !" : props.label; return h("button", {onClick:props.onClick}, label); }; let ElemTree = ({tree}) => { let activeItems = tree.Active.map( a=>{ return h("button", {disabled:a.Parent?false:true, key:a.ID, onClick:e=>App.Update.Select(a, tree)}, a.Display ) }) return h("div", {}, [ h("h3", {key:"title"}, tree.Display), h("div", {key:"filters-active"}, [ h("strong", {key:"label"}, "Showing:"), h("span", {key:"list"}, activeItems) ]), h("div", {key:"filters-browse"}, [ h("strong", {key:"label"}, "Available:"), h(ElemTreeNode, {key:"tree", node:tree.Root, tree:tree}) ]) ]); } let ElemTreeNode = ({node, tree}) => { let children = []; if(node.Children && node.Open) { children = node.Children.map( c=>h(ElemTreeNode, {node:c, tree:tree, key:c.ID})); } let elemTitle = (inExpandable, inButton) => { let attributes = null; let parts = [h("span", null, node.Display)]; if(inExpandable) { attributes = {onClick:e=>{ e.stopPropagation(); App.Update.Interact(node); }}; parts.unshift(h("span", null, node.Open?"-":"+")); } parts.push( h("button", { disabled: (!inButton&&node.Active) ? true : false, onClick:e=>{ e.stopPropagation(); App.Update.Select(node, tree); } }, node.Active?"remove":"add") ); return h("div", attributes, parts); } var partTitle; if(!node.Parent) { partTitle = elemTitle(true, false); } else { if(node.Children) { partTitle = elemTitle(true, true); } else { partTitle = elemTitle(false, true); } } return h("div", {style:{padding:"10px"}}, [ partTitle, h("div", {}, children) ]); }; let Node = { Create:(inID, inDisplay, inChildren)=> { let node = { ID:inID, Display:inDisplay, Open:false, Active:false, Parent:false, Children:inChildren, Leaves:[] }; if(inChildren) { inChildren.forEach(c=>c.Parent = node); } return node; }, IterateDown:(inNode, inIterator)=> { if( inIterator(inNode) ) { return true; } if(inNode.Children) { for(let i=0; i<inNode.Children.length; i++) { if(Node.IterateDown(inNode.Children[i], inIterator)) { return true; } } } }, IterateUp:(inNode, inIterator)=> { if( inIterator(inNode) ) { return true; } if(inNode.Parent) { Node.IterateUp(inNode.Parent, inIterator); } }, Path:(inNode, inList) => { let node = inNode; while(node) { inList.push(node); node = node.Parent; } }, Leaves:(inNode, inList) => { var i; var child; if(inNode.Children) { for(i=0; i<inNode.Children.length; i++) { Node.Decedents(inNode.Children[i], inList); } } else { inList.push(inNode); } } }; var App = { State: { Items: { All:[], Active:[] }, Pages: { All:[], Active:0, }, Tree: { Display:"Topics", Active:[], Root: Node.Create("root", "All", [ Node.Create("b1", "Branch 1", [ Node.Create("l1", "Leaf One"), Node.Create("l2", "Leaf Two"), Node.Create("l3", "Leaf Three") ]), Node.Create("b2", "Branch 2", [ Node.Create("l4", "Leaf Four"), Node.Create("l5", "Leaf Five"), ]) ]) }, Bible: { Display:"Bible", Active:[], Root: Node.Create("all", "All", [ Node.Create("ot", "Old Testament", [ Node.Create("1", "Genesis"), Node.Create("2", "Exodus"), Node.Create("3", "Leviticus"), Node.Create("4", "Duteronomy"), Node.Create("5", "Numbers") ]), Node.Create("nt", "New Testament", [ Node.Create("1", "Matthew"), Node.Create("2", "Mark"), Node.Create("3", "Luke"), Node.Create("4", "John"), Node.Create("5", "Acts"), Node.Create("6", "Romans"), ]) ]) }, Topics: { All: [ { id:"gospel-the", display:"The Gospel", active:false, members:[] }, { id:"jesus-christ", display:"Jesus Christ", active:false, members:[] }, { id:"biblical-figures", display:"Biblical Figures", active:false, members:[] }, { id:"free-will", display:"Free Will", active:false, members:[] }, { id:"secular-culture", display:"Secular Culture", active:false, members:[] } ], Active:[] }, Scripture: { All:{}, Active:[] } }, RootDOM:document.querySelector("#root"), RootComponent:ElemApp, Render:()=>ReactDOM.render( h(App.RootComponent), App.RootDOM ), ApplyFilters:()=> { if(App.State.Topics.Active.length == 0) { App.State.Items.Active = [...App.State.Items.All]; } else { App.State.Items.Active = App.State.Items.All.filter( item => { for(let i=0; i<item.topics.length; i++) { for(let j=0; j<App.State.Topics.Active.length; j++) { if(item.topics[i] == App.State.Topics.Active[j].id) { return true; } } } return false; }); } App.State.Pages.All = _.chunk(App.State.Items.Active, 10); App.State.Pages.Active = 0; }, Update: { Interact:(inNode)=> { if(inNode.Open) { Node.IterateDown(inNode, n=>{n.Open=false;}); } else { Node.IterateUp(inNode, n=>{n.Open=true;}); } App.Render(); }, Select:(inNode, inTree)=> { let clear = c => { let index = _.indexOf(inTree.Active, c); inTree.Active.splice( index, 1 ); c.Active = false; }; let add = a => { a.Active = true; inTree.Active.push(a); }; let itr = n=> { if(n.Active) { clear(n); } }; if(inNode.Active) { clear(inNode); if(inTree.Active.length == 0) { add(inTree.Root); } } else { Node.IterateUp(inNode, itr); Node.IterateDown(inNode, itr); add(inNode); } App.Render(); }, Load:(file)=> { fetch(file) .then(inAccept=>inAccept.text()) .then(inAccept=> { let columns = inAccept.split("|"); App.State.Items.All = []; for(let i=0; i<columns.length; i+=4) { let row = Math.floor(i/4); App.State.Items.All[row] = { title:columns[i+0], id:columns[i+1], bible:columns[i+2].split("*"), topics:columns[i+3].split("*") }; } }) .then(inAccept=> { App.ApplyFilters(); App.Render(); }) }, Topic:(topic)=> { if(topic.active) { topic.active = false; App.State.Topics.Active = App.State.Topics.Active.filter( t=>t.id != topic.id ); } else { topic.active = true; App.State.Topics.Active.push(topic); } App.ApplyFilters(); App.Render(); }, Page:(page)=> { if(page !== false) { App.State.Pages.Active = page; App.Render(); } } } }; App.Update.Load("data-flat.csv"); </script>