During this Greasemonkey bender I’m on I wanted to get a cleaner interface for working with GM_xmlhttpRequest. I’m using a resourceful style Rails app to serve up my data so I jimmied up this little library to handle my data storage, update, and retrieval needs. This requires jQuery and is designed to be included in a Greasemonkey script. Example to follow.
/*
Speakeasy.js
Version: 0.1.0
It's kind of like ActiveResource
Copyright (c) 2009, STRd6 (http://strd6.com)
Liscensed under the MIT License
Prerequisites:
Greasemonkey Environment
jQuery
*/
/**
Speakeasy abstracts the GM_xmlhttprequest and handles communication with the remote script server.
It's kind of like ActiveResource
*/
Speakeasy = function($) {
var baseUrl = 'http://localhost:3000/';
var apiKey = 0;
function generateArrayDataTransfer(objectType, callback) {
return function(responseData) {
var dataArray = eval('(' + responseData + ')');
var elements = $.map(dataArray, function(element) {
return element[objectType];
});
callback(elements);
};
}
function generateDataTransfer(objectType, callback) {
return function(responseData) {
var data = eval('(' + responseData + ')');
callback(data[objectType]);
};
}
function loadOptionsData(type, dataObject) {
var optionsData = {
api_key: apiKey
};
$.each(dataObject, function(field, value) {
log(field + ': ' + value)
optionsData[type + '[' + field +']'] = value;
});
return optionsData;
}
function makeRequest(resource, options) {
var method = options.method || 'GET';
var url = baseUrl + resource + '.js';
var headers = {
'User-agent': 'Mozilla/4.0 (compatible) Greasemonkey',
'Accept': 'application/json,application/atom+xml,application/xml,text/xml'
};
var data = $.param(options.data || '');
var onSuccess = options.onSuccess || (function(){});
if(method == 'POST') {
headers['Content-type'] = 'application/x-www-form-urlencoded';
} else if(method == 'GET') {
if(data) {
url += '?' + data;
}
}
GM_xmlhttpRequest({
method: method,
url: url,
headers: headers,
data: data,
onload: function(responseDetails) {
if(responseDetails.status == 200) {
onSuccess(responseDetails.responseText);
} else {
console.warn(url + ' - ' + responseDetails.status + ':nn' + responseDetails.responseText);
}
}
});
}
function generateResource(type) {
var pluralType = type + 's';
var all = function() {
return function(options, callback) {
var dataTransfer = generateArrayDataTransfer(type, callback);
options.onSuccess = dataTransfer;
makeRequest(pluralType, options);
};
}();
var create = function() {
return function(dataObject, callback) {
var options = {
method: 'POST'
};
options.data = loadOptionsData(type, dataObject);
makeRequest(pluralType, options);
};
}();
var find = function() {
return function(options, callback) {
var dataTransfer = generateDataTransfer(type, callback);
if(typeof(options) == 'number') {
options.onSuccess = dataTransfer;
makeRequest(pluralType + '/' + options, dataTransfer);
} else {
log("TODO: Non-integer find not currently supported!");
}
};
}();
var update = function() {
return function(dataObject, callback) {
var id = dataObject.id;
var options = {
method: 'POST'
};
options.data = loadOptionsData(type, dataObject);
makeRequest(pluralType + '/update/' + id, options);
};
}();
var resource = {
all: all,
create: create,
find: find,
update: update
};
return resource;
}
var self = {
annotation: generateResource('annotation'),
script: generateResource('script')
};
return self;
}(jQuery);
Example uses:
// ==UserScript==
// @name Speakeasy Demo
// @namespace http://strd6.com
// @description Super-simple website annotations shared with all!
// @include *
//
// @require http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js
// @require http://strd6.com/stuff/jqui/speakeasy.js
// ==/UserScript==
function display(annotation) {
var id = annotation.id;
$('')
.text(annotation.text)
.addClass('annotation')
.css({
top: annotation.top,
left: annotation.left
})
.bind('drag', function( event ) {
$( this ).css({
top: event.offsetY,
left: event.offsetX
});
})
.bind('dragend', function( event ) {
Speakeasy.annotation.update({id: id, top: $(this).css('top'), left: $(this).css('left')});
})
.fadeTo('fast', 0.75)
.appendTo('body');
}
Speakeasy.annotation.all({data: {url: currentUrl}}, function(annotations) {
$.each(annotations, function(index, annotation) {
display(annotation);
});
});
Enjoy!