Script Details
File Name: djsteveboy_playlist.user.js
Description
DJ Steve Boyett is a talented DJ (and apparently a writer as well) who has two podcasts (Podrunner and Groovelectic) that I currently am loving. However, I didn't really like certain aspects of his site. I was particularly aggravated by the following:
- Clicking the faux-download link under the "latest mix" doesn't start the download process, but rather opens up another section that has download instructions and a true download button to actually download the file. I don't need no stinkin' instructions— I just want the file.
- Expanding a description for a particular mix doesn't show the playlist. Sure, I can click another link to get the playlist, but who has time for that?
- There is no way to view all mix descriptions at once.
This script fixes all that- the download link now initiates the download, playlists are displayed when a mix is expanded, and an Expand/Collapse All Descriptions link is added under the "Archive" header.
Script Source
// DJ Steveboy: Playlist Displayer
// Copyright (c) 2006-2007 Orbona
// Version 1.0
// Release Date: 2007-10-22
//
// See also: http://www.orbona.com/greasemonkey/
//
// Original file name: djsteveboy_playlist.user.js
// Please reference the original file name when contacting me regarding this
// script.
//
// This software is licensed under the CC-GNU GPL:
// http://creativecommons.org/license/cc-gpl
//
// ----------------------------------------------------------------------------
// DESCRIPTION
//
// Adds the track listing to the expanded "archive" entries on DJ Steveboy's
// site. Also modifies the "Download" link under the latest offering so that
// it actually initiates a download, instead of expanding the section that
// explains how to download a file.
// ----------------------------------------------------------------------------
// ==UserScript==
// @name DJ Steveboy: Playlist Displayer
// @description Includes the track listing when expanding the "archive" entries on DJ Steveboy's site
// @namespace http://www.orbona.com/greasemonkey/
// @include http://www.djsteveboy.com/groovelectric.html
// @include http://www.djsteveboy.com/podrunner.html
// @include http://www.djsteveboy.com/intervals.html
// @include http://djsteveboy.com/groovelectric.html
// @include http://djsteveboy.com/podrunner.html
// @include http://djsteveboy.com/intervals.html
// ==/UserScript==
String.prototype.startsWith = function(s) { return this.indexOf(s)==0; }
String.prototype.endsWith = function (str) {
var offset = this.length - str.length;
return offset >= 0 && this.lastIndexOf(str) === offset;
};
// ----------------------------------------------------------------------------
// Retrieves the playlist based on the "id" associated with the button/link/
// whatever that was clicked
// ----------------------------------------------------------------------------
function showPlaylist(event) {
var target = event ? event.target : this;
var id = target.id;
if (!id && target.parentNode) id = target.parentNode.id;
if (id.startsWith('link')) id = id.substr(4)
unsafeWindow.retrievePlaylist(id);
}
// ----------------------------------------------------------------------------
// Main function that does everything described in the description area above.
// ----------------------------------------------------------------------------
function doIt() {
var currentDownloadSection;
if (document.URL.endsWith('intervals.html'))
currentDownloadSection = document.getElementById('qC') ;
else
currentDownloadSection=document.getElementById('qB') || document.getElementById('q2');
var regexID = /showParagraph\('([^)]+)'\)/im;
var allShowParagraphs="";
var i, archiveDiv, expandCollapseAll, actualDownloadLink, oldDownloadLinks, oldDownloadLink, thisLink, paragraphID, pID, script, scriptText, paragraphLinks, divsSetHeight,divSetHeight;
if (currentDownloadSection) {
actualDownloadLink=document.evaluate(".//A[contains(@href,'.mp3')]", currentDownloadSection, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
if (actualDownloadLink.snapshotLength) {
actualDownloadLink = actualDownloadLink.snapshotItem(0).href;
oldDownloadLinks = document.evaluate(".//A[contains(@href, '" + currentDownloadSection.id + "')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i=0; i < oldDownloadLinks.snapshotLength; i++) {
oldDownloadLink = oldDownloadLinks.snapshotItem(i);
if (oldDownloadLink.innerHTML.match(/download/i))
oldDownloadLink.href = actualDownloadLink;
}
}
}
archiveDiv= document.getElementById('qE') || document.getElementById('qD') || document.getElementById('q4');
if (!archiveDiv) return false;
expandCollapseAll = document.createElement('div');
expandCollapseAll.innerHTML='<div><table width="324" height="22" border="1" align="center" cellpadding="0" cellspacing="0" bordercolor="#CCCCCC" bgcolor="#000000"><tr><td width="287" bgcolor="#003366"><div align="center"><a href="#" onclick="toggleAllParagraphDisplay(); return false;" id="expandCollapseAll">Expand All Descriptions</a></div></td></tr></table></div>';
archiveDiv.insertBefore(expandCollapseAll, archiveDiv.firstChild);
paragraphLinks=document.evaluate(".//A[contains(@href,'javascript:showParagraph')]", archiveDiv, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
divsSetHeight= document.evaluate('//div[@class="style4"]', document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
fixHeight:
for (i=0; i < divsSetHeight.snapshotLength; i++) {
divSetHeight = divsSetHeight.snapshotItem(i);
if (divSetHeight.style.height=="500px") {
divSetHeight.style.height=null;
break fixHeight;
}
}
for (i=0; i < paragraphLinks.snapshotLength; i++) {
thisLink = paragraphLinks.snapshotItem(i);
if (paragraphID = regexID.exec( thisLink.href ) ) {
pID = paragraphID[1];
if (pID) {
//writing the code on the fly as we're going through the items we are interested in
allShowParagraphs += " forceToggleParagraphDisplay(displayStyle, '" +pID + "');\n";
thisLink.id ='link' + pID;
thisLink.addEventListener('click', showPlaylist, true);
} //have a paragraph id
} //paragraph match
} //for all showParagraph links
// yes, this could be written in a loop similar to the above
// but since we already know the paragraph ids, it seemed more efficient to not
// search for the ids again
if (allShowParagraphs) {
script = document.createElement('script');
script.type = 'text/javascript';
scriptText = "var showingAll=false;\n\nfunction forceToggleParagraphDisplay(displayStyle, parID) {\n var paragraph = document.getElementById(parID);\n if (paragraph != null) paragraph.style.display = displayStyle;\n if (showingAll) retrievePlaylist(parID);\n}\n";
scriptText += "\nfunction toggleAllParagraphDisplay() {\n showingAll=!showingAll;\n var displayStyle= showingAll ? 'block' : 'none';\n var expandCollapseAll = document.getElementById('expandCollapseAll');\n expandCollapseAll.innerHTML= !showingAll ? 'Expand All Descriptions' : 'Collapse All Descriptions';\n" + allShowParagraphs + "}\n";
scriptText += "\nfunction retrievePlaylist(id) {\n var paragraph, trackDiv, popupLinks, url, http, tracks, actualListing, track;\n var counter=0;\n var tracksRetrieved= document.getElementById('tracks'+id);\n if (tracksRetrieved) return;\n paragraph = document.getElementById(id);\n\n if (!paragraph ) return;\n\n trackDiv = document.createElement('div');\n trackDiv.innerHTML = '<div id=\\'tracks' + id +'\\'>Retrieving playlist...</div>';\n paragraph.appendChild(trackDiv);\n\n popupLinks=document.evaluate(\"//DIV[@id='\" + id + \"']//p//A[contains(@href,'javascript:popUp')]\",document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);\n\n if (popupLinks.snapshotLength !=1) return;\n\n url = '/' + popupLinks.snapshotItem(0).href.substring(18, popupLinks.snapshotItem(0).href.length-2);\n\n http = new XMLHttpRequest();\n http.open('GET',url,true)\n http.send(null);\n\n http.onreadystatechange = function() {\n if (http.readyState==4) {\n if (http.status==200) {\n tracks = http.responseText.replace(/<[^>]+>/gim, '');\n tracks = tracks.split('\\n');\n\n actualListing='';\n for (var i=0; i <tracks.length; i++) {\n track = tracks[i].replace(/^\\s+/g, '').replace(/\\s+$/g, '');\n\n if (track.match(/^\\d+\./)) {\n if (counter++>0)actualListing+='<br />\\n';\n actualListing+= track;\n }\n }\n\n trackDiv.innerHTML = '<div id=\\'tracks' + id +'\\'>' + actualListing + '</div>';\n\n }\n else{\n trackDiv.innerHTML = '<div id=\\'tracks' + id +'\\'>Unable to retrieve playlist.</div>';\n alert('Problem retrieving playlist data for id: ' + id)\n }\n } //if ready\n } //function definition for readystatechange\n}";
//GM_log(scriptText);
script.innerHTML = scriptText;
document.body.appendChild(script);
}
}
// ------------------END OF FUNCTIONS -----------------------------
window.addEventListener("load", function() { doIt(); }, false);