Source for "Last.fm - Lyrics"

By escapist
Has no other scripts.


// ==UserScript==
// @name           Last.fm - Lyrics
// @namespace      http://userscripts.org/users/43234
// @description    Adds lyrics from LyricWiki to a Last.fm song page.
// @include        http://www.last.fm/music/*/*/*
// @include        http://www.lastfm.*/music/*/*/*
// @version        2.1
// @author         escapist
// ==/UserScript==

Function.prototype.bind =
	function(object) {
		var method = this;
		return function () {method.apply(object, arguments);
	};
}

new LyricsPanel();

function LyricsPanel() {

	this.loadContent = function() {
		var headline = document.getElementsByTagName("h1")[2];
		var artist = headline.childNodes[0].childNodes[0].nodeValue;
		var track  = headline.childNodes[1].nodeValue.substring(3);

		GM_xmlhttpRequest({
			method: "GET",
			url: "http://lyricwiki.org/api.php?fmt=xml"
				+ "&artist=" + encodeURIComponent(artist)
				+ "&song="   + encodeURIComponent(track),
			headers: {"User-agent": "Mozilla/4.0 (compatible) Greasemonkey",
			          "Accept": "application/xml"},
			onload: this.handleResponse.bind(this)});
	}

	this.handleResponse = function(response) {
		var responseDoc = new DOMParser().parseFromString(response.responseText, "application/xml");
		responseDoc.normalize();
		var lyrics = responseDoc.getElementsByTagName("lyrics")[0].childNodes[0].nodeValue;
		var sourceURL = responseDoc.getElementsByTagName("url")[0].childNodes[0].nodeValue;
		this.content.innerHTML = lyrics.replace(/\n/g, "<br/>")
			+ "<br/><span class=\"moduleOptions\">"
			+ "<a href=\"" + sourceURL + "\" class=\"icon\">"
			+ "<img width=\"19\" height=\"19\" src=\"http://cdn.last.fm/flatness/icons/pencil.gif\" "
			+ "class=\"edit_icon transparent_png\" /><span>Edit</span></a></span>";
	}

	this.toggleDisplayed = function() {
		this.panelDisplayed = !this.panelDisplayed;
		GM_setValue("panelDisplayed", this.panelDisplayed);
		this.updateDisplayed();
	}

	this.updateDisplayed = function() {
		if (this.panelDisplayed && !this.contentLoaded) {
			this.loadContent();
			this.contentLoaded = true;
		}
		this.collapser.innerHTML = this.panelDisplayed ? "Hide" : "Show";
		this.content.style.display = this.panelDisplayed ? ""  : "none";
	}

	this.contentLoaded = false;
	this.panelDisplayed = GM_getValue("panelDisplayed", true);
	var menu = new DropDownMenu();

	var header = document.createElement("h2");
	header.setAttribute("class", "h2Brushed");
	header.innerHTML = "<span class=\"h2Wrapper\">Lyrics</span>";
	this.content = document.createElement("div");
	this.panel = document.createElement("div");
	this.panel.setAttribute("class", "module");

	this.collapser = document.createElement("a");
	this.collapser.addEventListener("click", this.toggleDisplayed.bind(this), false);
	this.collapser.style.cursor = "pointer";
	menu.addItem(this.collapser);

	var positioner = new PanelPositioner(this.panel);
	var controls = positioner.getControls();
	for (var i = 0; i < 4; i++)
		menu.addItem(controls[i]);

	this.panel.appendChild(menu.getComponent());
	this.panel.appendChild(header);
	this.panel.appendChild(this.content);

	this.updateDisplayed();
	positioner.insertPanel();
}

function DropDownMenu() {

	this.getComponent = function() {
		return this.menu;
	}

	this.addItem = function(element) {
		var item = document.createElement("li");
		item.appendChild(element);
		this.menuList.appendChild(item);
	}

	this.menuButton = document.createElement("a");
	this.menuButton.setAttribute("class", "lfmButton lfmSmallButton lfmSmallModuleButton");
	this.menuList = document.createElement("ul");
	this.menuList.setAttribute("class", "lfmDropDownBody");
	this.menuList.style.display = "none";
	this.menuList.style.position = "absolute";
	this.menuList.style.zIndex = "99";
	this.menu = document.createElement("div");
	this.menu.setAttribute("class", "moduleDropDown toggle");
	this.menu.appendChild(this.menuButton);
	this.menu.appendChild(this.menuList);

	document.addEventListener("click", function(event) {
		this.menuList.style.display = event.target == this.menuButton
			&& this.menuList.style.display == "none" ? ""  : "none";
	}.bind(this), false);
}

function PanelPositioner(panel) {

	this.panel = panel;
	this.panelOrientation = GM_getValue("panelOrientation", "right").toLowerCase();
	this.panelIndex = GM_getValue("panelIndex", 1);
	this.nHeadings = {"left": getHeadingCount("left"), "right": getHeadingCount("right")};

	this.getControls = function() {
		return [createMoveLink(this.moveLeft.bind(this),  "left"),
				createMoveLink(this.moveRight.bind(this), "right"),
				createMoveLink(this.moveUp.bind(this),    "up"),
				createMoveLink(this.moveDown.bind(this),  "down")];
	}

	this.insertPanel = function() {
		if (this.panel.parentNode)
			this.panel.parentNode.removeChild(this.panel);

		var containerClassName = this.panelOrientation + "Col" + (this.panelOrientation == "left" ? "Wrapper" : "");
		var expr = "//div[@class='" + containerClassName + "']";
		if (this.panelIndex <= this.nHeadings[this.panelOrientation]) {
			expr += "/h2[" + this.panelIndex + "]";
			var node = document.evaluate(expr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
			node.parentNode.insertBefore(this.panel, node);
		} else {
			this.panelIndex = this.nHeadings[this.panelOrientation] + 1;
			var node = document.evaluate(expr, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
			node.insertBefore(this.panel, null);
		}
		GM_setValue("panelOrientation", this.panelOrientation);
		GM_setValue("panelIndex", this.panelIndex);
	}

	this.moveUp = function() {
		if (this.panelIndex > 1) {
			this.panelIndex--;
			this.insertPanel();
		}
	}

	this.moveDown = function() {
		if (this.panelIndex <= this.nHeadings[this.panelOrientation]) {
			this.panelIndex++;
			this.insertPanel();
		}
	}

	this.moveLeft = function() {
		this.moveHorizontally("right", "left");
	}

	this.moveRight = function() {
		this.moveHorizontally("left", "right");
	}

	this.moveHorizontally = function(from, to) {
		if (this.panelOrientation == from) {
			this.panelOrientation = to;
			if (this.panelIndex > this.nHeadings[from])
				this.panelIndex = this.nHeadings[to] + 1;
			this.insertPanel();
		}
	}

	function createMoveLink(handler, direction) {
		var link = document.createElement("a");
		link.addEventListener("click", handler, false);
		link.innerHTML = direction.charAt(0).toUpperCase() + direction.substring(1); /* "Move " + direction */
		link.style.cursor = "pointer";
		return link;
	}

	function getHeadingCount(orientation) {
		return document.evaluate("count(//div[@class='" + orientation + "Col']/descendant::h2)", document, null, XPathResult.NUMBER_TYPE, null).numberValue;
	}
}