mirror of
https://github.com/TangentFoxy/memex.git
synced 2024-11-22 04:54:23 +00:00
Initial commit.
This commit is contained in:
commit
0c3f2d4eaf
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
/content/media/*
|
10
README.md
Normal file
10
README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Memex
|
||||
|
||||
Simple flat file formatted bookmarks/notes.
|
||||
|
||||
Small self contained project for playing with [Indental](https://wiki.xxiivv.com/#indental) and [Runic](https://wiki.xxiivv.com/#runic).
|
||||
|
||||
## Dependencies
|
||||
|
||||
- The database format is [Indental](https://wiki.xxiivv.com/#indental).
|
||||
- The templating language is [Runic](https://wiki.xxiivv.com/#runic).
|
49
asset/style.css
Normal file
49
asset/style.css
Normal file
@ -0,0 +1,49 @@
|
||||
html {
|
||||
background:#DCDCDC;
|
||||
font-family:'Monaco', 'Andale Mono', 'Deja Vu Sans Mono', 'Consolas', monospace;
|
||||
font-size:1em;
|
||||
}
|
||||
|
||||
/* LINK */
|
||||
a {
|
||||
color:#000;
|
||||
text-decoration:underline;
|
||||
}
|
||||
#entryImportant a {
|
||||
color:#fff;
|
||||
}
|
||||
a#urlseen {
|
||||
color:#666;
|
||||
}
|
||||
|
||||
/* ENTRY */
|
||||
#entry, #entryImportant {
|
||||
color:#000;
|
||||
background:#999;
|
||||
margin-bottom:1em;
|
||||
padding:0.5em;
|
||||
min-height:2.4em;
|
||||
}
|
||||
#entryImportant {
|
||||
color:#DCDCDC;
|
||||
background:#333333;
|
||||
}
|
||||
|
||||
/* TAGS */
|
||||
#tags {
|
||||
font-size:0.8em;
|
||||
color:#000;
|
||||
}
|
||||
#entryImportant #tags {
|
||||
color:#808080;
|
||||
}
|
||||
|
||||
/* NOTE, QUOTE, TERM */
|
||||
#note, #quote, #term {
|
||||
padding-top: 1em;
|
||||
font-size:0.8em;
|
||||
color:#000;
|
||||
}
|
||||
#entryImportant #note, #entryImportant #quote, #entryImportant #term {
|
||||
color:#808080;
|
||||
}
|
1066
content/Memex.ndtl
Normal file
1066
content/Memex.ndtl
Normal file
File diff suppressed because it is too large
Load Diff
19
index.html
Normal file
19
index.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<link rel="stylesheet" href="asset/style.css">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<script type="text/javascript" src="content/memex.ndtl"></script>
|
||||
<script src="logic/indental.js"></script>
|
||||
<script src="logic/main.js"></script>
|
||||
|
||||
<title>memex</title>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
let main = new Main();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
54
logic/indental.js
Normal file
54
logic/indental.js
Normal file
@ -0,0 +1,54 @@
|
||||
function Indental(data)
|
||||
{
|
||||
this.data = data;
|
||||
|
||||
this.parse = function(type)
|
||||
{
|
||||
var lines = this.data.split("\n").map(liner)
|
||||
// Assoc lines
|
||||
var stack = {}
|
||||
var target = lines[0]
|
||||
for(id in lines){
|
||||
var line = lines[id]
|
||||
if(line.skip){ continue; }
|
||||
target = stack[line.indent-2];
|
||||
if(target){ target.children.push(line) }
|
||||
stack[line.indent] = line
|
||||
}
|
||||
|
||||
// Format
|
||||
var h = {}
|
||||
for(id in lines){
|
||||
var line = lines[id];
|
||||
if(line.skip || line.indent > 0){ continue; }
|
||||
var key = line.content.toUpperCase()
|
||||
h[key] = type ? new type(key,format(line)) : format(line)
|
||||
}
|
||||
return h
|
||||
}
|
||||
|
||||
function format(line)
|
||||
{
|
||||
var a = [];
|
||||
var h = {};
|
||||
for(id in line.children){
|
||||
var child = line.children[id];
|
||||
if(child.key){ h[child.key.toUpperCase()] = child.value }
|
||||
else if(child.children.length == 0 && child.content){ a.push(child.content) }
|
||||
else{ h[child.content.toUpperCase()] = format(child) }
|
||||
}
|
||||
return a.length > 0 ? a : h
|
||||
}
|
||||
|
||||
function liner(line)
|
||||
{
|
||||
return {
|
||||
indent:line.search(/\S|$/),
|
||||
content:line.trim(),
|
||||
skip:line == "" || line.substr(0,1) == "~",
|
||||
key:line.indexOf(" : ") > -1 ? line.split(" : ")[0].trim() : null,
|
||||
value:line.indexOf(" : ") > -1 ? line.split(" : ")[1].trim() : null,
|
||||
children:[]
|
||||
}
|
||||
}
|
||||
}
|
150
logic/main.js
Normal file
150
logic/main.js
Normal file
@ -0,0 +1,150 @@
|
||||
this.DB = new Indental(DATABASE.memex).parse();
|
||||
let keys = Object.keys(DB);
|
||||
let page = 0;
|
||||
let lastEntry = -1;
|
||||
let postPerPage = 1000;
|
||||
|
||||
function Main()
|
||||
{
|
||||
console.log(DB);
|
||||
|
||||
let view = ``;
|
||||
let html = document.body;
|
||||
|
||||
view += `<div id="content">${displayEntries(DB)}</div>`;
|
||||
|
||||
html.innerHTML = view;
|
||||
}
|
||||
|
||||
function displayEntries(db)
|
||||
{
|
||||
let entries = ``;
|
||||
page += postPerPage;
|
||||
|
||||
var i = lastEntry+1;
|
||||
var value;
|
||||
while (i < Math.min(keys.length, page))
|
||||
{
|
||||
value = db[keys[i]];
|
||||
|
||||
// ENTRY
|
||||
var idEntry = "entry";
|
||||
if (typeof value.REVI !== 'undefined')
|
||||
{
|
||||
if (value.REVI == "true")
|
||||
{
|
||||
idEntry = "entryImportant";
|
||||
}
|
||||
}
|
||||
|
||||
entries += `<div id="${idEntry}">`;
|
||||
entries += `${keys[i].toProperCase()}`;
|
||||
|
||||
// LINK
|
||||
if (typeof value.LINK !== 'undefined')
|
||||
{
|
||||
var idUrl = "url";
|
||||
if (typeof value.SEEN !== 'undefined')
|
||||
{
|
||||
if (value.SEEN == "true")
|
||||
{
|
||||
idUrl = "urlseen";
|
||||
}
|
||||
}
|
||||
entries += `<div id="link"><a href="${String(value.LINK)}" id="${idUrl}">${extractRootDomain(value.LINK)}</a></div>`;
|
||||
}
|
||||
|
||||
// TAGS
|
||||
if (typeof value.TAGS !== 'undefined')
|
||||
{
|
||||
entries += `<div id="tags">${value.TAGS.toLowerCase()}</div>`;
|
||||
}
|
||||
|
||||
// NOTE
|
||||
if (typeof value.NOTE !== 'undefined')
|
||||
{
|
||||
entries += `<div id="note">NOTE: ${value.NOTE}</div>`;
|
||||
}
|
||||
|
||||
// QUOTE
|
||||
if (typeof value.QOTE !== 'undefined')
|
||||
{
|
||||
entries += `<div id="quote">QUOTE: ${value.QOTE}</div>`;
|
||||
}
|
||||
|
||||
// TERM
|
||||
if (typeof value.TERM !== 'undefined')
|
||||
{
|
||||
entries += `<div id="term">TERM(S): ${value.TERM}</div>`;
|
||||
}
|
||||
|
||||
entries += `</div>`;
|
||||
|
||||
lastEntry = i;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
entries += doPagination();
|
||||
return entries;
|
||||
}
|
||||
|
||||
function doPagination()
|
||||
{
|
||||
return `
|
||||
<div id="pagination">
|
||||
<a id="loadmore" onClick="loadMore();">${lastEntry < keys.length -1 ? `Load more ▼` : ``}</a>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
|
||||
function loadMore()
|
||||
{
|
||||
pagination.remove();
|
||||
document.getElementById("content").innerHTML += doJournal(DB);
|
||||
}
|
||||
|
||||
String.prototype.toProperCase = function ()
|
||||
{
|
||||
return this.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
|
||||
};
|
||||
|
||||
// Source: https://stackoverflow.com/questions/8498592/extract-hostname-name-from-string
|
||||
function extractHostname(url)
|
||||
{
|
||||
var hostname;
|
||||
//find & remove protocol (http, ftp, etc.) and get hostname
|
||||
|
||||
if (url.indexOf("://") > -1) {
|
||||
hostname = url.split('/')[2];
|
||||
}
|
||||
else {
|
||||
hostname = url.split('/')[0];
|
||||
}
|
||||
|
||||
//find & remove port number
|
||||
hostname = hostname.split(':')[0];
|
||||
//find & remove "?"
|
||||
hostname = hostname.split('?')[0];
|
||||
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/questions/8498592/extract-hostname-name-from-string
|
||||
function extractRootDomain(url)
|
||||
{
|
||||
var domain = extractHostname(url),
|
||||
splitArr = domain.split('.'),
|
||||
arrLen = splitArr.length;
|
||||
|
||||
//extracting the root domain here
|
||||
//if there is a subdomain
|
||||
if (arrLen > 2) {
|
||||
domain = splitArr[arrLen - 2] + '.' + splitArr[arrLen - 1];
|
||||
//check to see if it's using a Country Code Top Level Domain (ccTLD) (i.e. ".me.uk")
|
||||
if (splitArr[arrLen - 2].length == 2 && splitArr[arrLen - 1].length == 2) {
|
||||
//this is using a ccTLD
|
||||
domain = splitArr[arrLen - 3] + '.' + domain;
|
||||
}
|
||||
}
|
||||
return domain;
|
||||
}
|
149
logic/runic.js
Normal file
149
logic/runic.js
Normal file
@ -0,0 +1,149 @@
|
||||
function Runic(raw,tables)
|
||||
{
|
||||
this.tables = tables;
|
||||
this.raw = raw;
|
||||
|
||||
this.runes = {
|
||||
"&":{glyph:"&",tag:"p",class:""},
|
||||
"~":{glyph:"~",tag:"list",sub:"ln",class:"parent",stash:true},
|
||||
"-":{glyph:"-",tag:"list",sub:"ln",class:"",stash:true},
|
||||
"=":{glyph:"=",tag:"list",sub:"ln",class:"mini",stash:true},
|
||||
"!":{glyph:"!",tag:"table",sub:"tr",wrap:"th",class:"outline",stash:true},
|
||||
"|":{glyph:"|",tag:"table",sub:"tr",wrap:"td",class:"outline",stash:true},
|
||||
"#":{glyph:"#",tag:"code",sub:"ln",class:"",stash:true},
|
||||
"%":{glyph:"%"},
|
||||
"?":{glyph:"?",tag:"note",class:""},
|
||||
":":{glyph:":",tag:"info",class:""},
|
||||
"*":{glyph:"*",tag:"h2",class:""},
|
||||
"+":{glyph:"+",tag:"hs",class:""},
|
||||
">":{glyph:">",tag:"",class:""},
|
||||
"$":{glyph:">",tag:"",class:""},
|
||||
"@":{glyph:"@",tag:"quote",class:""}
|
||||
}
|
||||
|
||||
this.stash = {
|
||||
rune : "",
|
||||
all : [],
|
||||
add : function(rune,item){
|
||||
this.rune = this.copy(rune)
|
||||
this.all.push({rune:rune,item:item});
|
||||
},
|
||||
pop : function(){
|
||||
var copy = this.copy(this.all);
|
||||
this.all = [];
|
||||
return copy;
|
||||
},
|
||||
is_pop : function(rune){
|
||||
return this.all.length > 0 && rune.tag != this.rune.tag;
|
||||
},
|
||||
length: function()
|
||||
{
|
||||
return this.all.length;
|
||||
},
|
||||
copy : function(data){
|
||||
return JSON.parse(JSON.stringify(data));
|
||||
}
|
||||
}
|
||||
|
||||
this.parse = function(raw = this.raw)
|
||||
{
|
||||
if(!raw){ return ""; }
|
||||
|
||||
var html = "";
|
||||
var lines = raw;
|
||||
var lines = !Array.isArray(raw) ? raw.toString().split("\n") : raw;
|
||||
|
||||
for(id in lines){
|
||||
var char = lines[id].substr(0,1).trim().toString()
|
||||
var rune = this.runes[char];
|
||||
var trail = lines[id].substr(1,1);
|
||||
var line = lines[id].substr(2).to_markup();
|
||||
|
||||
if(!line || line.trim() == ""){ continue; }
|
||||
if(!rune){ console.log(`Unknown rune:${char} : ${line}`); continue; }
|
||||
if(trail != " "){ console.warn("Runic",`Non-rune[${trail}] at:${id}(${line})`); continue; }
|
||||
|
||||
if(this.stash.is_pop(rune)){ html += this.render_stash(); }
|
||||
|
||||
if(char == "$"){ html += `<p>${Ø("operation").request(line).to_markup()}</p>`; continue; }
|
||||
if(char == "%"){ html += this.media(line); continue; }
|
||||
if(char == "@"){ html += this.quote(line); continue; }
|
||||
if(char == ":"){ html += this.info(line); continue; }
|
||||
|
||||
if(rune.stash === true){ this.stash.add(rune,line) ; continue; }
|
||||
html += this.render(line,rune);
|
||||
}
|
||||
if(this.stash.length() > 0){ html += this.render_stash(); }
|
||||
return html;
|
||||
}
|
||||
|
||||
this.render_stash = function()
|
||||
{
|
||||
var rune = this.stash.rune;
|
||||
var stash = this.stash.pop();
|
||||
|
||||
var html = "";
|
||||
for(id in stash){
|
||||
var rune = stash[id].rune;
|
||||
var line = stash[id].item;
|
||||
html += rune.wrap ? `<${rune.sub}><${rune.wrap}>${line.replace(/\|/g,`</${rune.wrap}><${rune.wrap}>`).trim()}</${rune.wrap}></${rune.sub}>` : `<${rune.sub}>${line}</${rune.sub}>`;
|
||||
}
|
||||
return `<${rune.tag} class='${rune.class}'>${html}</${rune.tag}>`
|
||||
}
|
||||
|
||||
this.render = function(line = "",rune = null)
|
||||
{
|
||||
if(rune && rune.tag == "img"){ return `<img src='media/${line}'/>`; }
|
||||
if(rune && rune.tag == "table"){ return "HEY"; }
|
||||
|
||||
return rune ? (rune.tag ? `<${rune.tag} class='${rune.class}'>${line}</${rune.tag}>` : line) : "";
|
||||
}
|
||||
|
||||
this.media = function(val)
|
||||
{
|
||||
var service = val.split(" ")[0];
|
||||
var id = val.split(" ")[1];
|
||||
|
||||
if(service == "itchio"){ return `<iframe frameborder="0" src="https://itch.io/embed/${id}?link_color=000000" width="600" height="167"></iframe>`; }
|
||||
if(service == "bandcamp"){ return `<iframe style="border: 0; width: 600px; height: 274px;" src="https://bandcamp.com/EmbeddedPlayer/album=${id}/size=large/bgcol=ffffff/linkcol=333333/artwork=small/transparent=true/" seamless></iframe>`; }
|
||||
if(service == "youtube"){ return `<iframe width="600" height="315" src="https://www.youtube.com/embed/${id}" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>`; }
|
||||
if(service == "custom"){ return `<iframe src='${id}' style='width:100%;height:350px;'></iframe>`; }
|
||||
return `<img src='media/${val}'/>`
|
||||
}
|
||||
|
||||
this.quote = function(content)
|
||||
{
|
||||
var parts = content.split(" | ")
|
||||
var text = parts[0]
|
||||
var author = parts[1]
|
||||
var source = parts[2]
|
||||
var link = parts[3]
|
||||
|
||||
return `
|
||||
<quote>
|
||||
<p class='text'>
|
||||
${text.to_markup()}
|
||||
</p>
|
||||
<p class='attrib'>
|
||||
${author}${source && link ? `, <a href='${link}'>${source}</a>` : source ? `, <b>${source}</b>` : ''}
|
||||
</p>
|
||||
</quote>`
|
||||
}
|
||||
|
||||
this.info = function(content)
|
||||
{
|
||||
var key = content.split("|")[0].trim()
|
||||
var term = this.tables.lexicon[key.toUpperCase()]
|
||||
var log = term.logs[0]
|
||||
var glyph = term.glyph();
|
||||
|
||||
if(!log){ return '' }
|
||||
|
||||
return `<info><svg width="30" height="30" xmlns="http://www.w3.org/2000/svg" baseProfile="full" version="1.1"><g transform='scale(0.1)'><path d='${glyph}'/></g></svg><t class='key'>{{${key.capitalize()}}}</t><t class='val'>${log.name ? log.name : log.task.capitalize()}</t><t class='offset'>${log.time.offset_format(new Date().desamber(),true).capitalize()}, <b>${log.time}</b></t></info>`.to_markup()
|
||||
}
|
||||
|
||||
this.toString = function()
|
||||
{
|
||||
return this.parse();
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user