commit:80ad774f55998cc11e08694245dfd6cb1b49a86f
author:Chip Black
committer:Chip Black
date:Mon Apr 23 19:33:23 2012 -0700
parents:a579fec94d5058776ea0fb6f47f922ea48ff876f
Add Tag handler. Also convert all tabs to spaces.
diff --git a/www/js/blerg.js b/www/js/blerg.js
line changes: +0/-169
index fac5371..2125c5b
--- a/www/js/blerg.js
+++ b/www/js/blerg.js
@@ -14,89 +14,6 @@ var latestRecordsTemplate = new Template(
     '<div class="record"><a class="author ref" href="' + baseURL + '/\##{author}" onclick="return qlink()">@#{author}</a> #{data}</div>'
 );
 
-// Page elements
-var items;
-
-// Other globals
-var currentPager;
-var loginStatus;
-
-// Object to keep track of login status
-function LoginStatus() {
-    var cookies = {};
-    document.cookie.split(/;\s+/).each(function(v) {
-        kv = v.split('=');
-        cookies[kv[0]] = kv[1];
-    });
-    if (cookies.auth && cookies.username) {
-        this.loggedIn = true;
-        this.username = cookies.username;
-        this.requestFeedStatus();
-        this.feedStatusUpdateInterval = setInterval(this.requestFeedStatus.bind(this), 900000);
-    } else {
-        this.loggedIn = false;
-        this.username = null;
-    }
-    this.update();
-}
-
-LoginStatus.prototype.login = function(username, password) {
-    new Ajax.Request(baseURL + '/login', {
-        parameters: {
-            username: username,
-            password: password
-        },
-        onSuccess: function(r) {
-            var j = r.responseText.evalJSON();
-            if (j && j.status == 'success') {
-                this.loggedIn = true;
-                this.username = username;
-                document.cookie = "username=" + username;
-                $('login.password').value = '';
-                this.requestFeedStatus();
-                this.feedStatusUpdateInterval = setInterval(this.requestFeedStatus.bind(this), 900000);
-                this.update();
-            } else {
-                alert("Could not log in");
-                $('login.username').focus();
-            }
-        }.bind(this),
-        onFailure: function(r) {
-            alert("Could not log in");
-            $('login.username').focus();
-        }
-    });
-}
-
-LoginStatus.prototype.logout = function() {
-    new Ajax.Request(baseURL + '/logout', {
-        parameters: {
-            username: this.username
-        },
-        onComplete: function(r) {
-            this.loggedIn = false;
-            document.cookie = "auth=; expires=1-Jan-1970 00:00:00 GMT";
-            this.update();
-            clearInterval(this.feedStatusUpdateInterval);
-        }.bind(this)
-    });
-    document.cookie = "username=; expires=1-Jan-1970 00:00:00 GMT";
-}
-
-LoginStatus.prototype.update = function() {
-    if (this.loggedIn) {
-        $('userlink').href = baseURL + '/#' + this.username;
-        $('userlink').update('@' + this.username);
-        $('reflink').href = baseURL + '/#/ref/' + this.username;
-        $('login').hide();
-        $('logout').show();
-    } else {
-        $('post').hide();
-        $('login').show();
-        $('logout').hide();
-    }
-}
-
 LoginStatus.prototype.post = function(msg) {
     if (!this.loggedIn) {
         alert("You are not logged in!");
@@ -128,20 +45,6 @@ LoginStatus.prototype.post = function(msg) {
     });
 }
 
-LoginStatus.prototype.requestFeedStatus = function() {
-    new Ajax.Request('/feedinfo', {
-        parameters: { username: this.username },
-        onSuccess: function(r) {
-            var j = r.responseText.evalJSON();
-            if (j['new'] > 0) {
-                $('newFeedMessages').update('(' + j['new'] + ' new)');
-            } else {
-                $('newFeedMessages').update('');
-            }
-        }
-    });
-}
-
 // Base object for paged data
 function Pager() {
     this.itemsPerPage = 10;
@@ -245,78 +148,6 @@ function displayError(msg) {
     items.innerText = msg;
 }
 
-
-// Object for browsing tags
-function Tag(m) {
-    Pager.call(this);
-    this.type = m[1];
-    this.tag = m[2];
-    this.pageStart = parseInt(m[3]);
-
-    var url = baseURL + "/tag/";
-    switch(this.type) {
-    case 'tag':
-        //url += '%23';
-        url += 'H';  // apache is eating the hash, even encoded.  Probably a security feature.
-        this.baseFrag = '/tag/' + this.tag;
-        break;
-    case 'ref':
-        url += '%40';
-        this.baseFrag = '/ref/' + this.tag;
-        break;
-    default:
-        alert('Invalid tag type: ' + this.type);
-        return;
-    }
-    url += this.tag;
-
-    new Ajax.Request(url, {
-        method: 'get',
-        onSuccess: function(r) {
-            var j = r.responseText.evalJSON();
-            if (j) {
-                var maxid = j.length - 1;
-                j.each(function(v, i) {
-                    v.id = maxid - i;
-                    mangleRecord(v, tagRecordTemplate)
-                });
-                this.addItems(j);
-                if (!this.pageStart)
-                    this.pageStart = j.length - 1;
-                this.itemCount = j.length;
-            }
-            this.displayItems();
-        }.bind(this),
-        onFailure: function(r) {
-            this.displayItems();
-        }.bind(this)
-    });
-
-}
-Tag.prototype = new Pager();
-Tag.prototype.constructor = Tag;
-
-Tag.prototype.updateState = function(m) {
-    if (this.type != m[1] || this.tag != m[2])
-        return false;
-
-    this.pageStart = parseInt(m[3]) || this.itemCount - 1;
-    this.displayItems();
-
-    return true;
-}
-
-Tag.prototype.show = function() {
-    Pager.prototype.show.call(this);
-
-    var ctype = {ref: '@', tag: '#'}[this.type];
-
-    $$('[name=section]').each(function(v) {
-        v.update(' about ' + ctype + this.tag);
-    }.bind(this));
-}
-
-
 // Pager for browsing subscription feeds
 function Feed(m) {
     Pager.call(this);

diff --git a/www/jssrc/blerg/API.js b/www/jssrc/blerg/API.js
line changes: +162/-130
index 072eec5..1370234
--- a/www/jssrc/blerg/API.js
+++ b/www/jssrc/blerg/API.js
@@ -3,137 +3,169 @@ var baseURL = '';
 // The API state is static so that any instance can use login-dependent API
 // calls
 enyo.kind({
-	name: "blerg.API",
-	kind: "Component",
-	statics: {
-		apiInitialized: false,
-		loggedIn: false,
-		username: "",
-	},
-	create: function() {
-		this.inherited(arguments);
-		if (blerg.API.apiInitialized) {
-			if (blerg.API.loggedIn) {
-				setTimeout(function() {
-					this.bubble('onLoginSuccessful', {username: blerg.API.username});
-				}.bind(this), 0);
-			}
-			return;
-		}
+    name: "blerg.API",
+    kind: "Component",
+    statics: {
+        apiInitialized: false,
+        loggedIn: false,
+        username: "",
+    },
+    create: function() {
+        this.inherited(arguments);
+        if (blerg.API.apiInitialized) {
+            if (blerg.API.loggedIn) {
+                setTimeout(function() {
+                    this.bubble('onLoginSuccessful', {username: blerg.API.username});
+                }.bind(this), 0);
+            }
+            return;
+        }
 
-		if (enyo.getCookie('auth') && enyo.getCookie('username')) {
-			blerg.API.loggedIn = true;
-			blerg.API.username = enyo.getCookie('username');
-			// Defer the signal until everything's initialized
-			setTimeout(function() {
-				this.bubble('onLoginSuccessful', {username: blerg.API.username});
-			}.bind(this), 0);
-		}
+        if (enyo.getCookie('auth') && enyo.getCookie('username')) {
+            blerg.API.loggedIn = true;
+            blerg.API.username = enyo.getCookie('username');
+            // Defer the signal until everything's initialized
+            setTimeout(function() {
+                this.bubble('onLoginSuccessful', {username: blerg.API.username});
+            }.bind(this), 0);
+        }
 
-		blerg.API.apiInitialized = true;
-	},
-	login: function(username, password) {
-		var req = new enyo.Ajax({
-			url: baseURL + '/login',
-			method: 'POST'
-		});
-		req.response(function(inSender, inResponse) {
-			if (inResponse.status == 'success') {
-				blerg.API.loggedIn = true;
-				blerg.API.username = username;
-				enyo.setCookie('username', username);
-				this.bubble('onLoginSuccessful', {username: username});
-			} else {
-				enyo.setCookie('username', '', {"Max-Age": 0});
-				this.bubble('onLoginFailed');
-			}
-		}.bind(this));
-		req.go({
-			username: username,
-			password: password
-		});
-	},
-	logout: function() {
-		var req = new enyo.Ajax({
-			url: baseURL + '/logout',
-			method: 'POST'
-		});
-		req.response(function(inSender, inResponse) {
-			blerg.API.loggedIn = false;
-			enyo.setCookie('auth', '', {"Max-Age": 0});
-			this.bubble('onLogoutSuccessful');
-		}.bind(this));
-		req.go({
-			username: blerg.API.username
-		});
-		enyo.setCookie('username', '', {"Max-Age": 0});
-	},
-	requestFeedStatus: function() {
-		if (!blerg.API.loggedIn)
-			throw new Error('Cannot request feed status when not logged in');
-		// TODO
-	},
-	loadUserRecords: function(username, from ,to) {
-		var url;
-		if (from != undefined && to != undefined) {
-			url = baseURL +  '/get/' + username + '/' + from + '-' + to;
-		} else {
-			url = baseURL +  '/get/' + username;
-		}
+        blerg.API.apiInitialized = true;
+    },
+    login: function(username, password) {
+        var req = new enyo.Ajax({
+            url: baseURL + '/login',
+            method: 'POST'
+        });
+        req.response(function(inSender, inResponse) {
+            if (inResponse.status == 'success') {
+                blerg.API.loggedIn = true;
+                blerg.API.username = username;
+                enyo.setCookie('username', username);
+                this.bubble('onLoginSuccessful', {username: username});
+            } else {
+                enyo.setCookie('username', '', {"Max-Age": 0});
+                this.bubble('onLoginFailed');
+            }
+        }.bind(this));
+        req.go({
+            username: username,
+            password: password
+        });
+    },
+    logout: function() {
+        var req = new enyo.Ajax({
+            url: baseURL + '/logout',
+            method: 'POST'
+        });
+        req.response(function(inSender, inResponse) {
+            blerg.API.loggedIn = false;
+            enyo.setCookie('auth', '', {"Max-Age": 0});
+            this.bubble('onLogoutSuccessful');
+        }.bind(this));
+        req.go({
+            username: blerg.API.username
+        });
+        enyo.setCookie('username', '', {"Max-Age": 0});
+    },
+    requestFeedStatus: function() {
+        if (!blerg.API.loggedIn)
+            throw new Error('Cannot request feed status when not logged in');
+        // TODO
+    },
+    loadUserRecords: function(username, from ,to) {
+        var url;
+        if (from != undefined && to != undefined) {
+            url = baseURL +  '/get/' + username + '/' + from + '-' + to;
+        } else {
+            url = baseURL +  '/get/' + username;
+        }
 
-		var req = new enyo.Ajax({
-			url: url
-		});
-		req.response(function(inSender, inResponse) {
-			this.bubble('onItemsLoaded', {
-				entries: inResponse
-			});
-		}.bind(this));
-		req.go();
-	},
-	getSubscriptionStatus: function(username) {
-		var req = new enyo.Ajax({
-			url: baseURL + '/feedinfo/' + username,
-			method: 'POST'
-		});
-		req.response(function(inSender, inResponse) {
-			this.bubble('onSubscriptionStatus', {
-				username: username,
-				subscribed: inResponse.subscribed
-			});
-		}.bind(this));
-		req.go({
-			username: blerg.API.username
-		});
-	},
-	subscribe: function(username) {
-		var req = new enyo.Ajax({
-			url: baseURL + '/subscribe/' + username,
-			method: 'POST'
-		});
-		req.response(function(inSender, inResponse) {
-			this.bubble('onSubscriptionStatus', {
-				username: username,
-				subscribed: inResponse.status == "success"
-			});
-		}.bind(this));
-		req.go({
-			username: blerg.API.username
-		});
-	},
-	unsubscribe: function(username) {
-		var req = new enyo.Ajax({
-			url: baseURL + '/unsubscribe/' + username,
-			method: 'POST'
-		});
-		req.response(function(inSender, inResponse) {
-			this.bubble('onSubscriptionStatus', {
-				username: username,
-				subscribed: inResponse.status != "success"
-			});
-		}.bind(this));
-		req.go({
-			username: blerg.API.username
-		});
-	},
+        var req = new enyo.Ajax({
+            url: url
+        });
+        req.response(function(inSender, inResponse) {
+            this.bubble('onItemsLoaded', {
+                type: 'user',
+                username: username,
+                from: from,
+                to: to,
+                entries: inResponse
+            });
+        }.bind(this));
+        req.go();
+    },
+    loadTagRecords: function(type, tag) {
+        var url;
+        switch(type) {
+            case 'tag':
+                // Apache eats the hash, even encoded.  Probably a security
+                // feature.
+                url = baseURL + '/tag/H' + tag;
+                break;
+            case 'ref':
+                url = baseURL + '/tag/%40' + tag;
+                break;
+            default:
+                throw new Error("Invalid tag type: " + type);
+                return;
+        }
+        var req = new enyo.Ajax({
+            url: url
+        });
+        req.response(function(inSender, inResponse) {
+            this.bubble('onItemsLoaded', {
+                type: 'tag',
+                tagType: type,
+                tag: tag,
+                entries: inResponse
+            });
+        }.bind(this));
+        req.go();
+    },
+    getSubscriptionStatus: function(username) {
+        var req = new enyo.Ajax({
+            url: baseURL + '/feedinfo/' + username,
+            method: 'POST'
+        });
+        req.response(function(inSender, inResponse) {
+            this.bubble('onSubscriptionStatus', {
+                username: username,
+                subscribed: inResponse.subscribed
+            });
+        }.bind(this));
+        req.go({
+            username: blerg.API.username
+        });
+    },
+    subscribe: function(username) {
+        var req = new enyo.Ajax({
+            url: baseURL + '/subscribe/' + username,
+            method: 'POST'
+        });
+        req.response(function(inSender, inResponse) {
+            this.bubble('onSubscriptionStatus', {
+                username: username,
+                subscribed: inResponse.status == "success"
+            });
+        }.bind(this));
+        req.go({
+            username: blerg.API.username
+        });
+    },
+    unsubscribe: function(username) {
+        var req = new enyo.Ajax({
+            url: baseURL + '/unsubscribe/' + username,
+            method: 'POST'
+        });
+        req.response(function(inSender, inResponse) {
+            this.bubble('onSubscriptionStatus', {
+                username: username,
+                subscribed: inResponse.status != "success"
+            });
+        }.bind(this));
+        req.go({
+            username: blerg.API.username
+        });
+    },
 });

diff --git a/www/jssrc/blerg/Blerg.js b/www/jssrc/blerg/Blerg.js
line changes: +95/-95
index 52de4e3..f40a277
--- a/www/jssrc/blerg/Blerg.js
+++ b/www/jssrc/blerg/Blerg.js
@@ -1,102 +1,102 @@
 enyo.kind({
-	name: "blerg.Blerg",
-	kind: "Control",
-	lastHash: null,
-	handlers: {
-		onStartSignup: "showSignupDialog",
-		onTryLogin: "tryLogin",
-		onTryLogout: "tryLogout",
-		onSetTitle: "setTitle",
-		onPostVisibility: "postVisibilityUpdate"
-	},
-	components: [
-		{classes: "blerg-header", components: [
-			{name: "title", kind: "blerg.Title"},
-			{name: "controls", kind: "blerg.Controls"},
-			{style: "clear: both"},
-			{name: "post", kind: "blerg.Post", showing: false},
-			{name: "help", kind: "blerg.Help"}
-		]},
-		{name: "main", kind: "blerg.Main"},
-		{name: "signupDialog", kind: "blerg.SignupDialog"},
-		{name: "passwdDialog", kind: "blerg.PasswdDialog"},
-		{name: "api", kind: "blerg.API",
-		 onLoginSuccessful: "loginSuccessful",
-		 onLoginFailed: "loginFailed",
-		 onLogoutSuccessful: "logout"}
-	],
-	urlmap: [
-		['search', /^\?post\/([^/]+)\/(.+)/, "blerg.ExternalURLPost"],
-		['hash',   /^#\/(ref|tag)\/([A-Za-z0-9_-]+)(?:\/p(\d+))?$/, "blerg.Tag"],
-		['hash',   /^#\/feed(?:\/p(\d+))?$/, "blerg.Feed"],
-		['hash',   /^#([A-Za-z0-9_-]+)(?:\/(p)?(\d+))?$/, "blerg.User"]
-	],
-	pathHandlers: [ blerg.User, blerg.Welcome ],
-	rendered: function() {
-		this.inherited(arguments);
+    name: "blerg.Blerg",
+    kind: "Control",
+    lastHash: null,
+    handlers: {
+        onStartSignup: "showSignupDialog",
+        onTryLogin: "tryLogin",
+        onTryLogout: "tryLogout",
+        onSetTitle: "setTitle",
+        onPostVisibility: "postVisibilityUpdate"
+    },
+    components: [
+        {classes: "blerg-header", components: [
+            {name: "title", kind: "blerg.Title"},
+            {name: "controls", kind: "blerg.Controls"},
+            {style: "clear: both"},
+            {name: "post", kind: "blerg.Post", showing: false},
+            {name: "help", kind: "blerg.Help"}
+        ]},
+        {name: "main", kind: "blerg.Main"},
+        {name: "signupDialog", kind: "blerg.SignupDialog"},
+        {name: "passwdDialog", kind: "blerg.PasswdDialog"},
+        {name: "api", kind: "blerg.API",
+         onLoginSuccessful: "loginSuccessful",
+         onLoginFailed: "loginFailed",
+         onLogoutSuccessful: "logout"}
+    ],
+    urlmap: [
+        ['search', /^\?post\/([^/]+)\/(.+)/, "blerg.ExternalURLPost"],
+        ['hash',   /^#\/(ref|tag)\/([A-Za-z0-9_-]+)(?:\/p(\d+))?$/, "blerg.Tag"],
+        ['hash',   /^#\/feed(?:\/p(\d+))?$/, "blerg.Feed"],
+        ['hash',   /^#([A-Za-z0-9_-]+)(?:\/(p)?(\d+))?$/, "blerg.User"]
+    ],
+    pathHandlers: [ blerg.User, blerg.Tag, blerg.Welcome ],
+    rendered: function() {
+        this.inherited(arguments);
 
-		this.lastHash = location.hash;
-		this.urlSwitch();
+        this.lastHash = location.hash;
+        this.urlSwitch();
 
-		setInterval(this.hashCheck.bind(this), 250);
+        setInterval(this.hashCheck.bind(this), 250);
 
-		document.body.addEventListener('keyup', function(event) {
-			if (event.shiftKey && event.keyCode == 32) {
-				this.$.post.show();
-				event.stopPropagation();
-			}
-		}, false);
-	},
-	hashCheck: function() {
-		if (location.hash != this.lastHash) {
-			this.lastHash = location.hash;
-			this.urlSwitch();
-		}
-	},
-	urlSwitch: function() {
-		var m;
-		var objdef = null;
+        document.body.addEventListener('keyup', function(event) {
+            if (event.shiftKey && event.keyCode == 32) {
+                this.$.post.show();
+                event.stopPropagation();
+            }
+        }, false);
+    },
+    hashCheck: function() {
+        if (location.hash != this.lastHash) {
+            this.lastHash = location.hash;
+            this.urlSwitch();
+        }
+    },
+    urlSwitch: function() {
+        var m;
+        var objdef = null;
 
-		for (var i = 0; i < this.pathHandlers.length; i++) {
-			var handler = this.pathHandlers[i];
-			objdef = handler.locationDetect(window.location);
-			if (objdef)
-				break;
-		}
-		if (!objdef)
-			objdef = {classes: "blerg-error", content: "No handler found"}
+        for (var i = 0; i < this.pathHandlers.length; i++) {
+            var handler = this.pathHandlers[i];
+            objdef = handler.locationDetect(window.location);
+            if (objdef)
+                break;
+        }
+        if (!objdef)
+            objdef = {classes: "blerg-error", content: "No handler found"}
 
-		this.$.main.updateView(objdef);
-	},
-	showSignupDialog: function() {
-		this.$.signupDialog.show();
-	},
-	setTitle: function(inSender, inEvent) {
-		this.$.title.waterfall('onSetTitle', inEvent);
-	},
-	tryLogin: function(inSender, inEvent) {
-		this.$.api.login(inEvent.username, inEvent.password);
-	},
-	tryLogout: function(inSender, inEvent) {
-		this.$.api.logout();
-	},
-	loginSuccessful: function(inSender, inEvent) {
-		this.$.api.requestFeedStatus();
-		this.feedStatusUpdateInterval = setInterval(function() {
-			this.$.api.requestFeedStatus();
-		}.bind(this), 900000);
-		this.waterfall('onLogin', inEvent);
-	},
-	loginFailed: function(inSender, inEvent) {
-		alert('Login failed');
-		this.logout();
-	},
-	logout: function(inSender, inEvent) {
-		clearInterval(this.feedStatusUpdateInterval);
-		this.waterfall('onLogout');
-	},
-	postVisibilityUpdate: function(inSender, inEvent) {
-		this.$.post.waterfall('onPostVisibility', inEvent);
-		this.$.controls.waterfall('onPostVisibility', inEvent);
-	}
+        this.$.main.updateView(objdef);
+    },
+    showSignupDialog: function() {
+        this.$.signupDialog.show();
+    },
+    setTitle: function(inSender, inEvent) {
+        this.$.title.waterfall('onSetTitle', inEvent);
+    },
+    tryLogin: function(inSender, inEvent) {
+        this.$.api.login(inEvent.username, inEvent.password);
+    },
+    tryLogout: function(inSender, inEvent) {
+        this.$.api.logout();
+    },
+    loginSuccessful: function(inSender, inEvent) {
+        this.$.api.requestFeedStatus();
+        this.feedStatusUpdateInterval = setInterval(function() {
+            this.$.api.requestFeedStatus();
+        }.bind(this), 900000);
+        this.waterfall('onLogin', inEvent);
+    },
+    loginFailed: function(inSender, inEvent) {
+        alert('Login failed');
+        this.logout();
+    },
+    logout: function(inSender, inEvent) {
+        clearInterval(this.feedStatusUpdateInterval);
+        this.waterfall('onLogout');
+    },
+    postVisibilityUpdate: function(inSender, inEvent) {
+        this.$.post.waterfall('onPostVisibility', inEvent);
+        this.$.controls.waterfall('onPostVisibility', inEvent);
+    }
 });

diff --git a/www/jssrc/blerg/Controls.js b/www/jssrc/blerg/Controls.js
line changes: +95/-95
index f23f63f..1bbfd2d
--- a/www/jssrc/blerg/Controls.js
+++ b/www/jssrc/blerg/Controls.js
@@ -1,97 +1,97 @@
 enyo.kind({
-	name: "blerg.Controls",
-	kind: "Control",
-	classes: "blerg-controls",
-	username: null,
-	postShowing: false,
-	published: {
-		loggedIn: false
-	},
-	handlers: {
-		onLogin: "login",
-		onLogout: "logout",
-		onPostVisibility: "postVisibilityUpdate"
-	},
-	components: [
-		{name: "loggedOutControls", components: [
-			{tag: "form", onsubmit: "loginClicked", classes: "login", components: [
-				{kind: "onyx.Groupbox", components: [
-					{kind: "onyx.InputDecorator", components: [
-						{name: "username", kind: "onyx.Input", placeholder: "Username"}
-					]},
-					{kind: "onyx.InputDecorator", components: [
-						{name: "password", kind: "onyx.Input", placeholder: "Password", type: "password"}
-					]},
-				]},
-				{kind: "onyx.Button", content: "Login", onclick: "loginClicked"}
-			]}
-		]},
-		{name: "loggedInControls", showing: false, components: [
-			{name: "greeting", components: [
-				{noDom: true, content: "Hello, "},
-				{name: "userlink", tag: "a"},
-				{noDom: true, content: ". "},
-				{kind: "blerg.Link", content: "Logout", onNavigate: "logoutClicked"},
-				{noDom: true, content: "."}
-			]},
-			{components: [
-				{name: "rssButton", kind: "blerg.Link", showing: false, components: [
-					{kind: "Image", src: "/images/rss.png", width: 16, height: 16},
-					{noDom: true, content: " RSS"}
-				]}
-			]},
-			{classes: "blerg-controls-toolbar", components: [
-				{kind: "onyx.Button", content: "Feed Your Vanity", onclick: "chatterClicked"},
-				{kind: "onyx.Button", content: "Stalk Your Victims", onclick: "feedClicked"},
-				{name: "spewButton", kind: "onyx.Button", classes: "spew-button", content: "Spew It!", onclick: "spewToggle"}
-			]},
-		]}
-	],
-	showRSS: function(url) {
-		this.$.rssButton.show();
-		if (url)
-			this.$.rssButton.setAttribute('href', url);
-	},
-	hideRSS: function() {
-		this.$.rssButton.hide();
-	},
-	loggedInChanged: function() {
-		if (this.loggedIn) {
-			this.$.loggedOutControls.hide();
-			this.$.loggedInControls.show();
-		} else {
-			this.$.loggedOutControls.show();
-			this.$.loggedInControls.hide();
-		}
-	},
-	loginClicked: function(inSender, inEvent) {
-		this.bubble('onTryLogin', {
-			username: this.$.username.getValue(),
-			password: this.$.password.getValue()
-		});
-		inEvent.preventDefault();
-		return true;
-	},
-	logoutClicked: function() {
-		this.bubble('onTryLogout');
-		return true;
-	},
-	login: function(inSender, inEvent) {
-		this.$.password.setValue('');
-		this.setLoggedIn(true);
-		this.$.userlink.setAttribute('href', '/#' + inEvent.username);
-		this.$.userlink.setContent('@' + inEvent.username);
-		this.username = inEvent.username;
-	},
-	logout: function(inSender, inEvent) {
-		this.setLoggedIn(false);
-	},
-	spewToggle: function(inSender, inEvent) {
-		this.postShowing = !this.postShowing;
-		this.bubble('onPostVisibility', {showing: this.postShowing});
-	},
-	postVisibilityUpdate: function(inSender, inEvent) {
-		this.postShowing = inEvent.showing;
-		this.$.spewButton.addRemoveClass('active', inEvent.showing);
-	}
+    name: "blerg.Controls",
+    kind: "Control",
+    classes: "blerg-controls",
+    username: null,
+    postShowing: false,
+    published: {
+        loggedIn: false
+    },
+    handlers: {
+        onLogin: "login",
+        onLogout: "logout",
+        onPostVisibility: "postVisibilityUpdate"
+    },
+    components: [
+        {name: "loggedOutControls", components: [
+            {tag: "form", onsubmit: "loginClicked", classes: "login", components: [
+                {kind: "onyx.Groupbox", components: [
+                    {kind: "onyx.InputDecorator", components: [
+                        {name: "username", kind: "onyx.Input", placeholder: "Username"}
+                    ]},
+                    {kind: "onyx.InputDecorator", components: [
+                        {name: "password", kind: "onyx.Input", placeholder: "Password", type: "password"}
+                    ]},
+                ]},
+                {kind: "onyx.Button", content: "Login", onclick: "loginClicked"}
+            ]}
+        ]},
+        {name: "loggedInControls", showing: false, components: [
+            {name: "greeting", components: [
+                {noDom: true, content: "Hello, "},
+                {name: "userlink", tag: "a"},
+                {noDom: true, content: ". "},
+                {kind: "blerg.Link", content: "Logout", onNavigate: "logoutClicked"},
+                {noDom: true, content: "."}
+            ]},
+            {components: [
+                {name: "rssButton", kind: "blerg.Link", showing: false, components: [
+                    {kind: "Image", src: "/images/rss.png", width: 16, height: 16},
+                    {noDom: true, content: " RSS"}
+                ]}
+            ]},
+            {classes: "blerg-controls-toolbar", components: [
+                {kind: "onyx.Button", content: "Feed Your Vanity", onclick: "chatterClicked"},
+                {kind: "onyx.Button", content: "Stalk Your Victims", onclick: "feedClicked"},
+                {name: "spewButton", kind: "onyx.Button", classes: "spew-button", content: "Spew It!", onclick: "spewToggle"}
+            ]},
+        ]}
+    ],
+    showRSS: function(url) {
+        this.$.rssButton.show();
+        if (url)
+            this.$.rssButton.setAttribute('href', url);
+    },
+    hideRSS: function() {
+        this.$.rssButton.hide();
+    },
+    loggedInChanged: function() {
+        if (this.loggedIn) {
+            this.$.loggedOutControls.hide();
+            this.$.loggedInControls.show();
+        } else {
+            this.$.loggedOutControls.show();
+            this.$.loggedInControls.hide();
+        }
+    },
+    loginClicked: function(inSender, inEvent) {
+        this.bubble('onTryLogin', {
+            username: this.$.username.getValue(),
+            password: this.$.password.getValue()
+        });
+        inEvent.preventDefault();
+        return true;
+    },
+    logoutClicked: function() {
+        this.bubble('onTryLogout');
+        return true;
+    },
+    login: function(inSender, inEvent) {
+        this.$.password.setValue('');
+        this.setLoggedIn(true);
+        this.$.userlink.setAttribute('href', '/#' + inEvent.username);
+        this.$.userlink.setContent('@' + inEvent.username);
+        this.username = inEvent.username;
+    },
+    logout: function(inSender, inEvent) {
+        this.setLoggedIn(false);
+    },
+    spewToggle: function(inSender, inEvent) {
+        this.postShowing = !this.postShowing;
+        this.bubble('onPostVisibility', {showing: this.postShowing});
+    },
+    postVisibilityUpdate: function(inSender, inEvent) {
+        this.postShowing = inEvent.showing;
+        this.$.spewButton.addRemoveClass('active', inEvent.showing);
+    }
 });

diff --git a/www/jssrc/blerg/Help.js b/www/jssrc/blerg/Help.js
line changes: +4/-4
index 130b799..27f5a43
--- a/www/jssrc/blerg/Help.js
+++ b/www/jssrc/blerg/Help.js
@@ -1,6 +1,6 @@
 enyo.kind({
-	name: "blerg.Help",
-	kind: "Control",
-	components: [
-	]
+    name: "blerg.Help",
+    kind: "Control",
+    components: [
+    ]
 });

diff --git a/www/jssrc/blerg/Link.js b/www/jssrc/blerg/Link.js
line changes: +23/-23
index b2468bf..36cfb30
--- a/www/jssrc/blerg/Link.js
+++ b/www/jssrc/blerg/Link.js
@@ -1,25 +1,25 @@
 enyo.kind({
-	name: "blerg.Link",
-	kind: "Control",
-	tag: "a",
-	published: {
-		href: "#",
-	},
-	handlers: {
-		onclick: "muteLink",
-	},
-	create: function() {
-		this.inherited(arguments);
-		this.hrefChanged();
-	},
-	hrefChanged: function() {
-		this.setAttribute('href', this.href);
-	},
-	muteLink: function(inSender, inEvent) {
-		inEvent.preventDefault();
-		if (this.href != '#')
-			location.href = this.href;
-		this.bubble('onNavigate');
-		return true;
-	}
+    name: "blerg.Link",
+    kind: "Control",
+    tag: "a",
+    published: {
+        href: "#",
+    },
+    handlers: {
+        onclick: "muteLink",
+    },
+    create: function() {
+        this.inherited(arguments);
+        this.hrefChanged();
+    },
+    hrefChanged: function() {
+        this.setAttribute('href', this.href);
+    },
+    muteLink: function(inSender, inEvent) {
+        inEvent.preventDefault();
+        if (this.href != '#')
+            location.href = this.href;
+        this.bubble('onNavigate');
+        return true;
+    }
 });

diff --git a/www/jssrc/blerg/Main.js b/www/jssrc/blerg/Main.js
line changes: +22/-22
index 757779d..686b86c
--- a/www/jssrc/blerg/Main.js
+++ b/www/jssrc/blerg/Main.js
@@ -1,24 +1,24 @@
 enyo.kind({
-	name: "blerg.Main",
-	kind: "Control",
-	classes: "blerg-main",
-	currentView: null,
-	updateView: function(objdef) {
-		if (!this.currentView || objdef.kind != this.currentView.kind) {
-			this.destroyComponents();
-			this.currentView = this.createComponent(objdef);
-			this.currentView.render();
-		} else {
-			// We don't want to invoke any change handlers until
-			// these are all set, so we do that ourselves
-			for (var i in objdef) {
-				if (i == 'kind') continue;
-				this.currentView[i] = objdef[i];
-			}
-			for (var i in objdef) {
-				if (i == 'kind') continue;
-				this.currentView[i + 'Changed'] && this.currentView[i + 'Changed']();
-			}
-		}
-	}
+    name: "blerg.Main",
+    kind: "Control",
+    classes: "blerg-main",
+    currentView: null,
+    updateView: function(objdef) {
+        if (!this.currentView || objdef.kind != this.currentView.kind) {
+            this.destroyComponents();
+            this.currentView = this.createComponent(objdef);
+            this.currentView.render();
+        } else {
+            // We don't want to invoke any change handlers until
+            // these are all set, so we do that ourselves
+            for (var i in objdef) {
+                if (i == 'kind') continue;
+                this.currentView[i] = objdef[i];
+            }
+            for (var i in objdef) {
+                if (i == 'kind') continue;
+                this.currentView[i + 'Changed'] && this.currentView[i + 'Changed']();
+            }
+        }
+    }
 });

diff --git a/www/jssrc/blerg/Pager.js b/www/jssrc/blerg/Pager.js
line changes: +30/-33
index 9ae703b..2e27177
--- a/www/jssrc/blerg/Pager.js
+++ b/www/jssrc/blerg/Pager.js
@@ -1,36 +1,33 @@
 enyo.kind({
-	name: "blerg.Pager",
-	kind: "Control",
-	listKind: "Control",
-	lastRecord: null,
-	components: [
-		{name: "records"},
-		{name: "loadMoreButton", kind: "onyx.Button", onclick: "loadMore", content: "Load More"}
-	],
-	addItems: function(items) {
-		this.$.records.createComponents(items, {kind: this.listKind});
-		for (var i = 0; i < items.length; i++) {
-			var r = parseInt(items[i].record);
-			if (r < this.lastRecord || this.lastRecord == null)
-				this.lastRecord = r;
-		}
-		this.$.records.render();
-		if (this.lastRecord == 0)
-			this.$.loadMoreButton.hide();
-	},
-	loadMore: function() {
-		if (this.lastRecord == 0)
-			return;
+    name: "blerg.Pager",
+    kind: "Control",
+    listKind: "Control",
+    lastRecord: null,
+    components: [
+        {name: "records"}
+    ],
+    addItems: function(items) {
+        this.$.records.createComponents(items, {kind: this.listKind});
+        for (var i = 0; i < items.length; i++) {
+            var r = parseInt(items[i].record);
+            if (r < this.lastRecord || this.lastRecord == null)
+                this.lastRecord = r;
+        }
+        this.$.records.render();
+    },
+    loadMore: function() {
+        if (this.lastRecord == 0)
+            return;
 
-		if (this.lastRecord != null) {
-			var to = this.lastRecord - 1;
-			var from = this.lastRecord - 50;
-			if (from < 0)
-				from = 0;
-			this.loadItems(from, to);
-		} else {
-			this.loadItems();
-		}
-	},
-	loadItems: function(from, to) { }
+        if (this.lastRecord != null) {
+            var to = this.lastRecord - 1;
+            var from = this.lastRecord - 50;
+            if (from < 0)
+                from = 0;
+            this.loadItems(from, to);
+        } else {
+            this.loadItems();
+        }
+    },
+    loadItems: function(from, to) { }
 });

diff --git a/www/jssrc/blerg/PasswdDialog.js b/www/jssrc/blerg/PasswdDialog.js
line changes: +26/-26
index 4eed325..6144220
--- a/www/jssrc/blerg/PasswdDialog.js
+++ b/www/jssrc/blerg/PasswdDialog.js
@@ -1,28 +1,28 @@
 enyo.kind({
-	name: "blerg.PasswdDialog",
-	kind: "onyx.Popup",
-	classes: "blerg-dialog",
-	autoDismiss: true,
-	centered: true,
-	floating: true,
-	modal: true,
-	components: [
-		{tag: "h2", content: "Sign Up"},
-		{kind: "onyx.Groupbox", components: [
-			{kind: "onyx.InputDecorator", components: [
-				{name: "password1", kind: "onyx.Input", placeholder: "Username", type: "password"}
-			]},
-			{kind: "onyx.InputDecorator", components: [
-				{name: "password2", kind: "onyx.Input", placeholder: "Password", type: "password"}
-			]}
-		]},
-		{kind: "onyx.Button", content: "Change", onclick: "changeClick", classes: "onyx.affirmative"},
-		{kind: "onyx.Button", content: "Cancel", onclick: "cancelClick", classes: "onyx-negative"}
-	],
-	changeClick: function() {
-		// Do stuff
-	},
-	cancelClick: function() {
-		this.hide();
-	}
+    name: "blerg.PasswdDialog",
+    kind: "onyx.Popup",
+    classes: "blerg-dialog",
+    autoDismiss: true,
+    centered: true,
+    floating: true,
+    modal: true,
+    components: [
+        {tag: "h2", content: "Sign Up"},
+        {kind: "onyx.Groupbox", components: [
+            {kind: "onyx.InputDecorator", components: [
+                {name: "password1", kind: "onyx.Input", placeholder: "Username", type: "password"}
+            ]},
+            {kind: "onyx.InputDecorator", components: [
+                {name: "password2", kind: "onyx.Input", placeholder: "Password", type: "password"}
+            ]}
+        ]},
+        {kind: "onyx.Button", content: "Change", onclick: "changeClick", classes: "onyx.affirmative"},
+        {kind: "onyx.Button", content: "Cancel", onclick: "cancelClick", classes: "onyx-negative"}
+    ],
+    changeClick: function() {
+        // Do stuff
+    },
+    cancelClick: function() {
+        this.hide();
+    }
 });

diff --git a/www/jssrc/blerg/Post.js b/www/jssrc/blerg/Post.js
line changes: +58/-58
index 527ffc9..1f7ea7d
--- a/www/jssrc/blerg/Post.js
+++ b/www/jssrc/blerg/Post.js
@@ -1,60 +1,60 @@
 enyo.kind({
-	name: "blerg.Post",
-	kind: "Control",
-	classes: "blerg-post",
-	handlers: {
-		onPostVisibility: "postVisibilityUpdate"
-	},
-	resizePostContentTimeout: null,
-	components: [
-		{tag: "h2", content: "What's on your mind?"},
-		{kind: "onyx.InputDecorator", components: [
-			{name: "postContent", classes: "content", kind: "onyx.TextArea", onkeydown: "resizePostContent"}
-		]},
-		{classes: "buttons", components: [
-			{kind: "onyx.Button", content: "Close", onclick: "closePost", classes: "onyx-negative"},
-			{kind: "onyx.Button", content: "Post", onclick: "doPost", classes: "onyx-affirmative"}
-		]}
-	],
-	getData: function() {
-		return this.$.postContent.getValue();
-	},
-	setData: function(inVal) {
-		this.$.postContent.setValue(inVal);
-	},
-	closePost: function() {
-		this.bubble('onPostVisibility', {showing: false});
-	},
-	doPost: function() {
-		this.bubble('onPost', {data: this.getData()});
-	},
-	postVisibilityUpdate: function(inSender, inEvent) {
-		if (inEvent.showing) {
-			this.show();
-			if (inEvent.data && this.getData() == "")
-				this.setData(inEvent.data);
-		} else {
-			this.hide();
-		}
-	},
-	resizePostContent: function(inSender, inEvent) {
-		if (this.resizePostContentTimeout)
-			clearTimeout(this.resizePostContentTimeout);
-		this.resizePostContentTimeout = setTimeout(function() {
-			var n = this.$.postContent.hasNode();
-			if (!n)
-				return;
-			var c = this.getData();
-			var lines = Math.floor(c.length / (100 * (n.clientWidth / 1000))) + 1;
-			var m = c.match(/\r?\n/g);
-			if (m)
-				lines += m.length;
-			if (lines <= 3) {
-				this.$.postContent.setStyle("");
-			} else {
-				this.$.postContent.setStyle("height: " + (lines * 17) + "pt");
-			}
-			this.resizePostContentTimeout = null;
-		}.bind(this), 150);
-	}
+    name: "blerg.Post",
+    kind: "Control",
+    classes: "blerg-post",
+    handlers: {
+        onPostVisibility: "postVisibilityUpdate"
+    },
+    resizePostContentTimeout: null,
+    components: [
+        {tag: "h2", content: "What's on your mind?"},
+        {kind: "onyx.InputDecorator", components: [
+            {name: "postContent", classes: "content", kind: "onyx.TextArea", onkeydown: "resizePostContent"}
+        ]},
+        {classes: "buttons", components: [
+            {kind: "onyx.Button", content: "Close", onclick: "closePost", classes: "onyx-negative"},
+            {kind: "onyx.Button", content: "Post", onclick: "doPost", classes: "onyx-affirmative"}
+        ]}
+    ],
+    getData: function() {
+        return this.$.postContent.getValue();
+    },
+    setData: function(inVal) {
+        this.$.postContent.setValue(inVal);
+    },
+    closePost: function() {
+        this.bubble('onPostVisibility', {showing: false});
+    },
+    doPost: function() {
+        this.bubble('onPost', {data: this.getData()});
+    },
+    postVisibilityUpdate: function(inSender, inEvent) {
+        if (inEvent.showing) {
+            this.show();
+            if (inEvent.data && this.getData() == "")
+                this.setData(inEvent.data);
+        } else {
+            this.hide();
+        }
+    },
+    resizePostContent: function(inSender, inEvent) {
+        if (this.resizePostContentTimeout)
+            clearTimeout(this.resizePostContentTimeout);
+        this.resizePostContentTimeout = setTimeout(function() {
+            var n = this.$.postContent.hasNode();
+            if (!n)
+                return;
+            var c = this.getData();
+            var lines = Math.floor(c.length / (100 * (n.clientWidth / 1000))) + 1;
+            var m = c.match(/\r?\n/g);
+            if (m)
+                lines += m.length;
+            if (lines <= 3) {
+                this.$.postContent.setStyle("");
+            } else {
+                this.$.postContent.setStyle("height: " + (lines * 17) + "pt");
+            }
+            this.resizePostContentTimeout = null;
+        }.bind(this), 150);
+    }
 });

diff --git a/www/jssrc/blerg/Record.js b/www/jssrc/blerg/Record.js
line changes: +84/-66
index 7601de9..8fe0947
--- a/www/jssrc/blerg/Record.js
+++ b/www/jssrc/blerg/Record.js
@@ -1,74 +1,92 @@
 enyo.kind({
-	name: "blerg.Record",
-	kind: "Control",
-	classes: "record",
-	published: {
-		data: "",
-		timestamp: 0,
-		author: "",
-		record: null
-	},
-	components: [
-		{name: "data", noDom: true, allowHtml: true},
-		{classes: "info", components: [
-			{noDom: true, content: "Posted "},
-			{name: "date", noDom: true},
-			{noDom: true, content: ". "},
-			{name: "permalink", kind: "blerg.Link", content: "[permalink]"},
-			{noDom: true, content: " "},
-			{name: "reply", kind: "blerg.Link", content: "[reply]", onclick: "postPopup"}
-		]}
-	],
-	create: function() {
-		this.inherited(arguments);
-		this.dataChanged();
-		this.timestampChanged();
-		this.updateLinks();
-	},
-	dataChanged: function() {
-		this.$.data.setContent(blerg.Util.blergFormat(this.data));
-	},
-	timestampChanged: function() {
-		this.$.date.setContent(new Date(this.timestamp * 1000).toString());
-	},
-	authorChanged: function() {
-		this.updateLinks();
-	},
-	recordChanged: function() {
-		this.updateLinks();
-	},
-	updateLinks: function() {
-		this.$.permalink.setHref(baseURL + enyo.macroize("/#{$author}/{$record}", this));
-	},
-	postPopup: function() {
-		this.bubble('onPostVisibility', {
-			showing: true,
-			data: enyo.macroize("@{$author}/{$record}", this)
-		});
-		return true;
-	}
+    name: "blerg.Record",
+    kind: "Control",
+    classes: "record",
+    published: {
+        data: "",
+        timestamp: 0,
+        author: "",
+        record: null
+    },
+    components: [
+        {name: "data", noDom: true, allowHtml: true},
+        {classes: "info", components: [
+            {noDom: true, content: "Posted "},
+            {name: "date", noDom: true},
+            {noDom: true, content: ". "},
+            {name: "permalink", kind: "blerg.Link", content: "[permalink]"},
+            {noDom: true, content: " "},
+            {name: "reply", kind: "blerg.Link", content: "[reply]", onclick: "postPopup"}
+        ]}
+    ],
+    create: function() {
+        this.inherited(arguments);
+        this.dataChanged();
+        this.timestampChanged();
+        this.updateLinks();
+    },
+    dataChanged: function() {
+        this.$.data.setContent(blerg.Util.blergFormat(this.data));
+    },
+    timestampChanged: function() {
+        this.$.date.setContent(new Date(this.timestamp * 1000).toString());
+    },
+    authorChanged: function() {
+        this.updateLinks();
+    },
+    recordChanged: function() {
+        this.updateLinks();
+    },
+    updateLinks: function() {
+        this.$.permalink.setHref(baseURL + enyo.macroize("/#{$author}/{$record}", this));
+    },
+    postPopup: function() {
+        this.bubble('onPostVisibility', {
+            showing: true,
+            data: enyo.macroize("@{$author}/{$record}", this)
+        });
+        return true;
+    }
 });
 
 enyo.kind({
-	name: "blerg.TagRecord",
-	kind: "blerg.Record"
+    name: "blerg.TagRecord",
+    kind: "blerg.Record",
+    components: [
+        {name: "data", noDom: true, allowHtml: true},
+        {classes: "info", components: [
+            {noDom: true, content: "Posted by "},
+            {name: "author", kind: "blerg.Link", classes: "author ref"},
+            {noDom: true, content: " at "},
+            {name: "date", noDom: true},
+            {noDom: true, content: ". "},
+            {name: "permalink", kind: "blerg.Link", content: "[permalink]"},
+            {noDom: true, content: " "},
+            {name: "reply", kind: "blerg.Link", content: "[reply]", onclick: "postPopup"}
+        ]}
+    ],
+    updateLinks: function() {
+        this.inherited(arguments);
+        this.$.author.setContent('@' + this.author);
+        this.$.author.setHref('/#' + this.author);
+    }
 });
 
 enyo.kind({
-	name: "blerg.BriefRecord",
-	kind: "blerg.Record",
-	components: [
-		{name: "authorlink", kind: "blerg.Link", classes: "author ref"},
-		{noDom: true, content: " "},
-		{name: "data", noDom: true}
-	],
-	dataChanged: function() {
-		var d = this.data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
-		this.$.data.setContent(d);
-	},
-	timestampChanged: function() { },
-	updateLinks: function() {
-		this.$.authorlink.setHref(baseURL + '/#' + this.author);
-		this.$.authorlink.setContent('@' + this.author);
-	},
+    name: "blerg.BriefRecord",
+    kind: "blerg.Record",
+    components: [
+        {name: "authorlink", kind: "blerg.Link", classes: "author ref"},
+        {noDom: true, content: " "},
+        {name: "data", noDom: true}
+    ],
+    dataChanged: function() {
+        var d = this.data.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+        this.$.data.setContent(d);
+    },
+    timestampChanged: function() { },
+    updateLinks: function() {
+        this.$.authorlink.setHref(baseURL + '/#' + this.author);
+        this.$.authorlink.setContent('@' + this.author);
+    },
 });

diff --git a/www/jssrc/blerg/SignupDialog.js b/www/jssrc/blerg/SignupDialog.js
line changes: +26/-26
index c62108b..f51e526
--- a/www/jssrc/blerg/SignupDialog.js
+++ b/www/jssrc/blerg/SignupDialog.js
@@ -1,28 +1,28 @@
 enyo.kind({
-	name: "blerg.SignupDialog",
-	kind: "onyx.Popup",
-	classes: "blerg-dialog",
-	autoDismiss: true,
-	centered: true,
-	floating: true,
-	modal: true,
-	components: [
-		{tag: "h2", content: "Sign Up"},
-		{kind: "onyx.Groupbox", components: [
-			{kind: "onyx.InputDecorator", components: [
-				{name: "username", kind: "onyx.Input", placeholder: "Username"}
-			]},
-			{kind: "onyx.InputDecorator", components: [
-				{name: "password", kind: "onyx.Input", placeholder: "Password", type: "password"}
-			]}
-		]},
-		{kind: "onyx.Button", content: "Signup", onclick: "signupClick", classes: "onyx-affirmative"},
-		{kind: "onyx.Button", content: "Cancel", onclick: "cancelClick", classes: "onyx-negative"}
-	],
-	signupClick: function() {
-		// Do stuff
-	},
-	cancelClick: function() {
-		this.hide();
-	}
+    name: "blerg.SignupDialog",
+    kind: "onyx.Popup",
+    classes: "blerg-dialog",
+    autoDismiss: true,
+    centered: true,
+    floating: true,
+    modal: true,
+    components: [
+        {tag: "h2", content: "Sign Up"},
+        {kind: "onyx.Groupbox", components: [
+            {kind: "onyx.InputDecorator", components: [
+                {name: "username", kind: "onyx.Input", placeholder: "Username"}
+            ]},
+            {kind: "onyx.InputDecorator", components: [
+                {name: "password", kind: "onyx.Input", placeholder: "Password", type: "password"}
+            ]}
+        ]},
+        {kind: "onyx.Button", content: "Signup", onclick: "signupClick", classes: "onyx-affirmative"},
+        {kind: "onyx.Button", content: "Cancel", onclick: "cancelClick", classes: "onyx-negative"}
+    ],
+    signupClick: function() {
+        // Do stuff
+    },
+    cancelClick: function() {
+        this.hide();
+    }
 });

diff --git a/www/jssrc/blerg/Title.js b/www/jssrc/blerg/Title.js
line changes: +28/-28
index bd82520..53c87f2
--- a/www/jssrc/blerg/Title.js
+++ b/www/jssrc/blerg/Title.js
@@ -1,30 +1,30 @@
 enyo.kind({
-	name: "blerg.Title",
-	kind: "Control",
-	style: "float: left; width: 40%",
-	published: {
-		section: ""
-	},
-	handlers: {
-		onSetTitle: "setTitle"
-	},
-	components: [
-		{kind: "Image", classes: "logo", src: "images/blerglogo.png", attributes: {width: 125, height: 122}},
-		{tag: "h1", components: [
-			{kind: "blerg.Link", href: "#/", content: "Blërg!"}
-		]},
-		{tag: "h2", components: [
-			{name: "section", tag: "span"}
-		]}
-	],
-	create: function() {
-		this.inherited(arguments);
-	},
-	sectionChanged: function() {
-		this.$.section.setContent(this.section);
-	},
-	setTitle: function(inSender, inEvent) {
-		if (inEvent.section)
-			this.setSection(inEvent.section);
-	}
+    name: "blerg.Title",
+    kind: "Control",
+    style: "float: left; width: 40%",
+    published: {
+        section: ""
+    },
+    handlers: {
+        onSetTitle: "setTitle"
+    },
+    components: [
+        {kind: "Image", classes: "logo", src: "images/blerglogo.png", attributes: {width: 125, height: 122}},
+        {tag: "h1", components: [
+            {kind: "blerg.Link", href: "#/", content: "Blërg!"}
+        ]},
+        {tag: "h2", components: [
+            {name: "section", tag: "span"}
+        ]}
+    ],
+    create: function() {
+        this.inherited(arguments);
+    },
+    sectionChanged: function() {
+        this.$.section.setContent(this.section);
+    },
+    setTitle: function(inSender, inEvent) {
+        if (inEvent.section)
+            this.setSection(inEvent.section);
+    }
 });

diff --git a/www/jssrc/blerg/User.js b/www/jssrc/blerg/User.js
line changes: +100/-98
index 5bcdbd0..ac4c24c
--- a/www/jssrc/blerg/User.js
+++ b/www/jssrc/blerg/User.js
@@ -1,103 +1,105 @@
 enyo.kind({
-	name: "blerg.User",
-	kind: "blerg.Pager",
-	listKind: "blerg.Record",
-	handlers: {
-		onLogin: "getStalkStatus",
-		onLogout: "getStalkStatus"
-	},
-	published: {
-		username: "",
-		permalink: false,
-		record: null,
-	},
-	components: [
-		{classes: "blerg-user-controls", components: [
-			{name: "chatterLink", kind: "blerg.Link", content: "[chatter]"},
-			{name: "stalkLink", kind: "blerg.Link", content: "[stalk]", showing: false, onNavigate: "startStalking"},
-			{name: "unstalkLink", kind: "blerg.Link", content: "[stop stalking]", showing: false, onNavigate: "stopStalking"},
-			{name: "rssLink", kind: "blerg.Link", components: [
-				{noDom: true, content: "["},
-				{kind: "Image", src: "/images/rss.png", attributes: {width: 16, height: 16}},
-				{noDom: true, content: "RSS]"}
-			]}
-		]},
-		{name: "records"},
-		{name: "loadMoreButton", kind: "onyx.Button", onclick: "loadMore", content: "Load More"},
-		{name: "api", kind: "blerg.API",
-		 onItemsLoaded: "itemsLoaded",
-		 onSubscriptionStatus: "gotStalkStatus"}
-	],
-	statics: {
-		locationDetect: function(l) {
-			var m = l.hash.match(/^#([A-Za-z0-9_-]+)(?:\/(\d+))?$/);
-			if (m) {
-				return {
-					kind: "blerg.User",
-					username: m[1],
-					permalink: m[2] != undefined,
-					record: parseInt(m[2])
-				};
-			}
-		}
-	},
-	create: function() {
-		this.inherited(arguments);
-		this.usernameChanged();
-	},
-	usernameChanged: function() {
-		this.bubble('onSetTitle', {section: '@' + this.username});
-		this.$.records.destroyComponents();
-		this.lastRecord = null;
-		this.$.loadMoreButton.hide();
-		this.$.chatterLink.setHref('/#/ref/' + this.username);
-		this.$.rssLink.setHref('/rss/' + this.username);
-		this.getStalkStatus();
+    name: "blerg.User",
+    kind: "blerg.Pager",
+    listKind: "blerg.Record",
+    handlers: {
+        onLogin: "getStalkStatus",
+        onLogout: "getStalkStatus"
+    },
+    published: {
+        username: "",
+        permalink: false,
+        record: null,
+    },
+    components: [
+        {classes: "blerg-user-controls", components: [
+            {name: "chatterLink", kind: "blerg.Link", content: "[chatter]"},
+            {name: "stalkLink", kind: "blerg.Link", content: "[stalk]", showing: false, onNavigate: "startStalking"},
+            {name: "unstalkLink", kind: "blerg.Link", content: "[stop stalking]", showing: false, onNavigate: "stopStalking"},
+            {name: "rssLink", kind: "blerg.Link", components: [
+                {noDom: true, content: "["},
+                {kind: "Image", src: "/images/rss.png", attributes: {width: 16, height: 16}},
+                {noDom: true, content: "RSS]"}
+            ]}
+        ]},
+        {name: "records"},
+        {name: "loadMoreButton", kind: "onyx.Button", onclick: "loadMore", content: "Load More"},
+        {name: "api", kind: "blerg.API",
+         onItemsLoaded: "itemsLoaded",
+         onSubscriptionStatus: "gotStalkStatus"}
+    ],
+    statics: {
+        locationDetect: function(l) {
+            var m = l.hash.match(/^#([A-Za-z0-9_-]+)(?:\/(\d+))?$/);
+            if (m) {
+                return {
+                    kind: "blerg.User",
+                    username: m[1],
+                    permalink: m[2] != undefined,
+                    record: parseInt(m[2])
+                };
+            }
+        }
+    },
+    create: function() {
+        this.inherited(arguments);
+        this.usernameChanged();
+    },
+    usernameChanged: function() {
+        this.bubble('onSetTitle', {section: '@' + this.username});
+        this.$.records.destroyComponents();
+        this.lastRecord = null;
+        this.$.loadMoreButton.hide();
+        this.$.chatterLink.setHref('/#/ref/' + this.username);
+        this.$.rssLink.setHref('/rss/' + this.username);
+        this.getStalkStatus();
 
-		if (this.permalink) {
-			this.loadItems(this.record, this.record);
-		} else {
-			this.loadMore();
-		}
-	},
-	loadItems: function(from, to) {
-		this.inherited(arguments);
-		this.$.api.loadUserRecords(this.username, from, to);
-	},
-	itemsLoaded: function(inSender, inEvent) {
-		if (this.permalink) {
-			this.$.loadMoreButton.hide();
-		} else {
-			this.$.loadMoreButton.show();
-		}
+        if (this.permalink) {
+            this.loadItems(this.record, this.record);
+        } else {
+            this.loadMore();
+        }
+    },
+    loadItems: function(from, to) {
+        this.inherited(arguments);
+        this.$.api.loadUserRecords(this.username, from, to);
+    },
+    itemsLoaded: function(inSender, inEvent) {
+        if (this.permalink) {
+            this.$.loadMoreButton.hide();
+        } else {
+            this.$.loadMoreButton.show();
+        }
 
-		for (var i = 0; i < inEvent.entries.length; i++) {
-			inEvent.entries[i].author = this.username;
-		}
+        for (var i = 0; i < inEvent.entries.length; i++) {
+            inEvent.entries[i].author = this.username;
+        }
 
-		this.addItems(inEvent.entries);
-	},
-	getStalkStatus: function() {
-		if (!blerg.API.loggedIn) {
-			this.$.stalkLink.hide();
-			this.$.unstalkLink.hide();
-			return;
-		}
-		this.$.api.getSubscriptionStatus(this.username);
-	},
-	gotStalkStatus: function(inSender, inEvent) {
-		if (inEvent.subscribed) {
-			this.$.stalkLink.hide();
-			this.$.unstalkLink.show();
-		} else {
-			this.$.stalkLink.show();
-			this.$.unstalkLink.hide();
-		}
-	},
-	startStalking: function() {
-		this.$.api.subscribe(this.username);
-	},
-	stopStalking: function() {
-		this.$.api.unsubscribe(this.username);
-	}
+        this.addItems(inEvent.entries);
+        if (this.lastRecord == 0)
+            this.$.loadMoreButton.hide();
+    },
+    getStalkStatus: function() {
+        if (!blerg.API.loggedIn) {
+            this.$.stalkLink.hide();
+            this.$.unstalkLink.hide();
+            return;
+        }
+        this.$.api.getSubscriptionStatus(this.username);
+    },
+    gotStalkStatus: function(inSender, inEvent) {
+        if (inEvent.subscribed) {
+            this.$.stalkLink.hide();
+            this.$.unstalkLink.show();
+        } else {
+            this.$.stalkLink.show();
+            this.$.unstalkLink.hide();
+        }
+    },
+    startStalking: function() {
+        this.$.api.subscribe(this.username);
+    },
+    stopStalking: function() {
+        this.$.api.unsubscribe(this.username);
+    }
 });

diff --git a/www/jssrc/blerg/Util.js b/www/jssrc/blerg/Util.js
line changes: +112/-112
index b3f5369..d47770e
--- a/www/jssrc/blerg/Util.js
+++ b/www/jssrc/blerg/Util.js
@@ -4,119 +4,119 @@ blerg.Util = {};
 
 // Dirty, terrible shim for rewritten links below
 blerg.Util.qlink = function() {
-	try {
-		location.href = event.target.href;
-	} catch(e) { }
-	enyo.$.blerg.bubble('onNavigate');
-	return false;
+    try {
+        location.href = event.target.href;
+    } catch(e) { }
+    enyo.$.blerg.bubble('onNavigate');
+    return false;
 }
 
 blerg.Util.blergFormat = function(text) {
-	var lines = text.split(/\r?\n/);
-	if (lines[lines.length - 1] == '')
-		lines.pop();
-
-	var out = ['<p>'];
-	var endpush = null;
-	var listMode = false;
-	lines.forEach(function(l) {
-		if (l == '') {
-			if (out[out.length - 1] == '<br>') {
-				out[out.length - 1] = '<p>';
-			}
-			if (out[out.length - 1] == '</li>') {
-				out.push('</ul>');
-				out.push('<p>');
-				listMode = false;
-			}
-			return;
-		}
-
-		// Put quoted material into a special paragraph
-		if (l[0] == '>') {
-			var pi = out.lastIndexOf('<p>');
-			if (pi != -1) {
-				out[pi] = '<p class="quote">';
-				l = l.replace(/^>\s*/, '');
-			}
-		}
-
-		// Sanitize HTML input
-		l = l.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
-
-		// Turn HTTP URLs into links
-		l = l.replace(/(\s|^)(https?:\/\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\/([^\s"]*[^.!,;?()\s])?)?)/g, '$1<a href="$2">$2</a>');
-
-		// Turn markdown links into links
-		var re;
-
-		/*
-		// Craft a regex that finds URLs that end in the extensions specified by BlergMedia.audioExtensions.
-		re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9]\\/[^)"]*?\\.(' + BlergMedia.audioExtensions.join('|') + '))\\)', 'g');
-		l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_audio(); return false"><img src="/images/play.png"></a>');
-
-		// Ditto, but use the extended markdown link syntax to specify the format
-		re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\\/[^)"]*?)?)\\s+audio:(' + BlergMedia.audioExtensions.join('|') + ')\\)', 'g');
-		l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_audio(); return false"><img src="/images/play.png"></a>');
-
-		// Craft a regex that finds URLs that end in the extensions specified by BlergMedia.videoExtensions.
-		re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9]\\/[^)"]*?\\.(' + BlergMedia.videoExtensions.join('|') + '))\\)', 'g');
-		l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_video(); return false"><img src="/images/play.png"></a>');
-
-		// Ditto, but use the extended markdown link syntax to specify the format
-		re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\\/[^)"]*?)?)\\s+video:(' + BlergMedia.videoExtensions.join('|') + ')\\)', 'g');
-		l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_video(); return false"><img src="/images/play.png"></a>');
-		*/
-
-		// Regular markdown links
-		l = l.replace(/(\s|^)\[([^\]]+)\]\((https?:\/\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\/[^)"]*?)?)\)/g, '$1<a href="$3">$2</a>');
-
-		// Turn *foo* into italics and **foo** into bold
-		l = l.replace(/([^\w\\]|^)\*\*(\w[^*]*)\*\*(\W|$)/g, '$1<b>$2</b>$3');
-		l = l.replace(/([^\w\\]|^)\*(\w[^*]*)\*(\W|$)/g, '$1<i>$2</i>$3');
-
-		// Turn refs and tags into links
-		l = l.replace(/(\s|^)#([A-Za-z0-9_-]+)/g, '$1<a href="#/tag/$2" class="ref" onclick="return blerg.Util.qlink()">#$2</a>');
-		l = l.replace(/(\s|^)@([A-Za-z0-9_-]+)(\/\d+)?/g, '$1<a href="#$2$3" class="ref" onclick="return blerg.Util.qlink()">@$2</a>');
-
-		// Create lists when lines begin with *
-		if (l[0] == '*') {
-			if (!listMode) {
-				var pi = out.lastIndexOf('<p>');
-				out[pi] = '<ul>';
-				listMode = true;
-			}
-			l = l.replace(/^\*\s*/, '');
-			out.push('<li>');
-			endpush = '</li>';
-		}
-
-		// Create headers when lines begin with = or #
-		if (l[0] == '=' || l[0] == '#') {
-			var m = l.match(/^([=#]+)/);
-			var depth = m[1].length;
-			if (depth <= 5) {
-				l = l.replace(/^[=#]+\s*/, '').replace(/\s*[=#]+$/, '');
-				out.push('<h' + depth + '>');
-				endpush = '</h' + depth + '>';
-			}
-		}
-
-		// Remove backslashes from escaped metachars
-		l = l.replace(/\\([*\[\]@#])/g, '$1');
-
-		out.push(l);
-		if (endpush) {
-			out.push(endpush);
-			endpush = null;
-		} else {
-			out.push('<br>');
-		}
-	});
-	while (out[out.length - 1] == '<br>' || out[out.length - 1] == '<p>')
-		out.pop();
-	if (listMode)
-		out.push('</ul>');
-
-	return out.join('');
+    var lines = text.split(/\r?\n/);
+    if (lines[lines.length - 1] == '')
+        lines.pop();
+
+    var out = ['<p>'];
+    var endpush = null;
+    var listMode = false;
+    lines.forEach(function(l) {
+        if (l == '') {
+            if (out[out.length - 1] == '<br>') {
+                out[out.length - 1] = '<p>';
+            }
+            if (out[out.length - 1] == '</li>') {
+                out.push('</ul>');
+                out.push('<p>');
+                listMode = false;
+            }
+            return;
+        }
+
+        // Put quoted material into a special paragraph
+        if (l[0] == '>') {
+            var pi = out.lastIndexOf('<p>');
+            if (pi != -1) {
+                out[pi] = '<p class="quote">';
+                l = l.replace(/^>\s*/, '');
+            }
+        }
+
+        // Sanitize HTML input
+        l = l.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
+
+        // Turn HTTP URLs into links
+        l = l.replace(/(\s|^)(https?:\/\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\/([^\s"]*[^.!,;?()\s])?)?)/g, '$1<a href="$2">$2</a>');
+
+        // Turn markdown links into links
+        var re;
+
+        /*
+        // Craft a regex that finds URLs that end in the extensions specified by BlergMedia.audioExtensions.
+        re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9]\\/[^)"]*?\\.(' + BlergMedia.audioExtensions.join('|') + '))\\)', 'g');
+        l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_audio(); return false"><img src="/images/play.png"></a>');
+
+        // Ditto, but use the extended markdown link syntax to specify the format
+        re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\\/[^)"]*?)?)\\s+audio:(' + BlergMedia.audioExtensions.join('|') + ')\\)', 'g');
+        l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_audio(); return false"><img src="/images/play.png"></a>');
+
+        // Craft a regex that finds URLs that end in the extensions specified by BlergMedia.videoExtensions.
+        re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9]\\/[^)"]*?\\.(' + BlergMedia.videoExtensions.join('|') + '))\\)', 'g');
+        l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_video(); return false"><img src="/images/play.png"></a>');
+
+        // Ditto, but use the extended markdown link syntax to specify the format
+        re = new RegExp('(\\s|^)\\[([^\\]]+)\\]\\((https?:\\/\\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\\/[^)"]*?)?)\\s+video:(' + BlergMedia.videoExtensions.join('|') + ')\\)', 'g');
+        l = l.replace(re, '$1<a href="$3">$2</a> <a href="$3" onclick="play_video(); return false"><img src="/images/play.png"></a>');
+        */
+
+        // Regular markdown links
+        l = l.replace(/(\s|^)\[([^\]]+)\]\((https?:\/\/[a-zA-Z0-9.-]*[a-zA-Z0-9](\/[^)"]*?)?)\)/g, '$1<a href="$3">$2</a>');
+
+        // Turn *foo* into italics and **foo** into bold
+        l = l.replace(/([^\w\\]|^)\*\*(\w[^*]*)\*\*(\W|$)/g, '$1<b>$2</b>$3');
+        l = l.replace(/([^\w\\]|^)\*(\w[^*]*)\*(\W|$)/g, '$1<i>$2</i>$3');
+
+        // Turn refs and tags into links
+        l = l.replace(/(\s|^)#([A-Za-z0-9_-]+)/g, '$1<a href="#/tag/$2" class="ref" onclick="return blerg.Util.qlink()">#$2</a>');
+        l = l.replace(/(\s|^)@([A-Za-z0-9_-]+)(\/\d+)?/g, '$1<a href="#$2$3" class="ref" onclick="return blerg.Util.qlink()">@$2</a>');
+
+        // Create lists when lines begin with *
+        if (l[0] == '*') {
+            if (!listMode) {
+                var pi = out.lastIndexOf('<p>');
+                out[pi] = '<ul>';
+                listMode = true;
+            }
+            l = l.replace(/^\*\s*/, '');
+            out.push('<li>');
+            endpush = '</li>';
+        }
+
+        // Create headers when lines begin with = or #
+        if (l[0] == '=' || l[0] == '#') {
+            var m = l.match(/^([=#]+)/);
+            var depth = m[1].length;
+            if (depth <= 5) {
+                l = l.replace(/^[=#]+\s*/, '').replace(/\s*[=#]+$/, '');
+                out.push('<h' + depth + '>');
+                endpush = '</h' + depth + '>';
+            }
+        }
+
+        // Remove backslashes from escaped metachars
+        l = l.replace(/\\([*\[\]@#])/g, '$1');
+
+        out.push(l);
+        if (endpush) {
+            out.push(endpush);
+            endpush = null;
+        } else {
+            out.push('<br>');
+        }
+    });
+    while (out[out.length - 1] == '<br>' || out[out.length - 1] == '<p>')
+        out.pop();
+    if (listMode)
+        out.push('</ul>');
+
+    return out.join('');
 }

diff --git a/www/jssrc/blerg/Welcome.js b/www/jssrc/blerg/Welcome.js
line changes: +107/-107
index 769450e..32301ce
--- a/www/jssrc/blerg/Welcome.js
+++ b/www/jssrc/blerg/Welcome.js
@@ -1,111 +1,111 @@
 enyo.kind({
-	name: "blerg.Welcome",
-	statics: {
-		locationDetect: function(l) {
-			if (l.hash.match(/^(#\/?)?$/))
-				return {kind: "blerg.Welcome"}
-			else
-				return false;
-		}
-	},
-	components: [
-		{components: [
-			{style: "float: right; text-align: center; margin: 0 0 1em 1em", components: [
-				{style: "font-size: 14pt; margin-bottom: 4pt", content: "Curious? Click this unbelievably obnoxious button!"},
-				{kind: "onyx.Button", content: "I want to Blërg!", style: "font-size: 40pt; padding: 1em; background-color: #C0F; color: #F88", onclick: "startSignup"}
-			]}
-		]},
-		{allowHtml: true, content: '<h2>I am 12 and what is this</h2> <p>Blërg is a microblogging platform.  Or maybe a miniblogging platform.  Blërg is not sure.  Blërg is a lot like <a href="http://twitter.com/">Twitter</a>, but aims to fix some of its idiosyncracies.  Blërg does not want to be a full blogging platform like <a href="http://wordpress.com/">Wordpress</a> or <a href="http://livejournal.com/">Livejournal</a>.  Blërg is also an <a href="/doc/">open source tagged text database engine</a> written in C that does the back-end work. Blërg\'s author finds it entertaining to anthropomorphize Blërg in the third person.</p>'},
-		{name: "moreLink", kind: "blerg.Link", onNavigate: "loadMore", content: "Tell me more..."},
-		{style: "clear: both"},
-		{name: "contentBox", allowHtml: true},
-		{classes: "latest", components: [
-			{tag: "h2", content: "Latest posts"},
-			{name: "latestPosts", classes: "latest-posts", onmouseover: "pauseTicker", onmouseout: "startTicker"},
-			{tag: "h2", content: "Latest tags"},
-			{name: "latestTags", classes: "latest-tags"}
-		]}
-	],
-	create: function() {
-		this.inherited(arguments);
+    name: "blerg.Welcome",
+    statics: {
+        locationDetect: function(l) {
+            if (l.hash.match(/^(#\/?)?$/))
+                return {kind: "blerg.Welcome"}
+            else
+                return false;
+        }
+    },
+    components: [
+        {components: [
+            {style: "float: right; text-align: center; margin: 0 0 1em 1em", components: [
+                {style: "font-size: 14pt; margin-bottom: 4pt", content: "Curious? Click this unbelievably obnoxious button!"},
+                {kind: "onyx.Button", content: "I want to Blërg!", style: "font-size: 40pt; padding: 1em; background-color: #C0F; color: #F88", onclick: "startSignup"}
+            ]}
+        ]},
+        {allowHtml: true, content: '<h2>I am 12 and what is this</h2> <p>Blërg is a microblogging platform.  Or maybe a miniblogging platform.  Blërg is not sure.  Blërg is a lot like <a href="http://twitter.com/">Twitter</a>, but aims to fix some of its idiosyncracies.  Blërg does not want to be a full blogging platform like <a href="http://wordpress.com/">Wordpress</a> or <a href="http://livejournal.com/">Livejournal</a>.  Blërg is also an <a href="/doc/">open source tagged text database engine</a> written in C that does the back-end work. Blërg\'s author finds it entertaining to anthropomorphize Blërg in the third person.</p>'},
+        {name: "moreLink", kind: "blerg.Link", onNavigate: "loadMore", content: "Tell me more..."},
+        {style: "clear: both"},
+        {name: "contentBox", allowHtml: true},
+        {classes: "latest", components: [
+            {tag: "h2", content: "Latest posts"},
+            {name: "latestPosts", classes: "latest-posts", onmouseover: "pauseTicker", onmouseout: "startTicker"},
+            {tag: "h2", content: "Latest tags"},
+            {name: "latestTags", classes: "latest-tags"}
+        ]}
+    ],
+    create: function() {
+        this.inherited(arguments);
 
-		this.bubble("onSetTitle", {section: "Welcome!"});
-		this.loadLatest();
-	},
-	destroy: function() {
-		this.stopTicker();
-		this.inherited(arguments);
-	},
-	loadMore: function() {
-		var req = new enyo.Ajax({
-			url: "/welcome.html",
-			handleAs: "text"
-		});
-		req.response(function(inSender, inResponse) {
-			this.$.contentBox.setContent(inResponse);
-			this.$.moreLink.hide();
-		}.bind(this));
-		req.go();
-	},
-	startSignup: function() {
-		this.bubble('onStartSignup');
-	},
-	loadLatest: function() {
-		var req = new enyo.Ajax({
-			url: baseURL + '/latest.json'
-		});
-		req.response(function(inSender, inResponse) {
-			this.$.latestTags.destroyComponents();
-			for (var i = 0; i < inResponse.tags.length; i++) {
-				var v = inResponse.tags[i];
-				this.$.latestTags.createComponent({
-					kind: "blerg.Link",
-					href: baseURL + "/#/tag/" + v,
-					content: "#" + v,
-					classes: "ref"
-				});
-				this.$.latestTags.createComponent({noDom: true, content: " "});
-			}
-			this.$.latestTags.render();
+        this.bubble("onSetTitle", {section: "Welcome!"});
+        this.loadLatest();
+    },
+    destroy: function() {
+        this.stopTicker();
+        this.inherited(arguments);
+    },
+    loadMore: function() {
+        var req = new enyo.Ajax({
+            url: "/welcome.html",
+            handleAs: "text"
+        });
+        req.response(function(inSender, inResponse) {
+            this.$.contentBox.setContent(inResponse);
+            this.$.moreLink.hide();
+        }.bind(this));
+        req.go();
+    },
+    startSignup: function() {
+        this.bubble('onStartSignup');
+    },
+    loadLatest: function() {
+        var req = new enyo.Ajax({
+            url: baseURL + '/latest.json'
+        });
+        req.response(function(inSender, inResponse) {
+            this.$.latestTags.destroyComponents();
+            for (var i = 0; i < inResponse.tags.length; i++) {
+                var v = inResponse.tags[i];
+                this.$.latestTags.createComponent({
+                    kind: "blerg.Link",
+                    href: baseURL + "/#/tag/" + v,
+                    content: "#" + v,
+                    classes: "ref"
+                });
+                this.$.latestTags.createComponent({noDom: true, content: " "});
+            }
+            this.$.latestTags.render();
 
-			this.$.latestPosts.destroyComponents();
-			for (var i = 0; i < inResponse.records.length; i++) {
-				var v = inResponse.records[i];
-				this.$.latestPosts.createComponent({kind: "blerg.BriefRecord"}, v);
-			}
-			this.$.latestPosts.render();
-			this.startTickerCycle();
-		}.bind(this));
-		req.go();
-	},
-	startTickerCycle: function() {
-		this.tickerTimeout = setTimeout(function() {
-			this.$.latestPosts.node.scrollTop = 0;
-			this.startTicker();
-		}.bind(this), 2500);
-	},
-	startTicker: function() {
-		if (this.tickerInterval)
-			return;
-		this.tickerInterval = setInterval(this.ticker.bind(this), 100);
-	},
-	pauseTicker: function() {
-		clearInterval(this.tickerInterval);
-		this.tickerInterval = null;
-	},
-	stopTicker: function() {
-		clearTimeout(this.tickerTimeout);
-		this.tickerTimeout = null;
-		clearInterval(this.tickerInterval);
-		this.tickerInterval = null;
-	},
-	ticker: function() {
-		var n = this.$.latestPosts.node;
-		if (n.scrollTop < n.scrollHeight - n.clientHeight) {
-			n.scrollTop += 2;
-		} else {
-			this.stopTicker();
-			this.startTickerCycle();
-		}
-	}
+            this.$.latestPosts.destroyComponents();
+            for (var i = 0; i < inResponse.records.length; i++) {
+                var v = inResponse.records[i];
+                this.$.latestPosts.createComponent({kind: "blerg.BriefRecord"}, v);
+            }
+            this.$.latestPosts.render();
+            this.startTickerCycle();
+        }.bind(this));
+        req.go();
+    },
+    startTickerCycle: function() {
+        this.tickerTimeout = setTimeout(function() {
+            this.$.latestPosts.node.scrollTop = 0;
+            this.startTicker();
+        }.bind(this), 2500);
+    },
+    startTicker: function() {
+        if (this.tickerInterval)
+            return;
+        this.tickerInterval = setInterval(this.ticker.bind(this), 100);
+    },
+    pauseTicker: function() {
+        clearInterval(this.tickerInterval);
+        this.tickerInterval = null;
+    },
+    stopTicker: function() {
+        clearTimeout(this.tickerTimeout);
+        this.tickerTimeout = null;
+        clearInterval(this.tickerInterval);
+        this.tickerInterval = null;
+    },
+    ticker: function() {
+        var n = this.$.latestPosts.node;
+        if (n.scrollTop < n.scrollHeight - n.clientHeight) {
+            n.scrollTop += 2;
+        } else {
+            this.stopTicker();
+            this.startTickerCycle();
+        }
+    }
 });

diff --git a/www/jssrc/blerg/package.js b/www/jssrc/blerg/package.js
line changes: +16/-15
index dc52cd4..70d4520
--- a/www/jssrc/blerg/package.js
+++ b/www/jssrc/blerg/package.js
@@ -1,17 +1,18 @@
 enyo.depends(
-	'API.js',
-	'Util.js',
-	'Link.js',
-	'Record.js',
-	'Title.js',
-	'Controls.js',
-	'Post.js',
-	'Help.js',
-	'Main.js',
-	'SignupDialog.js',
-	'PasswdDialog.js',
-	'Welcome.js',
-	'Pager.js',
-	'User.js',
-	'Blerg.js'
+    'API.js',
+    'Util.js',
+    'Link.js',
+    'Record.js',
+    'Title.js',
+    'Controls.js',
+    'Post.js',
+    'Help.js',
+    'Main.js',
+    'SignupDialog.js',
+    'PasswdDialog.js',
+    'Welcome.js',
+    'Pager.js',
+    'User.js',
+    'Tag.js',
+    'Blerg.js'
 );