607 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
			
		
		
	
	
			607 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			HTML
		
	
	
	
	
	
| <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.Topics}),
 | |
|         h(ElemTree, {key:"tree2", tree:App.State.Bible}),
 | |
|         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("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 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+" ("+node.Leaves.length+") ")];
 | |
| 
 | |
|         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)=>
 | |
|     {
 | |
|         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);
 | |
|         }
 | |
|     },
 | |
|     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,
 | |
|         },
 | |
|         Topics:
 | |
|         {
 | |
|             Display:"Topics",
 | |
|             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",
 | |
|             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"),
 | |
|                     Node.Create("Romans"),
 | |
|                 ])
 | |
|             ])
 | |
|         }
 | |
|     },
 | |
| 
 | |
|     RootDOM:document.querySelector("#root"),
 | |
|     RootComponent:ElemApp,
 | |
|     Render:()=>ReactDOM.render( h(App.RootComponent), App.RootDOM ),
 | |
|     ApplyFilters:()=>
 | |
|     {
 | |
|         let topics = App.State.Topics;
 | |
|         let bible = App.State.Bible;
 | |
| 
 | |
|         let items = App.State.Items;
 | |
| 
 | |
|         let itrCollect = inNode =>
 | |
|         {
 | |
|             items.Active = items.Active.concat( inNode.Leaves );
 | |
|         };
 | |
| 
 | |
|         items.Active = [];
 | |
| 
 | |
|         topics.Active.forEach(itrCollect);
 | |
|         bible.Active.forEach(itrCollect);
 | |
| 
 | |
|         App.State.Pages.All = _.chunk(items.Active, 10);
 | |
|         App.State.Pages.Active = 0;
 | |
|     },
 | |
|     LeafStructure:(inTree)=>
 | |
|     {
 | |
|         let output = {};
 | |
|         Node.IterateDown(inTree.Root, n=>
 | |
|         {
 | |
|             if(!n.Children)
 | |
|             {
 | |
|                 output[n.ID] = n;
 | |
|             }
 | |
|         });
 | |
|         return output;
 | |
|     },
 | |
| 
 | |
|     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.ApplyFilters();
 | |
|             App.Render();
 | |
|         },
 | |
|         Load:(file)=>
 | |
|         {
 | |
|             
 | |
|             fetch(file)
 | |
|             .then(inAccept=>inAccept.text())
 | |
|             .then(inAccept=>
 | |
|             {
 | |
|                 let structTopics = App.LeafStructure(App.State.Topics);
 | |
|                 let structBible = App.LeafStructure(App.State.Bible);
 | |
| 
 | |
|                 let itrUp = inLeaf=>
 | |
|                 {
 | |
|                     Node.IterateUp(inLeaf, inBranch=>
 | |
|                     {
 | |
|                         inBranch.Leaves = _.union(inBranch.Leaves, inLeaf.Leaves)
 | |
|                     });
 | |
|                 };
 | |
| 
 | |
|                 let columns = inAccept.split("|");
 | |
| 
 | |
|                 for(let i=0; i<columns.length; i+=5)
 | |
|                 {
 | |
|                     let title  = columns[i+0];
 | |
|                     let id     = columns[i+1];
 | |
|                     let year   = columns[i+2];
 | |
|                     let bible  = columns[i+3].split("*");
 | |
|                     let topics = columns[i+4].split("*");
 | |
| 
 | |
|                     let output = { title, id, year, topics, bible };
 | |
| 
 | |
|                     let matched = false;
 | |
|                     topics.forEach(t=>
 | |
|                     {
 | |
|                         let match = structTopics[t];
 | |
|                         if(match)
 | |
|                         {
 | |
|                             match.Leaves.push(output);
 | |
|                             matched = true;
 | |
|                         }
 | |
|                     });
 | |
|                     if(!matched)
 | |
|                     {
 | |
|                         structTopics[""].Leaves.push(output);
 | |
|                     }
 | |
|                     matched = false;
 | |
|                     bible.forEach(t=>
 | |
|                     {
 | |
|                         let passage = Util.ParsePassage(t);
 | |
|                         let match = structBible[ passage[0] ];
 | |
|                         if(match)
 | |
|                         {
 | |
|                             match.Leaves.push(output);
 | |
|                             matched = true;
 | |
|                         }
 | |
|                     });
 | |
|                     if(!matched)
 | |
|                     {
 | |
|                         structBible[""].Leaves.push(output);
 | |
|                     }
 | |
| 
 | |
|                     App.State.Items.All.push(output);
 | |
|                 }
 | |
| 
 | |
|                 Object.values(structTopics).forEach(itrUp);
 | |
|                 Object.values(structBible).forEach(itrUp);
 | |
|                 console.log(App.State.Items.All.length);
 | |
|             })
 | |
|             .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-1.csv");
 | |
| App.Update.Load("data-2.csv");
 | |
| App.Update.Load("data-3.csv");
 | |
| 
 | |
| </script> |