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!