summaryrefslogtreecommitdiff
path: root/static/resizetest/dump.fm.av.files/pichat.js
diff options
context:
space:
mode:
Diffstat (limited to 'static/resizetest/dump.fm.av.files/pichat.js')
-rwxr-xr-xstatic/resizetest/dump.fm.av.files/pichat.js1704
1 files changed, 1704 insertions, 0 deletions
diff --git a/static/resizetest/dump.fm.av.files/pichat.js b/static/resizetest/dump.fm.av.files/pichat.js
new file mode 100755
index 0000000..8a293ee
--- /dev/null
+++ b/static/resizetest/dump.fm.av.files/pichat.js
@@ -0,0 +1,1704 @@
+// 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;}};
+
+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"}
+}
+
+// Utils
+
+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');
+ }
+}
+
+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: '/' });
+ }
+};
+
+function escapeHtml(txt) {
+ if (!txt) { return ""; }
+// txt = annoyingCaps(txt)
+ return $("<span>").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<urls.length; i++){
+ var url = urls[i]
+ var urlWithoutParams = url.replace(/\?.*$/i, "");
+ if (PicRegex.test(urlWithoutParams))
+ imgs.push(url)
+ }
+ return imgs
+}
+
+function linkify(text) {
+ LastMsgContainsImage = false
+ text = text.replace(URLRegex, linkReplace);
+ return text
+}
+
+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 + "'></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>";
+
+}
+
+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";
+
+ if (parseDomain(uri.host) == "youtube.com" && ('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>"
+}
+
+// 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";
+
+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 buildMsgContent(content) {
+ if (content.substr(0,6) == "<safe>")
+ 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 = (TextEnabled || LastMsgContainsImage) ? '' : ' style="display: none"';
+
+ return '<div class="msgDiv dump ' + loadingClass + containsImageClass + '" ' + msgId + displayStyle + '>'
+ + '<span class="nick"><b><a href="http://dump.fm/' + nick + ' ">' + nick + '</a></b>'
+ + ' <img src="'+Imgs.chatThumbDot+'" class="chat-thumb" onclick="Tag.favorite(this)"> '
+ + '</span>'
+ + '<span class="content">' + builtContent + '</span>'
+ + '</div>';
+}
+
+function buildUserDiv(user) {
+ if (user.avatar) {
+ return '<div class="username">'
+ + '<a href="http://dump.fm/' + escapeHtml(user.nick) + '" target="_blank">'
+ + '<img src="' + user.avatar + '" width="50" height="50">'
+ + escapeHtml(user.nick) + '</a></div>';
+ } else {
+ return '<div class="username">'
+ + '<a href="/' + escapeHtml(user.nick) + '" target="_blank">'
+ + '<img src="/static/img/noinfo.png" width="50" height="50">'
+ + escapeHtml(user.nick) + '</a></div>';
+ }
+}
+
+// Favs
+
+function buildFav(f) {
+ var h = '<div class="fav-note">'
+ + '<img src="/static/img/thumbs/chatheartover.gif">'
+ + '<a href="http://dump.fm/' + 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,
+ "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 = '<a href="http://dump.fm/' + 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!");
+ }
+}
+
+// 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 submitMessage() {
+ var content = $.trim($('#msgInput').val());
+
+ 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.init()
+
+ $('.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 (!TextEnabled && !dump.hasClass('contains-image'))
+ dump.hide();
+ else
+ dump.show();
+ });
+
+ $('#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()
+}
+
+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 = '<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 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 initProfile() {
+ Search.init()
+ $(".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.init()
+ $('.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<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 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 = '<img id="dashavatarPic" src="' + r + '" />';
+ $('#dashavatar').html(s).show();
+ };
+ new AjaxUpload(elementId, {
+ action: '/upload/avatar',
+ autoSubmit: true,
+ name: 'image',
+ onSubmit: onSubmit,
+ onComplete: onComplete
+ });
+}
+
+
+/*
+ search adds a script to the page... the script will call either Search.searchResult() or Search.searchError()
+ todo: clean this up. remove duplicated function names etc
+*/
+
+var Search = {
+
+ 'term': "",
+ 'imagesPerPage': 25,
+ 'images': [],
+ 'tokens': [],
+
+ 'init': function(){
+ $("#search-query").val("search dump.fm")
+ $("#search-query").focus(function(){
+ if ($("#search-query").val() == 'search dump.fm')
+ $("#search-query").val("")
+ })
+ $("#search-query").blur(function(){
+ if ($("#search-query").val().trim() == '')
+ $("#search-query").val("search dump.fm")
+ })
+ $("#search-query").keydown(ifEnter(Search.doSearch))
+ $("#search-results-images a").live("mouseup", Search.click)
+ },
+
+ 'addScript': function(term) {
+ $("#search-script").remove()
+ $("head").append("<scr"+"ipt src='/cmd/ghettosearch/"+term+"' id='search-script'></sc"+"ript>")
+ },
+ 'setContent': function(x){
+ $("#search-results-images").html(x)
+ },
+ 'setMessage': function(x){
+ $("#search-controls").css("display", "block")
+ $("#search-control-text").html(x)
+ },
+ 'searchError': function(error){
+ Search.setContent("")
+ $('#search-control-previous').css("visibility", "hidden")
+ $('#search-control-next').css("visibility", "hidden")
+ Search.setMessage(error)
+ },
+
+ 'doSearch': function(){
+ term = $("#search-query").val().trim().toLowerCase()
+ var rawTokens = term.split(" ")
+ Search.tokens = []
+ for(var t = 0; t < rawTokens.length; t++) {
+ if (rawTokens[t].length > 2)
+ Search.tokens.push(rawTokens[t])
+ }
+ if (Search.tokens.length == 0) {
+ Search.setMessage("search query too small")
+ } else {
+ Search.setMessage("searching for '"+Search.tokens.join(" and ")+"'")
+ Search.addScript(Search.tokens.join("+"))
+ }
+ },
+
+ 'renderPage': function(num){
+ $("#search-results-images").css("display", "block")
+ $("#search-controls").css("display", "block")
+ if (Search.images.length > 0)
+ Search.setMessage("results for '"+Search.tokens.join(" and ")+"' (page " + (num + 1) + ")");
+ var contentString = ''
+ var start = num * Search.imagesPerPage
+ var imageCounter = 0
+ for(var i = start; i < Search.images.length; i++){
+ if(imageCounter > Search.imagesPerPage) break;
+ contentString += '<a href="'+Search.images[i]+'" target="_blank" onclick="return false"><img src="'+Search.images[i]+'"></a>'
+ imageCounter += 1
+ }
+ contentString += '<br><br><div id="search-commands">'
+ if(num > 0) {
+ $('#search-control-previous').attr("href", 'javascript:Search.renderPage('+(num-1)+')')
+ $('#search-control-previous').css("visibility", "visible")
+ } else {
+ $('#search-control-previous').attr("href", 'javascript:void()')
+ $('#search-control-previous').css("visibility", "hidden")
+ }
+
+ if (Search.images.length > start + imageCounter) {
+ $('#search-control-next').attr("href", 'javascript:Search.renderPage('+(num+1)+')')
+ $('#search-control-next').css("visibility", "visible")
+ } else {
+ $('#search-control-next').attr("href", 'javascript:void()')
+ $('#search-control-next').css("visibility", "hidden")
+ }
+ Search.setContent(contentString)
+ },
+
+ '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
+ },
+
+ 'searchResult': function(results){
+ Search.images = []
+ var alreadyGot = {}
+ if(results === null || results.length == 0) {
+ Search.setContent("")
+ Search.setMessage("no results found")
+ } else {
+ for(var r = 0; r<results.length; r++){
+ var content = results[r]['content']
+ if (content.substring(0,6) == "<safe>") continue; // skip html posts
+ var imageUrls = getImagesAsArray(content);
+ for (var i=0; i<imageUrls.length; i++){
+ var imageUrl = imageUrls[i];
+ if (imageUrl in alreadyGot) continue;
+ alreadyGot[imageUrl] = true
+ var validImage = true;
+ for(var t = 0; t<Search.tokens.length; t++){
+ if (imageUrl.toLowerCase().indexOf(Search.tokens[t]) == -1) {
+ validImage = false;
+ break;
+ }
+ }
+ if (validImage)
+ Search.images.push(imageUrl);
+ }
+ }
+ if (Search.images.length == 0) {
+ Search.setMessage("no results found")
+ }
+ Search.renderPage(0)
+ }
+ },
+ 'close': function(){
+ Search.setContent("")
+ $('#search-control-previous').css("visibility", "hidden")
+ $('#search-control-next').css("visibility", "hidden")
+ $("#search-results-images").css("display", "none")
+ $("#search-controls").css("display", "none")
+ }
+
+
+}
+
+
+// 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.init()
+ 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 + "&notes=" + 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
+ });
+ }
+}
+
+
+// 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];}
+}
+
+function timeFunc(f){
+ var start = new Date().getTime();
+ var res = f();
+ console.log((new Date().getTime()) - start + " msecs");
+ return res;
+}
+
+// parseUri 1.2.2 from http://blog.stevenlevithan.com/archives/parseuri
+// (c) Steven Levithan <stevenlevithan.com>, MIT License
+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
+
+function parseDomain(host){
+ return host.toLowerCase().replace(/^www\./, "")
+}
+
+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 = $('<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;
+ });
+}
+
+// sha1.js
+
+/*
+ * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
+ * in FIPS 180-1
+ * Version 2.2 Copyright Paul Johnston 2000 - 2009.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for details.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); }
+function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); }
+function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); }
+function hex_hmac_sha1(k, d)
+ { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
+function b64_hmac_sha1(k, d)
+ { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); }
+function any_hmac_sha1(k, d, e)
+ { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function sha1_vm_test()
+{
+ return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d";
+}
+
+/*
+ * Calculate the SHA1 of a raw string
+ */
+function rstr_sha1(s)
+{
+ return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8));
+}
+
+/*
+ * Calculate the HMAC-SHA1 of a key and some data (raw strings)
+ */
+function rstr_hmac_sha1(key, data)
+{
+ var bkey = rstr2binb(key);
+ if(bkey.length > 16) bkey = binb_sha1(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 = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
+ return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160));
+}
+
+/*
+ * Convert a raw string to a hex string
+ */
+function rstr2hex(input)
+{
+ try { hexcase } catch(e) { hexcase=0; }
+ var hex_tab = 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;
+}
+
+/*
+ * Convert a raw string to a base-64 string
+ */
+function rstr2b64(input)
+{
+ try { b64pad } catch(e) { 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 += b64pad;
+ else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
+ }
+ }
+ return output;
+}
+
+/*
+ * Convert a raw string to an arbitrary string encoding
+ */
+function rstr2any(input, encoding)
+{
+ var divisor = encoding.length;
+ var remainders = Array();
+ var i, q, x, quotient;
+
+ /* Convert to an array of 16-bit big-endian values, forming the dividend */
+ 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);
+ }
+
+ /*
+ * Repeatedly perform a long division. The binary array forms the dividend,
+ * the length of the encoding is the divisor. Once computed, the quotient
+ * forms the dividend for the next step. We stop when the dividend is zero.
+ * All remainders are stored for later use.
+ */
+ 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;
+ }
+
+ /* Convert the remainders to the output string */
+ var output = "";
+ for(i = remainders.length - 1; i >= 0; i--)
+ output += encoding.charAt(remainders[i]);
+
+ /* Append leading zero equivalents */
+ 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;
+}
+
+/*
+ * Encode a string as utf-8.
+ * For efficiency, this assumes the input is valid utf-16.
+ */
+function str2rstr_utf8(input)
+{
+ var output = "";
+ var i = -1;
+ var x, y;
+
+ while(++i < input.length)
+ {
+ /* Decode utf-16 surrogate pairs */
+ 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++;
+ }
+
+ /* Encode output as utf-8 */
+ 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;
+}
+
+/*
+ * Encode a string as utf-16
+ */
+function str2rstr_utf16le(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;
+}
+
+function str2rstr_utf16be(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;
+}
+
+/*
+ * Convert a raw string to an array of big-endian words
+ * Characters >255 have their high-byte silently ignored.
+ */
+function rstr2binb(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;
+}
+
+/*
+ * Convert an array of big-endian words to a string
+ */
+function binb2rstr(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;
+}
+
+/*
+ * Calculate the SHA-1 of an array of big-endian words, and a bit length
+ */
+function binb_sha1(x, len)
+{
+ /* append padding */
+ 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] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
+ var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)),
+ safe_add(safe_add(e, w[j]), sha1_kt(j)));
+ e = d;
+ d = c;
+ c = bit_rol(b, 30);
+ b = a;
+ a = t;
+ }
+
+ a = safe_add(a, olda);
+ b = safe_add(b, oldb);
+ c = safe_add(c, oldc);
+ d = safe_add(d, oldd);
+ e = safe_add(e, olde);
+ }
+ return Array(a, b, c, d, e);
+
+}
+
+/*
+ * Perform the appropriate triplet combination function for the current
+ * iteration
+ */
+function sha1_ft(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;
+}
+
+/*
+ * Determine the appropriate additive constant for the current iteration
+ */
+function sha1_kt(t)
+{
+ return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
+ (t < 60) ? -1894007588 : -899497514;
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+ return (num << cnt) | (num >>> (32 - cnt));
+}