<div id="root"></div> <script type="module"> import _ from "https://dev.jspm.io/lodash"; import styled from "https://dev.jspm.io/styled-components"; 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 s = styled.default; let ElemApp = props => { return h("div", null, [ h(ElemTree, {key:"tree1", tree:App.State.Topics}), h(ElemTree, {key:"tree2", tree:App.State.Bible}), h(ElemTree, {key:"tree3", tree:App.State.Date}), h(ElemItems, {key:3}) ] ); }; let ElemItems = props => { if(App.State.Items.Active.length == 0) { return h("div", null, h("h3", null, "no results found") ) } 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:2}, props.title), h("em", {key:3}, props.id), h("strong", {key:4}, props.date), ] ), h("small", {key:4}, 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 ElemColumn = s.div` display:inline-block; vertical-align:top; `; 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(ElemColumn, {}, [ 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+" ("+node.Leaves.length+") ")]; if(inExpandable) { attributes = {onClick:e=>{ e.stopPropagation(); App.Update.Interact(node); }}; parts.unshift(h("span", null, node.Open?"-":"+")); } if(inButton) { parts.push( h("button", { 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)=> { if(!inDisplay) { inDisplay = inID; } 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); } } }; var App = { State: { Items: { All:[], Active:[] }, Pages: { All:[], Active:0, }, Topics: { Display:"Topics", Results:[], Active:[], Root: Node.Create("root", "All", [ Node.Create("", "Unclassified"), Node.Create("ideologies", "Ideologies", [ Node.Create("gospel-the", "The Gospel"), Node.Create("free-will", "Free Will"), Node.Create("secular-culture", "Secular Culture") ]), Node.Create("people", "People", [ Node.Create("biblical-figures", "Biblical Figures"), Node.Create("jesus-christ", "Jesus Christ"), ]) ]) }, Bible: { Display:"Bible", Results:[], Active:[], Root: Node.Create("all", "All", [ Node.Create("", "Unclassified"), Node.Create("old ", "Old Testament", [ Node.Create("pent", "Pentatuch", [ Node.Create("Genesis", "Genesis"), Node.Create("Exodus", "Exodus"), Node.Create("Leviticus", "Leviticus"), Node.Create("Duteronomy", "Duteronomy"), Node.Create("Numbers", "Numbers") ]), Node.Create("hist", "History", [ Node.Create("Josua", "Josua"), Node.Create("Judges", "Judges"), Node.Create("Ruth", "Ruth"), Node.Create("1 Samuel", "1 Samuel"), Node.Create("2 Samuel", "2 Samuel"), Node.Create("1 Kings", "1 Kings"), Node.Create("2 Kings", "2 Kings"), Node.Create("1 Chronicles", "1 Chronicles"), Node.Create("2 Chronicles", "2 Chronicles"), Node.Create("Ezra", "Ezra"), Node.Create("Nehemiah", "Nehemiah"), Node.Create("Esther", "Esther") ]), Node.Create("poet", "Poetry", [ Node.Create("Job", "Job"), Node.Create("Psalms", "Psalms"), Node.Create("Proverbs", "Proverbs"), Node.Create("Ecclesiastes", "Ecclesiastes"), Node.Create("Song of Solomon", "Song of Songs"), ]), Node.Create("majo", "Major Prophets", [ Node.Create("Isaiah", "Isaiah"), Node.Create("Jeremiah", "Jeremiah"), Node.Create("Lamentations", "Lamentations"), Node.Create("Ezekiel", "Ezekiel"), Node.Create("Daniel", "Daniel"), ]), Node.Create("mino", "Minor Prophets", [ Node.Create("Hosea"), Node.Create("Joel"), Node.Create("Amos"), Node.Create("Obadiah"), Node.Create("Jonah"), Node.Create("Micah"), Node.Create("Nahum"), Node.Create("Habakkuk"), Node.Create("Zephaniah"), Node.Create("Haggai"), Node.Create("Zechariah"), Node.Create("Malachi") ]) ]), Node.Create("nt", "New Testament", [ Node.Create("gospels", "The Gospels", [ Node.Create("Matthew"), Node.Create("Mark"), Node.Create("Luke"), Node.Create("John"), ]), Node.Create("Acts"), Node.Create("Romans"), Node.Create("1 Corinthians"), Node.Create("2 Corinthians"), Node.Create("Galatians"), Node.Create("Ephesians"), Node.Create("1 Thessalonians") ]) ]) }, Date: { Display:"Date", Results:[], Active:[], Root: Node.Create("all", "All", [ Node.Create("", "Unclassified"), Node.Create("8", "1980s"), Node.Create("9", "1990s"), Node.Create("0", "2000s"), Node.Create("1", "2010s"), Node.Create("2", "2020s") ]) } }, OldClassifier:()=> { let leafStructure = inTree=> { let output = []; Node.IterateDown(inTree.Root, n=> { if(!n.Children) { output[n.ID] = n; } }); return output; }; let structTopics = leafStructure(App.State.Topics) let structBible = leafStructure(App.State.Bible); let structDate = leafStructure(App.State.Date); //// var matched, match; // match topics matched = false; topics.forEach(t=> { match = structTopics[t]; if(match) { match.Leaves.push(output); matched = true; } }); if(!matched) { structTopics[""].Leaves.push(output); } // match bible matched = false; bible.forEach(t=> { match = structBible[ Util.ParsePassage(t)[0] ]; if(match) { match.Leaves.push(output); matched = true; } }); if(!matched) { structBible[""].Leaves.push(output); } // match date matched = false; match = structDate[date[2]]; if(match) { match.Leaves.push(output); matched = true; } if(!matched) { structDate[""].Leaves.push(output); } //// let itrUp = inLeaf=> { Node.IterateUp(inLeaf, inBranch=> { inBranch.Leaves = _.union(inBranch.Leaves, inLeaf.Leaves) }); }; Object.values(structTopics).forEach(itrUp); Object.values(structBible).forEach(itrUp); Object.values(structDate).forEach(itrUp); }, Update: { Load:(file)=> { fetch(file) .then(inAccept=>inAccept.text()) .then(inAccept=> { let columns = inAccept.split("|"); for(let i=0; i<columns.length; i+=5) { let title = columns[i+0]; let id = columns[i+1]; let date = columns[i+2]; let bible = columns[i+3].split("*"); let topics = columns[i+4].split("*"); let output = { title, id, date, topics, bible }; App.State.Items.All.push(output); } }) .then(inAccept=> { App.ApplyFilters(); App.Render(); }); }, 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); } else { Node.IterateUp(inNode, itr); Node.IterateDown(inNode, itr); add(inNode); } inTree.Results = []; inTree.Active.forEach(inNode => { inTree.Results = inTree.Results.concat( inNode.Leaves ); }); App.ApplyFilters(); App.Render(); }, Page:(page)=> { if(page !== false) { App.State.Pages.Active = page; App.Render(); } } }, RootDOM:document.querySelector("#root"), RootComponent:ElemApp, Render:()=>ReactDOM.render( h(App.RootComponent), App.RootDOM ), ApplyFilters:()=> { App.State.Items.Active = _.union(App.State.Topics.Results, App.State.Bible.Results, App.State.Date.Results); if(App.State.Items.Active.length == 0) { App.State.Items.Active = App.State.Items.All; } App.State.Pages.All = _.chunk(App.State.Items.Active, 10); App.State.Pages.Active = 0; } }; App.Update.Load("data-1.csv"); App.Update.Load("data-2.csv"); App.Update.Load("data-3.csv"); </script>