1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2 * BSD-style license. Please see the COPYING file for details.
7 var recordTemplate = new Template(
8 '<div class="record">#{data}<div class="info">Posted #{date}. <a href="' + baseURL + '/\##{author}/#{record}" onclick="return qlink()">[permalink]</a> <a href="#" onclick="postPopup(\'@#{author}/#{record}: \'); return false">[reply]</a></div></div>'
10 var tagRecordTemplate = new Template(
11 '<div class="record">#{data}<div class="info">Posted by <a class="author ref" href="/\##{author}" onclick="return qlink()">@#{author}</a> on #{date}. <a href="' + baseURL + '/\##{author}/#{record}" onclick="return qlink()">[permalink]</a> <a href="#" onclick="postPopup(\'@#{author}/#{record}: \'); return false">[reply]</a></div></div>'
13 var latestRecordsTemplate = new Template(
14 '<div class="record"><a class="author ref" href="' + baseURL + '/\##{author}" onclick="return qlink()">@#{author}</a> #{data}</div>'
24 // Object to keep track of login status
25 function LoginStatus() {
27 document.cookie.split(/;\s+/).each(function(v) {
29 cookies[kv[0]] = kv[1];
31 if (cookies.auth && cookies.username) {
33 this.username = cookies.username;
34 this.requestFeedStatus();
35 this.feedStatusUpdateInterval = setInterval(this.requestFeedStatus.bind(this), 900000);
37 this.loggedIn = false;
43 LoginStatus.prototype.login = function(username, password) {
44 new Ajax.Request(baseURL + '/login', {
49 onSuccess: function(r) {
50 var j = r.responseText.evalJSON();
51 if (j && j.status == 'success') {
53 this.username = username;
54 document.cookie = "username=" + username;
55 $('login.password').value = '';
56 this.requestFeedStatus();
57 this.feedStatusUpdateInterval = setInterval(this.requestFeedStatus.bind(this), 900000);
60 alert("Could not log in");
61 $('login.username').focus();
64 onFailure: function(r) {
65 alert("Could not log in");
66 $('login.username').focus();
71 LoginStatus.prototype.logout = function() {
72 new Ajax.Request(baseURL + '/logout', {
74 username: this.username
76 onComplete: function(r) {
77 this.loggedIn = false;
78 document.cookie = "auth=; expires=1-Jan-1970 00:00:00 GMT";
80 clearInterval(this.feedStatusUpdateInterval);
83 document.cookie = "username=; expires=1-Jan-1970 00:00:00 GMT";
86 LoginStatus.prototype.update = function() {
88 $('userlink').href = baseURL + '/#' + this.username;
89 $('userlink').update('@' + this.username);
90 $('reflink').href = baseURL + '/#/ref/' + this.username;
100 LoginStatus.prototype.post = function(msg) {
101 if (!this.loggedIn) {
102 alert("You are not logged in!");
106 new Ajax.Request(baseURL + '/put', {
108 username: this.username,
111 onSuccess: function(r) {
112 var j = r.responseText.evalJSON();
113 if (j && j.status == 'success') {
114 $('post.content').value = '';
115 if (location.hash != '#' + this.username) {
116 qlink(this.username);
118 currentPager.itemCount++;
119 currentPager.reload();
122 alert('Post failed!');
125 onFailure: function(r) {
126 alert('Post failed!');
131 LoginStatus.prototype.requestFeedStatus = function() {
132 new Ajax.Request('/feedinfo', {
133 parameters: { username: this.username },
134 onSuccess: function(r) {
135 var j = r.responseText.evalJSON();
137 $('newFeedMessages').update('(' + j['new'] + ' new)');
139 $('newFeedMessages').update('');
145 // Base object for paged data
147 this.itemsPerPage = 10;
148 this.itemCache = new Hash();
149 this.pageStart = null;
152 Pager.prototype.updateState = function(m) {
156 Pager.prototype.show = function() {
160 Pager.prototype.hide = function() {
163 $('newer_link').hide();
164 $('older_link').hide();
167 Pager.prototype.olderPage = function() {
168 if (this.pageStart >= this.itemsPerPage) {
169 qlink(this.baseFrag + '/p' + (this.pageStart - this.itemsPerPage));
173 Pager.prototype.newerPage = function() {
174 if (this.pageStart + this.itemsPerPage < this.itemCount) {
175 qlink(this.baseFrag + '/p' + (this.pageStart + this.itemsPerPage));
179 Pager.prototype.addItems = function(items) {
180 items.each(function(v) {
181 if (!this.itemCache[v.id])
182 this.itemCache[v.id] = v;
186 Pager.prototype.displayItems = function() {
187 if (this.pageStart == undefined)
188 this.pageStart == this.itemCount - 1;
191 if (this.pageStart != undefined && this.itemCache[this.pageStart]) {
192 var end = (this.pageStart >= this.itemsPerPage ? this.pageStart - this.itemsPerPage + 1 : 0);
193 for (var i = this.pageStart; i >= end; i--) {
194 items.insert(this.itemCache[i].html);
197 items.insert("There doesn't seem to be anything here!");
200 if (this.pageStart < this.itemCount - 1) {
201 $('newer_link').href = baseURL + '/#' + this.baseFrag + '/p' + (this.pageStart + this.itemsPerPage);
202 $('newer_link').show();
204 $('newer_link').hide();
207 if (this.pageStart >= 10) {
208 $('older_link').href = baseURL + '/#' + this.baseFrag + '/p' + (this.pageStart - this.itemsPerPage);
209 $('older_link').show();
211 $('older_link').hide();
214 document.body.scrollTo();
217 Pager.prototype.reload = function() {
218 this.pageStart = null;
219 this.loadItems(null, null, Pager.prototype.showPageAt.bind(this, this.itemCount - 1));
222 Pager.prototype.showPageAt = function(r) {
223 var end = (r - 9 > 0 ? r - 9 : 0);
224 if (this.itemCache[r] && this.itemCache[end]) {
228 this.loadItems((r >= 49 ? r - 49 : 0), r, Pager.prototype.showPageAt.bind(this, r));
232 Pager.prototype.showRecord = function(r) {
233 if (this.itemCache[r]) {
234 $('older_link').hide();
235 $('newer_link').hide();
236 items.update(this.itemCache[r].html);
238 this.loadItems(r, r, Pager.prototype.showRecord.bind(this, r));
242 Pager.prototype.loadItems = function(from, to, continuation) { }
245 // Object to render user pages
248 this.username = m[1];
249 this.baseFrag = m[1];
250 this.permalink = (m[2] != 'p');
251 this.pageStart = parseInt(m[3]);
253 User.prototype = new Pager();
254 User.prototype.constructor = User;
256 User.prototype.updateState = function(m) {
257 if (m[1] != this.username)
260 this.permalink = (m[2] != 'p');
261 this.pageStart = parseInt(m[3]);
267 User.prototype.show = function() {
268 Pager.prototype.show.call(this);
270 $$('[name=section]').each(function(v) { v.update(' @' + this.username) }.bind(this));
272 $('rsslink').href = '/rss/' + this.username;
273 $$('[name=user.reflink]').each(function(e) {
274 e.href = baseURL + '/#/ref/' + this.username;
276 $('usercontrols').show();
278 if (this.permalink && this.pageStart >= 0) {
279 this.showRecord(this.pageStart);
280 } else if (this.pageStart >= 0) {
281 this.showPageAt(this.pageStart);
287 User.prototype.hide = function() {
288 Pager.prototype.hide.call(this);
291 $('usercontrols').hide();
294 User.prototype.reload = function() {
295 this.pageStart = null;
297 $$('[name=user.subscribelink]').each(Element.hide);
298 $$('[name=user.unsubscribelink]').each(Element.hide);
300 if (loginStatus.loggedIn) {
301 new Ajax.Request(baseURL + '/feedinfo/' + this.username, {
304 username: loginStatus.username
306 onSuccess: function(r) {
307 var json = r.responseText.evalJSON();
308 if (json.subscribed) {
309 $$('[name=user.subscribelink]').each(Element.hide);
310 $$('[name=user.unsubscribelink]').each(Element.show);
312 $$('[name=user.subscribelink]').each(Element.show);
313 $$('[name=user.unsubscribelink]').each(Element.hide);
319 new Ajax.Request(baseURL + '/info/' + this.username, {
321 onSuccess: function(r) {
322 var response = r.responseText.evalJSON();
324 this.itemCount = parseInt(response.record_count);
325 this.showPageAt(this.itemCount - 1);
331 User.prototype.loadItems = function(from, to, continuation) {
336 if (from != undefined && to != undefined) {
337 url = baseURL + '/get/' + this.username + '/' + from + '-' + to;
340 url = baseURL + '/get/' + this.username;
343 new Ajax.Request(url, {
345 onSuccess: function(r) {
346 var records = r.responseText.evalJSON();
347 if (records && records.length > 0) {
348 records.each(function(v) {
350 v.author = this.username;
351 mangleRecord(v, recordTemplate);
353 this.addItems(records);
355 this.pageStart = records[0].recInt;
359 onFailure: function(r) {
363 displayError('User not found');
368 function displayError(msg) {
369 items.innerText = msg;
373 // Object for browsing tags
378 this.pageStart = parseInt(m[3]);
380 var url = baseURL + "/tag/";
384 url += 'H'; // apache is eating the hash, even encoded. Probably a security feature.
385 this.baseFrag = '/tag/' + this.tag;
389 this.baseFrag = '/ref/' + this.tag;
392 alert('Invalid tag type: ' + this.type);
397 new Ajax.Request(url, {
399 onSuccess: function(r) {
400 var j = r.responseText.evalJSON();
402 var maxid = j.length - 1;
403 j.each(function(v, i) {
405 mangleRecord(v, tagRecordTemplate)
409 this.pageStart = j.length - 1;
410 this.itemCount = j.length;
414 onFailure: function(r) {
420 Tag.prototype = new Pager();
421 Tag.prototype.constructor = Tag;
423 Tag.prototype.updateState = function(m) {
424 if (this.type != m[1] || this.tag != m[2])
427 this.pageStart = parseInt(m[3]) || this.itemCount - 1;
433 Tag.prototype.show = function() {
434 Pager.prototype.show.call(this);
436 var ctype = {ref: '@', tag: '#'}[this.type];
438 $$('[name=section]').each(function(v) {
439 v.update(' about ' + ctype + this.tag);
444 // Pager for browsing subscription feeds
447 this.username = loginStatus.username;
448 this.baseFrag = '/feed';
449 this.pageStart = parseInt(m[1]);
451 new Ajax.Request(baseURL + '/feed', {
454 username: loginStatus.username
456 onSuccess: function(r) {
457 var response = r.responseText.evalJSON();
459 var maxid = response.length - 1;
460 response.each(function(v, i) {
462 mangleRecord(v, tagRecordTemplate)
464 this.addItems(response);
466 this.pageStart = response.length - 1;
467 this.itemCount = response.length;
468 loginStatus.requestFeedStatus();
472 onFailure: function(r) {
477 Feed.prototype = new Pager();
478 Feed.prototype.constructor = Feed;
480 Feed.prototype.updateState = function(m) {
481 this.pageStart = parseInt(m[1]) || this.itemCount - 1;
487 Feed.prototype.show = function() {
488 Pager.prototype.show.call(this);
489 $$('[name=section]').each(function(v) {
490 v.update(' ' + loginStatus.username + "'s spycam");
495 function postPopup(initial) {
496 if (loginStatus.loggedIn || initial) {
497 var post = $('post');
498 if (post.visible()) {
503 $('post.content').value = initial;
504 } else if (!$('post.content').value && currentPager.username && currentPager.username != loginStatus.username) {
505 $('post.content').value = '@' + currentPager.username + ': ';
507 $('post.content').focus();
513 var username = $('signup.username').value;
514 var password = $('signup.password').value;
516 new Ajax.Request(baseURL + '/create', {
521 onSuccess: function(r) {
525 loginStatus.login(username, password);
527 onFailure: function(r) {
528 alert("Failed to create user");
533 function signup_cancel() {
539 var old_password = $('passwd.old_password').value;
540 var new_password = $('passwd.new_password').value;
542 new Ajax.Request(baseURL + '/passwd', {
544 username: loginStatus.username,
545 password: old_password,
546 new_password: new_password
548 onSuccess: function(r) {
549 if (r.responseJSON.status == 'success') {
550 alert('Password changed');
553 alert('Password change failed. Your password has NOT been changed.');
556 onFailure: function(r) {
557 alert('Password change error');
562 function passwd_cancel() {
564 $('navigation').show();
568 function subscribe() {
569 new Ajax.Request(baseURL + '/subscribe/' + currentPager.username, {
572 username: loginStatus.username
574 onSuccess: function(r) {
575 var response = r.responseText.evalJSON();
576 if (response.status == 'success') {
577 alert("You call " + currentPager.username + " and begin breathing heavily into the handset.");
578 $$('[name=user.subscribelink]').each(Element.hide);
579 $$('[name=user.unsubscribelink]').each(Element.show);
581 alert('Failed to subscribe. This is probably for the best');
584 onFailure: function(r) {
585 alert('Failed to subscribe. This is probably for the best');
590 function unsubscribe() {
591 new Ajax.Request(baseURL + '/unsubscribe/' + currentPager.username, {
594 username: loginStatus.username
596 onSuccess: function(r) {
597 var response = r.responseText.evalJSON();
598 if (response.status == 'success') {
599 alert("You come to your senses.");
600 $$('[name=user.subscribelink]').each(Element.show);
601 $$('[name=user.unsubscribelink]').each(Element.hide);
603 alert('You are unable to tear yourself away (because something failed on the server)');
606 onFailure: function(r) {
607 alert('You are unable to tear yourself away (because something failed on the server)');
612 var tickerTimer = null;
613 var tickerHead, tickerTail;
615 function tickerFader(a, b, p) {
619 a.style.lineHeight = (100 * p) + '%';
621 b.style.opacity = p2;
622 b.style.lineHeight = (100 * p2) + '%';
629 Bytex64.FX.run(tickerFader.curry(tickerHead, tickerTail), 0.5);
630 tickerHead = tickerHead.nextSibling;
631 tickerTail = tickerTail.nextSibling;
632 if (tickerHead == null) {
634 loadLatest.delay(10);
638 function startTicker() {
640 for (var elem = $('latest-posts').firstChild; elem != null; elem = elem.nextSibling) {
644 // Show the first five
645 tickerHead = $('latest-posts').firstChild;
646 for (var i = 0; i < 10 && tickerHead; i++) {
648 tickerHead = tickerHead.nextSibling;
650 tickerTail = $('latest-posts').firstChild;
651 tickerTimer = setInterval(ticker, 5000);
654 function stopTicker() {
656 clearInterval(tickerTimer);
660 function loadLatest() {
661 new Ajax.Request(baseURL + '/latest.json', {
663 onSuccess: function(r) {
664 var j = r.responseText.evalJSON();
666 $('latest-tags').update();
667 j.tags.each(function(v) {
668 var a = new Element('a', {href: baseURL + '/#/tag/' + v});
670 a.onclick = "return qlink()";
672 $('latest-tags').insert(a);
673 $('latest-tags').appendChild(document.createTextNode(' '));
676 $('latest-posts').update();
677 j.records.each(function(v) {
678 v.data = v.data.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
679 v.date = (new Date(v.timestamp * 1000)).toString();
680 var html = latestRecordsTemplate.evaluate(v);
681 $('latest-posts').insert(html);
688 function ExternalURLPost(m) {
689 this.title = decodeURIComponent(m[1]).replace(']','').replace('[','');
690 this.url = decodeURIComponent(m[2]);
693 ExternalURLPost.prototype.show = function() {
694 $('post.content').value = '[' + this.title + '](' + this.url + ')';