Quake Live Chat History

By wn_ Last update May 13, 2012 — Installed 886 times.

There are 10 previous versions of this script.

// ==UserScript==
// @id             qlchathistory@phob.net
// @name           Quake Live Chat History
// @version        0.51
// @namespace      phob.net
// @author         wn
// @description    Persist your Quake Live chat
// @include        http://*.quakelive.com/*
// @exclude        http://*.quakelive.com/forum*
// @run-at         document-end
// @updateURL      https://userscripts.org/scripts/source/125978.meta.js
// ==/UserScript==


// Source: http://wiki.greasespot.net/Content_Script_Injection
function contentEval(source) {
  // Check for function input.
  if ("function" == typeof(source)) {
    source = "(" + source + ")();";
  }

  // Create a script node holding this source code.
  var script = document.createElement("script");
  script.setAttribute("type", "application/javascript");
  script.textContent = source;

  // Insert the script node into the page, so it will run, and immediately
  // remove it to clean up.
  document.body.appendChild(script);
  document.body.removeChild(script);
}


// Stylin'
GM_addStyle(".qlch_spacer { margin: 2px auto; width: 94%; border-bottom: 1px solid #ccc }");


contentEval(function() {

/*!
 * Amplify Store - Persistent Client-Side Storage 1.1.0
 * 
 * Copyright 2011 appendTo LLC. (http://appendto.com/team)
 * Dual licensed under the MIT or GPL licenses.
 * http://appendto.com/open-source-licenses
 * 
 * http://amplifyjs.com
 */
(function(a,b){function e(a,e){c.addType(a,function(f,g,h){var i,j,k,l,m=g,n=(new Date).getTime();if(!f){m={},l=[],k=0;try{f=e.length;while(f=e.key(k++))d.test(f)&&(j=JSON.parse(e.getItem(f)),j.expires&&j.expires<=n?l.push(f):m[f.replace(d,"")]=j.data);while(f=l.pop())e.removeItem(f)}catch(o){}return m}f="__amplify__"+f;if(g===b){i=e.getItem(f),j=i?JSON.parse(i):{expires:-1};if(j.expires&&j.expires<=n)e.removeItem(f);else return j.data}else if(g===null)e.removeItem(f);else{j=JSON.stringify({data:g,expires:h.expires?n+h.expires:null});try{e.setItem(f,j)}catch(o){c[a]();try{e.setItem(f,j)}catch(o){throw c.error()}}}return m})}var c=a.store=function(a,b,d,e){var e=c.type;d&&d.type&&d.type in c.types&&(e=d.type);return c.types[e](a,b,d||{})};c.types={},c.type=null,c.addType=function(a,b){c.type||(c.type=a),c.types[a]=b,c[a]=function(b,d,e){e=e||{},e.type=a;return c(b,d,e)}},c.error=function(){return"amplify.store quota exceeded"};var d=/^__amplify__/;for(var f in{localStorage:1,sessionStorage:1})try{window[f].getItem&&e(f,window[f])}catch(g){}if(window.globalStorage)try{e("globalStorage",window.globalStorage[window.location.hostname]),c.type==="sessionStorage"&&(c.type="globalStorage")}catch(g){}(function(){if(!c.types.localStorage){var a=document.createElement("div"),d="amplify";a.style.display="none",document.getElementsByTagName("head")[0].appendChild(a);try{a.addBehavior("#default#userdata"),a.load(d)}catch(e){a.parentNode.removeChild(a);return}c.addType("userData",function(e,f,g){a.load(d);var h,i,j,k,l,m=f,n=(new Date).getTime();if(!e){m={},l=[],k=0;while(h=a.XMLDocument.documentElement.attributes[k++])i=JSON.parse(h.value),i.expires&&i.expires<=n?l.push(h.name):m[h.name]=i.data;while(e=l.pop())a.removeAttribute(e);a.save(d);return m}e=e.replace(/[^-._0-9A-Za-z\xb7\xc0-\xd6\xd8-\xf6\xf8-\u037d\u37f-\u1fff\u200c-\u200d\u203f\u2040\u2070-\u218f]/g,"-");if(f===b){h=a.getAttribute(e),i=h?JSON.parse(h):{expires:-1};if(i.expires&&i.expires<=n)a.removeAttribute(e);else return i.data}else f===null?a.removeAttribute(e):(j=a.getAttribute(e),i=JSON.stringify({data:f,expires:g.expires?n+g.expires:null}),a.setAttribute(e,i));try{a.save(d)}catch(o){j===null?a.removeAttribute(e):a.setAttribute(e,j),c.userData();try{a.setAttribute(e,i),a.save(d)}catch(o){j===null?a.removeAttribute(e):a.setAttribute(e,j);throw c.error()}}return m})}})(),function(){function e(a){return a===b?b:JSON.parse(JSON.stringify(a))}var a={},d={};c.addType("memory",function(c,f,g){if(!c)return e(a);if(f===b)return e(a[c]);d[c]&&(clearTimeout(d[c]),delete d[c]);if(f===null){delete a[c];return null}a[c]=f,g.expires&&(d[c]=setTimeout(function(){delete a[c],delete d[c]},g.expires));return f})}()})(this.amplify=this.amplify||{})


var suffix = ":history";

var oldAddContact = quakelive.mod_friends.roster.AddContact;
quakelive.mod_friends.roster.AddContact = function(item) {
  var preLen = quakelive.mod_friends.roster.fullRoster.length;

  // Call the original to (possibly) get the contact added.
  oldAddContact.call(quakelive.mod_friends.roster, item);

  // If a contact wasn't added, stop now.
  if (preLen == quakelive.mod_friends.roster.fullRoster.length) {
    return;
  }

  // Get stuff from the latest roster item.
  var fullRoster = quakelive.mod_friends.roster.fullRoster
    , frLast = fullRoster[fullRoster.length - 1]
    , oldReceivedMsg = frLast.ReceivedMsg
    , oldSendMessage = frLast.SendMessage
    , oldOnSelected = frLast.OnSelected
    , oldAppendToHistory = frLast.AppendToHistory;

  // Helper to save chat history.  Only save non-spacer items from within the
  // past two weeks.
  // TODO: always keep a certain minimum, even if older than 2 weeks
  frLast.QLCH_SaveHistory = function() {
    var now = new Date().getTime();
    var saveHist = frLast.history.filter(function(el) {
      return !("qlchSpacer" in el || now - el.date.getTime() > 12096E5);
    });
    amplify.store(frLast.jid.bare + suffix, saveHist);
  }

  // Override the message functions for this contact to make them persist the
  // history.
  frLast.ReceivedMsg = function(what, raw) {
    oldReceivedMsg.call(frLast, what, raw);
    frLast.QLCH_SaveHistory();
  }

  frLast.SendMessage = function(what) {
    oldSendMessage.call(frLast, what);
    frLast.QLCH_SaveHistory();
  }

  // Restore the contact history from storage.
  // SelectContact will handle filling in the content.
  frLast.history = amplify.store(frLast.jid.bare + suffix) || [];

  // Dates don't get converted back by Amplify... needed for timestamps.
  for (var i = 0, e = frLast.history.length; i < e; ++i) {
    frLast.history[i].date = new Date(frLast.history[i].date);
  }

  // If there were history items, add a spacer to separate them.
  if (frLast.history.length) {
    frLast.history.push({"qlchSpacer": true});
  }

  // Override OnSelected to reset the "date changed" tracker.
  frLast.qlchLastDate = null;

  frLast.OnSelected = function() {
    oldOnSelected.call(frLast);
    frLast.qlchLastDate = null;
  }

  // Override AppendToHistory to replace the chat timestamp and handle spacers.
  // Only show the full date when it changes.
  frLast.AppendToHistory = function(hist) {
    // Intercept chat history spacers.
    if ("qlchSpacer" in hist) {
      quakelive.mod_friends.node.find("#im-chat-body-bottom")
          .before("<div class='qlch_spacer'></div>");

      // Reset the chat origin for new items.
      frLast.prevChatOrigin = null;

      return;
    }

    oldAppendToHistory.call(frLast, hist);

    var d = hist.date.getFullYear() + "-" + (hist.date.getMonth()+1) + "-"
          + hist.date.getDate();

    if (d != frLast.qlchLastDate) {
      frLast.prevChatNode.find("span.chat-timestamp").last().text(
          "(" + d + " " + hist.date.getHours() + ":"
        + ZeroPad(hist.date.getMinutes()) + ":" + ZeroPad(hist.date.getSeconds())
        + ")");
    }

    frLast.qlchLastDate = d;
  }
};

}); // end of contentEval call