// ==UserScript==
// @name UserNotes
// @namespace jacalata
// @description adds ajaxy viewing of user notes on Metafilter whenever a username is shown
// @include *.metafilter.com/*
// @version 3.1 (firefox 8.0 support)
// 2.0 (optional marking added to usernames sitewide to indicate notes
// 1.5 (pressing enter in the text input box now submits the new note)
// 1.4 (fixed buttons into text-like pieces of ajaxy goodness)
// 1.3 (fixed bug that was not saving the first note on each page)
// 1.2 (added autoclear of textbox on focus)
// 1.1 (changed to handle multiple notes with individual deletion)
// ==/UserScript==
// chrome doesn't support GM_getValue, using replacement localStorage
// this code borrowed from http://devign.me/greasemonkey-gm_getvaluegm_setvalue-functions-for-google-chrome/
if (!this.GM_getValue || (this.GM_getValue.toString && this.GM_getValue.toString().indexOf("not supported")>-1)) {
this.GM_getValue=function (key,def) {
return localStorage[key] || def;
};
this.GM_setValue=function (key,value) {
return localStorage[key]=value;
};
this.GM_deleteValue=function (key) {
return delete localStorage[key];
};
}
var currentUserID = "User"+location.href.substring(31);
numUserNotes = GM_getValue(currentUserID, 0);
var isAjaxOn = "ajaxSetting";
var ajaxSetting = GM_getValue(isAjaxOn, false);
var currentBalloon;
// adding a button to turn on/off the sitewide notes
// (off means they are only visible on the profiles, no markings around the rest of the site)
ajaxButton = createButton("ajaxButton", "ajaxButton", "##", "sitewide notes off", "_self", toggleAjax);
//create an invisible div that will show the notes in a popup
// this is from http://blog.kung-foo.tv/archives/001614.html, with the style stuff based on the bookburro extension code
var notesBox = document.createElement('div');
notesBox.setAttribute("id", "balloon");
notesBox.setAttribute("style", "background-color:#778899;text-align:left;");
notesBox.style.position = "absolute";
notesBox.style.border = "1px solid navy";
notesBox.style.margin = "10";
notesBox.style.zIndex = "99";
notesBox.style.font = "8pt sans-serif";
notesBox.style.overflow = "hidden";
notesBox.style.opacity = "85";
notesBox.style.padding = "4px";
notesBox.style.display = "none";
var searchPattern = "//div[@class='mefimessages']";
var options = document.evaluate( searchPattern, document, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
var i;
for (var targetClass = null, i=0; (targetClass = options.snapshotItem(i)); i++)
{
targetClass.innerHTML += " | ";
targetClass.appendChild(ajaxButton);
targetClass.appendChild(notesBox);
}
// if this is a profile page, add all the note-taking elements
if (location.href.match("metafilter.com/user") )
{
takeNotes();
}
if (ajaxSetting == true)
{
ajaxButton.innerHTML = ajaxButton.innerHTML.replace("off", "on");
showLinkedNotes();
}
// called on loading a page with ajaxNotes set, or when turning on the ajax notes.
// searches for links to user profiles, and makes a mark next to those you have given notes
function showLinkedNotes()
{
// find every spot where a user profile is linked
var profileSearch = "//a[contains(@href, '/user/') and not(contains(./text(), 'My')) and not(contains(@href, 'rss' ))]";
var profiles = document.evaluate( profileSearch, document, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
var j;
for (var targetClass = null, j=0; (targetClass = profiles.snapshotItem(j)); j++)
{
var thisUserID = "User"+targetClass.href.substring(31);
var thisUserNotes = GM_getValue(thisUserID, 0);
if (thisUserNotes > 0)
{
targetClass.innerHTML += " <i>i</i> ";
targetClass.addEventListener("mouseover", viewNotes, false);
targetClass.addEventListener("click", hideBalloon, false);
}
}
} // function showLinkedNotes
// the user has just turned off the ajax notes feature - hide the little 'i' icons (ie; reverse what showLinkedNotes did)
function removeLinkedNotes()
{
hideBalloon(); //just in case it is currently displayed
// find every spot where a user profile is linked
var profileSearch = "//a[contains(@href, '/user/') and not(contains(./text(), 'My')) and not(contains(@href, 'rss' ))]";
var profiles = document.evaluate( profileSearch, document, null,
XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
var j;
for (var targetClass = null, j=0; (targetClass = profiles.snapshotItem(j)); j++)
{
var thisUserID = "User"+targetClass.href.substring(31);
var thisUserNotes = GM_getValue(thisUserID, 0);
if (thisUserNotes > 0)
{
targetClass.innerHTML = targetClass.innerHTML.replace(" <i>i</i> ", "");
targetClass.removeEventListener("mouseover", viewNotes, false);
}
}
}//funtion removeLinkedNotes
// displays the info balloon, containing the notes for the user whose profile you mousedover
// from http://blog.kung-foo.tv/archives/001614.html
function viewNotes()
{
var thisUserID = "User"+this.href.substring(31);
if ( thisUserID == currentBalloon )
{
return false;
}
currentBalloon = thisUserID; // global variable to track which info balloon is currently displayed
var objX = findPosX(this);
var objY = findPosY(this);
var balloon = document.getElementById("balloon");
if ( balloon && balloon.childNodes )
{
while ( balloon.childNodes.length > 0 )
{
balloon.removeChild(balloon.childNodes[0]); // remove anything in the current balloon
}
}
var closeElt = document.createElement('img');
closeElt.setAttribute("id","closebox");
closeElt.setAttribute("src","http://images.metafilter.com/mefi/icons/stockholm_mini/close.gif");
closeElt.addEventListener('click', hideBalloon, false);
balloon.appendChild(closeElt);
var newElt = document.createElement('div');
newElt.setAttribute("id","balloon_contents");
var thisUserID = "User"+this.href.substring(31);
var thisUserNotes = GM_getValue(thisUserID, 0);
var k;
var height = 13;
for (k=0; k<thisUserNotes; k++)
{
var newLine = "-" + GM_getValue(thisUserID+k, "");
newElt.innerHTML += newLine;
newElt.innerHTML += "<br>";
var nRows = Math.ceil( (newLine.length)/30);
console.log("line Lenght = " + newLine.length + "nRows = " + nRows);
height += 13 * nRows;
}
balloon.style.width = "150px";
balloon.style.height = height;
balloon.appendChild(newElt);
balloon.style.top = (objY-0) + 'px';
balloon.style.left = (objX+45) + 'px';
balloon.style.display = 'block';
} // function viewNotes
// make the info balloon disappear
// from http://blog.kung-foo.tv/archives/001614.html
function hideBalloon()
{
var balloon = document.getElementById("balloon");
if ( balloon )
{
balloon.style.display = 'none';
currentBalloon = null;
}
return false;
} // function hideBalloon
// find the position of an object along the x axis of the page
// from http://blog.kung-foo.tv/archives/001614.html
function findPosX(obj)
{
var curleft = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curleft += obj.offsetLeft;
obj = obj.offsetParent;
}
if ( obj != null )
{
curleft += obj.offsetLeft;
}
}
else if (obj.x)
{
curleft += obj.x;
}
return curleft;
} // function findPosX
// finds the position of an object along the y axis of the page
// from http://blog.kung-foo.tv/archives/001614.html
function findPosY(obj)
{
var curtop = 0;
if (obj.offsetParent)
{
while (obj.offsetParent)
{
curtop += obj.offsetTop;
obj = obj.offsetParent;
}
if ( obj != null )
{
curtop += obj.offsetTop;
}
}
else if (obj.y)
{
curtop += obj.y;
}
return curtop;
} // funcion findYPos
// creates a button element that calls a function on this page
function createButton(id, name, href, words, target, clickFunction)
{
//ajaxButton = createButton("ajaxButton", "ajaxButton", "##", "ajax notes", "_self", toggleAjax);
newButton = document.createElement("a");
newButton.setAttribute("id", id);
newButton.setAttribute("name", name);
newButton.setAttribute("href", href);
newButton.innerHTML = words;
newButton.setAttribute("target", target);
newButton.addEventListener("click", clickFunction, false);
return newButton;
} // function createButton
// toggles the value of our 'is ajax on?' boolean
function toggleAjax()
{
ajaxSetting = !ajaxSetting;
GM_setValue(isAjaxOn, ajaxSetting);
if (ajaxSetting) // turning it on
{
ajaxButton.innerHTML = ajaxButton.innerHTML.replace("off", "on");
showLinkedNotes();
}
else //turning it off
{
ajaxButton.innerHTML = ajaxButton.innerHTML.replace("on", "off");
removeLinkedNotes();
}
} // function toggleAjax
// for profile pages: create and display all the notes for this user.
function takeNotes()
{
anchor = document.getElementById('contact');
if (anchor == null) // this is your own profile - no notes about yourself, because the 'contact' element doesn't exist
{
return;
}
outputDiv = anchor.parentNode;
if (numUserNotes > 0)
{
notesHeadline = document.createElement("div");
notesHeadline.setAttribute("id", "headline");
notesHeadline.innerHTML = "<b>My Notes</b>";
delNotes = createButton("delBut", "DelNotes", "##", " [delete all]", "_self", delAllNotes);
outputDiv.appendChild(notesHeadline);
notesHeadline.appendChild(delNotes);
for (var i = 0; i < numUserNotes; i++)
{
note = GM_getValue(currentUserID+i, "");
currentNotes = document.createElement("div");
currentNotes.setAttribute("id", "currentNotes"+i);
currentNotes.innerHTML = note;
delCurrent = createButton("delBut"+i, "DelNote", "##", " [x]", "_self", delOneNote);
outputDiv.appendChild(currentNotes);
currentNotes.appendChild(delCurrent);
}
}
inputForm = document.createElement("form");
inputForm.setAttribute("method", "post");
inputForm.addEventListener("submit", addNote, false);
inputForm.setAttribute("target", "_self");
newNotes = document.createElement("input");
newNotes.setAttribute("type", "text");
newNotes.setAttribute("value", "new note...");
newNotes.setAttribute("id", "noteInput");
newNotes.addEventListener("focus", clearValue, true);
newNotes.innerHTML = "<br>";
addNotes = document.createElement("input");
addNotes.setAttribute("type", "submit");
addNotes.setAttribute("id", "addBut");
addNotes.setAttribute("value", "Add Note");
addNotes.setAttribute("target", "_self");
inputForm.appendChild(newNotes);
inputForm.appendChild(addNotes);
outputDiv.appendChild(inputForm);
} // function takeNotes
// clears the default text in the textbox when the user clicks on it
function clearValue()
{
textbox = document.getElementById("noteInput");
if (textbox.value == "new note...") {
textbox.setAttribute("value", "");
}
} // function clearValue
// creates and displays a new note, from the text in the textbox
function addNote()
{
var note = document.getElementById("noteInput");
var noteNum = numUserNotes;
if (note.value == "")
{
return;
}
if (numUserNotes == 0)
{
notesHeadline = document.createElement("div");
notesHeadline.setAttribute("id", "headline");
notesHeadline.innerHTML = "<b>My Notes</b>";
delNotes = createButton("delBut", "DelNotes", "##", " [delete all]", "_self", delAllNotes);
//alert(note);
note.parentNode.insertBefore(notesHeadline, note);
notesHeadline.appendChild(delNotes);
}
numUserNotes++;
GM_setValue(currentUserID, numUserNotes);
GM_setValue(currentUserID+noteNum, note.value);
currentNoteNew = document.createElement("div");
currentNoteNew.setAttribute("id", "currentNotes"+noteNum);
currentNoteNew.innerHTML = note.value;
delCurrentNew = createButton("delBut"+noteNum, "DelNote", "##", " [x]", "_self", delOneNote);
note.parentNode.insertBefore(currentNoteNew, note);
currentNoteNew.appendChild(delCurrentNew);
}//function addNotes
// delete a single note
function delOneNote()
{
var newID = (+this.id.substring(6));
var nextID = newID + 1;
while (nextID < numUserNotes)
{
var thisNoteID = currentUserID+newID;
var nextNoteID = currentUserID+nextID;
nextNoteValue = GM_getValue(nextNoteID, "");
currentNotes = document.getElementById("currentNotes"+nextID);
currentNotes.setAttribute("id", "currentNotes"+newID);
delCurrent = document.getElementById("delBut"+nextID);
delCurrent.setAttribute("id", "delBut"+newID);
GM_setValue(thisNoteID, nextNoteValue);
newID++;
nextID++;
}
numUserNotes--;
GM_setValue(currentUserID, numUserNotes);
noteText = this.parentNode;
noteText.removeChild(this);
noteText.parentNode.removeChild(noteText);
if (numUserNotes == 0)
{
headline = document.getElementById("headline");
headline.parentNode.removeChild(headline);
}
} // fucntion delOneNote
// delete all notes about this user
function delAllNotes()
{
var newID;
for (newID = 0; newID < numUserNotes; newID++)
{
note = document.getElementById("currentNotes"+newID);
note.parentNode.removeChild(note);
}
headline = document.getElementById("headline");
headline.parentNode.removeChild(headline);
numUserNotes = 0;
GM_setValue(currentUserID, numUserNotes);
return false;
} // function delAll Notes