Simplify loading code by using CsrfMagic wrapper object.
[csrf-magic.git] / csrf-magic.js
blob0bd04f8689278711dc5afd7b2b891e4ac3cf21ef
1 /**
2  * @file
3  *
4  * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
5  * plays nice with other JavaScript libraries, needs testing though.
6  */
7 // Here are the basic overloaded method definitions
8 // The wrapper must be set BEFORE onreadystatechange is written to, since
9 // a bug in ActiveXObject prevents us from properly testing for it.
10 var CsrfMagic = function (real) {
11     // overloaded
12     this.csrf = real;
13     // properties
14     var csrfMagic = this;
15     real.onreadystatechange = function() {
16         csrfMagic._updateProps();
17         return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
18     };
19     csrfMagic._updateProps();
22 CsrfMagic.prototype.open = function(method, url, async, username, password) {
23     if (method == 'POST') this.csrf_isPost = true;
24     // deal with Opera bug, thanks jQuery
25     if (username) return this.csrf_open(method, url, async, username, password);
26     else return this.csrf_open(method, url, async);
28 CsrfMagic.prototype.csrf_open = function(method, url, async, username, password) {
29     if (username) return this.csrf.open(method, url, async, username, password);
30     else return this.csrf.open(method, url, async);
33 CsrfMagic.prototype.send = function(data) {
34     if (!this.csrf_isPost) this.csrf_send(data);
35     prepend = csrfMagicName + '=' + csrfMagicToken + '&';
36     if (this.csrf_purportedLength === undefined) {
37         this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
38         delete this.csrf_purportedLength;
39     }
40     delete this.csrf_isPost;
41     return this.csrf_send(prepend + data);
43 CsrfMagic.prototype.csrf_send = function(data) {
44     return this.csrf.send(data);
47 CsrfMagic.prototype.setRequestHeader = function(header, value) {
48     // We have to auto-set this at the end, since we don't know how long the
49     // nonce is when added to the data.
50     if (this.csrf_isPost && header == "Content-length") {
51         this.csrf_purportedLength = value;
52         return;
53     }
54     return this.csrf_setRequestHeader(header, value);
56 CsrfMagic.prototype.csrf_setRequestHeader = function(header, value) {
57     return this.csrf.setRequestHeader(header, value);
60 CsrfMagic.prototype.abort = function () {
61     return this.csrf.abort();
63 CsrfMagic.prototype.getAllResponseHeaders = function() {
64     return this.csrf.getAllResponseHeaders();
66 CsrfMagic.prototype.getResponseHeader = function(header) {
67     return this.csrf.getResponseHeader(header);
70 // proprietary
71 CsrfMagic.prototype._updateProps = function() {
72     this.readyState = this.csrf.readyState;
73     if (this.readyState == 4) {
74         this.responseText = this.csrf.responseText;
75         this.responseXML  = this.csrf.responseXML;
76         this.status       = this.csrf.status;
77         this.statusText   = this.csrf.statusText;
78     }
80 CsrfMagic.process = function(base) {
81     var prepend = csrfMagicName + '=' + csrfMagicToken;
82     if (base) return prepend + '&' + base;
83     return prepend;
86 // Sets things up for Mozilla/Opera/nice browsers
87 if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
88     XMLHttpRequest.prototype.csrf_open = XMLHttpRequest.prototype.open;
89     XMLHttpRequest.prototype.csrf_send = XMLHttpRequest.prototype.send;
90     XMLHttpRequest.prototype.csrf_setRequestHeader = XMLHttpRequest.prototype.setRequestHeader;
91     
92     XMLHttpRequest.prototype.open = CsrfMagic.prototype.open;
93     XMLHttpRequest.prototype.send = CsrfMagic.prototype.send;
94     XMLHttpRequest.prototype.setRequestHeader = CsrfMagic.prototype.setRequestHeader;
95 } else {
96     // The only way we can do this is by modifying a library you have been
97     // using. We plan to support YUI, script.aculo.us, prototype, MooTools, 
98     // jQuery, Ext and Dojo.
99     if (window.jQuery) {
100         // jQuery didn't implement a new XMLHttpRequest function, so we have
101         // to do this the hard way.
102         jQuery.csrf_ajax = jQuery.ajax;
103         jQuery.ajax = function( s ) {
104             if (s.type && s.type.toUpperCase() == 'POST') {
105                 s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
106                 if ( s.data && s.processData && typeof s.data != "string" ) {
107                     s.data = jQuery.param(s.data);
108                 }
109                 s.data = CsrfMagic.process(s.data);
110             }
111             return jQuery.csrf_ajax( s );
112         }
113     } else if (window.Prototype) {
114         // This works for script.aculo.us too
115         Ajax.csrf_getTransport = Ajax.getTransport;
116         Ajax.getTransport = function() {
117             return new CsrfMagic(Ajax.csrf_getTransport());
118         }
119     } else if (window.MooTools) {
120         Browser.csrf_Request = Browser.Request;
121         Browser.Request = function () {
122             return new CsrfMagic(Browser.csrf_Request());
123         }
124     } else if (window.YAHOO) {
125         YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
126         YAHOO.util.Connect.createXhrObject = function (transaction) {
127             obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
128             var old = obj.conn;
129             obj.conn = new CsrfMagic(old);
130             return obj;
131         }
132     }