<script> let Node = { Create:(inID, inDisplay, inChildren)=> { let node = { ID:inID, Display:inDisplay, Active:false, Highlighted:0, Parent:false, Children:inChildren, Leaves:[] }; if(inChildren) { inChildren.forEach(c=>c.Parent = node); } return node; }, 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); } } }; </script> <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(ElemTreeNode, {key:"tree", node:App.State.Tree.Root}), 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 ElemTreeNode = ({node}) => { let children = []; if(node.Children) { children = node.Children.map( c=>h(ElemTreeNode, {node:c, key:c.ID})); } return h("div", {style:{padding:"10px"}}, [ h("strong", { onClick:e=> { e.stopPropagation(); App.Update.Select(App.State.Tree, node); }, style: { background:node.Highlighted?"red":"none" } }, node.Display), h("div", {}, children) ]); }; var App = { State: { Items: { All:[], Active:[] }, Pages: { All:[], Active:0, }, Tree: { Active:false, Highlighted:false, Root: Node.Create("root", "Root", [ Node.Create("b1", "Branch 1", [ Node.Create("l1", "Leaf One") ]), Node.Create("b2", "Branch 2", [ Node.Create("l2", "Leaf Two"), Node.Create("l3", "Leaf Three"), ]) ]) }, 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: { Select:(inTree, inNode)=> { if(inNode.Active) { inNode.Active = false; inTree.Highlighted.forEach( h=>h.Highlighted-- ); inTree.Root.Active = true; inTree.Root.Highlighted = true; inTree.Active = inTree.Root; inTree.Highlighted = [inTree.Root]; } else { if(inTree.Active) { inTree.Active.Active = false; inTree.Highlighted.forEach( h=>h.Highlighted-- ); } inTree.Active = inNode; inTree.Active.Active = true; inTree.Highlighted = []; Node.Path(inTree.Active, inTree.Highlighted); inTree.Highlighted.forEach( h=>h.Highlighted++ ); } console.log(inTree); 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("*") }; } 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>