// 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").text(txt).html() } 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(); URLRegex = /((\b(http\:\/\/|https\:\/\/|ftp\:\/\/)|(www\.))+(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi; PicRegex = /\.(jpg|jpeg|png|gif|bmp|svg|fid)$/i; function getImagesAsArray(text) { var imgs = [] var urls = text.match(URLRegex) if (urls === null) return imgs for (var i = 0; i"; } else if (type == 'youtube') { Youtube.startAnimation(); return "" + "" + "" } else if (type == 'midi' || type == 'wav') { return ' '+uri.file+'' } else return "" + url + ""; } 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" }, } 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 "" + url + "" } // 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") == "true"; var ImgsEnabled = Preferences.getProperty("chat.imgsEnabled", "true") == "true"; function setTextEnable() { if ($(this).attr('checked')) { TextEnabled = true; Preferences.setProperty("chat.textEnabled", "true"); track('UI', 'TextEnabled'); $('.dump').not('.contains-image').show(); } else { TextEnabled = false; Preferences.setProperty("chat.textEnabled", "false"); track('UI', 'TextDisabled'); $('.dump').not('.contains-image').hide() } }; function setImgsEnable() { if ($(this).attr('checked')) { ImgsEnabled = true; Preferences.setProperty("chat.imgsEnabled", "true"); track('UI', 'ImgsEnabled'); $('.contains-image').show(); } else { ImgsEnabled = false; Preferences.setProperty("chat.imgsEnabled", "false"); track('UI', 'ImgsDisabled'); $('.contains-image').hide(); } }; function buildMsgContent(content) { if (content.substr(0,6) == "") return content.substr(6,content.length - 13) else return linkify(escapeHtml(content)); } // todo: // isLoading doesn't get passed the right thing by $.map in addMessages function buildMessageDiv(msg, isLoading) { var nick = escapeHtml(msg.nick); removeOldMessages(); var builtContent = buildMsgContent(msg.content); var msgId = ('msg_id' in msg) ? 'id="message-' + msg.msg_id + '"' : ''; var loadingClass = isLoading ? ' loading' : ''; var containsImageClass = LastMsgContainsImage ? ' contains-image' : ''; var displayStyle = ((ImgsEnabled && LastMsgContainsImage) || (TextEnabled && !LastMsgContainsImage)) ? '' : ' style="display: none"'; return '
' + '' + nick + '' + ' ' + '' + '' + builtContent + '' + '
'; } function buildUserDiv(user) { if (user.avatar) { return ''; } else { return ''; } } // Favs function buildFav(f) { var h = '
' + '' + '' + f.from + '' + ' just faved you!' + '
'; 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, "easing": "easeInExpo", "complete": removeFavAndHideBox }) } function updateFavs(fs) { if (fs.length == 0) return; $('#favbox').show(); $(fs).each(function(i, f) { showFav(f) }); } // Growl function buildGrowlDataAndPopDatShit(msg) { var nick = escapeHtml(msg.nick); nick = '' + nick + ':' 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!"); } } // Messages 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; } // this shouldn't just be client side :V 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) .removeClass('loading').addClass('loaded'); }; 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(); } }; } function addNewMessages(msgs) { var msgStr = $.map(msgs, buildMessageDiv).join(''); $('#messageList').append(msgStr); } function addNewMessage(msg, isLoading) { var msgStr = buildMessageDiv(msg, isLoading); var div = $(msgStr).appendTo('#messageList'); 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.error(e); } } setTimeout(refresh, 1000); }; 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 paletteClicked(){ track('UI', 'FavPaletteActuallyClicked'); paletteToggle(); } 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())); if ((ImgsEnabled && dump.hasClass('contains-image')) || (TextEnabled && !dump.hasClass('contains-image'))) dump.show(); else dump.hide(); }); $('#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(); } function startChatUpdater() { setTimeout(refresh, 1000); } function makePlainText() { var j = $(this); j.text(j.text()); } 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 = ''; $('#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 enableProfileEdit() { $('img#contact').replaceWith('
'); $('img#bio').replaceWith('
'); $('#contact, #bio, #avatar').addClass('editable'); $('#avatar-editing').show(); var resetPage = function() { location.reload() }; $('#edit-toggle a').text('done editing').click(resetPage); activateProfileEditable(); } function initProfile() { Search.initInpage() $(".linkify").each(function() { var text = jQuery(this).text(); jQuery(this).html(linkifyWithoutImage(text)); }); $('#edit-toggle').click(enableProfileEdit); activateProfileEditable(); $('.dash-dump .content').each(function() { var t = $(this); t.html(buildMsgContent(t.text())); }); }; function initLog() { Search.initInpage() $('.logged-dump .content').each(function() { var t = $(this); t.html(buildMsgContent(t.text())); }); 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'); } } }) } 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) }) } } }) } 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") } } function paletteShow(){ $("#palette").css("display", "block") if (isEmptyObject(RawFavs)) { $('#palette-thumbs').html('
This is where all the stuff you FAV goes!

To FAV a post click the little heart next to a users name.

Everything you fav gets saved to your profile.. Have fun!
'); } else { paletteBuildImageThumbs(); } } function paletteHide(){ $("#palette").css("display", "none") $("#palette-thumbs").html("") } function paletteToggle(){ if ($("#palette").css("display") == "none") paletteShow() else paletteHide() } 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 }); } 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 = ''; $('#dashavatar').html(s).show(); }; new AjaxUpload(elementId, { action: '/upload/avatar', autoSubmit: true, name: 'image', onSubmit: onSubmit, onComplete: onComplete }); } // 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) } // 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); } function initDirectory() { $('.linkify').each(function() { var t = $(this); t.html(buildMsgContent(t.text())); }); Search.initInpage() initLogThumb('.dlogged-dump .thumb', '.dlogged-dump'); } //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) } // 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)} } 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 + "¬es=" + message.via Share.openLink(url) } } Tag = { "favorite": function(button){ var message = getMessageInfo(button) var favorited = ($(button).parents(".dump").hasClass("favorite")) ? true : false 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 }); } } /* 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": {}, // 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() 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("") } }, '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") } } 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 } } // 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&&i0&&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, 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 // 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] } 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); } }; 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 = $('') .attr({'href': img.attr('src') }) .addClass('msg-image-zoom') .append($('').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; }); } /* 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)) } }