summaryrefslogtreecommitdiff
path: root/static/js/pichat2.js
diff options
context:
space:
mode:
authorJules Laplace <jules@okfoc.us>2012-07-18 01:22:33 -0400
committerJules Laplace <jules@okfoc.us>2012-07-18 01:22:33 -0400
commit6a42d5728ced2ff42a234c72b173fa4889c64bb7 (patch)
tree1c5fb9554193cc105508e2a1075da2d19005def0 /static/js/pichat2.js
parentdd9892392b763b897714e0599fac58390527090d (diff)
pichat2.js
Diffstat (limited to 'static/js/pichat2.js')
-rw-r--r--static/js/pichat2.js2233
1 files changed, 2233 insertions, 0 deletions
diff --git a/static/js/pichat2.js b/static/js/pichat2.js
new file mode 100644
index 0000000..754439e
--- /dev/null
+++ b/static/js/pichat2.js
@@ -0,0 +1,2233 @@
+// http://plugins.jquery.com/files/jquery.cookie.js.txt
+jQuery.cookie=function(name,value,options){if(typeof value!='undefined'){options=options||{};if(value===null){value='';options.expires=-1;}
+var expires='';if(options.expires&&(typeof options.expires=='number'||options.expires.toUTCString)){var date;if(typeof options.expires=='number'){date=new Date();date.setTime(date.getTime()+(options.expires*24*60*60*1000));}else{date=options.expires;}
+expires='; expires='+date.toUTCString();}
+var path=options.path?'; path='+(options.path):'';var domain=options.domain?'; domain='+(options.domain):'';var secure=options.secure?'; secure':'';document.cookie=[name,'=',encodeURIComponent(value),expires,path,domain,secure].join('');}else{var cookieValue=null;if(document.cookie&&document.cookie!=''){var cookies=document.cookie.split(';');for(var i=0;i<cookies.length;i++){var cookie=jQuery.trim(cookies[i]);if(cookie.substring(0,name.length+1)==(name+'=')){cookieValue=decodeURIComponent(cookie.substring(name.length+1));break;}}}
+return cookieValue;}};
+// sha1.js
+
+/* SHA1.js (timb: compressed this)
+ * Version 2.2 Copyright Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * from http://pajhome.org.uk/crypt/md5/sha1.html
+ */
+var SHA1 = {
+"hexcase": 0,
+"b64pad": "",
+"hex": function(s) { return SHA1.rstr2hex(SHA1.rstr(SHA1.str2rstr_utf8(s))); },
+"b64": function(s) { return SHA1.rstr2b64(SHA1.rstr(SHA1.str2rstr_utf8(s))); },
+"any": function(s, e) { return SHA1.rstr2any(SHA1.rstr(SHA1.str2rstr_utf8(s)), e); },
+"hex_hmac": function(k, d){ return SHA1.rstr2hex(SHA1.rstr_hmac(SHA1.str2rstr_utf8(k), SHA1.str2rstr_utf8(d))); },
+"b64_hmac": function(k, d){ return SHA1.rstr2b64(SHA1.rstr_hmac(SHA1.str2rstr_utf8(k), SHA1.str2rstr_utf8(d))); },
+"any_hmac": function(k, d, e){ return SHA1.rstr2any(SHA1.rstr_hmac(SHA1.str2rstr_utf8(k), SHA1.str2rstr_utf8(d)), e); },
+"rstr": function(s) { return SHA1.binb2rstr(SHA1.binb(SHA1.rstr2binb(s), s.length * 8)); },
+"rstr_hmac": function(key, data){
+ var bkey = SHA1.rstr2binb(key);
+ if(bkey.length > 16) bkey = SHA1.binb(bkey, key.length * 8);
+ var ipad = Array(16), opad = Array(16);
+ for(var i = 0; i < 16; i++){
+ ipad[i] = bkey[i] ^ 0x36363636;
+ opad[i] = bkey[i] ^ 0x5C5C5C5C;
+ }
+ var hash = SHA1.binb(ipad.concat(SHA1.rstr2binb(data)), 512 + data.length * 8);
+ return SHA1.binb2rstr(SHA1.binb(opad.concat(hash), 512 + 160));
+},
+"rstr2hex": function(input){
+ try { SHA1.hexcase } catch(e) { SHA1.hexcase=0; }
+ var hex_tab = SHA1.hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+ var output = "";
+ var x;
+ for(var i = 0; i < input.length; i++){
+ x = input.charCodeAt(i);
+ output += hex_tab.charAt((x >>> 4) & 0x0F)
+ + hex_tab.charAt( x & 0x0F);
+ }
+ return output;
+},
+"rstr2b64": function(input){
+ try { SHA1.b64pad } catch(e) { SHA1.b64pad=''; }
+ var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ var output = "";
+ var len = input.length;
+ for(var i = 0; i < len; i += 3){
+ var triplet = (input.charCodeAt(i) << 16)
+ | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
+ | (i + 2 < len ? input.charCodeAt(i+2) : 0);
+ for(var j = 0; j < 4; j++){
+ if(i * 8 + j * 6 > input.length * 8) output += SHA1.b64pad;
+ else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
+ }
+ }
+ return output;
+},
+"rstr2any": function(input, encoding){
+ var divisor = encoding.length;
+ var remainders = Array();
+ var i, q, x, quotient;
+ var dividend = Array(Math.ceil(input.length / 2));
+ for(i = 0; i < dividend.length; i++)
+ dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
+ while(dividend.length > 0){
+ quotient = Array();
+ x = 0;
+ for(i = 0; i < dividend.length; i++){
+ x = (x << 16) + dividend[i];
+ q = Math.floor(x / divisor);
+ x -= q * divisor;
+ if(quotient.length > 0 || q > 0)
+ quotient[quotient.length] = q;
+ }
+ remainders[remainders.length] = x;
+ dividend = quotient;
+ }
+ var output = "";
+ for(i = remainders.length - 1; i >= 0; i--)
+ output += encoding.charAt(remainders[i]);
+ var full_length = Math.ceil(input.length * 8 /
+ (Math.log(encoding.length) / Math.log(2)))
+ for(i = output.length; i < full_length; i++)
+ output = encoding[0] + output;
+
+ return output;
+},
+"str2rstr_utf8": function(input){
+ var output = "";
+ var i = -1;
+ var x, y;
+ while(++i < input.length){
+ x = input.charCodeAt(i);
+ y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
+ if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF){
+ x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
+ i++;
+ }
+ if(x <= 0x7F)
+ output += String.fromCharCode(x);
+ else if(x <= 0x7FF)
+ output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
+ 0x80 | ( x & 0x3F));
+ else if(x <= 0xFFFF)
+ output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
+ 0x80 | ((x >>> 6 ) & 0x3F),
+ 0x80 | ( x & 0x3F));
+ else if(x <= 0x1FFFFF)
+ output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
+ 0x80 | ((x >>> 12) & 0x3F),
+ 0x80 | ((x >>> 6 ) & 0x3F),
+ 0x80 | ( x & 0x3F));
+ }
+ return output;
+},
+"str2rstr_utf16le": function(input){
+ var output = "";
+ for(var i = 0; i < input.length; i++)
+ output += String.fromCharCode( input.charCodeAt(i) & 0xFF,
+ (input.charCodeAt(i) >>> 8) & 0xFF);
+ return output;
+},
+"str2rstr_utf16be": function(input){
+ var output = "";
+ for(var i = 0; i < input.length; i++)
+ output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
+ input.charCodeAt(i) & 0xFF);
+ return output;
+},
+"rstr2binb": function(input){
+ var output = Array(input.length >> 2);
+ for(var i = 0; i < output.length; i++)
+ output[i] = 0;
+ for(var i = 0; i < input.length * 8; i += 8)
+ output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
+ return output;
+},
+"binb2rstr": function(input){
+ var output = "";
+ for(var i = 0; i < input.length * 32; i += 8)
+ output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
+ return output;
+},
+"binb": function(x, len){
+ x[len >> 5] |= 0x80 << (24 - len % 32);
+ x[((len + 64 >> 9) << 4) + 15] = len;
+ var w = Array(80);
+ var a = 1732584193;
+ var b = -271733879;
+ var c = -1732584194;
+ var d = 271733878;
+ var e = -1009589776;
+ for(var i = 0; i < x.length; i += 16){
+ var olda = a;
+ var oldb = b;
+ var oldc = c;
+ var oldd = d;
+ var olde = e;
+ for(var j = 0; j < 80; j++){
+ if(j < 16) w[j] = x[i + j];
+ else w[j] = SHA1.bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
+ var t = SHA1.safe_add(SHA1.safe_add(SHA1.bit_rol(a, 5), SHA1.ft(j, b, c, d)),
+ SHA1.safe_add(SHA1.safe_add(e, w[j]), SHA1.kt(j)));
+ e = d;
+ d = c;
+ c = SHA1.bit_rol(b, 30);
+ b = a;
+ a = t;
+ }
+ a = SHA1.safe_add(a, olda);
+ b = SHA1.safe_add(b, oldb);
+ c = SHA1.safe_add(c, oldc);
+ d = SHA1.safe_add(d, oldd);
+ e = SHA1.safe_add(e, olde);
+ }
+ return Array(a, b, c, d, e);
+},
+"ft": function(t, b, c, d){
+ if(t < 20) return (b & c) | ((~b) & d);
+ if(t < 40) return b ^ c ^ d;
+ if(t < 60) return (b & c) | (b & d) | (c & d);
+ return b ^ c ^ d;
+},
+"kt": function(t){
+ return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
+ (t < 60) ? -1894007588 : -899497514;
+},
+"safe_add": function(x, y){
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+},
+"bit_rol": function(num, cnt) { return (num << cnt) | (num >>> (32 - cnt)) }
+}
+var Away = {
+ "UnseenMsgCounter": 0,
+ "OrigTitle": "",
+ "HasFocus": true,
+ "UpdateFrequency": 3000,
+
+ "onFocus": function() {
+ Away.HasFocus = true;
+ Away.UnseenMsgCounter = 0;
+ // Courtesy http://stackoverflow.com/questions/2952384/changing-the-window-title-when-focussing-the-window-doesnt-work-in-chrome
+ window.setTimeout(function () { $('title').text(Away.OrigTitle); }, 100);
+ },
+ "onBlur": function() {
+ Away.HasFocus = false;
+ },
+
+ "updateTitle": function () {
+ if (Away.UnseenMsgCounter > 0) {
+ var plural = Away.UnseenMsgCounter > 1 ? 's' : '';
+ $('title').text(Away.UnseenMsgCounter + ' new dump' + plural + '! | ' + Away.OrigTitle);
+ }
+ setTimeout(Away.updateTitle, Away.UpdateFrequency);
+
+ },
+ "startTitleUpdater": function() {
+ Away.OrigTitle = $('title').text();
+ $(window).blur(Away.onBlur);
+ $(window).focus(Away.onFocus);
+ setTimeout(Away.updateTitle, Away.UpdateFrequency);
+ }
+};
+// Called on loading a chatroom
+
+function initChat() {
+ Search.initInpage()
+
+ $('#textbutton input').attr('checked', TextEnabled).change(setTextEnable);
+ $('#imgbutton input').attr('checked', ImgsEnabled).change(setImgsEnable);
+/* $('#clearbutton input').click(function() {
+ track('UI', 'ClearScreen');
+ $('.dump').remove();
+ $(this).removeAttr('checked');
+ return false;
+ });
+*/
+
+ $('.oldmsg').each(function() {
+ var dump = $(this);
+ var content = dump.find(".content")
+ MessageContentCache[dump.attr("id").substr(8)] = content.text()
+ content.html(buildMsgContent(content.text(), Recips));
+
+ if ((ImgsEnabled && dump.hasClass('contains-image')) || (TextEnabled && !dump.hasClass('contains-image')))
+ dump.show();
+ else
+ dump.hide();
+ });
+ Drag.bindImages();
+
+ $('#msgInput').keyup(ifEnter(submitMessage));
+ $('#msgSubmit').click(sendClicked);
+ $('#palette-button').click(paletteClicked);
+
+ messageList = $("#messageList")[0]
+
+ initChatThumb();
+
+ scrollToEnd()
+ scrollWatcher()
+
+ // see /static/webcam/webcam.js
+ if ('webcam' in window) webcam.init()
+
+ startChatUpdater();
+}
+
+
+var imgZoomThreshhold = [125, 125];
+
+function initChatMsgs() {
+ $('.msgDiv .content').live('mouseenter', function(e) {
+ $(this).addClass('msg-hover');
+ });
+
+ $('.msgDiv .content').live('mouseleave', function(e) {
+ $(this).removeClass('msg-hover');
+ });
+
+ $('.msgDiv .content .img-wrapper').live('mouseenter', function(e) {
+ var img = $(this).find('img');
+
+ if (img.width() < imgZoomThreshhold[0] || img.height() < imgZoomThreshhold[1])
+ return;
+
+ var zoomlink = $('<a>')
+ .attr({'href': img.attr('src') })
+ .addClass('msg-image-zoom')
+ .append($('<img>').attr('src', 'http://dump.fm/static/img/zoom.gif')
+ .addClass('zoom-icon'))
+ .click(function() { window.open(img.attr('src')); return false; });
+ $(this).append(zoomlink);
+ });
+
+ $('.msgDiv .content .img-wrapper').live('mouseleave', function(e) {
+ $(this).find('.msg-image-zoom').remove();
+ });
+
+
+ $('.content').live('click', function(e) {
+ var tagName = e.target.tagName;
+ if (tagName == 'A' || tagName == 'EMBED' || $(e.target).hasClass('youtube-thumb')) {
+ return true;
+ }
+ var msg = $(this).parent('.msgDiv');
+ var wasFavorited = msg.hasClass("favorite");
+ var button = msg.find('.chat-thumb');
+ if (wasFavorited) {
+ $(button).attr("src", Imgs.chatThumbOff);
+ } else {
+ $(button).attr("src", Imgs.chatThumbBig);
+ $(button).stop().animate(Anim.chatThumbBig, 'fast').animate(Anim.chatThumb, 'fast', 'swing');
+ }
+ Tag.favorite(button);
+ return false;
+ });
+}
+function startChatUpdater() {
+ setTimeout(refresh, 1000);
+}
+
+function makePlainText() {
+ var j = $(this);
+ j.text(j.text());
+}
+
+
+
+function initChatThumb(){
+ $(".chat-thumb").live('mouseover mouseout',
+ function(e) {
+ var favorited = $(this).parents(".dump").hasClass("favorite") ? true : false;
+ if (e.type == "mouseover") {
+ if (favorited) {
+ $(this).attr("src", Imgs.chatThumbOff);
+ } else {
+ $(this).attr("src", Imgs.chatThumbBig);
+ $(this).stop().animate(Anim.chatThumbBig, 'fast')
+ }
+ } else { // mouseout
+ if (favorited) {
+ $(this).attr("src", Imgs.chatThumb);
+ $(this).stop().animate(Anim.chatThumb, 'fast');
+ } else {
+ $(this).delay(600).stop().animate(Anim.chatThumbTiny, 'fast', 'swing',
+ function(){
+ $(this).attr("src", Imgs.chatThumbDot)
+ $(this).animate(Anim.chatThumb, 0)
+ })
+ }
+ }
+ })
+}
+
+
+
+
+
+// grab message id etc from some element e that's inside a dump
+// (messages have something like id="message-0001" class="dump" )
+function getMessageInfo(e){
+ var message = $(e).parents(".dump")
+ var id = message.attr("id").substr(8) // cut "message-001" to "001"
+ var nick = message.attr("nick")
+ var link = "http://dump.fm/p/" + nick + "/" + id
+ var content = message.find(".linkify")
+ if (!content.length) content = message.find(".content")
+ var rawContent = content.html()
+ var img = content.find("img").attr("src")
+ var via = "via " + nick + " on dump.fm"
+ return {"nick": nick, "id": id, "link": encodeURIComponent(link),
+ "content": rawContent, "img": encodeURIComponent(img),
+ "via": encodeURIComponent(via)}
+}
+// Message Handling
+
+var ImageMsgCount = 0
+function removeOldMessages(){
+ // don't count posts that are all text
+ if (LastMsgContainsImage) ImageMsgCount += 1;
+ while (ImageMsgCount > MaxImagePosts) {
+ var imgMsg = $(".contains-image:first")
+ if (imgMsg.length) {
+ imgMsg.prevAll().remove() // remove all text messages before the image message
+ imgMsg.remove()
+ } else break;
+ ImageMsgCount -= 1;
+ }
+}
+
+var TextEnabled = Preferences.getProperty("chat.textEnabled", "true") == "none";
+var ImgsEnabled = Preferences.getProperty("chat.imgsEnabled", "true") == "true";
+
+function muteSelector() {
+ var muted = [];
+ for (nick in MUTES) {
+ muted.push(".nick_" + nick);
+ }
+ return muted.join(",");
+}
+
+function setTextEnable() {
+ var muted = muteSelector();
+ if ($(this).attr('checked')) {
+ TextEnabled = true;
+ Preferences.setProperty("chat.textEnabled", "false");
+ track('UI', 'TextEnabled');
+ $('.dump').not('.contains-image,'+muted).show();
+ } else {
+ TextEnabled = false;
+ Preferences.setProperty("chat.textEnabled", "false");
+ track('UI', 'TextDisabled');
+ $('.dump').not('.contains-image').hide()
+ }
+};
+
+function setImgsEnable() {
+ var muted = muteSelector();
+ if ($(this).attr('checked')) {
+ ImgsEnabled = true;
+ Preferences.setProperty("chat.imgsEnabled", "true");
+ track('UI', 'ImgsEnabled');
+ $('.contains-image').not(muted).show();
+ } else {
+ ImgsEnabled = false;
+ Preferences.setProperty("chat.imgsEnabled", "false");
+ track('UI', 'ImgsDisabled');
+ $('.contains-image').hide();
+ }
+};
+
+
+// scrolling stuff
+// this code keeps the div scrolled to the bottom, but will also let the user scroll up, without jumping down
+
+function isScrolledToBottom(){
+ var threshold = 15;
+
+ var containerHeight = messageList.style.pixelHeight || messageList.offsetHeight
+ var currentHeight = (messageList.scrollHeight > 0) ? messageList.scrollHeight : 0
+
+ var result = (currentHeight - messageList.scrollTop - containerHeight < threshold);
+
+ return result;
+}
+
+function scrollIfPossible(){
+ if (lastScriptedScrolledPosition <= messageList.scrollTop || isScrolledToBottom())
+ scrollToEnd()
+}
+
+var lastScriptedScrolledPosition = 0
+function scrollToEnd(){
+ messageList.scrollTop = messageList.scrollHeight
+ lastScriptedScrolledPosition = messageList.scrollTop
+}
+
+function scrollWatcher(){
+ scrollIfPossible()
+ setTimeout(scrollWatcher, 500)
+}
+
+
+// Called upon loading /directory
+
+function initDirectory() {
+ $('.linkify').each(function() {
+ var t = $(this);
+ t.html(buildMsgContent(t.text()));
+ });
+ Search.initInpage()
+ initLogThumb('.dlogged-dump .thumb', '.dlogged-dump');
+}
+
+// See log.js
+function dragTarget (target, callbacks) {
+ if (! target) return;
+ var base = this;
+ base.target = target;
+ base.callbacks = callbacks;
+ base.init = function(){
+ base.bind();
+ }
+ base.bind = function(){
+ base.target.addEventListener('dragenter', base.enter, false);
+ base.target.addEventListener('dragover', base.over, false);
+ base.target.addEventListener('dragleave', base.leave, false);
+ base.target.addEventListener('dragend', base.end, false);
+ base.target.addEventListener('drop', base.drop, false);
+ }
+ base.enter = function(e){
+ base.target.classList.add('over');
+ if ('enter' in callbacks) {
+ callbacks.enter(Drag.imgSrc);
+ }
+ return false;
+ }
+ base.over = function(e){
+ if (e.preventDefault) {
+ e.preventDefault(); // Necessary. Allows us to drop.
+ }
+ if ('over' in callbacks) {
+ callbacks.over(Drag.imgSrc);
+ }
+ return false;
+ }
+ base.leave = function(e){
+ base.target.classList.remove('over');
+ if ('leave' in callbacks) {
+ callbacks.leave(Drag.imgSrc);
+ }
+ return false;
+ }
+ base.end = function(e){
+ base.target.classList.remove('over');
+ if ('leave' in callbacks) {
+ callbacks.leave(Drag.imgSrc);
+ }
+ return false;
+ }
+ base.drop = function(e){
+ base.target.classList.remove('over');
+ if (e.stopPropagation) {
+ e.stopPropagation(); // stops the browser from redirecting.
+ }
+ if (e.preventDefault) {
+ e.preventDefault();
+ }
+ if ('drop' in callbacks) {
+ callbacks.drop(Drag.imgSrc);
+ }
+ return false;
+ }
+ base.init();
+};
+
+window.Drag = {
+ 'imgSrc': "",
+ 'bindImages': function (){
+ $('.unbound').each(function(index,img){
+ img.addEventListener('dragstart', Drag.start, false);
+ $(this).removeClass('unbound');
+ });
+ },
+ 'start': function(e){
+ if (this.src) {
+ Drag.imgSrc = this.src;
+ }
+ }
+}
+
+$(function(){
+ // dragging images into the manual fave palette
+ var palette = document.getElementById("manual-palette");
+ var paletteButton = document.getElementById("manual-palette-button");
+ var paletteCallbacks = {
+ 'enter': function() {
+ manPaletteShow();
+ },
+ 'leave': function() {
+ manPaletteHide();
+ },
+ 'drop': function (url) {
+ addManualFav(url);
+ }
+ };
+ Drag.manualPalette = new dragTarget (palette, paletteCallbacks);
+ Drag.manualPaletteButton = new dragTarget (paletteButton, paletteCallbacks);
+
+ // dragging images directly into the chatbox (fixes chrome and safari)
+ if ($.browser.mozilla) return;
+ var msgInput = document.getElementById("msgInput");
+ Drag.chatbox = new dragTarget (msgInput, {
+ "drop": function(url) {
+ msgInput.value += " " + url;
+ msgInput.value += " ";
+ msgInput.focus();
+ }
+ });
+});
+// Favs
+
+function buildFav(f) {
+ var h = '<div class="fav-note">'
+ + '<img src="' + RootDomain + 'static/img/thumbs/chatheartover.gif">'
+ + '<a href="' + RootDomain + f.from + '">' + f.from + '</a>'
+ + '&nbsp;<span>just faved you!</span>'
+ + '</div>';
+ return $(h);
+}
+
+function removeFavAndHideBox() {
+ $(this).remove();
+ if ($('#favbox').children().length == 0)
+ $('#favbox').hide();
+}
+
+function showFav(f) {
+ $('#favbox').show();
+ buildFav(f).appendTo('#favbox').animate(
+ {"opacity": 0},
+ {"duration": 9000,
+ "complete": removeFavAndHideBox
+ });
+}
+
+
+function updateFavs(fs) {
+ if (fs.length == 0)
+ return;
+console.log("new faves");
+ $('#favbox').show();
+ $(fs).each(function(i, f) { showFav(f) });
+}
+
+// well fuck webkit for not supporting {text-decoration: blink}
+
+function blinkStart(){
+ blinkTimer = setInterval(function(){
+ $(".blink").removeClass("blink").addClass("blink-turning-off")
+ $(".blink-off").removeClass("blink-off").addClass("blink")
+ $(".blink-turning-off").removeClass("blink-turning-off").addClass("blink-off")
+ },500);
+}
+
+function blinkStop(){
+ clearInterval(blinkTimer);
+}
+
+
+
+//big hand stuff
+// TODO: replace this with simple pointer-events thing.
+function initBigHand(id){
+ var cursorId = "#cursor-big"
+ var cursor = $(cursorId)[0]
+
+ // jquery's reported element sizes are not exactly the same as the browser's 'mouseover' target sizes
+ // so we'll allow a few pixels extra
+ var fudgeFactor = 2
+
+ $(id).addClass("no-cursor")
+
+ // i have to do this weirdly bc putting the cursor image where the mouse cursor is causes problems with mouse events:
+ // * it stops mousemove events on the image below the mouse cursor
+ // * it fucks up mouseover/out and even mouseenter/leave events, as well as click
+
+ // so i am doing this:
+ // on mousing over the image:
+ // make cursor visible
+ // find image co-ords
+ // bind a global mousemove func
+ // bind cursor click event
+ // unbind mouseover
+ // mousemove func:
+ // move image to mouse co-ords
+ // if mouse co-ords are outside the image co-ords:
+ // make cursor invisible
+ // unbind mousemove func
+ // unbind cursor click event
+
+ var mousemove = function(e){
+ var y = e.pageY, x = e.pageX, coords = initBigHand.coords
+
+ cursor.style.top = y + "px"
+ cursor.style.left = x - 32 + "px" // 32: (4 pixels * 8 pixels per big pixel) to line up pointy finger with cursor
+ if (y < coords.top ||
+ y > coords.bottom ||
+ x < coords.left ||
+ x > coords.right) {
+ $(cursorId).addClass('invisible')
+ $(cursorId).css({"top": 0, "left": 0 })
+ $(cursorId).unbind('click', cursorClick)
+ $('logo7').unbind('mousemove', mousemove)
+ $(id).mouseover(imageMouseOver)
+ }
+ }
+
+ var cursorClick = function(){ $(id).click() }
+
+ var imageMouseOver = function(){
+ //console.log("moused over...")
+ initBigHand.coords = {
+ "left": $(id).offset().left - fudgeFactor,
+ "top": $(id).offset().top - fudgeFactor,
+ "right": $(id).offset().left + $(id).width() + fudgeFactor,
+ "bottom": $(id).offset().top + $(id).height() + fudgeFactor
+ }
+ $('body').mousemove(mousemove)
+ $(cursorId).click(cursorClick)
+ $(cursorId).removeClass('invisible')
+ $(id).unbind('mouseover', imageMouseOver)
+ }
+
+ $(id).mouseover(imageMouseOver)
+
+}
+
+
+// uhhh todo: move preload stuff into js:
+// var nextImage = new Image();
+// nextImage.src = "your-url/newImage.gif";
+
+// mAcRoMeDiA sHiT
+function MM_swapImgRestore() { //v3.0
+ var i,x,a=document.MM_sr; for(i=0;a&&i<a.length&&(x=a[i])&&x.oSrc;i++) x.src=x.oSrc;
+}
+
+function MM_preloadImages() { //v3.0
+ var d=document;if(d.images){ if(!d.MM_p) d.MM_p=new Array();var i,j=d.MM_p.length,a=MM_preloadImages.arguments; for(i=0; i<a.length; i++) if (a[i].indexOf("#")!=0){ d.MM_p[j]=new Image; d.MM_p[j++].src=a[i];}}
+}
+
+function MM_findObj(n, d) { //v4.01
+ var p,i,x; if(!d) d=document; if((p=n.indexOf("?"))>0&&parent.frames.length) {
+ d=parent.frames[n.substring(p+1)].document; n=n.substring(0,p);}
+ if(!(x=d[n])&&d.all) x=d.all[n]; for (i=0;!x&&i<d.forms.length;i++) x=d.forms[i][n];
+ for(i=0;!x&&d.layers&&i<d.layers.length;i++) x=MM_findObj(n,d.layers[i].document);
+ if(!x && d.getElementById) x=d.getElementById(n); return x;
+}
+
+function MM_swapImage() { //v3.0
+ var i,j=0,x,a=MM_swapImage.arguments; document.MM_sr=new Array; for(i=0;i<(a.length-2);i+=3)
+ if ((x=MM_findObj(a[i]))!=null){document.MM_sr[j++]=x; if(!x.oSrc) x.oSrc=x.src; x.src=a[i+2];}
+}
+// Growl
+
+function buildGrowlDataAndPopDatShit(msg) {
+ var nick = escapeHtml(msg.nick);
+ nick = '<a href="' + RootDomain + nick + ' " style="color:pink">' + nick + '</a>:'
+ var msg = buildMsgContent(msg.content)
+ growl(nick, msg)
+}
+
+function growl(user, msg) {
+ $.gritter.add({title: user, text: msg});
+}
+
+function handleMsgError(resp) {
+ var respText = resp.responseText ? resp.responseText.trim() : false;
+ if (respText == 'MUST_LOGIN') {
+ alert("Can't send message! Please login.");
+ } else if (respText) {
+ alert("Can't send message! " + respText);
+ } else {
+ alert("Can't send message!");
+ }
+}
+/*
+ timb:
+ the ImgCache manages loading images and keeping track of image sizes...
+ it can be passed a bunch of urls to load and a callback that gets called when more images are ready
+ image loading can also be paused and started again.
+ there can be separate ImageCaches, eg, one for search result images, one for chat images (but they all share the actual image cache)
+ In theory it should also avoid a few http requests bc we can just dup DOM nodes for images that are already loaded that don't have cache headers (not sure tho, browsers probably pretty aggressive with that already)
+*/
+
+var ImgCache = {
+ "imgs": {}, // <img> nodes indexed by url
+ "caches": {},
+
+ "init": function(name){
+ // don't clear callback
+ var callback = emptyFunc
+ if (name in ImgCache.caches)
+ callback = ImgCache.caches[name].onImgsLoaded
+ delete ImgCache.caches[name]
+
+ ImgCache.caches[name] = {
+ "loadAtATime": 10,
+ "urlsToLoad": [],
+ "imgsLoading": {},
+ "imgsLoadingCounter" : 0, // a hack so i don't have to iterate over the object to always get its size...
+ "imgsLoaded": {},
+ "onImgsLoaded": callback,
+ "paused": false
+ }
+ },
+
+ "add": function(name, urls){
+ if (!(name in ImgCache.caches)) ImgCache.init(name)
+ if (!$.isArray(urls)) urls = [urls];
+
+ var cache = ImgCache.caches[name]
+
+ urls.forEach(function(url){
+ cache.urlsToLoad.push(url)
+ })
+
+ },
+
+ "config": function(name, cfg){
+ if (!(name in ImgCache.caches)) ImgCache.init(name)
+ var cache = ImgCache.caches[name]
+ for(var key in cfg)
+ cache[key] = cfg[key]
+ },
+
+ "clear": function(name){ ImgCache.init(name) },
+ "pause": function(name){ ImgCache.caches[name].paused = true },
+ "unpause": function(name){ ImgCache.caches[name].paused = false },
+
+ "loadImages": function(cache){
+ if (cache.paused) return;
+
+ while(cache.urlsToLoad.length && cache.imgsLoadingCounter < cache.loadAtATime) {
+ var url = cache.urlsToLoad.shift()
+ if (url in ImgCache.imgs) { // already loading this image
+ var img = ImgCache.imgs[url]
+ if (img.complete) {
+ cache.imgsLoaded[url] = ImgCache.imgs[url]
+ } else if (!(url in cache.imgsLoading)) {
+ cache.imgsLoading[url] = ImgCache.imgs[url]
+ cache.imgsLoadingCounter += 1
+ }
+ } else {
+ var img = new Image()
+ img.src = url
+ img.animated = (parseUri(url)["file"].toLowerCase().substr(-3) == "gif") ? true : false;
+ ImgCache.imgs[url] = img
+ cache.imgsLoading[url] = img
+ cache.imgsLoadingCounter += 1
+ }
+ }
+ },
+ "processLoadingImages": function(cache){
+ for (var url in cache.imgsLoading) {
+ var img = cache.imgsLoading[url]
+ if (img.complete) {
+ cache.imgsLoaded[url] = img
+ delete cache.imgsLoading[url]
+ cache.imgsLoadingCounter -= 1
+ }
+ }
+ },
+
+ "loader": function(){
+ for (name in ImgCache.caches){
+ var cache = ImgCache.caches[name]
+ ImgCache.processLoadingImages(cache) // move images from imgsLoading into imgsLoaded
+ ImgCache.loadImages(cache) // put new images in imgsLoading/imgsLoaded from urlsToLoad
+ for (var url in cache.imgsLoaded) {
+ cache.onImgsLoaded(cache.imgsLoaded) // only call if new images actually loaded
+ delete cache.imgsLoaded
+ cache.imgsLoaded = {}
+ break;
+ }
+
+ }
+ setTimeout(ImgCache.loader, 500)
+ }
+}
+
+ImgCache.loader()
+
+
+// parseUri 1.2.2 from http://blog.stevenlevithan.com/archives/parseuri
+// (c) Steven Levithan <stevenlevithan.com>, MIT License
+// timb: todo: this can't deal with @s in urls correctly. ex: http://www.classicbattletech.com/images/gallery/Combat_Operations_Cover@1280x960.jpg
+function parseUri (str) {
+ var o = parseUri.options,
+ m = o.parser[o.strictMode ? "strict" : "loose"].exec(str),
+ uri = {},
+ i = 14;
+
+ while (i--) uri[o.key[i]] = m[i] || "";
+
+ uri[o.q.name] = {};
+ uri[o.key[12]].replace(o.q.parser, function ($0, $1, $2) {
+ if ($1) uri[o.q.name][$1] = $2;
+ });
+
+ return uri;
+};
+
+parseUri.options = {
+ strictMode: false,
+ key: ["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],
+ q: {
+ name: "queryKey",
+ parser: /(?:^|&)([^&=]*)=?([^&]*)/g
+ },
+ parser: {
+ strict: /^(?:([^:\/?#]+):)?(?:\/\/((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?))?((((?:[^?#\/]*\/)*)([^?#]*))(?:\?([^#]*))?(?:#(.*))?)/,
+ loose: /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/
+ }
+};
+// end parseUri
+// Called upon loading /user/log
+
+function initLog(recips) {
+ Search.initInpage();
+ $('.logged-dump .content').each(function() {
+ var t = $(this);
+ t.html(buildMsgContent(t.text(), recips));
+ });
+ initLogThumb(".logged-dump .thumb", '.dump');
+}
+
+
+function initLogThumb(selector, parentSelector) {
+ $(selector).bind('mouseover mouseout',
+ function(e) {
+ var favorited = $(this).parents(parentSelector).hasClass("favorite") ? true : false;
+ if (e.type == "mouseover") {
+ if (favorited) {
+ $(this).attr("src", Imgs.logThumbOff);
+ } else {
+ $(this).attr("src", Imgs.logThumbBig);
+ $(this).stop().animate(Anim.logThumbBig, 'fast');
+ }
+ } else { // mouseout
+ if (favorited) {
+ $(this).attr("src", Imgs.logThumb);
+ $(this).stop().animate(Anim.logThumb, 'fast');
+ } else {
+ $(this).attr("src", Imgs.logThumbOff);
+ $(this).stop().animate(Anim.logThumb, 'fast');
+ }
+ }
+ })
+}// The root domain is used so that subdomains don't result in
+// spurious extra urls (e.g. both dump.fm/nick and sub.dump.fm/nick)
+var RootDomain = location.href.match(/http:\/\/(\w)+\./)
+ ? 'http://dump.fm/' : '/';
+
+var cache = {};
+var PendingMessages = {};
+var MessageContentCache = {};
+var RawFavs = {};
+var MaxImagePosts = 30;
+
+// todo: preload these. also, look into image sprites (no go on animating their sizes tho)
+// css clipping perhaps?
+Imgs = {
+ "chatThumb": "/static/img/thumbs/smallheartfaved.gif",
+ "chatThumbBig": "/static/img/thumbs/chatheartover.gif",
+ "chatThumbOff": "/static/img/thumbs/smallheart.gif",
+ "chatThumbDot": "/static/img/thumbs/smallheart.gif",
+ "logThumb": "/static/img/thumbs/heartfaved.gif",
+ "logThumbBig": "/static/img/thumbs/heartover.gif",
+ "logThumbOff": "/static/img/thumbs/heart.gif"
+}
+
+Anim = {
+ "chatThumbBig": {"width": "54px", "height": "54px", "right": "0px", "bottom": "2px"},
+ "chatThumbTiny": {"width": "16px", "height": "16px", "right": "8px", "bottom": "8px"},
+ "chatThumb": {"width": "16px", "height": "16px", "right": "4px", "bottom": "4px"},
+ "logThumb": {"width": "27px", "height": "27px", "marginRight": "0px", "marginTop": "0px"},
+ "logThumbBig": {"width": "64px", "height": "64px", "marginRight": "-2px", "marginTop": "-2px"}
+}
+
+
+var Preferences = {
+ "Domain": '.dump.fm',
+
+ "getProperty": function(prop, defaultValue) {
+ var value = $.cookie(prop);
+ return (value !== null) ? value : defaultValue;
+ },
+
+ "setProperty": function(prop, val) {
+ $.cookie(prop, val, { domain: Preferences.Domain, path: '/' });
+ },
+
+ "delProperty": function(prop) {
+ $.cookie(prop, null, { domain: Preferences.Domain, path: '/' });
+ }
+};
+
+// Messages
+
+
+function buildMsgContent(content, recips) {
+ if (content.substr(0,6) == "<safe>")
+ return content.substr(6,content.length - 13);
+ else return linkify(escapeHtml(content), recips);
+}
+
+
+function invalidImageDomain(content) {
+ var words = content.toLowerCase().split(' ');
+ for (var i = 0; i < words.length; i++) {
+ var w = words[i];
+ if (PicRegex.test(w)) {
+ for (var j = 0; j < InvalidDomains.length; j++) {
+ var d = InvalidDomains[j];
+ if (w.indexOf(d) != -1) {
+ return d;
+ }
+ }
+ }
+ }
+}
+
+function clearMessages(){
+ track('UI', 'ClearScreen');
+ $('.dump').remove();
+}
+
+function submitMessage() {
+ var content = $.trim($('#msgInput').val());
+
+ if (content == "/clear") {
+ clearMessages()
+ $('#msgInput').val('');
+ return;
+ }
+
+ var invalidDomain = invalidImageDomain(content);
+ if (invalidDomain) {
+ $('#msgInput').blur(); // Remove focus to prevent FF alert loop
+ alert("Sorry, cannot accept images from " + invalidDomain + ". Maybe host the image elsewhere?");
+ return;
+ }
+
+ $('#msgInput').val('');
+ if (content == '') { return; }
+ if (content.length > 2468) {
+ alert("POST TOO LONG DUDE!");
+ return;
+ }
+ PendingMessages[content] = true;
+
+ var msg = { 'nick': Nick, 'content': content };
+ var div = addNewMessage(msg, true);
+
+ var onSuccess = function(json) {
+ if (typeof pageTracker !== 'undefined') {
+ pageTracker._trackEvent('Message', 'Submit',
+ typeof Room !== 'undefined' ? Room : 'UnknownRoom');
+ }
+ div.attr('id', 'message-' + json.msgid)
+ .removeClass('loading').addClass('loaded');
+ div.find('.content').html(buildMsgContent(content, json.recips));
+ };
+ var onError = function(resp, textStatus, errorThrown) {
+ div.remove();
+ handleMsgError(resp);
+ };
+
+ $.ajax({
+ type: 'POST',
+ timeout: 15000,
+ url: '/msg',
+ data: { 'room': Room, 'content': content },
+ cache: false,
+ dataType: 'json',
+ success: onSuccess,
+ error: onError
+ });
+}
+
+function ifEnter(fn) {
+ return function(e) {
+ if (e.keyCode == 13) { fn(); }
+ };
+}
+
+
+
+// todo:
+// isLoading doesn't get passed the right thing by $.map in addMessages
+function buildMessageDiv(msg, opts) {
+ var opts = opts || {};
+ var nick = escapeHtml(msg.nick);
+ removeOldMessages();
+
+ var builtContent = buildMsgContent(msg.content, msg.recips);
+ var msgId = ('msg_id' in msg) ? 'id="message-' + msg.msg_id + '"' : '';
+ var loadingClass = opts.isLoading ? ' loading' : '';
+ var containsImageClass = LastMsgContainsImage ? ' contains-image' : '';
+ var displayStyle = ((ImgsEnabled && LastMsgContainsImage) || (TextEnabled && !LastMsgContainsImage)) ? '' : ' style="display: none"';
+ if (displayStyle === '' && MUTES[nick])
+ displayStyle = ' style="display: none"';
+
+ return '<div class="msgDiv dump ' + loadingClass + containsImageClass + " nick_" + nick + '" ' + msgId + displayStyle + '>'
+ + '<span class="nick"><b><a href="' + RootDomain + nick + ' ">' + nick + '</a></b>'
+ + ' <img src="'+Imgs.chatThumbDot+'" class="chat-thumb" onclick="Tag.favorite(this)"> '
+ + '</span>'
+ + '<span class="content">' + builtContent + '</span>'
+ + '</div>';
+}
+
+function addNewMessages(msgs) {
+ var msgStr = $.map(msgs, buildMessageDiv).join('');
+ $('#messageList').append(msgStr);
+ Drag.bindImages();
+}
+
+function addNewMessage(msg, isLoading) {
+ var msgStr = buildMessageDiv(msg, { isLoading: true });
+ var div = $(msgStr).appendTo('#messageList');
+ Drag.bindImages();
+ return div;
+}
+
+function setUserList(users) {
+ $("#userList").html($.map(users, buildUserDiv).join(''));
+}
+
+function flattenUserJson(users) {
+ var s = "";
+ $.map(users.sort(), function(user) {
+ s += user.nick + user.avatar;
+ });
+ return s;
+}
+
+function updateUI(msgs, users, favs) {
+ if (window['growlize'] && msgs && msgs.length > 0) {
+ $.map(msgs, buildGrowlDataAndPopDatShit)
+ } else if (msgs && msgs.length > 0) {
+ addNewMessages(msgs);
+ }
+ if (users !== null) {
+ var flattened = flattenUserJson(users);
+ if (!('userlist' in cache) || flattened != cache.userlist) {
+ $("#userList").html($.map(users.sort(sortUsersByAlpha), buildUserDiv).join(''));
+ }
+ cache.userlist = flattened
+ }
+ updateFavs(favs);
+}
+
+function sortUsersByAlpha(a, b){
+ var nickA = a.nick.toLowerCase()
+ var nickB = b.nick.toLowerCase()
+ if (nickA > nickB) return 1
+ else if (nickA < nickB) return -1
+ return 0
+}
+
+function isDuplicateMessage(m) {
+ if (m.nick == Nick && m.content in PendingMessages) {
+ delete PendingMessages[m.content];
+ return true;
+ } else {
+ return false;
+ }
+}
+
+function refresh() {
+ var onSuccess = function(json) {
+ try {
+ Timestamp = json.timestamp;
+ $.map(json.messages, function(msg){ MessageContentCache[msg.msg_id.toString()] = msg.content })
+
+ var messages = $.grep(
+ json.messages,
+ function(m) { return !isDuplicateMessage(m) });
+ updateUI(messages, json.users, json.favs);
+ if (!Away.HasFocus)
+ Away.UnseenMsgCounter += messages.length;
+ } catch(e) {
+ if (IsAdmin && window.console) {
+ console.log(e);
+ }
+ }
+ setTimeout(refresh, 1500);
+ };
+ var onError = function(resp, textStatus, errorThrown) {
+ var msg = $.trim(resp.responseText);
+ if (msg == "UNKNOWN_ROOM")
+ location.href = "http://dump.fm";
+ if (IsAdmin && window.console) {
+ console.error(resp, textStatus, errorThrown);
+ }
+ setTimeout(refresh, 4000);
+ };
+
+ $.ajax({
+ type: 'GET',
+ timeout: 5000,
+ url: '/refresh',
+ data: { 'room': Room, 'since': Timestamp },
+ cache: false,
+ dataType: 'json',
+ success: onSuccess,
+ error: onError
+ });
+}
+
+function sendClicked(){
+ track('UI', 'SendButtonActuallyClicked');
+ submitMessage();
+}
+
+
+
+
+function setupUpload(elementId, roomKey) {
+ var onSubmit = function(file, ext) {
+ if (!(ext && /^(jpg|png|jpeg|gif|bmp|svg)$/i.test(ext))) {
+ alert('SORRY, NOT AN IMAGE DUDE... ');
+ return false;
+ }
+ };
+ var onComplete = function(file, response) {
+ var r = $.trim(response);
+ if (r.match(/FILE_TOO_BIG/)) {
+ var maxSize = r.split(" ")[1] / 1024;
+ alert("Sorry. Your file is just too darn big. "
+ + maxSize + "KB or less please.");
+ return;
+ } else if (r.match(/FILE_NOT_IMAGE/)) {
+ alert("What did you upload? Doesn't seem like an image. Sorry.");
+ return;
+ } else if (r.match(/INVALID_RESOLUTION/)) {
+ var maxWidth = r.split(" ")[1];
+ var maxHeight = r.split(" ")[2];
+ alert("Sorry, the maximum image resolution is "
+ + maxWidth + "x" + maxHeight);
+ return;
+ } else if (r != "OK") {
+ alert(r);
+ return;
+ }
+
+ if (typeof pageTracker !== 'undefined') {
+ var r = typeof Room !== 'undefined' ? Room : 'UnknownRoom';
+ pageTracker._trackEvent('Message', 'Upload', r);
+ }
+ }
+ new AjaxUpload(elementId, {
+ action: '/upload/message',
+ autoSubmit: true,
+ name: 'image',
+ data: { room: roomKey },
+ onSubmit: onSubmit,
+ onComplete: onComplete
+ });
+}
+
+// manual fave palette by ohgod
+$(function(){
+ $('#manual-palette-button').click(manPaletteClicked);
+ $('#manual-add-button').click(function () {
+ addManualFav($('#manual-add-url-txt').val().trim());
+ $('#manual-add-url-txt').val("");
+ });
+ $('#manual-remove-button').click(function () {
+ removeManualFav($('#manual-add-url-txt').val().trim());
+ $('#manual-add-url-txt').val("");
+ });
+});
+
+function manPaletteClicked() {
+ if ($("#manual-palette").css("display") == "none") {
+ manPaletteShow();
+ } else {
+ $("#manual-palette").css("display", "none");
+ $("#manual-palette-thumbs").html("");
+ }
+};
+
+function manPaletteShow() {
+ $("#manual-palette").show();
+ if (! hasLocalStorage()) {
+ $('#manual-palette-localstorage-error').show()
+ } else if (manPaletteIsEmpty()) {
+ $('#manual-palette-thumbs-empty').show();
+ } else {
+ manualPaletteBuildImageThumbs();
+ }
+};
+
+function hasLocalStorage() {
+ try {
+ return 'localStorage' in window && window['localStorage'] !== null;
+ } catch (e) {
+ return false;
+ }
+}
+
+function manPaletteIsEmpty() {
+ return localStorage.manual_favs === undefined || localStorage.manual_favs === "[]" || localStorage.manual_favs === "";
+};
+
+function getManualFaves() {
+ return localStorage.manual_favs ? JSON.parse(localStorage.manual_favs) : [];
+};
+
+function setManualFaves(mfavs) {
+ localStorage.manual_favs = JSON.stringify(mfavs);
+};
+
+function manualPaletteBuildImageThumbs() {
+ var imgs = JSON.parse(localStorage.manual_favs);
+ if (imgs && imgs.length != 0) {
+ for (var i = 0; i < imgs.length; i++) {
+ $("#manual-palette-thumbs").append("<img onclick='paletteToChat(this)' src='" + imgs[i] + "'>");
+ }
+ }
+};
+
+function addManualFav(url) {
+ if (!url) return;
+ if (hasLocalStorage()) {
+ if (manPaletteIsEmpty())
+ $("#manual-palette-thumbs").html("");
+ var mfavs = getManualFaves();
+ if ($.inArray(imgsrc, mfavs) == -1) {
+ mfavs.push(imgsrc);
+ setManualFaves(mfavs);
+ $("#manual-palette-thumbs").append("<img onclick='paletteToChat(this)' src='" + url + "'>");
+ }
+ }
+};
+
+function removeManualFav(url) {
+ if (hasLocalStorage()) {
+ var mfavs = getManualFaves();
+ var idx = $.inArray(url, mfavs);
+ if (idx != -1) {
+ mfavs.splice(idx, 1);
+ setManualFaves(mfavs);
+ $("#manual-palette-thumbs").html("");
+ manualPaletteBuildImageThumbs();
+ }
+ }
+};
+
+
+
+function paletteToChat(img){
+ var chatText = $("#msgInput").val()
+ if (chatText.length && chatText[chatText.length - 1] != " ")
+ chatText += " "
+ chatText += $(img).attr("src") + " "
+ $("#msgInput").val(chatText)
+ $("#msgInput").focus().val($("#msgInput").val()) // http://stackoverflow.com/questions/1056359/
+ paletteHide()
+}
+
+paletteImageCache = false
+function paletteBuildImageThumbs(){
+ if (paletteImageCache) {
+ var imgs = paletteImageCache
+ } else {
+ var imgs = []
+ var dupeFilter = {}
+ for(fav in RawFavs){
+ var parsedImgs = getImagesAsArray(RawFavs[fav])
+ for (var i=0; i<parsedImgs.length; i++){
+ var img = parsedImgs[i]
+ if (!dupeFilter[img]) {
+ imgs.push(img)
+ dupeFilter[img] = true
+ }
+ }
+ }
+ paletteImageCache = imgs
+ }
+
+ for(var i=0; i<imgs.length; i++){
+ $("#palette-thumbs").append("<img onclick='paletteToChat(this)' src='"+imgs[i]+"'>")
+ }
+}
+
+function paletteShow(){
+ $("#palette").css("display", "block")
+ if (isEmptyObject(RawFavs)) {
+ $('#palette-thumbs').html('<div style="width:300px;color:#000;">This is where all the stuff you FAV goes!<br><br>To FAV a post click the little heart <img src="/static/img/thumbs/smallheart.gif"> next to a users name.<br><br> Everything you fav gets saved to your profile.. Have fun!</div>');
+ } else {
+ paletteBuildImageThumbs();
+ }
+}
+function paletteHide(){
+ $("#palette").css("display", "none")
+ $("#palette-thumbs").html("")
+}
+
+function paletteToggle(){
+ if ($("#palette").css("display") == "none")
+ paletteShow()
+ else
+ paletteHide()
+}
+
+
+function paletteClicked(){
+ track('UI', 'FavPaletteActuallyClicked');
+ paletteToggle();
+}
+
+function initProfile(recips) {
+ Search.initInpage();
+ $(".linkify-text").each(function() {
+ var text = jQuery(this).text();
+ jQuery(this).html(linkifyWithoutImage(text));
+ });
+
+ $(".linkify-full").each(function() {
+ $(this).html(buildMsgContent($(this).text(), recips));
+ });
+
+ $('#edit-toggle').click(enableProfileEdit);
+ activateProfileEditable();
+
+ $('.dash-dump .content').each(function() {
+ var t = $(this);
+ t.html(buildMsgContent(t.text()));
+ });
+};
+
+
+function enableProfileEdit() {
+ $('img#contact').replaceWith('<div id="contact" class="linkify"></div>');
+ $('img#bio').replaceWith('<div id="bio" class="linkify"></div>');
+ $('#contact, #bio, #avatar').addClass('editable');
+ $('#avatar-editing').show();
+ var resetPage = function() { location.reload() };
+ $('#edit-toggle a').text('done editing').click(resetPage);
+ activateProfileEditable();
+}
+
+
+function activateProfileEditable() {
+ var onSubmit = function(attr, newVal, oldVal) {
+ newVal = $.trim(newVal);
+ if (newVal == oldVal) { return oldVal };
+
+ $.ajax({
+ type: "POST",
+ timeout: 5000,
+ url: "/update-profile",
+ data: { 'attr': attr, 'val': newVal }
+ });
+ if (attr == 'avatar') {
+ if (newVal != "") {
+ var s = '<img id="avatarPic" src="' + newVal + '" width="150" />';
+ $('#avatarPic').replaceWith(s).show();
+ } else {
+ $('#avatarPic').hide();
+ }
+ }
+ return escapeHtml(newVal);
+ };
+
+ if ($('#avatar-editing').length > 0)
+ setupUploadAvatar('uploadp');
+
+ var textareaOpts = { 'default_text': 'Enter here!',
+ 'callback': onSubmit,
+ 'field_type': 'textarea',
+ 'callbackShowErrors': false };
+ $('#contact.editable, #bio.editable')
+ .editInPlace(textareaOpts)
+ .each(makePlainText);
+}
+
+
+function setupUploadAvatar(elementId) {
+ // NOTE: AjaxUpload responses aren't converted from JSON.
+ var onSubmit = function(file, error) {
+ $('#spinner').show();
+ };
+ var onComplete = function(file, resp) {
+ $('#spinner').hide();
+ var r = $.trim(resp);
+
+ if (r == 'INVALID_REQUEST') {
+ location.reload();
+ } else if (r == 'NOT_LOGGED_IN') {
+ location.reload();
+ } else if (r == 'INVALID_IMAGE') {
+ alert("Sorry, dump.fm can't deal with your image. Pick another :(");
+ return;
+ } else if (r.match(/FILE_TOO_BIG/)) {
+ var maxSize = r.split(" ")[1] / 1024;
+ alert("Sorry. Your avatar is just too fucking big. "
+ + maxSize + "KB or less please.");
+ return;
+ } else if (r.match(/INVALID_RESOLUTION/)) {
+ var maxWidth = r.split(" ")[1];
+ var maxHeight = r.split(" ")[2];
+ alert("Sorry, the maximum avatar resolution is "
+ + maxWidth + "x" + maxHeight);
+ return;
+ }
+ var s = '<img id="dashavatarPic" src="' + r + '" />';
+ $('#dashavatar').html(s).show();
+ $('#dashtotal').css('background-image', 'url(' + r + ')');
+ };
+ new AjaxUpload(elementId, {
+ action: '/upload/avatar',
+ autoSubmit: true,
+ name: 'image',
+ onSubmit: onSubmit,
+ onComplete: onComplete
+ });
+}
+
+
+var Search = {
+
+ 'term': "",
+ 'images': [],
+ 'tokens': [],
+ 'closed': true,
+
+ 'initFullpage': function(){
+ Search.type = "fullpage"
+ Search.init()
+ Search.initSpaceFill = Search.initSpaceFillFullpage
+ },
+
+ 'initInpage': function(){
+ Search.type = "inpage"
+ Search.init()
+ Search.initSpaceFill = Search.initSpaceFillInpage
+ },
+
+ 'init': function(){
+ ImgCache.config("search", {"onImgsLoaded": Search.imgsLoaded})
+
+ $('#search-results-images a').live('hover', Search.resultsHover)
+
+ var input = Search.$input = $("#search-query")
+ var label = "search dump.fm"
+ Search.$container = $("#search-results-images")
+
+ input.val(label)
+ input.focus(function(){
+ if (input.val() == label) input.val("")
+ })
+ input.blur(function(){
+ if (input.val().trim() == '') input.val(label)
+ })
+ input.keydown(ifEnter(Search.doSearch))
+ $("#search-results-images a").live("mouseup", Search.click)
+ },
+
+ 'initSpaceFillFullpage': function() {
+ SpaceFill.init({
+ "container": "#search-results-images",
+ "width": $(document).width(),
+ "height": $(document).height(),
+ "type": "columns",
+ "spacing": "justify",
+ "minMargin": 16,
+ "columnWidth": 250
+ })
+ },
+
+ 'initSpaceFillInpage': function() {
+ SpaceFill.init({
+ "container": "#search-results-images",
+ "width": $(document).width() * 0.93,
+ "height": $(document).height(),
+ "type": "columns",
+ "spacing": "justify",
+ "minMargin": 8,
+ "columnWidth": 120
+ })
+ },
+
+ "resultsHover": function(e){
+ if (e.type == 'mouseover') {
+ var img = ImgCache.imgs[this.href]
+ if (img.animated) {
+ img.width = img.adjWidth
+ img.height = img.adjHeight
+ $(this).addClass("animating")
+ $(this).append(img)
+ }
+ } else {
+ var img = ImgCache.imgs[this.href]
+ if (img.animated) {
+ $(this).removeClass("animating")
+ this.removeChild(img)
+ }
+ }
+ },
+
+ "imgsLoaded": function(imgs){
+ //if (ColumnFill.isSpaceFilled()) return;
+
+ if (Search.closed) return;
+
+ if (Search.$container[0].style.display != "block") {
+ Search.$container.css("display", "block")
+ $("#userList").css("display", "none")
+ Search.setMessage("results for '"+Search.tokens.join(" and ")+"'")
+ }
+
+ for (var url in imgs){
+ var img = imgs[url]
+ if (isImgBroken(img)) continue;
+ var width = img.width
+ var height = img.height
+
+ var maxWidth = SpaceFill.config.columnWidth
+ var maxHeight = Math.floor(SpaceFill.config.columnWidth * 1.2)
+
+ if (width > maxWidth) {
+ scaleFactor = maxWidth / width
+ width = maxWidth
+ height = Math.floor(height * scaleFactor)
+ } else if (height > maxHeight) {
+ scaleFactor = maxHeight / height
+ height = maxHeight
+ width = Math.floor(width * scaleFactor)
+ }
+ img.adjWidth = width
+ img.adjHeight = height
+
+ var c = document.createElement("canvas")
+ c.width = width
+ c.height = height
+ var ctx = c.getContext('2d');
+ ctx.drawImage(img, 0, 0, c.width, c.height)
+
+ var a = document.createElement("a")
+ a.onclick = falseFunc
+ a.href = img.src
+ a.style.width = width + "px"
+ a.style.height = height + "px"
+
+ a.appendChild(c)
+
+ SpaceFill.add(a)
+ }
+ },
+
+ 'setContent': function(x){
+ $("#search-results-images").html(x)
+ },
+
+ 'setMessage': function(x){
+ $("#search-controls").css("display", "block")
+ $("#search-message").html(x)
+ },
+
+ 'searchError': function(error){
+ Search.setContent("")
+ Search.setMessage(error)
+ },
+
+ 'doSearch': function(){
+ Search.closed = false
+ term = $("#search-query").val().trim().toLowerCase()
+ var rawTokens = term.split(" ")
+ Search.tokens = []
+ rawTokens.forEach(function(t){ if (t.length > 2) Search.tokens.push(t) })
+ if (Search.tokens.length == 0) {
+ Search.setMessage("search query too small")
+ } else {
+ Search.setMessage("searching for '"+Search.tokens.join(" and ")+"'")
+ Search.doAjax(Search.tokens.join("+"))
+ }
+ },
+
+ 'doAjax': function(term) {
+ if (Domain == "http://dump.fm") {
+ $.ajax({
+ "dataType": "json",
+ "url": "/cmd/search/" + term,
+ "success": Search.results,
+ "error": Search.error,
+ "timeout": 20000,
+ })
+ } else { // search main site via jsonp
+ $("#search-script").remove()
+ $("head").append("<s"+"cript src='http://dump.fm/cmd/search/"+term+"?callback=Search.results' id='search-script'></s"+"cript>")
+ }
+ },
+
+ 'click': function(e){
+ if (e.which == 1) // left click
+ if (Search.addToChatBoxIfPossible(this))
+ window.open(this.href)
+ else if (e.which == 2) // middle click
+ window.open(this.href)
+ },
+
+ 'addToChatBoxIfPossible': function(img){
+ var chatBoxExists = $("#msgInput").length
+ if (chatBoxExists) {
+ var chatText = $("#msgInput").val()
+ if (chatText.length && chatText[chatText.length - 1] != " ")
+ chatText += " "
+ chatText += $(img).attr("href") + " "
+ $("#msgInput").val(chatText)
+ $("#msgInput").focus().val($("#msgInput").val()) //http://stackoverflow.com/questions/1056359/
+ return false
+ } else return true
+ },
+
+ 'results': function(results){
+ Search.resultsClear()
+ if(results === null || results.length == 0) {
+ Search.setMessage("no results found")
+ } else {
+ Search.initSpaceFill()
+ var urls = []
+ results.forEach(function(r){
+ var url = r.url
+ if (url.charAt(0) == '/')
+ url = 'http://dump.fm/images' + url
+ else
+ url = 'http://' + url
+ urls.push(url)
+ })
+ ImgCache.add("search", urls)
+ }
+ },
+
+ 'resultsClear': function(){
+ $("#search-results-images").html("")
+ //ImgCache.pause("search")
+ ImgCache.clear("search")
+ },
+
+ 'close': function(){
+ Search.resultsClear()
+ Search.closed = true
+ Search.$container.css("display", "none")
+ $("#search-controls").css("display", "none")
+ $("#userList").css("display", "block")
+ }
+
+}
+
+Share = {
+ "openLink": function(url){
+ window.open(url, "_blank")
+ },
+ "facebook": function(button){
+ var message = getMessageInfo(button)
+ var url = "http://www.facebook.com/share.php?u=" + message.img + "&t=" + message.via
+ Share.openLink(url)
+ },
+ "tumblr": function(button){
+ var message = getMessageInfo(button)
+ var url = "http://www.tumblr.com/share?v=3&u=" + message.img + "&t=" + message.via
+ Share.openLink(url)
+ },
+ "twitter": function(button){
+ var message = getMessageInfo(button)
+ var url = "http://twitter.com/home?status=" + message.img + encodeURIComponent(" ") + message.via
+ Share.openLink(url)
+ },
+ "delicious": function(button){
+ var message = getMessageInfo(button)
+ var url = "http://delicious.com/save?url=" + message.img + "&title=" + message.img + "&notes=" + message.via
+ Share.openLink(url)
+ }
+}
+var ColumnFill = {
+ "init": function(){
+ var cfg = SpaceFill.config
+ var numColumns = ColumnFill.calcColumns()
+ ColumnFill.columns = []
+ for (var i = 0; i < numColumns; i++) {
+ ColumnFill.columns.push({"height": 0})
+ }
+
+ cfg.marginWidth = cfg.marginHeight = cfg.minMargin
+
+ if (cfg.spacing == "center") {
+ cfg.columnSpacingAmt = (cfg.width - (numColumns * (cfg.columnWidth + cfg.marginWidth) + cfg.marginWidth)) / 2
+ } else if (cfg.spacing == "justify") {
+ cfg.marginWidth = (cfg.width - (numColumns * cfg.columnWidth)) / (numColumns + 1)
+ }
+
+
+ },
+ "add": function(obj){
+ var cfg = SpaceFill.config
+ var colIndex = ColumnFill.shortestColumn()
+ var col = ColumnFill.columns[colIndex]
+
+ if (cfg.spacing == "center") {
+ var colLeft = colIndex * (cfg.marginWidth + cfg.columnWidth) + cfg.columnSpacingAmt
+ var imgLeft = Math.floor((cfg.marginWidth / 2) + (cfg.columnWidth / 2) - (parseInt(obj.style.width) / 2)) + colLeft + "px"
+ } else if (cfg.spacing == "justify") {
+ var colLeft = (colIndex * (cfg.marginWidth + cfg.columnWidth))
+ var imgLeft = Math.floor((cfg.marginWidth / 2) + (cfg.columnWidth / 2) - (parseInt(obj.style.width) / 2)) + colLeft + "px"
+ }
+
+ obj.style.position = 'absolute'
+ obj.style.top = col.height + cfg.marginHeight + "px"
+ obj.style.left = imgLeft
+
+ col.height += cfg.marginHeight + parseInt(obj.style.height)
+
+ $(cfg.container).append(obj)
+
+ },
+ "calcColumns": function(){
+ var cfg = SpaceFill.config
+ var numColumns = 0
+ var width = cfg.width - cfg.minMargin
+ var columnSub = cfg.columnWidth + cfg.minMargin
+ while (width > columnSub) {
+ width -= columnSub
+ numColumns++
+ }
+ return numColumns
+ },
+
+ "shortestColumn": function(){
+ var min = Infinity
+ var mindex = 0
+ for(var i = 0; i< ColumnFill.columns.length; i++){
+ var col = ColumnFill.columns[i]
+ if ( min > col.height) {
+ min = col.height
+ mindex = i
+ }
+ }
+ return mindex
+ },
+
+ "isSpaceFilled": function(){
+ var config = SpaceFill.config
+ var colIndex = ColumnFill.shortestColumn()
+ var col = ColumnFill.columns[colIndex]
+
+ if (col.height > 4 * config.height) return true;
+ else return false;
+ }
+
+}
+
+var SpaceFill = {
+ "init": function(config){
+ config.type = "columns"
+ SpaceFill.config = config
+
+ SpaceFill.types[config.type].init()
+ SpaceFill.add = SpaceFill.types[config.type].add
+ },
+ "types": { "columns": ColumnFill }
+}
+
+Tag = {
+ "favorite": function(button) {
+ var message = getMessageInfo(button);
+ var favorited = $(button).parents(".dump").hasClass("favorite");
+ if (favorited) {
+ Tag.rm(message.id, "favorite");
+ $(button).parents(".dump").removeClass("favorite");
+ if (RawFavs[message.id]) {
+ delete RawFavs[message.id];
+ paletteImageCache = false;
+ }
+ } else {
+ Tag.add(message.id, "favorite");
+ $(button).parents(".dump").addClass("favorite");
+ if (RawFavs && MessageContentCache[message.id]) { // chat ui stuff
+ if ($("#palette-button").css("display") == "none")
+ paletteButtonShowAnim();
+ RawFavs[message.id] = MessageContentCache[message.id];
+ paletteImageCache = false;
+ }
+ }
+ },
+ "add": function(message_id, tag) {
+ Tag.ajax("/cmd/tag/add", {"message_id": message_id, "tag": tag});
+ },
+ "rm": function(message_id, tag) {
+ Tag.ajax("/cmd/tag/rm", {"message_id": message_id, "tag": tag});
+ },
+ "ajax": function(url, data) {
+ $.ajax({
+ "type": 'POST',
+ "timeout": 5000,
+ "url": url,
+ "data": data,
+ "cache": false
+ });
+ },
+ "animated_fav": function(button, scoreClass, bigTextSize, smallTextSize) {
+ if (!Nick) { return; }
+ Tag.favorite(button);
+ var $dump = $(button).parents(".dump");
+ var isAdding = $dump.hasClass('favorite');
+
+ // Frontpage-favs can be triggered by clicking score number,
+ // so manually sync heart-thumb visual state.
+ if ($(button).hasClass('hallscore')) {
+ $dump.find('.thumb').attr('src', isAdding ? Imgs.logThumb : Imgs.logThumbOff);
+ }
+
+ if ($dump.attr('nick') == Nick)
+ return;
+
+ var $score = $dump.find(scoreClass);
+ var inc = isAdding ? 1 : -1;
+ var oldScore = parseInt($score.text(), 10);
+ $score.text(oldScore + inc);
+ $score.stop().animate({ 'font-size': bigTextSize }, 250, function() {
+ $score.animate({ 'font-size': smallTextSize }, 250);
+ });
+
+ if (isAdding) {
+ var link = $('<a>')
+ .attr('href', Domain + '/' + Nick + '/popular')
+ .append($('<b>').text(Nick))
+ .append(" ");
+ $dump.find('.faver-list').append(link);
+ } else {
+ $dump.find('.faver-list b').filter(function() { return $(this).text() == Nick }).parent().remove();
+ }
+ }
+}
+
+// this doesn't properly deal with eg, .gov.uk .co.ck etc
+function parseDomain(host){
+ var chunks = host.split(".")
+ if (chunks.length == 1) return chunks[0]
+ else return chunks[chunks.length - 2]
+}
+
+function escapeHtml(txt) {
+ if (!txt) { return ""; }
+ // txt = annoyingCaps(txt)
+ return $("<span>").text(txt).html()
+}
+
+
+URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
+PicRegex = /\.(jpg|jpeg|png|gif|bmp|svg|fid)$/i;
+RecipRegex = /(^|\s)@\w+/g;
+TopicRegex = /(^|\s)#\w+/g;
+
+
+function getImagesAsArray(text) {
+ var imgs = []
+ var urls = text.match(URLRegex)
+ if (urls === null) return imgs
+ for (var i = 0; i<urls.length; i++){
+ var url = urls[i]
+ var urlWithoutParams = url.replace(/\?.*$/i, "");
+ if (PicRegex.test(urlWithoutParams))
+ imgs.push(url)
+ }
+ return imgs
+}
+
+function topicReplace(text) {
+ text = $.trim(text).toLowerCase();
+ var topicLabel = text.substring(1);
+ return ' <a target="_blank" href="'+ RootDomain + 't/' + topicLabel + '">' + text + '</a> ';
+}
+
+function recipientReplace(atText, recips) {
+ recips = recips || [];
+
+ var space = '';
+ if (atText[0] == ' ') {
+ atText = atText.slice(1);
+ space = ' ';
+ }
+
+ var nick = atText.slice(1).toLowerCase();
+ var matchedRecip;
+ for (var i = 0; i < recips.length; i++) {
+ if (recips[i].toLowerCase() == nick) {
+ matchedRecip = recips[i];
+ break;
+ }
+ }
+
+ if (matchedRecip) {
+ return space + '<a target="_blank" href="' + RootDomain + matchedRecip + '">@' + matchedRecip + '</a>';
+ } else {
+ return space + atText;
+ }
+}
+
+function linkify(text, recips) {
+ LastMsgContainsImage = false;
+ var recipWrapper = function(text) { return recipientReplace(text, recips); };
+ return text
+ .replace(URLRegex, linkReplace)
+ .replace(RecipRegex, recipWrapper)
+ .replace(TopicRegex, topicReplace);
+}
+
+// use this in escapeHtml to turn everyone's text lIkE tHiS
+function annoyingCaps(text){
+ var chunks = text.split(" ")
+ for(var i=0; i<chunks.length; i++){
+ var chunk = chunks[i]
+ if (!chunk.length || chunk.substr(0,4) == 'http') continue;
+ var letters=chunk.split("")
+ for(var j = 0; j<letters.length; j++){
+ if (j % 2) letters[j] = letters[j].toUpperCase()
+ else letters[j] = letters[j].toLowerCase()
+ }
+ chunks[i] = letters.join("")
+ }
+ return chunks.join(" ")
+}
+
+
+function imgClickHandler() {
+ // Ugly hack. Don't open new links in chat, only in logs.
+ // Ugly hack mkII: ensure middle-click opens images in new tab
+ // c.f. http://code.google.com/p/chromium/issues/detail?id=255
+ if ($.browser.webkit && event.button != 0) {
+ event.stopPropagation();
+ } else {
+ return $('#chatrap').length == 0;
+ }
+}
+
+// durty hack to use a global to check this... but otherwise i'd have to rewrite the String.replace function? :/
+var LastMsgContainsImage = false
+function linkReplace(url) {
+ var lowerurl = url.toLowerCase();
+ if (lowerurl.indexOf('http://') == 0 || lowerurl.indexOf('https://') == 0 || lowerurl.indexOf('ftp://') == 0)
+ linkUrl = url;
+ else
+ linkUrl = 'http://' + url;
+
+ var uri = parseUri(url)
+ var type = getUriType(uri)
+
+ if (type == 'image') {
+ LastMsgContainsImage = true;
+ return "<a target='_blank' href='" + linkUrl + "' class='img-wrapper' onclick='return imgClickHandler()'><img src='" + linkUrl + "' class='unbound'></a>";
+ } else if (type == 'youtube') {
+ Youtube.startAnimation();
+ return "<a target='_blank' class='youtube' href='" + linkUrl + "'>" +
+ "<img class='youtube-thumb' width='130' height='97' src='"+Youtube.nextThumbUrl(uri.queryKey.v)+"'>" +
+ "<img class='youtube-controls' src='/static/img/youtube.controls.png'></a>"
+ } else if (type == 'midi' || type == 'wav') {
+ return '<embed src="'+linkUrl+'" loop="false" autostart="false" volume="80" width="150" height="20" style="vertical-align:bottom"> <a href="'+linkUrl+'">'+uri.file+'</a>'
+ } else
+ return "<a target='_blank' href='" + linkUrl + "'>" + url + "</a>";
+
+}
+
+
+function getUriType(uri){
+ if (PicRegex.test(uri.file.toLowerCase()))
+ return "image";
+
+ var domain = parseDomain(uri.host)
+
+ if (domain == "gstatic" && uri.path == "/images" && 'q' in uri.queryKey)
+ return "image";
+
+ // actual image url = uri.queryKey['q'].split(":").slice(2).join(":") but often the original image is broken...
+
+
+ if (domain == "youtube" && ('v' in uri.queryKey || uri.anchor.indexOf('v') != -1))
+ return "youtube";
+
+ if (uri.path.substr(-4) == ".mid" || uri.path.substr(-5) == ".midi")
+ return "midi"
+
+ if (uri.path.substr(-4) == ".wav")
+ return "wav"
+
+
+ return "link";
+}
+
+function linkifyWithoutImage(text) {
+ LastMsgContainsImage = false
+ return text.replace(URLRegex, linkReplaceWithoutImage);
+}
+
+function linkReplaceWithoutImage(url){
+ var urlWithoutParams = url.replace(/\?.*$/i, "");
+ linkUrl = url.indexOf('http://') == 0 ? url : 'http://' + url;
+
+ return "<a target='_blank' href='" + linkUrl + "'>" + url + "</a>"
+}
+var MUTES = {};
+$(".mute").live("click", function(){
+ $(this).removeClass("mute");
+ $(this).addClass("unmute");
+ $(this).html("o");
+ var nick = $(this).parent().children("a").html().replace(/<img[^>]+>/,"");
+ $(".nick_" + nick).hide();
+ MUTES[nick] = true;
+});
+$(".unmute").live("click", function(){
+ $(this).removeClass("unmute");
+ $(this).addClass("mute");
+ $(this).html("x");
+ var nick = $(this).parent().children("a").html().replace(/<img[^>]+>/,"");
+ $(".nick_" + nick).show();
+ delete MUTES[nick];
+});
+
+function buildUserDiv(user) {
+ var muted = MUTES[user.nick] ? '<span class="unmute">o</span>' : '<span class="mute">x</span>';
+ if (user.avatar) {
+ return '<div class="username">'
+ + muted
+ + '<a href="' + RootDomain + escapeHtml(user.nick) + '" target="_blank">'
+ + '<img src="' + user.avatar + '" width="50" height="50">'
+ + escapeHtml(user.nick)
+ + '</a>'
+ + '</div>';
+ } else {
+ return '<div class="username">'
+ + muted
+ + '<a href="' + RootDomain + escapeHtml(user.nick) + '" target="_blank">'
+ + '<img src="' + RootDomain + 'static/img/noinfo.png" width="50" height="50">'
+ + escapeHtml(user.nick)
+ + '</a>'
+ + '</div>';
+ }
+}
+// Utils
+
+emptyFunc = function(){};
+falseFunc = function(){ return false };
+
+isImgBroken = function(img){
+ return (img.height == 0 || img.width == 0 || img.height == NaN || img.width == NaN)
+}
+
+isEmptyObject = function(obj) {
+ for (key in obj) {
+ if (obj.hasOwnProperty(key)) return false;
+ }
+ return true
+}
+
+String.prototype.trim = function(){ return this.replace(/^\s+|\s+$/g,'') }
+
+function isCSSPropertySupported(prop){ return prop in document.body.style }
+
+function track(group, name) {
+ if (typeof pageTracker !== 'undefined') {
+ pageTracker._trackEvent(group, name,
+ typeof Nick !== 'undefined' ? Nick : 'anon');
+ }
+}
+
+// generate a new CSS rule and apply it immediately
+// (more persistent than dumping a style tag)
+window.cssRule = function (selector, declaration) {
+ var x = document.styleSheets,y=x.length-1;
+ x[y].insertRule(selector+"{"+declaration+"}",x[y].cssRules.length);
+ $(selector).css(declaration.split(": "));
+};
+
+
+
+var Log = {
+ "Levels": ['info', 'warn', 'error'],
+ "AjaxSubmitLevels": ['warn', 'error'],
+ "AjaxSubmitPath": "/logerror",
+
+ "SupplementalInfo": function() {
+ return { 'user': UserInfo && UserInfo.nick };
+ },
+
+ "ajaxSubmit": function(level, component, msg) {
+ var info = Log.SupplementalInfo();
+ var data = { 'level': level, 'component': component, 'msg': msg };
+ $.extend(info, data);
+
+ $.ajax({type: 'POST',
+ timeout: 5000,
+ url: Log.AjaxSubmitPath,
+ data: info
+ });
+ },
+
+ "initialize": function() {
+ $.each(Log.Levels, function(i, level) {
+ Log[level] = function(component, msg) {
+ if (window.console && window.console[level])
+ window.console[level](args);
+ if (Log.AjaxSubmitLevels.indexOf(level) != -1)
+ Log.ajaxSubmit(level, args);
+ };
+ });
+ }
+};
+
+Log.initialize();
+
+
+// Time how long a function takes to complete
+function timeFunc(f){
+ var start = new Date().getTime();
+ var res = f();
+// console.log((new Date().getTime()) - start + " msecs");
+ return res;
+}
+Youtube = {
+ "timer": 0,
+
+ "startAnimation": function(){
+ if (!Youtube.timer)
+ Youtube.timer = setTimeout(Youtube.animate, 1000)
+ },
+
+ "animate": function(){
+ var thumbs = $(".youtube-thumb")
+ thumbs.each(Youtube.nextThumb)
+ if (thumbs.length == 0){
+ clearTimeout(Youtube.timer)
+ Youtube.timer = 0
+ } else Youtube.timer = setTimeout(Youtube.animate, 1000);
+ },
+
+ "nextThumb": function(){
+ var img = $(this);
+ // yt thumb url is http://i.ytimg.com/vi/0123456789A/1.jpg
+ var v = img.attr("src").substr(22,11)
+ var num = img.attr("src").charAt(34);
+ img.attr("src", (Youtube.nextThumbUrl(v, num)))
+ },
+
+ "nextThumbUrl": function(v, num){
+ if (!num) num = 0;
+ num = (parseInt(num) % 3) + 1 // cycle over 1,2,3
+ return "http://i.ytimg.com/vi/" + v + "/" + num + ".jpg"
+ },
+
+}