585 lines
16 KiB
HTML
585 lines
16 KiB
HTML
<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> |