/* appjet:version 0.1 */
import("storage", "quickforms");
import("lib-datestrings");
// Database
if (! storage.posts) { storage.posts = new StorableCollection(); }
if (! storage.seed) { storage.seed = Math.floor(Math.random() * 1e9); }
// favicon.ico
page.head.write(toHTML(
LINK({rel: "shortcut icon",
href: "http://appjet.com/favicon.ico"})));
// Redefine print to print into a contentDiv
var contentDiv = DIV({'class': "content"});
var realprint = print;
var print = function() {
contentDiv.push.apply(contentDiv, Array.prototype.slice.call(arguments));
}
import("lib-start");
import("lib-admin");
import("lib-atom");
// For adding/editing a ChangeLog entry.
function ChangeLogInputForm(prop) {
var form = new QuickForm({action: "/clmod", method: "post"});
form.addHeading("h", (prop && prop.id ? "Edit CL" : "Add a new CL"));
form.addInputTextArea("summary", {label: "Summary", cols: 60, rows: 3,
value: (prop ? prop.summary : undefined)});
form.addInputTextArea("diff", {label: "Changes", cols: 60, rows: 30,
value: (prop ? prop.diff : undefined)});
if (prop && prop.id) {
form.addInputHidden("modid", prop.id);
}
form.addSubmit("submit", "Submit");
return form;
}
// Object for displaying a changelog entry.
function ChangeLogDisplay(obj, f) {
var n = (typeof(f) == 'function' ? f(obj) : obj);
this.toHTML = function() {
return supplant(n, """
<div class="cl">
<div class="cldate"><a href="/change?id={id}">{date}</a></div>
<div class="cldiff">{diff}</div>
<div class="edit">{edit}</div>
</div>""");
}
}
// Takes a StorableObject and converts it to the format the ChangeLogDisplay expects
function CLConverter(obj) {
var ret = {
id: obj.id,
date: (new Date(obj.date)).toLocaleDateString(),
diff: obj.diff,
summary: obj.summary
};
if (isAdmin()) {
ret.edit = toHTML(A({href: "/edit?id="+obj.id }, "edit"));
} else {
ret.edit = ""
}
return ret;
}
// Page display
function printHeading() {
print(
DIV({id: "head"},
A({href: "/", id: "headlink"},
IMG({src: "http://appjet.com/img/aug/frontlogo.gif",
height: 60 })),
SPAN({id: "headtitle"}, "Recent Changes")),
DIV({className: "clearfloats"}, html("<!-- -->")),
DIV({id: "secondHead"},
DIV({id: "backlink"},
A({href: "http://appjet.com/"}, html("« Back to AppJet"))),
DIV({id: "rsslink"},
A({href: "/feed", style: "text-decoration: none;"},
IMG({src: "http://www.blogsmithmedia.com/www.engadget.com/media/feedicon_small.gif"}),
html(" RSS Feed")))),
DIV({className: "clearfloats"}, html("<!-- -->")));
}
function printFirstN(n) {
page.head.write(toHTML(LINK({rel: "alternate", title: "AppJet ChangeLog Atom Feed",
href: "http://changelog.appjet.com/feed",
type: "application/atom+xml"})));
printHeading();
var dateSorted = storage.posts.sortBy("-date")
dateSorted.limit(n).forEach(function(obj) {
print(new ChangeLogDisplay(obj, CLConverter));
});
dateSorted.skip(n).forEach(function(obj) {
print(DIV({className: 'cl'},
A({href: "/change?id="+obj.id, className: 'cllink'},
"Earlier change on "+obj.date.toLocaleDateString()+
" at "+obj.date.toLocaleTimeString())));
});
if (isAdmin()) {
print(ChangeLogInputForm());
}
}
// Index
function get_() {
printFirstN(10);
}
function get_all() {
printFirstN(10000);
}
function post_clmod() {
if(! isAdmin())
response.redirect("/");
if (request.params.modid) {
var o = getStorable(request.params.modid);
o.summary = request.params.summary;
o.diff = request.params.diff;
} else {
storage.posts.add({date: new Date(), summary: request.params.summary,
diff: request.params.diff});
}
response.redirect("/");
}
function get_edit() {
storage.posts.sortBy("-date").forEach(function(obj) {
if (obj.id == request.params.id) {
print(new ChangeLogInputForm(CLConverter(obj)));
} else {
print(new ChangeLogDisplay(obj, CLConverter));
}
});
}
function get_change() {
printHeading();
var o = getStorable(request.params.id);
if (! o)
response.notFound();
print(new ChangeLogDisplay(o, CLConverter));
}
function get_atom() {
var entries = {
base: storage.posts.sortBy("-date"),
i: 0,
forEach: function(f) {
this.base.forEach(function(n) {
return f({
author: "AppJet",
title: "Change on "+n.date.toLocaleDateString()+
" at "+n.date.toLocaleTimeString(),
link: "http://changelog.appjet.com/change?id="+n.id,
id: "tag:changelog.appjet.com,2008:obj/"+n.id,
updated: n.date,
summary: n.summary,
content: n.diff
})})}
}
page.setMode("plain");
response.setHeader("Content-Type", "application/atom+xml");
realprint(raw(getMainFeed("AppJet ChangeLog",
"http://changelog.appjet.com/atom",
"tag:changelog.appjet.com,2008:/1",
storage.posts.sortBy("-date").first().date, entries)));
response.stop(true);
}
function get_feed() {
response.redirect("http://feeds.feedburner.com/AppjetChangelog");
}
function get_summary() {
var max = 3;
storage.posts.sortBy("-date").forEach(function(obj) {
if (obj.summary) {
realprint(B(A({href: "/change?id="+obj.id, target: "_top"},
shortMon[obj.date.getMonth()], " ", obj.date.getDate(), " ", obj.date.getFullYear()),
": "), raw(obj.summary));
}
if (max-- <= 0) return false;
});
response.stop(true);
}
if ((! storage.adminpassword) && request.path != "/start") {
response.redirect("/start");
} else {
dispatch();
}
realprint(contentDiv);
realprint(raw("""
<script type="text/javascript">
var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www.");
document.write(unescape("%3Cscript src='" + gaJsHost + "google-analytics.com/ga.js' type='text/javascript'%3E%3C/script%3E"));
</script>
<script type="text/javascript">
var pageTracker = _gat._getTracker("UA-4373620-2");
pageTracker._initData();
pageTracker._trackPageview();
</script>
"""));
/* appjet:css */
body {
font-family: sans-serif;
background: #eee;
}
img { border: 0px; }
div#head {
text-align: center;
font-weight: bold;
font-size: 200%;
font-family: serif;
color: #333;
}
div#head img { vertical-align: middle; }
a#headlink { color: black; text-decoration: underline; }
span#headtitle {
margin-left: 1em;
}
div#secondHead { margin-top: 1em; margin-bottom: 4em; }
div#backlink { float: left; }
div#rsslink {
float: right;
text-align: left;
font-size: 100%;
text-align: right;
}
div.clearfloats { clear: both; }
div.content {
border: 1px solid #aaa;
background: white;
padding: 1em;
width: 700px;
margin-right: auto;
margin-left: auto;
}
.surroundtext { float: right; color: red; }
.cldate {
border-bottom: 1px solid gray;
margin-bottom: 1em;
font-size: 120%;
}
.cldate A {
text-decoration: none;
color: gray;
}
h3 { font-size: 100%; }
.cl { padding: 0.5em 0; font-size: 90%; }
.cl ul li { line-height: 150%; margin-bottom: 1.0em; }
.cllink { font-size: 90%; color: gray; }
div#appjetfooter { width: 700px; margin-left: auto; margin-right: auto; padding: 0 1em; }
code { font-family: monospace; font-size: 9pt; }
pre {
line-height: 120%;
font: 8pt monospace;
padding: 8px;
border: 1px dashed #999;
background-color: #fff3cc;
}