foam-framework
Version:
MVC metaprogramming framework
324 lines (274 loc) • 10.5 kB
HTML
<html>
<head>
<link rel="stylesheet" type="text/css" href="../../core/foam.css" />
<link rel="stylesheet" type="text/css" href="mreader.css" />
<script>var FOAM_BOOT_DIR="../../core/";</script>
<script language="javascript" src="../../core/bootFOAM.js"></script>
<title>Mail Reader</title>
</head>
<body style="overflow:hidden;margin-top:5px;">
<table>
<tr>
<td>
</td>
<td>
<div class="title"></div>
<div class="subtitle"> MBOX Reader</div>
</td>
<td width=90%></td>
<td valign="top">
<div class="titleBar">
<span style="float:right; margin-top: 23px;color:#666">MBOX File:<input type="file" onchange="loadmbox(event);"></span>
</div>
</td>
</tr>
</table>
<hr color="#9BC0FA">
<div id="search" style="position:absolute;background-color:#fff;">
<div id="subjectSearch" class="searchTitle"></div>
<div id="toSearch" class="searchTitle"></div>
<div id="fromSearch" class="searchTitle"></div>
<div id="labelSearch" class="searchTitle"></div>
<div onClick="resetSearch()" style="float:right" class="searchTitle"><u>Clear All</u></div>
<div id="viewMode" class="searchTitle"></div>
</div>
<div id="browse" style="position:absolute;background-color:#FFF;float:left;"></div>
<div id="edit" style="position:absolute;margin-top:10px;margin-right:15px;border-width:1px;border-style:solid;border-color:black;position:absolute;background-color:#FFF;">
</div>
<div id="footer" style="position:absolute;text-align:left;padding-top:3px;">
<!-- <hr color="#9BC0FA"> -->
<font size=-1 face="catull" style="padding-left:10px;text-shadow:rgba(64,64,64,0.3) 3px 3px 4px;">
<font color="#3333FF">F</font><font color="#FF0000">O</font><font color="#ddaa00">A</font><font color="#33CC00">M</font>
<font color2="#555555"> POWERED</font>
<div onClick="switchHands()" style="float:right;padding-right:10px;" class="searchTitle"><u>switch hands</u></div>
</div>
<script>
var GROUP_PROPERTY = EMail.CONV_ID;
var expanded = {};
var ThreadedEMail = EMail.deepClone();
ThreadedEMail.properties.push(Property.create({
name: 'count',
tableWidth: 45,
label: 'Count',
type: 'int',
tableFormatter: function(c, email, table) {
if ( c ) {
if ( c == 1 ) return " 1";
var isExpanded = expanded[GROUP_PROPERTY.f(email)];
var icon = isExpanded ? " -" : "+";
var id = table.on('click', (function(isExpanded) { return function() {
if ( isExpanded ) {
delete expanded[GROUP_PROPERTY.f(email)];
} else {
expanded[GROUP_PROPERTY.f(email)] = true;
}
updateQuery();
}})(isExpanded));
return '<span id=' + id + '>' + icon + '</span>' + " " + c;
}
return "";
},
}));
ThreadedEMail.tableProperties.unshift('count');
EMail.ATTACHMENTS.compare = function(o1, o2) {
return this.f(o1).length - this.f(o2).length;
};
// var header = $('header');
var footer = $('footer');
var search = $('search');
var browse = $('browse');
var edit = $('edit');
var reversed = false;
function pos(e, top, left, width, height) {
var s = e.style;
left = left || 0;
if ( reversed ) left = (window.innerWidth - 15) - left - (width || toNum(e.style.width));
top != null && (e.style.top = toNum(top) + 'px');
left != null && (e.style.left = toNum(left) + 'px');
width != null && (e.style.width = toNum(width) + 'px');
height != null && (e.style.height = toNum(height) + 'px');
}
var MIN_THREE_COLUMN_W = 1600;
var table;
function layout() {
var W = window.innerWidth - 15;
var H = window.innerHeight-5;
var HEADER_H = 85;
var FOOTER_H = 20;
var SEARCH_W = 400;
var SEARCH_H = H - HEADER_H - FOOTER_H;
var RIGHT_W = W - SEARCH_W - 5;
// pos(header,null,null,W,HEADER_H-10);
pos(search, HEADER_H, null, SEARCH_W, SEARCH_H);
if ( W > MIN_THREE_COLUMN_W ) {
pos(browse, HEADER_H, SEARCH_W + 10, RIGHT_W * 0.6, SEARCH_H+70); // ??? Why is the 70 needed
pos(edit, HEADER_H, SEARCH_W + 10 + RIGHT_W * 0.6, RIGHT_W * 0.4-10, SEARCH_H-15);
} else {
pos(browse, HEADER_H, SEARCH_W + 10, RIGHT_W, SEARCH_H/2);
pos(edit,
toNum(browse.style.top) + toNum(browse.style.height) + 5,
SEARCH_W + 10,
RIGHT_W-10,
SEARCH_H / 2 -20);
}
pos(footer, H-FOOTER_H, null, W, FOOTER_H);
table && table.layout();
}
window.onresize = layout;
layout();
emails = IDBDAO.create({model: EMail});
var dao = MDAO.create({model: EMail});
dao.addIndex(EMail.TIMESTAMP);
// dao.addIndex(EMail.CONV_ID);
dao.addIndex(EMail.TO);
dao.addIndex(EMail.FROM);
dao.addIndex(EMail.SUBJECT);
// var dao = [];
/* dao = PartitionDAO.create({ partitions: [
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail }),
WorkerDAO.create({ model: EMail })
] });
*/
/*
dao.index = AltIndex.create(
TreeIndex.create(EMail.TIMESTAMP, ValueIndex),
TreeIndex.create(EMail.TO, AltIndex.create(
TreeIndex.create(EMail.FROM, TreeIndex.create(EMail.TIMESTAMP, ValueIndex)),
TreeIndex.create(EMail.TIMESTAMP, ValueIndex)
)),
TreeIndex.create(EMail.FROM, TreeIndex.create(EMail.TIMESTAMP, ValueIndex)),
TreeIndex.create(EMail.SUBJECT, TreeIndex.create(EMail.TIMESTAMP, ValueIndex))
);
*/
// dao2.bulkLoad(dao2);
var d1 = dao;
var d2 = emails;
var lock = {};
var idbPerf;
dao = {
__proto__: dao,
find: function(query, sink) {
console.log('find:', query);
d1.find(query, sink);
},
select2: function(sink, options) {
var future = afuture();
var s = CountExpr.isInstance(sink) ? COUNT() : { put: function() { } };
asynchronized(aseq(
// alog('select ', sink, options && options.query && options.query.toSQL() || 'TRUE', options && options.skip, options && options.limit),
// atime('indexedDB', arepeat(1, function(ret) { d2.select(s, options)(ret); }), function(p) { idbPerf = p; }),
// alog('indexedDB to MDAO'),
// atime('idao', arepeat(100, function(ret) { d1.select(s, options)(ret); }), function(p) { console.log(idbPerf + ',' + (p/1000)); }),
function(ret) { d1.select(sink, options)(function(v) { future.set(sink); ret(); }); }
),lock)(anop);
return future.get;
}
};
// Quick hack to make search work on each keystroke
// TODO: make option of TextSearchView
DomValue.DEFAULT_EVENT = 'keyup';
table = ScrollBorder.create({
view: TableView.create({
model: EMail,
dao: dao,
rows: 20
}),
dao: dao
});
var searchSubject = TextSearchView.create({width:57, label: 'Search', property: CONCAT(EMail.SUBJECT, EMail.BODY)});
var byTo = GroupBySearchView.create({size: 9, dao: dao, property: EMail.TO});
var byFrom = GroupBySearchView.create({size: 11, dao: dao, property: EMail.FROM});
var byLabel = GroupBySearchView.create({size: 6, dao: dao, property: EMail.LABELS});
var viewMode = RadioBoxView.create({value:SimpleValue.create('Message'), choices: ['Message', 'Conversation']});
browse.innerHTML = table.toHTML();
searchSubject.insertInElement('subjectSearch');
viewMode.insertInElement('viewMode');
byTo.insertInElement('toSearch');
byFrom.insertInElement('fromSearch');
byLabel.insertInElement('labelSearch');
table.initHTML();
table.view.selection.addListener(function (src, property, oldValue, newValue) {
if ( ! newValue ) return;
var editView = SummaryView.create({value: table.view.selection});
editView.model = EMail;
edit.innerHTML = editView.toHTML();
editView.initHTML();
});
table.view.selection.set(table.view.objs[0]);
layout();
function updateQuery() {
var predicate = AND(
searchSubject.predicate,
byFrom.predicate,
byTo.predicate,
byLabel.predicate).partialEval();
// console.log('query: ', predicate.toSQL());
table.scrollbar.value = 0;
if ( viewMode.value.get() === 'Message' ) {
table.view.model = EMail;
table.dao = dao.where(predicate);
} else {
table.view.model = ThreadedEMail;
dao.where(predicate).select(ExpandableGroupByExpr.create({
arg1: GROUP_PROPERTY,
arg2: EMail.TIMESTAMP,
expanded: expanded
}))(function(groups) {
table.dao = groups;// .values;
});
}
byFrom.filter = AND(searchSubject.predicate, byTo.predicate, byLabel.predicate).partialEval();
byTo.filter = AND(searchSubject.predicate, byFrom.predicate, byLabel.predicate).partialEval();
byLabel.filter = AND(searchSubject.predicate, byTo.predicate, byFrom.predicate).partialEval();
}
viewMode.value.addListener(updateQuery);
Events.dynamic(function() {
searchSubject.predicate;
byFrom.predicate;
byTo.predicate;
byLabel.predicate;
},
updateQuery);
function resetSearch() {
byFrom.view.value.set(''); byTo.view.value.set(''); byLabel.view.value.set('');
byFrom.filter = byTo.filter = byLabel.filter = TRUE;
table.dao = dao;
}
function switchHands() { reversed = ! reversed; layout(); }
function loadmbox(event) {
emails.removeAll(); // this only works with storagedao because its synchronous
var file = event.target.files[0];
MBOXLoader.dao = emails;
SourceBlob(file, BlobToText(TextToLines(MBOXLoader)));
}
var a = [];
emails.select(a)(function() {
console.log('********************************************** loaded');
// Copy values from the array into the dao in an order
// which will cause a balanced tree to be created.
function bcopy(s,e) {
if ( s == e ) dao.put(a[s]);
if ( e <= s ) return;
var m = Math.floor((s+e)/2);
dao.put(a[m]);
bcopy(s,m-1);
bcopy(m+1,e);
}
bcopy(0, a.length-1);
a = undefined;
console.log('********************************************** inserted');
layout();
byTo.dao = byTo.dao;
byFrom.dao = byFrom.dao;
byLabel.dao = byLabel.dao;
resetSearch();
});
TemplateUtil = undefined;
</script>
</body>
</html>