Chickenfoot Site-Specific Scripts
From Chickenfoot Script Repository
| Table of contents |
Amazon.com
- Post to Ma.gnolia.com from Amazon.com This script will use data from an Amazon.com product page and post it to Ma.gnolia.com as a "wishlist" bookmark.
- Tab between Gmail and Calendar Navigate between your Gmail and Calendar tabs - Though this script does something very simple, it improves the feel of the Calendar/Gmail navigation links.
- Search blogs instead Adds a link to "Blogs" among the "Images/Groups/News" links on Google Web Search.
- Grab the Google Doodle Get the URL to the Google logo (doodle) that is currently displayed
- Adds Reader and Notebook links Adds links to Google Reader and Google Notebook in the navigation bar in Gmail.
Ma.gnolia.com
- Post to Ma.gnolia.com from Amazon.com This script will use data from an Amazon.com product page and post it to Ma.gnolia.com as a "wishlist" bookmark.
Safari.Oreilly.com
I love safari.oreilly.com, but hate having to hunt for the next/prev section button with my mouse (especially because i have a trackpad). Here's a small kludge to go to the next or previous page via the keyboard. on my powerbook, i prefer shift+right/left arrow for one-handed navigation, but you can use whatever combination you'd like.
// ==UserScript==
// @name Next and Previous Keyboard Shortcuts
// @when Pages Match
// @description add keyboard commands for next and previous buttons when browsing books
// @include http://*safari.oreilly.com/*
// ==/UserScript==
/* when browsing books on safari.oreilly.com,
* run this script to go to next or prev page
* --marcos ojeda, mmvi
*/
function navi(e){
function gogo(str){
m = find(str+" section link")
if(m.hasMatch)
go(m.element.href)
}
kc = e.keyCode
if(e.shiftKey && kc == 39) //rt arrow
gogo("next")
if(e.shiftKey && kc == 37) //lt arrow
gogo("previous")
}
window.addEventListener("keypress", navi, true)
MediaWiki
Save & continue
MediaWiki is a great piece of software, but when you're editing a wiki page (like this one), you can't click Save without losing your place in the edit box. That's a problem if you like to save reflexively.
This script fixes the problem by adding a "Save & continue" button to any MediaWiki edit page, which saves the page and then returns to the edit page again, restoring your scroll position and selection exactly where they were.
It also grabs Ctrl-S as a keyboard shortcut for the Save & continue functionality (but only while you're at the edit page).
// ==UserScript==
// @name Save and Continue
// @when Pages Match
// @description inserts a save and continue button to any mediawiki edit page
// @include http://groups.csail.mit.edu/uid/chickenfoot/scripts/index.php/*
// ==/UserScript==
// WikiSave trigger
// Assign this trigger to *action=edit* to make it
// fire on all MediaWiki edit pages (but possibly
// other pages too), or limit it to your favorite
// MediaWikis, e.g. http://groups.csail.mit.edu/uid/chickenfoot/scripts/*action=edit*
// Save this Wiki edit page, then come back to it
function wikiSave() {
var editPage = document.location.toString()
var textbox = find("textbox").element;
var editState = saveTextboxState(textbox)
click("save page button")
whenLoaded(function() {
go(editPage)
var textbox = find("textbox").element;
textbox.focus();
restoreTextboxState(textbox, editState);
})
}
// Saves a textbox's scroll and selection positions
function saveTextboxState(/*Node*/ tbox) {
return {
scrollTop : tbox.scrollTop,
scrollLeft : tbox.scrollLeft,
selectionStart : tbox.selectionStart,
selectionEnd : tbox.selectionEnd
};
}
// Restores a textbox's scroll and selection positions
function restoreTextboxState(/*Node*/ tbox, /*State*/ state) {
tbox.scrollTop = state.scrollTop;
tbox.scrollLeft = state.scrollLeft;
tbox.selectionStart = state.selectionStart;
tbox.selectionEnd = state.selectionEnd;
}
// inject Save & continue button
var b = new Button("Save & continue", wikiSave)
insert(after("save page button"), b)
// attach keyboard shortcut (Ctrl-S)
window.addEventListener("keypress", function(event) {
if (event.ctrlKey && event.charCode == 's'.charCodeAt(0)) {
wikiSave();
event.preventDefault();
}
}, true)
// use this with trigger */index.php/* to get a history
// of the last wiki pages of a MediaWiki site
MediaWiki navigation history
When moving around or creating new pages in MediaWiki, there is the problem that you cannot easily use the back button after having edited and saved a page, because this will bring you back to the editor and not to the page where you were before.
So, the following trigger script will take the last 6 visited wiki pages from your browser history and add them below the main navigation in a new box on the left site of the screen. It's best to set the trigger to something like */index.php/* or */wiki/*.
// ==UserScript==
// @name MediaWiki navigation history
// @when Pages Match
// @description adds new box on screen of last 6 visited wiki pages from browser history
// @include */index.php/*
// @include */wiki/*
// ==/UserScript==
function hist() {
var MAX_ENTRIES = 6; //maximum number of history entries
var TITLE_TEXT = 'wiki history';
var WIKI_PATTERN = /\/index.php|\/wiki\//; //URL pattern part for MediaWiki pages
var item, prevTitle, itemCount=0, links='';
for( var i=history.length-2;i>=0; i-- ) {
item = history[i];
if (itemCount==MAX_ENTRIES)
break;
if (!item.match(WIKI_PATTERN))
break;
if (item.indexOf("action") > 0 || item.indexOf("Special:Userlogin") > 0 )
continue;
var title = item.replace( /.*\//,"" ).replace( /#.*/,"" ).replace( /_/g, " " );
if (title == prevTitle)
continue;
itemCount++;
links+='<li><a href="'+item+'">'+title+'</a><br></li>';
prevTitle = title;
}
if( itemCount>0 )
insert(before(document.getElementById("p-search")),
'<div class="portlet"><h5>'+TITLE_TEXT+'</h5><div class="pBody"><ul>'+links+'</ul></div></div>');
}
hist();
mit412.com
This was used as an example script in a presentation on Chickenfoot. The presentation was given to the MIT class 15.564 on May 10, 2006 by Jason Carver, Mark Grimm and Jason Witzberger. The script is a proof-of-concept price comparison, using the mit412.com and Barnes and Noble prices.
Nice additions might include:
- Looking at other sites and returning the lowest price overall
- Speed improvement by manually forming B&N search URL instead of go,enter,click
- dealing with comma-separated authors in mit412 (B&N tends to get pretty confused by them)
Current limitations include:
- does not work with the mit412.com frames! (Must open the lower frame's page directly)
- does not return precise price (only floored dollar amount, to which we add 1)
- blindly returns first B&N hit on search "(Book) by (Author)"
- Fails catastrophically on non-standard search results, or 0 results returned
// ==UserScript==
// @name mit412
// @when Pages Match
// @description compares prices from mit412.com and Barnes and Noble
// @include http://www.mit412.com
// ==/UserScript==
/**
* Compare mit412.com book price to B&N price
* @version 0.2
* @date May 11th, 2006
* @authors Jason Carver, Mark Grimm, Jason Witzberger
* Trigger on: http://mit412.com/book.php?place=ube&id=*
* (We ran the script manually, and have not tested the trigger)
*/
output("Organizing Book Details")
var title = find( new TC('cell after "Book Title"'))
var author= find( new TC('cell after "Author"' ))
output("Opening B&N")
with(openTab('barnesandnoble.com',false)) { //now works on FireFox installations with Adblock
enter('first',title + " by " + author)
output( "Searching for: "+title + " by " + author)
click('first button')
output("Looking up price" )
price = find(new TC('number after "B&N price"'))
price++;
}
output("Inserting data into mit412 description page")
insert(new TC('after 5th row'),'<td><span style="color:red">B&N Price: </span><td><span style="color:red">$'+price +'</span>') //Sorry for the ugly html...
output("Script complete -- B&N Price is: $" + price)
//End of Script
Pubmed
Auto-add endnote references to pubmed feeds...
// ==UserScript==
// @name Pubmed
// @when Pages Match
// @description auto-add endnote references to pubmed feeds...
// @include http://www.ncbi.nlm.nih.gov/sites/*
// ==/UserScript==
/**
* Script by Brian Rosenthal, Robocommerce, LLC
* (contact through www.robocommerce.com)
*/
var i;
var tmp_links = [];
var tmp_nodes = [];
for (i = 0; (link = document.links[i]); i++) {
var list_uids = "";
if (link.href.indexOf("list_uids") >= 0
&& (link.href.indexOf("icon") < 0)) {
var url = link.href;
list_uids = url.replace(/.*list_uids=(\d+)\&.*/, "$1");
}
else if (link.href.indexOf("uid=") >= 0
&& (link.href.indexOf("icon") < 0)
&& (link.href.indexOf("from_uid")) < 0) {
var url = link.href;
list_uids = url.replace(/.*uid=(\d+)\&.*/, "$1");
}
if (list_uids != '') {
var span = document.createElement("span");
var a = document.createElement("a");
a.target = "_blank";
a.href = "http://www.hubmed.org/export/ris.cgi?uids=" + list_uids;
a.innerHTML = 'endnote';
span.innerHTML = ' ';
span.appendChild(a);
tmp_links.push(link);
tmp_nodes.push(span);
}
}
for (i = 0; (link = tmp_links[i]); i++) {
link.parentNode.appendChild(tmp_nodes[i]);
}
var s1 = find(/PMID:\W+\d+/);
var s2 = s1;
if (s2 != 'no matches') {
s2 = s2.replace(/[^\d]+/, "");
replace(s1, s1 + ' <a target="_blank" href="http://www.hubmed.org/export/ris.cgi?uids=' + s2 + '">endnote</a>');
}
6.001 Tutor
6.001 is the introductory programming course at MIT, which has a web-based tutor system. I used Chickenfoot to make several changes to the site's user interface.
// ==UserScript==
// @name 6.001 Tutor
// @when Pages Match
// @description modifies 6.001 online tutor's user interface
// @include http://sicp.csail.mit.edu/*
// ==/UserScript==
//First, I moved the login form to the top of the page:
// Move 6.001 login form to top of the page.
// Trigger assigned to http://sicp.csail.mit.edu/Spring-2007/
form = find("log in form").html;
insert(after("welcome to 6.001"), form)
//Chickenfoot really needs a move() command, or else remove() should return the chunk of page that it removed so that the chunk can be inserted //somewhere else.
//Second, I added a button that goes to a staff version of the tutor:
// Add button that jumps to staff tutor.
// Trigger assigned to http://sicp.csail.mit.edu/Spring-2007/
b = new Button("Staff Tutor", function() {
go("http://sicp.csail.mit.edu/Spring-2007/staff/");
pick("Staff");
setTimeout(function() { click("Log in"); }, 1);
});
insert(after("welcome to 6.001"), b);
//The setTimeout() call is a tricky hack, which gives Firefox a chance to autofill the username and password field of the staff login form before the //script clicks the Log In button. Without setTimeout, I'd have to put in enter() commands containing my username and password, too. (Note that //sleep() doesn't work properly here, unfortunately.)
//Third, the staff tutor has a complex form for generating reports. I added hyperlinks for the reports I use most frequently (the PS and Lecture //reports):
// Turn radio buttons into links on 6.001 report page.
// Assign trigger to https://sicp-s1.mit.edu/spring07/tutor.cgi
for (var m = find(/(PS|Lec) \d+/); m.hasMatch; m = m.next) {
addLink(m);
}
function addLink(m) {
var name = m.text; // e.g. "PS 1"
var link = new Link(name, function() {
find("form").element.reset()
pick(name);
click("submit");
});
replace(m, link);
}
//Basically this script finds "PS 1", "PS 2", etc., each of which is a label for a radiobutton. It then replaces each of those labels with a //hyperlink that selects the radio button and submits the form. A tricky bit here is that we have to reset the form, too, in order to get the result //we want. Chickenfoot probably needs a simple reset() command for this purpose.
Mailman
- Password management chooses the right password for the Mailman list you're logging into.
- Clear bounce flags clears the bounce flags for all members of a Mailman list.
CSAIL Member Resources page
This page (http://www.csail.mit.edu/resources/resources.html) that I use from time to time is very hard to scan for the link I want, because the links are displayed as URLs rather than as readable text. An example of the link format it uses is "Google - http://www.google.com". The script below finds all such links (using the simple keywords "http https") and puts the hyperlink around the text node preceding it instead (so the Google example would become simply "Google - (http://www.google.com)").
// ==UserScript==
// @name CSAIL Member Resources page
// @when Pages Match
// @description finds all links and puts the hyperlink around the text node preceding it instead
// @include http://www.csail.mit.edu/resources/resources.html
// ==/UserScript==
For good measure, the script also enlarges the link I use the most, to make it easier to find visually and easier to click on with the mouse.
// Fix weird hyperlinks and enlarge most useful link.
// Assign to trigger http://www.csail.mit.edu/resources/resources.html
for (m = find("http https link"); m.hasMatch; m = m.next) {
var link = m.element;
var text = link.previousSibling;
replace(text, "<a href='" + link + "'>" + text.nodeValue + "</a>");
remove(link);
}
mrbs = find("Book a CSAIL room link")
replace(mrbs, "<font size='+2'>" + mrbs.html + "</font>");
Major League Baseball Schedules
Spiegel Online
This script observes what you are looking at on Spiegel Online (http://www.spiegel.de), creates a simple interest profile, and adds a section with links to related articles near the top of each Spiegel page. It should be triggered on URLs matching http://www.spiegel.de/*.
I created the script to familiarize myself with Chickenfoot. It's not that useful on its own, but perhaps it will be helpful to Chickenfoot users who are going to create scripts providing related functionality.
// ==UserScript==
// @name Spiegel Online
// @when Pages Match
// @description observes what you are looking at, creates a simple interest profile, and adds a section with links to related articles near top
// @include http://www.spiegel.de
// ==/UserScript==
include("fileio.js");
include("json.js");
// constants ----------------------------------------
// max number of headlines to show
maxLinks = 5
// discounting factor used during interest profile update
interestDiscount = 0.8
// location of the serialized interest profile
profileFile = "~/spiegel-profile.json";
// extracts the subject from a Spiegel URL
subjectRegEx = /http:\/\/www\.spiegel\.de\/(\w+)\/.*/;
// used to identify page requests initiated by this script. This is necessary
// to prevent infinite recursion because each time we request a Spiegel Online
// page, another instance of this script is triggered by Chickenfoot. We abort
// script execution immediately if we were tiggered by a URL matching this pattern
hiddenPageRegEx = /as_hidden=true/;
// set of known subjects. Used to test whether what we extracted with subjectRegEx
// actually is a subject
subjects = { "politik" : 1, "wirtschaft" : 1, "panorama" : 1,
"sport" : 1, "kultur" : 1, "netzwelt" : 1, "wissenschaft" : 1,
"unispiegel" : 1, "schulspiegel" : 1, "reise" : 1,
"auto" : 1, "spam" : 1 };
// define the style for the infobox
infoboxHeader = "<div style='width: 765px; border: 1px gray dotted; " +
"margin-top: 10px; padding: 2px; float: left; font-size: small'>";
infoboxFooter = "</div>";
// profile definition ---------------------------
function Profile() {
profile = new Object();
profile.history = [];
profile.interests = new Object();
profile.addToHistory = function(url) {
this.history[this.history.length] = url;
}
profile.historyContains = function(url) {
for (i = 0; i < this.history.length; ++i) {
if (this.history[i] == url) {
return true;
}
}
return false;
}
profile.updateInterests = function(subject) {
// discount all interests
for (k in this.interests) {
this.interests[k] *= interestDiscount;
}
// increase interest in current subject
if (!this.interests[subject])
this.interests[subject] = 0;
this.interests[subject] += 1;
}
profile.mainInterest = function() {
mi = null;
ms = -1;
for (i in this.interests) {
if (this.interests[i] > ms) {
ms = this.interests[i];
mi = i;
}
}
return mi;
}
return profile;
}
function loadProfile() {
if (exists(profileFile)) {
s = read(profileFile);
return deserialize(s, false);
}
else {
return new Profile();
}
}
function saveProfile(profile) {
s = serialize(profile, false);
write(profileFile, s);
}
// functions ----------------------------------------
function adaptProfile(profile) {
match = subjectRegEx.exec(location.href);
if (match && (match[1] in subjects)) {
profile.updateInterests(match[1]);
profile.addToHistory(location.href);
saveProfile(profile);
}
}
function fetchLinks(subject, num) {
links = []
// mark the request as originating from this script by adding 'as_hidden'
hidden = fetch("http://www.spiegel.de/" + subject + "?as_hidden=true");
with (hidden) {
for (l = find(new XPath("//h3/a")); l.hasMatch; l = l.next) {
if (!profile.historyContains(l.element.getAttribute('href'))) {
links[links.length] = l;
if (links.length >= num) {
break;
}
}
}
}
hidden.close();
return links;
}
function sortInterests(interests) {
results = new Array();
for (i in interests) {
d = new Object();
d.subj = i;
d.val = interests[i];
results[results.length] = d;
}
return results.sort(function(a,b) { return b.val - a.val});
}
function mostRelevantLinks(profile) {
links = []
totalInterestVals = 0
for (interest in profile.interests) {
totalInterestVals += profile.interests[interest];
}
t = 0
sortedInterests = sortInterests(profile.interests)
for (j = 0; j < sortedInterests.length; ++j) {
interest = sortedInterests[j];
v = maxLinks * interest.val / totalInterestVals + t;
n = Math.round(v);
t = v - n;
if (n > 0) {
links = Array.concat(links, fetchLinks(interest.subj, n));
if (links.length >= maxLinks) {
break;
}
}
}
return links;
}
function createInfobox(profile) {
infobox = infoboxHeader;
infobox += "<h4>IHRE PERSÖNLICHE ARTIKELLISTE</h4>";
infobox += "<ul>";
links = mostRelevantLinks(profile);
for (i = 0; i < links.length; ++i) {
infobox += "<li>" + links[i].html + "</li>";
}
infobox += "</ul>";
infobox += infoboxFooter;
return infobox;
}
function createAndShowInfobox(profile) {
infobox = createInfobox(profile);
insert(before("login"), infobox);
}
// main ---------------------------------------------
if (!hiddenPageRegEx.test(location.href)) {
// load the user profile
profile = loadProfile();
// adapt the user profile
adaptProfile(profile);
// show the infobox if we know something about
// the user's interests
if (profile.mainInterest() != null) {
createAndShowInfobox(profile);
}
}
RememberTheMilk.com
Auto login to RememberTheMilk.com
// ==UserScript==
// @name Remember the Milk auto login
// @when Pages Match
// @description Auto login in RememberTheMilk.com
// @includes http://www.rememberthemilk.com/*
// ==/UserScript==
m = find("after 'Already a member?'").find('Login link');
if (m.hasMatch) {
go(m.element.href, false);
whenLoaded(function() {
enter('Username', 'YOUR_USERNANE_HERE');
enter('Password', 'YOUR_PASSWORD_HERE');
check('Remember me on this computer');
click('button');
});
}
Flyspray
We use the Flyspray (http://flyspray.org/) bug database. One feature we wanted to add was the ability to change severities, priorities, and assignments of bugs from the summary screen. This script provides that.
// ==UserScript==
// @name Add Dropdowns to Flyspray
// @when Pages Match
// @includes */flyspray/index.php*
// @excludes *do=*
// ==/UserScript==
insert(before("summary table"), new Link("Edit all bugs", modifyPage))
function modifyPage() {
retrieveFieldValues(fields);
for (var i = 0; i < fields.length; ++i) {
convertColumnToDropdowns(fields[i])
}
}
var fields = [
{ name: "Priority",
class: "project_priority",
},
{ name: "Severity",
class: "project_severity",
},
{ name: "Category",
class: "project_category",
},
{ name: "Status",
class: "project_status",
},
{ name: "assigned_to",
class: "project_assigned",
},
];
function retrieveFieldValues(fields) {
var firstBug = find(new XPath("//td[@class='project_summary']/a"))
with (openTab(firstBug.element.href)) {
click("Edit this task")
for (var i = 0; i < fields.length; ++i) {
listbox = find(fields[i].name + " listbox")
if (listbox.hasMatch) fields[i].html = listbox.html
else output("can't find " + fields[i].name)
}
close()
}
}
function convertColumnToDropdowns(field) {
if (!field.html) return;
for (priority in find(new XPath("//td[@class='" + field.class + "']"))) {
var currValue = priority.text
if (!currValue && field.name == "assigned_to") currValue = "No-one"
var cell = priority.element
cell.innerHTML = field.html
var select = cell.childNodes[0]
if (currValue) pick(select, "first " + currValue)
select.addEventListener("click", function(event) {
event.stopPropagation();
}, true);
select.addEventListener("change", changedDropdown, false);
}
function changedDropdown(event) {
try {
output(event.target)
var select = event.target
var newValue = select.options[select.selectedIndex].text
output(newValue)
var row = select.parentNode.parentNode
var link = find(row).find("link")
var href = link.element.href
with(openTab(href)) {
click("Edit this task")
pick(field.name, "first " + newValue)
click("Save Details")
wait()
close()
}
} catch (e) {
output(e)
}
}
}
